167909Seric /*
267909Seric * Copyright (c) 1993 Eric P. Allman
367909Seric * Copyright (c) 1993
467909Seric * The Regents of the University of California. All rights reserved.
567909Seric *
667909Seric * %sccs.include.redist.c%
767909Seric */
867909Seric
967909Seric #ifndef lint
10*68502Seric static char sccsid[] = "@(#)smrsh.c 8.2 (Berkeley) 03/08/95";
1167909Seric #endif /* not lint */
1267909Seric
1367909Seric /*
1467909Seric ** SMRSH -- sendmail restricted shell
1567909Seric **
1667909Seric ** This is a patch to get around the prog mailer bugs in most
1767909Seric ** versions of sendmail.
1867909Seric **
1967909Seric ** Use this in place of /bin/sh in the "prog" mailer definition
2067909Seric ** in your sendmail.cf file. You then create CMDDIR (owned by
2167909Seric ** root, mode 755) and put links to any programs you want
2267909Seric ** available to prog mailers in that directory. This should
2367909Seric ** include things like "vacation" and "procmail", but not "sed"
2467909Seric ** or "sh".
2567909Seric **
2667909Seric ** Leading pathnames are stripped from program names so that
2767909Seric ** existing .forward files that reference things like
2867909Seric ** "/usr/ucb/vacation" will continue to work.
2967909Seric **
3067909Seric ** The following characters are completely illegal:
3167909Seric ** < > | ^ ; & $ ` ( ) \n \r
3267909Seric ** This is more restrictive than strictly necessary.
3367909Seric **
3467909Seric ** To use this, edit /etc/sendmail.cf, search for ^Mprog, and
3567909Seric ** change P=/bin/sh to P=/usr/etc/smrsh, where this compiled
3667909Seric ** binary is installed /usr/etc/smrsh.
3767909Seric **
3867909Seric ** This can be used on any version of sendmail.
3967909Seric **
4067909Seric ** In loving memory of RTM. 11/02/93.
4167909Seric */
4267909Seric
43*68502Seric #include <unistd.h>
4467909Seric #include <stdio.h>
4567909Seric #include <sys/file.h>
4667909Seric #include <string.h>
4767909Seric #include <ctype.h>
4867909Seric #include <sysexits.h>
4967909Seric #include <syslog.h>
5067909Seric
5167909Seric /* directory in which all commands must reside */
5267909Seric #ifndef CMDDIR
5367909Seric # define CMDDIR "/usr/adm/sm.bin"
5467909Seric #endif
5567909Seric
5667909Seric /* characters disallowed in the shell "-c" argument */
5767909Seric #define SPECIALS "<|>^();&`$\r\n"
5867909Seric
5967909Seric /* default search path */
6067909Seric #ifndef PATH
6167909Seric # define PATH "/bin:/usr/bin:/usr/ucb"
6267909Seric #endif
6367909Seric
main(argc,argv)6467909Seric main(argc, argv)
6567909Seric int argc;
6667909Seric char **argv;
6767909Seric {
6867909Seric register char *p;
6967909Seric register char *q;
7067909Seric register char *cmd;
7167909Seric int i;
7267909Seric char *newenv[2];
7367909Seric char cmdbuf[1000];
7467909Seric char pathbuf[1000];
7567909Seric
7667909Seric #ifndef LOG_MAIL
7767909Seric openlog("smrsh", 0);
7867909Seric #else
7967909Seric openlog("smrsh", LOG_ODELAY|LOG_CONS, LOG_MAIL);
8067909Seric #endif
8167909Seric
8267909Seric strcpy(pathbuf, "PATH=");
8367909Seric strcat(pathbuf, PATH);
8467909Seric newenv[0] = pathbuf;
8567909Seric newenv[1] = NULL;
8667909Seric
8767909Seric /*
8867909Seric ** Do basic argv usage checking
8967909Seric */
9067909Seric
9167909Seric if (argc != 3 || strcmp(argv[1], "-c") != 0)
9267909Seric {
9367909Seric fprintf(stderr, "Usage: %s -c command\n", argv[0]);
9467909Seric syslog(LOG_ERR, "usage");
9567909Seric exit(EX_USAGE);
9667909Seric }
9767909Seric
9867909Seric /*
9967909Seric ** Disallow special shell syntax. This is overly restrictive,
10067909Seric ** but it should shut down all attacks.
10167909Seric ** Be sure to include 8-bit versions, since many shells strip
10267909Seric ** the address to 7 bits before checking.
10367909Seric */
10467909Seric
10567909Seric strcpy(cmdbuf, SPECIALS);
10667909Seric for (p = cmdbuf; *p != '\0'; p++)
10767909Seric *p |= '\200';
10867909Seric strcat(cmdbuf, SPECIALS);
10967909Seric p = strpbrk(argv[2], cmdbuf);
11067909Seric if (p != NULL)
11167909Seric {
11267909Seric fprintf(stderr, "%s: cannot use %c in command\n",
11367909Seric argv[0], *p);
11467909Seric syslog(LOG_CRIT, "uid %d: attempt to use %c in command: %s",
11567909Seric getuid(), *p, argv[2]);
11667909Seric exit(EX_UNAVAILABLE);
11767909Seric }
11867909Seric
11967909Seric /*
12067909Seric ** Do a quick sanity check on command line length.
12167909Seric */
12267909Seric
12367909Seric i = strlen(argv[2]);
12467909Seric if (i > (sizeof cmdbuf - sizeof CMDDIR - 2))
12567909Seric {
12667909Seric fprintf(stderr, "%s: command too long: %s\n", argv[0], argv[2]);
12767909Seric syslog(LOG_WARNING, "command too long: %.40s", argv[2]);
12867909Seric exit(EX_UNAVAILABLE);
12967909Seric }
13067909Seric
13167909Seric /*
13267909Seric ** Strip off a leading pathname on the command name. For
13367909Seric ** example, change /usr/ucb/vacation to vacation.
13467909Seric */
13567909Seric
13667909Seric /* strip leading spaces */
13767909Seric for (q = argv[2]; *q != '\0' && isascii(*q) && isspace(*q); )
13867909Seric q++;
13967909Seric
14067909Seric /* find the end of the command name */
14167909Seric p = strpbrk(q, " \t");
14267909Seric if (p == NULL)
14367909Seric cmd = &q[strlen(q)];
14467909Seric else
14567909Seric {
14667909Seric *p = '\0';
14767909Seric cmd = p;
14867909Seric }
14967909Seric
15067909Seric /* search backwards for last / (allow for 0200 bit) */
15167909Seric while (cmd > q)
15267909Seric {
15367909Seric if ((*--cmd & 0177) == '/')
15467909Seric {
15567909Seric cmd++;
15667909Seric break;
15767909Seric }
15867909Seric }
15967909Seric
16067909Seric /* cmd now points at final component of path name */
16167909Seric
16267909Seric /*
16367909Seric ** Check to see if the command name is legal.
16467909Seric */
16567909Seric
16667909Seric (void) strcpy(cmdbuf, CMDDIR);
16767909Seric (void) strcat(cmdbuf, "/");
16867909Seric (void) strcat(cmdbuf, cmd);
16967909Seric #ifdef DEBUG
17067909Seric printf("Trying %s\n", cmdbuf);
17167909Seric #endif
17267909Seric if (access(cmdbuf, X_OK) < 0)
17367909Seric {
17467909Seric /* oops.... crack attack possiblity */
17567909Seric fprintf(stderr, "%s: %s not available for sendmail programs\n",
17667909Seric argv[0], cmd);
17767909Seric if (p != NULL)
17867909Seric *p = ' ';
17967909Seric syslog(LOG_CRIT, "uid %d: attempt to use %s", getuid(), cmd);
18067909Seric exit(EX_UNAVAILABLE);
18167909Seric }
18267909Seric if (p != NULL)
18367909Seric *p = ' ';
18467909Seric
18567909Seric /*
18667909Seric ** Create the actual shell input.
18767909Seric */
18867909Seric
18967909Seric strcpy(cmdbuf, CMDDIR);
19067909Seric strcat(cmdbuf, "/");
19167909Seric strcat(cmdbuf, cmd);
19267909Seric
19367909Seric /*
19467909Seric ** Now invoke the shell
19567909Seric */
19667909Seric
19767909Seric #ifdef DEBUG
19867909Seric printf("%s\n", cmdbuf);
19967909Seric #endif
20067909Seric execle("/bin/sh", "/bin/sh", "-c", cmdbuf, NULL, newenv);
20167909Seric syslog(LOG_CRIT, "Cannot exec /bin/sh: %m");
20267909Seric perror("/bin/sh");
20367909Seric exit(EX_OSFILE);
20467909Seric }
205