xref: /illumos-gate/usr/src/lib/pam_modules/unix_cred/unix_cred.c (revision 148fd93e57d3d5813d90f1291e6bd30de45c7723)
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(1), 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