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
addcmd(s,cmd,len)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
main(argc,argv)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