Home | History | Annotate | Download | only in unix_cred
      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 #include <nss_dbdefs.h>
     27 #include <pwd.h>
     28 #include <stdlib.h>
     29 #include <string.h>
     30 #include <syslog.h>
     31 #include <unistd.h>
     32 #include <auth_attr.h>
     33 #include <deflt.h>
     34 #include <priv.h>
     35 #include <secdb.h>
     36 #include <user_attr.h>
     37 #include <sys/task.h>
     38 #include <libintl.h>
     39 #include <project.h>
     40 #include <errno.h>
     41 #include <alloca.h>
     42 
     43 #include <bsm/adt.h>
     44 #include <bsm/adt_event.h>	/* adt_get_auid() */
     45 
     46 #include <security/pam_appl.h>
     47 #include <security/pam_modules.h>
     48 #include <security/pam_impl.h>
     49 
     50 #define	PROJECT		"project="
     51 #define	PROJSZ		(sizeof (PROJECT) - 1)
     52 
     53 /*
     54  *	unix_cred - PAM auth modules must contain both pam_sm_authenticate
     55  *		and pam_sm_setcred.  Some other auth module is responsible
     56  *		for authentication (e.g., pam_unix_auth.so), this module
     57  *		only implements pam_sm_setcred so that the authentication
     58  *		can be separated without knowledge of the Solaris Unix style
     59  *		credential setting.
     60  *		Solaris Unix style credential setting includes initializing
     61  *		the audit characteristics if not already initialized and
     62  *		setting the user's default and limit privileges.
     63  */
     64 
     65 /*
     66  *	unix_cred - pam_sm_authenticate
     67  *
     68  *	Returns	PAM_IGNORE.
     69  */
     70 
     71 /*ARGSUSED*/
     72 int
     73 pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv)
     74 {
     75 	return (PAM_IGNORE);
     76 }
     77 
     78 /*
     79  * Obtain a privilege set "keyname" from userattr; if none is present,
     80  * fall back to the default, "defname".
     81  */
     82 static int
     83 getset(char *keyname, char *defname, userattr_t *ua, priv_set_t **res,
     84     void *defp)
     85 {
     86 	char *str;
     87 	priv_set_t *tmp;
     88 	char *badp;
     89 	int len;
     90 
     91 	if ((ua == NULL || ua->attr == NULL ||
     92 	    (str = kva_match(ua->attr, keyname)) == NULL) &&
     93 	    (defp == NULL || (str = defread_r(defname, defp)) == NULL))
     94 		return (0);
     95 
     96 	len = strlen(str) + 1;
     97 	badp = alloca(len);
     98 	(void) memset(badp, '\0', len);
     99 	do {
    100 		const char *q, *endp;
    101 		tmp = priv_str_to_set(str, ",", &endp);
    102 		if (tmp == NULL) {
    103 			if (endp == NULL)
    104 				break;
    105 
    106 			/* Now remove the bad privilege endp points to */
    107 			q = strchr(endp, ',');
    108 			if (q == NULL)
    109 				q = endp + strlen(endp);
    110 
    111 			if (*badp != '\0')
    112 				(void) strlcat(badp, ",", len);
    113 			/* Memset above guarantees NUL termination */
    114 			/* LINTED */
    115 			(void) strncat(badp, endp, q - endp);
    116 			/* excise bad privilege; strtok ignores 2x sep */
    117 			(void) memmove((void *)endp, q, strlen(q) + 1);
    118 		}
    119 	} while (tmp == NULL && *str != '\0');
    120 
    121 	if (tmp == NULL) {
    122 		syslog(LOG_AUTH|LOG_ERR,
    123 		    "pam_setcred: can't parse privilege specification: %m\n");
    124 		return (-1);
    125 	} else if (*badp != '\0') {
    126 		syslog(LOG_AUTH|LOG_DEBUG,
    127 		    "pam_setcred: unrecognized privilege(s): %s\n", badp);
    128 	}
    129 	*res = tmp;
    130 	return (0);
    131 }
    132 
    133 /*
    134  *	unix_cred - pam_sm_setcred
    135  *
    136  *	Entry flags = 	PAM_ESTABLISH_CRED, set up Solaris Unix cred.
    137  *			PAM_DELETE_CRED, NOP, return PAM_SUCCESS.
    138  *			PAM_REINITIALIZE_CRED, set up Solaris Unix cred,
    139  *				or merge the current context with the new
    140  *				user.
    141  *			PAM_REFRESH_CRED, set up Solaris Unix cred.
    142  *			PAM_SILENT, print no messages to user.
    143  *
    144  *	Returns	PAM_SUCCESS, if all successful.
    145  *		PAM_CRED_ERR, if unable to set credentials.
    146  *		PAM_USER_UNKNOWN, if PAM_USER not set, or unable to find
    147  *			user in databases.
    148  *		PAM_SYSTEM_ERR, if no valid flag, or unable to get/set
    149  *			user's audit state.
    150  */
    151 
    152 int
    153 pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv)
    154 {
    155 	int	i;
    156 	int	debug = 0;
    157 	uint_t	nowarn = flags & PAM_SILENT;
    158 	int	ret = PAM_SUCCESS;
    159 	char	*user;
    160 	char	*auser;
    161 	char	*rhost;
    162 	char	*tty;
    163 	au_id_t	auid;
    164 	adt_session_data_t *ah;
    165 	adt_termid_t	*termid = NULL;
    166 	userattr_t	*ua;
    167 	priv_set_t	*lim, *def, *tset;
    168 	char		messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE];
    169 	char		buf[PROJECT_BUFSZ];
    170 	struct project	proj, *pproj;
    171 	int		error;
    172 	char		*projname;
    173 	char		*kvs;
    174 	struct passwd	pwd;
    175 	char		pwbuf[NSS_BUFLEN_PASSWD];
    176 	void		*defp;
    177 
    178 	for (i = 0; i < argc; i++) {
    179 		if (strcmp(argv[i], "debug") == 0)
    180 			debug = 1;
    181 		else if (strcmp(argv[i], "nowarn") == 0)
    182 			nowarn |= 1;
    183 	}
    184 
    185 	if (debug)
    186 		syslog(LOG_AUTH | LOG_DEBUG,
    187 		    "pam_unix_cred: pam_sm_setcred(flags = %x, argc= %d)",
    188 		    flags, argc);
    189 
    190 	(void) pam_get_item(pamh, PAM_USER, (void **)&user);
    191 
    192 	if (user == NULL || *user == '\0') {
    193 		syslog(LOG_AUTH | LOG_ERR,
    194 		    "pam_unix_cred: USER NULL or empty!\n");
    195 		return (PAM_USER_UNKNOWN);
    196 	}
    197 	(void) pam_get_item(pamh, PAM_AUSER, (void **)&auser);
    198 	(void) pam_get_item(pamh, PAM_RHOST, (void **)&rhost);
    199 	(void) pam_get_item(pamh, PAM_TTY, (void **)&tty);
    200 	if (debug)
    201 		syslog(LOG_AUTH | LOG_DEBUG,
    202 		    "pam_unix_cred: user = %s, auser = %s, rhost = %s, "
    203 		    "tty = %s", user,
    204 		    (auser == NULL) ? "NULL" : (*auser == '\0') ? "ZERO" :
    205 		    auser,
    206 		    (rhost == NULL) ? "NULL" : (*rhost == '\0') ? "ZERO" :
    207 		    rhost,
    208 		    (tty == NULL) ? "NULL" : (*tty == '\0') ? "ZERO" :
    209 		    tty);
    210 
    211 	/* validate flags */
    212 	switch (flags & (PAM_ESTABLISH_CRED | PAM_DELETE_CRED |
    213 	    PAM_REINITIALIZE_CRED | PAM_REFRESH_CRED)) {
    214 	case 0:
    215 		/* set default flag */
    216 		flags |= PAM_ESTABLISH_CRED;
    217 		break;
    218 	case PAM_ESTABLISH_CRED:
    219 	case PAM_REINITIALIZE_CRED:
    220 	case PAM_REFRESH_CRED:
    221 		break;
    222 	case PAM_DELETE_CRED:
    223 		return (PAM_SUCCESS);
    224 	default:
    225 		syslog(LOG_AUTH | LOG_ERR,
    226 		    "pam_unix_cred: invalid flags %x", flags);
    227 		return (PAM_SYSTEM_ERR);
    228 	}
    229 
    230 	/*
    231 	 * if auditing on and process audit state not set,
    232 	 * setup audit context for process.
    233 	 */
    234 	if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA) != 0) {
    235 		syslog(LOG_AUTH | LOG_ERR,
    236 		    "pam_unix_cred: cannot create start audit session %m");
    237 		return (PAM_SYSTEM_ERR);
    238 	}
    239 	adt_get_auid(ah, &auid);
    240 	if (debug) {
    241 		int	auditstate;
    242 
    243 		if (auditon(A_GETCOND, (caddr_t)&auditstate,
    244 		    sizeof (auditstate)) != 0) {
    245 			auditstate = AUC_DISABLED;
    246 		}
    247 		syslog(LOG_AUTH | LOG_DEBUG,
    248 		    "pam_unix_cred: state = %d, auid = %d", auditstate,
    249 		    auid);
    250 	}
    251 	if (getpwnam_r(user, &pwd, pwbuf, sizeof (pwbuf)) == NULL) {
    252 		syslog(LOG_AUTH | LOG_ERR,
    253 		    "pam_unix_cred: cannot get passwd entry for user = %s",
    254 		    user);
    255 		ret = PAM_USER_UNKNOWN;
    256 		goto adt_done;
    257 	}
    258 
    259 	if ((auid == AU_NOAUDITID) &&
    260 	    (flags & PAM_ESTABLISH_CRED)) {
    261 		struct passwd	apwd;
    262 		char	apwbuf[NSS_BUFLEN_PASSWD];
    263 
    264 		errno = 0;
    265 		if ((rhost == NULL || *rhost == '\0')) {
    266 			if (adt_load_ttyname(tty, &termid) != 0) {
    267 				if (errno != 0)
    268 					syslog(LOG_AUTH | LOG_ERR,
    269 					    "pam_unix_cred: cannot load "
    270 					    "ttyname: %m.");
    271 				else
    272 					syslog(LOG_AUTH | LOG_ERR,
    273 					    "pam_unix_cred: cannot load "
    274 					    "ttyname.");
    275 				ret = PAM_SYSTEM_ERR;
    276 				goto adt_done;
    277 			}
    278 		} else {
    279 			if (adt_load_hostname(rhost, &termid) != 0) {
    280 				if (errno != 0)
    281 					syslog(LOG_AUTH | LOG_ERR,
    282 					    "pam_unix_cred: cannot load "
    283 					    "hostname: %m.");
    284 				else
    285 					syslog(LOG_AUTH | LOG_ERR,
    286 					    "pam_unix_cred: cannot load "
    287 					    "hostname.");
    288 				ret = PAM_SYSTEM_ERR;
    289 				goto adt_done;
    290 			}
    291 		}
    292 		if ((auser != NULL) && (*auser != '\0') &&
    293 		    (getpwnam_r(auser, &apwd, apwbuf,
    294 		    sizeof (apwbuf)) != NULL)) {
    295 			/*
    296 			 * set up the initial audit for user coming
    297 			 * from another user
    298 			 */
    299 			if (adt_set_user(ah, apwd.pw_uid, apwd.pw_gid,
    300 			    apwd.pw_uid, apwd.pw_gid, termid, ADT_NEW) != 0) {
    301 				syslog(LOG_AUTH | LOG_ERR,
    302 				    "pam_unix_cred: cannot set auser audit "
    303 				    "%m");
    304 				ret = PAM_SYSTEM_ERR;
    305 				goto adt_done;
    306 			}
    307 			if (adt_set_user(ah, pwd.pw_uid, pwd.pw_gid,
    308 			    pwd.pw_uid, pwd.pw_gid, NULL,
    309 			    ADT_UPDATE) != 0) {
    310 				syslog(LOG_AUTH | LOG_ERR,
    311 				    "pam_unix_cred: cannot merge user audit "
    312 				    "%m");
    313 				ret = PAM_SYSTEM_ERR;
    314 				goto adt_done;
    315 			}
    316 			if (debug) {
    317 				syslog(LOG_AUTH | LOG_DEBUG,
    318 				    "pam_unix_cred: new audit set for %d:%d",
    319 				    apwd.pw_uid, pwd.pw_uid);
    320 			}
    321 		} else {
    322 			/*
    323 			 * No authenticated user or authenticated user is
    324 			 * not a local user, no remote attribution, set
    325 			 * up the initial audit as for direct user login
    326 			 */
    327 			if (adt_set_user(ah, pwd.pw_uid, pwd.pw_gid,
    328 			    pwd.pw_uid, pwd.pw_gid, termid, ADT_NEW) != 0) {
    329 				syslog(LOG_AUTH | LOG_ERR,
    330 				    "pam_unix_cred: cannot set user audit %m");
    331 				ret = PAM_SYSTEM_ERR;
    332 				goto adt_done;
    333 			}
    334 		}
    335 		if (adt_set_proc(ah) != 0) {
    336 			syslog(LOG_AUTH | LOG_ERR,
    337 			    "pam_unix_cred: cannot set process audit %m");
    338 			ret = PAM_CRED_ERR;
    339 			goto adt_done;
    340 		}
    341 		if (debug) {
    342 			syslog(LOG_AUTH | LOG_DEBUG,
    343 			    "pam_unix_cred: new audit set for %d",
    344 			    pwd.pw_uid);
    345 		}
    346 	} else if ((auid != AU_NOAUDITID) &&
    347 	    (flags & PAM_REINITIALIZE_CRED)) {
    348 		if (adt_set_user(ah, pwd.pw_uid, pwd.pw_gid, pwd.pw_uid,
    349 		    pwd.pw_gid, NULL, ADT_UPDATE) != 0) {
    350 			syslog(LOG_AUTH | LOG_ERR,
    351 			    "pam_unix_cred: cannot set user audit %m");
    352 			ret = PAM_SYSTEM_ERR;
    353 			goto adt_done;
    354 		}
    355 		if (adt_set_proc(ah) != 0) {
    356 			syslog(LOG_AUTH | LOG_ERR,
    357 			    "pam_unix_cred: cannot set process audit %m");
    358 			ret = PAM_CRED_ERR;
    359 			goto adt_done;
    360 		}
    361 		if (debug) {
    362 			syslog(LOG_AUTH | LOG_DEBUG,
    363 			    "pam_unix_cred: audit merged for %d:%d",
    364 			    auid, pwd.pw_uid);
    365 		}
    366 	} else if (debug) {
    367 		syslog(LOG_AUTH | LOG_DEBUG,
    368 		    "pam_unix_cred: audit already set for %d", auid);
    369 	}
    370 adt_done:
    371 	if (termid != NULL)
    372 		free(termid);
    373 	if (adt_end_session(ah) != 0) {
    374 		syslog(LOG_AUTH | LOG_ERR,
    375 		    "pam_unix_cred: unable to end audit session");
    376 	}
    377 
    378 	if (ret != PAM_SUCCESS)
    379 		return (ret);
    380 
    381 	/* Initialize the user's project */
    382 	(void) pam_get_item(pamh, PAM_RESOURCE, (void **)&kvs);
    383 	if (kvs != NULL) {
    384 		char *tmp, *lasts, *tok;
    385 
    386 		kvs = tmp = strdup(kvs);
    387 		if (kvs == NULL)
    388 			return (PAM_BUF_ERR);
    389 
    390 		while ((tok = strtok_r(tmp, ";", &lasts)) != NULL) {
    391 			if (strncmp(tok, PROJECT, PROJSZ) == 0) {
    392 				projname = tok + PROJSZ;
    393 				break;
    394 			}
    395 			tmp = NULL;
    396 		}
    397 	} else {
    398 		projname = NULL;
    399 	}
    400 
    401 	if (projname == NULL || *projname == '\0') {
    402 		pproj = getdefaultproj(user, &proj, (void *)&buf,
    403 		    PROJECT_BUFSZ);
    404 	} else {
    405 		pproj = getprojbyname(projname, &proj, (void *)&buf,
    406 		    PROJECT_BUFSZ);
    407 	}
    408 	/* projname points into kvs, so this is the first opportunity to free */
    409 	if (kvs != NULL)
    410 		free(kvs);
    411 	if (pproj == NULL) {
    412 		syslog(LOG_AUTH | LOG_ERR,
    413 		    "pam_unix_cred: no default project for user %s", user);
    414 		if (!nowarn) {
    415 			(void) snprintf(messages[0], sizeof (messages[0]),
    416 			    dgettext(TEXT_DOMAIN, "No default project!"));
    417 			(void) __pam_display_msg(pamh, PAM_ERROR_MSG,
    418 			    1, messages, NULL);
    419 		}
    420 		return (PAM_SYSTEM_ERR);
    421 	}
    422 	if ((error = setproject(proj.pj_name, user, TASK_NORMAL)) != 0) {
    423 		kva_t *kv_array;
    424 
    425 		switch (error) {
    426 		case SETPROJ_ERR_TASK:
    427 			if (errno == EAGAIN) {
    428 				syslog(LOG_AUTH | LOG_ERR,
    429 				    "pam_unix_cred: project \"%s\" resource "
    430 				    "control limit has been reached",
    431 				    proj.pj_name);
    432 				(void) snprintf(messages[0],
    433 				    sizeof (messages[0]), dgettext(
    434 				    TEXT_DOMAIN,
    435 				    "Resource control limit has been "
    436 				    "reached"));
    437 			} else {
    438 				syslog(LOG_AUTH | LOG_ERR,
    439 				    "pam_unix_cred: user %s could not join "
    440 				    "project \"%s\": %m", user, proj.pj_name);
    441 				(void) snprintf(messages[0],
    442 				    sizeof (messages[0]), dgettext(
    443 				    TEXT_DOMAIN,
    444 				    "Could not join default project"));
    445 			}
    446 			if (!nowarn)
    447 				(void) __pam_display_msg(pamh, PAM_ERROR_MSG, 1,
    448 				    messages, NULL);
    449 			break;
    450 		case SETPROJ_ERR_POOL:
    451 			(void) snprintf(messages[0], sizeof (messages[0]),
    452 			    dgettext(TEXT_DOMAIN,
    453 			    "Could not bind to resource pool"));
    454 			switch (errno) {
    455 			case EACCES:
    456 				syslog(LOG_AUTH | LOG_ERR,
    457 				    "pam_unix_cred: project \"%s\" could not "
    458 				    "bind to resource pool: No resource pool "
    459 				    "accepting default bindings exists",
    460 				    proj.pj_name);
    461 				(void) snprintf(messages[1],
    462 				    sizeof (messages[1]),
    463 				    dgettext(TEXT_DOMAIN,
    464 				    "No resource pool accepting "
    465 				    "default bindings exists"));
    466 				break;
    467 			case ESRCH:
    468 				syslog(LOG_AUTH | LOG_ERR,
    469 				    "pam_unix_cred: project \"%s\" could not "
    470 				    "bind to resource pool: The resource pool "
    471 				    "is unknown", proj.pj_name);
    472 				(void) snprintf(messages[1],
    473 				    sizeof (messages[1]),
    474 				    dgettext(TEXT_DOMAIN,
    475 				    "The specified resource pool "
    476 				    "is unknown"));
    477 				break;
    478 			default:
    479 				(void) snprintf(messages[1],
    480 				    sizeof (messages[1]),
    481 				    dgettext(TEXT_DOMAIN,
    482 				    "Failure during pool binding"));
    483 				syslog(LOG_AUTH | LOG_ERR,
    484 				    "pam_unix_cred: project \"%s\" could not "
    485 				    "bind to resource pool: %m", proj.pj_name);
    486 			}
    487 			if (!nowarn)
    488 				(void) __pam_display_msg(pamh, PAM_ERROR_MSG,
    489 				    2, messages, NULL);
    490 			break;
    491 		default:
    492 			/*
    493 			 * Resource control assignment failed.  Unlike
    494 			 * newtask(1m), we treat this as an error.
    495 			 */
    496 			if (error < 0) {
    497 				/*
    498 				 * This isn't supposed to happen, but in
    499 				 * case it does, this error message
    500 				 * doesn't use error as an index, like
    501 				 * the others might.
    502 				 */
    503 				syslog(LOG_AUTH | LOG_ERR,
    504 				    "pam_unix_cred: unkwown error joining "
    505 				    "project \"%s\" (%d)", proj.pj_name, error);
    506 				(void) snprintf(messages[0],
    507 				    sizeof (messages[0]),
    508 				    dgettext(TEXT_DOMAIN,
    509 				    "unkwown error joining project \"%s\""
    510 				    " (%d)"), proj.pj_name, error);
    511 			} else if ((kv_array = _str2kva(proj.pj_attr, KV_ASSIGN,
    512 			    KV_DELIMITER)) != NULL) {
    513 				syslog(LOG_AUTH | LOG_ERR,
    514 				    "pam_unix_cred: %s resource control "
    515 				    "assignment failed for project \"%s\"",
    516 				    kv_array->data[error - 1].key,
    517 				    proj.pj_name);
    518 				(void) snprintf(messages[0],
    519 				    sizeof (messages[0]),
    520 				    dgettext(TEXT_DOMAIN,
    521 				    "%s resource control assignment failed for "
    522 				    "project \"%s\""),
    523 				    kv_array->data[error - 1].key,
    524 				    proj.pj_name);
    525 				_kva_free(kv_array);
    526 			} else {
    527 				syslog(LOG_AUTH | LOG_ERR,
    528 				    "pam_unix_cred: resource control "
    529 				    "assignment failed for project \"%s\""
    530 				    "attribute %d", proj.pj_name, error);
    531 				(void) snprintf(messages[0],
    532 				    sizeof (messages[0]),
    533 				    dgettext(TEXT_DOMAIN,
    534 				    "resource control assignment failed for "
    535 				    "project \"%s\" attribute %d"),
    536 				    proj.pj_name, error);
    537 			}
    538 			if (!nowarn)
    539 				(void) __pam_display_msg(pamh, PAM_ERROR_MSG,
    540 				    1, messages, NULL);
    541 		}
    542 		return (PAM_SYSTEM_ERR);
    543 	}
    544 
    545 	ua = getusernam(user);
    546 
    547 	defp = defopen_r(AUTH_POLICY);
    548 
    549 	tset = def = lim = NULL;
    550 
    551 	if (getset(USERATTR_LIMPRIV_KW, DEF_LIMITPRIV, ua, &lim, defp) != 0 ||
    552 	    getset(USERATTR_DFLTPRIV_KW, DEF_DFLTPRIV, ua, &def, defp) != 0) {
    553 		ret = PAM_SYSTEM_ERR;
    554 		goto out;
    555 	}
    556 
    557 	if (def == NULL) {
    558 		def = priv_str_to_set("basic", ",", NULL);
    559 		errno = 0;
    560 		if ((pathconf("/", _PC_CHOWN_RESTRICTED) == -1) && (errno == 0))
    561 			(void) priv_addset(def, PRIV_FILE_CHOWN_SELF);
    562 	}
    563 	/*
    564 	 * Silently limit the privileges to those actually available
    565 	 * in the current zone.
    566 	 */
    567 	tset = priv_allocset();
    568 	if (tset == NULL) {
    569 		ret = PAM_SYSTEM_ERR;
    570 		goto out;
    571 	}
    572 	if (getppriv(PRIV_PERMITTED, tset) != 0) {
    573 		ret = PAM_SYSTEM_ERR;
    574 		goto out;
    575 	}
    576 	if (!priv_issubset(def, tset))
    577 		priv_intersect(tset, def);
    578 	/*
    579 	 * We set privilege awareness here so that I gets copied to
    580 	 * P & E when the final setuid(uid) happens.
    581 	 */
    582 	(void) setpflags(PRIV_AWARE, 1);
    583 	if (setppriv(PRIV_SET, PRIV_INHERITABLE, def) != 0) {
    584 		syslog(LOG_AUTH | LOG_ERR,
    585 		    "pam_setcred: setppriv(defaultpriv) failed: %m");
    586 		ret = PAM_CRED_ERR;
    587 	}
    588 
    589 	if (lim != NULL) {
    590 		/*
    591 		 * Silently limit the privileges to the limit set available.
    592 		 */
    593 		if (getppriv(PRIV_LIMIT, tset) != 0) {
    594 			ret = PAM_SYSTEM_ERR;
    595 			goto out;
    596 		}
    597 		if (!priv_issubset(lim, tset))
    598 			priv_intersect(tset, lim);
    599 		if (setppriv(PRIV_SET, PRIV_LIMIT, lim) != 0) {
    600 			syslog(LOG_AUTH | LOG_ERR,
    601 			    "pam_setcred: setppriv(limitpriv) failed: %m");
    602 			ret = PAM_CRED_ERR;
    603 			goto out;
    604 		}
    605 		/*
    606 		 * In order not to surprise certain applications, we
    607 		 * need to get rid of privilege awareness and thus we must
    608 		 * set this flag which will cause a reset on set*uid().
    609 		 */
    610 		(void) setpflags(PRIV_AWARE_RESET, 1);
    611 	}
    612 	/*
    613 	 * This may fail but we do not care as this will be reset later
    614 	 * when the uids are set to their final values.
    615 	 */
    616 	(void) setpflags(PRIV_AWARE, 0);
    617 
    618 out:
    619 	if (defp != NULL)
    620 		defclose_r(defp);
    621 
    622 	if (ua != NULL)
    623 		free_userattr(ua);
    624 	if (lim != NULL)
    625 		priv_freeset(lim);
    626 	if (def != NULL)
    627 		priv_freeset(def);
    628 	if (tset != NULL)
    629 		priv_freeset(tset);
    630 
    631 	return (ret);
    632 }
    633