smrsh.c revision 98125
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 * 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 2398125SgshapiroSM_IDSTR(id, "@(#)$Id: smrsh.c,v 8.58 2002/05/25 02:41:31 ca Exp $") 2464565Sgshapiro 2564565Sgshapiro/* $FreeBSD: head/contrib/sendmail/smrsh/smrsh.c 98125 2002-06-11 21:16:51Z 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> 5998125Sgshapiro#include <sm/limits.h> 6090795Sgshapiro#include <sm/string.h> 6138032Speter#include <sys/file.h> 6238032Speter#include <string.h> 6338032Speter#include <ctype.h> 6464565Sgshapiro#include <errno.h> 6538032Speter#ifdef EX_OK 6638032Speter# undef EX_OK 6764565Sgshapiro#endif /* EX_OK */ 6838032Speter#include <sysexits.h> 6938032Speter#include <syslog.h> 7038032Speter#include <stdlib.h> 7138032Speter 7290795Sgshapiro#include <sm/conf.h> 7390795Sgshapiro#include <sm/errstring.h> 7464565Sgshapiro 7538032Speter/* directory in which all commands must reside */ 7638032Speter#ifndef CMDDIR 7790795Sgshapiro# ifdef SMRSH_CMDDIR 7890795Sgshapiro# define CMDDIR SMRSH_CMDDIR 7990795Sgshapiro# else /* SMRSH_CMDDIR */ 8090795Sgshapiro# define CMDDIR "/usr/adm/sm.bin" 8190795Sgshapiro# endif /* SMRSH_CMDDIR */ 8264565Sgshapiro#endif /* ! CMDDIR */ 8338032Speter 8438032Speter/* characters disallowed in the shell "-c" argument */ 8538032Speter#define SPECIALS "<|>^();&`$\r\n" 8638032Speter 8738032Speter/* default search path */ 8838032Speter#ifndef PATH 8990795Sgshapiro# ifdef SMRSH_PATH 9090795Sgshapiro# define PATH SMRSH_PATH 9190795Sgshapiro# else /* SMRSH_PATH */ 9290795Sgshapiro# define PATH "/bin:/usr/bin:/usr/ucb" 9390795Sgshapiro# endif /* SMRSH_PATH */ 9464565Sgshapiro#endif /* ! PATH */ 9538032Speter 9664565Sgshapirochar newcmdbuf[1000]; 9764565Sgshapirochar *prg, *par; 9864565Sgshapiro 9964565Sgshapiro/* 10064565Sgshapiro** ADDCMD -- add a string to newcmdbuf, check for overflow 10164565Sgshapiro** 10264565Sgshapiro** Parameters: 10364565Sgshapiro** s -- string to add 10464565Sgshapiro** cmd -- it's a command: prepend CMDDIR/ 10564565Sgshapiro** len -- length of string to add 10664565Sgshapiro** 10764565Sgshapiro** Side Effects: 10864565Sgshapiro** changes newcmdbuf or exits with a failure. 10964565Sgshapiro** 11064565Sgshapiro*/ 11164565Sgshapiro 11264565Sgshapirovoid 11364565Sgshapiroaddcmd(s, cmd, len) 11464565Sgshapiro char *s; 11590795Sgshapiro bool cmd; 11690795Sgshapiro size_t len; 11764565Sgshapiro{ 11864565Sgshapiro if (s == NULL || *s == '\0') 11964565Sgshapiro return; 12064565Sgshapiro 12164565Sgshapiro if (sizeof newcmdbuf - strlen(newcmdbuf) <= 12264565Sgshapiro len + (cmd ? (strlen(CMDDIR) + 1) : 0)) 12364565Sgshapiro { 12490795Sgshapiro (void)sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 12590795Sgshapiro "%s: command too long: %s\n", prg, par); 12664565Sgshapiro#ifndef DEBUG 12764565Sgshapiro syslog(LOG_WARNING, "command too long: %.40s", par); 12864565Sgshapiro#endif /* ! DEBUG */ 12964565Sgshapiro exit(EX_UNAVAILABLE); 13064565Sgshapiro } 13164565Sgshapiro if (cmd) 13298125Sgshapiro (void) sm_strlcat2(newcmdbuf, CMDDIR, "/", sizeof newcmdbuf); 13390795Sgshapiro (void) sm_strlcat(newcmdbuf, s, sizeof newcmdbuf); 13464565Sgshapiro} 13564565Sgshapiro 13638032Speterint 13738032Spetermain(argc, argv) 13838032Speter int argc; 13938032Speter char **argv; 14038032Speter{ 14138032Speter register char *p; 14238032Speter register char *q; 14364565Sgshapiro register char *r; 14438032Speter register char *cmd; 14564565Sgshapiro int isexec; 14664565Sgshapiro int save_errno; 14738032Speter char *newenv[2]; 14838032Speter char pathbuf[1000]; 14964565Sgshapiro char specialbuf[32]; 15038032Speter 15164565Sgshapiro#ifndef DEBUG 15264565Sgshapiro# ifndef LOG_MAIL 15338032Speter openlog("smrsh", 0); 15464565Sgshapiro# else /* ! LOG_MAIL */ 15538032Speter openlog("smrsh", LOG_ODELAY|LOG_CONS, LOG_MAIL); 15664565Sgshapiro# endif /* ! LOG_MAIL */ 15764565Sgshapiro#endif /* ! DEBUG */ 15838032Speter 15998125Sgshapiro (void) sm_strlcpyn(pathbuf, sizeof pathbuf, 2, "PATH=", PATH); 16038032Speter newenv[0] = pathbuf; 16138032Speter newenv[1] = NULL; 16238032Speter 16338032Speter /* 16438032Speter ** Do basic argv usage checking 16538032Speter */ 16638032Speter 16764565Sgshapiro prg = argv[0]; 16864565Sgshapiro 16938032Speter if (argc != 3 || strcmp(argv[1], "-c") != 0) 17038032Speter { 17190795Sgshapiro (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 17290795Sgshapiro "Usage: %s -c command\n", prg); 17364565Sgshapiro#ifndef DEBUG 17438032Speter syslog(LOG_ERR, "usage"); 17564565Sgshapiro#endif /* ! DEBUG */ 17638032Speter exit(EX_USAGE); 17738032Speter } 17838032Speter 17977352Sgshapiro par = argv[2]; 18077352Sgshapiro 18138032Speter /* 18238032Speter ** Disallow special shell syntax. This is overly restrictive, 18338032Speter ** but it should shut down all attacks. 18438032Speter ** Be sure to include 8-bit versions, since many shells strip 18538032Speter ** the address to 7 bits before checking. 18638032Speter */ 18738032Speter 18864565Sgshapiro if (strlen(SPECIALS) * 2 >= sizeof specialbuf) 18938032Speter { 19064565Sgshapiro#ifndef DEBUG 19164565Sgshapiro syslog(LOG_ERR, "too many specials: %.40s", SPECIALS); 19264565Sgshapiro#endif /* ! DEBUG */ 19338032Speter exit(EX_UNAVAILABLE); 19438032Speter } 19590795Sgshapiro (void) sm_strlcpy(specialbuf, SPECIALS, sizeof specialbuf); 19664565Sgshapiro for (p = specialbuf; *p != '\0'; p++) 19764565Sgshapiro *p |= '\200'; 19890795Sgshapiro (void) sm_strlcat(specialbuf, SPECIALS, sizeof specialbuf); 19938032Speter 20038032Speter /* 20138032Speter ** Do a quick sanity check on command line length. 20238032Speter */ 20338032Speter 20490795Sgshapiro if (strlen(par) > (sizeof newcmdbuf - sizeof CMDDIR - 2)) 20538032Speter { 20690795Sgshapiro (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 20790795Sgshapiro "%s: command too long: %s\n", prg, par); 20864565Sgshapiro#ifndef DEBUG 20964565Sgshapiro syslog(LOG_WARNING, "command too long: %.40s", par); 21064565Sgshapiro#endif /* ! DEBUG */ 21138032Speter exit(EX_UNAVAILABLE); 21238032Speter } 21338032Speter 21464565Sgshapiro q = par; 21564565Sgshapiro newcmdbuf[0] = '\0'; 21690795Sgshapiro isexec = false; 21738032Speter 21898125Sgshapiro while (*q != '\0') 21938032Speter { 22064565Sgshapiro /* 22164565Sgshapiro ** Strip off a leading pathname on the command name. For 22264565Sgshapiro ** example, change /usr/ucb/vacation to vacation. 22364565Sgshapiro */ 22438032Speter 22564565Sgshapiro /* strip leading spaces */ 22664565Sgshapiro while (*q != '\0' && isascii(*q) && isspace(*q)) 22764565Sgshapiro q++; 22864565Sgshapiro if (*q == '\0') 22938032Speter { 23064565Sgshapiro if (isexec) 23164565Sgshapiro { 23290795Sgshapiro (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 23390795Sgshapiro "%s: missing command to exec\n", 23490795Sgshapiro prg); 23564565Sgshapiro#ifndef DEBUG 23690795Sgshapiro syslog(LOG_CRIT, "uid %d: missing command to exec", (int) getuid()); 23764565Sgshapiro#endif /* ! DEBUG */ 23864565Sgshapiro exit(EX_UNAVAILABLE); 23964565Sgshapiro } 24038032Speter break; 24138032Speter } 24238032Speter 24364565Sgshapiro /* find the end of the command name */ 24464565Sgshapiro p = strpbrk(q, " \t"); 24564565Sgshapiro if (p == NULL) 24664565Sgshapiro cmd = &q[strlen(q)]; 24764565Sgshapiro else 24864565Sgshapiro { 24964565Sgshapiro *p = '\0'; 25064565Sgshapiro cmd = p; 25164565Sgshapiro } 25264565Sgshapiro /* search backwards for last / (allow for 0200 bit) */ 25364565Sgshapiro while (cmd > q) 25464565Sgshapiro { 25564565Sgshapiro if ((*--cmd & 0177) == '/') 25664565Sgshapiro { 25764565Sgshapiro cmd++; 25864565Sgshapiro break; 25964565Sgshapiro } 26064565Sgshapiro } 26164565Sgshapiro /* cmd now points at final component of path name */ 26238032Speter 26364565Sgshapiro /* allow a few shell builtins */ 26464565Sgshapiro if (strcmp(q, "exec") == 0 && p != NULL) 26564565Sgshapiro { 26690795Sgshapiro addcmd("exec ", false, strlen("exec ")); 26798125Sgshapiro 26864565Sgshapiro /* test _next_ arg */ 26964565Sgshapiro q = ++p; 27090795Sgshapiro isexec = true; 27164565Sgshapiro continue; 27264565Sgshapiro } 27364565Sgshapiro else if (strcmp(q, "exit") == 0 || strcmp(q, "echo") == 0) 27464565Sgshapiro { 27590795Sgshapiro addcmd(cmd, false, strlen(cmd)); 27698125Sgshapiro 27764565Sgshapiro /* test following chars */ 27864565Sgshapiro } 27964565Sgshapiro else 28064565Sgshapiro { 28198125Sgshapiro char cmdbuf[MAXPATHLEN]; 28298125Sgshapiro 28364565Sgshapiro /* 28464565Sgshapiro ** Check to see if the command name is legal. 28564565Sgshapiro */ 28698125Sgshapiro 28798125Sgshapiro if (sm_strlcpyn(cmdbuf, sizeof cmdbuf, 3, CMDDIR, 28898125Sgshapiro "/", cmd) >= sizeof cmdbuf) 28998125Sgshapiro { 29098125Sgshapiro /* too long */ 29198125Sgshapiro (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 29298125Sgshapiro "%s: %s not available for sendmail programs (filename too long)\n", 29398125Sgshapiro prg, cmd); 29498125Sgshapiro if (p != NULL) 29598125Sgshapiro *p = ' '; 29698125Sgshapiro#ifndef DEBUG 29798125Sgshapiro syslog(LOG_CRIT, "uid %d: attempt to use %s (filename too long)", 29898125Sgshapiro (int) getuid(), cmd); 29998125Sgshapiro#endif /* ! DEBUG */ 30098125Sgshapiro exit(EX_UNAVAILABLE); 30198125Sgshapiro } 30298125Sgshapiro 30364565Sgshapiro#ifdef DEBUG 30490795Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 30590795Sgshapiro "Trying %s\n", cmdbuf); 30664565Sgshapiro#endif /* DEBUG */ 30764565Sgshapiro if (access(cmdbuf, X_OK) < 0) 30864565Sgshapiro { 30964565Sgshapiro /* oops.... crack attack possiblity */ 31090795Sgshapiro (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 31190795Sgshapiro "%s: %s not available for sendmail programs\n", 31290795Sgshapiro prg, cmd); 31364565Sgshapiro if (p != NULL) 31464565Sgshapiro *p = ' '; 31564565Sgshapiro#ifndef DEBUG 31664565Sgshapiro syslog(LOG_CRIT, "uid %d: attempt to use %s", 31790795Sgshapiro (int) getuid(), cmd); 31864565Sgshapiro#endif /* ! DEBUG */ 31964565Sgshapiro exit(EX_UNAVAILABLE); 32064565Sgshapiro } 32138032Speter 32264565Sgshapiro /* 32364565Sgshapiro ** Create the actual shell input. 32464565Sgshapiro */ 32564565Sgshapiro 32690795Sgshapiro addcmd(cmd, true, strlen(cmd)); 32764565Sgshapiro } 32890795Sgshapiro isexec = false; 32964565Sgshapiro 33038032Speter if (p != NULL) 33138032Speter *p = ' '; 33264565Sgshapiro else 33364565Sgshapiro break; 33464565Sgshapiro 33564565Sgshapiro r = strpbrk(p, specialbuf); 33690795Sgshapiro if (r == NULL) 33790795Sgshapiro { 33890795Sgshapiro addcmd(p, false, strlen(p)); 33964565Sgshapiro break; 34064565Sgshapiro } 34164565Sgshapiro#if ALLOWSEMI 34290795Sgshapiro if (*r == ';') 34390795Sgshapiro { 34490795Sgshapiro addcmd(p, false, r - p + 1); 34564565Sgshapiro q = r + 1; 34664565Sgshapiro continue; 34764565Sgshapiro } 34864565Sgshapiro#endif /* ALLOWSEMI */ 34964565Sgshapiro if ((*r == '&' && *(r + 1) == '&') || 35064565Sgshapiro (*r == '|' && *(r + 1) == '|')) 35164565Sgshapiro { 35290795Sgshapiro addcmd(p, false, r - p + 2); 35364565Sgshapiro q = r + 2; 35464565Sgshapiro continue; 35564565Sgshapiro } 35664565Sgshapiro 35790795Sgshapiro (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 35890795Sgshapiro "%s: cannot use %c in command\n", prg, *r); 35964565Sgshapiro#ifndef DEBUG 36064565Sgshapiro syslog(LOG_CRIT, "uid %d: attempt to use %c in command: %s", 36190795Sgshapiro (int) getuid(), *r, par); 36264565Sgshapiro#endif /* ! DEBUG */ 36338032Speter exit(EX_UNAVAILABLE); 36498125Sgshapiro } 36564565Sgshapiro if (isexec) 36664565Sgshapiro { 36790795Sgshapiro (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 36890795Sgshapiro "%s: missing command to exec\n", prg); 36964565Sgshapiro#ifndef DEBUG 37090795Sgshapiro syslog(LOG_CRIT, "uid %d: missing command to exec", 37190795Sgshapiro (int) getuid()); 37264565Sgshapiro#endif /* ! DEBUG */ 37364565Sgshapiro exit(EX_UNAVAILABLE); 37438032Speter } 37564565Sgshapiro /* make sure we created something */ 37664565Sgshapiro if (newcmdbuf[0] == '\0') 37764565Sgshapiro { 37890795Sgshapiro (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 37990795Sgshapiro "Usage: %s -c command\n", prg); 38064565Sgshapiro#ifndef DEBUG 38164565Sgshapiro syslog(LOG_ERR, "usage"); 38264565Sgshapiro#endif /* ! DEBUG */ 38364565Sgshapiro exit(EX_USAGE); 38464565Sgshapiro } 38538032Speter 38638032Speter /* 38738032Speter ** Now invoke the shell 38838032Speter */ 38938032Speter 39038032Speter#ifdef DEBUG 39190795Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s\n", newcmdbuf); 39264565Sgshapiro#endif /* DEBUG */ 39364565Sgshapiro (void) execle("/bin/sh", "/bin/sh", "-c", newcmdbuf, NULL, newenv); 39464565Sgshapiro save_errno = errno; 39564565Sgshapiro#ifndef DEBUG 39690795Sgshapiro syslog(LOG_CRIT, "Cannot exec /bin/sh: %s", sm_errstring(errno)); 39764565Sgshapiro#endif /* ! DEBUG */ 39864565Sgshapiro errno = save_errno; 39990795Sgshapiro sm_perror("/bin/sh"); 40038032Speter exit(EX_OSFILE); 40164565Sgshapiro /* NOTREACHED */ 40264565Sgshapiro return EX_OSFILE; 40338032Speter} 404