[12553] | 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/ucb/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/local/etc/smrsh, where this compiled |
---|
| 40 | ** binary is installed /usr/local/etc/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/adm/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:/usr/ucb" |
---|
| 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 | } |
---|