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/wait.h> 29 #include <sys/stat.h> 30 #include <fcntl.h> 31 #include <stdlib.h> 32 #include <security/pam_appl.h> 33 #include <security/pam_modules.h> 34 #include <security/pam_impl.h> 35 #include <syslog.h> 36 #include <pwd.h> 37 #include <shadow.h> 38 #include <lastlog.h> 39 #include <ctype.h> 40 #include <unistd.h> 41 #include <stdlib.h> 42 #include <stdio.h> 43 #include <libintl.h> 44 #include <signal.h> 45 #include <thread.h> 46 #include <synch.h> 47 #include <errno.h> 48 #include <time.h> 49 #include <string.h> 50 #include <crypt.h> 51 #include <assert.h> 52 #include <deflt.h> 53 #include <libintl.h> 54 #include <passwdutil.h> 55 56 #define LASTLOG "/var/adm/lastlog" 57 #define LOGINADMIN "/etc/default/login" 58 #define UNIX_AUTH_DATA "SUNW-UNIX-AUTH-DATA" 59 #define UNIX_AUTHTOK_DATA "SUNW-UNIX-AUTHTOK-DATA" 60 61 /* 62 * Function Declarations 63 */ 64 extern void setusershell(); 65 extern int _nfssys(int, void *); 66 67 typedef struct _unix_authtok_data_ { 68 int age_status; 69 }unix_authtok_data; 70 71 /*ARGSUSED*/ 72 static void 73 unix_cleanup( 74 pam_handle_t *pamh, 75 void *data, 76 int pam_status) 77 { 78 free((unix_authtok_data *)data); 79 } 80 81 /* 82 * check_for_login_inactivity - Check for login inactivity 83 * 84 */ 85 86 static int 87 check_for_login_inactivity( 88 uid_t pw_uid, 89 struct spwd *shpwd) 90 { 91 int fdl; 92 struct lastlog ll; 93 int retval; 94 offset_t offset; 95 96 offset = (offset_t)pw_uid * (offset_t)sizeof (struct lastlog); 97 98 if ((fdl = open(LASTLOG, O_RDWR|O_CREAT, 0444)) >= 0) { 99 /* 100 * Read the last login (ll) time 101 */ 102 if (llseek(fdl, offset, SEEK_SET) != offset) { 103 __pam_log(LOG_AUTH | LOG_ERR, 104 "pam_unix_acct: pam_sm_acct_mgmt: " 105 "can't obtain last login info on uid %d " 106 "(uid too large)", pw_uid); 107 return (0); 108 } 109 110 retval = read(fdl, (char *)&ll, sizeof (ll)); 111 112 /* Check for login inactivity */ 113 114 if ((shpwd->sp_inact > 0) && (retval == sizeof (ll)) && 115 ll.ll_time) { 116 /* 117 * account inactive too long. 118 * and no update password set 119 * and no last pwd change date in shadow file 120 * and last pwd change more than inactive time 121 * then account inactive too long and no access. 122 */ 123 if (((time_t)((ll.ll_time / DAY) + shpwd->sp_inact) 124 < DAY_NOW) && 125 (shpwd->sp_lstchg != 0) && 126 (shpwd->sp_lstchg != -1) && 127 ((shpwd->sp_lstchg + shpwd->sp_inact) < DAY_NOW)) { 128 /* 129 * Account inactive for too long 130 */ 131 (void) close(fdl); 132 return (1); 133 } 134 } 135 136 (void) close(fdl); 137 } 138 return (0); 139 } 140 141 /* 142 * new_password_check() 143 * 144 * check to see if the user needs to change their password 145 */ 146 147 static int 148 new_password_check(shpwd, flags) 149 struct spwd *shpwd; 150 int flags; 151 { 152 time_t now = DAY_NOW; 153 154 /* 155 * We want to make sure that we change the password only if 156 * passwords are required for the system, the user does not 157 * have a password, AND the user's NULL password can be changed 158 * according to its password aging information 159 */ 160 161 if ((flags & PAM_DISALLOW_NULL_AUTHTOK) != 0) { 162 if (shpwd->sp_pwdp[0] == '\0') { 163 if (((shpwd->sp_max == -1) || 164 ((time_t)shpwd->sp_lstchg > now) || 165 ((now >= (time_t)(shpwd->sp_lstchg + 166 shpwd->sp_min)) && 167 (shpwd->sp_max >= shpwd->sp_min)))) { 168 return (PAM_NEW_AUTHTOK_REQD); 169 } 170 } 171 } 172 return (PAM_SUCCESS); 173 } 174 175 /* 176 * perform_passwd_aging_check 177 * - Check for password exipration. 178 */ 179 static int 180 perform_passwd_aging_check( 181 pam_handle_t *pamh, 182 struct spwd *shpwd, 183 int flags) 184 { 185 time_t now = DAY_NOW; 186 int idledays = -1; 187 char *ptr; 188 char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE]; 189 void *defp; 190 191 192 if ((defp = defopen_r(LOGINADMIN)) != NULL) { 193 if ((ptr = defread_r("IDLEWEEKS=", defp)) != NULL) 194 idledays = 7 * atoi(ptr); 195 defclose_r(defp); 196 } 197 198 /* 199 * if (sp_lstchg == 0), the administrator has forced the 200 * user to change his/her passwd 201 */ 202 if (shpwd->sp_lstchg == 0) 203 return (PAM_NEW_AUTHTOK_REQD); 204 205 /* If password aging is disabled (or min>max), all is well */ 206 if (shpwd->sp_max < 0 || shpwd->sp_max < shpwd->sp_min) 207 return (PAM_SUCCESS); 208 209 /* Password aging is enabled. See if the password has aged */ 210 if (now < (time_t)(shpwd->sp_lstchg + shpwd->sp_max)) 211 return (PAM_SUCCESS); 212 213 /* Password has aged. Has it aged more than idledays ? */ 214 if (idledays < 0) /* IDLEWEEKS not configured */ 215 return (PAM_NEW_AUTHTOK_REQD); 216 217 /* idledays is configured */ 218 if (idledays > 0 && (now < (time_t)(shpwd->sp_lstchg + idledays))) 219 return (PAM_NEW_AUTHTOK_REQD); 220 221 /* password has aged more that allowed for by IDLEWEEKS */ 222 if (!(flags & PAM_SILENT)) { 223 (void) strlcpy(messages[0], dgettext(TEXT_DOMAIN, 224 "Your password has been expired for too long."), 225 sizeof (messages[0])); 226 (void) strlcpy(messages[1], dgettext(TEXT_DOMAIN, 227 "Please contact the system administrator."), 228 sizeof (messages[0])); 229 (void) __pam_display_msg(pamh, PAM_ERROR_MSG, 2, messages, 230 NULL); 231 } 232 return (PAM_AUTHTOK_EXPIRED); 233 } 234 235 /* 236 * warn_user_passwd_will_expire - warn the user when the password will 237 * expire. 238 */ 239 240 static void 241 warn_user_passwd_will_expire( 242 pam_handle_t *pamh, 243 struct spwd shpwd) 244 { 245 time_t now = DAY_NOW; 246 char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE]; 247 time_t days; 248 249 250 if ((shpwd.sp_warn > 0) && (shpwd.sp_max > 0) && 251 (now + shpwd.sp_warn) >= (time_t)(shpwd.sp_lstchg + shpwd.sp_max)) { 252 days = (time_t)(shpwd.sp_lstchg + shpwd.sp_max) - now; 253 if (days <= 0) 254 (void) snprintf(messages[0], 255 sizeof (messages[0]), 256 dgettext(TEXT_DOMAIN, 257 "Your password will expire within 24 hours.")); 258 else if (days == 1) 259 (void) snprintf(messages[0], 260 sizeof (messages[0]), 261 dgettext(TEXT_DOMAIN, 262 "Your password will expire in 1 day.")); 263 else 264 (void) snprintf(messages[0], 265 sizeof (messages[0]), 266 dgettext(TEXT_DOMAIN, 267 "Your password will expire in %d days."), 268 (int)days); 269 270 (void) __pam_display_msg(pamh, PAM_TEXT_INFO, 1, messages, 271 NULL); 272 } 273 } 274 275 /* 276 * pam_sm_acct_mgmt - main account managment routine. 277 * Returns: module error or specific error on failure 278 */ 279 280 int 281 pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv) 282 { 283 uid_t pw_uid; 284 char *repository_name = NULL; 285 char *user; 286 attrlist attr_pw[3]; 287 attrlist attr_spw[7]; 288 pwu_repository_t *pwu_rep = PWU_DEFAULT_REP; 289 pwu_repository_t *auth_rep = NULL; 290 int error = PAM_ACCT_EXPIRED; 291 int result; 292 int i; 293 int debug = 0; 294 int server_policy = 0; 295 unix_authtok_data *status; 296 struct spwd shpwd = {NULL, NULL, 297 -1, -1, -1, -1, -1, -1, 0}; 298 299 for (i = 0; i < argc; i++) { 300 if (strcasecmp(argv[i], "debug") == 0) 301 debug = 1; 302 else if (strcasecmp(argv[i], "server_policy") == 0) 303 server_policy = 1; 304 else if (strcasecmp(argv[i], "nowarn") == 0) { 305 flags = flags | PAM_SILENT; 306 } else { 307 __pam_log(LOG_AUTH | LOG_ERR, 308 "ACCOUNT:pam_sm_acct_mgmt: illegal option %s", 309 argv[i]); 310 } 311 } 312 313 if (debug) 314 __pam_log(LOG_AUTH | LOG_DEBUG, 315 "pam_unix_account: entering pam_sm_acct_mgmt()"); 316 317 if ((error = pam_get_item(pamh, PAM_USER, (void **)&user)) 318 != PAM_SUCCESS) 319 goto out; 320 321 if (user == NULL) { 322 error = PAM_USER_UNKNOWN; 323 goto out; 324 } else 325 shpwd.sp_namp = user; 326 327 if ((error = pam_get_item(pamh, PAM_REPOSITORY, (void **)&auth_rep)) 328 != PAM_SUCCESS) 329 goto out; 330 331 if (auth_rep == NULL) { 332 pwu_rep = PWU_DEFAULT_REP; 333 } else { 334 if ((pwu_rep = calloc(1, sizeof (*pwu_rep))) == NULL) { 335 error = PAM_BUF_ERR; 336 goto out; 337 } 338 pwu_rep->type = auth_rep->type; 339 pwu_rep->scope = auth_rep->scope; 340 pwu_rep->scope_len = auth_rep->scope_len; 341 } 342 343 /* 344 * First get the password information 345 */ 346 attr_pw[0].type = ATTR_REP_NAME; attr_pw[0].next = &attr_pw[1]; 347 attr_pw[1].type = ATTR_UID; attr_pw[1].next = &attr_pw[2]; 348 attr_pw[2].type = ATTR_PASSWD; attr_pw[2].next = NULL; 349 result = __get_authtoken_attr(user, pwu_rep, attr_pw); 350 351 if (result == PWU_NOT_FOUND) { 352 error = PAM_USER_UNKNOWN; 353 goto out; 354 } else if (result == PWU_DENIED) { 355 error = PAM_PERM_DENIED; 356 goto out; 357 } else if (result == PWU_NOMEM) { 358 error = PAM_BUF_ERR; 359 goto out; 360 } else if (result != PWU_SUCCESS) { 361 error = PAM_SERVICE_ERR; 362 goto out; 363 } else { 364 repository_name = attr_pw[0].data.val_s; 365 pw_uid = attr_pw[1].data.val_i; 366 shpwd.sp_pwdp = attr_pw[2].data.val_s; 367 } 368 369 /* 370 * if repository is not files|nis|nisplus, and 371 * user wants server_policy, we don't care 372 * about aging and hence return PAM_IGNORE 373 */ 374 if (server_policy && 375 strcmp(repository_name, "files") != 0 && 376 strcmp(repository_name, "nis") != 0 && 377 strcmp(repository_name, "nisplus") != 0) { 378 error = PAM_IGNORE; 379 goto out; 380 } 381 382 /* 383 * Now get the aging information 384 */ 385 attr_spw[0].type = ATTR_LSTCHG; attr_spw[0].next = &attr_spw[1]; 386 attr_spw[1].type = ATTR_MIN; attr_spw[1].next = &attr_spw[2]; 387 attr_spw[2].type = ATTR_MAX; attr_spw[2].next = &attr_spw[3]; 388 attr_spw[3].type = ATTR_WARN; attr_spw[3].next = &attr_spw[4]; 389 attr_spw[4].type = ATTR_INACT; attr_spw[4].next = &attr_spw[5]; 390 attr_spw[5].type = ATTR_EXPIRE; attr_spw[5].next = &attr_spw[6]; 391 attr_spw[6].type = ATTR_FLAG; attr_spw[6].next = NULL; 392 393 result = __get_authtoken_attr(user, pwu_rep, attr_spw); 394 if (result == PWU_SUCCESS) { 395 shpwd.sp_lstchg = attr_spw[0].data.val_i; 396 shpwd.sp_min = attr_spw[1].data.val_i; 397 shpwd.sp_max = attr_spw[2].data.val_i; 398 shpwd.sp_warn = attr_spw[3].data.val_i; 399 shpwd.sp_inact = attr_spw[4].data.val_i; 400 shpwd.sp_expire = attr_spw[5].data.val_i; 401 shpwd.sp_flag = attr_spw[6].data.val_i; 402 } 403 404 if (debug) { 405 char *pw = "Unix PW"; 406 407 if (shpwd.sp_pwdp == NULL) 408 pw = "NULL"; 409 else if (strncmp(shpwd.sp_pwdp, LOCKSTRING, 410 sizeof (LOCKSTRING) - 1) == 0) 411 pw = LOCKSTRING; 412 else if (strcmp(shpwd.sp_pwdp, NOPWDRTR) == 0) 413 pw = NOPWDRTR; 414 415 if (result == PWU_DENIED) { 416 __pam_log(LOG_AUTH | LOG_DEBUG, 417 "pam_unix_account: %s: permission denied " 418 "to access password aging information. " 419 "Using defaults.", user); 420 } 421 422 __pam_log(LOG_AUTH | LOG_DEBUG, 423 "%s Policy:Unix, pw=%s, lstchg=%d, min=%d, max=%d, " 424 "warn=%d, inact=%d, expire=%d", 425 user, pw, shpwd.sp_lstchg, shpwd.sp_min, shpwd.sp_max, 426 shpwd.sp_warn, shpwd.sp_inact, shpwd.sp_expire); 427 } 428 429 if (pwu_rep != PWU_DEFAULT_REP) { 430 free(pwu_rep); 431 pwu_rep = PWU_DEFAULT_REP; 432 } 433 434 if (result == PWU_NOT_FOUND) { 435 error = PAM_USER_UNKNOWN; 436 goto out; 437 } else if (result == PWU_NOMEM) { 438 error = PAM_BUF_ERR; 439 goto out; 440 } else if (result != PWU_SUCCESS && result != PWU_DENIED) { 441 error = PAM_SERVICE_ERR; 442 goto out; 443 } 444 445 /* 446 * Check for locked account 447 */ 448 if (shpwd.sp_pwdp != NULL && 449 strncmp(shpwd.sp_pwdp, LOCKSTRING, sizeof (LOCKSTRING) - 1) == 0) { 450 char *service; 451 char *rhost = NULL; 452 453 (void) pam_get_item(pamh, PAM_SERVICE, (void **)&service); 454 (void) pam_get_item(pamh, PAM_RHOST, (void **)&rhost); 455 __pam_log(LOG_AUTH | LOG_NOTICE, 456 "pam_unix_account: %s attempting to validate locked " 457 "account %s from %s", 458 service, user, 459 (rhost != NULL && *rhost != '\0') ? rhost : "local host"); 460 error = PAM_PERM_DENIED; 461 goto out; 462 } 463 464 /* 465 * Check for NULL password and, if so, see if such is allowed 466 */ 467 if (shpwd.sp_pwdp[0] == '\0' && 468 (flags & PAM_DISALLOW_NULL_AUTHTOK) != 0) { 469 char *service; 470 char *rhost = NULL; 471 472 (void) pam_get_item(pamh, PAM_SERVICE, (void **)&service); 473 (void) pam_get_item(pamh, PAM_RHOST, (void **)&rhost); 474 475 __pam_log(LOG_AUTH | LOG_NOTICE, 476 "pam_unix_account: %s: empty password not allowed for " 477 "account %s from %s", service, user, 478 (rhost != NULL && *rhost != '\0') ? rhost : "local host"); 479 error = PAM_PERM_DENIED; 480 goto out; 481 } 482 483 /* 484 * Check for account expiration 485 */ 486 if (shpwd.sp_expire > 0 && 487 (time_t)shpwd.sp_expire < DAY_NOW) { 488 error = PAM_ACCT_EXPIRED; 489 goto out; 490 } 491 492 /* 493 * Check for excessive login account inactivity 494 */ 495 if (check_for_login_inactivity(pw_uid, &shpwd)) { 496 error = PAM_PERM_DENIED; 497 goto out; 498 } 499 500 /* 501 * Check to see if the user needs to change their password 502 */ 503 if (error = new_password_check(&shpwd, flags)) { 504 goto out; 505 } 506 507 /* 508 * Check to make sure password aging information is okay 509 */ 510 if ((error = perform_passwd_aging_check(pamh, &shpwd, flags)) 511 != PAM_SUCCESS) { 512 goto out; 513 } 514 515 /* 516 * Finally, warn the user if their password is about to expire. 517 */ 518 if (!(flags & PAM_SILENT)) { 519 warn_user_passwd_will_expire(pamh, shpwd); 520 } 521 522 /* 523 * All done, return Success 524 */ 525 error = PAM_SUCCESS; 526 527 out: 528 529 { 530 int pam_res; 531 unix_authtok_data *authtok_data; 532 533 if (debug) { 534 __pam_log(LOG_AUTH | LOG_DEBUG, 535 "pam_unix_account: %s: %s", 536 (user == NULL)?"NULL":user, 537 pam_strerror(pamh, error)); 538 } 539 540 if (repository_name) 541 free(repository_name); 542 if (pwu_rep != PWU_DEFAULT_REP) 543 free(pwu_rep); 544 if (shpwd.sp_pwdp) { 545 (void) memset(shpwd.sp_pwdp, 0, strlen(shpwd.sp_pwdp)); 546 free(shpwd.sp_pwdp); 547 } 548 549 /* store the password aging status in the pam handle */ 550 pam_res = pam_get_data(pamh, UNIX_AUTHTOK_DATA, 551 (const void **)&authtok_data); 552 553 if ((status = (unix_authtok_data *)calloc(1, 554 sizeof (unix_authtok_data))) == NULL) { 555 return (PAM_BUF_ERR); 556 } 557 558 if (pam_res == PAM_SUCCESS) 559 (void) memcpy(status, authtok_data, 560 sizeof (unix_authtok_data)); 561 562 status->age_status = error; 563 if (pam_set_data(pamh, UNIX_AUTHTOK_DATA, status, unix_cleanup) 564 != PAM_SUCCESS) { 565 free(status); 566 return (PAM_SERVICE_ERR); 567 } 568 } 569 570 return (error); 571 } 572