xref: /illumos-gate/usr/src/cmd/su/su.c (revision 7c478bd9)
1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * CDDL HEADER START
3*7c478bd9Sstevel@tonic-gate  *
4*7c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*7c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*7c478bd9Sstevel@tonic-gate  * with the License.
8*7c478bd9Sstevel@tonic-gate  *
9*7c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*7c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*7c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*7c478bd9Sstevel@tonic-gate  * and limitations under the License.
13*7c478bd9Sstevel@tonic-gate  *
14*7c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*7c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*7c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*7c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*7c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*7c478bd9Sstevel@tonic-gate  *
20*7c478bd9Sstevel@tonic-gate  * CDDL HEADER END
21*7c478bd9Sstevel@tonic-gate  */
22*7c478bd9Sstevel@tonic-gate /*
23*7c478bd9Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*7c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
25*7c478bd9Sstevel@tonic-gate  */
26*7c478bd9Sstevel@tonic-gate 
27*7c478bd9Sstevel@tonic-gate /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28*7c478bd9Sstevel@tonic-gate /*	  All Rights Reserved	*/
29*7c478bd9Sstevel@tonic-gate 
30*7c478bd9Sstevel@tonic-gate /*	Copyright (c) 1987, 1988 Microsoft Corporation	*/
31*7c478bd9Sstevel@tonic-gate /*	  All Rights Reserved	*/
32*7c478bd9Sstevel@tonic-gate 
33*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
34*7c478bd9Sstevel@tonic-gate 
35*7c478bd9Sstevel@tonic-gate /*
36*7c478bd9Sstevel@tonic-gate  *	su [-] [name [arg ...]] change userid, `-' changes environment.
37*7c478bd9Sstevel@tonic-gate  *	If SULOG is defined, all attempts to su to another user are
38*7c478bd9Sstevel@tonic-gate  *	logged there.
39*7c478bd9Sstevel@tonic-gate  *	If CONSOLE is defined, all successful attempts to su to uid 0
40*7c478bd9Sstevel@tonic-gate  *	are also logged there.
41*7c478bd9Sstevel@tonic-gate  *
42*7c478bd9Sstevel@tonic-gate  *	If su cannot create, open, or write entries into SULOG,
43*7c478bd9Sstevel@tonic-gate  *	(or on the CONSOLE, if defined), the entry will not
44*7c478bd9Sstevel@tonic-gate  *	be logged -- thus losing a record of the su's attempted
45*7c478bd9Sstevel@tonic-gate  *	during this period.
46*7c478bd9Sstevel@tonic-gate  */
47*7c478bd9Sstevel@tonic-gate 
48*7c478bd9Sstevel@tonic-gate #include <stdio.h>
49*7c478bd9Sstevel@tonic-gate #include <sys/types.h>
50*7c478bd9Sstevel@tonic-gate #include <sys/stat.h>
51*7c478bd9Sstevel@tonic-gate #include <sys/param.h>
52*7c478bd9Sstevel@tonic-gate #include <unistd.h>
53*7c478bd9Sstevel@tonic-gate #include <stdlib.h>
54*7c478bd9Sstevel@tonic-gate #include <crypt.h>
55*7c478bd9Sstevel@tonic-gate #include <pwd.h>
56*7c478bd9Sstevel@tonic-gate #include <shadow.h>
57*7c478bd9Sstevel@tonic-gate #include <time.h>
58*7c478bd9Sstevel@tonic-gate #include <signal.h>
59*7c478bd9Sstevel@tonic-gate #include <fcntl.h>
60*7c478bd9Sstevel@tonic-gate #include <string.h>
61*7c478bd9Sstevel@tonic-gate #include <locale.h>
62*7c478bd9Sstevel@tonic-gate #include <syslog.h>
63*7c478bd9Sstevel@tonic-gate #include <sys/utsname.h>
64*7c478bd9Sstevel@tonic-gate #include <grp.h>
65*7c478bd9Sstevel@tonic-gate #include <deflt.h>
66*7c478bd9Sstevel@tonic-gate #include <limits.h>
67*7c478bd9Sstevel@tonic-gate #include <errno.h>
68*7c478bd9Sstevel@tonic-gate #include <stdarg.h>
69*7c478bd9Sstevel@tonic-gate 
70*7c478bd9Sstevel@tonic-gate #ifdef DYNAMIC_SU
71*7c478bd9Sstevel@tonic-gate #include <security/pam_appl.h>
72*7c478bd9Sstevel@tonic-gate #endif
73*7c478bd9Sstevel@tonic-gate 
74*7c478bd9Sstevel@tonic-gate #define	PATH	"/usr/bin:"		/* path for users other than root */
75*7c478bd9Sstevel@tonic-gate #define	SUPATH	"/usr/sbin:/usr/bin"	/* path for root */
76*7c478bd9Sstevel@tonic-gate #define	SUPRMT	"PS1=# "		/* primary prompt for root */
77*7c478bd9Sstevel@tonic-gate #define	ELIM 128
78*7c478bd9Sstevel@tonic-gate #define	ROOT 0
79*7c478bd9Sstevel@tonic-gate #ifdef	DYNAMIC_SU
80*7c478bd9Sstevel@tonic-gate #define	EMBEDDED_NAME	"embedded_su"
81*7c478bd9Sstevel@tonic-gate #endif	/* DYNAMIC_SU */
82*7c478bd9Sstevel@tonic-gate 
83*7c478bd9Sstevel@tonic-gate /*
84*7c478bd9Sstevel@tonic-gate  * Intervals to sleep after failed su
85*7c478bd9Sstevel@tonic-gate  */
86*7c478bd9Sstevel@tonic-gate #ifndef SLEEPTIME
87*7c478bd9Sstevel@tonic-gate #define	SLEEPTIME	4
88*7c478bd9Sstevel@tonic-gate #endif
89*7c478bd9Sstevel@tonic-gate 
90*7c478bd9Sstevel@tonic-gate #define	DEFAULT_LOGIN "/etc/default/login"
91*7c478bd9Sstevel@tonic-gate #define	DEFFILE "/etc/default/su"
92*7c478bd9Sstevel@tonic-gate 
93*7c478bd9Sstevel@tonic-gate 
94*7c478bd9Sstevel@tonic-gate char	*Sulog, *Console;
95*7c478bd9Sstevel@tonic-gate char	*Path, *Supath;
96*7c478bd9Sstevel@tonic-gate 
97*7c478bd9Sstevel@tonic-gate /*
98*7c478bd9Sstevel@tonic-gate  * Locale variables to be propagated to "su -" environment
99*7c478bd9Sstevel@tonic-gate  */
100*7c478bd9Sstevel@tonic-gate static char *initvar;
101*7c478bd9Sstevel@tonic-gate static char *initenv[] = {
102*7c478bd9Sstevel@tonic-gate 	"TZ", "LANG", "LC_CTYPE",
103*7c478bd9Sstevel@tonic-gate 	"LC_NUMERIC", "LC_TIME", "LC_COLLATE",
104*7c478bd9Sstevel@tonic-gate 	"LC_MONETARY", "LC_MESSAGES", "LC_ALL", 0};
105*7c478bd9Sstevel@tonic-gate static char mail[30] = { "MAIL=/var/mail/" };
106*7c478bd9Sstevel@tonic-gate 
107*7c478bd9Sstevel@tonic-gate static void envalt(void);
108*7c478bd9Sstevel@tonic-gate static void log(char *where, char *towho, int how);
109*7c478bd9Sstevel@tonic-gate static void to(int sig);
110*7c478bd9Sstevel@tonic-gate 
111*7c478bd9Sstevel@tonic-gate enum messagemode { USAGE, ERR, WARN };
112*7c478bd9Sstevel@tonic-gate static void message(enum messagemode mode, char *fmt, ...);
113*7c478bd9Sstevel@tonic-gate 
114*7c478bd9Sstevel@tonic-gate static char *alloc_vsprintf(const char *fmt, va_list ap1);
115*7c478bd9Sstevel@tonic-gate static char *tail(char *a);
116*7c478bd9Sstevel@tonic-gate 
117*7c478bd9Sstevel@tonic-gate /*
118*7c478bd9Sstevel@tonic-gate  * Obsolescent Audit hooks for su command
119*7c478bd9Sstevel@tonic-gate  */
120*7c478bd9Sstevel@tonic-gate extern void audit_su_bad_authentication(void);
121*7c478bd9Sstevel@tonic-gate extern void audit_su_bad_username(void);
122*7c478bd9Sstevel@tonic-gate extern void audit_su_init_info(char *, char *);
123*7c478bd9Sstevel@tonic-gate extern void audit_su_reset_ai(void);
124*7c478bd9Sstevel@tonic-gate extern void audit_su_success(void);
125*7c478bd9Sstevel@tonic-gate extern void audit_su_unknown_failure(void);
126*7c478bd9Sstevel@tonic-gate 
127*7c478bd9Sstevel@tonic-gate #ifdef DYNAMIC_SU
128*7c478bd9Sstevel@tonic-gate static void validate(char *usernam);
129*7c478bd9Sstevel@tonic-gate static int legalenvvar(char *s);
130*7c478bd9Sstevel@tonic-gate static int su_conv(int, struct pam_message **, struct pam_response **, void *);
131*7c478bd9Sstevel@tonic-gate static int emb_su_conv(int, struct pam_message **, struct pam_response **,
132*7c478bd9Sstevel@tonic-gate     void *);
133*7c478bd9Sstevel@tonic-gate static void freeresponse(int num_msg, struct pam_response **response);
134*7c478bd9Sstevel@tonic-gate static struct pam_conv pam_conv = {su_conv, NULL};
135*7c478bd9Sstevel@tonic-gate static struct pam_conv emb_pam_conv = {emb_su_conv, NULL};
136*7c478bd9Sstevel@tonic-gate static pam_handle_t	*pamh;		/* Authentication handle */
137*7c478bd9Sstevel@tonic-gate static void quotemsg(char *fmt, ...);
138*7c478bd9Sstevel@tonic-gate static void readinitblock(void);
139*7c478bd9Sstevel@tonic-gate #endif	/* DYNAMIC_SU */
140*7c478bd9Sstevel@tonic-gate 
141*7c478bd9Sstevel@tonic-gate struct	passwd pwd;
142*7c478bd9Sstevel@tonic-gate char	pwdbuf[1024];			/* buffer for getpwnam_r() */
143*7c478bd9Sstevel@tonic-gate char	shell[] = "/usr/bin/sh";	/* default shell */
144*7c478bd9Sstevel@tonic-gate char	safe_shell[] = "/sbin/sh";	/* "fallback" shell */
145*7c478bd9Sstevel@tonic-gate char	su[PATH_MAX] = "su";		/* arg0 for exec of shprog */
146*7c478bd9Sstevel@tonic-gate char	homedir[PATH_MAX] = "HOME=";
147*7c478bd9Sstevel@tonic-gate char	logname[20] = "LOGNAME=";
148*7c478bd9Sstevel@tonic-gate char	*suprmt = SUPRMT;
149*7c478bd9Sstevel@tonic-gate char	termtyp[PATH_MAX] = "TERM=";
150*7c478bd9Sstevel@tonic-gate char	*term;
151*7c478bd9Sstevel@tonic-gate char	shelltyp[PATH_MAX] = "SHELL=";
152*7c478bd9Sstevel@tonic-gate char	*hz;
153*7c478bd9Sstevel@tonic-gate char	tznam[PATH_MAX];
154*7c478bd9Sstevel@tonic-gate char	hzname[10] = "HZ=";
155*7c478bd9Sstevel@tonic-gate char	path[PATH_MAX] = "PATH=";
156*7c478bd9Sstevel@tonic-gate char	supath[PATH_MAX] = "PATH=";
157*7c478bd9Sstevel@tonic-gate char	*envinit[ELIM];
158*7c478bd9Sstevel@tonic-gate extern	char **environ;
159*7c478bd9Sstevel@tonic-gate char *ttyn;
160*7c478bd9Sstevel@tonic-gate char *username;					/* the invoker */
161*7c478bd9Sstevel@tonic-gate static	int	dosyslog = 0;			/* use syslog? */
162*7c478bd9Sstevel@tonic-gate char	*myname;
163*7c478bd9Sstevel@tonic-gate #ifdef	DYNAMIC_SU
164*7c478bd9Sstevel@tonic-gate boolean_t embedded = B_FALSE;
165*7c478bd9Sstevel@tonic-gate #endif	/* DYNAMIC_SU */
166*7c478bd9Sstevel@tonic-gate 
167*7c478bd9Sstevel@tonic-gate int
168*7c478bd9Sstevel@tonic-gate main(int argc, char **argv)
169*7c478bd9Sstevel@tonic-gate {
170*7c478bd9Sstevel@tonic-gate #ifndef DYNAMIC_SU
171*7c478bd9Sstevel@tonic-gate 	struct spwd sp;
172*7c478bd9Sstevel@tonic-gate 	char  spbuf[1024];		/* buffer for getspnam_r() */
173*7c478bd9Sstevel@tonic-gate 	char *password;
174*7c478bd9Sstevel@tonic-gate #endif
175*7c478bd9Sstevel@tonic-gate 	char *nptr;
176*7c478bd9Sstevel@tonic-gate 	char *pshell;
177*7c478bd9Sstevel@tonic-gate 	int eflag = 0;
178*7c478bd9Sstevel@tonic-gate 	int envidx = 0;
179*7c478bd9Sstevel@tonic-gate 	uid_t uid;
180*7c478bd9Sstevel@tonic-gate 	gid_t gid;
181*7c478bd9Sstevel@tonic-gate 	char *dir, *shprog, *name;
182*7c478bd9Sstevel@tonic-gate 	char *ptr;
183*7c478bd9Sstevel@tonic-gate 	char *prog = argv[0];
184*7c478bd9Sstevel@tonic-gate #ifdef DYNAMIC_SU
185*7c478bd9Sstevel@tonic-gate 	int sleeptime = SLEEPTIME;
186*7c478bd9Sstevel@tonic-gate 	char **pam_env = 0;
187*7c478bd9Sstevel@tonic-gate 	int flags = 0;
188*7c478bd9Sstevel@tonic-gate 	int retcode;
189*7c478bd9Sstevel@tonic-gate 	int idx = 0;
190*7c478bd9Sstevel@tonic-gate #endif	/* DYNAMIC_SU */
191*7c478bd9Sstevel@tonic-gate 
192*7c478bd9Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
193*7c478bd9Sstevel@tonic-gate #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
194*7c478bd9Sstevel@tonic-gate #define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it wasn't */
195*7c478bd9Sstevel@tonic-gate #endif
196*7c478bd9Sstevel@tonic-gate 	(void) textdomain(TEXT_DOMAIN);
197*7c478bd9Sstevel@tonic-gate 
198*7c478bd9Sstevel@tonic-gate 	myname = tail(argv[0]);
199*7c478bd9Sstevel@tonic-gate 
200*7c478bd9Sstevel@tonic-gate #if	defined(DYNAMIC_SU)
201*7c478bd9Sstevel@tonic-gate 	if (strcmp(myname, EMBEDDED_NAME) == 0) {
202*7c478bd9Sstevel@tonic-gate 		embedded = B_TRUE;
203*7c478bd9Sstevel@tonic-gate 		setbuf(stdin, NULL);
204*7c478bd9Sstevel@tonic-gate 		setbuf(stdout, NULL);
205*7c478bd9Sstevel@tonic-gate 		readinitblock();
206*7c478bd9Sstevel@tonic-gate 	}
207*7c478bd9Sstevel@tonic-gate #endif	/* defined(DYNAMIC_SU) */
208*7c478bd9Sstevel@tonic-gate 
209*7c478bd9Sstevel@tonic-gate 	if (argc > 1 && *argv[1] == '-') {
210*7c478bd9Sstevel@tonic-gate 		/* Explicitly check for just `-' (no trailing chars) */
211*7c478bd9Sstevel@tonic-gate 		if (strlen(argv[1]) == 1) {
212*7c478bd9Sstevel@tonic-gate 			eflag++;	/* set eflag if `-' is specified */
213*7c478bd9Sstevel@tonic-gate 			argv++;
214*7c478bd9Sstevel@tonic-gate 			argc--;
215*7c478bd9Sstevel@tonic-gate 		} else {
216*7c478bd9Sstevel@tonic-gate 			message(USAGE,
217*7c478bd9Sstevel@tonic-gate 			    gettext("Usage: %s [-] [ username [ arg ... ] ]"),
218*7c478bd9Sstevel@tonic-gate 				prog);
219*7c478bd9Sstevel@tonic-gate 			exit(1);
220*7c478bd9Sstevel@tonic-gate 		}
221*7c478bd9Sstevel@tonic-gate 	}
222*7c478bd9Sstevel@tonic-gate 
223*7c478bd9Sstevel@tonic-gate 	/*
224*7c478bd9Sstevel@tonic-gate 	 * Determine specified userid, get their password file entry,
225*7c478bd9Sstevel@tonic-gate 	 * and set variables to values in password file entry fields.
226*7c478bd9Sstevel@tonic-gate 	 */
227*7c478bd9Sstevel@tonic-gate 	if (argc > 1) {
228*7c478bd9Sstevel@tonic-gate 		/*
229*7c478bd9Sstevel@tonic-gate 		 * Usernames can't start with a `-', so we check for that to
230*7c478bd9Sstevel@tonic-gate 		 * catch bad usage (like "su - -c ls").
231*7c478bd9Sstevel@tonic-gate 		 */
232*7c478bd9Sstevel@tonic-gate 		if (*argv[1] == '-') {
233*7c478bd9Sstevel@tonic-gate 			message(USAGE,
234*7c478bd9Sstevel@tonic-gate 			    gettext("Usage: %s [-] [ username [ arg ... ] ]"),
235*7c478bd9Sstevel@tonic-gate 				prog);
236*7c478bd9Sstevel@tonic-gate 			exit(1);
237*7c478bd9Sstevel@tonic-gate 		} else
238*7c478bd9Sstevel@tonic-gate 			nptr = argv[1];	/* use valid command-line username */
239*7c478bd9Sstevel@tonic-gate 	} else
240*7c478bd9Sstevel@tonic-gate 		nptr = "root";		/* use default "root" username */
241*7c478bd9Sstevel@tonic-gate 
242*7c478bd9Sstevel@tonic-gate 	if (defopen(DEFFILE) == 0) {
243*7c478bd9Sstevel@tonic-gate 
244*7c478bd9Sstevel@tonic-gate 		if (Sulog = defread("SULOG="))
245*7c478bd9Sstevel@tonic-gate 			Sulog = strdup(Sulog);
246*7c478bd9Sstevel@tonic-gate 		if (Console = defread("CONSOLE="))
247*7c478bd9Sstevel@tonic-gate 			Console = strdup(Console);
248*7c478bd9Sstevel@tonic-gate 		if (Path = defread("PATH="))
249*7c478bd9Sstevel@tonic-gate 			Path = strdup(Path);
250*7c478bd9Sstevel@tonic-gate 		if (Supath = defread("SUPATH="))
251*7c478bd9Sstevel@tonic-gate 			Supath = strdup(Supath);
252*7c478bd9Sstevel@tonic-gate 		if ((ptr = defread("SYSLOG=")) != NULL)
253*7c478bd9Sstevel@tonic-gate 			dosyslog = strcmp(ptr, "YES") == 0;
254*7c478bd9Sstevel@tonic-gate 
255*7c478bd9Sstevel@tonic-gate 		(void) defopen(NULL);
256*7c478bd9Sstevel@tonic-gate 	}
257*7c478bd9Sstevel@tonic-gate 	(void) strlcat(path, (Path) ? Path : PATH, sizeof (path));
258*7c478bd9Sstevel@tonic-gate 	(void) strlcat(supath, (Supath) ? Supath : SUPATH, sizeof (supath));
259*7c478bd9Sstevel@tonic-gate 
260*7c478bd9Sstevel@tonic-gate 	if ((ttyn = ttyname(0)) == NULL)
261*7c478bd9Sstevel@tonic-gate 		if ((ttyn = ttyname(1)) == NULL)
262*7c478bd9Sstevel@tonic-gate 			if ((ttyn = ttyname(2)) == NULL)
263*7c478bd9Sstevel@tonic-gate 				ttyn = "/dev/???";
264*7c478bd9Sstevel@tonic-gate 	if ((username = cuserid(NULL)) == NULL)
265*7c478bd9Sstevel@tonic-gate 		username = "(null)";
266*7c478bd9Sstevel@tonic-gate 
267*7c478bd9Sstevel@tonic-gate 	/*
268*7c478bd9Sstevel@tonic-gate 	 * if Sulog defined, create SULOG, if it does not exist, with
269*7c478bd9Sstevel@tonic-gate 	 * mode read/write user. Change owner and group to root
270*7c478bd9Sstevel@tonic-gate 	 */
271*7c478bd9Sstevel@tonic-gate 	if (Sulog != NULL) {
272*7c478bd9Sstevel@tonic-gate 		(void) close(open(Sulog, O_WRONLY | O_APPEND | O_CREAT,
273*7c478bd9Sstevel@tonic-gate 		    (S_IRUSR|S_IWUSR)));
274*7c478bd9Sstevel@tonic-gate 		(void) chown(Sulog, (uid_t)ROOT, (gid_t)ROOT);
275*7c478bd9Sstevel@tonic-gate 	}
276*7c478bd9Sstevel@tonic-gate 
277*7c478bd9Sstevel@tonic-gate #ifdef DYNAMIC_SU
278*7c478bd9Sstevel@tonic-gate 	if (pam_start(embedded ? EMBEDDED_NAME : "su", nptr,
279*7c478bd9Sstevel@tonic-gate 	    embedded ? &emb_pam_conv : &pam_conv, &pamh) != PAM_SUCCESS)
280*7c478bd9Sstevel@tonic-gate 		exit(1);
281*7c478bd9Sstevel@tonic-gate 	if (pam_set_item(pamh, PAM_TTY, ttyn) != PAM_SUCCESS)
282*7c478bd9Sstevel@tonic-gate 		exit(1);
283*7c478bd9Sstevel@tonic-gate #endif	/* DYNAMIC_SU */
284*7c478bd9Sstevel@tonic-gate 
285*7c478bd9Sstevel@tonic-gate 	/*
286*7c478bd9Sstevel@tonic-gate 	 * Save away the following for writing user-level audit records:
287*7c478bd9Sstevel@tonic-gate 	 *	username desired
288*7c478bd9Sstevel@tonic-gate 	 *	current tty
289*7c478bd9Sstevel@tonic-gate 	 *	whether or not username desired is expired
290*7c478bd9Sstevel@tonic-gate 	 *	auditinfo structure of current process
291*7c478bd9Sstevel@tonic-gate 	 *	uid's and gid's of current process
292*7c478bd9Sstevel@tonic-gate 	 */
293*7c478bd9Sstevel@tonic-gate 	audit_su_init_info(nptr, ttyn);
294*7c478bd9Sstevel@tonic-gate 	openlog("su", LOG_CONS, LOG_AUTH);
295*7c478bd9Sstevel@tonic-gate 
296*7c478bd9Sstevel@tonic-gate #ifdef DYNAMIC_SU
297*7c478bd9Sstevel@tonic-gate 
298*7c478bd9Sstevel@tonic-gate 	/*
299*7c478bd9Sstevel@tonic-gate 	 * Ignore SIGQUIT and SIGINT
300*7c478bd9Sstevel@tonic-gate 	 */
301*7c478bd9Sstevel@tonic-gate 	(void) signal(SIGQUIT, SIG_IGN);
302*7c478bd9Sstevel@tonic-gate 	(void) signal(SIGINT, SIG_IGN);
303*7c478bd9Sstevel@tonic-gate 
304*7c478bd9Sstevel@tonic-gate 	/* call pam_authenticate() to authenticate the user through PAM */
305*7c478bd9Sstevel@tonic-gate 	if (getpwnam_r(nptr, &pwd, pwdbuf, sizeof (pwdbuf)) == NULL)
306*7c478bd9Sstevel@tonic-gate 		retcode = PAM_USER_UNKNOWN;
307*7c478bd9Sstevel@tonic-gate 	else if ((flags = (getuid() != (uid_t)ROOT)) != 0) {
308*7c478bd9Sstevel@tonic-gate 		retcode = pam_authenticate(pamh, 0);
309*7c478bd9Sstevel@tonic-gate 	} else /* root user does not need to authenticate */
310*7c478bd9Sstevel@tonic-gate 		retcode = PAM_SUCCESS;
311*7c478bd9Sstevel@tonic-gate 
312*7c478bd9Sstevel@tonic-gate 	if (retcode != PAM_SUCCESS) {
313*7c478bd9Sstevel@tonic-gate 		/*
314*7c478bd9Sstevel@tonic-gate 		 * Use the same value of sleeptime that login(1) uses.
315*7c478bd9Sstevel@tonic-gate 		 * This is obtained by reading the file /etc/default/login
316*7c478bd9Sstevel@tonic-gate 		 * using the def*() functions
317*7c478bd9Sstevel@tonic-gate 		 */
318*7c478bd9Sstevel@tonic-gate 		if (defopen(DEFAULT_LOGIN) == 0) {
319*7c478bd9Sstevel@tonic-gate 			if ((ptr = defread("SLEEPTIME=")) != NULL)
320*7c478bd9Sstevel@tonic-gate 				sleeptime = atoi(ptr);
321*7c478bd9Sstevel@tonic-gate 			(void) defopen((char *)NULL);
322*7c478bd9Sstevel@tonic-gate 
323*7c478bd9Sstevel@tonic-gate 			if (sleeptime < 0 || sleeptime > 5)
324*7c478bd9Sstevel@tonic-gate 				sleeptime = SLEEPTIME;
325*7c478bd9Sstevel@tonic-gate 		}
326*7c478bd9Sstevel@tonic-gate 
327*7c478bd9Sstevel@tonic-gate 		/*
328*7c478bd9Sstevel@tonic-gate 		 * 1st step: log the error.
329*7c478bd9Sstevel@tonic-gate 		 * 2nd step: sleep.
330*7c478bd9Sstevel@tonic-gate 		 * 3rd step: print out message to user.
331*7c478bd9Sstevel@tonic-gate 		 */
332*7c478bd9Sstevel@tonic-gate 		switch (retcode) {
333*7c478bd9Sstevel@tonic-gate 		case PAM_USER_UNKNOWN:
334*7c478bd9Sstevel@tonic-gate 			audit_su_bad_username();
335*7c478bd9Sstevel@tonic-gate 			closelog();
336*7c478bd9Sstevel@tonic-gate 			(void) sleep(sleeptime);
337*7c478bd9Sstevel@tonic-gate 			message(ERR, gettext("Unknown id: %s"), nptr);
338*7c478bd9Sstevel@tonic-gate 			break;
339*7c478bd9Sstevel@tonic-gate 
340*7c478bd9Sstevel@tonic-gate 		case PAM_AUTH_ERR:
341*7c478bd9Sstevel@tonic-gate 			if (Sulog != NULL)
342*7c478bd9Sstevel@tonic-gate 				log(Sulog, nptr, 0);	/* log entry */
343*7c478bd9Sstevel@tonic-gate 			audit_su_bad_authentication();
344*7c478bd9Sstevel@tonic-gate 			if (dosyslog)
345*7c478bd9Sstevel@tonic-gate 				syslog(LOG_CRIT, "'su %s' failed for %s on %s",
346*7c478bd9Sstevel@tonic-gate 				    pwd.pw_name, username, ttyn);
347*7c478bd9Sstevel@tonic-gate 			closelog();
348*7c478bd9Sstevel@tonic-gate 			(void) sleep(sleeptime);
349*7c478bd9Sstevel@tonic-gate 			message(ERR, gettext("Sorry"));
350*7c478bd9Sstevel@tonic-gate 			break;
351*7c478bd9Sstevel@tonic-gate 
352*7c478bd9Sstevel@tonic-gate 		case PAM_CONV_ERR:
353*7c478bd9Sstevel@tonic-gate 		default:
354*7c478bd9Sstevel@tonic-gate 			audit_su_unknown_failure();
355*7c478bd9Sstevel@tonic-gate 			if (dosyslog)
356*7c478bd9Sstevel@tonic-gate 				syslog(LOG_CRIT, "'su %s' failed for %s on %s",
357*7c478bd9Sstevel@tonic-gate 				    pwd.pw_name, username, ttyn);
358*7c478bd9Sstevel@tonic-gate 			closelog();
359*7c478bd9Sstevel@tonic-gate 			(void) sleep(sleeptime);
360*7c478bd9Sstevel@tonic-gate 			message(ERR, gettext("Sorry"));
361*7c478bd9Sstevel@tonic-gate 			break;
362*7c478bd9Sstevel@tonic-gate 		}
363*7c478bd9Sstevel@tonic-gate 
364*7c478bd9Sstevel@tonic-gate 		(void) signal(SIGQUIT, SIG_DFL);
365*7c478bd9Sstevel@tonic-gate 		(void) signal(SIGINT, SIG_DFL);
366*7c478bd9Sstevel@tonic-gate 		exit(1);
367*7c478bd9Sstevel@tonic-gate 	}
368*7c478bd9Sstevel@tonic-gate 	if (flags)
369*7c478bd9Sstevel@tonic-gate 		validate(username);
370*7c478bd9Sstevel@tonic-gate 	if (pam_setcred(pamh, PAM_REINITIALIZE_CRED) != PAM_SUCCESS) {
371*7c478bd9Sstevel@tonic-gate 		message(ERR, gettext("unable to set credentials"));
372*7c478bd9Sstevel@tonic-gate 		exit(2);
373*7c478bd9Sstevel@tonic-gate 	}
374*7c478bd9Sstevel@tonic-gate 	audit_su_success();
375*7c478bd9Sstevel@tonic-gate 	if (dosyslog)
376*7c478bd9Sstevel@tonic-gate 		syslog(getuid() == 0 ? LOG_INFO : LOG_NOTICE,
377*7c478bd9Sstevel@tonic-gate 		    "'su %s' succeeded for %s on %s",
378*7c478bd9Sstevel@tonic-gate 		    pwd.pw_name, username, ttyn);
379*7c478bd9Sstevel@tonic-gate 	closelog();
380*7c478bd9Sstevel@tonic-gate 	(void) signal(SIGQUIT, SIG_DFL);
381*7c478bd9Sstevel@tonic-gate 	(void) signal(SIGINT, SIG_DFL);
382*7c478bd9Sstevel@tonic-gate #else	/* STATIC !PAM */
383*7c478bd9Sstevel@tonic-gate 	if ((getpwnam_r(nptr, &pwd, pwdbuf, sizeof (pwdbuf)) == NULL) ||
384*7c478bd9Sstevel@tonic-gate 	    (getspnam_r(nptr, &sp, spbuf, sizeof (spbuf)) == NULL)) {
385*7c478bd9Sstevel@tonic-gate 		message(ERR, gettext("Unknown id: %s"), nptr);
386*7c478bd9Sstevel@tonic-gate 		audit_su_bad_username();
387*7c478bd9Sstevel@tonic-gate 		closelog();
388*7c478bd9Sstevel@tonic-gate 		exit(1);
389*7c478bd9Sstevel@tonic-gate 	}
390*7c478bd9Sstevel@tonic-gate 
391*7c478bd9Sstevel@tonic-gate 	/*
392*7c478bd9Sstevel@tonic-gate 	 * Prompt for password if invoking user is not root or
393*7c478bd9Sstevel@tonic-gate 	 * if specified(new) user requires a password
394*7c478bd9Sstevel@tonic-gate 	 */
395*7c478bd9Sstevel@tonic-gate 	if (sp.sp_pwdp[0] == '\0' || getuid() == (uid_t)ROOT)
396*7c478bd9Sstevel@tonic-gate 		goto ok;
397*7c478bd9Sstevel@tonic-gate 	password = getpass(gettext("Password:"));
398*7c478bd9Sstevel@tonic-gate 
399*7c478bd9Sstevel@tonic-gate 	if ((strcmp(sp.sp_pwdp, crypt(password, sp.sp_pwdp)) != 0)) {
400*7c478bd9Sstevel@tonic-gate 		/* clear password file entry */
401*7c478bd9Sstevel@tonic-gate 		(void) memset((void *)spbuf, 0, sizeof (spbuf));
402*7c478bd9Sstevel@tonic-gate 		if (Sulog != NULL)
403*7c478bd9Sstevel@tonic-gate 			log(Sulog, nptr, 0);    /* log entry */
404*7c478bd9Sstevel@tonic-gate 		message(ERR, gettext("Sorry"));
405*7c478bd9Sstevel@tonic-gate 		audit_su_bad_authentication();
406*7c478bd9Sstevel@tonic-gate 		if (dosyslog)
407*7c478bd9Sstevel@tonic-gate 			syslog(LOG_CRIT, "'su %s' failed for %s on %s",
408*7c478bd9Sstevel@tonic-gate 			    pwd.pw_name, username, ttyn);
409*7c478bd9Sstevel@tonic-gate 		closelog();
410*7c478bd9Sstevel@tonic-gate 		exit(2);
411*7c478bd9Sstevel@tonic-gate 	}
412*7c478bd9Sstevel@tonic-gate 	/* clear password file entry */
413*7c478bd9Sstevel@tonic-gate 	(void) memset((void *)spbuf, 0, sizeof (spbuf));
414*7c478bd9Sstevel@tonic-gate ok:
415*7c478bd9Sstevel@tonic-gate 	audit_su_reset_ai();
416*7c478bd9Sstevel@tonic-gate 	audit_su_success();
417*7c478bd9Sstevel@tonic-gate 	if (dosyslog)
418*7c478bd9Sstevel@tonic-gate 		syslog(getuid() == 0 ? LOG_INFO : LOG_NOTICE,
419*7c478bd9Sstevel@tonic-gate 		    "'su %s' succeeded for %s on %s",
420*7c478bd9Sstevel@tonic-gate 		    pwd.pw_name, username, ttyn);
421*7c478bd9Sstevel@tonic-gate #endif	/* STATIC !PAM */
422*7c478bd9Sstevel@tonic-gate 
423*7c478bd9Sstevel@tonic-gate 	uid = pwd.pw_uid;
424*7c478bd9Sstevel@tonic-gate 	gid = pwd.pw_gid;
425*7c478bd9Sstevel@tonic-gate 	dir = strdup(pwd.pw_dir);
426*7c478bd9Sstevel@tonic-gate 	shprog = strdup(pwd.pw_shell);
427*7c478bd9Sstevel@tonic-gate 	name = strdup(pwd.pw_name);
428*7c478bd9Sstevel@tonic-gate 
429*7c478bd9Sstevel@tonic-gate 	if (Sulog != NULL)
430*7c478bd9Sstevel@tonic-gate 		log(Sulog, nptr, 1);	/* log entry */
431*7c478bd9Sstevel@tonic-gate 
432*7c478bd9Sstevel@tonic-gate 	/* set user and group ids to specified user */
433*7c478bd9Sstevel@tonic-gate 
434*7c478bd9Sstevel@tonic-gate 	/* set the real (and effective) GID */
435*7c478bd9Sstevel@tonic-gate 	if (setgid(gid) == -1) {
436*7c478bd9Sstevel@tonic-gate 		message(ERR, gettext("Invalid GID"));
437*7c478bd9Sstevel@tonic-gate 		exit(2);
438*7c478bd9Sstevel@tonic-gate 	}
439*7c478bd9Sstevel@tonic-gate 	/* Initialize the supplementary group access list. */
440*7c478bd9Sstevel@tonic-gate 	if (!nptr)
441*7c478bd9Sstevel@tonic-gate 		exit(2);
442*7c478bd9Sstevel@tonic-gate 	if (initgroups(nptr, gid) == -1) {
443*7c478bd9Sstevel@tonic-gate 		exit(2);
444*7c478bd9Sstevel@tonic-gate 	}
445*7c478bd9Sstevel@tonic-gate 	/* set the real (and effective) UID */
446*7c478bd9Sstevel@tonic-gate 	if (setuid(uid) == -1) {
447*7c478bd9Sstevel@tonic-gate 		message(ERR, gettext("Invalid UID"));
448*7c478bd9Sstevel@tonic-gate 		exit(2);
449*7c478bd9Sstevel@tonic-gate 	}
450*7c478bd9Sstevel@tonic-gate 
451*7c478bd9Sstevel@tonic-gate 	/*
452*7c478bd9Sstevel@tonic-gate 	 * If new user's shell field is neither NULL nor equal to /usr/bin/sh,
453*7c478bd9Sstevel@tonic-gate 	 * set:
454*7c478bd9Sstevel@tonic-gate 	 *
455*7c478bd9Sstevel@tonic-gate 	 *	pshell = their shell
456*7c478bd9Sstevel@tonic-gate 	 *	su = [-]last component of shell's pathname
457*7c478bd9Sstevel@tonic-gate 	 *
458*7c478bd9Sstevel@tonic-gate 	 * Otherwise, set the shell to /usr/bin/sh and set argv[0] to '[-]su'.
459*7c478bd9Sstevel@tonic-gate 	 */
460*7c478bd9Sstevel@tonic-gate 	if (shprog[0] != '\0' && strcmp(shell, shprog) != 0) {
461*7c478bd9Sstevel@tonic-gate 		char *p;
462*7c478bd9Sstevel@tonic-gate 
463*7c478bd9Sstevel@tonic-gate 		pshell = shprog;
464*7c478bd9Sstevel@tonic-gate 		(void) strcpy(su, eflag ? "-" : "");
465*7c478bd9Sstevel@tonic-gate 
466*7c478bd9Sstevel@tonic-gate 		if ((p = strrchr(pshell, '/')) != NULL)
467*7c478bd9Sstevel@tonic-gate 			(void) strlcat(su, p + 1, sizeof (su));
468*7c478bd9Sstevel@tonic-gate 		else
469*7c478bd9Sstevel@tonic-gate 			(void) strlcat(su, pshell, sizeof (su));
470*7c478bd9Sstevel@tonic-gate 	} else {
471*7c478bd9Sstevel@tonic-gate 		pshell = shell;
472*7c478bd9Sstevel@tonic-gate 		(void) strcpy(su, eflag ? "-su" : "su");
473*7c478bd9Sstevel@tonic-gate 	}
474*7c478bd9Sstevel@tonic-gate 
475*7c478bd9Sstevel@tonic-gate 	/*
476*7c478bd9Sstevel@tonic-gate 	 * set environment variables for new user;
477*7c478bd9Sstevel@tonic-gate 	 * arg0 for exec of shprog must now contain `-'
478*7c478bd9Sstevel@tonic-gate 	 * so that environment of new user is given
479*7c478bd9Sstevel@tonic-gate 	 */
480*7c478bd9Sstevel@tonic-gate 	if (eflag) {
481*7c478bd9Sstevel@tonic-gate 		int j;
482*7c478bd9Sstevel@tonic-gate 		char *var;
483*7c478bd9Sstevel@tonic-gate 
484*7c478bd9Sstevel@tonic-gate 		if (strlen(dir) == 0) {
485*7c478bd9Sstevel@tonic-gate 			(void) strcpy(dir, "/");
486*7c478bd9Sstevel@tonic-gate 			message(WARN, gettext("No directory! Using home=/"));
487*7c478bd9Sstevel@tonic-gate 		}
488*7c478bd9Sstevel@tonic-gate 		(void) strlcat(homedir, dir, sizeof (homedir));
489*7c478bd9Sstevel@tonic-gate 		(void) strlcat(logname, name, sizeof (logname));
490*7c478bd9Sstevel@tonic-gate 		if (hz = getenv("HZ"))
491*7c478bd9Sstevel@tonic-gate 			(void) strlcat(hzname, hz, sizeof (hzname));
492*7c478bd9Sstevel@tonic-gate 
493*7c478bd9Sstevel@tonic-gate 		(void) strlcat(shelltyp, pshell, sizeof (shelltyp));
494*7c478bd9Sstevel@tonic-gate 
495*7c478bd9Sstevel@tonic-gate 		if (chdir(dir) < 0) {
496*7c478bd9Sstevel@tonic-gate 			message(ERR, gettext("No directory!"));
497*7c478bd9Sstevel@tonic-gate 			exit(1);
498*7c478bd9Sstevel@tonic-gate 		}
499*7c478bd9Sstevel@tonic-gate 		envinit[envidx = 0] = homedir;
500*7c478bd9Sstevel@tonic-gate 		envinit[++envidx] = ((uid == (uid_t)ROOT) ? supath : path);
501*7c478bd9Sstevel@tonic-gate 		envinit[++envidx] = logname;
502*7c478bd9Sstevel@tonic-gate 		envinit[++envidx] = hzname;
503*7c478bd9Sstevel@tonic-gate 		if ((term = getenv("TERM")) != NULL) {
504*7c478bd9Sstevel@tonic-gate 			(void) strlcat(termtyp, term, sizeof (termtyp));
505*7c478bd9Sstevel@tonic-gate 			envinit[++envidx] = termtyp;
506*7c478bd9Sstevel@tonic-gate 		}
507*7c478bd9Sstevel@tonic-gate 		envinit[++envidx] = shelltyp;
508*7c478bd9Sstevel@tonic-gate 
509*7c478bd9Sstevel@tonic-gate 		(void) strlcat(mail, name, sizeof (mail));
510*7c478bd9Sstevel@tonic-gate 		envinit[++envidx] = mail;
511*7c478bd9Sstevel@tonic-gate 
512*7c478bd9Sstevel@tonic-gate 		/*
513*7c478bd9Sstevel@tonic-gate 		 * Fetch the relevant locale/TZ environment variables from
514*7c478bd9Sstevel@tonic-gate 		 * the inherited environment.
515*7c478bd9Sstevel@tonic-gate 		 *
516*7c478bd9Sstevel@tonic-gate 		 * We have a priority here for setting TZ. If TZ is set in
517*7c478bd9Sstevel@tonic-gate 		 * in the inherited environment, that value remains top
518*7c478bd9Sstevel@tonic-gate 		 * priority. If the file /etc/default/login has TIMEZONE set,
519*7c478bd9Sstevel@tonic-gate 		 * that has second highest priority.
520*7c478bd9Sstevel@tonic-gate 		 */
521*7c478bd9Sstevel@tonic-gate 		tznam[0] = '\0';
522*7c478bd9Sstevel@tonic-gate 		for (j = 0; initenv[j] != 0; j++) {
523*7c478bd9Sstevel@tonic-gate 			if (initvar = getenv(initenv[j])) {
524*7c478bd9Sstevel@tonic-gate 
525*7c478bd9Sstevel@tonic-gate 				/*
526*7c478bd9Sstevel@tonic-gate 				 * Skip over values beginning with '/' for
527*7c478bd9Sstevel@tonic-gate 				 * security.
528*7c478bd9Sstevel@tonic-gate 				 */
529*7c478bd9Sstevel@tonic-gate 				if (initvar[0] == '/')  continue;
530*7c478bd9Sstevel@tonic-gate 
531*7c478bd9Sstevel@tonic-gate 				if (strcmp(initenv[j], "TZ") == 0) {
532*7c478bd9Sstevel@tonic-gate 					(void) strcpy(tznam, "TZ=");
533*7c478bd9Sstevel@tonic-gate 					(void) strlcat(tznam, initvar,
534*7c478bd9Sstevel@tonic-gate 						sizeof (tznam));
535*7c478bd9Sstevel@tonic-gate 
536*7c478bd9Sstevel@tonic-gate 				} else {
537*7c478bd9Sstevel@tonic-gate 					var = (char *)
538*7c478bd9Sstevel@tonic-gate 						malloc(strlen(initenv[j])
539*7c478bd9Sstevel@tonic-gate 							+ strlen(initvar)
540*7c478bd9Sstevel@tonic-gate 							+ 2);
541*7c478bd9Sstevel@tonic-gate 					(void) strcpy(var, initenv[j]);
542*7c478bd9Sstevel@tonic-gate 					(void) strcat(var, "=");
543*7c478bd9Sstevel@tonic-gate 					(void) strcat(var, initvar);
544*7c478bd9Sstevel@tonic-gate 					envinit[++envidx] = var;
545*7c478bd9Sstevel@tonic-gate 				}
546*7c478bd9Sstevel@tonic-gate 			}
547*7c478bd9Sstevel@tonic-gate 		}
548*7c478bd9Sstevel@tonic-gate 
549*7c478bd9Sstevel@tonic-gate 		/*
550*7c478bd9Sstevel@tonic-gate 		 * Check if TZ was found. If not then try to read it from
551*7c478bd9Sstevel@tonic-gate 		 * /etc/default/login.
552*7c478bd9Sstevel@tonic-gate 		 */
553*7c478bd9Sstevel@tonic-gate 		if (tznam[0] == '\0') {
554*7c478bd9Sstevel@tonic-gate 			if (defopen(DEFAULT_LOGIN) == 0) {
555*7c478bd9Sstevel@tonic-gate 				if (initvar = defread("TIMEZONE=")) {
556*7c478bd9Sstevel@tonic-gate 					(void) strcpy(tznam, "TZ=");
557*7c478bd9Sstevel@tonic-gate 					(void) strlcat(tznam, initvar,
558*7c478bd9Sstevel@tonic-gate 							sizeof (tznam));
559*7c478bd9Sstevel@tonic-gate 				}
560*7c478bd9Sstevel@tonic-gate 				(void) defopen(NULL);
561*7c478bd9Sstevel@tonic-gate 			}
562*7c478bd9Sstevel@tonic-gate 		}
563*7c478bd9Sstevel@tonic-gate 
564*7c478bd9Sstevel@tonic-gate 		if (tznam[0] != '\0')
565*7c478bd9Sstevel@tonic-gate 			envinit[++envidx] = tznam;
566*7c478bd9Sstevel@tonic-gate 
567*7c478bd9Sstevel@tonic-gate #ifdef DYNAMIC_SU
568*7c478bd9Sstevel@tonic-gate 		/*
569*7c478bd9Sstevel@tonic-gate 		 * set the PAM environment variables -
570*7c478bd9Sstevel@tonic-gate 		 * check for legal environment variables
571*7c478bd9Sstevel@tonic-gate 		 */
572*7c478bd9Sstevel@tonic-gate 		if ((pam_env = pam_getenvlist(pamh)) != 0) {
573*7c478bd9Sstevel@tonic-gate 			while (pam_env[idx] != 0) {
574*7c478bd9Sstevel@tonic-gate 				if (envidx + 2 < ELIM &&
575*7c478bd9Sstevel@tonic-gate 				    legalenvvar(pam_env[idx])) {
576*7c478bd9Sstevel@tonic-gate 					envinit[++envidx] = pam_env[idx];
577*7c478bd9Sstevel@tonic-gate 				}
578*7c478bd9Sstevel@tonic-gate 				idx++;
579*7c478bd9Sstevel@tonic-gate 			}
580*7c478bd9Sstevel@tonic-gate 		}
581*7c478bd9Sstevel@tonic-gate #endif
582*7c478bd9Sstevel@tonic-gate 		envinit[++envidx] = NULL;
583*7c478bd9Sstevel@tonic-gate 		environ = envinit;
584*7c478bd9Sstevel@tonic-gate 	} else {
585*7c478bd9Sstevel@tonic-gate 		char **pp = environ, **qq, *p;
586*7c478bd9Sstevel@tonic-gate 
587*7c478bd9Sstevel@tonic-gate 		while ((p = *pp) != NULL) {
588*7c478bd9Sstevel@tonic-gate 			if (*p == 'L' && p[1] == 'D' && p[2] == '_') {
589*7c478bd9Sstevel@tonic-gate 				for (qq = pp; (*qq = qq[1]) != NULL; qq++)
590*7c478bd9Sstevel@tonic-gate 					;
591*7c478bd9Sstevel@tonic-gate 				/* pp is not advanced */
592*7c478bd9Sstevel@tonic-gate 			} else {
593*7c478bd9Sstevel@tonic-gate 				pp++;
594*7c478bd9Sstevel@tonic-gate 			}
595*7c478bd9Sstevel@tonic-gate 		}
596*7c478bd9Sstevel@tonic-gate 	}
597*7c478bd9Sstevel@tonic-gate 
598*7c478bd9Sstevel@tonic-gate #ifdef DYNAMIC_SU
599*7c478bd9Sstevel@tonic-gate 	if (pamh)
600*7c478bd9Sstevel@tonic-gate 		(void) pam_end(pamh, PAM_SUCCESS);
601*7c478bd9Sstevel@tonic-gate #endif
602*7c478bd9Sstevel@tonic-gate 
603*7c478bd9Sstevel@tonic-gate 	/*
604*7c478bd9Sstevel@tonic-gate 	 * if new user is root:
605*7c478bd9Sstevel@tonic-gate 	 *	if CONSOLE defined, log entry there;
606*7c478bd9Sstevel@tonic-gate 	 *	if eflag not set, change environment to that of root.
607*7c478bd9Sstevel@tonic-gate 	 */
608*7c478bd9Sstevel@tonic-gate 	if (uid == (uid_t)ROOT) {
609*7c478bd9Sstevel@tonic-gate 		if (Console != NULL)
610*7c478bd9Sstevel@tonic-gate 			if (strcmp(ttyn, Console) != 0) {
611*7c478bd9Sstevel@tonic-gate 				(void) signal(SIGALRM, to);
612*7c478bd9Sstevel@tonic-gate 				(void) alarm(30);
613*7c478bd9Sstevel@tonic-gate 				log(Console, nptr, 1);
614*7c478bd9Sstevel@tonic-gate 				(void) alarm(0);
615*7c478bd9Sstevel@tonic-gate 			}
616*7c478bd9Sstevel@tonic-gate 		if (!eflag)
617*7c478bd9Sstevel@tonic-gate 			envalt();
618*7c478bd9Sstevel@tonic-gate 	}
619*7c478bd9Sstevel@tonic-gate 
620*7c478bd9Sstevel@tonic-gate 	/*
621*7c478bd9Sstevel@tonic-gate 	 * Default for SIGCPU and SIGXFSZ.  Shells inherit
622*7c478bd9Sstevel@tonic-gate 	 * signal disposition from parent.  And the
623*7c478bd9Sstevel@tonic-gate 	 * shells should have default dispositions for these
624*7c478bd9Sstevel@tonic-gate 	 * signals.
625*7c478bd9Sstevel@tonic-gate 	 */
626*7c478bd9Sstevel@tonic-gate 	(void) signal(SIGXCPU, SIG_DFL);
627*7c478bd9Sstevel@tonic-gate 	(void) signal(SIGXFSZ, SIG_DFL);
628*7c478bd9Sstevel@tonic-gate 
629*7c478bd9Sstevel@tonic-gate #ifdef	DYNAMIC_SU
630*7c478bd9Sstevel@tonic-gate 	if (embedded) {
631*7c478bd9Sstevel@tonic-gate 		(void) puts("SUCCESS");
632*7c478bd9Sstevel@tonic-gate 		/*
633*7c478bd9Sstevel@tonic-gate 		 * After this point, we're no longer talking the
634*7c478bd9Sstevel@tonic-gate 		 * embedded_su protocol, so turn it off.
635*7c478bd9Sstevel@tonic-gate 		 */
636*7c478bd9Sstevel@tonic-gate 		embedded = B_FALSE;
637*7c478bd9Sstevel@tonic-gate 	}
638*7c478bd9Sstevel@tonic-gate #endif	/* DYNAMIC_SU */
639*7c478bd9Sstevel@tonic-gate 
640*7c478bd9Sstevel@tonic-gate 	/*
641*7c478bd9Sstevel@tonic-gate 	 * if additional arguments, exec shell program with array
642*7c478bd9Sstevel@tonic-gate 	 * of pointers to arguments:
643*7c478bd9Sstevel@tonic-gate 	 *	-> if shell = default, then su = [-]su
644*7c478bd9Sstevel@tonic-gate 	 *	-> if shell != default, then su = [-]last component of
645*7c478bd9Sstevel@tonic-gate 	 *						shell's pathname
646*7c478bd9Sstevel@tonic-gate 	 *
647*7c478bd9Sstevel@tonic-gate 	 * if no additional arguments, exec shell with arg0 of su
648*7c478bd9Sstevel@tonic-gate 	 * where:
649*7c478bd9Sstevel@tonic-gate 	 *	-> if shell = default, then su = [-]su
650*7c478bd9Sstevel@tonic-gate 	 *	-> if shell != default, then su = [-]last component of
651*7c478bd9Sstevel@tonic-gate 	 *						shell's pathname
652*7c478bd9Sstevel@tonic-gate 	 */
653*7c478bd9Sstevel@tonic-gate 	if (argc > 2) {
654*7c478bd9Sstevel@tonic-gate 		argv[1] = su;
655*7c478bd9Sstevel@tonic-gate 		(void) execv(pshell, &argv[1]);
656*7c478bd9Sstevel@tonic-gate 	} else
657*7c478bd9Sstevel@tonic-gate 		(void) execl(pshell, su, 0);
658*7c478bd9Sstevel@tonic-gate 
659*7c478bd9Sstevel@tonic-gate 
660*7c478bd9Sstevel@tonic-gate 	/*
661*7c478bd9Sstevel@tonic-gate 	 * Try to clean up after an administrator who has made a mistake
662*7c478bd9Sstevel@tonic-gate 	 * configuring root's shell; if root's shell is other than /sbin/sh,
663*7c478bd9Sstevel@tonic-gate 	 * try exec'ing /sbin/sh instead.
664*7c478bd9Sstevel@tonic-gate 	 */
665*7c478bd9Sstevel@tonic-gate 	if ((uid == (uid_t)ROOT) && (strcmp(name, "root") == 0) &&
666*7c478bd9Sstevel@tonic-gate 	    (strcmp(safe_shell, pshell) != 0)) {
667*7c478bd9Sstevel@tonic-gate 		message(WARN,
668*7c478bd9Sstevel@tonic-gate 		    gettext("No shell %s.  Trying fallback shell %s."),
669*7c478bd9Sstevel@tonic-gate 		    pshell, safe_shell);
670*7c478bd9Sstevel@tonic-gate 
671*7c478bd9Sstevel@tonic-gate 		if (eflag) {
672*7c478bd9Sstevel@tonic-gate 			(void) strcpy(su, "-sh");
673*7c478bd9Sstevel@tonic-gate 			(void) strlcpy(shelltyp + strlen("SHELL="),
674*7c478bd9Sstevel@tonic-gate 			    safe_shell, sizeof (shelltyp) - strlen("SHELL="));
675*7c478bd9Sstevel@tonic-gate 		} else {
676*7c478bd9Sstevel@tonic-gate 			(void) strcpy(su, "sh");
677*7c478bd9Sstevel@tonic-gate 		}
678*7c478bd9Sstevel@tonic-gate 
679*7c478bd9Sstevel@tonic-gate 		if (argc > 2) {
680*7c478bd9Sstevel@tonic-gate 			argv[1] = su;
681*7c478bd9Sstevel@tonic-gate 			(void) execv(safe_shell, &argv[1]);
682*7c478bd9Sstevel@tonic-gate 		} else {
683*7c478bd9Sstevel@tonic-gate 			(void) execl(safe_shell, su, 0);
684*7c478bd9Sstevel@tonic-gate 		}
685*7c478bd9Sstevel@tonic-gate 		message(ERR, gettext("Couldn't exec fallback shell %s: %s"),
686*7c478bd9Sstevel@tonic-gate 		    safe_shell, strerror(errno));
687*7c478bd9Sstevel@tonic-gate 	} else {
688*7c478bd9Sstevel@tonic-gate 		message(ERR, gettext("No shell"));
689*7c478bd9Sstevel@tonic-gate 	}
690*7c478bd9Sstevel@tonic-gate 	return (3);
691*7c478bd9Sstevel@tonic-gate }
692*7c478bd9Sstevel@tonic-gate 
693*7c478bd9Sstevel@tonic-gate /*
694*7c478bd9Sstevel@tonic-gate  * Environment altering routine -
695*7c478bd9Sstevel@tonic-gate  *	This routine is called when a user is su'ing to root
696*7c478bd9Sstevel@tonic-gate  *	without specifying the - flag.
697*7c478bd9Sstevel@tonic-gate  *	The user's PATH and PS1 variables are reset
698*7c478bd9Sstevel@tonic-gate  *	to the correct value for root.
699*7c478bd9Sstevel@tonic-gate  *	All of the user's other environment variables retain
700*7c478bd9Sstevel@tonic-gate  *	their current values after the su (if they are exported).
701*7c478bd9Sstevel@tonic-gate  */
702*7c478bd9Sstevel@tonic-gate static void
703*7c478bd9Sstevel@tonic-gate envalt(void)
704*7c478bd9Sstevel@tonic-gate {
705*7c478bd9Sstevel@tonic-gate 	/*
706*7c478bd9Sstevel@tonic-gate 	 * If user has PATH variable in their environment, change its value
707*7c478bd9Sstevel@tonic-gate 	 *		to /bin:/etc:/usr/bin ;
708*7c478bd9Sstevel@tonic-gate 	 * if user does not have PATH variable, add it to the user's
709*7c478bd9Sstevel@tonic-gate 	 *		environment;
710*7c478bd9Sstevel@tonic-gate 	 * if either of the above fail, an error message is printed.
711*7c478bd9Sstevel@tonic-gate 	 */
712*7c478bd9Sstevel@tonic-gate 	if (putenv(supath) != 0) {
713*7c478bd9Sstevel@tonic-gate 		message(ERR,
714*7c478bd9Sstevel@tonic-gate 		    gettext("unable to obtain memory to expand environment"));
715*7c478bd9Sstevel@tonic-gate 		exit(4);
716*7c478bd9Sstevel@tonic-gate 	}
717*7c478bd9Sstevel@tonic-gate 
718*7c478bd9Sstevel@tonic-gate 	/*
719*7c478bd9Sstevel@tonic-gate 	 * If user has PROMPT variable in their environment, change its value
720*7c478bd9Sstevel@tonic-gate 	 *		to # ;
721*7c478bd9Sstevel@tonic-gate 	 * if user does not have PROMPT variable, add it to the user's
722*7c478bd9Sstevel@tonic-gate 	 *		environment;
723*7c478bd9Sstevel@tonic-gate 	 * if either of the above fail, an error message is printed.
724*7c478bd9Sstevel@tonic-gate 	 */
725*7c478bd9Sstevel@tonic-gate 	if (putenv(suprmt) != 0) {
726*7c478bd9Sstevel@tonic-gate 		message(ERR,
727*7c478bd9Sstevel@tonic-gate 		    gettext("unable to obtain memory to expand environment"));
728*7c478bd9Sstevel@tonic-gate 		exit(4);
729*7c478bd9Sstevel@tonic-gate 	}
730*7c478bd9Sstevel@tonic-gate }
731*7c478bd9Sstevel@tonic-gate 
732*7c478bd9Sstevel@tonic-gate /*
733*7c478bd9Sstevel@tonic-gate  * Logging routine -
734*7c478bd9Sstevel@tonic-gate  *	where = SULOG or CONSOLE
735*7c478bd9Sstevel@tonic-gate  *	towho = specified user ( user being su'ed to )
736*7c478bd9Sstevel@tonic-gate  *	how = 0 if su attempt failed; 1 if su attempt succeeded
737*7c478bd9Sstevel@tonic-gate  */
738*7c478bd9Sstevel@tonic-gate static void
739*7c478bd9Sstevel@tonic-gate log(char *where, char *towho, int how)
740*7c478bd9Sstevel@tonic-gate {
741*7c478bd9Sstevel@tonic-gate 	FILE *logf;
742*7c478bd9Sstevel@tonic-gate 	time_t now;
743*7c478bd9Sstevel@tonic-gate 	struct tm *tmp;
744*7c478bd9Sstevel@tonic-gate 
745*7c478bd9Sstevel@tonic-gate 	/*
746*7c478bd9Sstevel@tonic-gate 	 * open SULOG or CONSOLE - if open fails, return
747*7c478bd9Sstevel@tonic-gate 	 */
748*7c478bd9Sstevel@tonic-gate 	if ((logf = fopen(where, "a")) == NULL)
749*7c478bd9Sstevel@tonic-gate 		return;
750*7c478bd9Sstevel@tonic-gate 
751*7c478bd9Sstevel@tonic-gate 	now = time(0);
752*7c478bd9Sstevel@tonic-gate 	tmp = localtime(&now);
753*7c478bd9Sstevel@tonic-gate 
754*7c478bd9Sstevel@tonic-gate 	/*
755*7c478bd9Sstevel@tonic-gate 	 * write entry into SULOG or onto CONSOLE - if write fails, return
756*7c478bd9Sstevel@tonic-gate 	 */
757*7c478bd9Sstevel@tonic-gate 	(void) fprintf(logf, "SU %.2d/%.2d %.2d:%.2d %c %s %s-%s\n",
758*7c478bd9Sstevel@tonic-gate 	    tmp->tm_mon + 1, tmp->tm_mday, tmp->tm_hour, tmp->tm_min,
759*7c478bd9Sstevel@tonic-gate 	    how ? '+' : '-', ttyn + sizeof ("/dev/") - 1, username, towho);
760*7c478bd9Sstevel@tonic-gate 
761*7c478bd9Sstevel@tonic-gate 	(void) fclose(logf);	/* close SULOG or CONSOLE */
762*7c478bd9Sstevel@tonic-gate }
763*7c478bd9Sstevel@tonic-gate 
764*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
765*7c478bd9Sstevel@tonic-gate static void
766*7c478bd9Sstevel@tonic-gate to(int sig)
767*7c478bd9Sstevel@tonic-gate {}
768*7c478bd9Sstevel@tonic-gate 
769*7c478bd9Sstevel@tonic-gate #ifdef DYNAMIC_SU
770*7c478bd9Sstevel@tonic-gate /*
771*7c478bd9Sstevel@tonic-gate  * su_conv():
772*7c478bd9Sstevel@tonic-gate  *	This is the conv (conversation) function called from
773*7c478bd9Sstevel@tonic-gate  *	a PAM authentication module to print error messages
774*7c478bd9Sstevel@tonic-gate  *	or garner information from the user.
775*7c478bd9Sstevel@tonic-gate  */
776*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
777*7c478bd9Sstevel@tonic-gate static int
778*7c478bd9Sstevel@tonic-gate su_conv(int num_msg, struct pam_message **msg, struct pam_response **response,
779*7c478bd9Sstevel@tonic-gate     void *appdata_ptr)
780*7c478bd9Sstevel@tonic-gate {
781*7c478bd9Sstevel@tonic-gate 	struct pam_message	*m;
782*7c478bd9Sstevel@tonic-gate 	struct pam_response	*r;
783*7c478bd9Sstevel@tonic-gate 	char			*temp;
784*7c478bd9Sstevel@tonic-gate 	int			k;
785*7c478bd9Sstevel@tonic-gate 	char			respbuf[PAM_MAX_RESP_SIZE];
786*7c478bd9Sstevel@tonic-gate 
787*7c478bd9Sstevel@tonic-gate 	if (num_msg <= 0)
788*7c478bd9Sstevel@tonic-gate 		return (PAM_CONV_ERR);
789*7c478bd9Sstevel@tonic-gate 
790*7c478bd9Sstevel@tonic-gate 	*response = (struct pam_response *)calloc(num_msg,
791*7c478bd9Sstevel@tonic-gate 	    sizeof (struct pam_response));
792*7c478bd9Sstevel@tonic-gate 	if (*response == NULL)
793*7c478bd9Sstevel@tonic-gate 		return (PAM_BUF_ERR);
794*7c478bd9Sstevel@tonic-gate 
795*7c478bd9Sstevel@tonic-gate 	k = num_msg;
796*7c478bd9Sstevel@tonic-gate 	m = *msg;
797*7c478bd9Sstevel@tonic-gate 	r = *response;
798*7c478bd9Sstevel@tonic-gate 	while (k--) {
799*7c478bd9Sstevel@tonic-gate 
800*7c478bd9Sstevel@tonic-gate 		switch (m->msg_style) {
801*7c478bd9Sstevel@tonic-gate 
802*7c478bd9Sstevel@tonic-gate 		case PAM_PROMPT_ECHO_OFF:
803*7c478bd9Sstevel@tonic-gate 			errno = 0;
804*7c478bd9Sstevel@tonic-gate 			temp = getpassphrase(m->msg);
805*7c478bd9Sstevel@tonic-gate 			if (errno == EINTR)
806*7c478bd9Sstevel@tonic-gate 				return (PAM_CONV_ERR);
807*7c478bd9Sstevel@tonic-gate 			if (temp != NULL) {
808*7c478bd9Sstevel@tonic-gate 				r->resp = strdup(temp);
809*7c478bd9Sstevel@tonic-gate 				if (r->resp == NULL) {
810*7c478bd9Sstevel@tonic-gate 					freeresponse(num_msg, response);
811*7c478bd9Sstevel@tonic-gate 					return (PAM_BUF_ERR);
812*7c478bd9Sstevel@tonic-gate 				}
813*7c478bd9Sstevel@tonic-gate 			}
814*7c478bd9Sstevel@tonic-gate 			break;
815*7c478bd9Sstevel@tonic-gate 
816*7c478bd9Sstevel@tonic-gate 		case PAM_PROMPT_ECHO_ON:
817*7c478bd9Sstevel@tonic-gate 			if (m->msg != NULL) {
818*7c478bd9Sstevel@tonic-gate 				(void) fputs(m->msg, stdout);
819*7c478bd9Sstevel@tonic-gate 			}
820*7c478bd9Sstevel@tonic-gate 
821*7c478bd9Sstevel@tonic-gate 			(void) fgets(respbuf, sizeof (respbuf), stdin);
822*7c478bd9Sstevel@tonic-gate 			temp = strchr(respbuf, '\n');
823*7c478bd9Sstevel@tonic-gate 			if (temp != NULL)
824*7c478bd9Sstevel@tonic-gate 				*temp = '\0';
825*7c478bd9Sstevel@tonic-gate 
826*7c478bd9Sstevel@tonic-gate 			r->resp = strdup(respbuf);
827*7c478bd9Sstevel@tonic-gate 			if (r->resp == NULL) {
828*7c478bd9Sstevel@tonic-gate 				freeresponse(num_msg, response);
829*7c478bd9Sstevel@tonic-gate 				return (PAM_BUF_ERR);
830*7c478bd9Sstevel@tonic-gate 			}
831*7c478bd9Sstevel@tonic-gate 			break;
832*7c478bd9Sstevel@tonic-gate 
833*7c478bd9Sstevel@tonic-gate 		case PAM_ERROR_MSG:
834*7c478bd9Sstevel@tonic-gate 			if (m->msg != NULL) {
835*7c478bd9Sstevel@tonic-gate 				(void) fputs(m->msg, stderr);
836*7c478bd9Sstevel@tonic-gate 				(void) fputs("\n", stderr);
837*7c478bd9Sstevel@tonic-gate 			}
838*7c478bd9Sstevel@tonic-gate 			break;
839*7c478bd9Sstevel@tonic-gate 
840*7c478bd9Sstevel@tonic-gate 		case PAM_TEXT_INFO:
841*7c478bd9Sstevel@tonic-gate 			if (m->msg != NULL) {
842*7c478bd9Sstevel@tonic-gate 				(void) fputs(m->msg, stdout);
843*7c478bd9Sstevel@tonic-gate 				(void) fputs("\n", stdout);
844*7c478bd9Sstevel@tonic-gate 			}
845*7c478bd9Sstevel@tonic-gate 			break;
846*7c478bd9Sstevel@tonic-gate 
847*7c478bd9Sstevel@tonic-gate 		default:
848*7c478bd9Sstevel@tonic-gate 			break;
849*7c478bd9Sstevel@tonic-gate 		}
850*7c478bd9Sstevel@tonic-gate 		m++;
851*7c478bd9Sstevel@tonic-gate 		r++;
852*7c478bd9Sstevel@tonic-gate 	}
853*7c478bd9Sstevel@tonic-gate 	return (PAM_SUCCESS);
854*7c478bd9Sstevel@tonic-gate }
855*7c478bd9Sstevel@tonic-gate 
856*7c478bd9Sstevel@tonic-gate /*
857*7c478bd9Sstevel@tonic-gate  * emb_su_conv():
858*7c478bd9Sstevel@tonic-gate  *	This is the conv (conversation) function called from
859*7c478bd9Sstevel@tonic-gate  *	a PAM authentication module to print error messages
860*7c478bd9Sstevel@tonic-gate  *	or garner information from the user.
861*7c478bd9Sstevel@tonic-gate  *	This version is used for embedded_su.
862*7c478bd9Sstevel@tonic-gate  */
863*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
864*7c478bd9Sstevel@tonic-gate static int
865*7c478bd9Sstevel@tonic-gate emb_su_conv(int num_msg, struct pam_message **msg,
866*7c478bd9Sstevel@tonic-gate     struct pam_response **response, void *appdata_ptr)
867*7c478bd9Sstevel@tonic-gate {
868*7c478bd9Sstevel@tonic-gate 	struct pam_message	*m;
869*7c478bd9Sstevel@tonic-gate 	struct pam_response	*r;
870*7c478bd9Sstevel@tonic-gate 	char			*temp;
871*7c478bd9Sstevel@tonic-gate 	int			k;
872*7c478bd9Sstevel@tonic-gate 	char			respbuf[PAM_MAX_RESP_SIZE];
873*7c478bd9Sstevel@tonic-gate 
874*7c478bd9Sstevel@tonic-gate 	if (num_msg <= 0)
875*7c478bd9Sstevel@tonic-gate 		return (PAM_CONV_ERR);
876*7c478bd9Sstevel@tonic-gate 
877*7c478bd9Sstevel@tonic-gate 	*response = (struct pam_response *)calloc(num_msg,
878*7c478bd9Sstevel@tonic-gate 	    sizeof (struct pam_response));
879*7c478bd9Sstevel@tonic-gate 	if (*response == NULL)
880*7c478bd9Sstevel@tonic-gate 		return (PAM_BUF_ERR);
881*7c478bd9Sstevel@tonic-gate 
882*7c478bd9Sstevel@tonic-gate 	/* First, send the prompts */
883*7c478bd9Sstevel@tonic-gate 	(void) printf("CONV %d\n", num_msg);
884*7c478bd9Sstevel@tonic-gate 	k = num_msg;
885*7c478bd9Sstevel@tonic-gate 	m = *msg;
886*7c478bd9Sstevel@tonic-gate 	while (k--) {
887*7c478bd9Sstevel@tonic-gate 		switch (m->msg_style) {
888*7c478bd9Sstevel@tonic-gate 
889*7c478bd9Sstevel@tonic-gate 		case PAM_PROMPT_ECHO_OFF:
890*7c478bd9Sstevel@tonic-gate 			(void) puts("PAM_PROMPT_ECHO_OFF");
891*7c478bd9Sstevel@tonic-gate 			goto msg_common;
892*7c478bd9Sstevel@tonic-gate 
893*7c478bd9Sstevel@tonic-gate 		case PAM_PROMPT_ECHO_ON:
894*7c478bd9Sstevel@tonic-gate 			(void) puts("PAM_PROMPT_ECHO_ON");
895*7c478bd9Sstevel@tonic-gate 			goto msg_common;
896*7c478bd9Sstevel@tonic-gate 
897*7c478bd9Sstevel@tonic-gate 		case PAM_ERROR_MSG:
898*7c478bd9Sstevel@tonic-gate 			(void) puts("PAM_ERROR_MSG");
899*7c478bd9Sstevel@tonic-gate 			goto msg_common;
900*7c478bd9Sstevel@tonic-gate 
901*7c478bd9Sstevel@tonic-gate 		case PAM_TEXT_INFO:
902*7c478bd9Sstevel@tonic-gate 			(void) puts("PAM_TEXT_INFO");
903*7c478bd9Sstevel@tonic-gate 			/* fall through to msg_common */
904*7c478bd9Sstevel@tonic-gate msg_common:
905*7c478bd9Sstevel@tonic-gate 			if (m->msg == NULL)
906*7c478bd9Sstevel@tonic-gate 				quotemsg(NULL);
907*7c478bd9Sstevel@tonic-gate 			else
908*7c478bd9Sstevel@tonic-gate 				quotemsg("%s", m->msg);
909*7c478bd9Sstevel@tonic-gate 			break;
910*7c478bd9Sstevel@tonic-gate 
911*7c478bd9Sstevel@tonic-gate 		default:
912*7c478bd9Sstevel@tonic-gate 			break;
913*7c478bd9Sstevel@tonic-gate 		}
914*7c478bd9Sstevel@tonic-gate 		m++;
915*7c478bd9Sstevel@tonic-gate 	}
916*7c478bd9Sstevel@tonic-gate 
917*7c478bd9Sstevel@tonic-gate 	/* Next, collect the responses */
918*7c478bd9Sstevel@tonic-gate 	k = num_msg;
919*7c478bd9Sstevel@tonic-gate 	m = *msg;
920*7c478bd9Sstevel@tonic-gate 	r = *response;
921*7c478bd9Sstevel@tonic-gate 	while (k--) {
922*7c478bd9Sstevel@tonic-gate 
923*7c478bd9Sstevel@tonic-gate 		switch (m->msg_style) {
924*7c478bd9Sstevel@tonic-gate 
925*7c478bd9Sstevel@tonic-gate 		case PAM_PROMPT_ECHO_OFF:
926*7c478bd9Sstevel@tonic-gate 		case PAM_PROMPT_ECHO_ON:
927*7c478bd9Sstevel@tonic-gate 			(void) fgets(respbuf, sizeof (respbuf), stdin);
928*7c478bd9Sstevel@tonic-gate 
929*7c478bd9Sstevel@tonic-gate 			temp = strchr(respbuf, '\n');
930*7c478bd9Sstevel@tonic-gate 			if (temp != NULL)
931*7c478bd9Sstevel@tonic-gate 				*temp = '\0';
932*7c478bd9Sstevel@tonic-gate 
933*7c478bd9Sstevel@tonic-gate 			r->resp = strdup(respbuf);
934*7c478bd9Sstevel@tonic-gate 			if (r->resp == NULL) {
935*7c478bd9Sstevel@tonic-gate 				freeresponse(num_msg, response);
936*7c478bd9Sstevel@tonic-gate 				return (PAM_BUF_ERR);
937*7c478bd9Sstevel@tonic-gate 			}
938*7c478bd9Sstevel@tonic-gate 
939*7c478bd9Sstevel@tonic-gate 			break;
940*7c478bd9Sstevel@tonic-gate 
941*7c478bd9Sstevel@tonic-gate 		case PAM_ERROR_MSG:
942*7c478bd9Sstevel@tonic-gate 		case PAM_TEXT_INFO:
943*7c478bd9Sstevel@tonic-gate 			break;
944*7c478bd9Sstevel@tonic-gate 
945*7c478bd9Sstevel@tonic-gate 		default:
946*7c478bd9Sstevel@tonic-gate 			break;
947*7c478bd9Sstevel@tonic-gate 		}
948*7c478bd9Sstevel@tonic-gate 		m++;
949*7c478bd9Sstevel@tonic-gate 		r++;
950*7c478bd9Sstevel@tonic-gate 	}
951*7c478bd9Sstevel@tonic-gate 	return (PAM_SUCCESS);
952*7c478bd9Sstevel@tonic-gate }
953*7c478bd9Sstevel@tonic-gate 
954*7c478bd9Sstevel@tonic-gate static void
955*7c478bd9Sstevel@tonic-gate freeresponse(int num_msg, struct pam_response **response)
956*7c478bd9Sstevel@tonic-gate {
957*7c478bd9Sstevel@tonic-gate 	struct pam_response *r;
958*7c478bd9Sstevel@tonic-gate 	int i;
959*7c478bd9Sstevel@tonic-gate 
960*7c478bd9Sstevel@tonic-gate 	/* free responses */
961*7c478bd9Sstevel@tonic-gate 	r = *response;
962*7c478bd9Sstevel@tonic-gate 	for (i = 0; i < num_msg; i++, r++) {
963*7c478bd9Sstevel@tonic-gate 		if (r->resp != NULL) {
964*7c478bd9Sstevel@tonic-gate 			/* Zap it in case it's a password */
965*7c478bd9Sstevel@tonic-gate 			(void) memset(r->resp, '\0', strlen(r->resp));
966*7c478bd9Sstevel@tonic-gate 			free(r->resp);
967*7c478bd9Sstevel@tonic-gate 		}
968*7c478bd9Sstevel@tonic-gate 	}
969*7c478bd9Sstevel@tonic-gate 	free(*response);
970*7c478bd9Sstevel@tonic-gate 	*response = NULL;
971*7c478bd9Sstevel@tonic-gate }
972*7c478bd9Sstevel@tonic-gate 
973*7c478bd9Sstevel@tonic-gate /*
974*7c478bd9Sstevel@tonic-gate  * Print a message, applying quoting for lines starting with '.'.
975*7c478bd9Sstevel@tonic-gate  *
976*7c478bd9Sstevel@tonic-gate  * I18n note:  \n is "safe" in all locales, and all locales use
977*7c478bd9Sstevel@tonic-gate  * a high-bit-set character to start multibyte sequences, so
978*7c478bd9Sstevel@tonic-gate  * scanning for a \n followed by a '.' is safe.
979*7c478bd9Sstevel@tonic-gate  */
980*7c478bd9Sstevel@tonic-gate static void
981*7c478bd9Sstevel@tonic-gate quotemsg(char *fmt, ...)
982*7c478bd9Sstevel@tonic-gate {
983*7c478bd9Sstevel@tonic-gate 	if (fmt != NULL) {
984*7c478bd9Sstevel@tonic-gate 		char *msg;
985*7c478bd9Sstevel@tonic-gate 		char *p;
986*7c478bd9Sstevel@tonic-gate 		boolean_t bol;
987*7c478bd9Sstevel@tonic-gate 		va_list v;
988*7c478bd9Sstevel@tonic-gate 
989*7c478bd9Sstevel@tonic-gate 		va_start(v, fmt);
990*7c478bd9Sstevel@tonic-gate 		msg = alloc_vsprintf(fmt, v);
991*7c478bd9Sstevel@tonic-gate 		va_end(v);
992*7c478bd9Sstevel@tonic-gate 
993*7c478bd9Sstevel@tonic-gate 		bol = B_TRUE;
994*7c478bd9Sstevel@tonic-gate 		for (p = msg; *p != '\0'; p++) {
995*7c478bd9Sstevel@tonic-gate 			if (bol) {
996*7c478bd9Sstevel@tonic-gate 				if (*p == '.')
997*7c478bd9Sstevel@tonic-gate 					(void) putchar('.');
998*7c478bd9Sstevel@tonic-gate 				bol = B_FALSE;
999*7c478bd9Sstevel@tonic-gate 			}
1000*7c478bd9Sstevel@tonic-gate 			(void) putchar(*p);
1001*7c478bd9Sstevel@tonic-gate 			if (*p == '\n')
1002*7c478bd9Sstevel@tonic-gate 				bol = B_TRUE;
1003*7c478bd9Sstevel@tonic-gate 		}
1004*7c478bd9Sstevel@tonic-gate 		(void) putchar('\n');
1005*7c478bd9Sstevel@tonic-gate 		free(msg);
1006*7c478bd9Sstevel@tonic-gate 	}
1007*7c478bd9Sstevel@tonic-gate 	(void) putchar('.');
1008*7c478bd9Sstevel@tonic-gate 	(void) putchar('\n');
1009*7c478bd9Sstevel@tonic-gate }
1010*7c478bd9Sstevel@tonic-gate 
1011*7c478bd9Sstevel@tonic-gate /*
1012*7c478bd9Sstevel@tonic-gate  * validate - Check that the account is valid for switching to.
1013*7c478bd9Sstevel@tonic-gate  *
1014*7c478bd9Sstevel@tonic-gate  * If the password has expired, we must refuse the 'su' attempt
1015*7c478bd9Sstevel@tonic-gate  * regardless of whether the password is null or not.
1016*7c478bd9Sstevel@tonic-gate  *
1017*7c478bd9Sstevel@tonic-gate  * If the password is NULL but has NOT expired then we allow the
1018*7c478bd9Sstevel@tonic-gate  * su attempt to succeed.
1019*7c478bd9Sstevel@tonic-gate  */
1020*7c478bd9Sstevel@tonic-gate static void
1021*7c478bd9Sstevel@tonic-gate validate(char *usernam)
1022*7c478bd9Sstevel@tonic-gate {
1023*7c478bd9Sstevel@tonic-gate 	int error = 0;
1024*7c478bd9Sstevel@tonic-gate 
1025*7c478bd9Sstevel@tonic-gate 	if ((error = pam_acct_mgmt(pamh, 0)) != 0) {
1026*7c478bd9Sstevel@tonic-gate 		if (Sulog != NULL)
1027*7c478bd9Sstevel@tonic-gate 			log(Sulog, pwd.pw_name, 0);    /* log entry */
1028*7c478bd9Sstevel@tonic-gate 		if (error == PAM_NEW_AUTHTOK_REQD) {
1029*7c478bd9Sstevel@tonic-gate 			message(ERR, gettext("Password for user "
1030*7c478bd9Sstevel@tonic-gate 			    "'%s' has expired - use passwd(1) to update it"),
1031*7c478bd9Sstevel@tonic-gate 			    pwd.pw_name);
1032*7c478bd9Sstevel@tonic-gate 			    audit_su_bad_authentication();
1033*7c478bd9Sstevel@tonic-gate 			    if (dosyslog)
1034*7c478bd9Sstevel@tonic-gate 				syslog(LOG_CRIT, "'su %s' failed for %s on %s",
1035*7c478bd9Sstevel@tonic-gate 				    pwd.pw_name, usernam, ttyn);
1036*7c478bd9Sstevel@tonic-gate 			closelog();
1037*7c478bd9Sstevel@tonic-gate 			exit(1);
1038*7c478bd9Sstevel@tonic-gate 		} else {
1039*7c478bd9Sstevel@tonic-gate 			message(ERR, gettext("Sorry"));
1040*7c478bd9Sstevel@tonic-gate 			audit_su_bad_authentication();
1041*7c478bd9Sstevel@tonic-gate 			if (dosyslog)
1042*7c478bd9Sstevel@tonic-gate 			    syslog(LOG_CRIT, "'su %s' failed for %s on %s",
1043*7c478bd9Sstevel@tonic-gate 				pwd.pw_name, usernam, ttyn);
1044*7c478bd9Sstevel@tonic-gate 			closelog();
1045*7c478bd9Sstevel@tonic-gate 			exit(3);
1046*7c478bd9Sstevel@tonic-gate 		}
1047*7c478bd9Sstevel@tonic-gate 	}
1048*7c478bd9Sstevel@tonic-gate }
1049*7c478bd9Sstevel@tonic-gate 
1050*7c478bd9Sstevel@tonic-gate static char *illegal[] = {
1051*7c478bd9Sstevel@tonic-gate 	"SHELL=",
1052*7c478bd9Sstevel@tonic-gate 	"HOME=",
1053*7c478bd9Sstevel@tonic-gate 	"LOGNAME=",
1054*7c478bd9Sstevel@tonic-gate #ifndef NO_MAIL
1055*7c478bd9Sstevel@tonic-gate 	"MAIL=",
1056*7c478bd9Sstevel@tonic-gate #endif
1057*7c478bd9Sstevel@tonic-gate 	"CDPATH=",
1058*7c478bd9Sstevel@tonic-gate 	"IFS=",
1059*7c478bd9Sstevel@tonic-gate 	"PATH=",
1060*7c478bd9Sstevel@tonic-gate 	"TZ=",
1061*7c478bd9Sstevel@tonic-gate 	"HZ=",
1062*7c478bd9Sstevel@tonic-gate 	"TERM=",
1063*7c478bd9Sstevel@tonic-gate 	0
1064*7c478bd9Sstevel@tonic-gate };
1065*7c478bd9Sstevel@tonic-gate 
1066*7c478bd9Sstevel@tonic-gate /*
1067*7c478bd9Sstevel@tonic-gate  * legalenvvar - can PAM modules insert this environmental variable?
1068*7c478bd9Sstevel@tonic-gate  */
1069*7c478bd9Sstevel@tonic-gate 
1070*7c478bd9Sstevel@tonic-gate static int
1071*7c478bd9Sstevel@tonic-gate legalenvvar(char *s)
1072*7c478bd9Sstevel@tonic-gate {
1073*7c478bd9Sstevel@tonic-gate 	register char **p;
1074*7c478bd9Sstevel@tonic-gate 
1075*7c478bd9Sstevel@tonic-gate 	for (p = illegal; *p; p++)
1076*7c478bd9Sstevel@tonic-gate 		if (strncmp(s, *p, strlen(*p)) == 0)
1077*7c478bd9Sstevel@tonic-gate 			return (0);
1078*7c478bd9Sstevel@tonic-gate 
1079*7c478bd9Sstevel@tonic-gate 	if (s[0] == 'L' && s[1] == 'D' && s[2] == '_')
1080*7c478bd9Sstevel@tonic-gate 		return (0);
1081*7c478bd9Sstevel@tonic-gate 
1082*7c478bd9Sstevel@tonic-gate 	return (1);
1083*7c478bd9Sstevel@tonic-gate }
1084*7c478bd9Sstevel@tonic-gate 
1085*7c478bd9Sstevel@tonic-gate /*
1086*7c478bd9Sstevel@tonic-gate  * The embedded_su protocol allows the client application to supply
1087*7c478bd9Sstevel@tonic-gate  * an initialization block terminated by a line with just a "." on it.
1088*7c478bd9Sstevel@tonic-gate  *
1089*7c478bd9Sstevel@tonic-gate  * This initialization block is currently unused, reserved for future
1090*7c478bd9Sstevel@tonic-gate  * expansion.  Ignore it.  This is made very slightly more complex by
1091*7c478bd9Sstevel@tonic-gate  * the desire to cleanly ignore input lines of any length, while still
1092*7c478bd9Sstevel@tonic-gate  * correctly detecting a line with just a "." on it.
1093*7c478bd9Sstevel@tonic-gate  *
1094*7c478bd9Sstevel@tonic-gate  * I18n note:  It appears that none of the Solaris-supported locales
1095*7c478bd9Sstevel@tonic-gate  * use 0x0a for any purpose other than newline, so looking for '\n'
1096*7c478bd9Sstevel@tonic-gate  * seems safe.
1097*7c478bd9Sstevel@tonic-gate  * All locales use high-bit-set leadin characters for their multi-byte
1098*7c478bd9Sstevel@tonic-gate  * sequences, so a line consisting solely of ".\n" is what it appears
1099*7c478bd9Sstevel@tonic-gate  * to be.
1100*7c478bd9Sstevel@tonic-gate  */
1101*7c478bd9Sstevel@tonic-gate static void
1102*7c478bd9Sstevel@tonic-gate readinitblock(void)
1103*7c478bd9Sstevel@tonic-gate {
1104*7c478bd9Sstevel@tonic-gate 	char buf[100];
1105*7c478bd9Sstevel@tonic-gate 	boolean_t bol;
1106*7c478bd9Sstevel@tonic-gate 
1107*7c478bd9Sstevel@tonic-gate 	bol = B_TRUE;
1108*7c478bd9Sstevel@tonic-gate 	for (;;) {
1109*7c478bd9Sstevel@tonic-gate 		if (fgets(buf, sizeof (buf), stdin) == NULL)
1110*7c478bd9Sstevel@tonic-gate 			return;
1111*7c478bd9Sstevel@tonic-gate 		if (bol && strcmp(buf, ".\n") == 0)
1112*7c478bd9Sstevel@tonic-gate 			return;
1113*7c478bd9Sstevel@tonic-gate 		bol = (strchr(buf, '\n') != NULL);
1114*7c478bd9Sstevel@tonic-gate 	}
1115*7c478bd9Sstevel@tonic-gate }
1116*7c478bd9Sstevel@tonic-gate #endif	/* DYNAMIC_SU */
1117*7c478bd9Sstevel@tonic-gate 
1118*7c478bd9Sstevel@tonic-gate /*
1119*7c478bd9Sstevel@tonic-gate  * Report an error, either a fatal one, a warning, or a usage message,
1120*7c478bd9Sstevel@tonic-gate  * depending on the mode parameter.
1121*7c478bd9Sstevel@tonic-gate  */
1122*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
1123*7c478bd9Sstevel@tonic-gate static void
1124*7c478bd9Sstevel@tonic-gate message(enum messagemode mode, char *fmt, ...)
1125*7c478bd9Sstevel@tonic-gate {
1126*7c478bd9Sstevel@tonic-gate 	char *s;
1127*7c478bd9Sstevel@tonic-gate 	va_list v;
1128*7c478bd9Sstevel@tonic-gate 
1129*7c478bd9Sstevel@tonic-gate 	va_start(v, fmt);
1130*7c478bd9Sstevel@tonic-gate 	s = alloc_vsprintf(fmt, v);
1131*7c478bd9Sstevel@tonic-gate 	va_end(v);
1132*7c478bd9Sstevel@tonic-gate 
1133*7c478bd9Sstevel@tonic-gate #ifdef	DYNAMIC_SU
1134*7c478bd9Sstevel@tonic-gate 	if (embedded) {
1135*7c478bd9Sstevel@tonic-gate 		if (mode == WARN) {
1136*7c478bd9Sstevel@tonic-gate 			(void) printf("CONV 1\n");
1137*7c478bd9Sstevel@tonic-gate 			(void) printf("PAM_ERROR_MSG\n");
1138*7c478bd9Sstevel@tonic-gate 		} else { /* ERR, USAGE */
1139*7c478bd9Sstevel@tonic-gate 			(void) printf("ERROR\n");
1140*7c478bd9Sstevel@tonic-gate 		}
1141*7c478bd9Sstevel@tonic-gate 		if (mode == USAGE) {
1142*7c478bd9Sstevel@tonic-gate 			quotemsg("%s", s);
1143*7c478bd9Sstevel@tonic-gate 		} else { /* ERR, WARN */
1144*7c478bd9Sstevel@tonic-gate 			quotemsg("%s: %s", myname, s);
1145*7c478bd9Sstevel@tonic-gate 		}
1146*7c478bd9Sstevel@tonic-gate 	} else {
1147*7c478bd9Sstevel@tonic-gate #endif	/* DYNAMIC_SU */
1148*7c478bd9Sstevel@tonic-gate 		if (mode == USAGE) {
1149*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, "%s\n", s);
1150*7c478bd9Sstevel@tonic-gate 		} else { /* ERR, WARN */
1151*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, "%s: %s\n", myname, s);
1152*7c478bd9Sstevel@tonic-gate 		}
1153*7c478bd9Sstevel@tonic-gate #ifdef	DYNAMIC_SU
1154*7c478bd9Sstevel@tonic-gate 	}
1155*7c478bd9Sstevel@tonic-gate #endif	/* DYNAMIC_SU */
1156*7c478bd9Sstevel@tonic-gate 
1157*7c478bd9Sstevel@tonic-gate 	free(s);
1158*7c478bd9Sstevel@tonic-gate }
1159*7c478bd9Sstevel@tonic-gate 
1160*7c478bd9Sstevel@tonic-gate /*
1161*7c478bd9Sstevel@tonic-gate  * Return a pointer to the last path component of a.
1162*7c478bd9Sstevel@tonic-gate  */
1163*7c478bd9Sstevel@tonic-gate static char *
1164*7c478bd9Sstevel@tonic-gate tail(char *a)
1165*7c478bd9Sstevel@tonic-gate {
1166*7c478bd9Sstevel@tonic-gate 	char *p;
1167*7c478bd9Sstevel@tonic-gate 
1168*7c478bd9Sstevel@tonic-gate 	p = strrchr(a, '/');
1169*7c478bd9Sstevel@tonic-gate 	if (p == NULL)
1170*7c478bd9Sstevel@tonic-gate 		p = a;
1171*7c478bd9Sstevel@tonic-gate 	else
1172*7c478bd9Sstevel@tonic-gate 		p++;	/* step over the '/' */
1173*7c478bd9Sstevel@tonic-gate 
1174*7c478bd9Sstevel@tonic-gate 	return (p);
1175*7c478bd9Sstevel@tonic-gate }
1176*7c478bd9Sstevel@tonic-gate 
1177*7c478bd9Sstevel@tonic-gate static char *
1178*7c478bd9Sstevel@tonic-gate alloc_vsprintf(const char *fmt, va_list ap1)
1179*7c478bd9Sstevel@tonic-gate {
1180*7c478bd9Sstevel@tonic-gate 	va_list ap2;
1181*7c478bd9Sstevel@tonic-gate 	int n;
1182*7c478bd9Sstevel@tonic-gate 	char buf[1];
1183*7c478bd9Sstevel@tonic-gate 	char *s;
1184*7c478bd9Sstevel@tonic-gate 
1185*7c478bd9Sstevel@tonic-gate 	/*
1186*7c478bd9Sstevel@tonic-gate 	 * We need to scan the argument list twice.  Save off a copy
1187*7c478bd9Sstevel@tonic-gate 	 * of the argument list pointer(s) for the second pass.  Note that
1188*7c478bd9Sstevel@tonic-gate 	 * we are responsible for va_end'ing our copy.
1189*7c478bd9Sstevel@tonic-gate 	 */
1190*7c478bd9Sstevel@tonic-gate 	va_copy(ap2, ap1);
1191*7c478bd9Sstevel@tonic-gate 
1192*7c478bd9Sstevel@tonic-gate 	/*
1193*7c478bd9Sstevel@tonic-gate 	 * vsnprintf into a dummy to get a length.  One might
1194*7c478bd9Sstevel@tonic-gate 	 * think that passing 0 as the length to snprintf would
1195*7c478bd9Sstevel@tonic-gate 	 * do what we want, but it's defined not to.
1196*7c478bd9Sstevel@tonic-gate 	 *
1197*7c478bd9Sstevel@tonic-gate 	 * Perhaps we should sprintf into a 100 character buffer
1198*7c478bd9Sstevel@tonic-gate 	 * or something like that, to avoid two calls to snprintf
1199*7c478bd9Sstevel@tonic-gate 	 * in most cases.
1200*7c478bd9Sstevel@tonic-gate 	 */
1201*7c478bd9Sstevel@tonic-gate 	n = vsnprintf(buf, sizeof (buf), fmt, ap2);
1202*7c478bd9Sstevel@tonic-gate 	va_end(ap2);
1203*7c478bd9Sstevel@tonic-gate 
1204*7c478bd9Sstevel@tonic-gate 	/*
1205*7c478bd9Sstevel@tonic-gate 	 * Allocate an appropriately-sized buffer.
1206*7c478bd9Sstevel@tonic-gate 	 */
1207*7c478bd9Sstevel@tonic-gate 	s = malloc(n + 1);
1208*7c478bd9Sstevel@tonic-gate 	if (s == NULL) {
1209*7c478bd9Sstevel@tonic-gate 		perror("malloc");
1210*7c478bd9Sstevel@tonic-gate 		exit(4);
1211*7c478bd9Sstevel@tonic-gate 	}
1212*7c478bd9Sstevel@tonic-gate 
1213*7c478bd9Sstevel@tonic-gate 	(void) vsnprintf(s, n+1, fmt, ap1);
1214*7c478bd9Sstevel@tonic-gate 
1215*7c478bd9Sstevel@tonic-gate 	return (s);
1216*7c478bd9Sstevel@tonic-gate }
1217