1 /* 2 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 #pragma ident "%Z%%M% %I% %E% SMI" 7 8 /* 9 * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING 10 * 11 * Openvision retains the copyright to derivative works of 12 * this source code. Do *NOT* create a derivative of this 13 * source code before consulting with your legal department. 14 * Do *NOT* integrate *ANY* of this source code into another 15 * product before consulting with your legal department. 16 * 17 * For further information, read the top-level Openvision 18 * copyright which is contained in the top-level MIT Kerberos 19 * copyright. 20 * 21 * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING 22 * 23 */ 24 25 26 /* 27 * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. 28 * 29 * $Header: /cvs/krbdev/krb5/src/lib/kadm5/chpass_util.c,v 1.18.18.1 2000/05/19 22:24:14 raeburn Exp $ 30 * 31 * 32 */ 33 34 35 #include <stdio.h> 36 #include <memory.h> 37 #include <time.h> 38 #include <locale.h> 39 40 #include <kadm5/admin.h> 41 #include "admin_internal.h" 42 43 #include <krb5.h> 44 45 #define string_text error_message 46 47 const char *chpw_error_message(kadm5_ret_t code); 48 49 /* 50 * Function: kadm5_chpass_principal_util 51 * 52 * Purpose: Wrapper around chpass_principal. We can read new pw, 53 * change pw and return useful messages 54 * 55 * Arguments: 56 * 57 * princ (input) a krb5b_principal structure for the 58 * principal whose password we should change. 59 * 60 * new_password (input) NULL or a null terminated string with the 61 * the principal's desired new password. If new_password 62 * is NULL then this routine will read a new password. 63 * 64 * pw_ret (output) if non-NULL, points to a static buffer 65 * containing the new password (if password is prompted 66 * internally), or to the new_password argument (if 67 * that is non-NULL). If the former, then the buffer 68 * is only valid until the next call to the function, 69 * and the caller should be sure to zero it when 70 * it is no longer needed. 71 * 72 * msg_ret (output) a useful message is copied here. 73 * 74 * <return value> exit status of 0 for success, else the com err code 75 * for the last significant routine called. 76 * 77 * Requires: 78 * 79 * A msg_ret should point to a buffer large enough for the messasge. 80 * 81 * Effects: 82 * 83 * Modifies: 84 * 85 * 86 */ 87 88 kadm5_ret_t _kadm5_chpass_principal_util(void *server_handle, 89 void *lhandle, 90 krb5_principal princ, 91 char *new_pw, 92 char **ret_pw, 93 char *msg_ret, 94 int msg_len) 95 { 96 int code, code2; 97 unsigned int pwsize; 98 static char buffer[255]; 99 char *new_password; 100 kadm5_principal_ent_rec princ_ent; 101 kadm5_policy_ent_rec policy_ent; 102 krb5_chgpwd_prot passwd_protocol; 103 104 _KADM5_CHECK_HANDLE(server_handle); 105 106 if (ret_pw) 107 *ret_pw = NULL; 108 109 if (new_pw != NULL) { 110 new_password = new_pw; 111 } else { /* read the password */ 112 krb5_context context; 113 114 if ((code = (int) krb5_init_context(&context)) == 0) { 115 pwsize = sizeof(buffer); 116 code = krb5_read_password(context, 117 KADM5_PW_FIRST_PROMPT, 118 KADM5_PW_SECOND_PROMPT, 119 buffer, &pwsize); 120 krb5_free_context(context); 121 } 122 123 if (code == 0) 124 new_password = buffer; 125 else { 126 #ifdef ZEROPASSWD 127 memset(buffer, 0, sizeof(buffer)); 128 #endif 129 if (code == KRB5_LIBOS_BADPWDMATCH) { 130 strncpy(msg_ret, string_text(CHPASS_UTIL_NEW_PASSWORD_MISMATCH), 131 msg_len - 1); 132 msg_ret[msg_len - 1] = '\0'; 133 return(code); 134 } else { 135 strncpy(msg_ret, error_message(code), msg_len - 1); 136 strncat(msg_ret, " ", msg_len - 1); 137 strncat(msg_ret, string_text(CHPASS_UTIL_WHILE_READING_PASSWORD), 138 msg_len - 1); 139 strncat(msg_ret, string_text(CHPASS_UTIL_PASSWORD_NOT_CHANGED), 140 msg_len - 1); 141 msg_ret[msg_len - 1] = '\0'; 142 return(code); 143 } 144 } 145 if (pwsize == 0) { 146 #ifdef ZEROPASSWD 147 memset(buffer, 0, sizeof(buffer)); 148 #endif 149 strncpy(msg_ret, string_text(CHPASS_UTIL_NO_PASSWORD_READ), msg_len - 1); 150 msg_ret[msg_len - 1] = '\0'; 151 return(KRB5_LIBOS_CANTREADPWD); /* could do better */ 152 } 153 } 154 155 if (ret_pw) 156 *ret_pw = new_password; 157 158 passwd_protocol = _kadm5_get_kpasswd_protocol(server_handle); 159 if (passwd_protocol == KRB5_CHGPWD_CHANGEPW_V2) { 160 kadm5_ret_t srvr_rsp_code; 161 krb5_data srvr_msg; 162 163 srvr_msg.length = 0; 164 srvr_msg.data = NULL; 165 166 code = kadm5_chpass_principal_v2(server_handle, princ, 167 new_password, 168 &srvr_rsp_code, 169 &srvr_msg); 170 if (srvr_rsp_code) { 171 sprintf(msg_ret, "%s%s%.*s\n", 172 chpw_error_message(srvr_rsp_code), 173 srvr_msg.length? ": " : "", 174 srvr_msg.length, 175 srvr_msg.data ? srvr_msg.data : ""); 176 177 return (srvr_rsp_code); 178 } 179 return (code); 180 181 } else if (passwd_protocol == KRB5_CHGPWD_RPCSEC) { 182 code = kadm5_chpass_principal(server_handle, princ, 183 new_password); 184 185 #ifdef ZEROPASSWD 186 if (!ret_pw) 187 memset(buffer, 0, sizeof (buffer)); 188 #endif 189 190 if (code == KADM5_OK) { 191 strncpy(msg_ret, string_text(CHPASS_UTIL_PASSWORD_CHANGED), msg_len - 1); 192 msg_ret[msg_len - 1] = '\0'; 193 return(0); 194 } 195 196 if ((code != KADM5_PASS_Q_TOOSHORT) && 197 (code != KADM5_PASS_REUSE) && 198 (code != KADM5_PASS_Q_CLASS) && 199 (code != KADM5_PASS_Q_DICT) && 200 (code != KADM5_PASS_TOOSOON)) { 201 /* Can't get more info for other errors */ 202 sprintf(buffer, "%s %s", error_message(code), 203 string_text(CHPASS_UTIL_WHILE_TRYING_TO_CHANGE)); 204 sprintf(msg_ret, "%s\n%s\n", 205 string_text(CHPASS_UTIL_PASSWORD_NOT_CHANGED), 206 buffer); 207 return(code); 208 } 209 210 /* Ok, we have a password quality error. Return a good message */ 211 212 if (code == KADM5_PASS_REUSE) { 213 strncpy(msg_ret, string_text(CHPASS_UTIL_PASSWORD_REUSE), msg_len - 1); 214 msg_ret[msg_len - 1] = '\0'; 215 return(code); 216 } 217 218 if (code == KADM5_PASS_Q_DICT) { 219 strncpy(msg_ret, string_text(CHPASS_UTIL_PASSWORD_IN_DICTIONARY), 220 msg_len - 1); 221 msg_ret[msg_len - 1] = '\0'; 222 return(code); 223 } 224 225 /* Look up policy for the remaining messages */ 226 227 code2 = kadm5_get_principal (lhandle, princ, &princ_ent, 228 KADM5_PRINCIPAL_NORMAL_MASK); 229 if (code2 != 0) { 230 strncpy(msg_ret, error_message(code2), msg_len - 1); 231 strncat(msg_ret, " ", msg_len - 1 - strlen(msg_ret)); 232 strncat(msg_ret, string_text(CHPASS_UTIL_GET_PRINC_INFO), msg_len - 1 - strlen(msg_ret)); 233 strncat(msg_ret, "\n", msg_len - 1 - strlen(msg_ret)); 234 strncat(msg_ret, error_message(code), msg_len - 1 - strlen(msg_ret)); 235 strncat(msg_ret, " ", msg_len - 1 - strlen(msg_ret)); 236 strncat(msg_ret, string_text(CHPASS_UTIL_WHILE_TRYING_TO_CHANGE), 237 msg_len - 1 - strlen(msg_ret)); 238 strncat(msg_ret, "\n\n", msg_len - 1 - strlen(msg_ret)); 239 strncat(msg_ret, string_text(CHPASS_UTIL_PASSWORD_NOT_CHANGED), 240 msg_len - 1 - strlen(msg_ret)); 241 strncat(msg_ret, "\n", msg_len - 1 - strlen(msg_ret)); 242 msg_ret[msg_len - 1] = '\0'; 243 return(code); 244 } 245 246 if ((princ_ent.aux_attributes & KADM5_POLICY) == 0) { 247 strncpy(msg_ret, error_message(code), msg_len - 1 - strlen(msg_ret)); 248 strncat(msg_ret, " ", msg_len - 1 - strlen(msg_ret)); 249 strncpy(msg_ret, string_text(CHPASS_UTIL_NO_POLICY_YET_Q_ERROR), 250 msg_len - 1 - strlen(msg_ret)); 251 strncat(msg_ret, "\n\n", msg_len - 1 - strlen(msg_ret)); 252 strncpy(msg_ret, string_text(CHPASS_UTIL_PASSWORD_NOT_CHANGED), 253 msg_len - 1 - strlen(msg_ret)); 254 msg_ret[msg_len - 1] = '\0'; 255 256 (void) kadm5_free_principal_ent(lhandle, &princ_ent); 257 return(code); 258 } 259 260 code2 = kadm5_get_policy(lhandle, princ_ent.policy, 261 &policy_ent); 262 if (code2 != 0) { 263 sprintf(msg_ret, "%s %s\n%s %s\n\n%s\n ", 264 error_message(code2), 265 string_text(CHPASS_UTIL_GET_POLICY_INFO), 266 error_message(code), 267 string_text(CHPASS_UTIL_WHILE_TRYING_TO_CHANGE), 268 string_text(CHPASS_UTIL_PASSWORD_NOT_CHANGED)); 269 (void) kadm5_free_principal_ent(lhandle, &princ_ent); 270 return(code); 271 } 272 273 if (code == KADM5_PASS_Q_TOOSHORT) { 274 sprintf(msg_ret, 275 string_text(CHPASS_UTIL_PASSWORD_TOO_SHORT), 276 policy_ent.pw_min_length); 277 (void) kadm5_free_principal_ent(lhandle, &princ_ent); 278 (void) kadm5_free_policy_ent(lhandle, &policy_ent); 279 return(code); 280 } 281 282 if (code == KADM5_PASS_Q_CLASS) { 283 sprintf(msg_ret, 284 string_text(CHPASS_UTIL_TOO_FEW_CLASSES), 285 policy_ent.pw_min_classes); 286 (void) kadm5_free_principal_ent(lhandle, &princ_ent); 287 (void) kadm5_free_policy_ent(lhandle, &policy_ent); 288 return(code); 289 } 290 291 if (code == KADM5_PASS_TOOSOON) { 292 time_t until; 293 char *time_string, *ptr; 294 295 until = princ_ent.last_pwd_change + 296 policy_ent.pw_min_life; 297 298 time_string = ctime(&until); 299 if (*(ptr = &time_string[strlen(time_string)-1]) == 300 '\n') 301 *ptr = '\0'; 302 303 sprintf(msg_ret, 304 string_text(CHPASS_UTIL_PASSWORD_TOO_SOON), 305 time_string); 306 (void) kadm5_free_principal_ent(lhandle, &princ_ent); 307 (void) kadm5_free_policy_ent(lhandle, &policy_ent); 308 return(code); 309 } else { 310 /* We should never get here, but just in case ... */ 311 sprintf(buffer, "%s %s", error_message(code), 312 string_text(CHPASS_UTIL_WHILE_TRYING_TO_CHANGE)); 313 sprintf(msg_ret, "%s\n%s\n", 314 string_text(CHPASS_UTIL_PASSWORD_NOT_CHANGED), 315 buffer); 316 (void) kadm5_free_principal_ent(lhandle, &princ_ent); 317 (void) kadm5_free_policy_ent(lhandle, &policy_ent); 318 return(code); 319 } 320 } else { 321 sprintf(msg_ret, "%s\n%s\n", 322 string_text(CHPASS_UTIL_PASSWORD_NOT_CHANGED), 323 "Password protocol in krb5.conf is not supported\n"); 324 return (-1); 325 } 326 } 327 328 /* 329 * krb5_chpw_result_code_string 330 * 331 * convert the return code received from the password server 332 * to a human-readable string. 333 */ 334 const char * 335 chpw_error_message(kadm5_ret_t result_code) 336 { 337 switch (result_code) { 338 case KRB5_KPASSWD_MALFORMED: 339 return (dgettext(TEXT_DOMAIN, "Malformed request error")); 340 case KRB5_KPASSWD_HARDERROR: 341 return (dgettext(TEXT_DOMAIN, "Server error")); 342 case KRB5_KPASSWD_AUTHERROR: 343 return (dgettext(TEXT_DOMAIN, "Authentication error")); 344 case KRB5_KPASSWD_SOFTERROR: 345 return (dgettext(TEXT_DOMAIN, "Password change rejected")); 346 case KRB5_KPASSWD_ACCESSDENIED: 347 return (dgettext(TEXT_DOMAIN, 348 "Not authorized to change password")); 349 case KRB5_KPASSWD_BAD_VERSION: 350 return (dgettext(TEXT_DOMAIN, "Protocol version unsupported")); 351 case KRB5_KPASSWD_INITIAL_FLAG_NEEDED: 352 return (dgettext(TEXT_DOMAIN, 353 "initial flag required in changepw request")); 354 case KRB5_KPASSWD_POLICY_REJECT: 355 return (dgettext(TEXT_DOMAIN, "new password fails policy")); 356 case KRB5_KPASSWD_BAD_PRINCIPAL: 357 return (dgettext(TEXT_DOMAIN, 358 "target principal does not exist for " 359 "changepw request")); 360 case KRB5_KPASSWD_ETYPE_NOSUPP: 361 return (dgettext(TEXT_DOMAIN, 362 "changepw request key sequence has an " 363 "unsupported Etype")); 364 default: 365 return (dgettext(TEXT_DOMAIN, "Password change failed")); 366 } 367 } 368