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