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