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 (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 27 #include <sys/types.h> 28 #include <sys/varargs.h> 29 #include <string.h> 30 #include <syslog.h> 31 #include <stdlib.h> 32 33 #include <security/pam_appl.h> 34 #include <security/pam_modules.h> 35 #include <security/pam_impl.h> 36 37 #include <libintl.h> 38 39 #include <passwdutil.h> 40 #include <shadow.h> 41 42 /*PRINTFLIKE3*/ 43 static void 44 error(int nowarn, pam_handle_t *pamh, char *fmt, ...) 45 { 46 va_list ap; 47 char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE]; 48 49 va_start(ap, fmt); 50 (void) vsnprintf(messages[0], sizeof (messages[0]), fmt, ap); 51 if (nowarn == 0) 52 (void) __pam_display_msg(pamh, PAM_ERROR_MSG, 1, messages, 53 NULL); 54 va_end(ap); 55 } 56 57 /*PRINTFLIKE3*/ 58 static void 59 info(int nowarn, pam_handle_t *pamh, char *fmt, ...) 60 { 61 va_list ap; 62 char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE]; 63 64 va_start(ap, fmt); 65 (void) vsnprintf(messages[0], sizeof (messages[0]), fmt, ap); 66 if (nowarn == 0) 67 (void) __pam_display_msg(pamh, PAM_TEXT_INFO, 1, messages, 68 NULL); 69 va_end(ap); 70 } 71 72 #if defined(ENABLE_AGING) 73 /* 74 * test if authtok is aged. 75 * returns 1 if it is, 0 otherwise 76 */ 77 static int 78 authtok_is_aged(pam_handle_t *pamh) 79 { 80 unix_authtok_data *status; 81 82 if (pam_get_data(pamh, UNIX_AUTHTOK_DATA, 83 (const void **)status) != PAM_SUCCESS) 84 return (0); 85 86 return (status->age_status == PAM_NEW_AUTHTOK_REQD) 87 } 88 #endif 89 90 int 91 pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv) 92 { 93 int i; 94 int debug = 0; 95 int nowarn = 0; 96 attrlist l; 97 pwu_repository_t *pwu_rep; 98 char *user; 99 char *oldpw; 100 char *newpw; 101 char *service; 102 struct pam_repository *auth_rep; 103 int res; 104 char msg[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE]; 105 int updated_reps = 0; 106 int server_policy = 0; 107 108 for (i = 0; i < argc; i++) { 109 if (strcmp(argv[i], "debug") == 0) 110 debug = 1; 111 else if (strcmp(argv[i], "nowarn") == 0) 112 nowarn = 1; 113 else if (strcmp(argv[i], "server_policy") == 0) 114 server_policy = 1; 115 } 116 117 if ((flags & PAM_PRELIM_CHECK) != 0) 118 return (PAM_IGNORE); 119 120 if ((flags & PAM_UPDATE_AUTHTOK) == 0) 121 return (PAM_SYSTEM_ERR); 122 123 if ((flags & PAM_SILENT) != 0) 124 nowarn = 1; 125 126 if (debug) 127 syslog(LOG_DEBUG, "pam_authtok_store: storing authtok"); 128 129 #if defined(ENABLE_AGING) 130 if ((flags & PAM_CHANGE_EXPIRED_AUTHTOK) && !authtok_is_aged(pamh)) { 131 syslog(LOG_DEBUG, "pam_authtok_store: System password young"); 132 return (PAM_IGNORE); 133 } 134 #endif 135 136 res = pam_get_item(pamh, PAM_SERVICE, (void **)&service); 137 if (res != PAM_SUCCESS) { 138 syslog(LOG_ERR, "pam_authtok_store: error getting SERVICE"); 139 return (PAM_SYSTEM_ERR); 140 } 141 142 res = pam_get_item(pamh, PAM_USER, (void **)&user); 143 if (res != PAM_SUCCESS) { 144 syslog(LOG_ERR, "pam_authtok_store: error getting USER"); 145 return (PAM_SYSTEM_ERR); 146 } 147 148 if (user == NULL || *user == '\0') { 149 syslog(LOG_ERR, "pam_authtok_store: username is empty"); 150 return (PAM_USER_UNKNOWN); 151 } 152 153 res = pam_get_item(pamh, PAM_OLDAUTHTOK, (void **)&oldpw); 154 if (res != PAM_SUCCESS) { 155 syslog(LOG_ERR, "pam_authtok_store: error getting OLDAUTHTOK"); 156 return (PAM_SYSTEM_ERR); 157 } 158 159 res = pam_get_item(pamh, PAM_AUTHTOK, (void **)&newpw); 160 if (res != PAM_SUCCESS || newpw == NULL) { 161 /* 162 * A module on the stack has removed PAM_AUTHTOK. We fail 163 */ 164 return (PAM_SYSTEM_ERR); 165 } 166 167 l.data.val_s = newpw; 168 /* 169 * If the server_policy option is specified, 170 * use the special attribute, ATTR_PASSWD_SERVER_POLICY, 171 * to tell the update routine for each repository 172 * to perform the necessary special operations. 173 * For now, only the LDAP routine treats this attribute 174 * differently that ATTR_PASSWD. It will skip the 175 * crypting of the password before storing it in the LDAP 176 * server. NIS, and FILES will handle ATTR_PASSWD_SERVER_POLICY 177 * the same as ATTR_PASSWD. 178 */ 179 if (server_policy) 180 l.type = ATTR_PASSWD_SERVER_POLICY; 181 else 182 l.type = ATTR_PASSWD; 183 l.next = NULL; 184 185 res = pam_get_item(pamh, PAM_REPOSITORY, (void **)&auth_rep); 186 if (res != PAM_SUCCESS) { 187 syslog(LOG_ERR, "pam_authtok_store: error getting repository"); 188 return (PAM_SYSTEM_ERR); 189 } 190 191 if (auth_rep == NULL) { 192 pwu_rep = PWU_DEFAULT_REP; 193 } else { 194 if ((pwu_rep = calloc(1, sizeof (*pwu_rep))) == NULL) 195 return (PAM_BUF_ERR); 196 pwu_rep->type = auth_rep->type; 197 pwu_rep->scope = auth_rep->scope; 198 pwu_rep->scope_len = auth_rep->scope_len; 199 } 200 201 res = __set_authtoken_attr(user, oldpw, pwu_rep, &l, &updated_reps); 202 203 if (pwu_rep != PWU_DEFAULT_REP) 204 free(pwu_rep); 205 /* 206 * now map the various passwdutil return states to user messages 207 * and PAM return codes. 208 */ 209 switch (res) { 210 case PWU_SUCCESS: 211 for (i = 1; i <= REP_LAST; i <<= 1) { 212 if ((updated_reps & i) == 0) 213 continue; 214 info(nowarn, pamh, dgettext(TEXT_DOMAIN, 215 "%s: password successfully changed for %s"), 216 service, user); 217 } 218 res = PAM_SUCCESS; 219 break; 220 case PWU_BUSY: 221 error(nowarn, pamh, dgettext(TEXT_DOMAIN, 222 "%s: Password database busy. Try again later."), 223 service); 224 res = PAM_AUTHTOK_LOCK_BUSY; 225 break; 226 case PWU_STAT_FAILED: 227 syslog(LOG_ERR, "%s: stat of password file failed", service); 228 res = PAM_AUTHTOK_ERR; 229 break; 230 case PWU_OPEN_FAILED: 231 case PWU_WRITE_FAILED: 232 case PWU_CLOSE_FAILED: 233 case PWU_UPDATE_FAILED: 234 error(nowarn, pamh, dgettext(TEXT_DOMAIN, 235 "%s: Unexpected failure. Password database unchanged."), 236 service); 237 res = PAM_SYSTEM_ERR; 238 break; 239 case PWU_NOT_FOUND: 240 /* Different error if repository was explicitly specified */ 241 if (auth_rep != NULL) { 242 error(nowarn, pamh, dgettext(TEXT_DOMAIN, 243 "%s: System error: no %s password for %s."), 244 service, auth_rep->type, user); 245 } else { 246 error(nowarn, pamh, dgettext(TEXT_DOMAIN, 247 "%s: %s does not exist."), service, user); 248 } 249 res = PAM_USER_UNKNOWN; 250 break; 251 case PWU_NOMEM: 252 error(nowarn, pamh, dgettext(TEXT_DOMAIN, 253 "%s: Internal memory allocation failure."), service); 254 res = PAM_BUF_ERR; 255 break; 256 case PWU_SERVER_ERROR: 257 res = PAM_SYSTEM_ERR; 258 break; 259 case PWU_SYSTEM_ERROR: 260 res = PAM_SYSTEM_ERR; 261 break; 262 case PWU_DENIED: 263 res = PAM_PERM_DENIED; 264 break; 265 case PWU_NO_CHANGE: 266 /* 267 * yppasswdd detected that we're not changing anything. 268 */ 269 info(nowarn, pamh, dgettext(TEXT_DOMAIN, 270 "%s: Password information unchanged."), service); 271 res = PAM_SUCCESS; 272 break; 273 case PWU_REPOSITORY_ERROR: 274 syslog(LOG_NOTICE, "pam_authtok_store: detected " 275 "unsupported configuration in /etc/nsswitch.conf."); 276 error(nowarn, pamh, dgettext(TEXT_DOMAIN, 277 "%s: System error: repository out of range."), service); 278 res = PAM_SYSTEM_ERR; 279 break; 280 case PWU_PWD_TOO_SHORT: 281 (void) snprintf(msg[0], sizeof (msg[0]), 282 dgettext(TEXT_DOMAIN, "%s: Password too short."), service); 283 (void) __pam_display_msg(pamh, PAM_ERROR_MSG, 1, msg, NULL); 284 res = PAM_AUTHTOK_ERR; 285 break; 286 case PWU_PWD_INVALID: 287 (void) snprintf(msg[0], sizeof (msg[0]), 288 dgettext(TEXT_DOMAIN, "%s: Invalid password syntax."), 289 service); 290 (void) __pam_display_msg(pamh, PAM_ERROR_MSG, 1, msg, NULL); 291 res = PAM_AUTHTOK_ERR; 292 break; 293 case PWU_PWD_IN_HISTORY: 294 (void) snprintf(msg[0], sizeof (msg[0]), 295 dgettext(TEXT_DOMAIN, "%s: Reuse of old passwords not " 296 "allowed, the new password is in the history list."), 297 service); 298 (void) __pam_display_msg(pamh, PAM_ERROR_MSG, 1, msg, NULL); 299 res = PAM_AUTHTOK_ERR; 300 break; 301 case PWU_CHANGE_NOT_ALLOWED: 302 (void) snprintf(msg[0], sizeof (msg[0]), 303 dgettext(TEXT_DOMAIN, "%s: You may not change " 304 "this password."), service); 305 (void) __pam_display_msg(pamh, PAM_ERROR_MSG, 1, msg, NULL); 306 res = PAM_PERM_DENIED; 307 break; 308 case PWU_WITHIN_MIN_AGE: 309 (void) snprintf(msg[0], sizeof (msg[0]), 310 dgettext(TEXT_DOMAIN, 311 "%s: Password can not be changed yet, " 312 "not enough time has passed."), service); 313 (void) __pam_display_msg(pamh, PAM_ERROR_MSG, 1, msg, NULL); 314 res = PAM_PERM_DENIED; 315 break; 316 default: 317 res = PAM_SYSTEM_ERR; 318 break; 319 } 320 321 return (res); 322 } 323