xref: /onnv-gate/usr/src/cmd/sendmail/aux/smrsh.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * Copyright (c) 1998-2004 Sendmail, Inc. and its suppliers.
3*0Sstevel@tonic-gate  *	All rights reserved.
4*0Sstevel@tonic-gate  * Copyright (c) 1993 Eric P. Allman.  All rights reserved.
5*0Sstevel@tonic-gate  * Copyright (c) 1993
6*0Sstevel@tonic-gate  *	The Regents of the University of California.  All rights reserved.
7*0Sstevel@tonic-gate  *
8*0Sstevel@tonic-gate  * By using this file, you agree to the terms and conditions set
9*0Sstevel@tonic-gate  * forth in the LICENSE file which can be found at the top level of
10*0Sstevel@tonic-gate  * the sendmail distribution.
11*0Sstevel@tonic-gate  *
12*0Sstevel@tonic-gate  */
13*0Sstevel@tonic-gate 
14*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
15*0Sstevel@tonic-gate 
16*0Sstevel@tonic-gate #include <sm/gen.h>
17*0Sstevel@tonic-gate 
18*0Sstevel@tonic-gate SM_IDSTR(copyright,
19*0Sstevel@tonic-gate "@(#) Copyright (c) 1998-2004 Sendmail, Inc. and its suppliers.\n\
20*0Sstevel@tonic-gate 	All rights reserved.\n\
21*0Sstevel@tonic-gate      Copyright (c) 1993 Eric P. Allman.  All rights reserved.\n\
22*0Sstevel@tonic-gate      Copyright (c) 1993\n\
23*0Sstevel@tonic-gate 	The Regents of the University of California.  All rights reserved.\n")
24*0Sstevel@tonic-gate 
25*0Sstevel@tonic-gate SM_IDSTR(id, "@(#)$Id: smrsh.c,v 8.65 2004/08/06 18:54:22 ca Exp $")
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate /*
28*0Sstevel@tonic-gate **  SMRSH -- sendmail restricted shell
29*0Sstevel@tonic-gate **
30*0Sstevel@tonic-gate **	This is a patch to get around the prog mailer bugs in most
31*0Sstevel@tonic-gate **	versions of sendmail.
32*0Sstevel@tonic-gate **
33*0Sstevel@tonic-gate **	Use this in place of /bin/sh in the "prog" mailer definition
34*0Sstevel@tonic-gate **	in your sendmail.cf file.  You then create CMDDIR (owned by
35*0Sstevel@tonic-gate **	root, mode 755) and put links to any programs you want
36*0Sstevel@tonic-gate **	available to prog mailers in that directory.  This should
37*0Sstevel@tonic-gate **	include things like "vacation" and "procmail", but not "sed"
38*0Sstevel@tonic-gate **	or "sh".
39*0Sstevel@tonic-gate **
40*0Sstevel@tonic-gate **	Leading pathnames are stripped from program names so that
41*0Sstevel@tonic-gate **	existing .forward files that reference things like
42*0Sstevel@tonic-gate **	"/usr/bin/vacation" will continue to work.
43*0Sstevel@tonic-gate **
44*0Sstevel@tonic-gate **	The following characters are completely illegal:
45*0Sstevel@tonic-gate **		<  >  ^  &  `  (  ) \n \r
46*0Sstevel@tonic-gate **	The following characters are sometimes illegal:
47*0Sstevel@tonic-gate **		|  &
48*0Sstevel@tonic-gate **	This is more restrictive than strictly necessary.
49*0Sstevel@tonic-gate **
50*0Sstevel@tonic-gate **	To use this, add FEATURE(`smrsh') to your .mc file.
51*0Sstevel@tonic-gate **
52*0Sstevel@tonic-gate **	This can be used on any version of sendmail.
53*0Sstevel@tonic-gate **
54*0Sstevel@tonic-gate **	In loving memory of RTM.  11/02/93.
55*0Sstevel@tonic-gate */
56*0Sstevel@tonic-gate 
57*0Sstevel@tonic-gate #include <unistd.h>
58*0Sstevel@tonic-gate #include <sm/io.h>
59*0Sstevel@tonic-gate #include <sm/limits.h>
60*0Sstevel@tonic-gate #include <sm/string.h>
61*0Sstevel@tonic-gate #include <sys/file.h>
62*0Sstevel@tonic-gate #include <sys/types.h>
63*0Sstevel@tonic-gate #include <sys/stat.h>
64*0Sstevel@tonic-gate #include <string.h>
65*0Sstevel@tonic-gate #include <ctype.h>
66*0Sstevel@tonic-gate #include <errno.h>
67*0Sstevel@tonic-gate #ifdef EX_OK
68*0Sstevel@tonic-gate # undef EX_OK
69*0Sstevel@tonic-gate #endif /* EX_OK */
70*0Sstevel@tonic-gate #include <sysexits.h>
71*0Sstevel@tonic-gate #include <syslog.h>
72*0Sstevel@tonic-gate #include <stdlib.h>
73*0Sstevel@tonic-gate 
74*0Sstevel@tonic-gate #include <sm/conf.h>
75*0Sstevel@tonic-gate #include <sm/errstring.h>
76*0Sstevel@tonic-gate 
77*0Sstevel@tonic-gate /* directory in which all commands must reside */
78*0Sstevel@tonic-gate #ifndef CMDDIR
79*0Sstevel@tonic-gate # ifdef SMRSH_CMDDIR
80*0Sstevel@tonic-gate #  define CMDDIR	SMRSH_CMDDIR
81*0Sstevel@tonic-gate # else /* SMRSH_CMDDIR */
82*0Sstevel@tonic-gate #  define CMDDIR	"/usr/adm/sm.bin"
83*0Sstevel@tonic-gate # endif /* SMRSH_CMDDIR */
84*0Sstevel@tonic-gate #endif /* ! CMDDIR */
85*0Sstevel@tonic-gate 
86*0Sstevel@tonic-gate /* characters disallowed in the shell "-c" argument */
87*0Sstevel@tonic-gate #define SPECIALS	"<|>^();&`$\r\n"
88*0Sstevel@tonic-gate 
89*0Sstevel@tonic-gate /* default search path */
90*0Sstevel@tonic-gate #ifndef PATH
91*0Sstevel@tonic-gate # ifdef SMRSH_PATH
92*0Sstevel@tonic-gate #  define PATH		SMRSH_PATH
93*0Sstevel@tonic-gate # else /* SMRSH_PATH */
94*0Sstevel@tonic-gate #  define PATH		"/bin:/usr/bin:/usr/ucb"
95*0Sstevel@tonic-gate # endif /* SMRSH_PATH */
96*0Sstevel@tonic-gate #endif /* ! PATH */
97*0Sstevel@tonic-gate 
98*0Sstevel@tonic-gate char newcmdbuf[1000];
99*0Sstevel@tonic-gate char *prg, *par;
100*0Sstevel@tonic-gate 
101*0Sstevel@tonic-gate static void	addcmd __P((char *, bool, size_t));
102*0Sstevel@tonic-gate 
103*0Sstevel@tonic-gate /*
104*0Sstevel@tonic-gate **  ADDCMD -- add a string to newcmdbuf, check for overflow
105*0Sstevel@tonic-gate **
106*0Sstevel@tonic-gate **    Parameters:
107*0Sstevel@tonic-gate **	s -- string to add
108*0Sstevel@tonic-gate **	cmd -- it's a command: prepend CMDDIR/
109*0Sstevel@tonic-gate **	len -- length of string to add
110*0Sstevel@tonic-gate **
111*0Sstevel@tonic-gate **    Side Effects:
112*0Sstevel@tonic-gate **	changes newcmdbuf or exits with a failure.
113*0Sstevel@tonic-gate **
114*0Sstevel@tonic-gate */
115*0Sstevel@tonic-gate 
116*0Sstevel@tonic-gate static void
addcmd(s,cmd,len)117*0Sstevel@tonic-gate addcmd(s, cmd, len)
118*0Sstevel@tonic-gate 	char *s;
119*0Sstevel@tonic-gate 	bool cmd;
120*0Sstevel@tonic-gate 	size_t len;
121*0Sstevel@tonic-gate {
122*0Sstevel@tonic-gate 	if (s == NULL || *s == '\0')
123*0Sstevel@tonic-gate 		return;
124*0Sstevel@tonic-gate 
125*0Sstevel@tonic-gate 	/* enough space for s (len) and CMDDIR + "/" and '\0'? */
126*0Sstevel@tonic-gate 	if (sizeof newcmdbuf - strlen(newcmdbuf) <=
127*0Sstevel@tonic-gate 	    len + 1 + (cmd ? (strlen(CMDDIR) + 1) : 0))
128*0Sstevel@tonic-gate 	{
129*0Sstevel@tonic-gate 		(void)sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
130*0Sstevel@tonic-gate 				    "%s: command too long: %s\n", prg, par);
131*0Sstevel@tonic-gate #ifndef DEBUG
132*0Sstevel@tonic-gate 		syslog(LOG_WARNING, "command too long: %.40s", par);
133*0Sstevel@tonic-gate #endif /* ! DEBUG */
134*0Sstevel@tonic-gate 		exit(EX_UNAVAILABLE);
135*0Sstevel@tonic-gate 	}
136*0Sstevel@tonic-gate 	if (cmd)
137*0Sstevel@tonic-gate 		(void) sm_strlcat2(newcmdbuf, CMDDIR, "/", sizeof newcmdbuf);
138*0Sstevel@tonic-gate 	(void) strncat(newcmdbuf, s, len);
139*0Sstevel@tonic-gate }
140*0Sstevel@tonic-gate 
141*0Sstevel@tonic-gate int
main(argc,argv)142*0Sstevel@tonic-gate main(argc, argv)
143*0Sstevel@tonic-gate 	int argc;
144*0Sstevel@tonic-gate 	char **argv;
145*0Sstevel@tonic-gate {
146*0Sstevel@tonic-gate 	register char *p;
147*0Sstevel@tonic-gate 	register char *q;
148*0Sstevel@tonic-gate 	register char *r;
149*0Sstevel@tonic-gate 	register char *cmd;
150*0Sstevel@tonic-gate 	int isexec;
151*0Sstevel@tonic-gate 	int save_errno;
152*0Sstevel@tonic-gate 	char *newenv[2];
153*0Sstevel@tonic-gate 	char pathbuf[1000];
154*0Sstevel@tonic-gate 	char specialbuf[32];
155*0Sstevel@tonic-gate 	struct stat st;
156*0Sstevel@tonic-gate 
157*0Sstevel@tonic-gate #ifndef DEBUG
158*0Sstevel@tonic-gate # ifndef LOG_MAIL
159*0Sstevel@tonic-gate 	openlog("smrsh", 0);
160*0Sstevel@tonic-gate # else /* ! LOG_MAIL */
161*0Sstevel@tonic-gate 	openlog("smrsh", LOG_ODELAY|LOG_CONS, LOG_MAIL);
162*0Sstevel@tonic-gate # endif /* ! LOG_MAIL */
163*0Sstevel@tonic-gate #endif /* ! DEBUG */
164*0Sstevel@tonic-gate 
165*0Sstevel@tonic-gate 	(void) sm_strlcpyn(pathbuf, sizeof pathbuf, 2, "PATH=", PATH);
166*0Sstevel@tonic-gate 	newenv[0] = pathbuf;
167*0Sstevel@tonic-gate 	newenv[1] = NULL;
168*0Sstevel@tonic-gate 
169*0Sstevel@tonic-gate 	/*
170*0Sstevel@tonic-gate 	**  Do basic argv usage checking
171*0Sstevel@tonic-gate 	*/
172*0Sstevel@tonic-gate 
173*0Sstevel@tonic-gate 	prg = argv[0];
174*0Sstevel@tonic-gate 
175*0Sstevel@tonic-gate 	if (argc != 3 || strcmp(argv[1], "-c") != 0)
176*0Sstevel@tonic-gate 	{
177*0Sstevel@tonic-gate 		(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
178*0Sstevel@tonic-gate 				     "Usage: %s -c command\n", prg);
179*0Sstevel@tonic-gate #ifndef DEBUG
180*0Sstevel@tonic-gate 		syslog(LOG_ERR, "usage");
181*0Sstevel@tonic-gate #endif /* ! DEBUG */
182*0Sstevel@tonic-gate 		exit(EX_USAGE);
183*0Sstevel@tonic-gate 	}
184*0Sstevel@tonic-gate 
185*0Sstevel@tonic-gate 	par = argv[2];
186*0Sstevel@tonic-gate 
187*0Sstevel@tonic-gate 	/*
188*0Sstevel@tonic-gate 	**  Disallow special shell syntax.  This is overly restrictive,
189*0Sstevel@tonic-gate 	**  but it should shut down all attacks.
190*0Sstevel@tonic-gate 	**  Be sure to include 8-bit versions, since many shells strip
191*0Sstevel@tonic-gate 	**  the address to 7 bits before checking.
192*0Sstevel@tonic-gate 	*/
193*0Sstevel@tonic-gate 
194*0Sstevel@tonic-gate 	if (strlen(SPECIALS) * 2 >= sizeof specialbuf)
195*0Sstevel@tonic-gate 	{
196*0Sstevel@tonic-gate #ifndef DEBUG
197*0Sstevel@tonic-gate 		syslog(LOG_ERR, "too many specials: %.40s", SPECIALS);
198*0Sstevel@tonic-gate #endif /* ! DEBUG */
199*0Sstevel@tonic-gate 		exit(EX_UNAVAILABLE);
200*0Sstevel@tonic-gate 	}
201*0Sstevel@tonic-gate 	(void) sm_strlcpy(specialbuf, SPECIALS, sizeof specialbuf);
202*0Sstevel@tonic-gate 	for (p = specialbuf; *p != '\0'; p++)
203*0Sstevel@tonic-gate 		*p |= '\200';
204*0Sstevel@tonic-gate 	(void) sm_strlcat(specialbuf, SPECIALS, sizeof specialbuf);
205*0Sstevel@tonic-gate 
206*0Sstevel@tonic-gate 	/*
207*0Sstevel@tonic-gate 	**  Do a quick sanity check on command line length.
208*0Sstevel@tonic-gate 	*/
209*0Sstevel@tonic-gate 
210*0Sstevel@tonic-gate 	if (strlen(par) > (sizeof newcmdbuf - sizeof CMDDIR - 2))
211*0Sstevel@tonic-gate 	{
212*0Sstevel@tonic-gate 		(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
213*0Sstevel@tonic-gate 				     "%s: command too long: %s\n", prg, par);
214*0Sstevel@tonic-gate #ifndef DEBUG
215*0Sstevel@tonic-gate 		syslog(LOG_WARNING, "command too long: %.40s", par);
216*0Sstevel@tonic-gate #endif /* ! DEBUG */
217*0Sstevel@tonic-gate 		exit(EX_UNAVAILABLE);
218*0Sstevel@tonic-gate 	}
219*0Sstevel@tonic-gate 
220*0Sstevel@tonic-gate 	q = par;
221*0Sstevel@tonic-gate 	newcmdbuf[0] = '\0';
222*0Sstevel@tonic-gate 	isexec = false;
223*0Sstevel@tonic-gate 
224*0Sstevel@tonic-gate 	while (*q != '\0')
225*0Sstevel@tonic-gate 	{
226*0Sstevel@tonic-gate 		/*
227*0Sstevel@tonic-gate 		**  Strip off a leading pathname on the command name.  For
228*0Sstevel@tonic-gate 		**  example, change /usr/ucb/vacation to vacation.
229*0Sstevel@tonic-gate 		*/
230*0Sstevel@tonic-gate 
231*0Sstevel@tonic-gate 		/* strip leading spaces */
232*0Sstevel@tonic-gate 		while (*q != '\0' && isascii(*q) && isspace(*q))
233*0Sstevel@tonic-gate 			q++;
234*0Sstevel@tonic-gate 		if (*q == '\0')
235*0Sstevel@tonic-gate 		{
236*0Sstevel@tonic-gate 			if (isexec)
237*0Sstevel@tonic-gate 			{
238*0Sstevel@tonic-gate 				(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
239*0Sstevel@tonic-gate 						     "%s: missing command to exec\n",
240*0Sstevel@tonic-gate 						     prg);
241*0Sstevel@tonic-gate #ifndef DEBUG
242*0Sstevel@tonic-gate 				syslog(LOG_CRIT, "uid %d: missing command to exec", (int) getuid());
243*0Sstevel@tonic-gate #endif /* ! DEBUG */
244*0Sstevel@tonic-gate 				exit(EX_UNAVAILABLE);
245*0Sstevel@tonic-gate 			}
246*0Sstevel@tonic-gate 			break;
247*0Sstevel@tonic-gate 		}
248*0Sstevel@tonic-gate 
249*0Sstevel@tonic-gate 		/* find the end of the command name */
250*0Sstevel@tonic-gate 		p = strpbrk(q, " \t");
251*0Sstevel@tonic-gate 		if (p == NULL)
252*0Sstevel@tonic-gate 			cmd = &q[strlen(q)];
253*0Sstevel@tonic-gate 		else
254*0Sstevel@tonic-gate 		{
255*0Sstevel@tonic-gate 			*p = '\0';
256*0Sstevel@tonic-gate 			cmd = p;
257*0Sstevel@tonic-gate 		}
258*0Sstevel@tonic-gate 		/* search backwards for last / (allow for 0200 bit) */
259*0Sstevel@tonic-gate 		while (cmd > q)
260*0Sstevel@tonic-gate 		{
261*0Sstevel@tonic-gate 			if ((*--cmd & 0177) == '/')
262*0Sstevel@tonic-gate 			{
263*0Sstevel@tonic-gate 				cmd++;
264*0Sstevel@tonic-gate 				break;
265*0Sstevel@tonic-gate 			}
266*0Sstevel@tonic-gate 		}
267*0Sstevel@tonic-gate 		/* cmd now points at final component of path name */
268*0Sstevel@tonic-gate 
269*0Sstevel@tonic-gate 		/* allow a few shell builtins */
270*0Sstevel@tonic-gate 		if (strcmp(q, "exec") == 0 && p != NULL)
271*0Sstevel@tonic-gate 		{
272*0Sstevel@tonic-gate 			addcmd("exec ", false, strlen("exec "));
273*0Sstevel@tonic-gate 
274*0Sstevel@tonic-gate 			/* test _next_ arg */
275*0Sstevel@tonic-gate 			q = ++p;
276*0Sstevel@tonic-gate 			isexec = true;
277*0Sstevel@tonic-gate 			continue;
278*0Sstevel@tonic-gate 		}
279*0Sstevel@tonic-gate 		else if (strcmp(q, "exit") == 0 || strcmp(q, "echo") == 0)
280*0Sstevel@tonic-gate 		{
281*0Sstevel@tonic-gate 			addcmd(cmd, false, strlen(cmd));
282*0Sstevel@tonic-gate 
283*0Sstevel@tonic-gate 			/* test following chars */
284*0Sstevel@tonic-gate 		}
285*0Sstevel@tonic-gate 		else
286*0Sstevel@tonic-gate 		{
287*0Sstevel@tonic-gate 			char cmdbuf[MAXPATHLEN];
288*0Sstevel@tonic-gate 
289*0Sstevel@tonic-gate 			/*
290*0Sstevel@tonic-gate 			**  Check to see if the command name is legal.
291*0Sstevel@tonic-gate 			*/
292*0Sstevel@tonic-gate 
293*0Sstevel@tonic-gate 			if (sm_strlcpyn(cmdbuf, sizeof cmdbuf, 3, CMDDIR,
294*0Sstevel@tonic-gate 					"/", cmd) >= sizeof cmdbuf)
295*0Sstevel@tonic-gate 			{
296*0Sstevel@tonic-gate 				/* too long */
297*0Sstevel@tonic-gate 				(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
298*0Sstevel@tonic-gate 						     "%s: \"%s\" not available for sendmail programs (filename too long)\n",
299*0Sstevel@tonic-gate 						      prg, cmd);
300*0Sstevel@tonic-gate 				if (p != NULL)
301*0Sstevel@tonic-gate 					*p = ' ';
302*0Sstevel@tonic-gate #ifndef DEBUG
303*0Sstevel@tonic-gate 				syslog(LOG_CRIT, "uid %d: attempt to use \"%s\" (filename too long)",
304*0Sstevel@tonic-gate 				       (int) getuid(), cmd);
305*0Sstevel@tonic-gate #endif /* ! DEBUG */
306*0Sstevel@tonic-gate 				exit(EX_UNAVAILABLE);
307*0Sstevel@tonic-gate 			}
308*0Sstevel@tonic-gate 
309*0Sstevel@tonic-gate #ifdef DEBUG
310*0Sstevel@tonic-gate 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
311*0Sstevel@tonic-gate 					     "Trying %s\n", cmdbuf);
312*0Sstevel@tonic-gate #endif /* DEBUG */
313*0Sstevel@tonic-gate 			if (stat(cmdbuf, &st) < 0)
314*0Sstevel@tonic-gate 			{
315*0Sstevel@tonic-gate 				/* can't stat it */
316*0Sstevel@tonic-gate 				(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
317*0Sstevel@tonic-gate 						     "%s: \"%s\" not available for sendmail programs (stat failed)\n",
318*0Sstevel@tonic-gate 						      prg, cmd);
319*0Sstevel@tonic-gate 				if (p != NULL)
320*0Sstevel@tonic-gate 					*p = ' ';
321*0Sstevel@tonic-gate #ifndef DEBUG
322*0Sstevel@tonic-gate 				syslog(LOG_CRIT, "uid %d: attempt to use \"%s\" (stat failed)",
323*0Sstevel@tonic-gate 				       (int) getuid(), cmd);
324*0Sstevel@tonic-gate #endif /* ! DEBUG */
325*0Sstevel@tonic-gate 				exit(EX_UNAVAILABLE);
326*0Sstevel@tonic-gate 			}
327*0Sstevel@tonic-gate 			if (!S_ISREG(st.st_mode)
328*0Sstevel@tonic-gate #ifdef S_ISLNK
329*0Sstevel@tonic-gate 			    && !S_ISLNK(st.st_mode)
330*0Sstevel@tonic-gate #endif /* S_ISLNK */
331*0Sstevel@tonic-gate 			   )
332*0Sstevel@tonic-gate 			{
333*0Sstevel@tonic-gate 				/* can't stat it */
334*0Sstevel@tonic-gate 				(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
335*0Sstevel@tonic-gate 						     "%s: \"%s\" not available for sendmail programs (not a file)\n",
336*0Sstevel@tonic-gate 						      prg, cmd);
337*0Sstevel@tonic-gate 				if (p != NULL)
338*0Sstevel@tonic-gate 					*p = ' ';
339*0Sstevel@tonic-gate #ifndef DEBUG
340*0Sstevel@tonic-gate 				syslog(LOG_CRIT, "uid %d: attempt to use \"%s\" (not a file)",
341*0Sstevel@tonic-gate 				       (int) getuid(), cmd);
342*0Sstevel@tonic-gate #endif /* ! DEBUG */
343*0Sstevel@tonic-gate 				exit(EX_UNAVAILABLE);
344*0Sstevel@tonic-gate 			}
345*0Sstevel@tonic-gate 			if (access(cmdbuf, X_OK) < 0)
346*0Sstevel@tonic-gate 			{
347*0Sstevel@tonic-gate 				/* oops....  crack attack possiblity */
348*0Sstevel@tonic-gate 				(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
349*0Sstevel@tonic-gate 						     "%s: \"%s\" not available for sendmail programs\n",
350*0Sstevel@tonic-gate 						      prg, cmd);
351*0Sstevel@tonic-gate 				if (p != NULL)
352*0Sstevel@tonic-gate 					*p = ' ';
353*0Sstevel@tonic-gate #ifndef DEBUG
354*0Sstevel@tonic-gate 				syslog(LOG_CRIT, "uid %d: attempt to use \"%s\"",
355*0Sstevel@tonic-gate 				       (int) getuid(), cmd);
356*0Sstevel@tonic-gate #endif /* ! DEBUG */
357*0Sstevel@tonic-gate 				exit(EX_UNAVAILABLE);
358*0Sstevel@tonic-gate 			}
359*0Sstevel@tonic-gate 
360*0Sstevel@tonic-gate 			/*
361*0Sstevel@tonic-gate 			**  Create the actual shell input.
362*0Sstevel@tonic-gate 			*/
363*0Sstevel@tonic-gate 
364*0Sstevel@tonic-gate 			addcmd(cmd, true, strlen(cmd));
365*0Sstevel@tonic-gate 		}
366*0Sstevel@tonic-gate 		isexec = false;
367*0Sstevel@tonic-gate 
368*0Sstevel@tonic-gate 		if (p != NULL)
369*0Sstevel@tonic-gate 			*p = ' ';
370*0Sstevel@tonic-gate 		else
371*0Sstevel@tonic-gate 			break;
372*0Sstevel@tonic-gate 
373*0Sstevel@tonic-gate 		r = strpbrk(p, specialbuf);
374*0Sstevel@tonic-gate 		if (r == NULL)
375*0Sstevel@tonic-gate 		{
376*0Sstevel@tonic-gate 			addcmd(p, false, strlen(p));
377*0Sstevel@tonic-gate 			break;
378*0Sstevel@tonic-gate 		}
379*0Sstevel@tonic-gate #if ALLOWSEMI
380*0Sstevel@tonic-gate 		if (*r == ';')
381*0Sstevel@tonic-gate 		{
382*0Sstevel@tonic-gate 			addcmd(p, false,  r - p + 1);
383*0Sstevel@tonic-gate 			q = r + 1;
384*0Sstevel@tonic-gate 			continue;
385*0Sstevel@tonic-gate 		}
386*0Sstevel@tonic-gate #endif /* ALLOWSEMI */
387*0Sstevel@tonic-gate 		if ((*r == '&' && *(r + 1) == '&') ||
388*0Sstevel@tonic-gate 		    (*r == '|' && *(r + 1) == '|'))
389*0Sstevel@tonic-gate 		{
390*0Sstevel@tonic-gate 			addcmd(p, false,  r - p + 2);
391*0Sstevel@tonic-gate 			q = r + 2;
392*0Sstevel@tonic-gate 			continue;
393*0Sstevel@tonic-gate 		}
394*0Sstevel@tonic-gate 
395*0Sstevel@tonic-gate 		(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
396*0Sstevel@tonic-gate 				     "%s: cannot use %c in command\n", prg, *r);
397*0Sstevel@tonic-gate #ifndef DEBUG
398*0Sstevel@tonic-gate 		syslog(LOG_CRIT, "uid %d: attempt to use %c in command: %s",
399*0Sstevel@tonic-gate 		       (int) getuid(), *r, par);
400*0Sstevel@tonic-gate #endif /* ! DEBUG */
401*0Sstevel@tonic-gate 		exit(EX_UNAVAILABLE);
402*0Sstevel@tonic-gate 	}
403*0Sstevel@tonic-gate 	if (isexec)
404*0Sstevel@tonic-gate 	{
405*0Sstevel@tonic-gate 		(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
406*0Sstevel@tonic-gate 				     "%s: missing command to exec\n", prg);
407*0Sstevel@tonic-gate #ifndef DEBUG
408*0Sstevel@tonic-gate 		syslog(LOG_CRIT, "uid %d: missing command to exec",
409*0Sstevel@tonic-gate 		       (int) getuid());
410*0Sstevel@tonic-gate #endif /* ! DEBUG */
411*0Sstevel@tonic-gate 		exit(EX_UNAVAILABLE);
412*0Sstevel@tonic-gate 	}
413*0Sstevel@tonic-gate 	/* make sure we created something */
414*0Sstevel@tonic-gate 	if (newcmdbuf[0] == '\0')
415*0Sstevel@tonic-gate 	{
416*0Sstevel@tonic-gate 		(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
417*0Sstevel@tonic-gate 				     "Usage: %s -c command\n", prg);
418*0Sstevel@tonic-gate #ifndef DEBUG
419*0Sstevel@tonic-gate 		syslog(LOG_ERR, "usage");
420*0Sstevel@tonic-gate #endif /* ! DEBUG */
421*0Sstevel@tonic-gate 		exit(EX_USAGE);
422*0Sstevel@tonic-gate 	}
423*0Sstevel@tonic-gate 
424*0Sstevel@tonic-gate 	/*
425*0Sstevel@tonic-gate 	**  Now invoke the shell
426*0Sstevel@tonic-gate 	*/
427*0Sstevel@tonic-gate 
428*0Sstevel@tonic-gate #ifdef DEBUG
429*0Sstevel@tonic-gate 	(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s\n", newcmdbuf);
430*0Sstevel@tonic-gate #endif /* DEBUG */
431*0Sstevel@tonic-gate 	(void) execle("/bin/sh", "/bin/sh", "-c", newcmdbuf,
432*0Sstevel@tonic-gate 		      (char *)NULL, newenv);
433*0Sstevel@tonic-gate 	save_errno = errno;
434*0Sstevel@tonic-gate #ifndef DEBUG
435*0Sstevel@tonic-gate 	syslog(LOG_CRIT, "Cannot exec /bin/sh: %s", sm_errstring(errno));
436*0Sstevel@tonic-gate #endif /* ! DEBUG */
437*0Sstevel@tonic-gate 	errno = save_errno;
438*0Sstevel@tonic-gate 	sm_perror("/bin/sh");
439*0Sstevel@tonic-gate 	exit(EX_OSFILE);
440*0Sstevel@tonic-gate 	/* NOTREACHED */
441*0Sstevel@tonic-gate 	return EX_OSFILE;
442*0Sstevel@tonic-gate }
443