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