2 * Copyright (c) 1998-2004 Sendmail, Inc. and its suppliers.
4 * Copyright (c) 1993 Eric P. Allman. All rights reserved.
6 * The Regents of the University of California. All rights reserved.
8 * By using this file, you agree to the terms and conditions set
9 * forth in the LICENSE file which can be found at the top level of
10 * the sendmail distribution.
18 "@(#) Copyright (c) 1998-2004 Sendmail, Inc. and its suppliers.\n\
19 All rights reserved.\n\
20 Copyright (c) 1993 Eric P. Allman. All rights reserved.\n\
22 The Regents of the University of California. All rights reserved.\n")
24 SM_IDSTR(id, "@(#)$Id: smrsh.c,v 8.65 2004/08/06 18:54:22 ca Exp $")
27 ** SMRSH -- sendmail restricted shell
29 ** This is a patch to get around the prog mailer bugs in most
30 ** versions of sendmail.
32 ** Use this in place of /bin/sh in the "prog" mailer definition
33 ** in your sendmail.cf file. You then create CMDDIR (owned by
34 ** root, mode 755) and put links to any programs you want
35 ** available to prog mailers in that directory. This should
36 ** include things like "vacation" and "procmail", but not "sed"
39 ** Leading pathnames are stripped from program names so that
40 ** existing .forward files that reference things like
41 ** "/usr/bin/vacation" will continue to work.
43 ** The following characters are completely illegal:
44 ** < > ^ & ` ( ) \n \r
45 ** The following characters are sometimes illegal:
47 ** This is more restrictive than strictly necessary.
49 ** To use this, add FEATURE(`smrsh') to your .mc file.
51 ** This can be used on any version of sendmail.
53 ** In loving memory of RTM. 11/02/93.
58 #include <sm/limits.h>
59 #include <sm/string.h>
61 #include <sys/types.h>
74 #include <sm/errstring.h>
76 /* directory in which all commands must reside */
79 # define CMDDIR SMRSH_CMDDIR
80 # else /* SMRSH_CMDDIR */
81 # define CMDDIR "/usr/adm/sm.bin"
82 # endif /* SMRSH_CMDDIR */
85 /* characters disallowed in the shell "-c" argument */
86 #define SPECIALS "<|>^();&`$\r\n"
88 /* default search path */
91 # define PATH SMRSH_PATH
92 # else /* SMRSH_PATH */
93 # define PATH "/bin:/usr/bin:/usr/ucb"
94 # endif /* SMRSH_PATH */
100 static void addcmd __P((char *, bool, size_t));
103 ** ADDCMD -- add a string to newcmdbuf, check for overflow
106 ** s -- string to add
107 ** cmd -- it's a command: prepend CMDDIR/
108 ** len -- length of string to add
111 ** changes newcmdbuf or exits with a failure.
121 if (s == NULL || *s == '\0')
124 /* enough space for s (len) and CMDDIR + "/" and '\0'? */
125 if (sizeof newcmdbuf - strlen(newcmdbuf) <=
126 len + 1 + (cmd ? (strlen(CMDDIR) + 1) : 0))
128 (void)sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
129 "%s: command too long: %s\n", prg, par);
131 syslog(LOG_WARNING, "command too long: %.40s", par);
133 exit(EX_UNAVAILABLE);
136 (void) sm_strlcat2(newcmdbuf, CMDDIR, "/", sizeof newcmdbuf);
137 (void) strncat(newcmdbuf, s, len);
159 # else /* ! LOG_MAIL */
160 openlog("smrsh", LOG_ODELAY|LOG_CONS, LOG_MAIL);
161 # endif /* ! LOG_MAIL */
164 (void) sm_strlcpyn(pathbuf, sizeof pathbuf, 2, "PATH=", PATH);
169 ** Do basic argv usage checking
174 if (argc != 3 || strcmp(argv[1], "-c") != 0)
176 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
177 "Usage: %s -c command\n", prg);
179 syslog(LOG_ERR, "usage");
187 ** Disallow special shell syntax. This is overly restrictive,
188 ** but it should shut down all attacks.
189 ** Be sure to include 8-bit versions, since many shells strip
190 ** the address to 7 bits before checking.
193 if (strlen(SPECIALS) * 2 >= sizeof specialbuf)
196 syslog(LOG_ERR, "too many specials: %.40s", SPECIALS);
198 exit(EX_UNAVAILABLE);
200 (void) sm_strlcpy(specialbuf, SPECIALS, sizeof specialbuf);
201 for (p = specialbuf; *p != '\0'; p++)
203 (void) sm_strlcat(specialbuf, SPECIALS, sizeof specialbuf);
206 ** Do a quick sanity check on command line length.
209 if (strlen(par) > (sizeof newcmdbuf - sizeof CMDDIR - 2))
211 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
212 "%s: command too long: %s\n", prg, par);
214 syslog(LOG_WARNING, "command too long: %.40s", par);
216 exit(EX_UNAVAILABLE);
226 ** Strip off a leading pathname on the command name. For
227 ** example, change /usr/ucb/vacation to vacation.
230 /* strip leading spaces */
231 while (*q != '\0' && isascii(*q) && isspace(*q))
237 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
238 "%s: missing command to exec\n",
241 syslog(LOG_CRIT, "uid %d: missing command to exec", (int) getuid());
243 exit(EX_UNAVAILABLE);
248 /* find the end of the command name */
249 p = strpbrk(q, " \t");
257 /* search backwards for last / (allow for 0200 bit) */
260 if ((*--cmd & 0177) == '/')
266 /* cmd now points at final component of path name */
268 /* allow a few shell builtins */
269 if (strcmp(q, "exec") == 0 && p != NULL)
271 addcmd("exec ", false, strlen("exec "));
273 /* test _next_ arg */
278 else if (strcmp(q, "exit") == 0 || strcmp(q, "echo") == 0)
280 addcmd(cmd, false, strlen(cmd));
282 /* test following chars */
286 char cmdbuf[MAXPATHLEN];
289 ** Check to see if the command name is legal.
292 if (sm_strlcpyn(cmdbuf, sizeof cmdbuf, 3, CMDDIR,
293 "/", cmd) >= sizeof cmdbuf)
296 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
297 "%s: \"%s\" not available for sendmail programs (filename too long)\n",
302 syslog(LOG_CRIT, "uid %d: attempt to use \"%s\" (filename too long)",
303 (int) getuid(), cmd);
305 exit(EX_UNAVAILABLE);
309 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
310 "Trying %s\n", cmdbuf);
312 if (stat(cmdbuf, &st) < 0)
315 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
316 "%s: \"%s\" not available for sendmail programs (stat failed)\n",
321 syslog(LOG_CRIT, "uid %d: attempt to use \"%s\" (stat failed)",
322 (int) getuid(), cmd);
324 exit(EX_UNAVAILABLE);
326 if (!S_ISREG(st.st_mode)
328 && !S_ISLNK(st.st_mode)
333 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
334 "%s: \"%s\" not available for sendmail programs (not a file)\n",
339 syslog(LOG_CRIT, "uid %d: attempt to use \"%s\" (not a file)",
340 (int) getuid(), cmd);
342 exit(EX_UNAVAILABLE);
344 if (access(cmdbuf, X_OK) < 0)
346 /* oops.... crack attack possiblity */
347 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
348 "%s: \"%s\" not available for sendmail programs\n",
353 syslog(LOG_CRIT, "uid %d: attempt to use \"%s\"",
354 (int) getuid(), cmd);
356 exit(EX_UNAVAILABLE);
360 ** Create the actual shell input.
363 addcmd(cmd, true, strlen(cmd));
372 r = strpbrk(p, specialbuf);
375 addcmd(p, false, strlen(p));
381 addcmd(p, false, r - p + 1);
385 #endif /* ALLOWSEMI */
386 if ((*r == '&' && *(r + 1) == '&') ||
387 (*r == '|' && *(r + 1) == '|'))
389 addcmd(p, false, r - p + 2);
394 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
395 "%s: cannot use %c in command\n", prg, *r);
397 syslog(LOG_CRIT, "uid %d: attempt to use %c in command: %s",
398 (int) getuid(), *r, par);
400 exit(EX_UNAVAILABLE);
404 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
405 "%s: missing command to exec\n", prg);
407 syslog(LOG_CRIT, "uid %d: missing command to exec",
410 exit(EX_UNAVAILABLE);
412 /* make sure we created something */
413 if (newcmdbuf[0] == '\0')
415 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
416 "Usage: %s -c command\n", prg);
418 syslog(LOG_ERR, "usage");
424 ** Now invoke the shell
428 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s\n", newcmdbuf);
430 (void) execle("/bin/sh", "/bin/sh", "-c", newcmdbuf,
431 (char *)NULL, newenv);
434 syslog(LOG_CRIT, "Cannot exec /bin/sh: %s", sm_errstring(errno));
437 sm_perror("/bin/sh");