smrsh.c revision 42580
1/* 2 * Copyright (c) 1998 Sendmail, Inc. All rights reserved. 3 * Copyright (c) 1993 Eric P. Allman. All rights reserved. 4 * Copyright (c) 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * By using this file, you agree to the terms and conditions set 8 * forth in the LICENSE file which can be found at the top level of 9 * the sendmail distribution. 10 * 11 */ 12 13#ifndef lint 14static char sccsid[] = "@(#)smrsh.c 8.11 (Berkeley) 5/19/1998"; 15#endif /* not lint */ 16 17/* 18** SMRSH -- sendmail restricted shell 19** 20** This is a patch to get around the prog mailer bugs in most 21** versions of sendmail. 22** 23** Use this in place of /bin/sh in the "prog" mailer definition 24** in your sendmail.cf file. You then create CMDDIR (owned by 25** root, mode 755) and put links to any programs you want 26** available to prog mailers in that directory. This should 27** include things like "vacation" and "procmail", but not "sed" 28** or "sh". 29** 30** Leading pathnames are stripped from program names so that 31** existing .forward files that reference things like 32** "/usr/bin/vacation" will continue to work. 33** 34** The following characters are completely illegal: 35** < > | ^ ; & $ ` ( ) \n \r 36** This is more restrictive than strictly necessary. 37** 38** To use this, edit /etc/sendmail.cf, search for ^Mprog, and 39** change P=/bin/sh to P=/usr/libexec/smrsh, where this compiled 40** binary is installed /usr/libexec/smrsh. 41** 42** This can be used on any version of sendmail. 43** 44** In loving memory of RTM. 11/02/93. 45*/ 46 47#include <unistd.h> 48#include <stdio.h> 49#include <sys/file.h> 50#include <string.h> 51#include <ctype.h> 52#ifdef EX_OK 53# undef EX_OK 54#endif 55#include <sysexits.h> 56#include <syslog.h> 57#include <stdlib.h> 58 59/* directory in which all commands must reside */ 60#ifndef CMDDIR 61# define CMDDIR "/usr/libexec/sm.bin" 62#endif 63 64/* characters disallowed in the shell "-c" argument */ 65#define SPECIALS "<|>^();&`$\r\n" 66 67/* default search path */ 68#ifndef PATH 69# define PATH "/bin:/usr/bin" 70#endif 71 72int 73main(argc, argv) 74 int argc; 75 char **argv; 76{ 77 register char *p; 78 register char *q; 79 register char *cmd; 80 int i; 81 char *newenv[2]; 82 char cmdbuf[1000]; 83 char pathbuf[1000]; 84 85#ifndef LOG_MAIL 86 openlog("smrsh", 0); 87#else 88 openlog("smrsh", LOG_ODELAY|LOG_CONS, LOG_MAIL); 89#endif 90 91 strcpy(pathbuf, "PATH="); 92 strcat(pathbuf, PATH); 93 newenv[0] = pathbuf; 94 newenv[1] = NULL; 95 96 /* 97 ** Do basic argv usage checking 98 */ 99 100 if (argc != 3 || strcmp(argv[1], "-c") != 0) 101 { 102 fprintf(stderr, "Usage: %s -c command\n", argv[0]); 103 syslog(LOG_ERR, "usage"); 104 exit(EX_USAGE); 105 } 106 107 /* 108 ** Disallow special shell syntax. This is overly restrictive, 109 ** but it should shut down all attacks. 110 ** Be sure to include 8-bit versions, since many shells strip 111 ** the address to 7 bits before checking. 112 */ 113 114 strcpy(cmdbuf, SPECIALS); 115 for (p = cmdbuf; *p != '\0'; p++) 116 *p |= '\200'; 117 strcat(cmdbuf, SPECIALS); 118 p = strpbrk(argv[2], cmdbuf); 119 if (p != NULL) 120 { 121 fprintf(stderr, "%s: cannot use %c in command\n", 122 argv[0], *p); 123 syslog(LOG_CRIT, "uid %d: attempt to use %c in command: %s", 124 getuid(), *p, argv[2]); 125 exit(EX_UNAVAILABLE); 126 } 127 128 /* 129 ** Do a quick sanity check on command line length. 130 */ 131 132 i = strlen(argv[2]); 133 if (i > (sizeof cmdbuf - sizeof CMDDIR - 2)) 134 { 135 fprintf(stderr, "%s: command too long: %s\n", argv[0], argv[2]); 136 syslog(LOG_WARNING, "command too long: %.40s", argv[2]); 137 exit(EX_UNAVAILABLE); 138 } 139 140 /* 141 ** Strip off a leading pathname on the command name. For 142 ** example, change /usr/ucb/vacation to vacation. 143 */ 144 145 /* strip leading spaces */ 146 for (q = argv[2]; *q != '\0' && isascii(*q) && isspace(*q); ) 147 q++; 148 149 /* find the end of the command name */ 150 p = strpbrk(q, " \t"); 151 if (p == NULL) 152 cmd = &q[strlen(q)]; 153 else 154 { 155 *p = '\0'; 156 cmd = p; 157 } 158 159 /* search backwards for last / (allow for 0200 bit) */ 160 while (cmd > q) 161 { 162 if ((*--cmd & 0177) == '/') 163 { 164 cmd++; 165 break; 166 } 167 } 168 169 /* cmd now points at final component of path name */ 170 171 /* 172 ** Check to see if the command name is legal. 173 */ 174 175 (void) strcpy(cmdbuf, CMDDIR); 176 (void) strcat(cmdbuf, "/"); 177 (void) strcat(cmdbuf, cmd); 178#ifdef DEBUG 179 printf("Trying %s\n", cmdbuf); 180#endif 181 if (access(cmdbuf, X_OK) < 0) 182 { 183 /* oops.... crack attack possiblity */ 184 fprintf(stderr, "%s: %s not available for sendmail programs\n", 185 argv[0], cmd); 186 if (p != NULL) 187 *p = ' '; 188 syslog(LOG_CRIT, "uid %d: attempt to use %s", getuid(), cmd); 189 exit(EX_UNAVAILABLE); 190 } 191 if (p != NULL) 192 *p = ' '; 193 194 /* 195 ** Create the actual shell input. 196 */ 197 198 strcpy(cmdbuf, CMDDIR); 199 strcat(cmdbuf, "/"); 200 strcat(cmdbuf, cmd); 201 202 /* 203 ** Now invoke the shell 204 */ 205 206#ifdef DEBUG 207 printf("%s\n", cmdbuf); 208#endif 209 execle("/bin/sh", "/bin/sh", "-c", cmdbuf, NULL, newenv); 210 syslog(LOG_CRIT, "Cannot exec /bin/sh: %m"); 211 perror("/bin/sh"); 212 exit(EX_OSFILE); 213} 214