/*+************************************************************************* ** ** k5passwd ** ** Changes your password in the Kerberos V5. This should have been ** part of the kadm stuff but we're forced to build a nicer API on top ** of the calls they provide. ** ***************************************************************************/ #ifdef KRB5 #include #include #include #include "krb5.h" #include "com_err.h" #include "adm.h" #include "adm_proto.h" static const char *kadm_replies[] = { "Operation successful", /* KRB5_ADM_SUCCESS */ "Command not recognized", /* KRB5_ADM_CMD_UNKNOWN */ "Password unacceptable to server", /* KRB5_ADM_PW_UNACCEPT */ "Old password incorrect", /* KRB5_ADM_BAD_PW */ "Invalid ticket (TKT_FLAG_INITIAL not set)",/* KRB5_ADM_NOT_IN_TKT */ "Server refused password change", /* KRB5_ADM_CANT_CHANGE */ "Language not supported", /* KRB5_ADM_LANG_NOT_SUPPORTED */ }; static const char *kadm_replies_unknown = "UNKNOWN ERROR"; static char errbuf[1024]; /* For response from kadm */ /*+************************************************************************* ** ** get_admin_response ** ** Builds into a static buffer the replies sent back by the admin server. ** ***************************************************************************/ static char * get_admin_response ( krb5_int32 status, // Type of error krb5_int32 nreplies, // Size of reply krb5_data * reply) // Buffer of messages { char *ptr; // For building the response char *end = errbuf + sizeof (errbuf); // So we don't overflow int i; // Index int n; // Length if (status <= KRB5_ADM_LANG_NOT_SUPPORTED) // Is it of a known type??? strcpy (errbuf, kadm_replies[status]); else strcpy (errbuf, kadm_replies_unknown); // Unknown error type ptr = errbuf + strlen (errbuf); // Point at the end if (nreplies > 0) { // Are there more message? *ptr++ = ':'; *ptr = '\0'; } for (i = 0; i < nreplies; ++i) { // Append additional messages *ptr++ = '\n'; n = reply[i].length; // Easier to work with if (ptr + n + 2 >= errbuf) // Check for overflow break; memcpy (ptr, reply[i].data, n); // Add the message ptr += n; // Point to the end *ptr = '\0'; } return errbuf; } /*+************************************************************************* ** ** keyadmin_send_recieve ** ** Sends a command to the key admin and reads the reply. ** ***************************************************************************/ static krb5_error_code keyadmin_send_receive ( krb5_context k5context, int * conn_socket, krb5_auth_context * auth_context, krb5_int32 nargs, krb5_data * arglist, krb5_int32 * cmd_stat, krb5_int32 * nreplies, krb5_data ** reply) { krb5_error_code kret; kret = krb5_send_adm_cmd (k5context, conn_socket, auth_context, nargs, arglist); if (! kret) kret = krb5_read_adm_reply (k5context, conn_socket, auth_context, cmd_stat, nreplies, reply); return kret; } /*+************************************************************************* ** ** k5_change_password ** ** Bundles all the crude needed to change the password into one file. ** ***************************************************************************/ krb5_error_code k5_change_password ( krb5_context k5context, char *user, char *realm, char *opasswd, char *npasswd, char **text) { krb5_error_code kret, kret2; krb5_auth_context * auth_context; krb5_ccache ccache; int conn_socket; /* Socket for talking over */ krb5_int32 nreplies; krb5_data data[3]; krb5_data * reply; krb5_int32 status; char * name; *text = NULL; /* Be safe */ name = malloc (strlen (user) + strlen (realm) + 2); if (name == NULL) return ENOMEM; sprintf (name, "%s@%s", user, realm); /* ** Establish the connection. */ kret = krb5_adm_connect (k5context, name, NULL, opasswd, &conn_socket, &auth_context, &ccache); if (kret) goto done; /* ** Check to see if it's an acceptable password */ data[0].data = KRB5_ADM_CHECKPW_CMD; data[0].length = strlen (data[0].data); data[1].data = npasswd; data[1].length = strlen (npasswd); kret = keyadmin_send_receive (k5context, &conn_socket, auth_context, 2, data, &status, &nreplies, &reply); if (kret) /* Some external error */ goto cleanup; if (status != KRB5_ADM_SUCCESS) { /* Some problem??? */ kret = status; *text = get_admin_response (status, nreplies, reply); krb5_free_adm_data (k5context, nreplies, reply); goto quit; } krb5_free_adm_data (k5context, nreplies, reply); /* ** The new password is ok, so now actually change the password */ data[0].data = KRB5_ADM_CHANGEPW_CMD; data[0].length = strlen (data[0].data); data[1].data = opasswd; data[1].length = strlen (opasswd); data[2].data = npasswd; data[2].length = strlen (npasswd); kret = keyadmin_send_receive (k5context, &conn_socket, auth_context, 3, data, &status, &nreplies, &reply); if (kret) goto cleanup; if (status != KRB5_ADM_SUCCESS) { kret = status; *text = get_admin_response (status, nreplies, reply); krb5_free_adm_data (k5context, nreplies, reply); goto quit; } krb5_free_adm_data (k5context, nreplies, reply); /*+ ** Need to send quit command. */ quit: data[0].data = KRB5_ADM_QUIT_CMD; data[0].length = strlen (data[0].data); kret2 = keyadmin_send_receive (k5context, &conn_socket, auth_context, 1, data, &status, &nreplies, &reply); if (kret2) { if (! kret) kret = kret2; } else if (status != KRB5_ADM_SUCCESS) { if (! kret) kret = status; if (*text == NULL) *text = get_admin_response (status, nreplies, reply); } krb5_free_adm_data (k5context, nreplies, reply); cleanup: krb5_adm_disconnect (k5context, &conn_socket, auth_context, ccache); done: free (name); return kret; } #endif /* KRB5 */