/* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * * Openvision retains the copyright to derivative works of * this source code. Do *NOT* create a derivative of this * source code before consulting with your legal department. * Do *NOT* integrate *ANY* of this source code into another * product before consulting with your legal department. * * For further information, read the top-level Openvision * copyright which is contained in the top-level MIT Kerberos * copyright. * * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * */ /* * Copyright 1993-1994 OpenVision Technologies, Inc., All Rights Reserved. * * $Header$ * * */ static char rcsid[] = "$Id: kpasswd.c 17258 2005-06-21 01:36:03Z raeburn $"; #include #include #include "kpasswd_strings.h" #define string_text error_message #include "kpasswd.h" #include #include #include #include extern char *whoami; extern void display_intro_message(); extern long read_old_password(); extern long read_new_password(); #define MISC_EXIT_STATUS 6 /* * Function: kpasswd * * Purpose: Initialize and call lower level routines to change a password * * Arguments: * * context (r) krb5_context to use * argc/argv (r) principal name to use, optional * read_old_password (f) function to read old password * read_new_password (f) function to read new and change password * display_intro_message (f) function to display intro message * whoami (extern) argv[0] * * Returns: * exit status of 0 for success * 1 principal unknown * 2 old password wrong * 3 cannot initialize admin server session * 4 new passwd mismatch or error trying to change pw * 5 password not typed * 6 misc error * 7 incorrect usage * * Requires: * Passwords cannot be more than 255 characters long. * * Effects: * * If argc is 2, the password for the principal specified in argv[1] * is changed; otherwise, the principal of the default credential * cache or username is used. display_intro_message is called with * the arguments KPW_STR_CHANGING_PW_FOR and the principal name. * read_old_password is then called to prompt for the old password. * The admin system is then initialized, the principal's policy * retrieved and explained, if appropriate, and finally * read_new_password is called to read the new password and change the * principal's password (presumably ovsec_kadm_chpass_principal). * admin system is de-initialized before the function returns. * * Modifies: * * Changes the principal's password. * */ int kpasswd(context, argc, argv) krb5_context context; int argc; char *argv[]; { kadm5_ret_t code; krb5_ccache ccache = NULL; krb5_principal princ = 0; char *princ_str; struct passwd *pw = 0; unsigned int pwsize; char password[255]; /* I don't really like 255 but that's what kinit uses */ char msg_ret[1024], admin_realm[1024]; kadm5_principal_ent_rec principal_entry; kadm5_policy_ent_rec policy_entry; void *server_handle; kadm5_config_params params; char *cpw_service; memset((char *)¶ms, 0, sizeof (params)); memset(&principal_entry, 0, sizeof (principal_entry)); memset(&policy_entry, 0, sizeof (policy_entry)); if (argc > 2) { com_err(whoami, KPW_STR_USAGE, 0); return(7); /*NOTREACHED*/ } /************************************ * Get principal name to change * ************************************/ /* Look on the command line first, followed by the default credential cache, followed by defaulting to the Unix user name */ if (argc == 2) princ_str = strdup(argv[1]); else { code = krb5_cc_default(context, &ccache); /* If we succeed, find who is in the credential cache */ if (code == 0) { /* Get default principal from cache if one exists */ code = krb5_cc_get_principal(context, ccache, &princ); /* if we got a principal, unparse it, otherwise get out of the if with an error code */ (void) krb5_cc_close(context, ccache); if (code == 0) { code = krb5_unparse_name(context, princ, &princ_str); if (code != 0) { com_err(whoami, code, string_text(KPW_STR_UNPARSE_NAME)); return(MISC_EXIT_STATUS); } } } /* this is a crock.. we want to compare against */ /* "KRB5_CC_DOESNOTEXIST" but there is no such error code, and */ /* both the file and stdio types return FCC_NOFILE. If there is */ /* ever another ccache type (or if the error codes are ever */ /* fixed), this code will have to be updated. */ if (code && code != KRB5_FCC_NOFILE) { com_err(whoami, code, string_text(KPW_STR_WHILE_LOOKING_AT_CC)); return(MISC_EXIT_STATUS); } /* if either krb5_cc failed check the passwd file */ if (code != 0) { pw = getpwuid( getuid()); if (pw == NULL) { com_err(whoami, 0, string_text(KPW_STR_NOT_IN_PASSWD_FILE)); return(MISC_EXIT_STATUS); } princ_str = strdup(pw->pw_name); } } display_intro_message(string_text(KPW_STR_CHANGING_PW_FOR), princ_str); /* Need to get a krb5_principal, unless we started from with one from the credential cache */ if (! princ) { code = krb5_parse_name (context, princ_str, &princ); if (code != 0) { com_err(whoami, code, string_text(KPW_STR_PARSE_NAME), princ_str); free(princ_str); return(MISC_EXIT_STATUS); } } pwsize = sizeof(password); code = read_old_password(context, password, &pwsize); if (code != 0) { memset(password, 0, sizeof(password)); com_err(whoami, code, string_text(KPW_STR_WHILE_READING_PASSWORD)); krb5_free_principal(context, princ); free(princ_str); return(MISC_EXIT_STATUS); } if (pwsize == 0) { memset(password, 0, sizeof(password)); com_err(whoami, 0, string_text(KPW_STR_NO_PASSWORD_READ)); krb5_free_principal(context, princ); free(princ_str); return(5); } snprintf(admin_realm, sizeof (admin_realm), krb5_princ_realm(context, princ)->data); params.mask |= KADM5_CONFIG_REALM; params.realm = admin_realm; if (kadm5_get_cpw_host_srv_name(context, admin_realm, &cpw_service)) { fprintf(stderr, gettext("%s: unable to get host based " "service name for realm %s\n"), whoami, admin_realm); exit(1); } code = kadm5_init_with_password(princ_str, password, cpw_service, ¶ms, KADM5_STRUCT_VERSION, KADM5_API_VERSION_2, NULL, &server_handle); free(cpw_service); if (code != 0) { if (code == KADM5_BAD_PASSWORD) com_err(whoami, 0, string_text(KPW_STR_OLD_PASSWORD_INCORRECT)); else com_err(whoami, 0, string_text(KPW_STR_CANT_OPEN_ADMIN_SERVER), admin_realm, error_message(code)); krb5_free_principal(context, princ); free(princ_str); return ((code == KADM5_BAD_PASSWORD) ? 2 : 3); } /* * we can only check the policy if the server speaks * RPCSEC_GSS */ if (_kadm5_get_kpasswd_protocol(server_handle) == KRB5_CHGPWD_RPCSEC) { /* Explain policy restrictions on new password if any. */ /* * Note: copy of this exists in login * (kverify.c/get_verified_in_tkt). */ code = kadm5_get_principal(server_handle, princ, &principal_entry, KADM5_PRINCIPAL_NORMAL_MASK); if (code != 0) { com_err(whoami, 0, string_text((code == KADM5_UNK_PRINC) ? KPW_STR_PRIN_UNKNOWN : KPW_STR_CANT_GET_POLICY_INFO), princ_str); krb5_free_principal(context, princ); free(princ_str); (void) kadm5_destroy(server_handle); return ((code == KADM5_UNK_PRINC) ? 1 : MISC_EXIT_STATUS); } if ((principal_entry.aux_attributes & KADM5_POLICY) != 0) { code = kadm5_get_policy(server_handle, principal_entry.policy, &policy_entry); if (code != 0) { /* * doesn't matter which error comes back, * there's no nice recovery or need to * differentiate to the user */ com_err(whoami, 0, string_text(KPW_STR_CANT_GET_POLICY_INFO), princ_str); (void) kadm5_free_principal_ent(server_handle, &principal_entry); krb5_free_principal(context, princ); free(princ_str); free(princ_str); (void) kadm5_destroy(server_handle); return (MISC_EXIT_STATUS); } com_err(whoami, 0, string_text(KPW_STR_POLICY_EXPLANATION), princ_str, principal_entry.policy, policy_entry.pw_min_length, policy_entry.pw_min_classes); if (code = kadm5_free_principal_ent(server_handle, &principal_entry)) { (void) kadm5_free_policy_ent(server_handle, &policy_entry); krb5_free_principal(context, princ); free(princ_str); com_err(whoami, code, string_text(KPW_STR_WHILE_FREEING_PRINCIPAL)); (void) kadm5_destroy(server_handle); return (MISC_EXIT_STATUS); } if (code = kadm5_free_policy_ent(server_handle, &policy_entry)) { krb5_free_principal(context, princ); free(princ_str); com_err(whoami, code, string_text(KPW_STR_WHILE_FREEING_POLICY)); (void) kadm5_destroy(server_handle); return (MISC_EXIT_STATUS); } } else { /* * kpasswd *COULD* output something here to * encourage the choice of good passwords, * in the absence of an enforced policy. */ if (code = kadm5_free_principal_ent(server_handle, &principal_entry)) { krb5_free_principal(context, princ); free(princ_str); com_err(whoami, code, string_text(KPW_STR_WHILE_FREEING_PRINCIPAL)); (void) kadm5_destroy(server_handle); return (MISC_EXIT_STATUS); } } } /* if protocol == KRB5_CHGPWD_RPCSEC */ pwsize = sizeof(password); code = read_new_password(server_handle, password, &pwsize, msg_ret, sizeof (msg_ret), princ); memset(password, 0, sizeof(password)); if (code) com_err(whoami, 0, msg_ret); krb5_free_principal(context, princ); free(princ_str); (void) kadm5_destroy(server_handle); if (code == KRB5_LIBOS_CANTREADPWD) return(5); else if (code) return(4); else return(0); }