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 14 static 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 72 int 73 main(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