smrsh.c revision 64565
138032Speter/* 264565Sgshapiro * Copyright (c) 1998-2000 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 1438032Speter#ifndef lint 1564565Sgshapirostatic char copyright[] = 1664565Sgshapiro"@(#) Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers.\n\ 1764565Sgshapiro All rights reserved.\n\ 1864565Sgshapiro Copyright (c) 1993 Eric P. Allman. All rights reserved.\n\ 1964565Sgshapiro Copyright (c) 1993\n\ 2064565Sgshapiro The Regents of the University of California. All rights reserved.\n"; 2164565Sgshapiro#endif /* ! lint */ 2238032Speter 2364565Sgshapiro#ifndef lint 2464565Sgshapirostatic char id[] = "@(#)$Id: smrsh.c,v 8.31.4.4 2000/05/25 21:44:29 gshapiro Exp $"; 2564565Sgshapiro#endif /* ! lint */ 2664565Sgshapiro 2764565Sgshapiro/* $FreeBSD: head/contrib/sendmail/smrsh/smrsh.c 64565 2000-08-12 22:19:16Z gshapiro $ */ 2864565Sgshapiro 2938032Speter/* 3038032Speter** SMRSH -- sendmail restricted shell 3138032Speter** 3238032Speter** This is a patch to get around the prog mailer bugs in most 3338032Speter** versions of sendmail. 3438032Speter** 3538032Speter** Use this in place of /bin/sh in the "prog" mailer definition 3638032Speter** in your sendmail.cf file. You then create CMDDIR (owned by 3738032Speter** root, mode 755) and put links to any programs you want 3838032Speter** available to prog mailers in that directory. This should 3938032Speter** include things like "vacation" and "procmail", but not "sed" 4038032Speter** or "sh". 4138032Speter** 4238032Speter** Leading pathnames are stripped from program names so that 4338032Speter** existing .forward files that reference things like 4438081Speter** "/usr/bin/vacation" will continue to work. 4538032Speter** 4638032Speter** The following characters are completely illegal: 4764565Sgshapiro** < > ^ & ` ( ) \n \r 4864565Sgshapiro** The following characters are sometimes illegal: 4964565Sgshapiro** | & 5038032Speter** This is more restrictive than strictly necessary. 5138032Speter** 5264565Sgshapiro** To use this, add FEATURE(`smrsh') to your .mc file. 5338032Speter** 5438032Speter** This can be used on any version of sendmail. 5538032Speter** 5638032Speter** In loving memory of RTM. 11/02/93. 5738032Speter*/ 5838032Speter 5938032Speter#include <unistd.h> 6038032Speter#include <stdio.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 7264565Sgshapiro#ifndef TRUE 7364565Sgshapiro# define TRUE 1 7464565Sgshapiro# define FALSE 0 7564565Sgshapiro#endif /* ! TRUE */ 7664565Sgshapiro 7738032Speter/* directory in which all commands must reside */ 7838032Speter#ifndef CMDDIR 7938081Speter# define CMDDIR "/usr/libexec/sm.bin" 8064565Sgshapiro#endif /* ! CMDDIR */ 8138032Speter 8238032Speter/* characters disallowed in the shell "-c" argument */ 8338032Speter#define SPECIALS "<|>^();&`$\r\n" 8438032Speter 8538032Speter/* default search path */ 8638032Speter#ifndef PATH 8738081Speter# define PATH "/bin:/usr/bin" 8864565Sgshapiro#endif /* ! PATH */ 8938032Speter 9064565Sgshapiro#ifndef __P 9164565Sgshapiro# include "sendmail/cdefs.h" 9264565Sgshapiro#endif /* ! __P */ 9364565Sgshapiro 9464565Sgshapiroextern size_t strlcpy __P((char *, const char *, size_t)); 9564565Sgshapiroextern size_t strlcat __P((char *, const char *, size_t)); 9664565Sgshapiro 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; 11664565Sgshapiro int cmd; 11764565Sgshapiro int 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 { 12564565Sgshapiro fprintf(stderr, "%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) 13264565Sgshapiro { 13364565Sgshapiro (void) strlcat(newcmdbuf, CMDDIR, sizeof newcmdbuf); 13464565Sgshapiro (void) strlcat(newcmdbuf, "/", sizeof newcmdbuf); 13564565Sgshapiro } 13664565Sgshapiro (void) strlcat(newcmdbuf, s, sizeof newcmdbuf); 13764565Sgshapiro} 13864565Sgshapiro 13938032Speterint 14038032Spetermain(argc, argv) 14138032Speter int argc; 14238032Speter char **argv; 14338032Speter{ 14438032Speter register char *p; 14538032Speter register char *q; 14664565Sgshapiro register char *r; 14738032Speter register char *cmd; 14838032Speter int i; 14964565Sgshapiro int isexec; 15064565Sgshapiro int save_errno; 15138032Speter char *newenv[2]; 15238032Speter char cmdbuf[1000]; 15338032Speter char pathbuf[1000]; 15464565Sgshapiro char specialbuf[32]; 15538032Speter 15664565Sgshapiro#ifndef DEBUG 15764565Sgshapiro# ifndef LOG_MAIL 15838032Speter openlog("smrsh", 0); 15964565Sgshapiro# else /* ! LOG_MAIL */ 16038032Speter openlog("smrsh", LOG_ODELAY|LOG_CONS, LOG_MAIL); 16164565Sgshapiro# endif /* ! LOG_MAIL */ 16264565Sgshapiro#endif /* ! DEBUG */ 16338032Speter 16464565Sgshapiro (void) strlcpy(pathbuf, "PATH=", sizeof pathbuf); 16564565Sgshapiro (void) strlcat(pathbuf, PATH, sizeof pathbuf); 16638032Speter newenv[0] = pathbuf; 16738032Speter newenv[1] = NULL; 16838032Speter 16938032Speter /* 17038032Speter ** Do basic argv usage checking 17138032Speter */ 17238032Speter 17364565Sgshapiro prg = argv[0]; 17464565Sgshapiro par = argv[2]; 17564565Sgshapiro 17638032Speter if (argc != 3 || strcmp(argv[1], "-c") != 0) 17738032Speter { 17864565Sgshapiro fprintf(stderr, "Usage: %s -c command\n", prg); 17964565Sgshapiro#ifndef DEBUG 18038032Speter syslog(LOG_ERR, "usage"); 18164565Sgshapiro#endif /* ! DEBUG */ 18238032Speter exit(EX_USAGE); 18338032Speter } 18438032Speter 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 } 19964565Sgshapiro (void) strlcpy(specialbuf, SPECIALS, sizeof specialbuf); 20064565Sgshapiro for (p = specialbuf; *p != '\0'; p++) 20164565Sgshapiro *p |= '\200'; 20264565Sgshapiro (void) strlcat(specialbuf, SPECIALS, sizeof specialbuf); 20338032Speter 20438032Speter /* 20538032Speter ** Do a quick sanity check on command line length. 20638032Speter */ 20738032Speter 20864565Sgshapiro i = strlen(par); 20964565Sgshapiro if (i > (sizeof newcmdbuf - sizeof CMDDIR - 2)) 21038032Speter { 21164565Sgshapiro fprintf(stderr, "%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'; 22064565Sgshapiro 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 { 23664565Sgshapiro fprintf(stderr, "%s: missing command to exec\n", 23764565Sgshapiro prg); 23864565Sgshapiro#ifndef DEBUG 23964565Sgshapiro syslog(LOG_CRIT, "uid %d: missing command to exec", getuid()); 24064565Sgshapiro#endif /* ! DEBUG */ 24164565Sgshapiro exit(EX_UNAVAILABLE); 24264565Sgshapiro } 24338032Speter break; 24438032Speter } 24538032Speter 24664565Sgshapiro /* find the end of the command name */ 24764565Sgshapiro p = strpbrk(q, " \t"); 24864565Sgshapiro if (p == NULL) 24964565Sgshapiro cmd = &q[strlen(q)]; 25064565Sgshapiro else 25164565Sgshapiro { 25264565Sgshapiro *p = '\0'; 25364565Sgshapiro cmd = p; 25464565Sgshapiro } 25564565Sgshapiro /* search backwards for last / (allow for 0200 bit) */ 25664565Sgshapiro while (cmd > q) 25764565Sgshapiro { 25864565Sgshapiro if ((*--cmd & 0177) == '/') 25964565Sgshapiro { 26064565Sgshapiro cmd++; 26164565Sgshapiro break; 26264565Sgshapiro } 26364565Sgshapiro } 26464565Sgshapiro /* cmd now points at final component of path name */ 26538032Speter 26664565Sgshapiro /* allow a few shell builtins */ 26764565Sgshapiro if (strcmp(q, "exec") == 0 && p != NULL) 26864565Sgshapiro { 26964565Sgshapiro addcmd("exec ", FALSE, strlen("exec ")); 27064565Sgshapiro /* test _next_ arg */ 27164565Sgshapiro q = ++p; 27264565Sgshapiro isexec = TRUE; 27364565Sgshapiro continue; 27464565Sgshapiro } 27564565Sgshapiro else if (strcmp(q, "exit") == 0 || strcmp(q, "echo") == 0) 27664565Sgshapiro { 27764565Sgshapiro addcmd(cmd, FALSE, strlen(cmd)); 27864565Sgshapiro /* test following chars */ 27964565Sgshapiro } 28064565Sgshapiro else 28164565Sgshapiro { 28264565Sgshapiro /* 28364565Sgshapiro ** Check to see if the command name is legal. 28464565Sgshapiro */ 28564565Sgshapiro (void) strlcpy(cmdbuf, CMDDIR, sizeof cmdbuf); 28664565Sgshapiro (void) strlcat(cmdbuf, "/", sizeof cmdbuf); 28764565Sgshapiro (void) strlcat(cmdbuf, cmd, sizeof cmdbuf); 28864565Sgshapiro#ifdef DEBUG 28964565Sgshapiro printf("Trying %s\n", cmdbuf); 29064565Sgshapiro#endif /* DEBUG */ 29164565Sgshapiro if (access(cmdbuf, X_OK) < 0) 29264565Sgshapiro { 29364565Sgshapiro /* oops.... crack attack possiblity */ 29464565Sgshapiro fprintf(stderr, 29564565Sgshapiro "%s: %s not available for sendmail programs\n", 29664565Sgshapiro prg, cmd); 29764565Sgshapiro if (p != NULL) 29864565Sgshapiro *p = ' '; 29964565Sgshapiro#ifndef DEBUG 30064565Sgshapiro syslog(LOG_CRIT, "uid %d: attempt to use %s", 30164565Sgshapiro getuid(), cmd); 30264565Sgshapiro#endif /* ! DEBUG */ 30364565Sgshapiro exit(EX_UNAVAILABLE); 30464565Sgshapiro } 30538032Speter 30664565Sgshapiro /* 30764565Sgshapiro ** Create the actual shell input. 30864565Sgshapiro */ 30964565Sgshapiro 31064565Sgshapiro addcmd(cmd, TRUE, strlen(cmd)); 31164565Sgshapiro } 31264565Sgshapiro isexec = FALSE; 31364565Sgshapiro 31438032Speter if (p != NULL) 31538032Speter *p = ' '; 31664565Sgshapiro else 31764565Sgshapiro break; 31864565Sgshapiro 31964565Sgshapiro r = strpbrk(p, specialbuf); 32064565Sgshapiro if (r == NULL) { 32164565Sgshapiro addcmd(p, FALSE, strlen(p)); 32264565Sgshapiro break; 32364565Sgshapiro } 32464565Sgshapiro#if ALLOWSEMI 32564565Sgshapiro if (*r == ';') { 32664565Sgshapiro addcmd(p, FALSE, r - p + 1); 32764565Sgshapiro q = r + 1; 32864565Sgshapiro continue; 32964565Sgshapiro } 33064565Sgshapiro#endif /* ALLOWSEMI */ 33164565Sgshapiro if ((*r == '&' && *(r + 1) == '&') || 33264565Sgshapiro (*r == '|' && *(r + 1) == '|')) 33364565Sgshapiro { 33464565Sgshapiro addcmd(p, FALSE, r - p + 2); 33564565Sgshapiro q = r + 2; 33664565Sgshapiro continue; 33764565Sgshapiro } 33864565Sgshapiro 33964565Sgshapiro fprintf(stderr, "%s: cannot use %c in command\n", prg, *r); 34064565Sgshapiro#ifndef DEBUG 34164565Sgshapiro syslog(LOG_CRIT, "uid %d: attempt to use %c in command: %s", 34264565Sgshapiro getuid(), *r, par); 34364565Sgshapiro#endif /* ! DEBUG */ 34438032Speter exit(EX_UNAVAILABLE); 34564565Sgshapiro } /* end of while *q */ 34664565Sgshapiro if (isexec) 34764565Sgshapiro { 34864565Sgshapiro fprintf(stderr, "%s: missing command to exec\n", prg); 34964565Sgshapiro#ifndef DEBUG 35064565Sgshapiro syslog(LOG_CRIT, "uid %d: missing command to exec", getuid()); 35164565Sgshapiro#endif /* ! DEBUG */ 35264565Sgshapiro exit(EX_UNAVAILABLE); 35338032Speter } 35464565Sgshapiro /* make sure we created something */ 35564565Sgshapiro if (newcmdbuf[0] == '\0') 35664565Sgshapiro { 35764565Sgshapiro fprintf(stderr, "Usage: %s -c command\n", prg); 35864565Sgshapiro#ifndef DEBUG 35964565Sgshapiro syslog(LOG_ERR, "usage"); 36064565Sgshapiro#endif /* ! DEBUG */ 36164565Sgshapiro exit(EX_USAGE); 36264565Sgshapiro } 36338032Speter 36438032Speter /* 36538032Speter ** Now invoke the shell 36638032Speter */ 36738032Speter 36838032Speter#ifdef DEBUG 36964565Sgshapiro printf("%s\n", newcmdbuf); 37064565Sgshapiro#endif /* DEBUG */ 37164565Sgshapiro (void) execle("/bin/sh", "/bin/sh", "-c", newcmdbuf, NULL, newenv); 37264565Sgshapiro save_errno = errno; 37364565Sgshapiro#ifndef DEBUG 37438032Speter syslog(LOG_CRIT, "Cannot exec /bin/sh: %m"); 37564565Sgshapiro#endif /* ! DEBUG */ 37664565Sgshapiro errno = save_errno; 37738032Speter perror("/bin/sh"); 37838032Speter exit(EX_OSFILE); 37964565Sgshapiro /* NOTREACHED */ 38064565Sgshapiro return EX_OSFILE; 38138032Speter} 382