OpenGrok

Cross Reference: unix_acct.c
xref: /onnv/onnv-gate/usr/src/lib/pam_modules/unix_account/unix_acct.c
Home | History | Annotate | Line # | Download | only in unix_account
      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 			(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
    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
    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
    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
    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