xref: /illumos-gate/usr/src/cmd/su/su.c (revision 06ccc4b8)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
509295472Sgww  * Common Development and Distribution License (the "License").
609295472Sgww  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22*06ccc4b8SMarek Pospisil  * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
237c478bd9Sstevel@tonic-gate  */
247c478bd9Sstevel@tonic-gate /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
257c478bd9Sstevel@tonic-gate /*	  All Rights Reserved	*/
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate /*	Copyright (c) 1987, 1988 Microsoft Corporation	*/
287c478bd9Sstevel@tonic-gate /*	  All Rights Reserved	*/
297c478bd9Sstevel@tonic-gate 
307c478bd9Sstevel@tonic-gate /*
317c478bd9Sstevel@tonic-gate  *	su [-] [name [arg ...]] change userid, `-' changes environment.
327c478bd9Sstevel@tonic-gate  *	If SULOG is defined, all attempts to su to another user are
337c478bd9Sstevel@tonic-gate  *	logged there.
347c478bd9Sstevel@tonic-gate  *	If CONSOLE is defined, all successful attempts to su to uid 0
357c478bd9Sstevel@tonic-gate  *	are also logged there.
367c478bd9Sstevel@tonic-gate  *
377c478bd9Sstevel@tonic-gate  *	If su cannot create, open, or write entries into SULOG,
387c478bd9Sstevel@tonic-gate  *	(or on the CONSOLE, if defined), the entry will not
397c478bd9Sstevel@tonic-gate  *	be logged -- thus losing a record of the su's attempted
407c478bd9Sstevel@tonic-gate  *	during this period.
417c478bd9Sstevel@tonic-gate  */
427c478bd9Sstevel@tonic-gate 
437c478bd9Sstevel@tonic-gate #include <stdio.h>
447c478bd9Sstevel@tonic-gate #include <sys/types.h>
457c478bd9Sstevel@tonic-gate #include <sys/stat.h>
467c478bd9Sstevel@tonic-gate #include <sys/param.h>
477c478bd9Sstevel@tonic-gate #include <unistd.h>
487c478bd9Sstevel@tonic-gate #include <stdlib.h>
497c478bd9Sstevel@tonic-gate #include <crypt.h>
507c478bd9Sstevel@tonic-gate #include <pwd.h>
517c478bd9Sstevel@tonic-gate #include <shadow.h>
527c478bd9Sstevel@tonic-gate #include <time.h>
537c478bd9Sstevel@tonic-gate #include <signal.h>
547c478bd9Sstevel@tonic-gate #include <fcntl.h>
557c478bd9Sstevel@tonic-gate #include <string.h>
567c478bd9Sstevel@tonic-gate #include <locale.h>
577c478bd9Sstevel@tonic-gate #include <syslog.h>
587c478bd9Sstevel@tonic-gate #include <sys/utsname.h>
5909295472Sgww #include <sys/wait.h>
607c478bd9Sstevel@tonic-gate #include <grp.h>
617c478bd9Sstevel@tonic-gate #include <deflt.h>
627c478bd9Sstevel@tonic-gate #include <limits.h>
637c478bd9Sstevel@tonic-gate #include <errno.h>
647c478bd9Sstevel@tonic-gate #include <stdarg.h>
6509295472Sgww #include <user_attr.h>
6609295472Sgww #include <priv.h>
677c478bd9Sstevel@tonic-gate 
685435d801Sgww #include <bsm/adt.h>
695435d801Sgww #include <bsm/adt_event.h>
705435d801Sgww 
717c478bd9Sstevel@tonic-gate #include <security/pam_appl.h>
727c478bd9Sstevel@tonic-gate 
737c478bd9Sstevel@tonic-gate #define	PATH	"/usr/bin:"		/* path for users other than root */
747c478bd9Sstevel@tonic-gate #define	SUPATH	"/usr/sbin:/usr/bin"	/* path for root */
757c478bd9Sstevel@tonic-gate #define	SUPRMT	"PS1=# "		/* primary prompt for root */
767c478bd9Sstevel@tonic-gate #define	ELIM 128
777c478bd9Sstevel@tonic-gate #define	ROOT 0
787c478bd9Sstevel@tonic-gate #ifdef	DYNAMIC_SU
797c478bd9Sstevel@tonic-gate #define	EMBEDDED_NAME	"embedded_su"
805435d801Sgww #define	DEF_ATTEMPTS	3		/* attempts to change password */
817c478bd9Sstevel@tonic-gate #endif	/* DYNAMIC_SU */
827c478bd9Sstevel@tonic-gate 
835435d801Sgww #define	PW_FALSE	1		/* no password change */
845435d801Sgww #define	PW_TRUE		2		/* successful password change */
855435d801Sgww #define	PW_FAILED	3		/* failed password change */
865435d801Sgww 
877c478bd9Sstevel@tonic-gate /*
887c478bd9Sstevel@tonic-gate  * Intervals to sleep after failed su
897c478bd9Sstevel@tonic-gate  */
907c478bd9Sstevel@tonic-gate #ifndef SLEEPTIME
917c478bd9Sstevel@tonic-gate #define	SLEEPTIME	4
927c478bd9Sstevel@tonic-gate #endif
937c478bd9Sstevel@tonic-gate 
947c478bd9Sstevel@tonic-gate #define	DEFAULT_LOGIN "/etc/default/login"
957c478bd9Sstevel@tonic-gate #define	DEFFILE "/etc/default/su"
967c478bd9Sstevel@tonic-gate 
977c478bd9Sstevel@tonic-gate 
987c478bd9Sstevel@tonic-gate char	*Sulog, *Console;
997c478bd9Sstevel@tonic-gate char	*Path, *Supath;
1007c478bd9Sstevel@tonic-gate 
1017c478bd9Sstevel@tonic-gate /*
1027c478bd9Sstevel@tonic-gate  * Locale variables to be propagated to "su -" environment
1037c478bd9Sstevel@tonic-gate  */
1047c478bd9Sstevel@tonic-gate static char *initvar;
1057c478bd9Sstevel@tonic-gate static char *initenv[] = {
1067c478bd9Sstevel@tonic-gate 	"TZ", "LANG", "LC_CTYPE",
1077c478bd9Sstevel@tonic-gate 	"LC_NUMERIC", "LC_TIME", "LC_COLLATE",
1087c478bd9Sstevel@tonic-gate 	"LC_MONETARY", "LC_MESSAGES", "LC_ALL", 0};
1097c478bd9Sstevel@tonic-gate static char mail[30] = { "MAIL=/var/mail/" };
1107c478bd9Sstevel@tonic-gate 
1117c478bd9Sstevel@tonic-gate static void envalt(void);
1125435d801Sgww static void log(char *, char *, int);
1135435d801Sgww static void to(int);
1147c478bd9Sstevel@tonic-gate 
1157c478bd9Sstevel@tonic-gate enum messagemode { USAGE, ERR, WARN };
1165435d801Sgww static void message(enum messagemode, char *, ...);
1177c478bd9Sstevel@tonic-gate 
1185435d801Sgww static char *alloc_vsprintf(const char *, va_list);
1195435d801Sgww static char *tail(char *);
1207c478bd9Sstevel@tonic-gate 
1215435d801Sgww static void audit_success(int, struct passwd *);
12209295472Sgww static void audit_logout(adt_session_data_t *, au_event_t);
12309295472Sgww static void audit_failure(int, struct passwd *, char *, int);
1247c478bd9Sstevel@tonic-gate 
1257c478bd9Sstevel@tonic-gate #ifdef DYNAMIC_SU
1265435d801Sgww static void validate(char *, int *);
1275435d801Sgww static int legalenvvar(char *);
1287c478bd9Sstevel@tonic-gate static int su_conv(int, struct pam_message **, struct pam_response **, void *);
1297c478bd9Sstevel@tonic-gate static int emb_su_conv(int, struct pam_message **, struct pam_response **,
1307c478bd9Sstevel@tonic-gate     void *);
1315435d801Sgww static void freeresponse(int, struct pam_response **response);
1327c478bd9Sstevel@tonic-gate static struct pam_conv pam_conv = {su_conv, NULL};
1337c478bd9Sstevel@tonic-gate static struct pam_conv emb_pam_conv = {emb_su_conv, NULL};
1345435d801Sgww static void quotemsg(char *, ...);
1357c478bd9Sstevel@tonic-gate static void readinitblock(void);
1365435d801Sgww #else	/* !DYNAMIC_SU */
1375435d801Sgww static void update_audit(struct passwd *pwd);
1387c478bd9Sstevel@tonic-gate #endif	/* DYNAMIC_SU */
1397c478bd9Sstevel@tonic-gate 
1405435d801Sgww static pam_handle_t	*pamh = NULL;	/* Authentication handle */
1417c478bd9Sstevel@tonic-gate struct	passwd pwd;
1427c478bd9Sstevel@tonic-gate char	pwdbuf[1024];			/* buffer for getpwnam_r() */
1437c478bd9Sstevel@tonic-gate char	shell[] = "/usr/bin/sh";	/* default shell */
1447c478bd9Sstevel@tonic-gate char	safe_shell[] = "/sbin/sh";	/* "fallback" shell */
1457c478bd9Sstevel@tonic-gate char	su[PATH_MAX] = "su";		/* arg0 for exec of shprog */
1467c478bd9Sstevel@tonic-gate char	homedir[PATH_MAX] = "HOME=";
1477c478bd9Sstevel@tonic-gate char	logname[20] = "LOGNAME=";
1487c478bd9Sstevel@tonic-gate char	*suprmt = SUPRMT;
1497c478bd9Sstevel@tonic-gate char	termtyp[PATH_MAX] = "TERM=";
1507c478bd9Sstevel@tonic-gate char	*term;
1517c478bd9Sstevel@tonic-gate char	shelltyp[PATH_MAX] = "SHELL=";
1527c478bd9Sstevel@tonic-gate char	*hz;
1537c478bd9Sstevel@tonic-gate char	tznam[PATH_MAX];
1547c478bd9Sstevel@tonic-gate char	hzname[10] = "HZ=";
1557c478bd9Sstevel@tonic-gate char	path[PATH_MAX] = "PATH=";
1567c478bd9Sstevel@tonic-gate char	supath[PATH_MAX] = "PATH=";
1577c478bd9Sstevel@tonic-gate char	*envinit[ELIM];
1587c478bd9Sstevel@tonic-gate extern	char **environ;
1597c478bd9Sstevel@tonic-gate char *ttyn;
1607c478bd9Sstevel@tonic-gate char *username;					/* the invoker */
1617c478bd9Sstevel@tonic-gate static	int	dosyslog = 0;			/* use syslog? */
1627c478bd9Sstevel@tonic-gate char	*myname;
1637c478bd9Sstevel@tonic-gate #ifdef	DYNAMIC_SU
16457c40785SJoep Vesseur int pam_flags = 0;
1657c478bd9Sstevel@tonic-gate boolean_t embedded = B_FALSE;
1667c478bd9Sstevel@tonic-gate #endif	/* DYNAMIC_SU */
1677c478bd9Sstevel@tonic-gate 
1687c478bd9Sstevel@tonic-gate int
1697c478bd9Sstevel@tonic-gate main(int argc, char **argv)
1707c478bd9Sstevel@tonic-gate {
1717c478bd9Sstevel@tonic-gate #ifndef DYNAMIC_SU
1727c478bd9Sstevel@tonic-gate 	struct spwd sp;
1737c478bd9Sstevel@tonic-gate 	char  spbuf[1024];		/* buffer for getspnam_r() */
1747c478bd9Sstevel@tonic-gate 	char *password;
1755435d801Sgww #endif	/* !DYNAMIC_SU */
1767c478bd9Sstevel@tonic-gate 	char *nptr;
1777c478bd9Sstevel@tonic-gate 	char *pshell;
1787c478bd9Sstevel@tonic-gate 	int eflag = 0;
1797c478bd9Sstevel@tonic-gate 	int envidx = 0;
1807c478bd9Sstevel@tonic-gate 	uid_t uid;
1817c478bd9Sstevel@tonic-gate 	gid_t gid;
1827c478bd9Sstevel@tonic-gate 	char *dir, *shprog, *name;
1837c478bd9Sstevel@tonic-gate 	char *ptr;
1847c478bd9Sstevel@tonic-gate 	char *prog = argv[0];
1857c478bd9Sstevel@tonic-gate #ifdef DYNAMIC_SU
1867c478bd9Sstevel@tonic-gate 	int sleeptime = SLEEPTIME;
1877c478bd9Sstevel@tonic-gate 	char **pam_env = 0;
1887c478bd9Sstevel@tonic-gate 	int flags = 0;
1897c478bd9Sstevel@tonic-gate 	int retcode;
1907c478bd9Sstevel@tonic-gate 	int idx = 0;
1917c478bd9Sstevel@tonic-gate #endif	/* DYNAMIC_SU */
1925435d801Sgww 	int pw_change = PW_FALSE;
1937c478bd9Sstevel@tonic-gate 
1947c478bd9Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
1957c478bd9Sstevel@tonic-gate #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
1967c478bd9Sstevel@tonic-gate #define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it wasn't */
1977c478bd9Sstevel@tonic-gate #endif
1987c478bd9Sstevel@tonic-gate 	(void) textdomain(TEXT_DOMAIN);
1997c478bd9Sstevel@tonic-gate 
2007c478bd9Sstevel@tonic-gate 	myname = tail(argv[0]);
2017c478bd9Sstevel@tonic-gate 
2025435d801Sgww #ifdef	DYNAMIC_SU
2037c478bd9Sstevel@tonic-gate 	if (strcmp(myname, EMBEDDED_NAME) == 0) {
2047c478bd9Sstevel@tonic-gate 		embedded = B_TRUE;
2057c478bd9Sstevel@tonic-gate 		setbuf(stdin, NULL);
2067c478bd9Sstevel@tonic-gate 		setbuf(stdout, NULL);
2077c478bd9Sstevel@tonic-gate 		readinitblock();
2087c478bd9Sstevel@tonic-gate 	}
2095435d801Sgww #endif	/* DYNAMIC_SU */
2107c478bd9Sstevel@tonic-gate 
2117c478bd9Sstevel@tonic-gate 	if (argc > 1 && *argv[1] == '-') {
2127c478bd9Sstevel@tonic-gate 		/* Explicitly check for just `-' (no trailing chars) */
2137c478bd9Sstevel@tonic-gate 		if (strlen(argv[1]) == 1) {
2147c478bd9Sstevel@tonic-gate 			eflag++;	/* set eflag if `-' is specified */
2157c478bd9Sstevel@tonic-gate 			argv++;
2167c478bd9Sstevel@tonic-gate 			argc--;
2177c478bd9Sstevel@tonic-gate 		} else {
2187c478bd9Sstevel@tonic-gate 			message(USAGE,
2197c478bd9Sstevel@tonic-gate 			    gettext("Usage: %s [-] [ username [ arg ... ] ]"),
22086ecf0b4SJan Kryl 			    prog);
2217c478bd9Sstevel@tonic-gate 			exit(1);
2227c478bd9Sstevel@tonic-gate 		}
2237c478bd9Sstevel@tonic-gate 	}
2247c478bd9Sstevel@tonic-gate 
2257c478bd9Sstevel@tonic-gate 	/*
2267c478bd9Sstevel@tonic-gate 	 * Determine specified userid, get their password file entry,
2277c478bd9Sstevel@tonic-gate 	 * and set variables to values in password file entry fields.
2287c478bd9Sstevel@tonic-gate 	 */
2297c478bd9Sstevel@tonic-gate 	if (argc > 1) {
2307c478bd9Sstevel@tonic-gate 		/*
2317c478bd9Sstevel@tonic-gate 		 * Usernames can't start with a `-', so we check for that to
2327c478bd9Sstevel@tonic-gate 		 * catch bad usage (like "su - -c ls").
2337c478bd9Sstevel@tonic-gate 		 */
2347c478bd9Sstevel@tonic-gate 		if (*argv[1] == '-') {
2357c478bd9Sstevel@tonic-gate 			message(USAGE,
2367c478bd9Sstevel@tonic-gate 			    gettext("Usage: %s [-] [ username [ arg ... ] ]"),
23786ecf0b4SJan Kryl 			    prog);
2387c478bd9Sstevel@tonic-gate 			exit(1);
2397c478bd9Sstevel@tonic-gate 		} else
2407c478bd9Sstevel@tonic-gate 			nptr = argv[1];	/* use valid command-line username */
2417c478bd9Sstevel@tonic-gate 	} else
2427c478bd9Sstevel@tonic-gate 		nptr = "root";		/* use default "root" username */
2437c478bd9Sstevel@tonic-gate 
2447c478bd9Sstevel@tonic-gate 	if (defopen(DEFFILE) == 0) {
2457c478bd9Sstevel@tonic-gate 
2467c478bd9Sstevel@tonic-gate 		if (Sulog = defread("SULOG="))
2477c478bd9Sstevel@tonic-gate 			Sulog = strdup(Sulog);
2487c478bd9Sstevel@tonic-gate 		if (Console = defread("CONSOLE="))
2497c478bd9Sstevel@tonic-gate 			Console = strdup(Console);
2507c478bd9Sstevel@tonic-gate 		if (Path = defread("PATH="))
2517c478bd9Sstevel@tonic-gate 			Path = strdup(Path);
2527c478bd9Sstevel@tonic-gate 		if (Supath = defread("SUPATH="))
2537c478bd9Sstevel@tonic-gate 			Supath = strdup(Supath);
2547c478bd9Sstevel@tonic-gate 		if ((ptr = defread("SYSLOG=")) != NULL)
2557c478bd9Sstevel@tonic-gate 			dosyslog = strcmp(ptr, "YES") == 0;
2567c478bd9Sstevel@tonic-gate 
2577c478bd9Sstevel@tonic-gate 		(void) defopen(NULL);
2587c478bd9Sstevel@tonic-gate 	}
2597c478bd9Sstevel@tonic-gate 	(void) strlcat(path, (Path) ? Path : PATH, sizeof (path));
2607c478bd9Sstevel@tonic-gate 	(void) strlcat(supath, (Supath) ? Supath : SUPATH, sizeof (supath));
2617c478bd9Sstevel@tonic-gate 
2627c478bd9Sstevel@tonic-gate 	if ((ttyn = ttyname(0)) == NULL)
2637c478bd9Sstevel@tonic-gate 		if ((ttyn = ttyname(1)) == NULL)
2647c478bd9Sstevel@tonic-gate 			if ((ttyn = ttyname(2)) == NULL)
2657c478bd9Sstevel@tonic-gate 				ttyn = "/dev/???";
2667c478bd9Sstevel@tonic-gate 	if ((username = cuserid(NULL)) == NULL)
2677c478bd9Sstevel@tonic-gate 		username = "(null)";
2687c478bd9Sstevel@tonic-gate 
2697c478bd9Sstevel@tonic-gate 	/*
2707c478bd9Sstevel@tonic-gate 	 * if Sulog defined, create SULOG, if it does not exist, with
2717c478bd9Sstevel@tonic-gate 	 * mode read/write user. Change owner and group to root
2727c478bd9Sstevel@tonic-gate 	 */
2737c478bd9Sstevel@tonic-gate 	if (Sulog != NULL) {
2747c478bd9Sstevel@tonic-gate 		(void) close(open(Sulog, O_WRONLY | O_APPEND | O_CREAT,
2757c478bd9Sstevel@tonic-gate 		    (S_IRUSR|S_IWUSR)));
2767c478bd9Sstevel@tonic-gate 		(void) chown(Sulog, (uid_t)ROOT, (gid_t)ROOT);
2777c478bd9Sstevel@tonic-gate 	}
2787c478bd9Sstevel@tonic-gate 
2797c478bd9Sstevel@tonic-gate #ifdef DYNAMIC_SU
2807c478bd9Sstevel@tonic-gate 	if (pam_start(embedded ? EMBEDDED_NAME : "su", nptr,
2817c478bd9Sstevel@tonic-gate 	    embedded ? &emb_pam_conv : &pam_conv, &pamh) != PAM_SUCCESS)
2827c478bd9Sstevel@tonic-gate 		exit(1);
2837c478bd9Sstevel@tonic-gate 	if (pam_set_item(pamh, PAM_TTY, ttyn) != PAM_SUCCESS)
2847c478bd9Sstevel@tonic-gate 		exit(1);
2857c478bd9Sstevel@tonic-gate #endif	/* DYNAMIC_SU */
2867c478bd9Sstevel@tonic-gate 
2877c478bd9Sstevel@tonic-gate 	openlog("su", LOG_CONS, LOG_AUTH);
2887c478bd9Sstevel@tonic-gate 
2897c478bd9Sstevel@tonic-gate #ifdef DYNAMIC_SU
2907c478bd9Sstevel@tonic-gate 
2915435d801Sgww 	/*
2925435d801Sgww 	 * Use the same value of sleeptime and password required that
2935435d801Sgww 	 * login(1) uses.
2945435d801Sgww 	 * This is obtained by reading the file /etc/default/login
2955435d801Sgww 	 * using the def*() functions
2965435d801Sgww 	 */
2975435d801Sgww 	if (defopen(DEFAULT_LOGIN) == 0) {
2985435d801Sgww 		if ((ptr = defread("SLEEPTIME=")) != NULL) {
2995435d801Sgww 			sleeptime = atoi(ptr);
3005435d801Sgww 			if (sleeptime < 0 || sleeptime > 5)
3015435d801Sgww 				sleeptime = SLEEPTIME;
3025435d801Sgww 		}
3035435d801Sgww 
3045435d801Sgww 		if ((ptr = defread("PASSREQ=")) != NULL &&
3055435d801Sgww 		    strcasecmp("YES", ptr) == 0)
30657c40785SJoep Vesseur 			pam_flags |= PAM_DISALLOW_NULL_AUTHTOK;
3075435d801Sgww 
3085435d801Sgww 		(void) defopen((char *)NULL);
3095435d801Sgww 	}
3107c478bd9Sstevel@tonic-gate 	/*
3117c478bd9Sstevel@tonic-gate 	 * Ignore SIGQUIT and SIGINT
3127c478bd9Sstevel@tonic-gate 	 */
3137c478bd9Sstevel@tonic-gate 	(void) signal(SIGQUIT, SIG_IGN);
3147c478bd9Sstevel@tonic-gate 	(void) signal(SIGINT, SIG_IGN);
3157c478bd9Sstevel@tonic-gate 
3167c478bd9Sstevel@tonic-gate 	/* call pam_authenticate() to authenticate the user through PAM */
3177c478bd9Sstevel@tonic-gate 	if (getpwnam_r(nptr, &pwd, pwdbuf, sizeof (pwdbuf)) == NULL)
3187c478bd9Sstevel@tonic-gate 		retcode = PAM_USER_UNKNOWN;
3197c478bd9Sstevel@tonic-gate 	else if ((flags = (getuid() != (uid_t)ROOT)) != 0) {
32057c40785SJoep Vesseur 		retcode = pam_authenticate(pamh, pam_flags);
3217c478bd9Sstevel@tonic-gate 	} else /* root user does not need to authenticate */
3227c478bd9Sstevel@tonic-gate 		retcode = PAM_SUCCESS;
3237c478bd9Sstevel@tonic-gate 
3247c478bd9Sstevel@tonic-gate 	if (retcode != PAM_SUCCESS) {
3257c478bd9Sstevel@tonic-gate 		/*
3265435d801Sgww 		 * 1st step: audit and log the error.
3277c478bd9Sstevel@tonic-gate 		 * 2nd step: sleep.
3287c478bd9Sstevel@tonic-gate 		 * 3rd step: print out message to user.
3297c478bd9Sstevel@tonic-gate 		 */
33009295472Sgww 		/* don't let audit_failure distinguish a role here */
33109295472Sgww 		audit_failure(PW_FALSE, NULL, nptr, retcode);
3327c478bd9Sstevel@tonic-gate 		switch (retcode) {
3337c478bd9Sstevel@tonic-gate 		case PAM_USER_UNKNOWN:
3347c478bd9Sstevel@tonic-gate 			closelog();
3357c478bd9Sstevel@tonic-gate 			(void) sleep(sleeptime);
3367c478bd9Sstevel@tonic-gate 			message(ERR, gettext("Unknown id: %s"), nptr);
3377c478bd9Sstevel@tonic-gate 			break;
3387c478bd9Sstevel@tonic-gate 
3397c478bd9Sstevel@tonic-gate 		case PAM_AUTH_ERR:
3407c478bd9Sstevel@tonic-gate 			if (Sulog != NULL)
3417c478bd9Sstevel@tonic-gate 				log(Sulog, nptr, 0);	/* log entry */
3427c478bd9Sstevel@tonic-gate 			if (dosyslog)
3437c478bd9Sstevel@tonic-gate 				syslog(LOG_CRIT, "'su %s' failed for %s on %s",
3447c478bd9Sstevel@tonic-gate 				    pwd.pw_name, username, ttyn);
3457c478bd9Sstevel@tonic-gate 			closelog();
3467c478bd9Sstevel@tonic-gate 			(void) sleep(sleeptime);
3477c478bd9Sstevel@tonic-gate 			message(ERR, gettext("Sorry"));
3487c478bd9Sstevel@tonic-gate 			break;
3497c478bd9Sstevel@tonic-gate 
3507c478bd9Sstevel@tonic-gate 		case PAM_CONV_ERR:
3517c478bd9Sstevel@tonic-gate 		default:
3527c478bd9Sstevel@tonic-gate 			if (dosyslog)
3537c478bd9Sstevel@tonic-gate 				syslog(LOG_CRIT, "'su %s' failed for %s on %s",
3547c478bd9Sstevel@tonic-gate 				    pwd.pw_name, username, ttyn);
3557c478bd9Sstevel@tonic-gate 			closelog();
3567c478bd9Sstevel@tonic-gate 			(void) sleep(sleeptime);
3577c478bd9Sstevel@tonic-gate 			message(ERR, gettext("Sorry"));
3587c478bd9Sstevel@tonic-gate 			break;
3597c478bd9Sstevel@tonic-gate 		}
3607c478bd9Sstevel@tonic-gate 
3617c478bd9Sstevel@tonic-gate 		(void) signal(SIGQUIT, SIG_DFL);
3627c478bd9Sstevel@tonic-gate 		(void) signal(SIGINT, SIG_DFL);
3637c478bd9Sstevel@tonic-gate 		exit(1);
3647c478bd9Sstevel@tonic-gate 	}
3657c478bd9Sstevel@tonic-gate 	if (flags)
3665435d801Sgww 		validate(username, &pw_change);
3677c478bd9Sstevel@tonic-gate 	if (pam_setcred(pamh, PAM_REINITIALIZE_CRED) != PAM_SUCCESS) {
3687c478bd9Sstevel@tonic-gate 		message(ERR, gettext("unable to set credentials"));
3697c478bd9Sstevel@tonic-gate 		exit(2);
3707c478bd9Sstevel@tonic-gate 	}
3717c478bd9Sstevel@tonic-gate 	if (dosyslog)
37286ecf0b4SJan Kryl 		syslog(pwd.pw_uid == 0 ? LOG_NOTICE : LOG_INFO,
3737c478bd9Sstevel@tonic-gate 		    "'su %s' succeeded for %s on %s",
3747c478bd9Sstevel@tonic-gate 		    pwd.pw_name, username, ttyn);
3757c478bd9Sstevel@tonic-gate 	closelog();
3767c478bd9Sstevel@tonic-gate 	(void) signal(SIGQUIT, SIG_DFL);
3777c478bd9Sstevel@tonic-gate 	(void) signal(SIGINT, SIG_DFL);
3785435d801Sgww #else	/* !DYNAMIC_SU */
3797c478bd9Sstevel@tonic-gate 	if ((getpwnam_r(nptr, &pwd, pwdbuf, sizeof (pwdbuf)) == NULL) ||
3807c478bd9Sstevel@tonic-gate 	    (getspnam_r(nptr, &sp, spbuf, sizeof (spbuf)) == NULL)) {
3817c478bd9Sstevel@tonic-gate 		message(ERR, gettext("Unknown id: %s"), nptr);
38209295472Sgww 		audit_failure(PW_FALSE, NULL, nptr, PAM_USER_UNKNOWN);
3837c478bd9Sstevel@tonic-gate 		closelog();
3847c478bd9Sstevel@tonic-gate 		exit(1);
3857c478bd9Sstevel@tonic-gate 	}
3867c478bd9Sstevel@tonic-gate 
3877c478bd9Sstevel@tonic-gate 	/*
3887c478bd9Sstevel@tonic-gate 	 * Prompt for password if invoking user is not root or
3897c478bd9Sstevel@tonic-gate 	 * if specified(new) user requires a password
3907c478bd9Sstevel@tonic-gate 	 */
3917c478bd9Sstevel@tonic-gate 	if (sp.sp_pwdp[0] == '\0' || getuid() == (uid_t)ROOT)
3927c478bd9Sstevel@tonic-gate 		goto ok;
3937c478bd9Sstevel@tonic-gate 	password = getpass(gettext("Password:"));
3947c478bd9Sstevel@tonic-gate 
3957c478bd9Sstevel@tonic-gate 	if ((strcmp(sp.sp_pwdp, crypt(password, sp.sp_pwdp)) != 0)) {
3967c478bd9Sstevel@tonic-gate 		/* clear password file entry */
3977c478bd9Sstevel@tonic-gate 		(void) memset((void *)spbuf, 0, sizeof (spbuf));
3987c478bd9Sstevel@tonic-gate 		if (Sulog != NULL)
3997c478bd9Sstevel@tonic-gate 			log(Sulog, nptr, 0);    /* log entry */
4007c478bd9Sstevel@tonic-gate 		message(ERR, gettext("Sorry"));
40109295472Sgww 		audit_failure(PW_FALSE, NULL, nptr, PAM_AUTH_ERR);
4027c478bd9Sstevel@tonic-gate 		if (dosyslog)
4037c478bd9Sstevel@tonic-gate 			syslog(LOG_CRIT, "'su %s' failed for %s on %s",
4047c478bd9Sstevel@tonic-gate 			    pwd.pw_name, username, ttyn);
4057c478bd9Sstevel@tonic-gate 		closelog();
4067c478bd9Sstevel@tonic-gate 		exit(2);
4077c478bd9Sstevel@tonic-gate 	}
4087c478bd9Sstevel@tonic-gate 	/* clear password file entry */
4097c478bd9Sstevel@tonic-gate 	(void) memset((void *)spbuf, 0, sizeof (spbuf));
4107c478bd9Sstevel@tonic-gate ok:
4115435d801Sgww 	/* update audit session in a non-pam environment */
4125435d801Sgww 	update_audit(&pwd);
4137c478bd9Sstevel@tonic-gate 	if (dosyslog)
41486ecf0b4SJan Kryl 		syslog(pwd.pw_uid == 0 ? LOG_NOTICE : LOG_INFO,
4157c478bd9Sstevel@tonic-gate 		    "'su %s' succeeded for %s on %s",
4167c478bd9Sstevel@tonic-gate 		    pwd.pw_name, username, ttyn);
4175435d801Sgww #endif	/* DYNAMIC_SU */
4187c478bd9Sstevel@tonic-gate 
4195435d801Sgww 	audit_success(pw_change, &pwd);
4207c478bd9Sstevel@tonic-gate 	uid = pwd.pw_uid;
4217c478bd9Sstevel@tonic-gate 	gid = pwd.pw_gid;
4227c478bd9Sstevel@tonic-gate 	dir = strdup(pwd.pw_dir);
4237c478bd9Sstevel@tonic-gate 	shprog = strdup(pwd.pw_shell);
4247c478bd9Sstevel@tonic-gate 	name = strdup(pwd.pw_name);
4257c478bd9Sstevel@tonic-gate 
4267c478bd9Sstevel@tonic-gate 	if (Sulog != NULL)
4277c478bd9Sstevel@tonic-gate 		log(Sulog, nptr, 1);	/* log entry */
4287c478bd9Sstevel@tonic-gate 
4297c478bd9Sstevel@tonic-gate 	/* set user and group ids to specified user */
4307c478bd9Sstevel@tonic-gate 
4317c478bd9Sstevel@tonic-gate 	/* set the real (and effective) GID */
4327c478bd9Sstevel@tonic-gate 	if (setgid(gid) == -1) {
4337c478bd9Sstevel@tonic-gate 		message(ERR, gettext("Invalid GID"));
4347c478bd9Sstevel@tonic-gate 		exit(2);
4357c478bd9Sstevel@tonic-gate 	}
4367c478bd9Sstevel@tonic-gate 	/* Initialize the supplementary group access list. */
4377c478bd9Sstevel@tonic-gate 	if (!nptr)
4387c478bd9Sstevel@tonic-gate 		exit(2);
4397c478bd9Sstevel@tonic-gate 	if (initgroups(nptr, gid) == -1) {
4407c478bd9Sstevel@tonic-gate 		exit(2);
4417c478bd9Sstevel@tonic-gate 	}
4427c478bd9Sstevel@tonic-gate 	/* set the real (and effective) UID */
4437c478bd9Sstevel@tonic-gate 	if (setuid(uid) == -1) {
4447c478bd9Sstevel@tonic-gate 		message(ERR, gettext("Invalid UID"));
4457c478bd9Sstevel@tonic-gate 		exit(2);
4467c478bd9Sstevel@tonic-gate 	}
4477c478bd9Sstevel@tonic-gate 
4487c478bd9Sstevel@tonic-gate 	/*
4497c478bd9Sstevel@tonic-gate 	 * If new user's shell field is neither NULL nor equal to /usr/bin/sh,
4507c478bd9Sstevel@tonic-gate 	 * set:
4517c478bd9Sstevel@tonic-gate 	 *
4527c478bd9Sstevel@tonic-gate 	 *	pshell = their shell
4537c478bd9Sstevel@tonic-gate 	 *	su = [-]last component of shell's pathname
4547c478bd9Sstevel@tonic-gate 	 *
4557c478bd9Sstevel@tonic-gate 	 * Otherwise, set the shell to /usr/bin/sh and set argv[0] to '[-]su'.
4567c478bd9Sstevel@tonic-gate 	 */
4577c478bd9Sstevel@tonic-gate 	if (shprog[0] != '\0' && strcmp(shell, shprog) != 0) {
4587c478bd9Sstevel@tonic-gate 		char *p;
4597c478bd9Sstevel@tonic-gate 
4607c478bd9Sstevel@tonic-gate 		pshell = shprog;
4617c478bd9Sstevel@tonic-gate 		(void) strcpy(su, eflag ? "-" : "");
4627c478bd9Sstevel@tonic-gate 
4637c478bd9Sstevel@tonic-gate 		if ((p = strrchr(pshell, '/')) != NULL)
4647c478bd9Sstevel@tonic-gate 			(void) strlcat(su, p + 1, sizeof (su));
4657c478bd9Sstevel@tonic-gate 		else
4667c478bd9Sstevel@tonic-gate 			(void) strlcat(su, pshell, sizeof (su));
4677c478bd9Sstevel@tonic-gate 	} else {
4687c478bd9Sstevel@tonic-gate 		pshell = shell;
4697c478bd9Sstevel@tonic-gate 		(void) strcpy(su, eflag ? "-su" : "su");
4707c478bd9Sstevel@tonic-gate 	}
4717c478bd9Sstevel@tonic-gate 
4727c478bd9Sstevel@tonic-gate 	/*
4737c478bd9Sstevel@tonic-gate 	 * set environment variables for new user;
4747c478bd9Sstevel@tonic-gate 	 * arg0 for exec of shprog must now contain `-'
4757c478bd9Sstevel@tonic-gate 	 * so that environment of new user is given
4767c478bd9Sstevel@tonic-gate 	 */
4777c478bd9Sstevel@tonic-gate 	if (eflag) {
4787c478bd9Sstevel@tonic-gate 		int j;
4797c478bd9Sstevel@tonic-gate 		char *var;
4807c478bd9Sstevel@tonic-gate 
4817c478bd9Sstevel@tonic-gate 		if (strlen(dir) == 0) {
4827c478bd9Sstevel@tonic-gate 			(void) strcpy(dir, "/");
4837c478bd9Sstevel@tonic-gate 			message(WARN, gettext("No directory! Using home=/"));
4847c478bd9Sstevel@tonic-gate 		}
4857c478bd9Sstevel@tonic-gate 		(void) strlcat(homedir, dir, sizeof (homedir));
4867c478bd9Sstevel@tonic-gate 		(void) strlcat(logname, name, sizeof (logname));
4877c478bd9Sstevel@tonic-gate 		if (hz = getenv("HZ"))
4887c478bd9Sstevel@tonic-gate 			(void) strlcat(hzname, hz, sizeof (hzname));
4897c478bd9Sstevel@tonic-gate 
4907c478bd9Sstevel@tonic-gate 		(void) strlcat(shelltyp, pshell, sizeof (shelltyp));
4917c478bd9Sstevel@tonic-gate 
4927c478bd9Sstevel@tonic-gate 		if (chdir(dir) < 0) {
4937c478bd9Sstevel@tonic-gate 			message(ERR, gettext("No directory!"));
4947c478bd9Sstevel@tonic-gate 			exit(1);
4957c478bd9Sstevel@tonic-gate 		}
4967c478bd9Sstevel@tonic-gate 		envinit[envidx = 0] = homedir;
4977c478bd9Sstevel@tonic-gate 		envinit[++envidx] = ((uid == (uid_t)ROOT) ? supath : path);
4987c478bd9Sstevel@tonic-gate 		envinit[++envidx] = logname;
4997c478bd9Sstevel@tonic-gate 		envinit[++envidx] = hzname;
5007c478bd9Sstevel@tonic-gate 		if ((term = getenv("TERM")) != NULL) {
5017c478bd9Sstevel@tonic-gate 			(void) strlcat(termtyp, term, sizeof (termtyp));
5027c478bd9Sstevel@tonic-gate 			envinit[++envidx] = termtyp;
5037c478bd9Sstevel@tonic-gate 		}
5047c478bd9Sstevel@tonic-gate 		envinit[++envidx] = shelltyp;
5057c478bd9Sstevel@tonic-gate 
5067c478bd9Sstevel@tonic-gate 		(void) strlcat(mail, name, sizeof (mail));
5077c478bd9Sstevel@tonic-gate 		envinit[++envidx] = mail;
5087c478bd9Sstevel@tonic-gate 
5097c478bd9Sstevel@tonic-gate 		/*
5107c478bd9Sstevel@tonic-gate 		 * Fetch the relevant locale/TZ environment variables from
5117c478bd9Sstevel@tonic-gate 		 * the inherited environment.
5127c478bd9Sstevel@tonic-gate 		 *
5137c478bd9Sstevel@tonic-gate 		 * We have a priority here for setting TZ. If TZ is set in
5147c478bd9Sstevel@tonic-gate 		 * in the inherited environment, that value remains top
5157c478bd9Sstevel@tonic-gate 		 * priority. If the file /etc/default/login has TIMEZONE set,
5167c478bd9Sstevel@tonic-gate 		 * that has second highest priority.
5177c478bd9Sstevel@tonic-gate 		 */
5187c478bd9Sstevel@tonic-gate 		tznam[0] = '\0';
5197c478bd9Sstevel@tonic-gate 		for (j = 0; initenv[j] != 0; j++) {
5207c478bd9Sstevel@tonic-gate 			if (initvar = getenv(initenv[j])) {
5217c478bd9Sstevel@tonic-gate 
5227c478bd9Sstevel@tonic-gate 				/*
5237c478bd9Sstevel@tonic-gate 				 * Skip over values beginning with '/' for
5247c478bd9Sstevel@tonic-gate 				 * security.
5257c478bd9Sstevel@tonic-gate 				 */
5267c478bd9Sstevel@tonic-gate 				if (initvar[0] == '/')  continue;
5277c478bd9Sstevel@tonic-gate 
5287c478bd9Sstevel@tonic-gate 				if (strcmp(initenv[j], "TZ") == 0) {
5297c478bd9Sstevel@tonic-gate 					(void) strcpy(tznam, "TZ=");
5307c478bd9Sstevel@tonic-gate 					(void) strlcat(tznam, initvar,
53186ecf0b4SJan Kryl 					    sizeof (tznam));
5327c478bd9Sstevel@tonic-gate 
5337c478bd9Sstevel@tonic-gate 				} else {
5347c478bd9Sstevel@tonic-gate 					var = (char *)
53586ecf0b4SJan Kryl 					    malloc(strlen(initenv[j])
53686ecf0b4SJan Kryl 					    + strlen(initvar)
53786ecf0b4SJan Kryl 					    + 2);
5387c478bd9Sstevel@tonic-gate 					(void) strcpy(var, initenv[j]);
5397c478bd9Sstevel@tonic-gate 					(void) strcat(var, "=");
5407c478bd9Sstevel@tonic-gate 					(void) strcat(var, initvar);
5417c478bd9Sstevel@tonic-gate 					envinit[++envidx] = var;
5427c478bd9Sstevel@tonic-gate 				}
5437c478bd9Sstevel@tonic-gate 			}
5447c478bd9Sstevel@tonic-gate 		}
5457c478bd9Sstevel@tonic-gate 
5467c478bd9Sstevel@tonic-gate 		/*
5477c478bd9Sstevel@tonic-gate 		 * Check if TZ was found. If not then try to read it from
5487c478bd9Sstevel@tonic-gate 		 * /etc/default/login.
5497c478bd9Sstevel@tonic-gate 		 */
5507c478bd9Sstevel@tonic-gate 		if (tznam[0] == '\0') {
5517c478bd9Sstevel@tonic-gate 			if (defopen(DEFAULT_LOGIN) == 0) {
5527c478bd9Sstevel@tonic-gate 				if (initvar = defread("TIMEZONE=")) {
5537c478bd9Sstevel@tonic-gate 					(void) strcpy(tznam, "TZ=");
5547c478bd9Sstevel@tonic-gate 					(void) strlcat(tznam, initvar,
55586ecf0b4SJan Kryl 					    sizeof (tznam));
5567c478bd9Sstevel@tonic-gate 				}
5577c478bd9Sstevel@tonic-gate 				(void) defopen(NULL);
5587c478bd9Sstevel@tonic-gate 			}
5597c478bd9Sstevel@tonic-gate 		}
5607c478bd9Sstevel@tonic-gate 
5617c478bd9Sstevel@tonic-gate 		if (tznam[0] != '\0')
5627c478bd9Sstevel@tonic-gate 			envinit[++envidx] = tznam;
5637c478bd9Sstevel@tonic-gate 
5647c478bd9Sstevel@tonic-gate #ifdef DYNAMIC_SU
5657c478bd9Sstevel@tonic-gate 		/*
5667c478bd9Sstevel@tonic-gate 		 * set the PAM environment variables -
5677c478bd9Sstevel@tonic-gate 		 * check for legal environment variables
5687c478bd9Sstevel@tonic-gate 		 */
5697c478bd9Sstevel@tonic-gate 		if ((pam_env = pam_getenvlist(pamh)) != 0) {
5707c478bd9Sstevel@tonic-gate 			while (pam_env[idx] != 0) {
5717c478bd9Sstevel@tonic-gate 				if (envidx + 2 < ELIM &&
5727c478bd9Sstevel@tonic-gate 				    legalenvvar(pam_env[idx])) {
5737c478bd9Sstevel@tonic-gate 					envinit[++envidx] = pam_env[idx];
5747c478bd9Sstevel@tonic-gate 				}
5757c478bd9Sstevel@tonic-gate 				idx++;
5767c478bd9Sstevel@tonic-gate 			}
5777c478bd9Sstevel@tonic-gate 		}
5785435d801Sgww #endif	/* DYNAMIC_SU */
5797c478bd9Sstevel@tonic-gate 		envinit[++envidx] = NULL;
5807c478bd9Sstevel@tonic-gate 		environ = envinit;
5817c478bd9Sstevel@tonic-gate 	} else {
5827c478bd9Sstevel@tonic-gate 		char **pp = environ, **qq, *p;
5837c478bd9Sstevel@tonic-gate 
5847c478bd9Sstevel@tonic-gate 		while ((p = *pp) != NULL) {
5857c478bd9Sstevel@tonic-gate 			if (*p == 'L' && p[1] == 'D' && p[2] == '_') {
5867c478bd9Sstevel@tonic-gate 				for (qq = pp; (*qq = qq[1]) != NULL; qq++)
5877c478bd9Sstevel@tonic-gate 					;
5887c478bd9Sstevel@tonic-gate 				/* pp is not advanced */
5897c478bd9Sstevel@tonic-gate 			} else {
5907c478bd9Sstevel@tonic-gate 				pp++;
5917c478bd9Sstevel@tonic-gate 			}
5927c478bd9Sstevel@tonic-gate 		}
5937c478bd9Sstevel@tonic-gate 	}
5947c478bd9Sstevel@tonic-gate 
5957c478bd9Sstevel@tonic-gate #ifdef DYNAMIC_SU
5967c478bd9Sstevel@tonic-gate 	if (pamh)
5977c478bd9Sstevel@tonic-gate 		(void) pam_end(pamh, PAM_SUCCESS);
5985435d801Sgww #endif	/* DYNAMIC_SU */
5997c478bd9Sstevel@tonic-gate 
6007c478bd9Sstevel@tonic-gate 	/*
6017c478bd9Sstevel@tonic-gate 	 * if new user is root:
6027c478bd9Sstevel@tonic-gate 	 *	if CONSOLE defined, log entry there;
6037c478bd9Sstevel@tonic-gate 	 *	if eflag not set, change environment to that of root.
6047c478bd9Sstevel@tonic-gate 	 */
6057c478bd9Sstevel@tonic-gate 	if (uid == (uid_t)ROOT) {
6067c478bd9Sstevel@tonic-gate 		if (Console != NULL)
6077c478bd9Sstevel@tonic-gate 			if (strcmp(ttyn, Console) != 0) {
6087c478bd9Sstevel@tonic-gate 				(void) signal(SIGALRM, to);
6097c478bd9Sstevel@tonic-gate 				(void) alarm(30);
6107c478bd9Sstevel@tonic-gate 				log(Console, nptr, 1);
6117c478bd9Sstevel@tonic-gate 				(void) alarm(0);
6127c478bd9Sstevel@tonic-gate 			}
6137c478bd9Sstevel@tonic-gate 		if (!eflag)
6147c478bd9Sstevel@tonic-gate 			envalt();
6157c478bd9Sstevel@tonic-gate 	}
6167c478bd9Sstevel@tonic-gate 
6177c478bd9Sstevel@tonic-gate 	/*
6187c478bd9Sstevel@tonic-gate 	 * Default for SIGCPU and SIGXFSZ.  Shells inherit
6197c478bd9Sstevel@tonic-gate 	 * signal disposition from parent.  And the
6207c478bd9Sstevel@tonic-gate 	 * shells should have default dispositions for these
6217c478bd9Sstevel@tonic-gate 	 * signals.
6227c478bd9Sstevel@tonic-gate 	 */
6237c478bd9Sstevel@tonic-gate 	(void) signal(SIGXCPU, SIG_DFL);
6247c478bd9Sstevel@tonic-gate 	(void) signal(SIGXFSZ, SIG_DFL);
6257c478bd9Sstevel@tonic-gate 
6267c478bd9Sstevel@tonic-gate #ifdef	DYNAMIC_SU
6277c478bd9Sstevel@tonic-gate 	if (embedded) {
6287c478bd9Sstevel@tonic-gate 		(void) puts("SUCCESS");
6297c478bd9Sstevel@tonic-gate 		/*
6307c478bd9Sstevel@tonic-gate 		 * After this point, we're no longer talking the
6317c478bd9Sstevel@tonic-gate 		 * embedded_su protocol, so turn it off.
6327c478bd9Sstevel@tonic-gate 		 */
6337c478bd9Sstevel@tonic-gate 		embedded = B_FALSE;
6347c478bd9Sstevel@tonic-gate 	}
6357c478bd9Sstevel@tonic-gate #endif	/* DYNAMIC_SU */
6367c478bd9Sstevel@tonic-gate 
6377c478bd9Sstevel@tonic-gate 	/*
6387c478bd9Sstevel@tonic-gate 	 * if additional arguments, exec shell program with array
6397c478bd9Sstevel@tonic-gate 	 * of pointers to arguments:
6407c478bd9Sstevel@tonic-gate 	 *	-> if shell = default, then su = [-]su
6417c478bd9Sstevel@tonic-gate 	 *	-> if shell != default, then su = [-]last component of
6427c478bd9Sstevel@tonic-gate 	 *						shell's pathname
6437c478bd9Sstevel@tonic-gate 	 *
6447c478bd9Sstevel@tonic-gate 	 * if no additional arguments, exec shell with arg0 of su
6457c478bd9Sstevel@tonic-gate 	 * where:
6467c478bd9Sstevel@tonic-gate 	 *	-> if shell = default, then su = [-]su
6477c478bd9Sstevel@tonic-gate 	 *	-> if shell != default, then su = [-]last component of
6487c478bd9Sstevel@tonic-gate 	 *						shell's pathname
6497c478bd9Sstevel@tonic-gate 	 */
6507c478bd9Sstevel@tonic-gate 	if (argc > 2) {
6517c478bd9Sstevel@tonic-gate 		argv[1] = su;
6527c478bd9Sstevel@tonic-gate 		(void) execv(pshell, &argv[1]);
6537c478bd9Sstevel@tonic-gate 	} else
6547c478bd9Sstevel@tonic-gate 		(void) execl(pshell, su, 0);
6557c478bd9Sstevel@tonic-gate 
6567c478bd9Sstevel@tonic-gate 
6577c478bd9Sstevel@tonic-gate 	/*
6587c478bd9Sstevel@tonic-gate 	 * Try to clean up after an administrator who has made a mistake
6597c478bd9Sstevel@tonic-gate 	 * configuring root's shell; if root's shell is other than /sbin/sh,
6607c478bd9Sstevel@tonic-gate 	 * try exec'ing /sbin/sh instead.
6617c478bd9Sstevel@tonic-gate 	 */
6627c478bd9Sstevel@tonic-gate 	if ((uid == (uid_t)ROOT) && (strcmp(name, "root") == 0) &&
6637c478bd9Sstevel@tonic-gate 	    (strcmp(safe_shell, pshell) != 0)) {
6647c478bd9Sstevel@tonic-gate 		message(WARN,
6657c478bd9Sstevel@tonic-gate 		    gettext("No shell %s.  Trying fallback shell %s."),
6667c478bd9Sstevel@tonic-gate 		    pshell, safe_shell);
6677c478bd9Sstevel@tonic-gate 
6687c478bd9Sstevel@tonic-gate 		if (eflag) {
6697c478bd9Sstevel@tonic-gate 			(void) strcpy(su, "-sh");
6707c478bd9Sstevel@tonic-gate 			(void) strlcpy(shelltyp + strlen("SHELL="),
6717c478bd9Sstevel@tonic-gate 			    safe_shell, sizeof (shelltyp) - strlen("SHELL="));
6727c478bd9Sstevel@tonic-gate 		} else {
6737c478bd9Sstevel@tonic-gate 			(void) strcpy(su, "sh");
6747c478bd9Sstevel@tonic-gate 		}
6757c478bd9Sstevel@tonic-gate 
6767c478bd9Sstevel@tonic-gate 		if (argc > 2) {
6777c478bd9Sstevel@tonic-gate 			argv[1] = su;
6787c478bd9Sstevel@tonic-gate 			(void) execv(safe_shell, &argv[1]);
6797c478bd9Sstevel@tonic-gate 		} else {
6807c478bd9Sstevel@tonic-gate 			(void) execl(safe_shell, su, 0);
6817c478bd9Sstevel@tonic-gate 		}
6827c478bd9Sstevel@tonic-gate 		message(ERR, gettext("Couldn't exec fallback shell %s: %s"),
6837c478bd9Sstevel@tonic-gate 		    safe_shell, strerror(errno));
6847c478bd9Sstevel@tonic-gate 	} else {
6857c478bd9Sstevel@tonic-gate 		message(ERR, gettext("No shell"));
6867c478bd9Sstevel@tonic-gate 	}
6877c478bd9Sstevel@tonic-gate 	return (3);
6887c478bd9Sstevel@tonic-gate }
6897c478bd9Sstevel@tonic-gate 
6907c478bd9Sstevel@tonic-gate /*
6917c478bd9Sstevel@tonic-gate  * Environment altering routine -
6927c478bd9Sstevel@tonic-gate  *	This routine is called when a user is su'ing to root
6937c478bd9Sstevel@tonic-gate  *	without specifying the - flag.
6947c478bd9Sstevel@tonic-gate  *	The user's PATH and PS1 variables are reset
6957c478bd9Sstevel@tonic-gate  *	to the correct value for root.
6967c478bd9Sstevel@tonic-gate  *	All of the user's other environment variables retain
6977c478bd9Sstevel@tonic-gate  *	their current values after the su (if they are exported).
6987c478bd9Sstevel@tonic-gate  */
6997c478bd9Sstevel@tonic-gate static void
7007c478bd9Sstevel@tonic-gate envalt(void)
7017c478bd9Sstevel@tonic-gate {
7027c478bd9Sstevel@tonic-gate 	/*
7037c478bd9Sstevel@tonic-gate 	 * If user has PATH variable in their environment, change its value
7047c478bd9Sstevel@tonic-gate 	 *		to /bin:/etc:/usr/bin ;
7057c478bd9Sstevel@tonic-gate 	 * if user does not have PATH variable, add it to the user's
7067c478bd9Sstevel@tonic-gate 	 *		environment;
7077c478bd9Sstevel@tonic-gate 	 * if either of the above fail, an error message is printed.
7087c478bd9Sstevel@tonic-gate 	 */
7097c478bd9Sstevel@tonic-gate 	if (putenv(supath) != 0) {
7107c478bd9Sstevel@tonic-gate 		message(ERR,
7117c478bd9Sstevel@tonic-gate 		    gettext("unable to obtain memory to expand environment"));
7127c478bd9Sstevel@tonic-gate 		exit(4);
7137c478bd9Sstevel@tonic-gate 	}
7147c478bd9Sstevel@tonic-gate 
7157c478bd9Sstevel@tonic-gate 	/*
7167c478bd9Sstevel@tonic-gate 	 * If user has PROMPT variable in their environment, change its value
7177c478bd9Sstevel@tonic-gate 	 *		to # ;
7187c478bd9Sstevel@tonic-gate 	 * if user does not have PROMPT variable, add it to the user's
7197c478bd9Sstevel@tonic-gate 	 *		environment;
7207c478bd9Sstevel@tonic-gate 	 * if either of the above fail, an error message is printed.
7217c478bd9Sstevel@tonic-gate 	 */
7227c478bd9Sstevel@tonic-gate 	if (putenv(suprmt) != 0) {
7237c478bd9Sstevel@tonic-gate 		message(ERR,
7247c478bd9Sstevel@tonic-gate 		    gettext("unable to obtain memory to expand environment"));
7257c478bd9Sstevel@tonic-gate 		exit(4);
7267c478bd9Sstevel@tonic-gate 	}
7277c478bd9Sstevel@tonic-gate }
7287c478bd9Sstevel@tonic-gate 
7297c478bd9Sstevel@tonic-gate /*
7307c478bd9Sstevel@tonic-gate  * Logging routine -
7317c478bd9Sstevel@tonic-gate  *	where = SULOG or CONSOLE
7327c478bd9Sstevel@tonic-gate  *	towho = specified user ( user being su'ed to )
7337c478bd9Sstevel@tonic-gate  *	how = 0 if su attempt failed; 1 if su attempt succeeded
7347c478bd9Sstevel@tonic-gate  */
7357c478bd9Sstevel@tonic-gate static void
7367c478bd9Sstevel@tonic-gate log(char *where, char *towho, int how)
7377c478bd9Sstevel@tonic-gate {
7387c478bd9Sstevel@tonic-gate 	FILE *logf;
7397c478bd9Sstevel@tonic-gate 	time_t now;
7407c478bd9Sstevel@tonic-gate 	struct tm *tmp;
7417c478bd9Sstevel@tonic-gate 
7427c478bd9Sstevel@tonic-gate 	/*
7437c478bd9Sstevel@tonic-gate 	 * open SULOG or CONSOLE - if open fails, return
7447c478bd9Sstevel@tonic-gate 	 */
7457c478bd9Sstevel@tonic-gate 	if ((logf = fopen(where, "a")) == NULL)
7467c478bd9Sstevel@tonic-gate 		return;
7477c478bd9Sstevel@tonic-gate 
7487c478bd9Sstevel@tonic-gate 	now = time(0);
7497c478bd9Sstevel@tonic-gate 	tmp = localtime(&now);
7507c478bd9Sstevel@tonic-gate 
7517c478bd9Sstevel@tonic-gate 	/*
7527c478bd9Sstevel@tonic-gate 	 * write entry into SULOG or onto CONSOLE - if write fails, return
7537c478bd9Sstevel@tonic-gate 	 */
7547c478bd9Sstevel@tonic-gate 	(void) fprintf(logf, "SU %.2d/%.2d %.2d:%.2d %c %s %s-%s\n",
7557c478bd9Sstevel@tonic-gate 	    tmp->tm_mon + 1, tmp->tm_mday, tmp->tm_hour, tmp->tm_min,
7567c478bd9Sstevel@tonic-gate 	    how ? '+' : '-', ttyn + sizeof ("/dev/") - 1, username, towho);
7577c478bd9Sstevel@tonic-gate 
7587c478bd9Sstevel@tonic-gate 	(void) fclose(logf);	/* close SULOG or CONSOLE */
7597c478bd9Sstevel@tonic-gate }
7607c478bd9Sstevel@tonic-gate 
7617c478bd9Sstevel@tonic-gate /*ARGSUSED*/
7627c478bd9Sstevel@tonic-gate static void
7637c478bd9Sstevel@tonic-gate to(int sig)
7647c478bd9Sstevel@tonic-gate {}
7657c478bd9Sstevel@tonic-gate 
7665435d801Sgww /*
7675435d801Sgww  * audit_success - audit successful su
7685435d801Sgww  *
7695435d801Sgww  *	Entry	process audit context established -- i.e., pam_setcred()
7705435d801Sgww  *			or equivalent called.
7715435d801Sgww  *		pw_change = PW_TRUE, if successful password change audit
7725435d801Sgww  *				required.
7735435d801Sgww  *		pwd = passwd entry for new user.
7745435d801Sgww  */
7755435d801Sgww 
7765435d801Sgww static void
7775435d801Sgww audit_success(int pw_change, struct passwd *pwd)
7785435d801Sgww {
7795435d801Sgww 	adt_session_data_t	*ah = NULL;
7805435d801Sgww 	adt_event_data_t	*event;
78109295472Sgww 	au_event_t		event_id = ADT_su;
78209295472Sgww 	userattr_t		*user_entry;
78309295472Sgww 	char			*kva_value;
7845435d801Sgww 
7855435d801Sgww 	if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA) != 0) {
7865435d801Sgww 		syslog(LOG_AUTH | LOG_ALERT,
7875435d801Sgww 		    "adt_start_session(ADT_su): %m");
7885435d801Sgww 		return;
7895435d801Sgww 	}
79009295472Sgww 	if (((user_entry = getusernam(pwd->pw_name)) != NULL) &&
79109295472Sgww 	    ((kva_value = kva_match((kva_t *)user_entry->attr,
79209295472Sgww 	    USERATTR_TYPE_KW)) != NULL) &&
79309295472Sgww 	    ((strcmp(kva_value, USERATTR_TYPE_NONADMIN_KW) == 0) ||
79409295472Sgww 	    (strcmp(kva_value, USERATTR_TYPE_ADMIN_KW) == 0))) {
79509295472Sgww 		event_id = ADT_role_login;
79609295472Sgww 	}
79709295472Sgww 	free_userattr(user_entry);	/* OK to use, checks for NULL */
79809295472Sgww 
7995435d801Sgww 	/* since proc uid/gid not yet updated */
8005435d801Sgww 	if (adt_set_user(ah, pwd->pw_uid, pwd->pw_gid, pwd->pw_uid,
8015435d801Sgww 	    pwd->pw_gid, NULL, ADT_USER) != 0) {
8025435d801Sgww 		syslog(LOG_AUTH | LOG_ERR,
8035435d801Sgww 		    "adt_set_user(ADT_su, ADT_FAILURE): %m");
8045435d801Sgww 	}
80509295472Sgww 	if ((event = adt_alloc_event(ah, event_id)) == NULL) {
8065435d801Sgww 		syslog(LOG_AUTH | LOG_ALERT, "adt_alloc_event(ADT_su): %m");
8075435d801Sgww 	} else if (adt_put_event(event, ADT_SUCCESS, ADT_SUCCESS) != 0) {
8085435d801Sgww 		syslog(LOG_AUTH | LOG_ALERT,
8095435d801Sgww 		    "adt_put_event(ADT_su, ADT_SUCCESS): %m");
8105435d801Sgww 	}
8115435d801Sgww 
8125435d801Sgww 	if (pw_change == PW_TRUE) {
8135435d801Sgww 		/* Also audit password change */
8145435d801Sgww 		adt_free_event(event);
8155435d801Sgww 		if ((event = adt_alloc_event(ah, ADT_passwd)) == NULL) {
8165435d801Sgww 			syslog(LOG_AUTH | LOG_ALERT,
8175435d801Sgww 			    "adt_alloc_event(ADT_passwd): %m");
8185435d801Sgww 		} else if (adt_put_event(event, ADT_SUCCESS,
8195435d801Sgww 		    ADT_SUCCESS) != 0) {
8205435d801Sgww 			syslog(LOG_AUTH | LOG_ALERT,
8215435d801Sgww 			    "adt_put_event(ADT_passwd, ADT_SUCCESS): %m");
8225435d801Sgww 		}
8235435d801Sgww 	}
8245435d801Sgww 	adt_free_event(event);
82509295472Sgww 	/*
82609295472Sgww 	 * The preceeding code is a noop if audit isn't enabled,
82709295472Sgww 	 * but, let's not make a new process when it's not necessary.
82809295472Sgww 	 */
829*06ccc4b8SMarek Pospisil 	if (adt_audit_state(AUC_AUDITING)) {
830a98aba88Sgww 		audit_logout(ah, event_id);	/* fork to catch logout */
83109295472Sgww 	}
83209295472Sgww 	(void) adt_end_session(ah);
83309295472Sgww }
83409295472Sgww 
83509295472Sgww 
83609295472Sgww /*
83709295472Sgww  * audit_logout - audit successful su logout
83809295472Sgww  *
83909295472Sgww  *	Entry	ah = Successful su audit handle
84009295472Sgww  *		event_id = su event ID: ADT_su, ADT_role_login
84109295472Sgww  *
84209295472Sgww  *	Exit	Errors are just ignored and we go on.
84309295472Sgww  *		su logout event written.
84409295472Sgww  */
84509295472Sgww static void
84609295472Sgww audit_logout(adt_session_data_t *ah, au_event_t event_id)
84709295472Sgww {
84809295472Sgww 	adt_event_data_t	*event;
84909295472Sgww 	int			status;		/* wait status */
85009295472Sgww 	pid_t			pid;
85109295472Sgww 	priv_set_t		*priv;		/* waiting process privs */
85209295472Sgww 
853a98aba88Sgww 	if (event_id == ADT_su) {
854a98aba88Sgww 		event_id = ADT_su_logout;
855a98aba88Sgww 	} else {
856a98aba88Sgww 		event_id = ADT_role_logout;
857a98aba88Sgww 	}
858a98aba88Sgww 	if ((event = adt_alloc_event(ah, event_id)) == NULL) {
859a98aba88Sgww 		syslog(LOG_AUTH | LOG_ALERT,
860a98aba88Sgww 		    "adt_alloc_event(ADT_su_logout): %m");
86109295472Sgww 		return;
862a98aba88Sgww 	}
863a98aba88Sgww 	if ((priv = priv_allocset())  == NULL) {
864a98aba88Sgww 		syslog(LOG_AUTH | LOG_ALERT,
865634e26ecSCasper H.S. Dik 		    "su audit_logout: could not alloc basic privs: %m");
866a98aba88Sgww 		adt_free_event(event);
86709295472Sgww 		return;
868a98aba88Sgww 	}
86909295472Sgww 
870a98aba88Sgww 	/*
871a98aba88Sgww 	 * The child returns and continues su processing.
872a98aba88Sgww 	 * The parent's sole job is to wait for child exit, write the
873a98aba88Sgww 	 * logout audit record, and replay the child's exit code.
874a98aba88Sgww 	 */
875a98aba88Sgww 	if ((pid = fork()) == 0) {
876a98aba88Sgww 		/* child */
87709295472Sgww 
87809295472Sgww 		adt_free_event(event);
879a98aba88Sgww 		priv_freeset(priv);
880a98aba88Sgww 		return;
88109295472Sgww 	}
882a98aba88Sgww 	if (pid == -1) {
883a98aba88Sgww 		/* failure */
884a98aba88Sgww 
885a98aba88Sgww 		syslog(LOG_AUTH | LOG_ALERT,
886a98aba88Sgww 		    "su audit_logout: could not fork: %m");
887a98aba88Sgww 		adt_free_event(event);
888a98aba88Sgww 		priv_freeset(priv);
889a98aba88Sgww 		return;
890a98aba88Sgww 	}
891a98aba88Sgww 
892a98aba88Sgww 	/* parent process */
893a98aba88Sgww 
894a98aba88Sgww 	/*
895a98aba88Sgww 	 * When this routine is called, the current working
896a98aba88Sgww 	 * directory is the unknown and there are unknown open
897a98aba88Sgww 	 * files. For the waiting process, change the current
898a98aba88Sgww 	 * directory to root and close open files so that
899a98aba88Sgww 	 * directories can be unmounted if necessary.
900a98aba88Sgww 	 */
901a98aba88Sgww 	if (chdir("/") != 0) {
902a98aba88Sgww 		syslog(LOG_AUTH | LOG_ALERT,
903a98aba88Sgww 		    "su audit_logout: could not chdir /: %m");
904a98aba88Sgww 	}
905a98aba88Sgww 	/*
906a98aba88Sgww 	 * Reduce privileges to just those needed.
907a98aba88Sgww 	 */
908634e26ecSCasper H.S. Dik 	priv_basicset(priv);
909634e26ecSCasper H.S. Dik 	(void) priv_delset(priv, PRIV_PROC_EXEC);
910634e26ecSCasper H.S. Dik 	(void) priv_delset(priv, PRIV_PROC_FORK);
911634e26ecSCasper H.S. Dik 	(void) priv_delset(priv, PRIV_PROC_INFO);
912634e26ecSCasper H.S. Dik 	(void) priv_delset(priv, PRIV_PROC_SESSION);
913634e26ecSCasper H.S. Dik 	(void) priv_delset(priv, PRIV_FILE_LINK_ANY);
914a98aba88Sgww 	if ((priv_addset(priv, PRIV_PROC_AUDIT) != 0) ||
915a98aba88Sgww 	    (setppriv(PRIV_SET, PRIV_PERMITTED, priv) != 0)) {
916a98aba88Sgww 		syslog(LOG_AUTH | LOG_ALERT,
917a98aba88Sgww 		    "su audit_logout: could not reduce privs: %m");
918a98aba88Sgww 	}
919a98aba88Sgww 	closefrom(0);
920a98aba88Sgww 	priv_freeset(priv);
921*06ccc4b8SMarek Pospisil 
922*06ccc4b8SMarek Pospisil 	for (;;) {
923*06ccc4b8SMarek Pospisil 		if (pid != waitpid(pid, &status, WUNTRACED)) {
924*06ccc4b8SMarek Pospisil 			if (errno == ECHILD) {
925*06ccc4b8SMarek Pospisil 				/*
926*06ccc4b8SMarek Pospisil 				 * No existing child with the given pid. Lets
927*06ccc4b8SMarek Pospisil 				 * audit the logout.
928*06ccc4b8SMarek Pospisil 				 */
929*06ccc4b8SMarek Pospisil 				break;
930*06ccc4b8SMarek Pospisil 			}
931*06ccc4b8SMarek Pospisil 			continue;
932*06ccc4b8SMarek Pospisil 		}
933*06ccc4b8SMarek Pospisil 
934*06ccc4b8SMarek Pospisil 		if (WIFEXITED(status) || WIFSIGNALED(status)) {
935*06ccc4b8SMarek Pospisil 			/*
936*06ccc4b8SMarek Pospisil 			 * The child shell exited or was terminated by
937*06ccc4b8SMarek Pospisil 			 * a signal. Lets audit logout.
938*06ccc4b8SMarek Pospisil 			 */
939*06ccc4b8SMarek Pospisil 			break;
940*06ccc4b8SMarek Pospisil 		} else if (WIFSTOPPED(status)) {
941*06ccc4b8SMarek Pospisil 			pid_t pgid;
942*06ccc4b8SMarek Pospisil 			int fd;
943*06ccc4b8SMarek Pospisil 			void (*sg_handler)();
944*06ccc4b8SMarek Pospisil 			/*
945*06ccc4b8SMarek Pospisil 			 * The child shell has been stopped/suspended.
946*06ccc4b8SMarek Pospisil 			 * We need to suspend here as well and pass down
947*06ccc4b8SMarek Pospisil 			 * the control to the parent process.
948*06ccc4b8SMarek Pospisil 			 */
949*06ccc4b8SMarek Pospisil 			sg_handler = signal(WSTOPSIG(status), SIG_DFL);
950*06ccc4b8SMarek Pospisil 			(void) sigsend(P_PGID, getpgrp(), WSTOPSIG(status));
951*06ccc4b8SMarek Pospisil 			/*
952*06ccc4b8SMarek Pospisil 			 * We stop here. When resumed, mark the child
953*06ccc4b8SMarek Pospisil 			 * shell group as foreground process group
954*06ccc4b8SMarek Pospisil 			 * which gives the child shell a control over
955*06ccc4b8SMarek Pospisil 			 * the controlling terminal.
956*06ccc4b8SMarek Pospisil 			 */
957*06ccc4b8SMarek Pospisil 			(void) signal(WSTOPSIG(status), sg_handler);
958*06ccc4b8SMarek Pospisil 
959*06ccc4b8SMarek Pospisil 			pgid = getpgid(pid);
960*06ccc4b8SMarek Pospisil 			if ((fd = open("/dev/tty", O_RDWR)) != -1) {
961*06ccc4b8SMarek Pospisil 				/*
962*06ccc4b8SMarek Pospisil 				 * Pass down the control over the controlling
963*06ccc4b8SMarek Pospisil 				 * terminal iff we are in a foreground process
964*06ccc4b8SMarek Pospisil 				 * group. Otherwise, we are in a background
965*06ccc4b8SMarek Pospisil 				 * process group and the kernel will send
966*06ccc4b8SMarek Pospisil 				 * SIGTTOU signal to stop us (by default).
967*06ccc4b8SMarek Pospisil 				 */
968*06ccc4b8SMarek Pospisil 				if (tcgetpgrp(fd) == getpgrp()) {
969*06ccc4b8SMarek Pospisil 					(void) tcsetpgrp(fd, pgid);
970*06ccc4b8SMarek Pospisil 				}
971*06ccc4b8SMarek Pospisil 				(void) close(fd);
972*06ccc4b8SMarek Pospisil 			}
973*06ccc4b8SMarek Pospisil 			/* Wake up the child shell */
974*06ccc4b8SMarek Pospisil 			(void) sigsend(P_PGID, pgid, SIGCONT);
975*06ccc4b8SMarek Pospisil 		}
976*06ccc4b8SMarek Pospisil 	}
977a98aba88Sgww 
978a98aba88Sgww 	(void) adt_put_event(event, ADT_SUCCESS, ADT_SUCCESS);
979a98aba88Sgww 	adt_free_event(event);
980a98aba88Sgww 	(void) adt_end_session(ah);
981a98aba88Sgww 	exit(WEXITSTATUS(status));
9825435d801Sgww }
9835435d801Sgww 
9845435d801Sgww 
9855435d801Sgww /*
9865435d801Sgww  * audit_failure - audit failed su
9875435d801Sgww  *
9885435d801Sgww  *	Entry	New audit context not set.
9895435d801Sgww  *		pw_change == PW_FALSE, if no password change requested.
9905435d801Sgww  *			     PW_FAILED, if failed password change audit
9915435d801Sgww  *				      required.
99209295472Sgww  *		pwd = NULL, or password entry to use.
99309295472Sgww  *		user = username entered.  Add to record if pwd == NULL.
9945435d801Sgww  *		pamerr = PAM error code; reason for failure.
9955435d801Sgww  */
9965435d801Sgww 
9975435d801Sgww static void
99809295472Sgww audit_failure(int pw_change, struct passwd *pwd, char *user, int pamerr)
9995435d801Sgww {
10005435d801Sgww 	adt_session_data_t	*ah;	/* audit session handle */
10015435d801Sgww 	adt_event_data_t	*event;	/* event to generate */
100209295472Sgww 	au_event_t		event_id = ADT_su;
100309295472Sgww 	userattr_t		*user_entry;
100409295472Sgww 	char			*kva_value;
10055435d801Sgww 
10065435d801Sgww 	if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA) != 0) {
10075435d801Sgww 		syslog(LOG_AUTH | LOG_ALERT,
10085435d801Sgww 		    "adt_start_session(ADT_su, ADT_FAILURE): %m");
10095435d801Sgww 		return;
10105435d801Sgww 	}
101109295472Sgww 
10125435d801Sgww 	if (pwd != NULL) {
10135435d801Sgww 		/* target user authenticated, merge audit state */
10145435d801Sgww 		if (adt_set_user(ah, pwd->pw_uid, pwd->pw_gid, pwd->pw_uid,
10155435d801Sgww 		    pwd->pw_gid, NULL, ADT_UPDATE) != 0) {
10165435d801Sgww 			syslog(LOG_AUTH | LOG_ERR,
10175435d801Sgww 			    "adt_set_user(ADT_su, ADT_FAILURE): %m");
10185435d801Sgww 		}
101909295472Sgww 		if (((user_entry = getusernam(pwd->pw_name)) != NULL) &&
102009295472Sgww 		    ((kva_value = kva_match((kva_t *)user_entry->attr,
102109295472Sgww 		    USERATTR_TYPE_KW)) != NULL) &&
102209295472Sgww 		    ((strcmp(kva_value, USERATTR_TYPE_NONADMIN_KW) == 0) ||
102309295472Sgww 		    (strcmp(kva_value, USERATTR_TYPE_ADMIN_KW) == 0))) {
102409295472Sgww 			event_id = ADT_role_login;
102509295472Sgww 		}
102609295472Sgww 		free_userattr(user_entry);	/* OK to use, checks for NULL */
10275435d801Sgww 	}
102809295472Sgww 	if ((event = adt_alloc_event(ah, event_id)) == NULL) {
10295435d801Sgww 		syslog(LOG_AUTH | LOG_ALERT,
10305435d801Sgww 		    "adt_alloc_event(ADT_su, ADT_FAILURE): %m");
10315435d801Sgww 		return;
103209295472Sgww 	}
103309295472Sgww 	/*
103409295472Sgww 	 * can't tell if user not found is a role, so always use su
103509295472Sgww 	 * If we do pass in pwd when the JNI is fixed, then can
103609295472Sgww 	 * distinguish and set name in both su and role_login
103709295472Sgww 	 */
103809295472Sgww 	if (pwd == NULL) {
103909295472Sgww 		/*
104009295472Sgww 		 * this should be "fail_user" rather than "message"
104109295472Sgww 		 * see adt_xml.  The JNI breaks, so for now we leave
104209295472Sgww 		 * this alone.
104309295472Sgww 		 */
104409295472Sgww 		event->adt_su.message = user;
104509295472Sgww 	}
104609295472Sgww 	if (adt_put_event(event, ADT_FAILURE,
10475435d801Sgww 	    ADT_FAIL_PAM + pamerr) != 0) {
10485435d801Sgww 		syslog(LOG_AUTH | LOG_ALERT,
10495435d801Sgww 		    "adt_put_event(ADT_su(ADT_FAIL, %s): %m",
10505435d801Sgww 		    pam_strerror(pamh, pamerr));
10515435d801Sgww 	}
10525435d801Sgww 	if (pw_change != PW_FALSE) {
10535435d801Sgww 		/* Also audit password change failed */
10545435d801Sgww 		adt_free_event(event);
10555435d801Sgww 		if ((event = adt_alloc_event(ah, ADT_passwd)) == NULL) {
10565435d801Sgww 			syslog(LOG_AUTH | LOG_ALERT,
1057a98aba88Sgww 			    "su: adt_alloc_event(ADT_passwd): %m");
10585435d801Sgww 		} else if (adt_put_event(event, ADT_FAILURE,
10595435d801Sgww 		    ADT_FAIL_PAM + pamerr) != 0) {
10605435d801Sgww 			syslog(LOG_AUTH | LOG_ALERT,
1061a98aba88Sgww 			    "su: adt_put_event(ADT_passwd, ADT_FAILURE): %m");
10625435d801Sgww 		}
10635435d801Sgww 	}
10645435d801Sgww 	adt_free_event(event);
106509295472Sgww 	(void) adt_end_session(ah);
10665435d801Sgww }
10675435d801Sgww 
10687c478bd9Sstevel@tonic-gate #ifdef DYNAMIC_SU
10697c478bd9Sstevel@tonic-gate /*
10707c478bd9Sstevel@tonic-gate  * su_conv():
10717c478bd9Sstevel@tonic-gate  *	This is the conv (conversation) function called from
10727c478bd9Sstevel@tonic-gate  *	a PAM authentication module to print error messages
10737c478bd9Sstevel@tonic-gate  *	or garner information from the user.
10747c478bd9Sstevel@tonic-gate  */
10757c478bd9Sstevel@tonic-gate /*ARGSUSED*/
10767c478bd9Sstevel@tonic-gate static int
10777c478bd9Sstevel@tonic-gate su_conv(int num_msg, struct pam_message **msg, struct pam_response **response,
10787c478bd9Sstevel@tonic-gate     void *appdata_ptr)
10797c478bd9Sstevel@tonic-gate {
10807c478bd9Sstevel@tonic-gate 	struct pam_message	*m;
10817c478bd9Sstevel@tonic-gate 	struct pam_response	*r;
10827c478bd9Sstevel@tonic-gate 	char			*temp;
10837c478bd9Sstevel@tonic-gate 	int			k;
10847c478bd9Sstevel@tonic-gate 	char			respbuf[PAM_MAX_RESP_SIZE];
10857c478bd9Sstevel@tonic-gate 
10867c478bd9Sstevel@tonic-gate 	if (num_msg <= 0)
10877c478bd9Sstevel@tonic-gate 		return (PAM_CONV_ERR);
10887c478bd9Sstevel@tonic-gate 
10897c478bd9Sstevel@tonic-gate 	*response = (struct pam_response *)calloc(num_msg,
10907c478bd9Sstevel@tonic-gate 	    sizeof (struct pam_response));
10917c478bd9Sstevel@tonic-gate 	if (*response == NULL)
10927c478bd9Sstevel@tonic-gate 		return (PAM_BUF_ERR);
10937c478bd9Sstevel@tonic-gate 
10947c478bd9Sstevel@tonic-gate 	k = num_msg;
10957c478bd9Sstevel@tonic-gate 	m = *msg;
10967c478bd9Sstevel@tonic-gate 	r = *response;
10977c478bd9Sstevel@tonic-gate 	while (k--) {
10987c478bd9Sstevel@tonic-gate 
10997c478bd9Sstevel@tonic-gate 		switch (m->msg_style) {
11007c478bd9Sstevel@tonic-gate 
11017c478bd9Sstevel@tonic-gate 		case PAM_PROMPT_ECHO_OFF:
11027c478bd9Sstevel@tonic-gate 			errno = 0;
11037c478bd9Sstevel@tonic-gate 			temp = getpassphrase(m->msg);
11047c478bd9Sstevel@tonic-gate 			if (errno == EINTR)
11057c478bd9Sstevel@tonic-gate 				return (PAM_CONV_ERR);
11067c478bd9Sstevel@tonic-gate 			if (temp != NULL) {
11077c478bd9Sstevel@tonic-gate 				r->resp = strdup(temp);
11087c478bd9Sstevel@tonic-gate 				if (r->resp == NULL) {
11097c478bd9Sstevel@tonic-gate 					freeresponse(num_msg, response);
11107c478bd9Sstevel@tonic-gate 					return (PAM_BUF_ERR);
11117c478bd9Sstevel@tonic-gate 				}
11127c478bd9Sstevel@tonic-gate 			}
11137c478bd9Sstevel@tonic-gate 			break;
11147c478bd9Sstevel@tonic-gate 
11157c478bd9Sstevel@tonic-gate 		case PAM_PROMPT_ECHO_ON:
11167c478bd9Sstevel@tonic-gate 			if (m->msg != NULL) {
11177c478bd9Sstevel@tonic-gate 				(void) fputs(m->msg, stdout);
11187c478bd9Sstevel@tonic-gate 			}
11197c478bd9Sstevel@tonic-gate 
11207c478bd9Sstevel@tonic-gate 			(void) fgets(respbuf, sizeof (respbuf), stdin);
11217c478bd9Sstevel@tonic-gate 			temp = strchr(respbuf, '\n');
11227c478bd9Sstevel@tonic-gate 			if (temp != NULL)
11237c478bd9Sstevel@tonic-gate 				*temp = '\0';
11247c478bd9Sstevel@tonic-gate 
11257c478bd9Sstevel@tonic-gate 			r->resp = strdup(respbuf);
11267c478bd9Sstevel@tonic-gate 			if (r->resp == NULL) {
11277c478bd9Sstevel@tonic-gate 				freeresponse(num_msg, response);
11287c478bd9Sstevel@tonic-gate 				return (PAM_BUF_ERR);
11297c478bd9Sstevel@tonic-gate 			}
11307c478bd9Sstevel@tonic-gate 			break;
11317c478bd9Sstevel@tonic-gate 
11327c478bd9Sstevel@tonic-gate 		case PAM_ERROR_MSG:
11337c478bd9Sstevel@tonic-gate 			if (m->msg != NULL) {
11347c478bd9Sstevel@tonic-gate 				(void) fputs(m->msg, stderr);
11357c478bd9Sstevel@tonic-gate 				(void) fputs("\n", stderr);
11367c478bd9Sstevel@tonic-gate 			}
11377c478bd9Sstevel@tonic-gate 			break;
11387c478bd9Sstevel@tonic-gate 
11397c478bd9Sstevel@tonic-gate 		case PAM_TEXT_INFO:
11407c478bd9Sstevel@tonic-gate 			if (m->msg != NULL) {
11417c478bd9Sstevel@tonic-gate 				(void) fputs(m->msg, stdout);
11427c478bd9Sstevel@tonic-gate 				(void) fputs("\n", stdout);
11437c478bd9Sstevel@tonic-gate 			}
11447c478bd9Sstevel@tonic-gate 			break;
11457c478bd9Sstevel@tonic-gate 
11467c478bd9Sstevel@tonic-gate 		default:
11477c478bd9Sstevel@tonic-gate 			break;
11487c478bd9Sstevel@tonic-gate 		}
11497c478bd9Sstevel@tonic-gate 		m++;
11507c478bd9Sstevel@tonic-gate 		r++;
11517c478bd9Sstevel@tonic-gate 	}
11527c478bd9Sstevel@tonic-gate 	return (PAM_SUCCESS);
11537c478bd9Sstevel@tonic-gate }
11547c478bd9Sstevel@tonic-gate 
11557c478bd9Sstevel@tonic-gate /*
11567c478bd9Sstevel@tonic-gate  * emb_su_conv():
11577c478bd9Sstevel@tonic-gate  *	This is the conv (conversation) function called from
11587c478bd9Sstevel@tonic-gate  *	a PAM authentication module to print error messages
11597c478bd9Sstevel@tonic-gate  *	or garner information from the user.
11607c478bd9Sstevel@tonic-gate  *	This version is used for embedded_su.
11617c478bd9Sstevel@tonic-gate  */
11627c478bd9Sstevel@tonic-gate /*ARGSUSED*/
11637c478bd9Sstevel@tonic-gate static int
11647c478bd9Sstevel@tonic-gate emb_su_conv(int num_msg, struct pam_message **msg,
11657c478bd9Sstevel@tonic-gate     struct pam_response **response, void *appdata_ptr)
11667c478bd9Sstevel@tonic-gate {
11677c478bd9Sstevel@tonic-gate 	struct pam_message	*m;
11687c478bd9Sstevel@tonic-gate 	struct pam_response	*r;
11697c478bd9Sstevel@tonic-gate 	char			*temp;
11707c478bd9Sstevel@tonic-gate 	int			k;
11717c478bd9Sstevel@tonic-gate 	char			respbuf[PAM_MAX_RESP_SIZE];
11727c478bd9Sstevel@tonic-gate 
11737c478bd9Sstevel@tonic-gate 	if (num_msg <= 0)
11747c478bd9Sstevel@tonic-gate 		return (PAM_CONV_ERR);
11757c478bd9Sstevel@tonic-gate 
11767c478bd9Sstevel@tonic-gate 	*response = (struct pam_response *)calloc(num_msg,
11777c478bd9Sstevel@tonic-gate 	    sizeof (struct pam_response));
11787c478bd9Sstevel@tonic-gate 	if (*response == NULL)
11797c478bd9Sstevel@tonic-gate 		return (PAM_BUF_ERR);
11807c478bd9Sstevel@tonic-gate 
11817c478bd9Sstevel@tonic-gate 	/* First, send the prompts */
11827c478bd9Sstevel@tonic-gate 	(void) printf("CONV %d\n", num_msg);
11837c478bd9Sstevel@tonic-gate 	k = num_msg;
11847c478bd9Sstevel@tonic-gate 	m = *msg;
11857c478bd9Sstevel@tonic-gate 	while (k--) {
11867c478bd9Sstevel@tonic-gate 		switch (m->msg_style) {
11877c478bd9Sstevel@tonic-gate 
11887c478bd9Sstevel@tonic-gate 		case PAM_PROMPT_ECHO_OFF:
11897c478bd9Sstevel@tonic-gate 			(void) puts("PAM_PROMPT_ECHO_OFF");
11907c478bd9Sstevel@tonic-gate 			goto msg_common;
11917c478bd9Sstevel@tonic-gate 
11927c478bd9Sstevel@tonic-gate 		case PAM_PROMPT_ECHO_ON:
11937c478bd9Sstevel@tonic-gate 			(void) puts("PAM_PROMPT_ECHO_ON");
11947c478bd9Sstevel@tonic-gate 			goto msg_common;
11957c478bd9Sstevel@tonic-gate 
11967c478bd9Sstevel@tonic-gate 		case PAM_ERROR_MSG:
11977c478bd9Sstevel@tonic-gate 			(void) puts("PAM_ERROR_MSG");
11987c478bd9Sstevel@tonic-gate 			goto msg_common;
11997c478bd9Sstevel@tonic-gate 
12007c478bd9Sstevel@tonic-gate 		case PAM_TEXT_INFO:
12017c478bd9Sstevel@tonic-gate 			(void) puts("PAM_TEXT_INFO");
12027c478bd9Sstevel@tonic-gate 			/* fall through to msg_common */
12037c478bd9Sstevel@tonic-gate msg_common:
12047c478bd9Sstevel@tonic-gate 			if (m->msg == NULL)
12057c478bd9Sstevel@tonic-gate 				quotemsg(NULL);
12067c478bd9Sstevel@tonic-gate 			else
12077c478bd9Sstevel@tonic-gate 				quotemsg("%s", m->msg);
12087c478bd9Sstevel@tonic-gate 			break;
12097c478bd9Sstevel@tonic-gate 
12107c478bd9Sstevel@tonic-gate 		default:
12117c478bd9Sstevel@tonic-gate 			break;
12127c478bd9Sstevel@tonic-gate 		}
12137c478bd9Sstevel@tonic-gate 		m++;
12147c478bd9Sstevel@tonic-gate 	}
12157c478bd9Sstevel@tonic-gate 
12167c478bd9Sstevel@tonic-gate 	/* Next, collect the responses */
12177c478bd9Sstevel@tonic-gate 	k = num_msg;
12187c478bd9Sstevel@tonic-gate 	m = *msg;
12197c478bd9Sstevel@tonic-gate 	r = *response;
12207c478bd9Sstevel@tonic-gate 	while (k--) {
12217c478bd9Sstevel@tonic-gate 
12227c478bd9Sstevel@tonic-gate 		switch (m->msg_style) {
12237c478bd9Sstevel@tonic-gate 
12247c478bd9Sstevel@tonic-gate 		case PAM_PROMPT_ECHO_OFF:
12257c478bd9Sstevel@tonic-gate 		case PAM_PROMPT_ECHO_ON:
12267c478bd9Sstevel@tonic-gate 			(void) fgets(respbuf, sizeof (respbuf), stdin);
12277c478bd9Sstevel@tonic-gate 
12287c478bd9Sstevel@tonic-gate 			temp = strchr(respbuf, '\n');
12297c478bd9Sstevel@tonic-gate 			if (temp != NULL)
12307c478bd9Sstevel@tonic-gate 				*temp = '\0';
12317c478bd9Sstevel@tonic-gate 
12327c478bd9Sstevel@tonic-gate 			r->resp = strdup(respbuf);
12337c478bd9Sstevel@tonic-gate 			if (r->resp == NULL) {
12347c478bd9Sstevel@tonic-gate 				freeresponse(num_msg, response);
12357c478bd9Sstevel@tonic-gate 				return (PAM_BUF_ERR);
12367c478bd9Sstevel@tonic-gate 			}
12377c478bd9Sstevel@tonic-gate 
12387c478bd9Sstevel@tonic-gate 			break;
12397c478bd9Sstevel@tonic-gate 
12407c478bd9Sstevel@tonic-gate 		case PAM_ERROR_MSG:
12417c478bd9Sstevel@tonic-gate 		case PAM_TEXT_INFO:
12427c478bd9Sstevel@tonic-gate 			break;
12437c478bd9Sstevel@tonic-gate 
12447c478bd9Sstevel@tonic-gate 		default:
12457c478bd9Sstevel@tonic-gate 			break;
12467c478bd9Sstevel@tonic-gate 		}
12477c478bd9Sstevel@tonic-gate 		m++;
12487c478bd9Sstevel@tonic-gate 		r++;
12497c478bd9Sstevel@tonic-gate 	}
12507c478bd9Sstevel@tonic-gate 	return (PAM_SUCCESS);
12517c478bd9Sstevel@tonic-gate }
12527c478bd9Sstevel@tonic-gate 
12537c478bd9Sstevel@tonic-gate static void
12547c478bd9Sstevel@tonic-gate freeresponse(int num_msg, struct pam_response **response)
12557c478bd9Sstevel@tonic-gate {
12567c478bd9Sstevel@tonic-gate 	struct pam_response *r;
12577c478bd9Sstevel@tonic-gate 	int i;
12587c478bd9Sstevel@tonic-gate 
12597c478bd9Sstevel@tonic-gate 	/* free responses */
12607c478bd9Sstevel@tonic-gate 	r = *response;
12617c478bd9Sstevel@tonic-gate 	for (i = 0; i < num_msg; i++, r++) {
12627c478bd9Sstevel@tonic-gate 		if (r->resp != NULL) {
12637c478bd9Sstevel@tonic-gate 			/* Zap it in case it's a password */
12647c478bd9Sstevel@tonic-gate 			(void) memset(r->resp, '\0', strlen(r->resp));
12657c478bd9Sstevel@tonic-gate 			free(r->resp);
12667c478bd9Sstevel@tonic-gate 		}
12677c478bd9Sstevel@tonic-gate 	}
12687c478bd9Sstevel@tonic-gate 	free(*response);
12697c478bd9Sstevel@tonic-gate 	*response = NULL;
12707c478bd9Sstevel@tonic-gate }
12717c478bd9Sstevel@tonic-gate 
12727c478bd9Sstevel@tonic-gate /*
12737c478bd9Sstevel@tonic-gate  * Print a message, applying quoting for lines starting with '.'.
12747c478bd9Sstevel@tonic-gate  *
12757c478bd9Sstevel@tonic-gate  * I18n note:  \n is "safe" in all locales, and all locales use
12767c478bd9Sstevel@tonic-gate  * a high-bit-set character to start multibyte sequences, so
12777c478bd9Sstevel@tonic-gate  * scanning for a \n followed by a '.' is safe.
12787c478bd9Sstevel@tonic-gate  */
12797c478bd9Sstevel@tonic-gate static void
12807c478bd9Sstevel@tonic-gate quotemsg(char *fmt, ...)
12817c478bd9Sstevel@tonic-gate {
12827c478bd9Sstevel@tonic-gate 	if (fmt != NULL) {
12837c478bd9Sstevel@tonic-gate 		char *msg;
12847c478bd9Sstevel@tonic-gate 		char *p;
12857c478bd9Sstevel@tonic-gate 		boolean_t bol;
12867c478bd9Sstevel@tonic-gate 		va_list v;
12877c478bd9Sstevel@tonic-gate 
12887c478bd9Sstevel@tonic-gate 		va_start(v, fmt);
12897c478bd9Sstevel@tonic-gate 		msg = alloc_vsprintf(fmt, v);
12907c478bd9Sstevel@tonic-gate 		va_end(v);
12917c478bd9Sstevel@tonic-gate 
12927c478bd9Sstevel@tonic-gate 		bol = B_TRUE;
12937c478bd9Sstevel@tonic-gate 		for (p = msg; *p != '\0'; p++) {
12947c478bd9Sstevel@tonic-gate 			if (bol) {
12957c478bd9Sstevel@tonic-gate 				if (*p == '.')
12967c478bd9Sstevel@tonic-gate 					(void) putchar('.');
12977c478bd9Sstevel@tonic-gate 				bol = B_FALSE;
12987c478bd9Sstevel@tonic-gate 			}
12997c478bd9Sstevel@tonic-gate 			(void) putchar(*p);
13007c478bd9Sstevel@tonic-gate 			if (*p == '\n')
13017c478bd9Sstevel@tonic-gate 				bol = B_TRUE;
13027c478bd9Sstevel@tonic-gate 		}
13037c478bd9Sstevel@tonic-gate 		(void) putchar('\n');
13047c478bd9Sstevel@tonic-gate 		free(msg);
13057c478bd9Sstevel@tonic-gate 	}
13067c478bd9Sstevel@tonic-gate 	(void) putchar('.');
13077c478bd9Sstevel@tonic-gate 	(void) putchar('\n');
13087c478bd9Sstevel@tonic-gate }
13097c478bd9Sstevel@tonic-gate 
13107c478bd9Sstevel@tonic-gate /*
13117c478bd9Sstevel@tonic-gate  * validate - Check that the account is valid for switching to.
13127c478bd9Sstevel@tonic-gate  */
13137c478bd9Sstevel@tonic-gate static void
13145435d801Sgww validate(char *usernam, int *pw_change)
13157c478bd9Sstevel@tonic-gate {
13165435d801Sgww 	int error;
13175435d801Sgww 	int tries;
13185435d801Sgww 
131957c40785SJoep Vesseur 	if ((error = pam_acct_mgmt(pamh, pam_flags)) != PAM_SUCCESS) {
13207c478bd9Sstevel@tonic-gate 		if (Sulog != NULL)
13217c478bd9Sstevel@tonic-gate 			log(Sulog, pwd.pw_name, 0);    /* log entry */
13227c478bd9Sstevel@tonic-gate 		if (error == PAM_NEW_AUTHTOK_REQD) {
13235435d801Sgww 			tries = 0;
13247c478bd9Sstevel@tonic-gate 			message(ERR, gettext("Password for user "
13255435d801Sgww 			    "'%s' has expired"), pwd.pw_name);
1326f00e6aa6Sdarrenm 			while ((error = pam_chauthtok(pamh,
1327f00e6aa6Sdarrenm 			    PAM_CHANGE_EXPIRED_AUTHTOK)) != PAM_SUCCESS) {
13285435d801Sgww 				if ((error == PAM_AUTHTOK_ERR ||
13295435d801Sgww 				    error == PAM_TRY_AGAIN) &&
13305435d801Sgww 				    (tries++ < DEF_ATTEMPTS)) {
13315435d801Sgww 					continue;
13325435d801Sgww 				}
13335435d801Sgww 				message(ERR, gettext("Sorry"));
133409295472Sgww 				audit_failure(PW_FAILED, &pwd, NULL, error);
13355435d801Sgww 				if (dosyslog)
13365435d801Sgww 					syslog(LOG_CRIT,
13375435d801Sgww 					    "'su %s' failed for %s on %s",
13385435d801Sgww 					    pwd.pw_name, usernam, ttyn);
13395435d801Sgww 				closelog();
13405435d801Sgww 				exit(1);
13415435d801Sgww 			}
13425435d801Sgww 			*pw_change = PW_TRUE;
13435435d801Sgww 			return;
13447c478bd9Sstevel@tonic-gate 		} else {
13457c478bd9Sstevel@tonic-gate 			message(ERR, gettext("Sorry"));
134609295472Sgww 			audit_failure(PW_FALSE, &pwd, NULL, error);
13477c478bd9Sstevel@tonic-gate 			if (dosyslog)
134886ecf0b4SJan Kryl 				syslog(LOG_CRIT, "'su %s' failed for %s on %s",
134986ecf0b4SJan Kryl 				    pwd.pw_name, usernam, ttyn);
13507c478bd9Sstevel@tonic-gate 			closelog();
13517c478bd9Sstevel@tonic-gate 			exit(3);
13527c478bd9Sstevel@tonic-gate 		}
13537c478bd9Sstevel@tonic-gate 	}
13547c478bd9Sstevel@tonic-gate }
13557c478bd9Sstevel@tonic-gate 
13567c478bd9Sstevel@tonic-gate static char *illegal[] = {
13577c478bd9Sstevel@tonic-gate 	"SHELL=",
13587c478bd9Sstevel@tonic-gate 	"HOME=",
13597c478bd9Sstevel@tonic-gate 	"LOGNAME=",
13607c478bd9Sstevel@tonic-gate #ifndef NO_MAIL
13617c478bd9Sstevel@tonic-gate 	"MAIL=",
13627c478bd9Sstevel@tonic-gate #endif
13637c478bd9Sstevel@tonic-gate 	"CDPATH=",
13647c478bd9Sstevel@tonic-gate 	"IFS=",
13657c478bd9Sstevel@tonic-gate 	"PATH=",
13667c478bd9Sstevel@tonic-gate 	"TZ=",
13677c478bd9Sstevel@tonic-gate 	"HZ=",
13687c478bd9Sstevel@tonic-gate 	"TERM=",
13697c478bd9Sstevel@tonic-gate 	0
13707c478bd9Sstevel@tonic-gate };
13717c478bd9Sstevel@tonic-gate 
13727c478bd9Sstevel@tonic-gate /*
13737c478bd9Sstevel@tonic-gate  * legalenvvar - can PAM modules insert this environmental variable?
13747c478bd9Sstevel@tonic-gate  */
13757c478bd9Sstevel@tonic-gate 
13767c478bd9Sstevel@tonic-gate static int
13777c478bd9Sstevel@tonic-gate legalenvvar(char *s)
13787c478bd9Sstevel@tonic-gate {
13797c478bd9Sstevel@tonic-gate 	register char **p;
13807c478bd9Sstevel@tonic-gate 
13817c478bd9Sstevel@tonic-gate 	for (p = illegal; *p; p++)
13827c478bd9Sstevel@tonic-gate 		if (strncmp(s, *p, strlen(*p)) == 0)
13837c478bd9Sstevel@tonic-gate 			return (0);
13847c478bd9Sstevel@tonic-gate 
13857c478bd9Sstevel@tonic-gate 	if (s[0] == 'L' && s[1] == 'D' && s[2] == '_')
13867c478bd9Sstevel@tonic-gate 		return (0);
13877c478bd9Sstevel@tonic-gate 
13887c478bd9Sstevel@tonic-gate 	return (1);
13897c478bd9Sstevel@tonic-gate }
13907c478bd9Sstevel@tonic-gate 
13917c478bd9Sstevel@tonic-gate /*
13927c478bd9Sstevel@tonic-gate  * The embedded_su protocol allows the client application to supply
13937c478bd9Sstevel@tonic-gate  * an initialization block terminated by a line with just a "." on it.
13947c478bd9Sstevel@tonic-gate  *
13957c478bd9Sstevel@tonic-gate  * This initialization block is currently unused, reserved for future
13967c478bd9Sstevel@tonic-gate  * expansion.  Ignore it.  This is made very slightly more complex by
13977c478bd9Sstevel@tonic-gate  * the desire to cleanly ignore input lines of any length, while still
13987c478bd9Sstevel@tonic-gate  * correctly detecting a line with just a "." on it.
13997c478bd9Sstevel@tonic-gate  *
14007c478bd9Sstevel@tonic-gate  * I18n note:  It appears that none of the Solaris-supported locales
14017c478bd9Sstevel@tonic-gate  * use 0x0a for any purpose other than newline, so looking for '\n'
14027c478bd9Sstevel@tonic-gate  * seems safe.
14037c478bd9Sstevel@tonic-gate  * All locales use high-bit-set leadin characters for their multi-byte
14047c478bd9Sstevel@tonic-gate  * sequences, so a line consisting solely of ".\n" is what it appears
14057c478bd9Sstevel@tonic-gate  * to be.
14067c478bd9Sstevel@tonic-gate  */
14077c478bd9Sstevel@tonic-gate static void
14087c478bd9Sstevel@tonic-gate readinitblock(void)
14097c478bd9Sstevel@tonic-gate {
14107c478bd9Sstevel@tonic-gate 	char buf[100];
14117c478bd9Sstevel@tonic-gate 	boolean_t bol;
14127c478bd9Sstevel@tonic-gate 
14137c478bd9Sstevel@tonic-gate 	bol = B_TRUE;
14147c478bd9Sstevel@tonic-gate 	for (;;) {
14157c478bd9Sstevel@tonic-gate 		if (fgets(buf, sizeof (buf), stdin) == NULL)
14167c478bd9Sstevel@tonic-gate 			return;
14177c478bd9Sstevel@tonic-gate 		if (bol && strcmp(buf, ".\n") == 0)
14187c478bd9Sstevel@tonic-gate 			return;
14197c478bd9Sstevel@tonic-gate 		bol = (strchr(buf, '\n') != NULL);
14207c478bd9Sstevel@tonic-gate 	}
14217c478bd9Sstevel@tonic-gate }
14225435d801Sgww #else	/* !DYNAMIC_SU */
14235435d801Sgww static void
14245435d801Sgww update_audit(struct passwd *pwd)
14255435d801Sgww {
14265435d801Sgww 	adt_session_data_t	*ah;	/* audit session handle */
14275435d801Sgww 
14285435d801Sgww 	if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA) != 0) {
14295435d801Sgww 		message(ERR, gettext("Sorry"));
14305435d801Sgww 		if (dosyslog)
14315435d801Sgww 			syslog(LOG_CRIT, "'su %s' failed for %s "
14325435d801Sgww 			    "cannot start audit session %m",
14335435d801Sgww 			    pwd->pw_name, username);
14345435d801Sgww 		closelog();
14355435d801Sgww 		exit(2);
14365435d801Sgww 	}
14375435d801Sgww 	if (adt_set_user(ah, pwd->pw_uid, pwd->pw_gid, pwd->pw_uid,
14385435d801Sgww 	    pwd->pw_gid, NULL, ADT_UPDATE) != 0) {
14395435d801Sgww 		if (dosyslog)
14405435d801Sgww 			syslog(LOG_CRIT, "'su %s' failed for %s "
14415435d801Sgww 			    "cannot update audit session %m",
14425435d801Sgww 			    pwd->pw_name, username);
14435435d801Sgww 		closelog();
14445435d801Sgww 		exit(2);
14455435d801Sgww 	}
14465435d801Sgww }
14477c478bd9Sstevel@tonic-gate #endif	/* DYNAMIC_SU */
14487c478bd9Sstevel@tonic-gate 
14497c478bd9Sstevel@tonic-gate /*
14507c478bd9Sstevel@tonic-gate  * Report an error, either a fatal one, a warning, or a usage message,
14517c478bd9Sstevel@tonic-gate  * depending on the mode parameter.
14527c478bd9Sstevel@tonic-gate  */
14537c478bd9Sstevel@tonic-gate /*ARGSUSED*/
14547c478bd9Sstevel@tonic-gate static void
14557c478bd9Sstevel@tonic-gate message(enum messagemode mode, char *fmt, ...)
14567c478bd9Sstevel@tonic-gate {
14577c478bd9Sstevel@tonic-gate 	char *s;
14587c478bd9Sstevel@tonic-gate 	va_list v;
14597c478bd9Sstevel@tonic-gate 
14607c478bd9Sstevel@tonic-gate 	va_start(v, fmt);
14617c478bd9Sstevel@tonic-gate 	s = alloc_vsprintf(fmt, v);
14627c478bd9Sstevel@tonic-gate 	va_end(v);
14637c478bd9Sstevel@tonic-gate 
14647c478bd9Sstevel@tonic-gate #ifdef	DYNAMIC_SU
14657c478bd9Sstevel@tonic-gate 	if (embedded) {
14667c478bd9Sstevel@tonic-gate 		if (mode == WARN) {
14677c478bd9Sstevel@tonic-gate 			(void) printf("CONV 1\n");
14687c478bd9Sstevel@tonic-gate 			(void) printf("PAM_ERROR_MSG\n");
14697c478bd9Sstevel@tonic-gate 		} else { /* ERR, USAGE */
14707c478bd9Sstevel@tonic-gate 			(void) printf("ERROR\n");
14717c478bd9Sstevel@tonic-gate 		}
14727c478bd9Sstevel@tonic-gate 		if (mode == USAGE) {
14737c478bd9Sstevel@tonic-gate 			quotemsg("%s", s);
14747c478bd9Sstevel@tonic-gate 		} else { /* ERR, WARN */
14757c478bd9Sstevel@tonic-gate 			quotemsg("%s: %s", myname, s);
14767c478bd9Sstevel@tonic-gate 		}
14777c478bd9Sstevel@tonic-gate 	} else {
14787c478bd9Sstevel@tonic-gate #endif	/* DYNAMIC_SU */
14797c478bd9Sstevel@tonic-gate 		if (mode == USAGE) {
14807c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, "%s\n", s);
14817c478bd9Sstevel@tonic-gate 		} else { /* ERR, WARN */
14827c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, "%s: %s\n", myname, s);
14837c478bd9Sstevel@tonic-gate 		}
14847c478bd9Sstevel@tonic-gate #ifdef	DYNAMIC_SU
14857c478bd9Sstevel@tonic-gate 	}
14867c478bd9Sstevel@tonic-gate #endif	/* DYNAMIC_SU */
14877c478bd9Sstevel@tonic-gate 
14887c478bd9Sstevel@tonic-gate 	free(s);
14897c478bd9Sstevel@tonic-gate }
14907c478bd9Sstevel@tonic-gate 
14917c478bd9Sstevel@tonic-gate /*
14927c478bd9Sstevel@tonic-gate  * Return a pointer to the last path component of a.
14937c478bd9Sstevel@tonic-gate  */
14947c478bd9Sstevel@tonic-gate static char *
14957c478bd9Sstevel@tonic-gate tail(char *a)
14967c478bd9Sstevel@tonic-gate {
14977c478bd9Sstevel@tonic-gate 	char *p;
14987c478bd9Sstevel@tonic-gate 
14997c478bd9Sstevel@tonic-gate 	p = strrchr(a, '/');
15007c478bd9Sstevel@tonic-gate 	if (p == NULL)
15017c478bd9Sstevel@tonic-gate 		p = a;
15027c478bd9Sstevel@tonic-gate 	else
15037c478bd9Sstevel@tonic-gate 		p++;	/* step over the '/' */
15047c478bd9Sstevel@tonic-gate 
15057c478bd9Sstevel@tonic-gate 	return (p);
15067c478bd9Sstevel@tonic-gate }
15077c478bd9Sstevel@tonic-gate 
15087c478bd9Sstevel@tonic-gate static char *
15097c478bd9Sstevel@tonic-gate alloc_vsprintf(const char *fmt, va_list ap1)
15107c478bd9Sstevel@tonic-gate {
15117c478bd9Sstevel@tonic-gate 	va_list ap2;
15127c478bd9Sstevel@tonic-gate 	int n;
15137c478bd9Sstevel@tonic-gate 	char buf[1];
15147c478bd9Sstevel@tonic-gate 	char *s;
15157c478bd9Sstevel@tonic-gate 
15167c478bd9Sstevel@tonic-gate 	/*
15177c478bd9Sstevel@tonic-gate 	 * We need to scan the argument list twice.  Save off a copy
15187c478bd9Sstevel@tonic-gate 	 * of the argument list pointer(s) for the second pass.  Note that
15197c478bd9Sstevel@tonic-gate 	 * we are responsible for va_end'ing our copy.
15207c478bd9Sstevel@tonic-gate 	 */
15217c478bd9Sstevel@tonic-gate 	va_copy(ap2, ap1);
15227c478bd9Sstevel@tonic-gate 
15237c478bd9Sstevel@tonic-gate 	/*
15247c478bd9Sstevel@tonic-gate 	 * vsnprintf into a dummy to get a length.  One might
15257c478bd9Sstevel@tonic-gate 	 * think that passing 0 as the length to snprintf would
15267c478bd9Sstevel@tonic-gate 	 * do what we want, but it's defined not to.
15277c478bd9Sstevel@tonic-gate 	 *
15287c478bd9Sstevel@tonic-gate 	 * Perhaps we should sprintf into a 100 character buffer
15297c478bd9Sstevel@tonic-gate 	 * or something like that, to avoid two calls to snprintf
15307c478bd9Sstevel@tonic-gate 	 * in most cases.
15317c478bd9Sstevel@tonic-gate 	 */
15327c478bd9Sstevel@tonic-gate 	n = vsnprintf(buf, sizeof (buf), fmt, ap2);
15337c478bd9Sstevel@tonic-gate 	va_end(ap2);
15347c478bd9Sstevel@tonic-gate 
15357c478bd9Sstevel@tonic-gate 	/*
15367c478bd9Sstevel@tonic-gate 	 * Allocate an appropriately-sized buffer.
15377c478bd9Sstevel@tonic-gate 	 */
15387c478bd9Sstevel@tonic-gate 	s = malloc(n + 1);
15397c478bd9Sstevel@tonic-gate 	if (s == NULL) {
15407c478bd9Sstevel@tonic-gate 		perror("malloc");
15417c478bd9Sstevel@tonic-gate 		exit(4);
15427c478bd9Sstevel@tonic-gate 	}
15437c478bd9Sstevel@tonic-gate 
15447c478bd9Sstevel@tonic-gate 	(void) vsnprintf(s, n+1, fmt, ap1);
15457c478bd9Sstevel@tonic-gate 
15467c478bd9Sstevel@tonic-gate 	return (s);
15477c478bd9Sstevel@tonic-gate }
1548