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*/
71int
72pam_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 */
81static int
82getset(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
128typedef struct deflim {
129	char *def;
130	char *lim;
131} deflim_t;
132
133/*ARGSUSED*/
134static int
135finddeflim(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
172int
173pam_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		}
324adt_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	}
403adt_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
659out:
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