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 2005 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 <stdlib.h> 33 #include <syslog.h> 34 #include <unistd.h> 35 #include <crypt.h> 36 #include <pwd.h> 37 #include <libintl.h> 38 39 #include <security/pam_appl.h> 40 #include <security/pam_modules.h> 41 #include <security/pam_impl.h> 42 43 #include <passwdutil.h> 44 45 #include <sys/note.h> 46 47 /*PRINTFLIKE3*/ 48 void 49 error(int nowarn, pam_handle_t *pamh, char *fmt, ...) 50 { 51 va_list ap; 52 char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE]; 53 54 va_start(ap, fmt); 55 (void) vsnprintf(messages[0], sizeof (messages[0]), fmt, ap); 56 if (nowarn == 0) 57 (void) __pam_display_msg(pamh, PAM_ERROR_MSG, 1, messages, 58 NULL); 59 va_end(ap); 60 } 61 62 /* 63 * int pam_sm_authenticate(pamh, flags, argc, argv) 64 * 65 * Read authentication token from user. 66 */ 67 int 68 pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) 69 { 70 71 char *user; 72 char *password; 73 char *service; 74 int i; 75 int debug = 0; 76 int nowarn = 0; 77 int res; 78 char prompt[PAM_MAX_MSG_SIZE]; 79 char *auth_user = NULL; 80 int retval; 81 int privileged; 82 char *rep_passwd = NULL; 83 char *repository_name = NULL; 84 attrlist al[8]; 85 int min; 86 int max; 87 int lstchg; 88 int server_policy = 0; 89 90 pam_repository_t *auth_rep = NULL; 91 pwu_repository_t *pwu_rep = NULL; 92 93 for (i = 0; i < argc; i++) { 94 if (strcmp(argv[i], "debug") == 0) 95 debug = 1; 96 if (strcmp(argv[i], "nowarn") == 0) 97 nowarn = 1; 98 if (strcmp(argv[i], "server_policy") == 0) 99 server_policy = 1; 100 } 101 102 if (flags & PAM_SILENT) 103 nowarn = 1; 104 105 if ((res = pam_get_user(pamh, &user, NULL)) != PAM_SUCCESS) { 106 if (debug) 107 syslog(LOG_DEBUG, "pam_passwd_auth: " 108 "get user failed: %s", pam_strerror(pamh, res)); 109 return (res); 110 } 111 112 if (user == NULL || *user == '\0') { 113 syslog(LOG_ERR, "pam_passwd_auth: pam_sm_authenticate: " 114 "PAM_USER NULL or empty"); 115 return (PAM_SYSTEM_ERR); 116 } 117 118 res = pam_get_item(pamh, PAM_AUTHTOK, (void **)&password); 119 if (res != PAM_SUCCESS) 120 return (res); 121 122 if (password != NULL) 123 return (PAM_SUCCESS); 124 125 res = pam_get_item(pamh, PAM_SERVICE, (void **)&service); 126 if (res != PAM_SUCCESS) 127 return (res); 128 129 res = pam_get_item(pamh, PAM_REPOSITORY, (void **)&auth_rep); 130 if (res != PAM_SUCCESS) { 131 syslog(LOG_ERR, "pam_passwd_auth: pam_sm_authenticate: " 132 "error getting repository"); 133 return (PAM_SYSTEM_ERR); 134 } 135 136 if (auth_rep == NULL) { 137 pwu_rep = PWU_DEFAULT_REP; 138 } else { 139 if ((pwu_rep = calloc(1, sizeof (*pwu_rep))) == NULL) 140 return (PAM_BUF_ERR); 141 pwu_rep->type = auth_rep->type; 142 pwu_rep->scope = auth_rep->scope; 143 pwu_rep->scope_len = auth_rep->scope_len; 144 } 145 146 res = __user_to_authenticate(user, pwu_rep, &auth_user, &privileged); 147 if (res != PWU_SUCCESS) { 148 if (res == PWU_NOT_FOUND) 149 retval = PAM_USER_UNKNOWN; 150 else if (res == PWU_DENIED) 151 retval = PAM_PERM_DENIED; 152 else if (res == PWU_REPOSITORY_ERROR) { 153 syslog(LOG_NOTICE, 154 "pam_passwd_auth: detected unsupported " 155 "configuration in /etc/nsswitch.conf."); 156 error(nowarn, pamh, dgettext(TEXT_DOMAIN, "%s: " 157 "Unsupported nsswitch entry for \"passwd:\"." 158 " Use \"-r repository \"."), service); 159 retval = PAM_SYSTEM_ERR; 160 } else 161 retval = PAM_SYSTEM_ERR; 162 if (debug) 163 syslog(LOG_DEBUG, "passwd_auth: __user_to_authenticate " 164 "returned %d", retval); 165 goto out; 166 } 167 168 if (auth_user == NULL) { /* No authentication needed */ 169 if (debug) 170 syslog(LOG_DEBUG, 171 "passwd_auth: no authentication needed."); 172 retval = PAM_SUCCESS; 173 goto out; 174 } 175 176 /* 177 * The password prompt differs between users updating their 178 * own password, and users updating other an user's password 179 */ 180 if (privileged) { 181 /* 182 * TRANSLATION_NOTE 183 * The following string has a single space at the end 184 */ 185 (void) snprintf(prompt, sizeof (prompt), 186 dgettext(TEXT_DOMAIN, "Enter %s's password: "), 187 auth_user); 188 } else { 189 /* 190 * TRANSLATION_NOTE 191 * The following string has a single space at the end 192 */ 193 (void) snprintf(prompt, sizeof (prompt), 194 dgettext(TEXT_DOMAIN, "Enter existing login password: ")); 195 } 196 197 retval = __pam_get_authtok(pamh, PAM_PROMPT, PAM_AUTHTOK, prompt, 198 &password); 199 if (retval != PAM_SUCCESS) 200 goto out; 201 202 if (password == NULL) { 203 syslog(LOG_ERR, "pam_passwd_auth: pam_sm_authenticate: " 204 "got NULL password from get_authtok()"); 205 retval = PAM_AUTH_ERR; 206 goto out; 207 } 208 209 /* Privileged users can skip the tests that follow */ 210 if (privileged) 211 goto setitem; 212 213 /* 214 * Non privileged user: so we need to check the old password 215 * and possible restrictions on password changes. 216 */ 217 218 /* Get password and it's age from the repository specified */ 219 al[0].type = ATTR_PASSWD; al[0].next = &al[1]; 220 al[1].type = ATTR_MIN; al[1].next = &al[2]; 221 al[2].type = ATTR_MAX; al[2].next = &al[3]; 222 al[3].type = ATTR_LSTCHG; al[3].next = &al[4]; 223 al[4].type = ATTR_WARN; al[4].next = &al[5]; 224 al[5].type = ATTR_INACT; al[5].next = &al[6]; 225 al[6].type = ATTR_EXPIRE; al[6].next = &al[7]; 226 al[7].type = ATTR_REP_NAME; al[7].next = NULL; 227 228 res = __get_authtoken_attr(auth_user, pwu_rep, al); 229 230 if (res != PWU_SUCCESS) { 231 retval = PAM_SYSTEM_ERR; 232 goto out; 233 } 234 235 repository_name = al[7].data.val_s; 236 237 /* 238 * if repository isn't files|nis|nisplus, and 239 * user wants to follow server policy, 240 * return PAM_IGNORE 241 */ 242 if (server_policy && 243 strcmp(repository_name, "files") != 0 && 244 strcmp(repository_name, "nis") != 0 && 245 strcmp(repository_name, "nisplus") != 0) { 246 retval = PAM_IGNORE; 247 goto out; 248 } 249 250 rep_passwd = al[0].data.val_s; 251 252 /* 253 * Chop off old SunOS-style password aging information. 254 * 255 * Note: old style password aging is only defined for UNIX-style 256 * crypt strings, hence the comma will always be at position 14. 257 * Note: This code is here because some other vendors might still 258 * support this style of password aging. If we don't remove 259 * the age field, users won't be able to change their password. 260 * XXX yank this code when we're certain this "compatibility" 261 * isn't needed anymore. 262 */ 263 if (rep_passwd != NULL && rep_passwd[0] != '$' && 264 strlen(rep_passwd) > 13 && rep_passwd[13] == ',') 265 rep_passwd[13] = '\0'; 266 267 if (strcmp(crypt(password, rep_passwd), rep_passwd) != 0) { 268 retval = PAM_AUTH_ERR; 269 goto out; 270 } 271 272 /* 273 * Now check to see if the user is allowed to change 274 * the password. 275 */ 276 min = al[1].data.val_i; 277 max = al[2].data.val_i; 278 lstchg = al[3].data.val_i; 279 280 if (max != -1 && lstchg != 0) { 281 /* aging is turned on, and a change is not forced */ 282 time_t daynow = DAY_NOW_32; 283 if ((time_t)lstchg <= daynow) { 284 /* Aged enough? */ 285 if (daynow < (time_t)(lstchg + min)) { 286 error(nowarn, pamh, dgettext(TEXT_DOMAIN, 287 "%s: Sorry: less than %d days " 288 "since the last change."), 289 service, min); 290 retval = PAM_PERM_DENIED; 291 goto out; 292 } 293 294 /* 295 * users with min>max are not allowed to 296 * change their password. 297 */ 298 if (min > max) { 299 error(nowarn, pamh, dgettext(TEXT_DOMAIN, 300 "%s: You may not change " 301 "this password."), service); 302 retval = PAM_PERM_DENIED; 303 goto out; 304 } 305 } 306 } 307 308 setitem: 309 310 retval = pam_set_item(pamh, PAM_AUTHTOK, (void *)password); 311 312 out: 313 if (password) { 314 (void) memset(password, 0, strlen(password)); 315 free(password); 316 } 317 if (rep_passwd) { 318 (void) memset(rep_passwd, 0, strlen(rep_passwd)); 319 free(rep_passwd); 320 } 321 if (pwu_rep) 322 free(pwu_rep); 323 if (auth_user) 324 free(auth_user); 325 if (repository_name) 326 free(repository_name); 327 328 return (retval); 329 } 330 331 /*ARGSUSED*/ 332 int 333 pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) 334 { 335 return (PAM_IGNORE); 336 } 337