MFC: An off-by-one malloc size was corrupting the installer's memory,
[dragonfly.git] / contrib / sendmail-8.14 / smrsh / smrsh.c
blob284ffef7700d0f4d8f5bcf7eddc433526ae2fdf3
1 /*
2 * Copyright (c) 1998-2004 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.
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.
14 #include <sm/gen.h>
16 SM_IDSTR(copyright,
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\
20 Copyright (c) 1993\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"
36 ** or "sh".
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:
45 ** | &
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.
55 #include <unistd.h>
56 #include <sm/io.h>
57 #include <sm/limits.h>
58 #include <sm/string.h>
59 #include <sys/file.h>
60 #include <sys/types.h>
61 #include <sys/stat.h>
62 #include <string.h>
63 #include <ctype.h>
64 #include <errno.h>
65 #ifdef EX_OK
66 # undef EX_OK
67 #endif /* EX_OK */
68 #include <sysexits.h>
69 #include <syslog.h>
70 #include <stdlib.h>
72 #include <sm/conf.h>
73 #include <sm/errstring.h>
75 /* directory in which all commands must reside */
76 #ifndef CMDDIR
77 # ifdef SMRSH_CMDDIR
78 # define CMDDIR SMRSH_CMDDIR
79 # else /* SMRSH_CMDDIR */
80 # define CMDDIR "/usr/adm/sm.bin"
81 # endif /* SMRSH_CMDDIR */
82 #endif /* ! CMDDIR */
84 /* characters disallowed in the shell "-c" argument */
85 #define SPECIALS "<|>^();&`$\r\n"
87 /* default search path */
88 #ifndef PATH
89 # ifdef SMRSH_PATH
90 # define PATH SMRSH_PATH
91 # else /* SMRSH_PATH */
92 # define PATH "/bin:/usr/bin:/usr/ucb"
93 # endif /* SMRSH_PATH */
94 #endif /* ! PATH */
96 char newcmdbuf[1000];
97 char *prg, *par;
99 static void addcmd __P((char *, bool, size_t));
102 ** ADDCMD -- add a string to newcmdbuf, check for overflow
104 ** Parameters:
105 ** s -- string to add
106 ** cmd -- it's a command: prepend CMDDIR/
107 ** len -- length of string to add
109 ** Side Effects:
110 ** changes newcmdbuf or exits with a failure.
114 static void
115 addcmd(s, cmd, len)
116 char *s;
117 bool cmd;
118 size_t len;
120 if (s == NULL || *s == '\0')
121 return;
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);
129 #ifndef DEBUG
130 syslog(LOG_WARNING, "command too long: %.40s", par);
131 #endif /* ! DEBUG */
132 exit(EX_UNAVAILABLE);
134 if (cmd)
135 (void) sm_strlcat2(newcmdbuf, CMDDIR, "/", sizeof newcmdbuf);
136 (void) strncat(newcmdbuf, s, len);
140 main(argc, argv)
141 int argc;
142 char **argv;
144 register char *p;
145 register char *q;
146 register char *r;
147 register char *cmd;
148 int isexec;
149 int save_errno;
150 char *newenv[2];
151 char pathbuf[1000];
152 char specialbuf[32];
153 struct stat st;
155 #ifndef DEBUG
156 # ifndef LOG_MAIL
157 openlog("smrsh", 0);
158 # else /* ! LOG_MAIL */
159 openlog("smrsh", LOG_ODELAY|LOG_CONS, LOG_MAIL);
160 # endif /* ! LOG_MAIL */
161 #endif /* ! DEBUG */
163 (void) sm_strlcpyn(pathbuf, sizeof pathbuf, 2, "PATH=", PATH);
164 newenv[0] = pathbuf;
165 newenv[1] = NULL;
168 ** Do basic argv usage checking
171 prg = argv[0];
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);
177 #ifndef DEBUG
178 syslog(LOG_ERR, "usage");
179 #endif /* ! DEBUG */
180 exit(EX_USAGE);
183 par = argv[2];
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)
194 #ifndef DEBUG
195 syslog(LOG_ERR, "too many specials: %.40s", SPECIALS);
196 #endif /* ! DEBUG */
197 exit(EX_UNAVAILABLE);
199 (void) sm_strlcpy(specialbuf, SPECIALS, sizeof specialbuf);
200 for (p = specialbuf; *p != '\0'; p++)
201 *p |= '\200';
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);
212 #ifndef DEBUG
213 syslog(LOG_WARNING, "command too long: %.40s", par);
214 #endif /* ! DEBUG */
215 exit(EX_UNAVAILABLE);
218 q = par;
219 newcmdbuf[0] = '\0';
220 isexec = false;
222 while (*q != '\0')
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))
231 q++;
232 if (*q == '\0')
234 if (isexec)
236 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
237 "%s: missing command to exec\n",
238 prg);
239 #ifndef DEBUG
240 syslog(LOG_CRIT, "uid %d: missing command to exec", (int) getuid());
241 #endif /* ! DEBUG */
242 exit(EX_UNAVAILABLE);
244 break;
247 /* find the end of the command name */
248 p = strpbrk(q, " \t");
249 if (p == NULL)
250 cmd = &q[strlen(q)];
251 else
253 *p = '\0';
254 cmd = p;
256 /* search backwards for last / (allow for 0200 bit) */
257 while (cmd > q)
259 if ((*--cmd & 0177) == '/')
261 cmd++;
262 break;
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 */
273 q = ++p;
274 isexec = true;
275 continue;
277 else if (strcmp(q, "exit") == 0 || strcmp(q, "echo") == 0)
279 addcmd(cmd, false, strlen(cmd));
281 /* test following chars */
283 else
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)
294 /* too long */
295 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
296 "%s: \"%s\" not available for sendmail programs (filename too long)\n",
297 prg, cmd);
298 if (p != NULL)
299 *p = ' ';
300 #ifndef DEBUG
301 syslog(LOG_CRIT, "uid %d: attempt to use \"%s\" (filename too long)",
302 (int) getuid(), cmd);
303 #endif /* ! DEBUG */
304 exit(EX_UNAVAILABLE);
307 #ifdef DEBUG
308 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
309 "Trying %s\n", cmdbuf);
310 #endif /* DEBUG */
311 if (stat(cmdbuf, &st) < 0)
313 /* can't stat it */
314 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
315 "%s: \"%s\" not available for sendmail programs (stat failed)\n",
316 prg, cmd);
317 if (p != NULL)
318 *p = ' ';
319 #ifndef DEBUG
320 syslog(LOG_CRIT, "uid %d: attempt to use \"%s\" (stat failed)",
321 (int) getuid(), cmd);
322 #endif /* ! DEBUG */
323 exit(EX_UNAVAILABLE);
325 if (!S_ISREG(st.st_mode)
326 #ifdef S_ISLNK
327 && !S_ISLNK(st.st_mode)
328 #endif /* S_ISLNK */
331 /* can't stat it */
332 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
333 "%s: \"%s\" not available for sendmail programs (not a file)\n",
334 prg, cmd);
335 if (p != NULL)
336 *p = ' ';
337 #ifndef DEBUG
338 syslog(LOG_CRIT, "uid %d: attempt to use \"%s\" (not a file)",
339 (int) getuid(), cmd);
340 #endif /* ! DEBUG */
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",
348 prg, cmd);
349 if (p != NULL)
350 *p = ' ';
351 #ifndef DEBUG
352 syslog(LOG_CRIT, "uid %d: attempt to use \"%s\"",
353 (int) getuid(), cmd);
354 #endif /* ! DEBUG */
355 exit(EX_UNAVAILABLE);
359 ** Create the actual shell input.
362 addcmd(cmd, true, strlen(cmd));
364 isexec = false;
366 if (p != NULL)
367 *p = ' ';
368 else
369 break;
371 r = strpbrk(p, specialbuf);
372 if (r == NULL)
374 addcmd(p, false, strlen(p));
375 break;
377 #if ALLOWSEMI
378 if (*r == ';')
380 addcmd(p, false, r - p + 1);
381 q = r + 1;
382 continue;
384 #endif /* ALLOWSEMI */
385 if ((*r == '&' && *(r + 1) == '&') ||
386 (*r == '|' && *(r + 1) == '|'))
388 addcmd(p, false, r - p + 2);
389 q = r + 2;
390 continue;
393 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
394 "%s: cannot use %c in command\n", prg, *r);
395 #ifndef DEBUG
396 syslog(LOG_CRIT, "uid %d: attempt to use %c in command: %s",
397 (int) getuid(), *r, par);
398 #endif /* ! DEBUG */
399 exit(EX_UNAVAILABLE);
401 if (isexec)
403 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
404 "%s: missing command to exec\n", prg);
405 #ifndef DEBUG
406 syslog(LOG_CRIT, "uid %d: missing command to exec",
407 (int) getuid());
408 #endif /* ! DEBUG */
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);
416 #ifndef DEBUG
417 syslog(LOG_ERR, "usage");
418 #endif /* ! DEBUG */
419 exit(EX_USAGE);
423 ** Now invoke the shell
426 #ifdef DEBUG
427 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s\n", newcmdbuf);
428 #endif /* DEBUG */
429 (void) execle("/bin/sh", "/bin/sh", "-c", newcmdbuf,
430 (char *)NULL, newenv);
431 save_errno = errno;
432 #ifndef DEBUG
433 syslog(LOG_CRIT, "Cannot exec /bin/sh: %s", sm_errstring(errno));
434 #endif /* ! DEBUG */
435 errno = save_errno;
436 sm_perror("/bin/sh");
437 exit(EX_OSFILE);
438 /* NOTREACHED */
439 return EX_OSFILE;