2 * Copyright (c) 1998-2002 Sendmail, Inc. and its suppliers.
4 * Copyright (c) 1993 Eric P. Allman. All rights reserved.
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.
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\
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"
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:
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.
57 #include <sm/limits.h>
58 #include <sm/string.h>
60 #include <sys/types.h>
73 #include <sm/errstring.h>
75 /* directory in which all commands must reside */
78 # define CMDDIR SMRSH_CMDDIR
79 # else /* SMRSH_CMDDIR */
80 # define CMDDIR "/usr/adm/sm.bin"
81 # endif /* SMRSH_CMDDIR */
84 /* characters disallowed in the shell "-c" argument */
85 #define SPECIALS "<|>^();&`$\r\n"
87 /* default search path */
90 # define PATH SMRSH_PATH
91 # else /* SMRSH_PATH */
92 # define PATH "/bin:/usr/bin:/usr/ucb"
93 # endif /* SMRSH_PATH */
100 ** ADDCMD -- add a string to newcmdbuf, check for overflow
103 ** s -- string to add
104 ** cmd -- it's a command: prepend CMDDIR/
105 ** len -- length of string to add
108 ** changes newcmdbuf or exits with a failure.
118 if (s
== NULL
|| *s
== '\0')
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
);
127 syslog(LOG_WARNING
, "command too long: %.40s", par
);
129 exit(EX_UNAVAILABLE
);
132 (void) sm_strlcat2(newcmdbuf
, CMDDIR
, "/", sizeof newcmdbuf
);
133 (void) sm_strlcat(newcmdbuf
, s
, sizeof newcmdbuf
);
155 # else /* ! LOG_MAIL */
156 openlog("smrsh", LOG_ODELAY
|LOG_CONS
, LOG_MAIL
);
157 # endif /* ! LOG_MAIL */
160 (void) sm_strlcpyn(pathbuf
, sizeof pathbuf
, 2, "PATH=", PATH
);
165 ** Do basic argv usage checking
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
);
175 syslog(LOG_ERR
, "usage");
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
)
192 syslog(LOG_ERR
, "too many specials: %.40s", SPECIALS
);
194 exit(EX_UNAVAILABLE
);
196 (void) sm_strlcpy(specialbuf
, SPECIALS
, sizeof specialbuf
);
197 for (p
= specialbuf
; *p
!= '\0'; p
++)
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
);
210 syslog(LOG_WARNING
, "command too long: %.40s", par
);
212 exit(EX_UNAVAILABLE
);
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
))
233 (void) sm_io_fprintf(smioerr
, SM_TIME_DEFAULT
,
234 "%s: missing command to exec\n",
237 syslog(LOG_CRIT
, "uid %d: missing command to exec", (int) getuid());
239 exit(EX_UNAVAILABLE
);
244 /* find the end of the command name */
245 p
= strpbrk(q
, " \t");
253 /* search backwards for last / (allow for 0200 bit) */
256 if ((*--cmd
& 0177) == '/')
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 */
274 else if (strcmp(q
, "exit") == 0 || strcmp(q
, "echo") == 0)
276 addcmd(cmd
, false, strlen(cmd
));
278 /* test following chars */
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
)
292 (void) sm_io_fprintf(smioerr
, SM_TIME_DEFAULT
,
293 "%s: \"%s\" not available for sendmail programs (filename too long)\n",
298 syslog(LOG_CRIT
, "uid %d: attempt to use \"%s\" (filename too long)",
299 (int) getuid(), cmd
);
301 exit(EX_UNAVAILABLE
);
305 (void) sm_io_fprintf(smioout
, SM_TIME_DEFAULT
,
306 "Trying %s\n", cmdbuf
);
308 if (stat(cmdbuf
, &st
) < 0)
311 (void) sm_io_fprintf(smioerr
, SM_TIME_DEFAULT
,
312 "%s: \"%s\" not available for sendmail programs (stat failed)\n",
317 syslog(LOG_CRIT
, "uid %d: attempt to use \"%s\" (stat failed)",
318 (int) getuid(), cmd
);
320 exit(EX_UNAVAILABLE
);
322 if (!S_ISREG(st
.st_mode
)
324 && !S_ISLNK(st
.st_mode
)
329 (void) sm_io_fprintf(smioerr
, SM_TIME_DEFAULT
,
330 "%s: \"%s\" not available for sendmail programs (not a file)\n",
335 syslog(LOG_CRIT
, "uid %d: attempt to use \"%s\" (not a file)",
336 (int) getuid(), cmd
);
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",
349 syslog(LOG_CRIT
, "uid %d: attempt to use \"%s\"",
350 (int) getuid(), cmd
);
352 exit(EX_UNAVAILABLE
);
356 ** Create the actual shell input.
359 addcmd(cmd
, true, strlen(cmd
));
368 r
= strpbrk(p
, specialbuf
);
371 addcmd(p
, false, strlen(p
));
377 addcmd(p
, false, r
- p
+ 1);
381 #endif /* ALLOWSEMI */
382 if ((*r
== '&' && *(r
+ 1) == '&') ||
383 (*r
== '|' && *(r
+ 1) == '|'))
385 addcmd(p
, false, r
- p
+ 2);
390 (void) sm_io_fprintf(smioerr
, SM_TIME_DEFAULT
,
391 "%s: cannot use %c in command\n", prg
, *r
);
393 syslog(LOG_CRIT
, "uid %d: attempt to use %c in command: %s",
394 (int) getuid(), *r
, par
);
396 exit(EX_UNAVAILABLE
);
400 (void) sm_io_fprintf(smioerr
, SM_TIME_DEFAULT
,
401 "%s: missing command to exec\n", prg
);
403 syslog(LOG_CRIT
, "uid %d: missing command to exec",
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
);
414 syslog(LOG_ERR
, "usage");
420 ** Now invoke the shell
424 (void) sm_io_fprintf(smioout
, SM_TIME_DEFAULT
, "%s\n", newcmdbuf
);
426 (void) execle("/bin/sh", "/bin/sh", "-c", newcmdbuf
,
427 (char *)NULL
, newenv
);
430 syslog(LOG_CRIT
, "Cannot exec /bin/sh: %s", sm_errstring(errno
));
433 sm_perror("/bin/sh");