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