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.
17 "@(#) Copyright (c) 1998-2004 Sendmail, Inc. and its suppliers.\n\
18 All rights reserved.\n\
19 Copyright (c) 1993 Eric P. Allman. All rights reserved.\n\
21 The Regents of the University of California. All rights reserved.\n")
23 SM_IDSTR(id, "@(#)$Id: smrsh.c,v 8.65 2004/08/06 18:54:22 ca Exp $")
26 ** SMRSH -- sendmail restricted shell
28 ** This is a patch to get around the prog mailer bugs in most
29 ** versions of sendmail.
31 ** Use this in place of /bin/sh in the "prog" mailer definition
32 ** in your sendmail.cf file. You then create CMDDIR (owned by
33 ** root, mode 755) and put links to any programs you want
34 ** available to prog mailers in that directory. This should
35 ** include things like "vacation" and "procmail", but not "sed"
38 ** Leading pathnames are stripped from program names so that
39 ** existing .forward files that reference things like
40 ** "/usr/bin/vacation" will continue to work.
42 ** The following characters are completely illegal:
43 ** < > ^ & ` ( ) \n \r
44 ** The following characters are sometimes illegal:
46 ** This is more restrictive than strictly necessary.
48 ** To use this, add FEATURE(`smrsh') to your .mc file.
50 ** This can be used on any version of sendmail.
52 ** In loving memory of RTM. 11/02/93.
57 #include <sm/limits.h>
58 #include <sm/string.h>
60 #include <sys/types.h>
73 #include <sm/errstring.h>
75 /* directory in which all commands must reside */
78 # define CMDDIR SMRSH_CMDDIR
79 # else /* SMRSH_CMDDIR */
80 # define CMDDIR "/usr/adm/sm.bin"
81 # endif /* SMRSH_CMDDIR */
84 /* characters disallowed in the shell "-c" argument */
85 #define SPECIALS "<|>^();&`$\r\n"
87 /* default search path */
90 # define PATH SMRSH_PATH
91 # else /* SMRSH_PATH */
92 # define PATH "/bin:/usr/bin:/usr/ucb"
93 # endif /* SMRSH_PATH */
99 static void addcmd __P((char *, bool, size_t));
102 ** ADDCMD -- add a string to newcmdbuf, check for overflow
105 ** s -- string to add
106 ** cmd -- it's a command: prepend CMDDIR/
107 ** len -- length of string to add
110 ** changes newcmdbuf or exits with a failure.
120 if (s == NULL || *s == '\0')
123 /* enough space for s (len) and CMDDIR + "/" and '\0'? */
124 if (sizeof newcmdbuf - strlen(newcmdbuf) <=
125 len + 1 + (cmd ? (strlen(CMDDIR) + 1) : 0))
127 (void)sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
128 "%s: command too long: %s\n", prg, par);
130 syslog(LOG_WARNING, "command too long: %.40s", par);
132 exit(EX_UNAVAILABLE);
135 (void) sm_strlcat2(newcmdbuf, CMDDIR, "/", sizeof newcmdbuf);
136 (void) strncat(newcmdbuf, s, len);
158 # else /* ! LOG_MAIL */
159 openlog("smrsh", LOG_ODELAY|LOG_CONS, LOG_MAIL);
160 # endif /* ! LOG_MAIL */
163 (void) sm_strlcpyn(pathbuf, sizeof pathbuf, 2, "PATH=", PATH);
168 ** Do basic argv usage checking
173 if (argc != 3 || strcmp(argv[1], "-c") != 0)
175 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
176 "Usage: %s -c command\n", prg);
178 syslog(LOG_ERR, "usage");
186 ** Disallow special shell syntax. This is overly restrictive,
187 ** but it should shut down all attacks.
188 ** Be sure to include 8-bit versions, since many shells strip
189 ** the address to 7 bits before checking.
192 if (strlen(SPECIALS) * 2 >= sizeof specialbuf)
195 syslog(LOG_ERR, "too many specials: %.40s", SPECIALS);
197 exit(EX_UNAVAILABLE);
199 (void) sm_strlcpy(specialbuf, SPECIALS, sizeof specialbuf);
200 for (p = specialbuf; *p != '\0'; p++)
202 (void) sm_strlcat(specialbuf, SPECIALS, sizeof specialbuf);
205 ** Do a quick sanity check on command line length.
208 if (strlen(par) > (sizeof newcmdbuf - sizeof CMDDIR - 2))
210 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
211 "%s: command too long: %s\n", prg, par);
213 syslog(LOG_WARNING, "command too long: %.40s", par);
215 exit(EX_UNAVAILABLE);
225 ** Strip off a leading pathname on the command name. For
226 ** example, change /usr/ucb/vacation to vacation.
229 /* strip leading spaces */
230 while (*q != '\0' && isascii(*q) && isspace(*q))
236 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
237 "%s: missing command to exec\n",
240 syslog(LOG_CRIT, "uid %d: missing command to exec", (int) getuid());
242 exit(EX_UNAVAILABLE);
247 /* find the end of the command name */
248 p = strpbrk(q, " \t");
256 /* search backwards for last / (allow for 0200 bit) */
259 if ((*--cmd & 0177) == '/')
265 /* cmd now points at final component of path name */
267 /* allow a few shell builtins */
268 if (strcmp(q, "exec") == 0 && p != NULL)
270 addcmd("exec ", false, strlen("exec "));
272 /* test _next_ arg */
277 else if (strcmp(q, "exit") == 0 || strcmp(q, "echo") == 0)
279 addcmd(cmd, false, strlen(cmd));
281 /* test following chars */
285 char cmdbuf[MAXPATHLEN];
288 ** Check to see if the command name is legal.
291 if (sm_strlcpyn(cmdbuf, sizeof cmdbuf, 3, CMDDIR,
292 "/", cmd) >= sizeof cmdbuf)
295 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
296 "%s: \"%s\" not available for sendmail programs (filename too long)\n",
301 syslog(LOG_CRIT, "uid %d: attempt to use \"%s\" (filename too long)",
302 (int) getuid(), cmd);
304 exit(EX_UNAVAILABLE);
308 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
309 "Trying %s\n", cmdbuf);
311 if (stat(cmdbuf, &st) < 0)
314 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
315 "%s: \"%s\" not available for sendmail programs (stat failed)\n",
320 syslog(LOG_CRIT, "uid %d: attempt to use \"%s\" (stat failed)",
321 (int) getuid(), cmd);
323 exit(EX_UNAVAILABLE);
325 if (!S_ISREG(st.st_mode)
327 && !S_ISLNK(st.st_mode)
332 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
333 "%s: \"%s\" not available for sendmail programs (not a file)\n",
338 syslog(LOG_CRIT, "uid %d: attempt to use \"%s\" (not a file)",
339 (int) getuid(), cmd);
341 exit(EX_UNAVAILABLE);
343 if (access(cmdbuf, X_OK) < 0)
345 /* oops.... crack attack possiblity */
346 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
347 "%s: \"%s\" not available for sendmail programs\n",
352 syslog(LOG_CRIT, "uid %d: attempt to use \"%s\"",
353 (int) getuid(), cmd);
355 exit(EX_UNAVAILABLE);
359 ** Create the actual shell input.
362 addcmd(cmd, true, strlen(cmd));
371 r = strpbrk(p, specialbuf);
374 addcmd(p, false, strlen(p));
380 addcmd(p, false, r - p + 1);
384 #endif /* ALLOWSEMI */
385 if ((*r == '&' && *(r + 1) == '&') ||
386 (*r == '|' && *(r + 1) == '|'))
388 addcmd(p, false, r - p + 2);
393 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
394 "%s: cannot use %c in command\n", prg, *r);
396 syslog(LOG_CRIT, "uid %d: attempt to use %c in command: %s",
397 (int) getuid(), *r, par);
399 exit(EX_UNAVAILABLE);
403 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
404 "%s: missing command to exec\n", prg);
406 syslog(LOG_CRIT, "uid %d: missing command to exec",
409 exit(EX_UNAVAILABLE);
411 /* make sure we created something */
412 if (newcmdbuf[0] == '\0')
414 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
415 "Usage: %s -c command\n", prg);
417 syslog(LOG_ERR, "usage");
423 ** Now invoke the shell
427 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s\n", newcmdbuf);
429 (void) execle("/bin/sh", "/bin/sh", "-c", newcmdbuf,
430 (char *)NULL, newenv);
433 syslog(LOG_CRIT, "Cannot exec /bin/sh: %s", sm_errstring(errno));
436 sm_perror("/bin/sh");