1 /*
2 * Copyright (c) 1993 Eric P. Allman
3 * Copyright (c) 1993
4 * The Regents of the University of California. All rights reserved.
5 *
6 * %sccs.include.redist.c%
7 */
8
9 #ifndef lint
10 static char sccsid[] = "@(#)smrsh.c 8.2 (Berkeley) 03/08/95";
11 #endif /* not lint */
12
13 /*
14 ** SMRSH -- sendmail restricted shell
15 **
16 ** This is a patch to get around the prog mailer bugs in most
17 ** versions of sendmail.
18 **
19 ** Use this in place of /bin/sh in the "prog" mailer definition
20 ** in your sendmail.cf file. You then create CMDDIR (owned by
21 ** root, mode 755) and put links to any programs you want
22 ** available to prog mailers in that directory. This should
23 ** include things like "vacation" and "procmail", but not "sed"
24 ** or "sh".
25 **
26 ** Leading pathnames are stripped from program names so that
27 ** existing .forward files that reference things like
28 ** "/usr/ucb/vacation" will continue to work.
29 **
30 ** The following characters are completely illegal:
31 ** < > | ^ ; & $ ` ( ) \n \r
32 ** This is more restrictive than strictly necessary.
33 **
34 ** To use this, edit /etc/sendmail.cf, search for ^Mprog, and
35 ** change P=/bin/sh to P=/usr/etc/smrsh, where this compiled
36 ** binary is installed /usr/etc/smrsh.
37 **
38 ** This can be used on any version of sendmail.
39 **
40 ** In loving memory of RTM. 11/02/93.
41 */
42
43 #include <unistd.h>
44 #include <stdio.h>
45 #include <sys/file.h>
46 #include <string.h>
47 #include <ctype.h>
48 #include <sysexits.h>
49 #include <syslog.h>
50
51 /* directory in which all commands must reside */
52 #ifndef CMDDIR
53 # define CMDDIR "/usr/adm/sm.bin"
54 #endif
55
56 /* characters disallowed in the shell "-c" argument */
57 #define SPECIALS "<|>^();&`$\r\n"
58
59 /* default search path */
60 #ifndef PATH
61 # define PATH "/bin:/usr/bin:/usr/ucb"
62 #endif
63
main(argc,argv)64 main(argc, argv)
65 int argc;
66 char **argv;
67 {
68 register char *p;
69 register char *q;
70 register char *cmd;
71 int i;
72 char *newenv[2];
73 char cmdbuf[1000];
74 char pathbuf[1000];
75
76 #ifndef LOG_MAIL
77 openlog("smrsh", 0);
78 #else
79 openlog("smrsh", LOG_ODELAY|LOG_CONS, LOG_MAIL);
80 #endif
81
82 strcpy(pathbuf, "PATH=");
83 strcat(pathbuf, PATH);
84 newenv[0] = pathbuf;
85 newenv[1] = NULL;
86
87 /*
88 ** Do basic argv usage checking
89 */
90
91 if (argc != 3 || strcmp(argv[1], "-c") != 0)
92 {
93 fprintf(stderr, "Usage: %s -c command\n", argv[0]);
94 syslog(LOG_ERR, "usage");
95 exit(EX_USAGE);
96 }
97
98 /*
99 ** Disallow special shell syntax. This is overly restrictive,
100 ** but it should shut down all attacks.
101 ** Be sure to include 8-bit versions, since many shells strip
102 ** the address to 7 bits before checking.
103 */
104
105 strcpy(cmdbuf, SPECIALS);
106 for (p = cmdbuf; *p != '\0'; p++)
107 *p |= '\200';
108 strcat(cmdbuf, SPECIALS);
109 p = strpbrk(argv[2], cmdbuf);
110 if (p != NULL)
111 {
112 fprintf(stderr, "%s: cannot use %c in command\n",
113 argv[0], *p);
114 syslog(LOG_CRIT, "uid %d: attempt to use %c in command: %s",
115 getuid(), *p, argv[2]);
116 exit(EX_UNAVAILABLE);
117 }
118
119 /*
120 ** Do a quick sanity check on command line length.
121 */
122
123 i = strlen(argv[2]);
124 if (i > (sizeof cmdbuf - sizeof CMDDIR - 2))
125 {
126 fprintf(stderr, "%s: command too long: %s\n", argv[0], argv[2]);
127 syslog(LOG_WARNING, "command too long: %.40s", argv[2]);
128 exit(EX_UNAVAILABLE);
129 }
130
131 /*
132 ** Strip off a leading pathname on the command name. For
133 ** example, change /usr/ucb/vacation to vacation.
134 */
135
136 /* strip leading spaces */
137 for (q = argv[2]; *q != '\0' && isascii(*q) && isspace(*q); )
138 q++;
139
140 /* find the end of the command name */
141 p = strpbrk(q, " \t");
142 if (p == NULL)
143 cmd = &q[strlen(q)];
144 else
145 {
146 *p = '\0';
147 cmd = p;
148 }
149
150 /* search backwards for last / (allow for 0200 bit) */
151 while (cmd > q)
152 {
153 if ((*--cmd & 0177) == '/')
154 {
155 cmd++;
156 break;
157 }
158 }
159
160 /* cmd now points at final component of path name */
161
162 /*
163 ** Check to see if the command name is legal.
164 */
165
166 (void) strcpy(cmdbuf, CMDDIR);
167 (void) strcat(cmdbuf, "/");
168 (void) strcat(cmdbuf, cmd);
169 #ifdef DEBUG
170 printf("Trying %s\n", cmdbuf);
171 #endif
172 if (access(cmdbuf, X_OK) < 0)
173 {
174 /* oops.... crack attack possiblity */
175 fprintf(stderr, "%s: %s not available for sendmail programs\n",
176 argv[0], cmd);
177 if (p != NULL)
178 *p = ' ';
179 syslog(LOG_CRIT, "uid %d: attempt to use %s", getuid(), cmd);
180 exit(EX_UNAVAILABLE);
181 }
182 if (p != NULL)
183 *p = ' ';
184
185 /*
186 ** Create the actual shell input.
187 */
188
189 strcpy(cmdbuf, CMDDIR);
190 strcat(cmdbuf, "/");
191 strcat(cmdbuf, cmd);
192
193 /*
194 ** Now invoke the shell
195 */
196
197 #ifdef DEBUG
198 printf("%s\n", cmdbuf);
199 #endif
200 execle("/bin/sh", "/bin/sh", "-c", cmdbuf, NULL, newenv);
201 syslog(LOG_CRIT, "Cannot exec /bin/sh: %m");
202 perror("/bin/sh");
203 exit(EX_OSFILE);
204 }
205