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