smrsh.c revision 77352
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 1438032Speter#ifndef lint 1564565Sgshapirostatic char copyright[] = 1673191Sgshapiro"@(#) Copyright (c) 1998-2001 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 2477352Sgshapirostatic char id[] = "@(#)$Id: smrsh.c,v 8.31.4.9 2001/04/24 04:11:51 ca Exp $"; 2564565Sgshapiro#endif /* ! lint */ 2664565Sgshapiro 2764565Sgshapiro/* $FreeBSD: head/contrib/sendmail/smrsh/smrsh.c 77352 2001-05-28 17:10:35Z 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 7971348Sgshapiro# if defined(HPUX10) || defined(HPUX11) || SOLARIS >= 20800 8071348Sgshapiro# define CMDDIR "/var/adm/sm.bin" 8173191Sgshapiro# else /* HPUX10 || HPUX11 || SOLARIS >= 20800 */ 8271348Sgshapiro# define CMDDIR "/usr/libexec/sm.bin" 8373191Sgshapiro# endif /* HPUX10 || HPUX11 || SOLARIS >= 20800 */ 8464565Sgshapiro#endif /* ! CMDDIR */ 8538032Speter 8638032Speter/* characters disallowed in the shell "-c" argument */ 8738032Speter#define SPECIALS "<|>^();&`$\r\n" 8838032Speter 8938032Speter/* default search path */ 9038032Speter#ifndef PATH 9138081Speter# define PATH "/bin:/usr/bin" 9264565Sgshapiro#endif /* ! PATH */ 9338032Speter 9464565Sgshapiro#ifndef __P 9564565Sgshapiro# include "sendmail/cdefs.h" 9664565Sgshapiro#endif /* ! __P */ 9764565Sgshapiro 9864565Sgshapiroextern size_t strlcpy __P((char *, const char *, size_t)); 9964565Sgshapiroextern size_t strlcat __P((char *, const char *, size_t)); 10064565Sgshapiro 10164565Sgshapirochar newcmdbuf[1000]; 10264565Sgshapirochar *prg, *par; 10364565Sgshapiro 10464565Sgshapiro/* 10564565Sgshapiro** ADDCMD -- add a string to newcmdbuf, check for overflow 10664565Sgshapiro** 10764565Sgshapiro** Parameters: 10864565Sgshapiro** s -- string to add 10964565Sgshapiro** cmd -- it's a command: prepend CMDDIR/ 11064565Sgshapiro** len -- length of string to add 11164565Sgshapiro** 11264565Sgshapiro** Side Effects: 11364565Sgshapiro** changes newcmdbuf or exits with a failure. 11464565Sgshapiro** 11564565Sgshapiro*/ 11664565Sgshapiro 11764565Sgshapirovoid 11864565Sgshapiroaddcmd(s, cmd, len) 11964565Sgshapiro char *s; 12064565Sgshapiro int cmd; 12164565Sgshapiro int len; 12264565Sgshapiro{ 12364565Sgshapiro if (s == NULL || *s == '\0') 12464565Sgshapiro return; 12564565Sgshapiro 12664565Sgshapiro if (sizeof newcmdbuf - strlen(newcmdbuf) <= 12764565Sgshapiro len + (cmd ? (strlen(CMDDIR) + 1) : 0)) 12864565Sgshapiro { 12964565Sgshapiro fprintf(stderr, "%s: command too long: %s\n", prg, par); 13064565Sgshapiro#ifndef DEBUG 13164565Sgshapiro syslog(LOG_WARNING, "command too long: %.40s", par); 13264565Sgshapiro#endif /* ! DEBUG */ 13364565Sgshapiro exit(EX_UNAVAILABLE); 13464565Sgshapiro } 13564565Sgshapiro if (cmd) 13664565Sgshapiro { 13764565Sgshapiro (void) strlcat(newcmdbuf, CMDDIR, sizeof newcmdbuf); 13864565Sgshapiro (void) strlcat(newcmdbuf, "/", sizeof newcmdbuf); 13964565Sgshapiro } 14064565Sgshapiro (void) strlcat(newcmdbuf, s, sizeof newcmdbuf); 14164565Sgshapiro} 14264565Sgshapiro 14338032Speterint 14438032Spetermain(argc, argv) 14538032Speter int argc; 14638032Speter char **argv; 14738032Speter{ 14838032Speter register char *p; 14938032Speter register char *q; 15064565Sgshapiro register char *r; 15138032Speter register char *cmd; 15238032Speter int i; 15364565Sgshapiro int isexec; 15464565Sgshapiro int save_errno; 15538032Speter char *newenv[2]; 15638032Speter char cmdbuf[1000]; 15738032Speter char pathbuf[1000]; 15864565Sgshapiro char specialbuf[32]; 15938032Speter 16064565Sgshapiro#ifndef DEBUG 16164565Sgshapiro# ifndef LOG_MAIL 16238032Speter openlog("smrsh", 0); 16364565Sgshapiro# else /* ! LOG_MAIL */ 16438032Speter openlog("smrsh", LOG_ODELAY|LOG_CONS, LOG_MAIL); 16564565Sgshapiro# endif /* ! LOG_MAIL */ 16664565Sgshapiro#endif /* ! DEBUG */ 16738032Speter 16864565Sgshapiro (void) strlcpy(pathbuf, "PATH=", sizeof pathbuf); 16964565Sgshapiro (void) strlcat(pathbuf, PATH, sizeof pathbuf); 17038032Speter newenv[0] = pathbuf; 17138032Speter newenv[1] = NULL; 17238032Speter 17338032Speter /* 17438032Speter ** Do basic argv usage checking 17538032Speter */ 17638032Speter 17764565Sgshapiro prg = argv[0]; 17864565Sgshapiro 17938032Speter if (argc != 3 || strcmp(argv[1], "-c") != 0) 18038032Speter { 18164565Sgshapiro fprintf(stderr, "Usage: %s -c command\n", prg); 18264565Sgshapiro#ifndef DEBUG 18338032Speter syslog(LOG_ERR, "usage"); 18464565Sgshapiro#endif /* ! DEBUG */ 18538032Speter exit(EX_USAGE); 18638032Speter } 18738032Speter 18877352Sgshapiro par = argv[2]; 18977352Sgshapiro 19038032Speter /* 19138032Speter ** Disallow special shell syntax. This is overly restrictive, 19238032Speter ** but it should shut down all attacks. 19338032Speter ** Be sure to include 8-bit versions, since many shells strip 19438032Speter ** the address to 7 bits before checking. 19538032Speter */ 19638032Speter 19764565Sgshapiro if (strlen(SPECIALS) * 2 >= sizeof specialbuf) 19838032Speter { 19964565Sgshapiro#ifndef DEBUG 20064565Sgshapiro syslog(LOG_ERR, "too many specials: %.40s", SPECIALS); 20164565Sgshapiro#endif /* ! DEBUG */ 20238032Speter exit(EX_UNAVAILABLE); 20338032Speter } 20464565Sgshapiro (void) strlcpy(specialbuf, SPECIALS, sizeof specialbuf); 20564565Sgshapiro for (p = specialbuf; *p != '\0'; p++) 20664565Sgshapiro *p |= '\200'; 20764565Sgshapiro (void) strlcat(specialbuf, SPECIALS, sizeof specialbuf); 20838032Speter 20938032Speter /* 21038032Speter ** Do a quick sanity check on command line length. 21138032Speter */ 21238032Speter 21364565Sgshapiro i = strlen(par); 21464565Sgshapiro if (i > (sizeof newcmdbuf - sizeof CMDDIR - 2)) 21538032Speter { 21664565Sgshapiro fprintf(stderr, "%s: command too long: %s\n", prg, par); 21764565Sgshapiro#ifndef DEBUG 21864565Sgshapiro syslog(LOG_WARNING, "command too long: %.40s", par); 21964565Sgshapiro#endif /* ! DEBUG */ 22038032Speter exit(EX_UNAVAILABLE); 22138032Speter } 22238032Speter 22364565Sgshapiro q = par; 22464565Sgshapiro newcmdbuf[0] = '\0'; 22564565Sgshapiro isexec = FALSE; 22638032Speter 22764565Sgshapiro while (*q) 22838032Speter { 22964565Sgshapiro /* 23064565Sgshapiro ** Strip off a leading pathname on the command name. For 23164565Sgshapiro ** example, change /usr/ucb/vacation to vacation. 23264565Sgshapiro */ 23338032Speter 23464565Sgshapiro /* strip leading spaces */ 23564565Sgshapiro while (*q != '\0' && isascii(*q) && isspace(*q)) 23664565Sgshapiro q++; 23764565Sgshapiro if (*q == '\0') 23838032Speter { 23964565Sgshapiro if (isexec) 24064565Sgshapiro { 24164565Sgshapiro fprintf(stderr, "%s: missing command to exec\n", 24264565Sgshapiro prg); 24364565Sgshapiro#ifndef DEBUG 24464565Sgshapiro syslog(LOG_CRIT, "uid %d: missing command to exec", getuid()); 24564565Sgshapiro#endif /* ! DEBUG */ 24664565Sgshapiro exit(EX_UNAVAILABLE); 24764565Sgshapiro } 24838032Speter break; 24938032Speter } 25038032Speter 25164565Sgshapiro /* find the end of the command name */ 25264565Sgshapiro p = strpbrk(q, " \t"); 25364565Sgshapiro if (p == NULL) 25464565Sgshapiro cmd = &q[strlen(q)]; 25564565Sgshapiro else 25664565Sgshapiro { 25764565Sgshapiro *p = '\0'; 25864565Sgshapiro cmd = p; 25964565Sgshapiro } 26064565Sgshapiro /* search backwards for last / (allow for 0200 bit) */ 26164565Sgshapiro while (cmd > q) 26264565Sgshapiro { 26364565Sgshapiro if ((*--cmd & 0177) == '/') 26464565Sgshapiro { 26564565Sgshapiro cmd++; 26664565Sgshapiro break; 26764565Sgshapiro } 26864565Sgshapiro } 26964565Sgshapiro /* cmd now points at final component of path name */ 27038032Speter 27164565Sgshapiro /* allow a few shell builtins */ 27264565Sgshapiro if (strcmp(q, "exec") == 0 && p != NULL) 27364565Sgshapiro { 27464565Sgshapiro addcmd("exec ", FALSE, strlen("exec ")); 27564565Sgshapiro /* test _next_ arg */ 27664565Sgshapiro q = ++p; 27764565Sgshapiro isexec = TRUE; 27864565Sgshapiro continue; 27964565Sgshapiro } 28064565Sgshapiro else if (strcmp(q, "exit") == 0 || strcmp(q, "echo") == 0) 28164565Sgshapiro { 28264565Sgshapiro addcmd(cmd, FALSE, strlen(cmd)); 28364565Sgshapiro /* test following chars */ 28464565Sgshapiro } 28564565Sgshapiro else 28664565Sgshapiro { 28764565Sgshapiro /* 28864565Sgshapiro ** Check to see if the command name is legal. 28964565Sgshapiro */ 29064565Sgshapiro (void) strlcpy(cmdbuf, CMDDIR, sizeof cmdbuf); 29164565Sgshapiro (void) strlcat(cmdbuf, "/", sizeof cmdbuf); 29264565Sgshapiro (void) strlcat(cmdbuf, cmd, sizeof cmdbuf); 29364565Sgshapiro#ifdef DEBUG 29464565Sgshapiro printf("Trying %s\n", cmdbuf); 29564565Sgshapiro#endif /* DEBUG */ 29664565Sgshapiro if (access(cmdbuf, X_OK) < 0) 29764565Sgshapiro { 29864565Sgshapiro /* oops.... crack attack possiblity */ 29964565Sgshapiro fprintf(stderr, 30064565Sgshapiro "%s: %s not available for sendmail programs\n", 30164565Sgshapiro prg, cmd); 30264565Sgshapiro if (p != NULL) 30364565Sgshapiro *p = ' '; 30464565Sgshapiro#ifndef DEBUG 30564565Sgshapiro syslog(LOG_CRIT, "uid %d: attempt to use %s", 30664565Sgshapiro getuid(), cmd); 30764565Sgshapiro#endif /* ! DEBUG */ 30864565Sgshapiro exit(EX_UNAVAILABLE); 30964565Sgshapiro } 31038032Speter 31164565Sgshapiro /* 31264565Sgshapiro ** Create the actual shell input. 31364565Sgshapiro */ 31464565Sgshapiro 31564565Sgshapiro addcmd(cmd, TRUE, strlen(cmd)); 31664565Sgshapiro } 31764565Sgshapiro isexec = FALSE; 31864565Sgshapiro 31938032Speter if (p != NULL) 32038032Speter *p = ' '; 32164565Sgshapiro else 32264565Sgshapiro break; 32364565Sgshapiro 32464565Sgshapiro r = strpbrk(p, specialbuf); 32564565Sgshapiro if (r == NULL) { 32664565Sgshapiro addcmd(p, FALSE, strlen(p)); 32764565Sgshapiro break; 32864565Sgshapiro } 32964565Sgshapiro#if ALLOWSEMI 33064565Sgshapiro if (*r == ';') { 33164565Sgshapiro addcmd(p, FALSE, r - p + 1); 33264565Sgshapiro q = r + 1; 33364565Sgshapiro continue; 33464565Sgshapiro } 33564565Sgshapiro#endif /* ALLOWSEMI */ 33664565Sgshapiro if ((*r == '&' && *(r + 1) == '&') || 33764565Sgshapiro (*r == '|' && *(r + 1) == '|')) 33864565Sgshapiro { 33964565Sgshapiro addcmd(p, FALSE, r - p + 2); 34064565Sgshapiro q = r + 2; 34164565Sgshapiro continue; 34264565Sgshapiro } 34364565Sgshapiro 34464565Sgshapiro fprintf(stderr, "%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", 34764565Sgshapiro getuid(), *r, par); 34864565Sgshapiro#endif /* ! DEBUG */ 34938032Speter exit(EX_UNAVAILABLE); 35064565Sgshapiro } /* end of while *q */ 35164565Sgshapiro if (isexec) 35264565Sgshapiro { 35364565Sgshapiro fprintf(stderr, "%s: missing command to exec\n", prg); 35464565Sgshapiro#ifndef DEBUG 35564565Sgshapiro syslog(LOG_CRIT, "uid %d: missing command to exec", getuid()); 35664565Sgshapiro#endif /* ! DEBUG */ 35764565Sgshapiro exit(EX_UNAVAILABLE); 35838032Speter } 35964565Sgshapiro /* make sure we created something */ 36064565Sgshapiro if (newcmdbuf[0] == '\0') 36164565Sgshapiro { 36264565Sgshapiro fprintf(stderr, "Usage: %s -c command\n", prg); 36364565Sgshapiro#ifndef DEBUG 36464565Sgshapiro syslog(LOG_ERR, "usage"); 36564565Sgshapiro#endif /* ! DEBUG */ 36664565Sgshapiro exit(EX_USAGE); 36764565Sgshapiro } 36838032Speter 36938032Speter /* 37038032Speter ** Now invoke the shell 37138032Speter */ 37238032Speter 37338032Speter#ifdef DEBUG 37464565Sgshapiro printf("%s\n", newcmdbuf); 37564565Sgshapiro#endif /* DEBUG */ 37664565Sgshapiro (void) execle("/bin/sh", "/bin/sh", "-c", newcmdbuf, NULL, newenv); 37764565Sgshapiro save_errno = errno; 37864565Sgshapiro#ifndef DEBUG 37938032Speter syslog(LOG_CRIT, "Cannot exec /bin/sh: %m"); 38064565Sgshapiro#endif /* ! DEBUG */ 38164565Sgshapiro errno = save_errno; 38238032Speter perror("/bin/sh"); 38338032Speter exit(EX_OSFILE); 38464565Sgshapiro /* NOTREACHED */ 38564565Sgshapiro return EX_OSFILE; 38638032Speter} 387