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