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