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