1 /* 2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 7 /* 8 * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING 9 * 10 * Openvision retains the copyright to derivative works of 11 * this source code. Do *NOT* create a derivative of this 12 * source code before consulting with your legal department. 13 * Do *NOT* integrate *ANY* of this source code into another 14 * product before consulting with your legal department. 15 * 16 * For further information, read the top-level Openvision 17 * copyright which is contained in the top-level MIT Kerberos 18 * copyright. 19 * 20 * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING 21 * 22 */ 23 24 25 /* 26 * Copyright 1993-1994 OpenVision Technologies, Inc., All Rights Reserved. 27 * 28 * $Header$ 29 * 30 * 31 */ 32 33 static char rcsid[] = "$Id: kpasswd.c 17258 2005-06-21 01:36:03Z raeburn $"; 34 35 #include <kadm5/admin.h> 36 #include <krb5.h> 37 38 #include "kpasswd_strings.h" 39 #define string_text error_message 40 41 #include "kpasswd.h" 42 43 #include <stdio.h> 44 #include <pwd.h> 45 #include <string.h> 46 #include <libintl.h> 47 48 extern char *whoami; 49 50 extern void display_intro_message(); 51 extern long read_old_password(); 52 extern long read_new_password(); 53 54 #define MISC_EXIT_STATUS 6 55 56 /* 57 * Function: kpasswd 58 * 59 * Purpose: Initialize and call lower level routines to change a password 60 * 61 * Arguments: 62 * 63 * context (r) krb5_context to use 64 * argc/argv (r) principal name to use, optional 65 * read_old_password (f) function to read old password 66 * read_new_password (f) function to read new and change password 67 * display_intro_message (f) function to display intro message 68 * whoami (extern) argv[0] 69 * 70 * Returns: 71 * exit status of 0 for success 72 * 1 principal unknown 73 * 2 old password wrong 74 * 3 cannot initialize admin server session 75 * 4 new passwd mismatch or error trying to change pw 76 * 5 password not typed 77 * 6 misc error 78 * 7 incorrect usage 79 * 80 * Requires: 81 * Passwords cannot be more than 255 characters long. 82 * 83 * Effects: 84 * 85 * If argc is 2, the password for the principal specified in argv[1] 86 * is changed; otherwise, the principal of the default credential 87 * cache or username is used. display_intro_message is called with 88 * the arguments KPW_STR_CHANGING_PW_FOR and the principal name. 89 * read_old_password is then called to prompt for the old password. 90 * The admin system is then initialized, the principal's policy 91 * retrieved and explained, if appropriate, and finally 92 * read_new_password is called to read the new password and change the 93 * principal's password (presumably ovsec_kadm_chpass_principal). 94 * admin system is de-initialized before the function returns. 95 * 96 * Modifies: 97 * 98 * Changes the principal's password. 99 * 100 */ 101 int 102 kpasswd(context, argc, argv) 103 krb5_context context; 104 int argc; 105 char *argv[]; 106 { 107 kadm5_ret_t code; 108 krb5_ccache ccache = NULL; 109 krb5_principal princ = 0; 110 char *princ_str; 111 struct passwd *pw = 0; 112 unsigned int pwsize; 113 char password[255]; /* I don't really like 255 but that's what kinit uses */ 114 char msg_ret[1024], admin_realm[1024]; 115 kadm5_principal_ent_rec principal_entry; 116 kadm5_policy_ent_rec policy_entry; 117 void *server_handle; 118 kadm5_config_params params; 119 char *cpw_service; 120 121 memset((char *)¶ms, 0, sizeof (params)); 122 memset(&principal_entry, 0, sizeof (principal_entry)); 123 memset(&policy_entry, 0, sizeof (policy_entry)); 124 125 if (argc > 2) { 126 com_err(whoami, KPW_STR_USAGE, 0); 127 return(7); 128 /*NOTREACHED*/ 129 } 130 131 /************************************ 132 * Get principal name to change * 133 ************************************/ 134 135 /* Look on the command line first, followed by the default credential 136 cache, followed by defaulting to the Unix user name */ 137 138 if (argc == 2) 139 princ_str = strdup(argv[1]); 140 else { 141 code = krb5_cc_default(context, &ccache); 142 /* If we succeed, find who is in the credential cache */ 143 if (code == 0) { 144 /* Get default principal from cache if one exists */ 145 code = krb5_cc_get_principal(context, ccache, &princ); 146 /* if we got a principal, unparse it, otherwise get out of the if 147 with an error code */ 148 (void) krb5_cc_close(context, ccache); 149 if (code == 0) { 150 code = krb5_unparse_name(context, princ, &princ_str); 151 if (code != 0) { 152 com_err(whoami, code, string_text(KPW_STR_UNPARSE_NAME)); 153 return(MISC_EXIT_STATUS); 154 } 155 } 156 } 157 158 /* this is a crock.. we want to compare against */ 159 /* "KRB5_CC_DOESNOTEXIST" but there is no such error code, and */ 160 /* both the file and stdio types return FCC_NOFILE. If there is */ 161 /* ever another ccache type (or if the error codes are ever */ 162 /* fixed), this code will have to be updated. */ 163 if (code && code != KRB5_FCC_NOFILE) { 164 com_err(whoami, code, string_text(KPW_STR_WHILE_LOOKING_AT_CC)); 165 return(MISC_EXIT_STATUS); 166 } 167 168 /* if either krb5_cc failed check the passwd file */ 169 if (code != 0) { 170 pw = getpwuid( getuid()); 171 if (pw == NULL) { 172 com_err(whoami, 0, string_text(KPW_STR_NOT_IN_PASSWD_FILE)); 173 return(MISC_EXIT_STATUS); 174 } 175 princ_str = strdup(pw->pw_name); 176 } 177 } 178 179 display_intro_message(string_text(KPW_STR_CHANGING_PW_FOR), princ_str); 180 181 /* Need to get a krb5_principal, unless we started from with one from 182 the credential cache */ 183 184 if (! princ) { 185 code = krb5_parse_name (context, princ_str, &princ); 186 if (code != 0) { 187 com_err(whoami, code, string_text(KPW_STR_PARSE_NAME), princ_str); 188 free(princ_str); 189 return(MISC_EXIT_STATUS); 190 } 191 } 192 193 pwsize = sizeof(password); 194 code = read_old_password(context, password, &pwsize); 195 196 if (code != 0) { 197 memset(password, 0, sizeof(password)); 198 com_err(whoami, code, string_text(KPW_STR_WHILE_READING_PASSWORD)); 199 krb5_free_principal(context, princ); 200 free(princ_str); 201 return(MISC_EXIT_STATUS); 202 } 203 if (pwsize == 0) { 204 memset(password, 0, sizeof(password)); 205 com_err(whoami, 0, string_text(KPW_STR_NO_PASSWORD_READ)); 206 krb5_free_principal(context, princ); 207 free(princ_str); 208 return(5); 209 } 210 211 snprintf(admin_realm, sizeof (admin_realm), 212 krb5_princ_realm(context, princ)->data); 213 params.mask |= KADM5_CONFIG_REALM; 214 params.realm = admin_realm; 215 216 217 if (kadm5_get_cpw_host_srv_name(context, admin_realm, &cpw_service)) { 218 fprintf(stderr, gettext("%s: unable to get host based " 219 "service name for realm %s\n"), 220 whoami, admin_realm); 221 exit(1); 222 } 223 224 code = kadm5_init_with_password(princ_str, password, cpw_service, 225 ¶ms, KADM5_STRUCT_VERSION, 226 KADM5_API_VERSION_2, NULL, 227 &server_handle); 228 free(cpw_service); 229 if (code != 0) { 230 if (code == KADM5_BAD_PASSWORD) 231 com_err(whoami, 0, 232 string_text(KPW_STR_OLD_PASSWORD_INCORRECT)); 233 else 234 com_err(whoami, 0, 235 string_text(KPW_STR_CANT_OPEN_ADMIN_SERVER), 236 admin_realm, 237 error_message(code)); 238 krb5_free_principal(context, princ); 239 free(princ_str); 240 return ((code == KADM5_BAD_PASSWORD) ? 2 : 3); 241 } 242 243 /* 244 * we can only check the policy if the server speaks 245 * RPCSEC_GSS 246 */ 247 if (_kadm5_get_kpasswd_protocol(server_handle) == KRB5_CHGPWD_RPCSEC) { 248 /* Explain policy restrictions on new password if any. */ 249 /* 250 * Note: copy of this exists in login 251 * (kverify.c/get_verified_in_tkt). 252 */ 253 254 code = kadm5_get_principal(server_handle, princ, 255 &principal_entry, 256 KADM5_PRINCIPAL_NORMAL_MASK); 257 if (code != 0) { 258 com_err(whoami, 0, 259 string_text((code == KADM5_UNK_PRINC) 260 ? KPW_STR_PRIN_UNKNOWN : 261 KPW_STR_CANT_GET_POLICY_INFO), 262 princ_str); 263 krb5_free_principal(context, princ); 264 free(princ_str); 265 (void) kadm5_destroy(server_handle); 266 return ((code == KADM5_UNK_PRINC) ? 1 : 267 MISC_EXIT_STATUS); 268 } 269 if ((principal_entry.aux_attributes & KADM5_POLICY) != 0) { 270 code = kadm5_get_policy(server_handle, 271 principal_entry.policy, 272 &policy_entry); 273 if (code != 0) { 274 /* 275 * doesn't matter which error comes back, 276 * there's no nice recovery or need to 277 * differentiate to the user 278 */ 279 com_err(whoami, 0, 280 string_text(KPW_STR_CANT_GET_POLICY_INFO), 281 princ_str); 282 (void) kadm5_free_principal_ent(server_handle, 283 &principal_entry); 284 krb5_free_principal(context, princ); 285 free(princ_str); 286 free(princ_str); 287 (void) kadm5_destroy(server_handle); 288 return (MISC_EXIT_STATUS); 289 } 290 com_err(whoami, 0, 291 string_text(KPW_STR_POLICY_EXPLANATION), 292 princ_str, principal_entry.policy, 293 policy_entry.pw_min_length, 294 policy_entry.pw_min_classes); 295 if (code = kadm5_free_principal_ent(server_handle, 296 &principal_entry)) { 297 (void) kadm5_free_policy_ent(server_handle, 298 &policy_entry); 299 krb5_free_principal(context, princ); 300 free(princ_str); 301 com_err(whoami, code, 302 string_text(KPW_STR_WHILE_FREEING_PRINCIPAL)); 303 (void) kadm5_destroy(server_handle); 304 return (MISC_EXIT_STATUS); 305 } 306 if (code = kadm5_free_policy_ent(server_handle, 307 &policy_entry)) { 308 krb5_free_principal(context, princ); 309 free(princ_str); 310 com_err(whoami, code, 311 string_text(KPW_STR_WHILE_FREEING_POLICY)); 312 (void) kadm5_destroy(server_handle); 313 return (MISC_EXIT_STATUS); 314 } 315 } else { 316 /* 317 * kpasswd *COULD* output something here to 318 * encourage the choice of good passwords, 319 * in the absence of an enforced policy. 320 */ 321 if (code = kadm5_free_principal_ent(server_handle, 322 &principal_entry)) { 323 krb5_free_principal(context, princ); 324 free(princ_str); 325 com_err(whoami, code, 326 string_text(KPW_STR_WHILE_FREEING_PRINCIPAL)); 327 (void) kadm5_destroy(server_handle); 328 return (MISC_EXIT_STATUS); 329 } 330 } 331 } /* if protocol == KRB5_CHGPWD_RPCSEC */ 332 333 pwsize = sizeof(password); 334 code = read_new_password(server_handle, password, &pwsize, msg_ret, sizeof (msg_ret), princ); 335 memset(password, 0, sizeof(password)); 336 337 if (code) 338 com_err(whoami, 0, msg_ret); 339 340 krb5_free_principal(context, princ); 341 free(princ_str); 342 343 (void) kadm5_destroy(server_handle); 344 345 if (code == KRB5_LIBOS_CANTREADPWD) 346 return(5); 347 else if (code) 348 return(4); 349 else 350 return(0); 351 } 352