Update zoneinfo database.
[dragonfly/netmp.git] / contrib / sendmail / smrsh / smrsh.c
blob92df1003fed6b6accd43e9bea47176cdb32a2e7c
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.
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-2001 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.58.2.2 2002/09/24 21:40:05 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;
100 ** ADDCMD -- add a string to newcmdbuf, check for overflow
102 ** Parameters:
103 ** s -- string to add
104 ** cmd -- it's a command: prepend CMDDIR/
105 ** len -- length of string to add
107 ** Side Effects:
108 ** changes newcmdbuf or exits with a failure.
112 void
113 addcmd(s, cmd, len)
114 char *s;
115 bool cmd;
116 size_t len;
118 if (s == NULL || *s == '\0')
119 return;
121 if (sizeof newcmdbuf - strlen(newcmdbuf) <=
122 len + (cmd ? (strlen(CMDDIR) + 1) : 0))
124 (void)sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
125 "%s: command too long: %s\n", prg, par);
126 #ifndef DEBUG
127 syslog(LOG_WARNING, "command too long: %.40s", par);
128 #endif /* ! DEBUG */
129 exit(EX_UNAVAILABLE);
131 if (cmd)
132 (void) sm_strlcat2(newcmdbuf, CMDDIR, "/", sizeof newcmdbuf);
133 (void) sm_strlcat(newcmdbuf, s, sizeof newcmdbuf);
137 main(argc, argv)
138 int argc;
139 char **argv;
141 register char *p;
142 register char *q;
143 register char *r;
144 register char *cmd;
145 int isexec;
146 int save_errno;
147 char *newenv[2];
148 char pathbuf[1000];
149 char specialbuf[32];
150 struct stat st;
152 #ifndef DEBUG
153 # ifndef LOG_MAIL
154 openlog("smrsh", 0);
155 # else /* ! LOG_MAIL */
156 openlog("smrsh", LOG_ODELAY|LOG_CONS, LOG_MAIL);
157 # endif /* ! LOG_MAIL */
158 #endif /* ! DEBUG */
160 (void) sm_strlcpyn(pathbuf, sizeof pathbuf, 2, "PATH=", PATH);
161 newenv[0] = pathbuf;
162 newenv[1] = NULL;
165 ** Do basic argv usage checking
168 prg = argv[0];
170 if (argc != 3 || strcmp(argv[1], "-c") != 0)
172 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
173 "Usage: %s -c command\n", prg);
174 #ifndef DEBUG
175 syslog(LOG_ERR, "usage");
176 #endif /* ! DEBUG */
177 exit(EX_USAGE);
180 par = argv[2];
183 ** Disallow special shell syntax. This is overly restrictive,
184 ** but it should shut down all attacks.
185 ** Be sure to include 8-bit versions, since many shells strip
186 ** the address to 7 bits before checking.
189 if (strlen(SPECIALS) * 2 >= sizeof specialbuf)
191 #ifndef DEBUG
192 syslog(LOG_ERR, "too many specials: %.40s", SPECIALS);
193 #endif /* ! DEBUG */
194 exit(EX_UNAVAILABLE);
196 (void) sm_strlcpy(specialbuf, SPECIALS, sizeof specialbuf);
197 for (p = specialbuf; *p != '\0'; p++)
198 *p |= '\200';
199 (void) sm_strlcat(specialbuf, SPECIALS, sizeof specialbuf);
202 ** Do a quick sanity check on command line length.
205 if (strlen(par) > (sizeof newcmdbuf - sizeof CMDDIR - 2))
207 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
208 "%s: command too long: %s\n", prg, par);
209 #ifndef DEBUG
210 syslog(LOG_WARNING, "command too long: %.40s", par);
211 #endif /* ! DEBUG */
212 exit(EX_UNAVAILABLE);
215 q = par;
216 newcmdbuf[0] = '\0';
217 isexec = false;
219 while (*q != '\0')
222 ** Strip off a leading pathname on the command name. For
223 ** example, change /usr/ucb/vacation to vacation.
226 /* strip leading spaces */
227 while (*q != '\0' && isascii(*q) && isspace(*q))
228 q++;
229 if (*q == '\0')
231 if (isexec)
233 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
234 "%s: missing command to exec\n",
235 prg);
236 #ifndef DEBUG
237 syslog(LOG_CRIT, "uid %d: missing command to exec", (int) getuid());
238 #endif /* ! DEBUG */
239 exit(EX_UNAVAILABLE);
241 break;
244 /* find the end of the command name */
245 p = strpbrk(q, " \t");
246 if (p == NULL)
247 cmd = &q[strlen(q)];
248 else
250 *p = '\0';
251 cmd = p;
253 /* search backwards for last / (allow for 0200 bit) */
254 while (cmd > q)
256 if ((*--cmd & 0177) == '/')
258 cmd++;
259 break;
262 /* cmd now points at final component of path name */
264 /* allow a few shell builtins */
265 if (strcmp(q, "exec") == 0 && p != NULL)
267 addcmd("exec ", false, strlen("exec "));
269 /* test _next_ arg */
270 q = ++p;
271 isexec = true;
272 continue;
274 else if (strcmp(q, "exit") == 0 || strcmp(q, "echo") == 0)
276 addcmd(cmd, false, strlen(cmd));
278 /* test following chars */
280 else
282 char cmdbuf[MAXPATHLEN];
285 ** Check to see if the command name is legal.
288 if (sm_strlcpyn(cmdbuf, sizeof cmdbuf, 3, CMDDIR,
289 "/", cmd) >= sizeof cmdbuf)
291 /* too long */
292 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
293 "%s: \"%s\" not available for sendmail programs (filename too long)\n",
294 prg, cmd);
295 if (p != NULL)
296 *p = ' ';
297 #ifndef DEBUG
298 syslog(LOG_CRIT, "uid %d: attempt to use \"%s\" (filename too long)",
299 (int) getuid(), cmd);
300 #endif /* ! DEBUG */
301 exit(EX_UNAVAILABLE);
304 #ifdef DEBUG
305 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
306 "Trying %s\n", cmdbuf);
307 #endif /* DEBUG */
308 if (stat(cmdbuf, &st) < 0)
310 /* can't stat it */
311 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
312 "%s: \"%s\" not available for sendmail programs (stat failed)\n",
313 prg, cmd);
314 if (p != NULL)
315 *p = ' ';
316 #ifndef DEBUG
317 syslog(LOG_CRIT, "uid %d: attempt to use \"%s\" (stat failed)",
318 (int) getuid(), cmd);
319 #endif /* ! DEBUG */
320 exit(EX_UNAVAILABLE);
322 if (!S_ISREG(st.st_mode)
323 #ifdef S_ISLNK
324 && !S_ISLNK(st.st_mode)
325 #endif /* S_ISLNK */
328 /* can't stat it */
329 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
330 "%s: \"%s\" not available for sendmail programs (not a file)\n",
331 prg, cmd);
332 if (p != NULL)
333 *p = ' ';
334 #ifndef DEBUG
335 syslog(LOG_CRIT, "uid %d: attempt to use \"%s\" (not a file)",
336 (int) getuid(), cmd);
337 #endif /* ! DEBUG */
338 exit(EX_UNAVAILABLE);
340 if (access(cmdbuf, X_OK) < 0)
342 /* oops.... crack attack possiblity */
343 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
344 "%s: \"%s\" not available for sendmail programs\n",
345 prg, cmd);
346 if (p != NULL)
347 *p = ' ';
348 #ifndef DEBUG
349 syslog(LOG_CRIT, "uid %d: attempt to use \"%s\"",
350 (int) getuid(), cmd);
351 #endif /* ! DEBUG */
352 exit(EX_UNAVAILABLE);
356 ** Create the actual shell input.
359 addcmd(cmd, true, strlen(cmd));
361 isexec = false;
363 if (p != NULL)
364 *p = ' ';
365 else
366 break;
368 r = strpbrk(p, specialbuf);
369 if (r == NULL)
371 addcmd(p, false, strlen(p));
372 break;
374 #if ALLOWSEMI
375 if (*r == ';')
377 addcmd(p, false, r - p + 1);
378 q = r + 1;
379 continue;
381 #endif /* ALLOWSEMI */
382 if ((*r == '&' && *(r + 1) == '&') ||
383 (*r == '|' && *(r + 1) == '|'))
385 addcmd(p, false, r - p + 2);
386 q = r + 2;
387 continue;
390 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
391 "%s: cannot use %c in command\n", prg, *r);
392 #ifndef DEBUG
393 syslog(LOG_CRIT, "uid %d: attempt to use %c in command: %s",
394 (int) getuid(), *r, par);
395 #endif /* ! DEBUG */
396 exit(EX_UNAVAILABLE);
398 if (isexec)
400 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
401 "%s: missing command to exec\n", prg);
402 #ifndef DEBUG
403 syslog(LOG_CRIT, "uid %d: missing command to exec",
404 (int) getuid());
405 #endif /* ! DEBUG */
406 exit(EX_UNAVAILABLE);
408 /* make sure we created something */
409 if (newcmdbuf[0] == '\0')
411 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
412 "Usage: %s -c command\n", prg);
413 #ifndef DEBUG
414 syslog(LOG_ERR, "usage");
415 #endif /* ! DEBUG */
416 exit(EX_USAGE);
420 ** Now invoke the shell
423 #ifdef DEBUG
424 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s\n", newcmdbuf);
425 #endif /* DEBUG */
426 (void) execle("/bin/sh", "/bin/sh", "-c", newcmdbuf,
427 (char *)NULL, newenv);
428 save_errno = errno;
429 #ifndef DEBUG
430 syslog(LOG_CRIT, "Cannot exec /bin/sh: %s", sm_errstring(errno));
431 #endif /* ! DEBUG */
432 errno = save_errno;
433 sm_perror("/bin/sh");
434 exit(EX_OSFILE);
435 /* NOTREACHED */
436 return EX_OSFILE;