1 /* 2 * Copyright 2006 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 #ifdef HAVE_MEMORY_H 37 #include <memory.h> 38 #endif 39 #include <time.h> 40 #include <locale.h> 41 42 #include <kadm5/admin.h> 43 #include "admin_internal.h" 44 45 #include <krb5.h> 46 47 #define string_text error_message 48 49 const char *chpw_error_message(kadm5_ret_t code); 50 51 /* 52 * Function: kadm5_chpass_principal_util 53 * 54 * Purpose: Wrapper around chpass_principal. We can read new pw, change pw and return useful messages 55 * 56 * Arguments: 57 * 58 * princ (input) a krb5b_principal structure for the 59 * principal whose password we should change. 60 * 61 * new_password (input) NULL or a null terminated string with the 62 * the principal's desired new password. If new_password 63 * is NULL then this routine will read a new password. 64 * 65 * pw_ret (output) if non-NULL, points to a static buffer 66 * containing the new password (if password is prompted 67 * internally), or to the new_password argument (if 68 * that is non-NULL). If the former, then the buffer 69 * is only valid until the next call to the function, 70 * and the caller should be sure to zero it when 71 * it is no longer needed. 72 * 73 * msg_ret (output) a useful message is copied here. 74 * 75 * <return value> exit status of 0 for success, else the com err code 76 * for the last significant routine called. 77 * 78 * Requires: 79 * 80 * A msg_ret should point to a buffer large enough for the messasge. 81 * 82 * Effects: 83 * 84 * Modifies: 85 * 86 * 87 */ 88 89 kadm5_ret_t _kadm5_chpass_principal_util(void *server_handle, 90 void *lhandle, 91 krb5_principal princ, 92 char *new_pw, 93 char **ret_pw, 94 char *msg_ret, 95 unsigned int msg_len) 96 { 97 int code, code2; 98 unsigned int pwsize; 99 static char buffer[255]; 100 char *new_password; 101 kadm5_principal_ent_rec princ_ent; 102 kadm5_policy_ent_rec policy_ent; 103 krb5_chgpwd_prot passwd_protocol; 104 105 _KADM5_CHECK_HANDLE(server_handle); 106 107 if (ret_pw) 108 *ret_pw = NULL; 109 110 if (new_pw != NULL) { 111 new_password = new_pw; 112 } else { /* read the password */ 113 krb5_context context; 114 115 if ((code = (int) krb5_init_context(&context)) == 0) { 116 pwsize = sizeof(buffer); 117 code = krb5_read_password(context, 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)); /* in case we read a new password */ 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) &&(code != KADM5_PASS_Q_CLASS) && 198 (code != KADM5_PASS_Q_DICT) && (code != KADM5_PASS_TOOSOON)) { 199 /* Can't get more info for other errors */ 200 sprintf(buffer, "%s %s", error_message(code), 201 string_text(CHPASS_UTIL_WHILE_TRYING_TO_CHANGE)); 202 sprintf(msg_ret, "%s\n%s\n", string_text(CHPASS_UTIL_PASSWORD_NOT_CHANGED), 203 buffer); 204 return(code); 205 } 206 207 /* Ok, we have a password quality error. Return a good message */ 208 209 if (code == KADM5_PASS_REUSE) { 210 strncpy(msg_ret, string_text(CHPASS_UTIL_PASSWORD_REUSE), msg_len - 1); 211 msg_ret[msg_len - 1] = '\0'; 212 return(code); 213 } 214 215 if (code == KADM5_PASS_Q_DICT) { 216 strncpy(msg_ret, string_text(CHPASS_UTIL_PASSWORD_IN_DICTIONARY), 217 msg_len - 1); 218 msg_ret[msg_len - 1] = '\0'; 219 return(code); 220 } 221 222 /* Look up policy for the remaining messages */ 223 224 code2 = kadm5_get_principal (lhandle, princ, &princ_ent, 225 KADM5_PRINCIPAL_NORMAL_MASK); 226 if (code2 != 0) { 227 strncpy(msg_ret, error_message(code2), msg_len - 1); 228 strncat(msg_ret, " ", msg_len - 1 - strlen(msg_ret)); 229 strncat(msg_ret, string_text(CHPASS_UTIL_GET_PRINC_INFO), msg_len - 1 - strlen(msg_ret)); 230 strncat(msg_ret, "\n", msg_len - 1 - strlen(msg_ret)); 231 strncat(msg_ret, error_message(code), msg_len - 1 - strlen(msg_ret)); 232 strncat(msg_ret, " ", msg_len - 1 - strlen(msg_ret)); 233 strncat(msg_ret, string_text(CHPASS_UTIL_WHILE_TRYING_TO_CHANGE), 234 msg_len - 1 - strlen(msg_ret)); 235 strncat(msg_ret, "\n\n", msg_len - 1 - strlen(msg_ret)); 236 strncat(msg_ret, string_text(CHPASS_UTIL_PASSWORD_NOT_CHANGED), 237 msg_len - 1 - strlen(msg_ret)); 238 strncat(msg_ret, "\n", msg_len - 1 - strlen(msg_ret)); 239 msg_ret[msg_len - 1] = '\0'; 240 return(code); 241 } 242 243 if ((princ_ent.aux_attributes & KADM5_POLICY) == 0) { 244 strncpy(msg_ret, error_message(code), msg_len - 1 - strlen(msg_ret)); 245 strncat(msg_ret, " ", msg_len - 1 - strlen(msg_ret)); 246 strncpy(msg_ret, string_text(CHPASS_UTIL_NO_POLICY_YET_Q_ERROR), 247 msg_len - 1 - strlen(msg_ret)); 248 strncat(msg_ret, "\n\n", msg_len - 1 - strlen(msg_ret)); 249 strncpy(msg_ret, string_text(CHPASS_UTIL_PASSWORD_NOT_CHANGED), 250 msg_len - 1 - strlen(msg_ret)); 251 msg_ret[msg_len - 1] = '\0'; 252 253 (void) kadm5_free_principal_ent(lhandle, &princ_ent); 254 return(code); 255 } 256 257 code2 = kadm5_get_policy(lhandle, princ_ent.policy, 258 &policy_ent); 259 if (code2 != 0) { 260 sprintf(msg_ret, "%s %s\n%s %s\n\n%s\n ", error_message(code2), 261 string_text(CHPASS_UTIL_GET_POLICY_INFO), 262 error_message(code), 263 string_text(CHPASS_UTIL_WHILE_TRYING_TO_CHANGE), 264 string_text(CHPASS_UTIL_PASSWORD_NOT_CHANGED)); 265 (void) kadm5_free_principal_ent(lhandle, &princ_ent); 266 return(code); 267 } 268 269 if (code == KADM5_PASS_Q_TOOSHORT) { 270 sprintf(msg_ret, string_text(CHPASS_UTIL_PASSWORD_TOO_SHORT), 271 policy_ent.pw_min_length); 272 (void) kadm5_free_principal_ent(lhandle, &princ_ent); 273 (void) kadm5_free_policy_ent(lhandle, &policy_ent); 274 return(code); 275 } 276 277 278 if (code == KADM5_PASS_Q_CLASS) { 279 sprintf(msg_ret, string_text(CHPASS_UTIL_TOO_FEW_CLASSES), 280 policy_ent.pw_min_classes); 281 (void) kadm5_free_principal_ent(lhandle, &princ_ent); 282 (void) kadm5_free_policy_ent(lhandle, &policy_ent); 283 return(code); 284 } 285 286 if (code == KADM5_PASS_TOOSOON) { 287 time_t until; 288 char *time_string, *ptr; 289 290 until = princ_ent.last_pwd_change + policy_ent.pw_min_life; 291 292 time_string = ctime(&until); 293 if (*(ptr = &time_string[strlen(time_string)-1]) == '\n') 294 *ptr = '\0'; 295 296 sprintf(msg_ret, string_text(CHPASS_UTIL_PASSWORD_TOO_SOON), 297 time_string); 298 (void) kadm5_free_principal_ent(lhandle, &princ_ent); 299 (void) kadm5_free_policy_ent(lhandle, &policy_ent); 300 return(code); 301 } else { 302 303 /* We should never get here, but just in case ... */ 304 sprintf(buffer, "%s %s", error_message(code), 305 string_text(CHPASS_UTIL_WHILE_TRYING_TO_CHANGE)); 306 sprintf(msg_ret, "%s\n%s\n", string_text(CHPASS_UTIL_PASSWORD_NOT_CHANGED), 307 buffer); 308 (void) kadm5_free_principal_ent(lhandle, &princ_ent); 309 (void) kadm5_free_policy_ent(lhandle, &policy_ent); 310 return(code); 311 } 312 } else { 313 sprintf(msg_ret, "%s\n%s\n", 314 string_text(CHPASS_UTIL_PASSWORD_NOT_CHANGED), 315 "Password protocol in krb5.conf is not supported\n"); 316 return (-1); 317 } 318 } 319 320 /* 321 * krb5_chpw_result_code_string 322 * 323 * convert the return code received from the password server 324 * to a human-readable string. 325 */ 326 const char * 327 chpw_error_message(kadm5_ret_t result_code) 328 { 329 switch (result_code) { 330 case KRB5_KPASSWD_MALFORMED: 331 return (dgettext(TEXT_DOMAIN, "Malformed request error")); 332 case KRB5_KPASSWD_HARDERROR: 333 return (dgettext(TEXT_DOMAIN, "Server error")); 334 case KRB5_KPASSWD_AUTHERROR: 335 return (dgettext(TEXT_DOMAIN, "Authentication error")); 336 case KRB5_KPASSWD_SOFTERROR: 337 return (dgettext(TEXT_DOMAIN, "Password change rejected")); 338 case KRB5_KPASSWD_ACCESSDENIED: 339 return (dgettext(TEXT_DOMAIN, 340 "Not authorized to change password")); 341 case KRB5_KPASSWD_BAD_VERSION: 342 return (dgettext(TEXT_DOMAIN, "Protocol version unsupported")); 343 case KRB5_KPASSWD_INITIAL_FLAG_NEEDED: 344 return (dgettext(TEXT_DOMAIN, 345 "initial flag required in changepw request")); 346 case KRB5_KPASSWD_POLICY_REJECT: 347 return (dgettext(TEXT_DOMAIN, "new password fails policy")); 348 case KRB5_KPASSWD_BAD_PRINCIPAL: 349 return (dgettext(TEXT_DOMAIN, 350 "target principal does not exist for " 351 "changepw request")); 352 case KRB5_KPASSWD_ETYPE_NOSUPP: 353 return (dgettext(TEXT_DOMAIN, 354 "changepw request key sequence has an " 355 "unsupported Etype")); 356 default: 357 return (dgettext(TEXT_DOMAIN, "Password change failed")); 358 } 359 } 360