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.2 (Berkeley) 03/08/95"; 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 <unistd.h> 44 #include <stdio.h> 45 #include <sys/file.h> 46 #include <string.h> 47 #include <ctype.h> 48 #include <sysexits.h> 49 #include <syslog.h> 50 51 /* directory in which all commands must reside */ 52 #ifndef CMDDIR 53 # define CMDDIR "/usr/adm/sm.bin" 54 #endif 55 56 /* characters disallowed in the shell "-c" argument */ 57 #define SPECIALS "<|>^();&`$\r\n" 58 59 /* default search path */ 60 #ifndef PATH 61 # define PATH "/bin:/usr/bin:/usr/ucb" 62 #endif 63 64 main(argc, argv) 65 int argc; 66 char **argv; 67 { 68 register char *p; 69 register char *q; 70 register char *cmd; 71 int i; 72 char *newenv[2]; 73 char cmdbuf[1000]; 74 char pathbuf[1000]; 75 76 #ifndef LOG_MAIL 77 openlog("smrsh", 0); 78 #else 79 openlog("smrsh", LOG_ODELAY|LOG_CONS, LOG_MAIL); 80 #endif 81 82 strcpy(pathbuf, "PATH="); 83 strcat(pathbuf, PATH); 84 newenv[0] = pathbuf; 85 newenv[1] = NULL; 86 87 /* 88 ** Do basic argv usage checking 89 */ 90 91 if (argc != 3 || strcmp(argv[1], "-c") != 0) 92 { 93 fprintf(stderr, "Usage: %s -c command\n", argv[0]); 94 syslog(LOG_ERR, "usage"); 95 exit(EX_USAGE); 96 } 97 98 /* 99 ** Disallow special shell syntax. This is overly restrictive, 100 ** but it should shut down all attacks. 101 ** Be sure to include 8-bit versions, since many shells strip 102 ** the address to 7 bits before checking. 103 */ 104 105 strcpy(cmdbuf, SPECIALS); 106 for (p = cmdbuf; *p != '\0'; p++) 107 *p |= '\200'; 108 strcat(cmdbuf, SPECIALS); 109 p = strpbrk(argv[2], cmdbuf); 110 if (p != NULL) 111 { 112 fprintf(stderr, "%s: cannot use %c in command\n", 113 argv[0], *p); 114 syslog(LOG_CRIT, "uid %d: attempt to use %c in command: %s", 115 getuid(), *p, argv[2]); 116 exit(EX_UNAVAILABLE); 117 } 118 119 /* 120 ** Do a quick sanity check on command line length. 121 */ 122 123 i = strlen(argv[2]); 124 if (i > (sizeof cmdbuf - sizeof CMDDIR - 2)) 125 { 126 fprintf(stderr, "%s: command too long: %s\n", argv[0], argv[2]); 127 syslog(LOG_WARNING, "command too long: %.40s", argv[2]); 128 exit(EX_UNAVAILABLE); 129 } 130 131 /* 132 ** Strip off a leading pathname on the command name. For 133 ** example, change /usr/ucb/vacation to vacation. 134 */ 135 136 /* strip leading spaces */ 137 for (q = argv[2]; *q != '\0' && isascii(*q) && isspace(*q); ) 138 q++; 139 140 /* find the end of the command name */ 141 p = strpbrk(q, " \t"); 142 if (p == NULL) 143 cmd = &q[strlen(q)]; 144 else 145 { 146 *p = '\0'; 147 cmd = p; 148 } 149 150 /* search backwards for last / (allow for 0200 bit) */ 151 while (cmd > q) 152 { 153 if ((*--cmd & 0177) == '/') 154 { 155 cmd++; 156 break; 157 } 158 } 159 160 /* cmd now points at final component of path name */ 161 162 /* 163 ** Check to see if the command name is legal. 164 */ 165 166 (void) strcpy(cmdbuf, CMDDIR); 167 (void) strcat(cmdbuf, "/"); 168 (void) strcat(cmdbuf, cmd); 169 #ifdef DEBUG 170 printf("Trying %s\n", cmdbuf); 171 #endif 172 if (access(cmdbuf, X_OK) < 0) 173 { 174 /* oops.... crack attack possiblity */ 175 fprintf(stderr, "%s: %s not available for sendmail programs\n", 176 argv[0], cmd); 177 if (p != NULL) 178 *p = ' '; 179 syslog(LOG_CRIT, "uid %d: attempt to use %s", getuid(), cmd); 180 exit(EX_UNAVAILABLE); 181 } 182 if (p != NULL) 183 *p = ' '; 184 185 /* 186 ** Create the actual shell input. 187 */ 188 189 strcpy(cmdbuf, CMDDIR); 190 strcat(cmdbuf, "/"); 191 strcat(cmdbuf, cmd); 192 193 /* 194 ** Now invoke the shell 195 */ 196 197 #ifdef DEBUG 198 printf("%s\n", cmdbuf); 199 #endif 200 execle("/bin/sh", "/bin/sh", "-c", cmdbuf, NULL, newenv); 201 syslog(LOG_CRIT, "Cannot exec /bin/sh: %m"); 202 perror("/bin/sh"); 203 exit(EX_OSFILE); 204 } 205