smrsh.c revision 90795
138032Speter/* 273191Sgshapiro * Copyright (c) 1998-2001 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 * 1238032Speter */ 1338032Speter 1490795Sgshapiro#include <sm/gen.h> 1590795Sgshapiro 1690795SgshapiroSM_IDSTR(copyright, 1773191Sgshapiro"@(#) Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.\n\ 1864565Sgshapiro All rights reserved.\n\ 1964565Sgshapiro Copyright (c) 1993 Eric P. Allman. All rights reserved.\n\ 2064565Sgshapiro Copyright (c) 1993\n\ 2190795Sgshapiro The Regents of the University of California. All rights reserved.\n") 2238032Speter 2390795SgshapiroSM_IDSTR(id, "@(#)$Id: smrsh.c,v 8.55 2001/09/11 04:05:22 gshapiro Exp $") 2464565Sgshapiro 2564565Sgshapiro/* $FreeBSD: head/contrib/sendmail/smrsh/smrsh.c 90795 2002-02-17 21:58:34Z gshapiro $ */ 2664565Sgshapiro 2738032Speter/* 2838032Speter** SMRSH -- sendmail restricted shell 2938032Speter** 3038032Speter** This is a patch to get around the prog mailer bugs in most 3138032Speter** versions of sendmail. 3238032Speter** 3338032Speter** Use this in place of /bin/sh in the "prog" mailer definition 3438032Speter** in your sendmail.cf file. You then create CMDDIR (owned by 3538032Speter** root, mode 755) and put links to any programs you want 3638032Speter** available to prog mailers in that directory. This should 3738032Speter** include things like "vacation" and "procmail", but not "sed" 3838032Speter** or "sh". 3938032Speter** 4038032Speter** Leading pathnames are stripped from program names so that 4138032Speter** existing .forward files that reference things like 4238081Speter** "/usr/bin/vacation" will continue to work. 4338032Speter** 4438032Speter** The following characters are completely illegal: 4564565Sgshapiro** < > ^ & ` ( ) \n \r 4664565Sgshapiro** The following characters are sometimes illegal: 4764565Sgshapiro** | & 4838032Speter** This is more restrictive than strictly necessary. 4938032Speter** 5064565Sgshapiro** To use this, add FEATURE(`smrsh') to your .mc file. 5138032Speter** 5238032Speter** This can be used on any version of sendmail. 5338032Speter** 5438032Speter** In loving memory of RTM. 11/02/93. 5538032Speter*/ 5638032Speter 5738032Speter#include <unistd.h> 5890795Sgshapiro#include <sm/io.h> 5990795Sgshapiro#include <sm/string.h> 6038032Speter#include <sys/file.h> 6138032Speter#include <string.h> 6238032Speter#include <ctype.h> 6364565Sgshapiro#include <errno.h> 6438032Speter#ifdef EX_OK 6538032Speter# undef EX_OK 6664565Sgshapiro#endif /* EX_OK */ 6738032Speter#include <sysexits.h> 6838032Speter#include <syslog.h> 6938032Speter#include <stdlib.h> 7038032Speter 7190795Sgshapiro#include <sm/conf.h> 7290795Sgshapiro#include <sm/errstring.h> 7364565Sgshapiro 7438032Speter/* directory in which all commands must reside */ 7538032Speter#ifndef CMDDIR 7690795Sgshapiro# ifdef SMRSH_CMDDIR 7790795Sgshapiro# define CMDDIR SMRSH_CMDDIR 7890795Sgshapiro# else /* SMRSH_CMDDIR */ 7990795Sgshapiro# define CMDDIR "/usr/adm/sm.bin" 8090795Sgshapiro# endif /* SMRSH_CMDDIR */ 8164565Sgshapiro#endif /* ! CMDDIR */ 8238032Speter 8338032Speter/* characters disallowed in the shell "-c" argument */ 8438032Speter#define SPECIALS "<|>^();&`$\r\n" 8538032Speter 8638032Speter/* default search path */ 8738032Speter#ifndef PATH 8890795Sgshapiro# ifdef SMRSH_PATH 8990795Sgshapiro# define PATH SMRSH_PATH 9090795Sgshapiro# else /* SMRSH_PATH */ 9190795Sgshapiro# define PATH "/bin:/usr/bin:/usr/ucb" 9290795Sgshapiro# endif /* SMRSH_PATH */ 9364565Sgshapiro#endif /* ! PATH */ 9438032Speter 9564565Sgshapirochar newcmdbuf[1000]; 9664565Sgshapirochar *prg, *par; 9764565Sgshapiro 9864565Sgshapiro/* 9964565Sgshapiro** ADDCMD -- add a string to newcmdbuf, check for overflow 10064565Sgshapiro** 10164565Sgshapiro** Parameters: 10264565Sgshapiro** s -- string to add 10364565Sgshapiro** cmd -- it's a command: prepend CMDDIR/ 10464565Sgshapiro** len -- length of string to add 10564565Sgshapiro** 10664565Sgshapiro** Side Effects: 10764565Sgshapiro** changes newcmdbuf or exits with a failure. 10864565Sgshapiro** 10964565Sgshapiro*/ 11064565Sgshapiro 11164565Sgshapirovoid 11264565Sgshapiroaddcmd(s, cmd, len) 11364565Sgshapiro char *s; 11490795Sgshapiro bool cmd; 11590795Sgshapiro size_t len; 11664565Sgshapiro{ 11764565Sgshapiro if (s == NULL || *s == '\0') 11864565Sgshapiro return; 11964565Sgshapiro 12064565Sgshapiro if (sizeof newcmdbuf - strlen(newcmdbuf) <= 12164565Sgshapiro len + (cmd ? (strlen(CMDDIR) + 1) : 0)) 12264565Sgshapiro { 12390795Sgshapiro (void)sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 12490795Sgshapiro "%s: command too long: %s\n", prg, par); 12564565Sgshapiro#ifndef DEBUG 12664565Sgshapiro syslog(LOG_WARNING, "command too long: %.40s", par); 12764565Sgshapiro#endif /* ! DEBUG */ 12864565Sgshapiro exit(EX_UNAVAILABLE); 12964565Sgshapiro } 13064565Sgshapiro if (cmd) 13164565Sgshapiro { 13290795Sgshapiro (void) sm_strlcat(newcmdbuf, CMDDIR, sizeof newcmdbuf); 13390795Sgshapiro (void) sm_strlcat(newcmdbuf, "/", sizeof newcmdbuf); 13464565Sgshapiro } 13590795Sgshapiro (void) sm_strlcat(newcmdbuf, s, sizeof newcmdbuf); 13664565Sgshapiro} 13764565Sgshapiro 13838032Speterint 13938032Spetermain(argc, argv) 14038032Speter int argc; 14138032Speter char **argv; 14238032Speter{ 14338032Speter register char *p; 14438032Speter register char *q; 14564565Sgshapiro register char *r; 14638032Speter register char *cmd; 14764565Sgshapiro int isexec; 14864565Sgshapiro int save_errno; 14938032Speter char *newenv[2]; 15038032Speter char cmdbuf[1000]; 15138032Speter char pathbuf[1000]; 15264565Sgshapiro char specialbuf[32]; 15338032Speter 15464565Sgshapiro#ifndef DEBUG 15564565Sgshapiro# ifndef LOG_MAIL 15638032Speter openlog("smrsh", 0); 15764565Sgshapiro# else /* ! LOG_MAIL */ 15838032Speter openlog("smrsh", LOG_ODELAY|LOG_CONS, LOG_MAIL); 15964565Sgshapiro# endif /* ! LOG_MAIL */ 16064565Sgshapiro#endif /* ! DEBUG */ 16138032Speter 16290795Sgshapiro (void) sm_strlcpy(pathbuf, "PATH=", sizeof pathbuf); 16390795Sgshapiro (void) sm_strlcat(pathbuf, PATH, sizeof pathbuf); 16438032Speter newenv[0] = pathbuf; 16538032Speter newenv[1] = NULL; 16638032Speter 16738032Speter /* 16838032Speter ** Do basic argv usage checking 16938032Speter */ 17038032Speter 17164565Sgshapiro prg = argv[0]; 17264565Sgshapiro 17338032Speter if (argc != 3 || strcmp(argv[1], "-c") != 0) 17438032Speter { 17590795Sgshapiro (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 17690795Sgshapiro "Usage: %s -c command\n", prg); 17764565Sgshapiro#ifndef DEBUG 17838032Speter syslog(LOG_ERR, "usage"); 17964565Sgshapiro#endif /* ! DEBUG */ 18038032Speter exit(EX_USAGE); 18138032Speter } 18238032Speter 18377352Sgshapiro par = argv[2]; 18477352Sgshapiro 18538032Speter /* 18638032Speter ** Disallow special shell syntax. This is overly restrictive, 18738032Speter ** but it should shut down all attacks. 18838032Speter ** Be sure to include 8-bit versions, since many shells strip 18938032Speter ** the address to 7 bits before checking. 19038032Speter */ 19138032Speter 19264565Sgshapiro if (strlen(SPECIALS) * 2 >= sizeof specialbuf) 19338032Speter { 19464565Sgshapiro#ifndef DEBUG 19564565Sgshapiro syslog(LOG_ERR, "too many specials: %.40s", SPECIALS); 19664565Sgshapiro#endif /* ! DEBUG */ 19738032Speter exit(EX_UNAVAILABLE); 19838032Speter } 19990795Sgshapiro (void) sm_strlcpy(specialbuf, SPECIALS, sizeof specialbuf); 20064565Sgshapiro for (p = specialbuf; *p != '\0'; p++) 20164565Sgshapiro *p |= '\200'; 20290795Sgshapiro (void) sm_strlcat(specialbuf, SPECIALS, sizeof specialbuf); 20338032Speter 20438032Speter /* 20538032Speter ** Do a quick sanity check on command line length. 20638032Speter */ 20738032Speter 20890795Sgshapiro if (strlen(par) > (sizeof newcmdbuf - sizeof CMDDIR - 2)) 20938032Speter { 21090795Sgshapiro (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 21190795Sgshapiro "%s: command too long: %s\n", prg, par); 21264565Sgshapiro#ifndef DEBUG 21364565Sgshapiro syslog(LOG_WARNING, "command too long: %.40s", par); 21464565Sgshapiro#endif /* ! DEBUG */ 21538032Speter exit(EX_UNAVAILABLE); 21638032Speter } 21738032Speter 21864565Sgshapiro q = par; 21964565Sgshapiro newcmdbuf[0] = '\0'; 22090795Sgshapiro isexec = false; 22138032Speter 22264565Sgshapiro while (*q) 22338032Speter { 22464565Sgshapiro /* 22564565Sgshapiro ** Strip off a leading pathname on the command name. For 22664565Sgshapiro ** example, change /usr/ucb/vacation to vacation. 22764565Sgshapiro */ 22838032Speter 22964565Sgshapiro /* strip leading spaces */ 23064565Sgshapiro while (*q != '\0' && isascii(*q) && isspace(*q)) 23164565Sgshapiro q++; 23264565Sgshapiro if (*q == '\0') 23338032Speter { 23464565Sgshapiro if (isexec) 23564565Sgshapiro { 23690795Sgshapiro (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 23790795Sgshapiro "%s: missing command to exec\n", 23890795Sgshapiro prg); 23964565Sgshapiro#ifndef DEBUG 24090795Sgshapiro syslog(LOG_CRIT, "uid %d: missing command to exec", (int) getuid()); 24164565Sgshapiro#endif /* ! DEBUG */ 24264565Sgshapiro exit(EX_UNAVAILABLE); 24364565Sgshapiro } 24438032Speter break; 24538032Speter } 24638032Speter 24764565Sgshapiro /* find the end of the command name */ 24864565Sgshapiro p = strpbrk(q, " \t"); 24964565Sgshapiro if (p == NULL) 25064565Sgshapiro cmd = &q[strlen(q)]; 25164565Sgshapiro else 25264565Sgshapiro { 25364565Sgshapiro *p = '\0'; 25464565Sgshapiro cmd = p; 25564565Sgshapiro } 25664565Sgshapiro /* search backwards for last / (allow for 0200 bit) */ 25764565Sgshapiro while (cmd > q) 25864565Sgshapiro { 25964565Sgshapiro if ((*--cmd & 0177) == '/') 26064565Sgshapiro { 26164565Sgshapiro cmd++; 26264565Sgshapiro break; 26364565Sgshapiro } 26464565Sgshapiro } 26564565Sgshapiro /* cmd now points at final component of path name */ 26638032Speter 26764565Sgshapiro /* allow a few shell builtins */ 26864565Sgshapiro if (strcmp(q, "exec") == 0 && p != NULL) 26964565Sgshapiro { 27090795Sgshapiro addcmd("exec ", false, strlen("exec ")); 27164565Sgshapiro /* test _next_ arg */ 27264565Sgshapiro q = ++p; 27390795Sgshapiro isexec = true; 27464565Sgshapiro continue; 27564565Sgshapiro } 27664565Sgshapiro else if (strcmp(q, "exit") == 0 || strcmp(q, "echo") == 0) 27764565Sgshapiro { 27890795Sgshapiro addcmd(cmd, false, strlen(cmd)); 27964565Sgshapiro /* test following chars */ 28064565Sgshapiro } 28164565Sgshapiro else 28264565Sgshapiro { 28364565Sgshapiro /* 28464565Sgshapiro ** Check to see if the command name is legal. 28564565Sgshapiro */ 28690795Sgshapiro (void) sm_strlcpy(cmdbuf, CMDDIR, sizeof cmdbuf); 28790795Sgshapiro (void) sm_strlcat(cmdbuf, "/", sizeof cmdbuf); 28890795Sgshapiro (void) sm_strlcat(cmdbuf, cmd, sizeof cmdbuf); 28964565Sgshapiro#ifdef DEBUG 29090795Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 29190795Sgshapiro "Trying %s\n", cmdbuf); 29264565Sgshapiro#endif /* DEBUG */ 29364565Sgshapiro if (access(cmdbuf, X_OK) < 0) 29464565Sgshapiro { 29564565Sgshapiro /* oops.... crack attack possiblity */ 29690795Sgshapiro (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 29790795Sgshapiro "%s: %s not available for sendmail programs\n", 29890795Sgshapiro prg, cmd); 29964565Sgshapiro if (p != NULL) 30064565Sgshapiro *p = ' '; 30164565Sgshapiro#ifndef DEBUG 30264565Sgshapiro syslog(LOG_CRIT, "uid %d: attempt to use %s", 30390795Sgshapiro (int) getuid(), cmd); 30464565Sgshapiro#endif /* ! DEBUG */ 30564565Sgshapiro exit(EX_UNAVAILABLE); 30664565Sgshapiro } 30738032Speter 30864565Sgshapiro /* 30964565Sgshapiro ** Create the actual shell input. 31064565Sgshapiro */ 31164565Sgshapiro 31290795Sgshapiro addcmd(cmd, true, strlen(cmd)); 31364565Sgshapiro } 31490795Sgshapiro isexec = false; 31564565Sgshapiro 31638032Speter if (p != NULL) 31738032Speter *p = ' '; 31864565Sgshapiro else 31964565Sgshapiro break; 32064565Sgshapiro 32164565Sgshapiro r = strpbrk(p, specialbuf); 32290795Sgshapiro if (r == NULL) 32390795Sgshapiro { 32490795Sgshapiro addcmd(p, false, strlen(p)); 32564565Sgshapiro break; 32664565Sgshapiro } 32764565Sgshapiro#if ALLOWSEMI 32890795Sgshapiro if (*r == ';') 32990795Sgshapiro { 33090795Sgshapiro addcmd(p, false, r - p + 1); 33164565Sgshapiro q = r + 1; 33264565Sgshapiro continue; 33364565Sgshapiro } 33464565Sgshapiro#endif /* ALLOWSEMI */ 33564565Sgshapiro if ((*r == '&' && *(r + 1) == '&') || 33664565Sgshapiro (*r == '|' && *(r + 1) == '|')) 33764565Sgshapiro { 33890795Sgshapiro addcmd(p, false, r - p + 2); 33964565Sgshapiro q = r + 2; 34064565Sgshapiro continue; 34164565Sgshapiro } 34264565Sgshapiro 34390795Sgshapiro (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 34490795Sgshapiro "%s: cannot use %c in command\n", prg, *r); 34564565Sgshapiro#ifndef DEBUG 34664565Sgshapiro syslog(LOG_CRIT, "uid %d: attempt to use %c in command: %s", 34790795Sgshapiro (int) getuid(), *r, par); 34864565Sgshapiro#endif /* ! DEBUG */ 34938032Speter exit(EX_UNAVAILABLE); 35064565Sgshapiro } /* end of while *q */ 35164565Sgshapiro if (isexec) 35264565Sgshapiro { 35390795Sgshapiro (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 35490795Sgshapiro "%s: missing command to exec\n", prg); 35564565Sgshapiro#ifndef DEBUG 35690795Sgshapiro syslog(LOG_CRIT, "uid %d: missing command to exec", 35790795Sgshapiro (int) getuid()); 35864565Sgshapiro#endif /* ! DEBUG */ 35964565Sgshapiro exit(EX_UNAVAILABLE); 36038032Speter } 36164565Sgshapiro /* make sure we created something */ 36264565Sgshapiro if (newcmdbuf[0] == '\0') 36364565Sgshapiro { 36490795Sgshapiro (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 36590795Sgshapiro "Usage: %s -c command\n", prg); 36664565Sgshapiro#ifndef DEBUG 36764565Sgshapiro syslog(LOG_ERR, "usage"); 36864565Sgshapiro#endif /* ! DEBUG */ 36964565Sgshapiro exit(EX_USAGE); 37064565Sgshapiro } 37138032Speter 37238032Speter /* 37338032Speter ** Now invoke the shell 37438032Speter */ 37538032Speter 37638032Speter#ifdef DEBUG 37790795Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s\n", newcmdbuf); 37864565Sgshapiro#endif /* DEBUG */ 37964565Sgshapiro (void) execle("/bin/sh", "/bin/sh", "-c", newcmdbuf, NULL, newenv); 38064565Sgshapiro save_errno = errno; 38164565Sgshapiro#ifndef DEBUG 38290795Sgshapiro syslog(LOG_CRIT, "Cannot exec /bin/sh: %s", sm_errstring(errno)); 38364565Sgshapiro#endif /* ! DEBUG */ 38464565Sgshapiro errno = save_errno; 38590795Sgshapiro sm_perror("/bin/sh"); 38638032Speter exit(EX_OSFILE); 38764565Sgshapiro /* NOTREACHED */ 38864565Sgshapiro return EX_OSFILE; 38938032Speter} 390