1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <sys/types.h> 30 #include <sys/varargs.h> 31 #include <string.h> 32 #include <syslog.h> 33 #include <stdlib.h> 34 35 #include <security/pam_appl.h> 36 #include <security/pam_modules.h> 37 #include <security/pam_impl.h> 38 39 #include <libintl.h> 40 41 #include <passwdutil.h> 42 43 #define SUNW_OLDRPCPASS "SUNW-OLD-RPC-PASSWORD" 44 45 /*PRINTFLIKE3*/ 46 void 47 error(int nowarn, pam_handle_t *pamh, char *fmt, ...) 48 { 49 va_list ap; 50 char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE]; 51 52 va_start(ap, fmt); 53 (void) vsnprintf(messages[0], sizeof (messages[0]), fmt, ap); 54 if (nowarn == 0) 55 (void) __pam_display_msg(pamh, PAM_ERROR_MSG, 1, messages, 56 NULL); 57 va_end(ap); 58 } 59 60 #if defined(ENABLE_AGING) 61 /* 62 * test if authtok is aged. 63 * returns 1 if it is, 0 otherwise 64 */ 65 static int 66 authtok_is_aged(pam_handle_t *pamh) 67 { 68 unix_authtok_data *status; 69 70 if (pam_get_data(pamh, UNIX_AUTHTOK_DATA, 71 (const void **)status) != PAM_SUCCESS) 72 return (0); 73 74 return (status->age_status == PAM_NEW_AUTHTOK_REQD) 75 } 76 #endif 77 78 int 79 pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv) 80 { 81 int i; 82 int debug = 0; 83 int nowarn = 0; 84 attrlist l; 85 pwu_repository_t *pwu_rep; 86 char *user; 87 char *oldpw; 88 char *oldrpcpw = "*NP*"; 89 char *newpw; 90 char *service; 91 struct pam_repository *auth_rep; 92 int res; 93 char msg[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE]; 94 int updated_reps = 0; 95 int server_policy = 0; 96 97 for (i = 0; i < argc; i++) { 98 if (strcmp(argv[i], "debug") == 0) 99 debug = 1; 100 else if (strcmp(argv[i], "nowarn") == 0) 101 nowarn = 1; 102 else if (strcmp(argv[i], "server_policy") == 0) 103 server_policy = 1; 104 } 105 106 if ((flags & PAM_PRELIM_CHECK) != 0) 107 return (PAM_IGNORE); 108 109 if ((flags & PAM_UPDATE_AUTHTOK) == 0) 110 return (PAM_SYSTEM_ERR); 111 112 if ((flags & PAM_SILENT) != 0) 113 nowarn = 1; 114 115 if (debug) 116 syslog(LOG_DEBUG, "pam_authtok_store: storing authtok"); 117 118 #if defined(ENABLE_AGING) 119 if ((flags & PAM_CHANGE_EXPIRED_AUTHTOK) && !authtok_is_aged(pamh)) { 120 syslog(LOG_DEBUG, "pam_authtok_store: System password young"); 121 return (PAM_IGNORE) 122 } 123 #endif 124 125 res = pam_get_item(pamh, PAM_SERVICE, (void **)&service); 126 if (res != PAM_SUCCESS) { 127 syslog(LOG_ERR, "pam_authtok_store: error getting SERVICE"); 128 return (PAM_SYSTEM_ERR); 129 } 130 131 res = pam_get_item(pamh, PAM_USER, (void **)&user); 132 if (res != PAM_SUCCESS) { 133 syslog(LOG_ERR, "pam_authtok_store: error getting USER"); 134 return (PAM_SYSTEM_ERR); 135 } 136 137 if (user == NULL || *user == '\0') { 138 syslog(LOG_ERR, "pam_authtok_store: username is empty"); 139 return (PAM_USER_UNKNOWN); 140 } 141 142 res = pam_get_item(pamh, PAM_OLDAUTHTOK, (void **)&oldpw); 143 if (res != PAM_SUCCESS) { 144 syslog(LOG_ERR, "pam_authtok_store: error getting OLDAUTHTOK"); 145 return (PAM_SYSTEM_ERR); 146 } 147 148 res = pam_get_item(pamh, PAM_AUTHTOK, (void **)&newpw); 149 if (res != PAM_SUCCESS || newpw == NULL) { 150 /* 151 * A module on the stack has removed PAM_AUTHTOK. We fail 152 */ 153 return (PAM_SYSTEM_ERR); 154 } 155 156 l.data.val_s = newpw; 157 /* 158 * If the server_policy option is specified, 159 * use the special attribute, ATTR_PASSWD_SERVER_POLICY, 160 * to tell the update routine for each repository 161 * to perform the necessary special operations. 162 * For now, only the LDAP routine treats this attribute 163 * differently that ATTR_PASSWD. It will skip the 164 * crypting of the password before storing it in the LDAP 165 * server. NIS, NISPLUS, and FILES will handle 166 * ATTR_PASSWD_SERVER_POLICY the same as ATTR_PASSWD. 167 */ 168 if (server_policy) 169 l.type = ATTR_PASSWD_SERVER_POLICY; 170 else 171 l.type = ATTR_PASSWD; 172 l.next = NULL; 173 174 res = pam_get_item(pamh, PAM_REPOSITORY, (void **)&auth_rep); 175 if (res != PAM_SUCCESS) { 176 syslog(LOG_ERR, "pam_authtok_store: error getting repository"); 177 return (PAM_SYSTEM_ERR); 178 } 179 180 if (auth_rep == NULL) { 181 pwu_rep = PWU_DEFAULT_REP; 182 } else { 183 if ((pwu_rep = calloc(1, sizeof (*pwu_rep))) == NULL) 184 return (PAM_BUF_ERR); 185 pwu_rep->type = auth_rep->type; 186 pwu_rep->scope = auth_rep->scope; 187 pwu_rep->scope_len = auth_rep->scope_len; 188 } 189 190 /* 191 * The pam_dhkeys module might have set SUNW_OLDRPCPASS if it 192 * discovered that the user's old password doesn't decrypt the 193 * user's secure RPC credentials. In that case, the 194 * item SUNW_OLDRPCPASS contains the correct password to 195 * decrypt these credentials. 196 */ 197 198 res = pam_get_data(pamh, SUNW_OLDRPCPASS, (const void **)&oldrpcpw); 199 if (res != PAM_SUCCESS && res != PAM_NO_MODULE_DATA) { 200 syslog(LOG_ERR, "pam_authtok_store: error getting OLDRPCPASS"); 201 return (PAM_SYSTEM_ERR); 202 } 203 204 res = __set_authtoken_attr(user, oldpw, oldrpcpw, pwu_rep, &l, 205 &updated_reps); 206 207 if (pwu_rep != PWU_DEFAULT_REP) 208 free(pwu_rep); 209 /* 210 * now map the various passwdutil return states to user messages 211 * and PAM return codes. 212 */ 213 switch (res) { 214 case PWU_SUCCESS: 215 for (i = 1; i <= REP_LAST; i <<= 1) { 216 if ((updated_reps & i) == 0) 217 continue; 218 error(nowarn, pamh, dgettext(TEXT_DOMAIN, 219 "%s: password successfully changed for %s"), 220 service, user); 221 } 222 223 /* 224 * If we have updated NIS+, and we got SUCCESS (not one of 225 * the partial failures), this indicates that the credential 226 * update went well too... Inform the user 227 */ 228 if (updated_reps & REP_NISPLUS) 229 error(nowarn, pamh, dgettext(TEXT_DOMAIN, 230 "%s: credential information changed for %s"), 231 service, user); 232 res = PAM_SUCCESS; 233 break; 234 case PWU_BUSY: 235 error(nowarn, pamh, dgettext(TEXT_DOMAIN, 236 "%s: Password database busy. Try again later."), 237 service); 238 res = PAM_AUTHTOK_LOCK_BUSY; 239 break; 240 case PWU_STAT_FAILED: 241 syslog(LOG_ERR, "%s: stat of password file failed", service); 242 res = PAM_AUTHTOK_ERR; 243 break; 244 case PWU_OPEN_FAILED: 245 case PWU_WRITE_FAILED: 246 case PWU_CLOSE_FAILED: 247 case PWU_UPDATE_FAILED: 248 error(nowarn, pamh, dgettext(TEXT_DOMAIN, 249 "%s: Unexpected failure. Password database unchanged."), 250 service); 251 res = PAM_SYSTEM_ERR; 252 break; 253 case PWU_NOT_FOUND: 254 /* Different error if repository was explicitly specified */ 255 if (auth_rep != NULL) { 256 error(nowarn, pamh, dgettext(TEXT_DOMAIN, 257 "%s: System error: no %s password for %s."), 258 service, auth_rep->type, user); 259 } else { 260 error(nowarn, pamh, dgettext(TEXT_DOMAIN, 261 "%s: %s does not exist."), service, user); 262 } 263 res = PAM_USER_UNKNOWN; 264 break; 265 case PWU_NOMEM: 266 error(nowarn, pamh, dgettext(TEXT_DOMAIN, 267 "%s: Internal memory allocation failure."), service); 268 res = PAM_BUF_ERR; 269 break; 270 case PWU_SERVER_ERROR: 271 res = PAM_SYSTEM_ERR; 272 break; 273 case PWU_SYSTEM_ERROR: 274 res = PAM_SYSTEM_ERR; 275 break; 276 case PWU_DENIED: 277 res = PAM_PERM_DENIED; 278 break; 279 case PWU_NO_CHANGE: 280 /* 281 * yppasswdd detected that we're not changing anything. 282 */ 283 error(nowarn, pamh, dgettext(TEXT_DOMAIN, 284 "%s: Password information unchanged."), service); 285 res = PAM_SUCCESS; 286 break; 287 case PWU_REPOSITORY_ERROR: 288 syslog(LOG_NOTICE, "pam_authtok_store: detected " 289 "unsupported configuration in /etc/nsswitch.conf."); 290 error(nowarn, pamh, dgettext(TEXT_DOMAIN, 291 "%s: System error: repository out of range."), service); 292 res = PAM_SYSTEM_ERR; 293 break; 294 case PWU_RECOVERY_ERR: 295 res = PAM_AUTHTOK_RECOVERY_ERR; 296 break; 297 case PWU_NO_PRIV_CRED_UPDATE: 298 /* 299 * A privileged process has updated a user's password. 300 * In this case, the password will be updated, but the 301 * credentials won't. This is not a failure, but we need 302 * to inform the user about it, and return PAM_SUCCESS 303 */ 304 305 /* First inform the user about the passsword update */ 306 error(nowarn, pamh, dgettext(TEXT_DOMAIN, 307 "%s: password successfully changed for %s"), 308 service, user); 309 310 /* and now the bad news */ 311 (void) sprintf(msg[0], " "); 312 (void) snprintf(msg[1], sizeof (msg[1]), 313 dgettext(TEXT_DOMAIN, 314 "The Secure RPC credential information for %s " 315 "will not be changed."), user); 316 (void) snprintf(msg[2], sizeof (msg[2]), 317 dgettext(TEXT_DOMAIN, "User %s must do the following to " 318 "update his/her"), user); 319 (void) snprintf(msg[3], sizeof (msg[3]), 320 dgettext(TEXT_DOMAIN, "credential information:")); 321 (void) snprintf(msg[4], sizeof (msg[4]), 322 dgettext(TEXT_DOMAIN, "Use NEW passwd for login and OLD " 323 "passwd for keylogin.")); 324 (void) snprintf(msg[5], sizeof (msg[5]), 325 dgettext(TEXT_DOMAIN, "Use \"chkey -p\" to reencrypt the " 326 "credentials with the")); 327 (void) snprintf(msg[6], sizeof (msg[6]), 328 dgettext(TEXT_DOMAIN, "new login passwd.")); 329 (void) snprintf(msg[7], sizeof (msg[7]), 330 dgettext(TEXT_DOMAIN, "The user must keylogin explicitly " 331 "after their next login.")); 332 (void) sprintf(msg[8], " "); 333 (void) __pam_display_msg(pamh, PAM_ERROR_MSG, 9, msg, NULL); 334 res = PAM_SUCCESS; 335 break; 336 case PWU_UPDATED_SOME_CREDS: 337 error(nowarn, pamh, dgettext(TEXT_DOMAIN, 338 "%s: password successfully changed for %s"), 339 service, user); 340 341 error(nowarn, pamh, dgettext(TEXT_DOMAIN, 342 "WARNING: some but not all credentials were reencrypted " 343 "for user %s"), user); 344 res = PAM_SUCCESS; 345 break; 346 case PWU_PWD_TOO_SHORT: 347 (void) snprintf(msg[0], sizeof (msg[0]), 348 dgettext(TEXT_DOMAIN, "%s: Password too short."), service); 349 (void) __pam_display_msg(pamh, PAM_ERROR_MSG, 1, msg, NULL); 350 res = PAM_AUTHTOK_ERR; 351 break; 352 case PWU_PWD_INVALID: 353 (void) snprintf(msg[0], sizeof (msg[0]), 354 dgettext(TEXT_DOMAIN, "%s: Invalid password syntax."), 355 service); 356 (void) __pam_display_msg(pamh, PAM_ERROR_MSG, 1, msg, NULL); 357 res = PAM_AUTHTOK_ERR; 358 break; 359 case PWU_PWD_IN_HISTORY: 360 (void) snprintf(msg[0], sizeof (msg[0]), 361 dgettext(TEXT_DOMAIN, "%s: Reuse of old passwords not " 362 "allowed, the new password is in the history list."), 363 service); 364 (void) __pam_display_msg(pamh, PAM_ERROR_MSG, 1, msg, NULL); 365 res = PAM_AUTHTOK_ERR; 366 break; 367 case PWU_CHANGE_NOT_ALLOWED: 368 (void) snprintf(msg[0], sizeof (msg[0]), 369 dgettext(TEXT_DOMAIN, "%s: You may not change " 370 "this password."), service); 371 (void) __pam_display_msg(pamh, PAM_ERROR_MSG, 1, msg, NULL); 372 res = PAM_PERM_DENIED; 373 break; 374 case PWU_WITHIN_MIN_AGE: 375 (void) snprintf(msg[0], sizeof (msg[0]), 376 dgettext(TEXT_DOMAIN, 377 "%s: Password can not be changed yet, " 378 "not enough time has passed."), service); 379 (void) __pam_display_msg(pamh, PAM_ERROR_MSG, 1, msg, NULL); 380 res = PAM_PERM_DENIED; 381 break; 382 default: 383 res = PAM_SYSTEM_ERR; 384 break; 385 } 386 387 return (res); 388 } 389