smrsh.c revision 121826
138032Speter/* 298125Sgshapiro * Copyright (c) 1998-2002 Sendmail, Inc. and its suppliers. 364565Sgshapiro * All rights reserved. 438032Speter * Copyright (c) 1993 Eric P. Allman. All rights reserved. 538032Speter * Copyright (c) 1993 638032Speter * The Regents of the University of California. All rights reserved. 738032Speter * 838032Speter * By using this file, you agree to the terms and conditions set 938032Speter * forth in the LICENSE file which can be found at the top level of 1038032Speter * the sendmail distribution. 1138032Speter * 12121826Sgshapiro * $FreeBSD: head/contrib/sendmail/smrsh/smrsh.c 121826 2003-10-31 22:12:09Z gshapiro $ 1338032Speter */ 1438032Speter 1590795Sgshapiro#include <sm/gen.h> 1690795Sgshapiro 1790795SgshapiroSM_IDSTR(copyright, 1873191Sgshapiro"@(#) Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.\n\ 1964565Sgshapiro All rights reserved.\n\ 2064565Sgshapiro Copyright (c) 1993 Eric P. Allman. All rights reserved.\n\ 2164565Sgshapiro Copyright (c) 1993\n\ 2290795Sgshapiro The Regents of the University of California. All rights reserved.\n") 2338032Speter 24110563SgshapiroSM_IDSTR(id, "@(#)$Id: smrsh.c,v 8.58.2.2 2002/09/24 21:40:05 ca Exp $") 2564565Sgshapiro 2638032Speter/* 2738032Speter** SMRSH -- sendmail restricted shell 2838032Speter** 2938032Speter** This is a patch to get around the prog mailer bugs in most 3038032Speter** versions of sendmail. 3138032Speter** 3238032Speter** Use this in place of /bin/sh in the "prog" mailer definition 3338032Speter** in your sendmail.cf file. You then create CMDDIR (owned by 3438032Speter** root, mode 755) and put links to any programs you want 3538032Speter** available to prog mailers in that directory. This should 3638032Speter** include things like "vacation" and "procmail", but not "sed" 3738032Speter** or "sh". 3838032Speter** 3938032Speter** Leading pathnames are stripped from program names so that 4038032Speter** existing .forward files that reference things like 4138081Speter** "/usr/bin/vacation" will continue to work. 4238032Speter** 4338032Speter** The following characters are completely illegal: 4464565Sgshapiro** < > ^ & ` ( ) \n \r 4564565Sgshapiro** The following characters are sometimes illegal: 4664565Sgshapiro** | & 4738032Speter** This is more restrictive than strictly necessary. 4838032Speter** 4964565Sgshapiro** To use this, add FEATURE(`smrsh') to your .mc file. 5038032Speter** 5138032Speter** This can be used on any version of sendmail. 5238032Speter** 5338032Speter** In loving memory of RTM. 11/02/93. 5438032Speter*/ 5538032Speter 5638032Speter#include <unistd.h> 5790795Sgshapiro#include <sm/io.h> 5898125Sgshapiro#include <sm/limits.h> 5990795Sgshapiro#include <sm/string.h> 6038032Speter#include <sys/file.h> 61105016Sgshapiro#include <sys/types.h> 62105016Sgshapiro#include <sys/stat.h> 6338032Speter#include <string.h> 6438032Speter#include <ctype.h> 6564565Sgshapiro#include <errno.h> 6638032Speter#ifdef EX_OK 6738032Speter# undef EX_OK 6864565Sgshapiro#endif /* EX_OK */ 6938032Speter#include <sysexits.h> 7038032Speter#include <syslog.h> 7138032Speter#include <stdlib.h> 7238032Speter 7390795Sgshapiro#include <sm/conf.h> 7490795Sgshapiro#include <sm/errstring.h> 7564565Sgshapiro 7638032Speter/* directory in which all commands must reside */ 7738032Speter#ifndef CMDDIR 7890795Sgshapiro# ifdef SMRSH_CMDDIR 7990795Sgshapiro# define CMDDIR SMRSH_CMDDIR 8090795Sgshapiro# else /* SMRSH_CMDDIR */ 8190795Sgshapiro# define CMDDIR "/usr/adm/sm.bin" 8290795Sgshapiro# endif /* SMRSH_CMDDIR */ 8364565Sgshapiro#endif /* ! CMDDIR */ 8438032Speter 8538032Speter/* characters disallowed in the shell "-c" argument */ 8638032Speter#define SPECIALS "<|>^();&`$\r\n" 8738032Speter 8838032Speter/* default search path */ 8938032Speter#ifndef PATH 9090795Sgshapiro# ifdef SMRSH_PATH 9190795Sgshapiro# define PATH SMRSH_PATH 9290795Sgshapiro# else /* SMRSH_PATH */ 9390795Sgshapiro# define PATH "/bin:/usr/bin:/usr/ucb" 9490795Sgshapiro# endif /* SMRSH_PATH */ 9564565Sgshapiro#endif /* ! PATH */ 9638032Speter 9764565Sgshapirochar newcmdbuf[1000]; 9864565Sgshapirochar *prg, *par; 9964565Sgshapiro 10064565Sgshapiro/* 10164565Sgshapiro** ADDCMD -- add a string to newcmdbuf, check for overflow 10264565Sgshapiro** 10364565Sgshapiro** Parameters: 10464565Sgshapiro** s -- string to add 10564565Sgshapiro** cmd -- it's a command: prepend CMDDIR/ 10664565Sgshapiro** len -- length of string to add 10764565Sgshapiro** 10864565Sgshapiro** Side Effects: 10964565Sgshapiro** changes newcmdbuf or exits with a failure. 11064565Sgshapiro** 11164565Sgshapiro*/ 11264565Sgshapiro 11364565Sgshapirovoid 11464565Sgshapiroaddcmd(s, cmd, len) 11564565Sgshapiro char *s; 11690795Sgshapiro bool cmd; 11790795Sgshapiro size_t len; 11864565Sgshapiro{ 11964565Sgshapiro if (s == NULL || *s == '\0') 12064565Sgshapiro return; 12164565Sgshapiro 12264565Sgshapiro if (sizeof newcmdbuf - strlen(newcmdbuf) <= 12364565Sgshapiro len + (cmd ? (strlen(CMDDIR) + 1) : 0)) 12464565Sgshapiro { 12590795Sgshapiro (void)sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 12690795Sgshapiro "%s: command too long: %s\n", prg, par); 12764565Sgshapiro#ifndef DEBUG 12864565Sgshapiro syslog(LOG_WARNING, "command too long: %.40s", par); 12964565Sgshapiro#endif /* ! DEBUG */ 13064565Sgshapiro exit(EX_UNAVAILABLE); 13164565Sgshapiro } 13264565Sgshapiro if (cmd) 13398125Sgshapiro (void) sm_strlcat2(newcmdbuf, CMDDIR, "/", sizeof newcmdbuf); 13490795Sgshapiro (void) sm_strlcat(newcmdbuf, s, sizeof newcmdbuf); 13564565Sgshapiro} 13664565Sgshapiro 13738032Speterint 13838032Spetermain(argc, argv) 13938032Speter int argc; 14038032Speter char **argv; 14138032Speter{ 14238032Speter register char *p; 14338032Speter register char *q; 14464565Sgshapiro register char *r; 14538032Speter register char *cmd; 14664565Sgshapiro int isexec; 14764565Sgshapiro int save_errno; 14838032Speter char *newenv[2]; 14938032Speter char pathbuf[1000]; 15064565Sgshapiro char specialbuf[32]; 151105016Sgshapiro struct stat st; 15238032Speter 15364565Sgshapiro#ifndef DEBUG 15464565Sgshapiro# ifndef LOG_MAIL 15538032Speter openlog("smrsh", 0); 15664565Sgshapiro# else /* ! LOG_MAIL */ 15738032Speter openlog("smrsh", LOG_ODELAY|LOG_CONS, LOG_MAIL); 15864565Sgshapiro# endif /* ! LOG_MAIL */ 15964565Sgshapiro#endif /* ! DEBUG */ 16038032Speter 16198125Sgshapiro (void) sm_strlcpyn(pathbuf, sizeof pathbuf, 2, "PATH=", PATH); 16238032Speter newenv[0] = pathbuf; 16338032Speter newenv[1] = NULL; 16438032Speter 16538032Speter /* 16638032Speter ** Do basic argv usage checking 16738032Speter */ 16838032Speter 16964565Sgshapiro prg = argv[0]; 17064565Sgshapiro 17138032Speter if (argc != 3 || strcmp(argv[1], "-c") != 0) 17238032Speter { 17390795Sgshapiro (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 17490795Sgshapiro "Usage: %s -c command\n", prg); 17564565Sgshapiro#ifndef DEBUG 17638032Speter syslog(LOG_ERR, "usage"); 17764565Sgshapiro#endif /* ! DEBUG */ 17838032Speter exit(EX_USAGE); 17938032Speter } 18038032Speter 18177352Sgshapiro par = argv[2]; 18277352Sgshapiro 18338032Speter /* 18438032Speter ** Disallow special shell syntax. This is overly restrictive, 18538032Speter ** but it should shut down all attacks. 18638032Speter ** Be sure to include 8-bit versions, since many shells strip 18738032Speter ** the address to 7 bits before checking. 18838032Speter */ 18938032Speter 19064565Sgshapiro if (strlen(SPECIALS) * 2 >= sizeof specialbuf) 19138032Speter { 19264565Sgshapiro#ifndef DEBUG 19364565Sgshapiro syslog(LOG_ERR, "too many specials: %.40s", SPECIALS); 19464565Sgshapiro#endif /* ! DEBUG */ 19538032Speter exit(EX_UNAVAILABLE); 19638032Speter } 19790795Sgshapiro (void) sm_strlcpy(specialbuf, SPECIALS, sizeof specialbuf); 19864565Sgshapiro for (p = specialbuf; *p != '\0'; p++) 19964565Sgshapiro *p |= '\200'; 20090795Sgshapiro (void) sm_strlcat(specialbuf, SPECIALS, sizeof specialbuf); 20138032Speter 20238032Speter /* 20338032Speter ** Do a quick sanity check on command line length. 20438032Speter */ 20538032Speter 20690795Sgshapiro if (strlen(par) > (sizeof newcmdbuf - sizeof CMDDIR - 2)) 20738032Speter { 20890795Sgshapiro (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 20990795Sgshapiro "%s: command too long: %s\n", prg, par); 21064565Sgshapiro#ifndef DEBUG 21164565Sgshapiro syslog(LOG_WARNING, "command too long: %.40s", par); 21264565Sgshapiro#endif /* ! DEBUG */ 21338032Speter exit(EX_UNAVAILABLE); 21438032Speter } 21538032Speter 21664565Sgshapiro q = par; 21764565Sgshapiro newcmdbuf[0] = '\0'; 21890795Sgshapiro isexec = false; 21938032Speter 22098125Sgshapiro while (*q != '\0') 22138032Speter { 22264565Sgshapiro /* 22364565Sgshapiro ** Strip off a leading pathname on the command name. For 22464565Sgshapiro ** example, change /usr/ucb/vacation to vacation. 22564565Sgshapiro */ 22638032Speter 22764565Sgshapiro /* strip leading spaces */ 22864565Sgshapiro while (*q != '\0' && isascii(*q) && isspace(*q)) 22964565Sgshapiro q++; 23064565Sgshapiro if (*q == '\0') 23138032Speter { 23264565Sgshapiro if (isexec) 23364565Sgshapiro { 23490795Sgshapiro (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 23590795Sgshapiro "%s: missing command to exec\n", 23690795Sgshapiro prg); 23764565Sgshapiro#ifndef DEBUG 23890795Sgshapiro syslog(LOG_CRIT, "uid %d: missing command to exec", (int) getuid()); 23964565Sgshapiro#endif /* ! DEBUG */ 24064565Sgshapiro exit(EX_UNAVAILABLE); 24164565Sgshapiro } 24238032Speter break; 24338032Speter } 24438032Speter 24564565Sgshapiro /* find the end of the command name */ 24664565Sgshapiro p = strpbrk(q, " \t"); 24764565Sgshapiro if (p == NULL) 24864565Sgshapiro cmd = &q[strlen(q)]; 24964565Sgshapiro else 25064565Sgshapiro { 25164565Sgshapiro *p = '\0'; 25264565Sgshapiro cmd = p; 25364565Sgshapiro } 25464565Sgshapiro /* search backwards for last / (allow for 0200 bit) */ 25564565Sgshapiro while (cmd > q) 25664565Sgshapiro { 25764565Sgshapiro if ((*--cmd & 0177) == '/') 25864565Sgshapiro { 25964565Sgshapiro cmd++; 26064565Sgshapiro break; 26164565Sgshapiro } 26264565Sgshapiro } 26364565Sgshapiro /* cmd now points at final component of path name */ 26438032Speter 26564565Sgshapiro /* allow a few shell builtins */ 26664565Sgshapiro if (strcmp(q, "exec") == 0 && p != NULL) 26764565Sgshapiro { 26890795Sgshapiro addcmd("exec ", false, strlen("exec ")); 26998125Sgshapiro 27064565Sgshapiro /* test _next_ arg */ 27164565Sgshapiro q = ++p; 27290795Sgshapiro isexec = true; 27364565Sgshapiro continue; 27464565Sgshapiro } 27564565Sgshapiro else if (strcmp(q, "exit") == 0 || strcmp(q, "echo") == 0) 27664565Sgshapiro { 27790795Sgshapiro addcmd(cmd, false, strlen(cmd)); 27898125Sgshapiro 27964565Sgshapiro /* test following chars */ 28064565Sgshapiro } 28164565Sgshapiro else 28264565Sgshapiro { 28398125Sgshapiro char cmdbuf[MAXPATHLEN]; 28498125Sgshapiro 28564565Sgshapiro /* 28664565Sgshapiro ** Check to see if the command name is legal. 28764565Sgshapiro */ 28898125Sgshapiro 28998125Sgshapiro if (sm_strlcpyn(cmdbuf, sizeof cmdbuf, 3, CMDDIR, 29098125Sgshapiro "/", cmd) >= sizeof cmdbuf) 29198125Sgshapiro { 29298125Sgshapiro /* too long */ 29398125Sgshapiro (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 294110563Sgshapiro "%s: \"%s\" not available for sendmail programs (filename too long)\n", 29598125Sgshapiro prg, cmd); 29698125Sgshapiro if (p != NULL) 29798125Sgshapiro *p = ' '; 29898125Sgshapiro#ifndef DEBUG 299110563Sgshapiro syslog(LOG_CRIT, "uid %d: attempt to use \"%s\" (filename too long)", 30098125Sgshapiro (int) getuid(), cmd); 30198125Sgshapiro#endif /* ! DEBUG */ 30298125Sgshapiro exit(EX_UNAVAILABLE); 30398125Sgshapiro } 30498125Sgshapiro 30564565Sgshapiro#ifdef DEBUG 30690795Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 30790795Sgshapiro "Trying %s\n", cmdbuf); 30864565Sgshapiro#endif /* DEBUG */ 309105016Sgshapiro if (stat(cmdbuf, &st) < 0) 310105016Sgshapiro { 311105016Sgshapiro /* can't stat it */ 312105016Sgshapiro (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 313110563Sgshapiro "%s: \"%s\" not available for sendmail programs (stat failed)\n", 314105016Sgshapiro prg, cmd); 315105016Sgshapiro if (p != NULL) 316105016Sgshapiro *p = ' '; 317105016Sgshapiro#ifndef DEBUG 318110563Sgshapiro syslog(LOG_CRIT, "uid %d: attempt to use \"%s\" (stat failed)", 319105016Sgshapiro (int) getuid(), cmd); 320105016Sgshapiro#endif /* ! DEBUG */ 321105016Sgshapiro exit(EX_UNAVAILABLE); 322105016Sgshapiro } 323105016Sgshapiro if (!S_ISREG(st.st_mode) 324105016Sgshapiro#ifdef S_ISLNK 325105016Sgshapiro && !S_ISLNK(st.st_mode) 326105016Sgshapiro#endif /* S_ISLNK */ 327105016Sgshapiro ) 328105016Sgshapiro { 329105016Sgshapiro /* can't stat it */ 330105016Sgshapiro (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 331110563Sgshapiro "%s: \"%s\" not available for sendmail programs (not a file)\n", 332105016Sgshapiro prg, cmd); 333105016Sgshapiro if (p != NULL) 334105016Sgshapiro *p = ' '; 335105016Sgshapiro#ifndef DEBUG 336110563Sgshapiro syslog(LOG_CRIT, "uid %d: attempt to use \"%s\" (not a file)", 337105016Sgshapiro (int) getuid(), cmd); 338105016Sgshapiro#endif /* ! DEBUG */ 339105016Sgshapiro exit(EX_UNAVAILABLE); 340105016Sgshapiro } 34164565Sgshapiro if (access(cmdbuf, X_OK) < 0) 34264565Sgshapiro { 34364565Sgshapiro /* oops.... crack attack possiblity */ 34490795Sgshapiro (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 345110563Sgshapiro "%s: \"%s\" not available for sendmail programs\n", 34690795Sgshapiro prg, cmd); 34764565Sgshapiro if (p != NULL) 34864565Sgshapiro *p = ' '; 34964565Sgshapiro#ifndef DEBUG 350110563Sgshapiro syslog(LOG_CRIT, "uid %d: attempt to use \"%s\"", 35190795Sgshapiro (int) getuid(), cmd); 35264565Sgshapiro#endif /* ! DEBUG */ 35364565Sgshapiro exit(EX_UNAVAILABLE); 35464565Sgshapiro } 35538032Speter 35664565Sgshapiro /* 35764565Sgshapiro ** Create the actual shell input. 35864565Sgshapiro */ 35964565Sgshapiro 36090795Sgshapiro addcmd(cmd, true, strlen(cmd)); 36164565Sgshapiro } 36290795Sgshapiro isexec = false; 36364565Sgshapiro 36438032Speter if (p != NULL) 36538032Speter *p = ' '; 36664565Sgshapiro else 36764565Sgshapiro break; 36864565Sgshapiro 36964565Sgshapiro r = strpbrk(p, specialbuf); 37090795Sgshapiro if (r == NULL) 37190795Sgshapiro { 37290795Sgshapiro addcmd(p, false, strlen(p)); 37364565Sgshapiro break; 37464565Sgshapiro } 37564565Sgshapiro#if ALLOWSEMI 37690795Sgshapiro if (*r == ';') 37790795Sgshapiro { 37890795Sgshapiro addcmd(p, false, r - p + 1); 37964565Sgshapiro q = r + 1; 38064565Sgshapiro continue; 38164565Sgshapiro } 38264565Sgshapiro#endif /* ALLOWSEMI */ 38364565Sgshapiro if ((*r == '&' && *(r + 1) == '&') || 38464565Sgshapiro (*r == '|' && *(r + 1) == '|')) 38564565Sgshapiro { 38690795Sgshapiro addcmd(p, false, r - p + 2); 38764565Sgshapiro q = r + 2; 38864565Sgshapiro continue; 38964565Sgshapiro } 39064565Sgshapiro 39190795Sgshapiro (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 39290795Sgshapiro "%s: cannot use %c in command\n", prg, *r); 39364565Sgshapiro#ifndef DEBUG 39464565Sgshapiro syslog(LOG_CRIT, "uid %d: attempt to use %c in command: %s", 39590795Sgshapiro (int) getuid(), *r, par); 39664565Sgshapiro#endif /* ! DEBUG */ 39738032Speter exit(EX_UNAVAILABLE); 39898125Sgshapiro } 39964565Sgshapiro if (isexec) 40064565Sgshapiro { 40190795Sgshapiro (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 40290795Sgshapiro "%s: missing command to exec\n", prg); 40364565Sgshapiro#ifndef DEBUG 40490795Sgshapiro syslog(LOG_CRIT, "uid %d: missing command to exec", 40590795Sgshapiro (int) getuid()); 40664565Sgshapiro#endif /* ! DEBUG */ 40764565Sgshapiro exit(EX_UNAVAILABLE); 40838032Speter } 40964565Sgshapiro /* make sure we created something */ 41064565Sgshapiro if (newcmdbuf[0] == '\0') 41164565Sgshapiro { 41290795Sgshapiro (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 41390795Sgshapiro "Usage: %s -c command\n", prg); 41464565Sgshapiro#ifndef DEBUG 41564565Sgshapiro syslog(LOG_ERR, "usage"); 41664565Sgshapiro#endif /* ! DEBUG */ 41764565Sgshapiro exit(EX_USAGE); 41864565Sgshapiro } 41938032Speter 42038032Speter /* 42138032Speter ** Now invoke the shell 42238032Speter */ 42338032Speter 42438032Speter#ifdef DEBUG 42590795Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s\n", newcmdbuf); 42664565Sgshapiro#endif /* DEBUG */ 427121826Sgshapiro (void) execle("/bin/sh", "/bin/sh", "-c", newcmdbuf, 428121826Sgshapiro (char *)NULL, newenv); 42964565Sgshapiro save_errno = errno; 43064565Sgshapiro#ifndef DEBUG 43190795Sgshapiro syslog(LOG_CRIT, "Cannot exec /bin/sh: %s", sm_errstring(errno)); 43264565Sgshapiro#endif /* ! DEBUG */ 43364565Sgshapiro errno = save_errno; 43490795Sgshapiro sm_perror("/bin/sh"); 43538032Speter exit(EX_OSFILE); 43664565Sgshapiro /* NOTREACHED */ 43764565Sgshapiro return EX_OSFILE; 43838032Speter} 439