xref: /minix3/usr.bin/su/su_pam.c (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
1*0a6a1f1dSLionel Sambuc /*	$NetBSD: su_pam.c,v 1.20 2015/08/09 09:39:21 shm Exp $	*/
24de51eedSBen Gras 
34de51eedSBen Gras /*
44de51eedSBen Gras  * Copyright (c) 1988 The Regents of the University of California.
54de51eedSBen Gras  * All rights reserved.
64de51eedSBen Gras  *
74de51eedSBen Gras  * Redistribution and use in source and binary forms, with or without
84de51eedSBen Gras  * modification, are permitted provided that the following conditions
94de51eedSBen Gras  * are met:
104de51eedSBen Gras  * 1. Redistributions of source code must retain the above copyright
114de51eedSBen Gras  *    notice, this list of conditions and the following disclaimer.
124de51eedSBen Gras  * 2. Redistributions in binary form must reproduce the above copyright
134de51eedSBen Gras  *    notice, this list of conditions and the following disclaimer in the
144de51eedSBen Gras  *    documentation and/or other materials provided with the distribution.
154de51eedSBen Gras  * 3. Neither the name of the University nor the names of its contributors
164de51eedSBen Gras  *    may be used to endorse or promote products derived from this software
174de51eedSBen Gras  *    without specific prior written permission.
184de51eedSBen Gras  *
194de51eedSBen Gras  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
204de51eedSBen Gras  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
214de51eedSBen Gras  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
224de51eedSBen Gras  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
234de51eedSBen Gras  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
244de51eedSBen Gras  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
254de51eedSBen Gras  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
264de51eedSBen Gras  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
274de51eedSBen Gras  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
284de51eedSBen Gras  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
294de51eedSBen Gras  * SUCH DAMAGE.
304de51eedSBen Gras  */
314de51eedSBen Gras 
324de51eedSBen Gras #include <sys/cdefs.h>
334de51eedSBen Gras #ifndef lint
344de51eedSBen Gras __COPYRIGHT("@(#) Copyright (c) 1988\
354de51eedSBen Gras  The Regents of the University of California.  All rights reserved.");
364de51eedSBen Gras #endif /* not lint */
374de51eedSBen Gras 
384de51eedSBen Gras #ifndef lint
394de51eedSBen Gras #if 0
404de51eedSBen Gras static char sccsid[] = "@(#)su.c	8.3 (Berkeley) 4/2/94";*/
414de51eedSBen Gras #else
42*0a6a1f1dSLionel Sambuc __RCSID("$NetBSD: su_pam.c,v 1.20 2015/08/09 09:39:21 shm Exp $");
434de51eedSBen Gras #endif
444de51eedSBen Gras #endif /* not lint */
454de51eedSBen Gras 
464de51eedSBen Gras #include <sys/param.h>
474de51eedSBen Gras #include <sys/time.h>
484de51eedSBen Gras #include <sys/resource.h>
494de51eedSBen Gras #include <sys/wait.h>
504de51eedSBen Gras #include <err.h>
514de51eedSBen Gras #include <errno.h>
524de51eedSBen Gras #include <grp.h>
534de51eedSBen Gras #include <paths.h>
544de51eedSBen Gras #include <pwd.h>
554de51eedSBen Gras #include <signal.h>
564de51eedSBen Gras #include <stdio.h>
574de51eedSBen Gras #include <stdlib.h>
584de51eedSBen Gras #include <string.h>
594de51eedSBen Gras #include <syslog.h>
604de51eedSBen Gras #include <time.h>
614de51eedSBen Gras #include <tzfile.h>
624de51eedSBen Gras #include <unistd.h>
634de51eedSBen Gras #include <util.h>
644de51eedSBen Gras #include <login_cap.h>
654de51eedSBen Gras 
664de51eedSBen Gras #include <security/pam_appl.h>
674de51eedSBen Gras #include <security/openpam.h>   /* for openpam_ttyconv() */
684de51eedSBen Gras 
694de51eedSBen Gras #ifdef ALLOW_GROUP_CHANGE
704de51eedSBen Gras #include "grutil.h"
714de51eedSBen Gras #endif
724de51eedSBen Gras #include "suutil.h"
734de51eedSBen Gras 
744de51eedSBen Gras static const struct pam_conv pamc = { &openpam_ttyconv, NULL };
754de51eedSBen Gras 
764de51eedSBen Gras #define	ARGSTRX	"-dflm"
774de51eedSBen Gras 
784de51eedSBen Gras #ifdef LOGIN_CAP
794de51eedSBen Gras #define ARGSTR	ARGSTRX "c:"
804de51eedSBen Gras #else
814de51eedSBen Gras #define ARGSTR ARGSTRX
824de51eedSBen Gras #endif
834de51eedSBen Gras 
8484d9c625SLionel Sambuc static void logit(const char *, ...) __printflike(1, 2);
8584d9c625SLionel Sambuc 
8684d9c625SLionel Sambuc static const char *
safe_pam_strerror(pam_handle_t * pamh,int pam_err)8784d9c625SLionel Sambuc safe_pam_strerror(pam_handle_t *pamh, int pam_err) {
8884d9c625SLionel Sambuc 	const char *msg;
8984d9c625SLionel Sambuc 
9084d9c625SLionel Sambuc 	if ((msg = pam_strerror(pamh, pam_err)) != NULL)
9184d9c625SLionel Sambuc 		return msg;
9284d9c625SLionel Sambuc 
9384d9c625SLionel Sambuc 	static char buf[1024];
9484d9c625SLionel Sambuc 	snprintf(buf, sizeof(buf), "Unknown pam error %d", pam_err);
9584d9c625SLionel Sambuc 	return buf;
9684d9c625SLionel Sambuc }
974de51eedSBen Gras 
984de51eedSBen Gras int
main(int argc,char ** argv)994de51eedSBen Gras main(int argc, char **argv)
1004de51eedSBen Gras {
1014de51eedSBen Gras 	extern char **environ;
1024de51eedSBen Gras 	struct passwd *pwd, pwres;
1034de51eedSBen Gras 	char *p;
1044de51eedSBen Gras 	uid_t ruid;
1054de51eedSBen Gras 	int asme, ch, asthem, fastlogin, prio, gohome;
1064de51eedSBen Gras 	u_int setwhat;
1074de51eedSBen Gras 	enum { UNSET, YES, NO } iscsh = UNSET;
1084de51eedSBen Gras 	const char *user, *shell, *avshell;
1094de51eedSBen Gras 	char *username, *class;
1104de51eedSBen Gras 	char **np;
1114de51eedSBen Gras 	char shellbuf[MAXPATHLEN], avshellbuf[MAXPATHLEN];
1124de51eedSBen Gras 	int pam_err;
1134de51eedSBen Gras 	char hostname[MAXHOSTNAMELEN];
1144de51eedSBen Gras 	char *tty;
1154de51eedSBen Gras 	const char *func;
1164de51eedSBen Gras 	const void *newuser;
1174de51eedSBen Gras 	login_cap_t *lc;
1184de51eedSBen Gras 	pam_handle_t *pamh = NULL;
1194de51eedSBen Gras 	char pwbuf[1024];
1204de51eedSBen Gras #ifdef PAM_DEBUG
1214de51eedSBen Gras 	extern int _openpam_debug;
1224de51eedSBen Gras 
1234de51eedSBen Gras 	_openpam_debug = 1;
1244de51eedSBen Gras #endif
1254de51eedSBen Gras #ifdef ALLOW_GROUP_CHANGE
1264de51eedSBen Gras 	char *gname;
1274de51eedSBen Gras #endif
1284de51eedSBen Gras 
1294de51eedSBen Gras 	(void)setprogname(argv[0]);
1304de51eedSBen Gras 	asme = asthem = fastlogin = 0;
1314de51eedSBen Gras 	gohome = 1;
1324de51eedSBen Gras 	shell = class = NULL;
1334de51eedSBen Gras 	while ((ch = getopt(argc, argv, ARGSTR)) != -1)
1344de51eedSBen Gras 		switch((char)ch) {
1354de51eedSBen Gras 		case 'c':
1364de51eedSBen Gras 			class = optarg;
1374de51eedSBen Gras 			break;
1384de51eedSBen Gras 		case 'd':
1394de51eedSBen Gras 			asme = 0;
1404de51eedSBen Gras 			asthem = 1;
1414de51eedSBen Gras 			gohome = 0;
1424de51eedSBen Gras 			break;
1434de51eedSBen Gras 		case 'f':
1444de51eedSBen Gras 			fastlogin = 1;
1454de51eedSBen Gras 			break;
1464de51eedSBen Gras 		case '-':
1474de51eedSBen Gras 		case 'l':
1484de51eedSBen Gras 			asme = 0;
1494de51eedSBen Gras 			asthem = 1;
1504de51eedSBen Gras 			break;
1514de51eedSBen Gras 		case 'm':
1524de51eedSBen Gras 			asme = 1;
1534de51eedSBen Gras 			asthem = 0;
1544de51eedSBen Gras 			break;
1554de51eedSBen Gras 		case '?':
1564de51eedSBen Gras 		default:
1574de51eedSBen Gras 			(void)fprintf(stderr,
1584de51eedSBen Gras #ifdef ALLOW_GROUP_CHANGE
1594de51eedSBen Gras 			    "Usage: %s [%s] [login[:group] [shell arguments]]\n",
1604de51eedSBen Gras #else
1614de51eedSBen Gras 			    "Usage: %s [%s] [login [shell arguments]]\n",
1624de51eedSBen Gras #endif
1634de51eedSBen Gras 			    getprogname(), ARGSTR);
1644de51eedSBen Gras 			exit(EXIT_FAILURE);
1654de51eedSBen Gras 		}
1664de51eedSBen Gras 	argv += optind;
1674de51eedSBen Gras 
1684de51eedSBen Gras 	/* Lower the priority so su runs faster */
1694de51eedSBen Gras 	errno = 0;
1704de51eedSBen Gras 	prio = getpriority(PRIO_PROCESS, 0);
1714de51eedSBen Gras 	if (errno)
1724de51eedSBen Gras 		prio = 0;
1734de51eedSBen Gras 	if (prio > -2)
1744de51eedSBen Gras 		(void)setpriority(PRIO_PROCESS, 0, -2);
1754de51eedSBen Gras 	openlog("su", 0, LOG_AUTH);
1764de51eedSBen Gras 
1774de51eedSBen Gras 	/* get current login name and shell */
1784de51eedSBen Gras 	ruid = getuid();
1794de51eedSBen Gras 	username = getlogin();
1804de51eedSBen Gras 	if (username == NULL ||
1814de51eedSBen Gras 	    getpwnam_r(username, &pwres, pwbuf, sizeof(pwbuf), &pwd) != 0 ||
1824de51eedSBen Gras 	    pwd == NULL || pwd->pw_uid != ruid) {
1834de51eedSBen Gras 		if (getpwuid_r(ruid, &pwres, pwbuf, sizeof(pwbuf), &pwd) != 0)
1844de51eedSBen Gras 			pwd = NULL;
1854de51eedSBen Gras 	}
1864de51eedSBen Gras 	if (pwd == NULL)
1874de51eedSBen Gras 		errx(EXIT_FAILURE, "who are you?");
1884de51eedSBen Gras 	username = estrdup(pwd->pw_name);
1894de51eedSBen Gras 
1904de51eedSBen Gras 	if (asme) {
1914de51eedSBen Gras 		if (pwd->pw_shell && *pwd->pw_shell) {
1924de51eedSBen Gras 			(void)estrlcpy(shellbuf, pwd->pw_shell, sizeof(shellbuf));
1934de51eedSBen Gras 			shell = shellbuf;
1944de51eedSBen Gras 		} else {
1954de51eedSBen Gras 			shell = _PATH_BSHELL;
1964de51eedSBen Gras 			iscsh = NO;
1974de51eedSBen Gras 		}
1984de51eedSBen Gras 	}
1994de51eedSBen Gras 	/* get target login information, default to root */
2004de51eedSBen Gras 	user = *argv ? *argv : "root";
2014de51eedSBen Gras 	np = *argv ? argv : argv - 1;
2024de51eedSBen Gras 
2034de51eedSBen Gras #ifdef ALLOW_GROUP_CHANGE
2044de51eedSBen Gras 	if ((p = strchr(user, ':')) != NULL) {
2054de51eedSBen Gras 		*p = '\0';
2064de51eedSBen Gras 		gname = ++p;
2074de51eedSBen Gras 	}
2084de51eedSBen Gras 	else
2094de51eedSBen Gras 		gname = NULL;
2104de51eedSBen Gras 
2114de51eedSBen Gras #ifdef ALLOW_EMPTY_USER
2124de51eedSBen Gras 	if (user[0] == '\0')
2134de51eedSBen Gras 		user = username;
2144de51eedSBen Gras #endif
2154de51eedSBen Gras #endif
2164de51eedSBen Gras 	if (getpwnam_r(user, &pwres, pwbuf, sizeof(pwbuf), &pwd) != 0 ||
2174de51eedSBen Gras 	    pwd == NULL)
2184de51eedSBen Gras 		errx(EXIT_FAILURE, "unknown login %s", user);
2194de51eedSBen Gras 
2204de51eedSBen Gras 	/*
2214de51eedSBen Gras 	 * PAM initialization
2224de51eedSBen Gras 	 */
2234de51eedSBen Gras #define PAM_END(msg) do { func = msg; goto done;} /* NOTREACHED */ while (/*CONSTCOND*/0)
2244de51eedSBen Gras 
2254de51eedSBen Gras 	if ((pam_err = pam_start("su", user, &pamc, &pamh)) != PAM_SUCCESS) {
2264de51eedSBen Gras 		if (pamh != NULL)
2274de51eedSBen Gras 			PAM_END("pam_start");
2284de51eedSBen Gras 		/* Things went really bad... */
2294de51eedSBen Gras 		syslog(LOG_ERR, "pam_start failed: %s",
23084d9c625SLionel Sambuc 		    safe_pam_strerror(pamh, pam_err));
2314de51eedSBen Gras 		errx(EXIT_FAILURE, "pam_start failed");
2324de51eedSBen Gras 	}
2334de51eedSBen Gras 
2344de51eedSBen Gras #define PAM_END_ITEM(item)	PAM_END("pam_set_item(" # item ")")
2354de51eedSBen Gras #define PAM_SET_ITEM(item, var) 					    \
2364de51eedSBen Gras 	if ((pam_err = pam_set_item(pamh, (item), (var))) != PAM_SUCCESS)   \
2374de51eedSBen Gras 		PAM_END_ITEM(item)
2384de51eedSBen Gras 
2394de51eedSBen Gras 	/*
2404de51eedSBen Gras 	 * Fill hostname, username and tty
2414de51eedSBen Gras 	 */
2424de51eedSBen Gras 	PAM_SET_ITEM(PAM_RUSER, username);
2434de51eedSBen Gras 	if (gethostname(hostname, sizeof(hostname)) != -1)
2444de51eedSBen Gras 		PAM_SET_ITEM(PAM_RHOST, hostname);
2454de51eedSBen Gras 
2464de51eedSBen Gras 	if ((tty = ttyname(STDERR_FILENO)) != NULL)
2474de51eedSBen Gras 		PAM_SET_ITEM(PAM_TTY, tty);
2484de51eedSBen Gras 
2494de51eedSBen Gras 	/*
2504de51eedSBen Gras 	 * Authentication
2514de51eedSBen Gras 	 */
2524de51eedSBen Gras 	if ((pam_err = pam_authenticate(pamh, 0)) != PAM_SUCCESS) {
2534de51eedSBen Gras 		syslog(LOG_WARNING, "BAD SU %s to %s%s: %s",
25484d9c625SLionel Sambuc 		    username, user, ontty(), safe_pam_strerror(pamh, pam_err));
2554de51eedSBen Gras 		(void)pam_end(pamh, pam_err);
256*0a6a1f1dSLionel Sambuc 		errx(EXIT_FAILURE, "Sorry: %s", safe_pam_strerror(NULL, pam_err));
2574de51eedSBen Gras 	}
2584de51eedSBen Gras 
2594de51eedSBen Gras 	/*
2604de51eedSBen Gras 	 * Authorization
2614de51eedSBen Gras 	 */
2624de51eedSBen Gras 	switch(pam_err = pam_acct_mgmt(pamh, 0)) {
2634de51eedSBen Gras 	case PAM_NEW_AUTHTOK_REQD:
2644de51eedSBen Gras 		pam_err = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
2654de51eedSBen Gras 		if (pam_err != PAM_SUCCESS)
2664de51eedSBen Gras 			PAM_END("pam_chauthok");
2674de51eedSBen Gras 		break;
2684de51eedSBen Gras 	case PAM_SUCCESS:
2694de51eedSBen Gras 		break;
2704de51eedSBen Gras 	default:
2714de51eedSBen Gras 		PAM_END("pam_acct_mgmt");
2724de51eedSBen Gras 		break;
2734de51eedSBen Gras 	}
2744de51eedSBen Gras 
2754de51eedSBen Gras 	/*
2764de51eedSBen Gras 	 * pam_authenticate might have changed the target user.
2774de51eedSBen Gras 	 * refresh pwd and user
2784de51eedSBen Gras 	 */
2794de51eedSBen Gras 	pam_err = pam_get_item(pamh, PAM_USER, &newuser);
2804de51eedSBen Gras 	if (pam_err != PAM_SUCCESS) {
2814de51eedSBen Gras 		syslog(LOG_WARNING,
28284d9c625SLionel Sambuc 		    "pam_get_item(PAM_USER): %s", safe_pam_strerror(pamh, pam_err));
2834de51eedSBen Gras 	} else {
2844de51eedSBen Gras 		user = (char *)__UNCONST(newuser);
2854de51eedSBen Gras 		if (getpwnam_r(user, &pwres, pwbuf, sizeof(pwbuf), &pwd) != 0 ||
2864de51eedSBen Gras 		    pwd == NULL) {
2874de51eedSBen Gras 			(void)pam_end(pamh, pam_err);
2884de51eedSBen Gras 			syslog(LOG_ERR, "unknown login: %s", username);
2894de51eedSBen Gras 			errx(EXIT_FAILURE, "unknown login: %s", username);
2904de51eedSBen Gras 		}
2914de51eedSBen Gras 	}
2924de51eedSBen Gras 
2934de51eedSBen Gras #define ERRX_PAM_END(args) do {			\
2944de51eedSBen Gras 	(void)pam_end(pamh, pam_err);		\
2954de51eedSBen Gras 	errx args;				\
2964de51eedSBen Gras } while (/* CONSTCOND */0)
2974de51eedSBen Gras 
2984de51eedSBen Gras #define ERR_PAM_END(args) do {			\
2994de51eedSBen Gras 	(void)pam_end(pamh, pam_err);		\
3004de51eedSBen Gras 	err args;				\
3014de51eedSBen Gras } while (/* CONSTCOND */0)
3024de51eedSBen Gras 
3034de51eedSBen Gras 	/* force the usage of specified class */
3044de51eedSBen Gras 	if (class) {
3054de51eedSBen Gras 		if (ruid)
3064de51eedSBen Gras 			ERRX_PAM_END((EXIT_FAILURE, "Only root may use -c"));
3074de51eedSBen Gras 
3084de51eedSBen Gras 		pwd->pw_class = class;
3094de51eedSBen Gras 	}
3104de51eedSBen Gras 
3114de51eedSBen Gras 	if ((lc = login_getclass(pwd->pw_class)) == NULL)
3124de51eedSBen Gras 		ERRX_PAM_END((EXIT_FAILURE,
3134de51eedSBen Gras 		    "Unknown class %s\n", pwd->pw_class));
3144de51eedSBen Gras 
3154de51eedSBen Gras 	if (asme) {
3164de51eedSBen Gras 		/* if asme and non-standard target shell, must be root */
3174de51eedSBen Gras 		if (chshell(pwd->pw_shell) == 0 && ruid)
3184de51eedSBen Gras 			ERRX_PAM_END((EXIT_FAILURE,
3194de51eedSBen Gras 			    "permission denied (shell)."));
3204de51eedSBen Gras 	} else if (pwd->pw_shell && *pwd->pw_shell) {
3214de51eedSBen Gras 		shell = pwd->pw_shell;
3224de51eedSBen Gras 		iscsh = UNSET;
3234de51eedSBen Gras 	} else {
3244de51eedSBen Gras 		shell = _PATH_BSHELL;
3254de51eedSBen Gras 		iscsh = NO;
3264de51eedSBen Gras 	}
3274de51eedSBen Gras 
3284de51eedSBen Gras 	if ((p = strrchr(shell, '/')) != NULL)
3294de51eedSBen Gras 		avshell = p + 1;
3304de51eedSBen Gras 	else
3314de51eedSBen Gras 		avshell = shell;
3324de51eedSBen Gras 
3334de51eedSBen Gras 	/* if we're forking a csh, we want to slightly muck the args */
3344de51eedSBen Gras 	if (iscsh == UNSET)
3354de51eedSBen Gras 		iscsh = strstr(avshell, "csh") ? YES : NO;
3364de51eedSBen Gras 
3374de51eedSBen Gras 	/*
3384de51eedSBen Gras 	 * Initialize the supplemental groups before pam gets to them,
3394de51eedSBen Gras 	 * so that other pam modules get a chance to add more when
3404de51eedSBen Gras 	 * we do setcred. Note, we don't relinguish our set-userid yet
3414de51eedSBen Gras 	 */
3424de51eedSBen Gras 	/* if we aren't changing users, keep the current group members */
3434de51eedSBen Gras 	if (ruid != pwd->pw_uid &&
3444de51eedSBen Gras 	    setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) == -1)
3454de51eedSBen Gras 		ERR_PAM_END((EXIT_FAILURE, "setting user context"));
3464de51eedSBen Gras 
3474de51eedSBen Gras #ifdef ALLOW_GROUP_CHANGE
3484de51eedSBen Gras 	addgroup(lc, gname, pwd, ruid, "Group Password:");
3494de51eedSBen Gras #endif
3504de51eedSBen Gras 	if ((pam_err = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS)
3514de51eedSBen Gras 		PAM_END("pam_setcred");
3524de51eedSBen Gras 
3534de51eedSBen Gras 	/*
3544de51eedSBen Gras 	 * Manage session.
3554de51eedSBen Gras 	 */
3564de51eedSBen Gras 	if (asthem) {
3574de51eedSBen Gras 		pid_t pid, xpid;
3584de51eedSBen Gras 		int status = 1;
3594de51eedSBen Gras 		struct sigaction sa, sa_int, sa_pipe, sa_quit;
3604de51eedSBen Gras 		int fds[2];
3614de51eedSBen Gras 
3624de51eedSBen Gras  		if ((pam_err = pam_open_session(pamh, 0)) != PAM_SUCCESS)
3634de51eedSBen Gras 			PAM_END("pam_open_session");
3644de51eedSBen Gras 
3654de51eedSBen Gras 		/*
3664de51eedSBen Gras 		 * In order to call pam_close_session after the
3674de51eedSBen Gras 		 * command terminates, we need to fork.
3684de51eedSBen Gras 		 */
3694de51eedSBen Gras 		sa.sa_flags = SA_RESTART;
3704de51eedSBen Gras 		sa.sa_handler = SIG_IGN;
3714de51eedSBen Gras 		(void)sigemptyset(&sa.sa_mask);
3724de51eedSBen Gras 		(void)sigaction(SIGINT, &sa, &sa_int);
3734de51eedSBen Gras 		(void)sigaction(SIGQUIT, &sa, &sa_quit);
3744de51eedSBen Gras 		(void)sigaction(SIGPIPE, &sa, &sa_pipe);
3754de51eedSBen Gras 		sa.sa_handler = SIG_DFL;
3764de51eedSBen Gras 		(void)sigaction(SIGTSTP, &sa, NULL);
3774de51eedSBen Gras 		/*
3784de51eedSBen Gras 		 * Use a pipe to guarantee the order of execution of
3794de51eedSBen Gras 		 * the parent and the child.
3804de51eedSBen Gras 		 */
3814de51eedSBen Gras 		if (pipe(fds) == -1) {
3824de51eedSBen Gras 			warn("pipe failed");
3834de51eedSBen Gras 			goto out;
3844de51eedSBen Gras 		}
3854de51eedSBen Gras 
3864de51eedSBen Gras 		switch (pid = fork()) {
3874de51eedSBen Gras 		case -1:
3884de51eedSBen Gras 			logit("fork failed (%s)", strerror(errno));
3894de51eedSBen Gras 			goto out;
3904de51eedSBen Gras 
3914de51eedSBen Gras 		case 0:	/* Child */
3924de51eedSBen Gras 			(void)close(fds[1]);
3934de51eedSBen Gras 			(void)read(fds[0], &status, 1);
3944de51eedSBen Gras 			(void)close(fds[0]);
3954de51eedSBen Gras 			(void)sigaction(SIGINT, &sa_int, NULL);
3964de51eedSBen Gras 			(void)sigaction(SIGQUIT, &sa_quit, NULL);
3974de51eedSBen Gras 			(void)sigaction(SIGPIPE, &sa_pipe, NULL);
3984de51eedSBen Gras 			break;
3994de51eedSBen Gras 
4004de51eedSBen Gras 		default:
4014de51eedSBen Gras 			sa.sa_handler = SIG_IGN;
4024de51eedSBen Gras 			(void)sigaction(SIGTTOU, &sa, NULL);
4034de51eedSBen Gras 			(void)close(fds[0]);
4044de51eedSBen Gras 			(void)setpgid(pid, pid);
4054de51eedSBen Gras 			(void)tcsetpgrp(STDERR_FILENO, pid);
4064de51eedSBen Gras 			(void)close(fds[1]);
4074de51eedSBen Gras 			(void)sigaction(SIGPIPE, &sa_pipe, NULL);
4084de51eedSBen Gras 			/*
4094de51eedSBen Gras 			 * Parent: wait for the child to terminate
4104de51eedSBen Gras 			 * and call pam_close_session.
4114de51eedSBen Gras 			 */
4124de51eedSBen Gras 			while ((xpid = waitpid(pid, &status, WUNTRACED))
4134de51eedSBen Gras 			    == pid) {
4144de51eedSBen Gras 				if (WIFSTOPPED(status)) {
4154de51eedSBen Gras 					(void)kill(getpid(), SIGSTOP);
4164de51eedSBen Gras 					(void)tcsetpgrp(STDERR_FILENO,
4174de51eedSBen Gras 					    getpgid(pid));
4184de51eedSBen Gras 					(void)kill(pid, SIGCONT);
4194de51eedSBen Gras 					status = 1;
4204de51eedSBen Gras 					continue;
4214de51eedSBen Gras 				}
4224de51eedSBen Gras 				break;
4234de51eedSBen Gras 			}
4244de51eedSBen Gras 
4254de51eedSBen Gras 			(void)tcsetpgrp(STDERR_FILENO, getpgid(0));
4264de51eedSBen Gras 
4274de51eedSBen Gras 			if (xpid == -1) {
4284de51eedSBen Gras 			    logit("Error waiting for pid %d (%s)", pid,
4294de51eedSBen Gras 				strerror(errno));
4304de51eedSBen Gras 			} else if (xpid != pid) {
4314de51eedSBen Gras 			    /* Can't happen. */
4324de51eedSBen Gras 			    logit("Wrong PID: %d != %d", pid, xpid);
4334de51eedSBen Gras 			}
4344de51eedSBen Gras out:
4354de51eedSBen Gras 			pam_err = pam_setcred(pamh, PAM_DELETE_CRED);
4364de51eedSBen Gras 			if (pam_err != PAM_SUCCESS)
4374de51eedSBen Gras 				logit("pam_setcred: %s",
43884d9c625SLionel Sambuc 				    safe_pam_strerror(pamh, pam_err));
4394de51eedSBen Gras 			pam_err = pam_close_session(pamh, 0);
4404de51eedSBen Gras 			if (pam_err != PAM_SUCCESS)
4414de51eedSBen Gras 				logit("pam_close_session: %s",
44284d9c625SLionel Sambuc 				    safe_pam_strerror(pamh, pam_err));
4434de51eedSBen Gras 			(void)pam_end(pamh, pam_err);
4444de51eedSBen Gras 			exit(WEXITSTATUS(status));
4454de51eedSBen Gras 			break;
4464de51eedSBen Gras 		}
4474de51eedSBen Gras 	}
4484de51eedSBen Gras 
4494de51eedSBen Gras 	/*
4504de51eedSBen Gras 	 * The child: starting here, we don't have to care about
4514de51eedSBen Gras 	 * handling PAM issues if we exit, the parent will do the
4524de51eedSBen Gras 	 * job when we exit.
4534de51eedSBen Gras 	 */
4544de51eedSBen Gras #undef PAM_END
4554de51eedSBen Gras #undef ERR_PAM_END
4564de51eedSBen Gras #undef ERRX_PAM_END
4574de51eedSBen Gras 
4584de51eedSBen Gras 	if (!asme) {
4594de51eedSBen Gras 		if (asthem) {
4604de51eedSBen Gras 			char **pamenv;
4614de51eedSBen Gras 
4624de51eedSBen Gras 			p = getenv("TERM");
4634de51eedSBen Gras 			/*
4644de51eedSBen Gras 			 * Create an empty environment
4654de51eedSBen Gras 			 */
4664de51eedSBen Gras 			environ = emalloc(sizeof(char *));
4674de51eedSBen Gras 			environ[0] = NULL;
4684de51eedSBen Gras 
4694de51eedSBen Gras 			/*
4704de51eedSBen Gras 			 * Add PAM environement, before the LOGIN_CAP stuff:
4714de51eedSBen Gras 			 * if the login class is unspecified, we'll get the
4724de51eedSBen Gras 			 * same data from PAM, if -c was used, the specified
4734de51eedSBen Gras 			 * class must override PAM.
4744de51eedSBen Gras 	 		 */
4754de51eedSBen Gras 			if ((pamenv = pam_getenvlist(pamh)) != NULL) {
4764de51eedSBen Gras 				char **envitem;
4774de51eedSBen Gras 
4784de51eedSBen Gras 				/*
4794de51eedSBen Gras 				 * XXX Here FreeBSD filters out
4804de51eedSBen Gras 				 * SHELL, LOGNAME, MAIL, CDPATH, IFS, PATH
4814de51eedSBen Gras 				 * how could we get untrusted data here?
4824de51eedSBen Gras 				 */
4834de51eedSBen Gras 				for (envitem = pamenv; *envitem; envitem++) {
4844de51eedSBen Gras 					if (putenv(*envitem) == -1)
4854de51eedSBen Gras 						free(*envitem);
4864de51eedSBen Gras 				}
4874de51eedSBen Gras 
4884de51eedSBen Gras 				free(pamenv);
4894de51eedSBen Gras 			}
4904de51eedSBen Gras 
4914de51eedSBen Gras 			if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETPATH |
4924de51eedSBen Gras 				LOGIN_SETENV | LOGIN_SETUMASK) == -1)
4934de51eedSBen Gras 				err(EXIT_FAILURE, "setting user context");
4944de51eedSBen Gras 			if (p)
4954de51eedSBen Gras 				(void)setenv("TERM", p, 1);
4964de51eedSBen Gras 		}
4974de51eedSBen Gras 
4984de51eedSBen Gras 		if (asthem || pwd->pw_uid) {
4994de51eedSBen Gras 			(void)setenv("LOGNAME", pwd->pw_name, 1);
5004de51eedSBen Gras 			(void)setenv("USER", pwd->pw_name, 1);
5014de51eedSBen Gras 		}
5024de51eedSBen Gras 		(void)setenv("HOME", pwd->pw_dir, 1);
5034de51eedSBen Gras 		(void)setenv("SHELL", shell, 1);
5044de51eedSBen Gras 	}
5054de51eedSBen Gras 	(void)setenv("SU_FROM", username, 1);
5064de51eedSBen Gras 
5074de51eedSBen Gras 	if (iscsh == YES) {
5084de51eedSBen Gras 		if (fastlogin)
5094de51eedSBen Gras 			*np-- = __UNCONST("-f");
5104de51eedSBen Gras 		if (asme)
5114de51eedSBen Gras 			*np-- = __UNCONST("-m");
5124de51eedSBen Gras 	} else {
5134de51eedSBen Gras 		if (fastlogin)
5144de51eedSBen Gras 			(void)unsetenv("ENV");
5154de51eedSBen Gras 	}
5164de51eedSBen Gras 
5174de51eedSBen Gras 	if (asthem) {
5184de51eedSBen Gras 		avshellbuf[0] = '-';
5194de51eedSBen Gras 		(void)estrlcpy(avshellbuf + 1, avshell, sizeof(avshellbuf) - 1);
5204de51eedSBen Gras 		avshell = avshellbuf;
5214de51eedSBen Gras 	} else if (iscsh == YES) {
5224de51eedSBen Gras 		/* csh strips the first character... */
5234de51eedSBen Gras 		avshellbuf[0] = '_';
5244de51eedSBen Gras 		(void)estrlcpy(avshellbuf + 1, avshell, sizeof(avshellbuf) - 1);
5254de51eedSBen Gras 		avshell = avshellbuf;
5264de51eedSBen Gras 	}
5274de51eedSBen Gras 	*np = __UNCONST(avshell);
5284de51eedSBen Gras 
5294de51eedSBen Gras 	if (ruid != 0)
5304de51eedSBen Gras 		syslog(LOG_NOTICE, "%s to %s%s",
5314de51eedSBen Gras 		    username, pwd->pw_name, ontty());
5324de51eedSBen Gras 
5334de51eedSBen Gras 	/* Raise our priority back to what we had before */
5344de51eedSBen Gras 	(void)setpriority(PRIO_PROCESS, 0, prio);
5354de51eedSBen Gras 
5364de51eedSBen Gras 	/*
5374de51eedSBen Gras 	 * Set user context, except for umask, and the stuff
5384de51eedSBen Gras 	 * we have done before.
5394de51eedSBen Gras 	 */
5404de51eedSBen Gras 	setwhat = LOGIN_SETALL & ~(LOGIN_SETENV | LOGIN_SETUMASK |
5414de51eedSBen Gras 	    LOGIN_SETLOGIN | LOGIN_SETPATH | LOGIN_SETGROUP);
5424de51eedSBen Gras 
5434de51eedSBen Gras 	/*
5444de51eedSBen Gras 	 * Don't touch resource/priority settings if -m has been used
5454de51eedSBen Gras 	 * or -l and -c hasn't, and we're not su'ing to root.
5464de51eedSBen Gras 	 */
5474de51eedSBen Gras 	if ((asme || (!asthem && class == NULL)) && pwd->pw_uid)
5484de51eedSBen Gras 		setwhat &= ~(LOGIN_SETPRIORITY | LOGIN_SETRESOURCES);
5494de51eedSBen Gras 
5504de51eedSBen Gras 	if (setusercontext(lc, pwd, pwd->pw_uid, setwhat) == -1)
5514de51eedSBen Gras 		err(EXIT_FAILURE, "setusercontext");
5524de51eedSBen Gras 
55384d9c625SLionel Sambuc 	if (!asme) {
55484d9c625SLionel Sambuc 		if (asthem) {
55584d9c625SLionel Sambuc 			if (gohome && chdir(pwd->pw_dir) == -1)
55684d9c625SLionel Sambuc 				errx(EXIT_FAILURE, "no directory");
55784d9c625SLionel Sambuc 		}
55884d9c625SLionel Sambuc 	}
55984d9c625SLionel Sambuc 
5604de51eedSBen Gras 	(void)execv(shell, np);
5614de51eedSBen Gras 	err(EXIT_FAILURE, "%s", shell);
5624de51eedSBen Gras done:
56384d9c625SLionel Sambuc 	logit("%s: %s", func, safe_pam_strerror(pamh, pam_err));
5644de51eedSBen Gras 	(void)pam_end(pamh, pam_err);
5654de51eedSBen Gras 	return EXIT_FAILURE;
5664de51eedSBen Gras }
5674de51eedSBen Gras 
5684de51eedSBen Gras static void
logit(const char * fmt,...)5694de51eedSBen Gras logit(const char *fmt, ...)
5704de51eedSBen Gras {
5714de51eedSBen Gras 	va_list ap;
5724de51eedSBen Gras 
5734de51eedSBen Gras 	va_start(ap, fmt);
5744de51eedSBen Gras 	vwarnx(fmt, ap);
57584d9c625SLionel Sambuc 	va_end(ap);
57684d9c625SLionel Sambuc 	va_start(ap, fmt);
5774de51eedSBen Gras 	vsyslog(LOG_ERR, fmt, ap);
5784de51eedSBen Gras 	va_end(ap);
5794de51eedSBen Gras }
580