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 | } |
---|