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