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