Deleted Added
full compact
1/*
2 * Copyright (c) 1998-2002 Sendmail, Inc. and its suppliers.
3 * All rights reserved.
4 * Copyright (c) 1993 Eric P. Allman. All rights reserved.
5 * Copyright (c) 1993
6 * The Regents of the University of California. All rights reserved.
7 *
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.
11 *
12 * $FreeBSD: head/contrib/sendmail/smrsh/smrsh.c 125823 2004-02-14 21:58:08Z gshapiro $
12 * $FreeBSD: head/contrib/sendmail/smrsh/smrsh.c 132946 2004-08-01 01:16:16Z gshapiro $
13 */
14
15#include <sm/gen.h>
16
17SM_IDSTR(copyright,
18"@(#) Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.\n\
19 All rights reserved.\n\
20 Copyright (c) 1993 Eric P. Allman. All rights reserved.\n\
21 Copyright (c) 1993\n\
22 The Regents of the University of California. All rights reserved.\n")
23
24SM_IDSTR(id, "@(#)$Id: smrsh.c,v 8.58.2.5 2003/12/15 17:09:39 ca Exp $")
24SM_IDSTR(id, "@(#)$Id: smrsh.c,v 8.63 2003/12/15 17:12:29 ca Exp $")
25
26/*
27** SMRSH -- sendmail restricted shell
28**
29** This is a patch to get around the prog mailer bugs in most
30** versions of sendmail.
31**
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"
37** or "sh".
38**
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.
42**
43** The following characters are completely illegal:
44** < > ^ & ` ( ) \n \r
45** The following characters are sometimes illegal:
46** | &
47** This is more restrictive than strictly necessary.
48**
49** To use this, add FEATURE(`smrsh') to your .mc file.
50**
51** This can be used on any version of sendmail.
52**
53** In loving memory of RTM. 11/02/93.
54*/
55
56#include <unistd.h>
57#include <sm/io.h>
58#include <sm/limits.h>
59#include <sm/string.h>
60#include <sys/file.h>
61#include <sys/types.h>
62#include <sys/stat.h>
63#include <string.h>
64#include <ctype.h>
65#include <errno.h>
66#ifdef EX_OK
67# undef EX_OK
68#endif /* EX_OK */
69#include <sysexits.h>
70#include <syslog.h>
71#include <stdlib.h>
72
73#include <sm/conf.h>
74#include <sm/errstring.h>
75
76/* directory in which all commands must reside */
77#ifndef CMDDIR
78# ifdef SMRSH_CMDDIR
79# define CMDDIR SMRSH_CMDDIR
80# else /* SMRSH_CMDDIR */
81# define CMDDIR "/usr/adm/sm.bin"
82# endif /* SMRSH_CMDDIR */
83#endif /* ! CMDDIR */
84
85/* characters disallowed in the shell "-c" argument */
86#define SPECIALS "<|>^();&`$\r\n"
87
88/* default search path */
89#ifndef PATH
90# ifdef SMRSH_PATH
91# define PATH SMRSH_PATH
92# else /* SMRSH_PATH */
93# define PATH "/bin:/usr/bin:/usr/ucb"
94# endif /* SMRSH_PATH */
95#endif /* ! PATH */
96
97char newcmdbuf[1000];
98char *prg, *par;
99
100/*
101** ADDCMD -- add a string to newcmdbuf, check for overflow
102**
103** Parameters:
104** s -- string to add
105** cmd -- it's a command: prepend CMDDIR/
106** len -- length of string to add
107**
108** Side Effects:
109** changes newcmdbuf or exits with a failure.
110**
111*/
112
113void
114addcmd(s, cmd, len)
115 char *s;
116 bool cmd;
117 size_t len;
118{
119 if (s == NULL || *s == '\0')
120 return;
121
122 /* enough space for s (len) and CMDDIR + "/" and '\0'? */
123 if (sizeof newcmdbuf - strlen(newcmdbuf) <=
124 len + 1 + (cmd ? (strlen(CMDDIR) + 1) : 0))
125 {
126 (void)sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
127 "%s: command too long: %s\n", prg, par);
128#ifndef DEBUG
129 syslog(LOG_WARNING, "command too long: %.40s", par);
130#endif /* ! DEBUG */
131 exit(EX_UNAVAILABLE);
132 }
133 if (cmd)
134 (void) sm_strlcat2(newcmdbuf, CMDDIR, "/", sizeof newcmdbuf);
135 (void) strncat(newcmdbuf, s, len);
136}
137
138int
139main(argc, argv)
140 int argc;
141 char **argv;
142{
143 register char *p;
144 register char *q;
145 register char *r;
146 register char *cmd;
147 int isexec;
148 int save_errno;
149 char *newenv[2];
150 char pathbuf[1000];
151 char specialbuf[32];
152 struct stat st;
153
154#ifndef DEBUG
155# ifndef LOG_MAIL
156 openlog("smrsh", 0);
157# else /* ! LOG_MAIL */
158 openlog("smrsh", LOG_ODELAY|LOG_CONS, LOG_MAIL);
159# endif /* ! LOG_MAIL */
160#endif /* ! DEBUG */
161
162 (void) sm_strlcpyn(pathbuf, sizeof pathbuf, 2, "PATH=", PATH);
163 newenv[0] = pathbuf;
164 newenv[1] = NULL;
165
166 /*
167 ** Do basic argv usage checking
168 */
169
170 prg = argv[0];
171
172 if (argc != 3 || strcmp(argv[1], "-c") != 0)
173 {
174 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
175 "Usage: %s -c command\n", prg);
176#ifndef DEBUG
177 syslog(LOG_ERR, "usage");
178#endif /* ! DEBUG */
179 exit(EX_USAGE);
180 }
181
182 par = argv[2];
183
184 /*
185 ** Disallow special shell syntax. This is overly restrictive,
186 ** but it should shut down all attacks.
187 ** Be sure to include 8-bit versions, since many shells strip
188 ** the address to 7 bits before checking.
189 */
190
191 if (strlen(SPECIALS) * 2 >= sizeof specialbuf)
192 {
193#ifndef DEBUG
194 syslog(LOG_ERR, "too many specials: %.40s", SPECIALS);
195#endif /* ! DEBUG */
196 exit(EX_UNAVAILABLE);
197 }
198 (void) sm_strlcpy(specialbuf, SPECIALS, sizeof specialbuf);
199 for (p = specialbuf; *p != '\0'; p++)
200 *p |= '\200';
201 (void) sm_strlcat(specialbuf, SPECIALS, sizeof specialbuf);
202
203 /*
204 ** Do a quick sanity check on command line length.
205 */
206
207 if (strlen(par) > (sizeof newcmdbuf - sizeof CMDDIR - 2))
208 {
209 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
210 "%s: command too long: %s\n", prg, par);
211#ifndef DEBUG
212 syslog(LOG_WARNING, "command too long: %.40s", par);
213#endif /* ! DEBUG */
214 exit(EX_UNAVAILABLE);
215 }
216
217 q = par;
218 newcmdbuf[0] = '\0';
219 isexec = false;
220
221 while (*q != '\0')
222 {
223 /*
224 ** Strip off a leading pathname on the command name. For
225 ** example, change /usr/ucb/vacation to vacation.
226 */
227
228 /* strip leading spaces */
229 while (*q != '\0' && isascii(*q) && isspace(*q))
230 q++;
231 if (*q == '\0')
232 {
233 if (isexec)
234 {
235 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
236 "%s: missing command to exec\n",
237 prg);
238#ifndef DEBUG
239 syslog(LOG_CRIT, "uid %d: missing command to exec", (int) getuid());
240#endif /* ! DEBUG */
241 exit(EX_UNAVAILABLE);
242 }
243 break;
244 }
245
246 /* find the end of the command name */
247 p = strpbrk(q, " \t");
248 if (p == NULL)
249 cmd = &q[strlen(q)];
250 else
251 {
252 *p = '\0';
253 cmd = p;
254 }
255 /* search backwards for last / (allow for 0200 bit) */
256 while (cmd > q)
257 {
258 if ((*--cmd & 0177) == '/')
259 {
260 cmd++;
261 break;
262 }
263 }
264 /* cmd now points at final component of path name */
265
266 /* allow a few shell builtins */
267 if (strcmp(q, "exec") == 0 && p != NULL)
268 {
269 addcmd("exec ", false, strlen("exec "));
270
271 /* test _next_ arg */
272 q = ++p;
273 isexec = true;
274 continue;
275 }
276 else if (strcmp(q, "exit") == 0 || strcmp(q, "echo") == 0)
277 {
278 addcmd(cmd, false, strlen(cmd));
279
280 /* test following chars */
281 }
282 else
283 {
284 char cmdbuf[MAXPATHLEN];
285
286 /*
287 ** Check to see if the command name is legal.
288 */
289
290 if (sm_strlcpyn(cmdbuf, sizeof cmdbuf, 3, CMDDIR,
291 "/", cmd) >= sizeof cmdbuf)
292 {
293 /* too long */
294 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
295 "%s: \"%s\" not available for sendmail programs (filename too long)\n",
296 prg, cmd);
297 if (p != NULL)
298 *p = ' ';
299#ifndef DEBUG
300 syslog(LOG_CRIT, "uid %d: attempt to use \"%s\" (filename too long)",
301 (int) getuid(), cmd);
302#endif /* ! DEBUG */
303 exit(EX_UNAVAILABLE);
304 }
305
306#ifdef DEBUG
307 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
308 "Trying %s\n", cmdbuf);
309#endif /* DEBUG */
310 if (stat(cmdbuf, &st) < 0)
311 {
312 /* can't stat it */
313 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
314 "%s: \"%s\" not available for sendmail programs (stat failed)\n",
315 prg, cmd);
316 if (p != NULL)
317 *p = ' ';
318#ifndef DEBUG
319 syslog(LOG_CRIT, "uid %d: attempt to use \"%s\" (stat failed)",
320 (int) getuid(), cmd);
321#endif /* ! DEBUG */
322 exit(EX_UNAVAILABLE);
323 }
324 if (!S_ISREG(st.st_mode)
325#ifdef S_ISLNK
326 && !S_ISLNK(st.st_mode)
327#endif /* S_ISLNK */
328 )
329 {
330 /* can't stat it */
331 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
332 "%s: \"%s\" not available for sendmail programs (not a file)\n",
333 prg, cmd);
334 if (p != NULL)
335 *p = ' ';
336#ifndef DEBUG
337 syslog(LOG_CRIT, "uid %d: attempt to use \"%s\" (not a file)",
338 (int) getuid(), cmd);
339#endif /* ! DEBUG */
340 exit(EX_UNAVAILABLE);
341 }
342 if (access(cmdbuf, X_OK) < 0)
343 {
344 /* oops.... crack attack possiblity */
345 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
346 "%s: \"%s\" not available for sendmail programs\n",
347 prg, cmd);
348 if (p != NULL)
349 *p = ' ';
350#ifndef DEBUG
351 syslog(LOG_CRIT, "uid %d: attempt to use \"%s\"",
352 (int) getuid(), cmd);
353#endif /* ! DEBUG */
354 exit(EX_UNAVAILABLE);
355 }
356
357 /*
358 ** Create the actual shell input.
359 */
360
361 addcmd(cmd, true, strlen(cmd));
362 }
363 isexec = false;
364
365 if (p != NULL)
366 *p = ' ';
367 else
368 break;
369
370 r = strpbrk(p, specialbuf);
371 if (r == NULL)
372 {
373 addcmd(p, false, strlen(p));
374 break;
375 }
376#if ALLOWSEMI
377 if (*r == ';')
378 {
379 addcmd(p, false, r - p + 1);
380 q = r + 1;
381 continue;
382 }
383#endif /* ALLOWSEMI */
384 if ((*r == '&' && *(r + 1) == '&') ||
385 (*r == '|' && *(r + 1) == '|'))
386 {
387 addcmd(p, false, r - p + 2);
388 q = r + 2;
389 continue;
390 }
391
392 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
393 "%s: cannot use %c in command\n", prg, *r);
394#ifndef DEBUG
395 syslog(LOG_CRIT, "uid %d: attempt to use %c in command: %s",
396 (int) getuid(), *r, par);
397#endif /* ! DEBUG */
398 exit(EX_UNAVAILABLE);
399 }
400 if (isexec)
401 {
402 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
403 "%s: missing command to exec\n", prg);
404#ifndef DEBUG
405 syslog(LOG_CRIT, "uid %d: missing command to exec",
406 (int) getuid());
407#endif /* ! DEBUG */
408 exit(EX_UNAVAILABLE);
409 }
410 /* make sure we created something */
411 if (newcmdbuf[0] == '\0')
412 {
413 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
414 "Usage: %s -c command\n", prg);
415#ifndef DEBUG
416 syslog(LOG_ERR, "usage");
417#endif /* ! DEBUG */
418 exit(EX_USAGE);
419 }
420
421 /*
422 ** Now invoke the shell
423 */
424
425#ifdef DEBUG
426 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s\n", newcmdbuf);
427#endif /* DEBUG */
428 (void) execle("/bin/sh", "/bin/sh", "-c", newcmdbuf,
429 (char *)NULL, newenv);
430 save_errno = errno;
431#ifndef DEBUG
432 syslog(LOG_CRIT, "Cannot exec /bin/sh: %s", sm_errstring(errno));
433#endif /* ! DEBUG */
434 errno = save_errno;
435 sm_perror("/bin/sh");
436 exit(EX_OSFILE);
437 /* NOTREACHED */
438 return EX_OSFILE;
439}