source: trunk/third/sendmail/smrsh/smrsh.c @ 19204

Revision 19204, 10.0 KB checked in by zacheiss, 22 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r19203, which included commits to RCS files with non-trunk default branches.
Line 
1/*
2 * Copyright (c) 1998-2002 Sendmail, Inc. and its suppliers.
3 *      All rights reserved.
4 * Copyright (c) 1993 Eric P. Allman.  All rights reserved.
5 * Copyright (c) 1993
6 *      The Regents of the University of California.  All rights reserved.
7 *
8 * By using this file, you agree to the terms and conditions set
9 * forth in the LICENSE file which can be found at the top level of
10 * the sendmail distribution.
11 *
12 */
13
14#include <sm/gen.h>
15
16SM_IDSTR(copyright,
17"@(#) Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.\n\
18        All rights reserved.\n\
19     Copyright (c) 1993 Eric P. Allman.  All rights reserved.\n\
20     Copyright (c) 1993\n\
21        The Regents of the University of California.  All rights reserved.\n")
22
23SM_IDSTR(id, "@(#)$Id: smrsh.c,v 1.1.1.2 2003-04-08 15:08:12 zacheiss Exp $")
24
25/*
26**  SMRSH -- sendmail restricted shell
27**
28**      This is a patch to get around the prog mailer bugs in most
29**      versions of sendmail.
30**
31**      Use this in place of /bin/sh in the "prog" mailer definition
32**      in your sendmail.cf file.  You then create CMDDIR (owned by
33**      root, mode 755) and put links to any programs you want
34**      available to prog mailers in that directory.  This should
35**      include things like "vacation" and "procmail", but not "sed"
36**      or "sh".
37**
38**      Leading pathnames are stripped from program names so that
39**      existing .forward files that reference things like
40**      "/usr/bin/vacation" will continue to work.
41**
42**      The following characters are completely illegal:
43**              <  >  ^  &  `  (  ) \n \r
44**      The following characters are sometimes illegal:
45**              |  &
46**      This is more restrictive than strictly necessary.
47**
48**      To use this, add FEATURE(`smrsh') to your .mc file.
49**
50**      This can be used on any version of sendmail.
51**
52**      In loving memory of RTM.  11/02/93.
53*/
54
55#include <unistd.h>
56#include <sm/io.h>
57#include <sm/limits.h>
58#include <sm/string.h>
59#include <sys/file.h>
60#include <sys/types.h>
61#include <sys/stat.h>
62#include <string.h>
63#include <ctype.h>
64#include <errno.h>
65#ifdef EX_OK
66# undef EX_OK
67#endif /* EX_OK */
68#include <sysexits.h>
69#include <syslog.h>
70#include <stdlib.h>
71
72#include <sm/conf.h>
73#include <sm/errstring.h>
74
75/* directory in which all commands must reside */
76#ifndef CMDDIR
77# ifdef SMRSH_CMDDIR
78#  define CMDDIR        SMRSH_CMDDIR
79# else /* SMRSH_CMDDIR */
80#  define CMDDIR        "/usr/adm/sm.bin"
81# endif /* SMRSH_CMDDIR */
82#endif /* ! CMDDIR */
83
84/* characters disallowed in the shell "-c" argument */
85#define SPECIALS        "<|>^();&`$\r\n"
86
87/* default search path */
88#ifndef PATH
89# ifdef SMRSH_PATH
90#  define PATH          SMRSH_PATH
91# else /* SMRSH_PATH */
92#  define PATH          "/bin:/usr/bin:/usr/ucb"
93# endif /* SMRSH_PATH */
94#endif /* ! PATH */
95
96char newcmdbuf[1000];
97char *prg, *par;
98
99/*
100**  ADDCMD -- add a string to newcmdbuf, check for overflow
101**
102**    Parameters:
103**      s -- string to add
104**      cmd -- it's a command: prepend CMDDIR/
105**      len -- length of string to add
106**
107**    Side Effects:
108**      changes newcmdbuf or exits with a failure.
109**
110*/
111
112void
113addcmd(s, cmd, len)
114        char *s;
115        bool cmd;
116        size_t len;
117{
118        if (s == NULL || *s == '\0')
119                return;
120
121        if (sizeof newcmdbuf - strlen(newcmdbuf) <=
122            len + (cmd ? (strlen(CMDDIR) + 1) : 0))
123        {
124                (void)sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
125                                    "%s: command too long: %s\n", prg, par);
126#ifndef DEBUG
127                syslog(LOG_WARNING, "command too long: %.40s", par);
128#endif /* ! DEBUG */
129                exit(EX_UNAVAILABLE);
130        }
131        if (cmd)
132                (void) sm_strlcat2(newcmdbuf, CMDDIR, "/", sizeof newcmdbuf);
133        (void) sm_strlcat(newcmdbuf, s, sizeof newcmdbuf);
134}
135
136int
137main(argc, argv)
138        int argc;
139        char **argv;
140{
141        register char *p;
142        register char *q;
143        register char *r;
144        register char *cmd;
145        int isexec;
146        int save_errno;
147        char *newenv[2];
148        char pathbuf[1000];
149        char specialbuf[32];
150        struct stat st;
151
152#ifndef DEBUG
153# ifndef LOG_MAIL
154        openlog("smrsh", 0);
155# else /* ! LOG_MAIL */
156        openlog("smrsh", LOG_ODELAY|LOG_CONS, LOG_MAIL);
157# endif /* ! LOG_MAIL */
158#endif /* ! DEBUG */
159
160        (void) sm_strlcpyn(pathbuf, sizeof pathbuf, 2, "PATH=", PATH);
161        newenv[0] = pathbuf;
162        newenv[1] = NULL;
163
164        /*
165        **  Do basic argv usage checking
166        */
167
168        prg = argv[0];
169
170        if (argc != 3 || strcmp(argv[1], "-c") != 0)
171        {
172                (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
173                                     "Usage: %s -c command\n", prg);
174#ifndef DEBUG
175                syslog(LOG_ERR, "usage");
176#endif /* ! DEBUG */
177                exit(EX_USAGE);
178        }
179
180        par = argv[2];
181
182        /*
183        **  Disallow special shell syntax.  This is overly restrictive,
184        **  but it should shut down all attacks.
185        **  Be sure to include 8-bit versions, since many shells strip
186        **  the address to 7 bits before checking.
187        */
188
189        if (strlen(SPECIALS) * 2 >= sizeof specialbuf)
190        {
191#ifndef DEBUG
192                syslog(LOG_ERR, "too many specials: %.40s", SPECIALS);
193#endif /* ! DEBUG */
194                exit(EX_UNAVAILABLE);
195        }
196        (void) sm_strlcpy(specialbuf, SPECIALS, sizeof specialbuf);
197        for (p = specialbuf; *p != '\0'; p++)
198                *p |= '\200';
199        (void) sm_strlcat(specialbuf, SPECIALS, sizeof specialbuf);
200
201        /*
202        **  Do a quick sanity check on command line length.
203        */
204
205        if (strlen(par) > (sizeof newcmdbuf - sizeof CMDDIR - 2))
206        {
207                (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
208                                     "%s: command too long: %s\n", prg, par);
209#ifndef DEBUG
210                syslog(LOG_WARNING, "command too long: %.40s", par);
211#endif /* ! DEBUG */
212                exit(EX_UNAVAILABLE);
213        }
214
215        q = par;
216        newcmdbuf[0] = '\0';
217        isexec = false;
218
219        while (*q != '\0')
220        {
221                /*
222                **  Strip off a leading pathname on the command name.  For
223                **  example, change /usr/ucb/vacation to vacation.
224                */
225
226                /* strip leading spaces */
227                while (*q != '\0' && isascii(*q) && isspace(*q))
228                        q++;
229                if (*q == '\0')
230                {
231                        if (isexec)
232                        {
233                                (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
234                                                     "%s: missing command to exec\n",
235                                                     prg);
236#ifndef DEBUG
237                                syslog(LOG_CRIT, "uid %d: missing command to exec", (int) getuid());
238#endif /* ! DEBUG */
239                                exit(EX_UNAVAILABLE);
240                        }
241                        break;
242                }
243
244                /* find the end of the command name */
245                p = strpbrk(q, " \t");
246                if (p == NULL)
247                        cmd = &q[strlen(q)];
248                else
249                {
250                        *p = '\0';
251                        cmd = p;
252                }
253                /* search backwards for last / (allow for 0200 bit) */
254                while (cmd > q)
255                {
256                        if ((*--cmd & 0177) == '/')
257                        {
258                                cmd++;
259                                break;
260                        }
261                }
262                /* cmd now points at final component of path name */
263
264                /* allow a few shell builtins */
265                if (strcmp(q, "exec") == 0 && p != NULL)
266                {
267                        addcmd("exec ", false, strlen("exec "));
268
269                        /* test _next_ arg */
270                        q = ++p;
271                        isexec = true;
272                        continue;
273                }
274                else if (strcmp(q, "exit") == 0 || strcmp(q, "echo") == 0)
275                {
276                        addcmd(cmd, false, strlen(cmd));
277
278                        /* test following chars */
279                }
280                else
281                {
282                        char cmdbuf[MAXPATHLEN];
283
284                        /*
285                        **  Check to see if the command name is legal.
286                        */
287
288                        if (sm_strlcpyn(cmdbuf, sizeof cmdbuf, 3, CMDDIR,
289                                        "/", cmd) >= sizeof cmdbuf)
290                        {
291                                /* too long */
292                                (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
293                                                     "%s: \"%s\" not available for sendmail programs (filename too long)\n",
294                                                      prg, cmd);
295                                if (p != NULL)
296                                        *p = ' ';
297#ifndef DEBUG
298                                syslog(LOG_CRIT, "uid %d: attempt to use \"%s\" (filename too long)",
299                                       (int) getuid(), cmd);
300#endif /* ! DEBUG */
301                                exit(EX_UNAVAILABLE);
302                        }
303
304#ifdef DEBUG
305                        (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
306                                             "Trying %s\n", cmdbuf);
307#endif /* DEBUG */
308                        if (stat(cmdbuf, &st) < 0)
309                        {
310                                /* can't stat it */
311                                (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
312                                                     "%s: \"%s\" not available for sendmail programs (stat failed)\n",
313                                                      prg, cmd);
314                                if (p != NULL)
315                                        *p = ' ';
316#ifndef DEBUG
317                                syslog(LOG_CRIT, "uid %d: attempt to use \"%s\" (stat failed)",
318                                       (int) getuid(), cmd);
319#endif /* ! DEBUG */
320                                exit(EX_UNAVAILABLE);
321                        }
322                        if (!S_ISREG(st.st_mode)
323#ifdef S_ISLNK
324                            && !S_ISLNK(st.st_mode)
325#endif /* S_ISLNK */
326                           )
327                        {
328                                /* can't stat it */
329                                (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
330                                                     "%s: \"%s\" not available for sendmail programs (not a file)\n",
331                                                      prg, cmd);
332                                if (p != NULL)
333                                        *p = ' ';
334#ifndef DEBUG
335                                syslog(LOG_CRIT, "uid %d: attempt to use \"%s\" (not a file)",
336                                       (int) getuid(), cmd);
337#endif /* ! DEBUG */
338                                exit(EX_UNAVAILABLE);
339                        }
340                        if (access(cmdbuf, X_OK) < 0)
341                        {
342                                /* oops....  crack attack possiblity */
343                                (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
344                                                     "%s: \"%s\" not available for sendmail programs\n",
345                                                      prg, cmd);
346                                if (p != NULL)
347                                        *p = ' ';
348#ifndef DEBUG
349                                syslog(LOG_CRIT, "uid %d: attempt to use \"%s\"",
350                                       (int) getuid(), cmd);
351#endif /* ! DEBUG */
352                                exit(EX_UNAVAILABLE);
353                        }
354
355                        /*
356                        **  Create the actual shell input.
357                        */
358
359                        addcmd(cmd, true, strlen(cmd));
360                }
361                isexec = false;
362
363                if (p != NULL)
364                        *p = ' ';
365                else
366                        break;
367
368                r = strpbrk(p, specialbuf);
369                if (r == NULL)
370                {
371                        addcmd(p, false, strlen(p));
372                        break;
373                }
374#if ALLOWSEMI
375                if (*r == ';')
376                {
377                        addcmd(p, false,  r - p + 1);
378                        q = r + 1;
379                        continue;
380                }
381#endif /* ALLOWSEMI */
382                if ((*r == '&' && *(r + 1) == '&') ||
383                    (*r == '|' && *(r + 1) == '|'))
384                {
385                        addcmd(p, false,  r - p + 2);
386                        q = r + 2;
387                        continue;
388                }
389
390                (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
391                                     "%s: cannot use %c in command\n", prg, *r);
392#ifndef DEBUG
393                syslog(LOG_CRIT, "uid %d: attempt to use %c in command: %s",
394                       (int) getuid(), *r, par);
395#endif /* ! DEBUG */
396                exit(EX_UNAVAILABLE);
397        }
398        if (isexec)
399        {
400                (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
401                                     "%s: missing command to exec\n", prg);
402#ifndef DEBUG
403                syslog(LOG_CRIT, "uid %d: missing command to exec",
404                       (int) getuid());
405#endif /* ! DEBUG */
406                exit(EX_UNAVAILABLE);
407        }
408        /* make sure we created something */
409        if (newcmdbuf[0] == '\0')
410        {
411                (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
412                                     "Usage: %s -c command\n", prg);
413#ifndef DEBUG
414                syslog(LOG_ERR, "usage");
415#endif /* ! DEBUG */
416                exit(EX_USAGE);
417        }
418
419        /*
420        **  Now invoke the shell
421        */
422
423#ifdef DEBUG
424        (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s\n", newcmdbuf);
425#endif /* DEBUG */
426        (void) execle("/bin/sh", "/bin/sh", "-c", newcmdbuf, NULL, newenv);
427        save_errno = errno;
428#ifndef DEBUG
429        syslog(LOG_CRIT, "Cannot exec /bin/sh: %s", sm_errstring(errno));
430#endif /* ! DEBUG */
431        errno = save_errno;
432        sm_perror("/bin/sh");
433        exit(EX_OSFILE);
434        /* NOTREACHED */
435        return EX_OSFILE;
436}
Note: See TracBrowser for help on using the repository browser.