"Fossies" - the Fresh Open Source Software Archive

Member "sendmail-8.15.2/smrsh/smrsh.c" (5 Mar 2014, 10309 Bytes) of package /linux/misc/sendmail.8.15.2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "smrsh.c" see the Fossies "Dox" file reference documentation.

    1 /*
    2  * Copyright (c) 1998-2004 Proofpoint, 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 
   16 SM_IDSTR(copyright,
   17 "@(#) Copyright (c) 1998-2004 Proofpoint, 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 
   23 SM_IDSTR(id, "@(#)$Id: smrsh.c,v 8.66 2013-11-22 20:52:00 ca 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 
   96 char newcmdbuf[1000];
   97 char *prg, *par;
   98 
   99 static void addcmd __P((char *, bool, size_t));
  100 
  101 /*
  102 **  ADDCMD -- add a string to newcmdbuf, check for overflow
  103 **
  104 **    Parameters:
  105 **  s -- string to add
  106 **  cmd -- it's a command: prepend CMDDIR/
  107 **  len -- length of string to add
  108 **
  109 **    Side Effects:
  110 **  changes newcmdbuf or exits with a failure.
  111 **
  112 */
  113 
  114 static void
  115 addcmd(s, cmd, len)
  116     char *s;
  117     bool cmd;
  118     size_t len;
  119 {
  120     if (s == NULL || *s == '\0')
  121         return;
  122 
  123     /* enough space for s (len) and CMDDIR + "/" and '\0'? */
  124     if (sizeof newcmdbuf - strlen(newcmdbuf) <=
  125         len + 1 + (cmd ? (strlen(CMDDIR) + 1) : 0))
  126     {
  127         (void)sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
  128                     "%s: command too long: %s\n", prg, par);
  129 #ifndef DEBUG
  130         syslog(LOG_WARNING, "command too long: %.40s", par);
  131 #endif /* ! DEBUG */
  132         exit(EX_UNAVAILABLE);
  133     }
  134     if (cmd)
  135         (void) sm_strlcat2(newcmdbuf, CMDDIR, "/", sizeof newcmdbuf);
  136     (void) strncat(newcmdbuf, s, len);
  137 }
  138 
  139 int
  140 main(argc, argv)
  141     int argc;
  142     char **argv;
  143 {
  144     register char *p;
  145     register char *q;
  146     register char *r;
  147     register char *cmd;
  148     int isexec;
  149     int save_errno;
  150     char *newenv[2];
  151     char pathbuf[1000];
  152     char specialbuf[32];
  153     struct stat st;
  154 
  155 #ifndef DEBUG
  156 # ifndef LOG_MAIL
  157     openlog("smrsh", 0);
  158 # else /* ! LOG_MAIL */
  159     openlog("smrsh", LOG_ODELAY|LOG_CONS, LOG_MAIL);
  160 # endif /* ! LOG_MAIL */
  161 #endif /* ! DEBUG */
  162 
  163     (void) sm_strlcpyn(pathbuf, sizeof pathbuf, 2, "PATH=", PATH);
  164     newenv[0] = pathbuf;
  165     newenv[1] = NULL;
  166 
  167     /*
  168     **  Do basic argv usage checking
  169     */
  170 
  171     prg = argv[0];
  172 
  173     if (argc != 3 || strcmp(argv[1], "-c") != 0)
  174     {
  175         (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
  176                      "Usage: %s -c command\n", prg);
  177 #ifndef DEBUG
  178         syslog(LOG_ERR, "usage");
  179 #endif /* ! DEBUG */
  180         exit(EX_USAGE);
  181     }
  182 
  183     par = argv[2];
  184 
  185     /*
  186     **  Disallow special shell syntax.  This is overly restrictive,
  187     **  but it should shut down all attacks.
  188     **  Be sure to include 8-bit versions, since many shells strip
  189     **  the address to 7 bits before checking.
  190     */
  191 
  192     if (strlen(SPECIALS) * 2 >= sizeof specialbuf)
  193     {
  194 #ifndef DEBUG
  195         syslog(LOG_ERR, "too many specials: %.40s", SPECIALS);
  196 #endif /* ! DEBUG */
  197         exit(EX_UNAVAILABLE);
  198     }
  199     (void) sm_strlcpy(specialbuf, SPECIALS, sizeof specialbuf);
  200     for (p = specialbuf; *p != '\0'; p++)
  201         *p |= '\200';
  202     (void) sm_strlcat(specialbuf, SPECIALS, sizeof specialbuf);
  203 
  204     /*
  205     **  Do a quick sanity check on command line length.
  206     */
  207 
  208     if (strlen(par) > (sizeof newcmdbuf - sizeof CMDDIR - 2))
  209     {
  210         (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
  211                      "%s: command too long: %s\n", prg, par);
  212 #ifndef DEBUG
  213         syslog(LOG_WARNING, "command too long: %.40s", par);
  214 #endif /* ! DEBUG */
  215         exit(EX_UNAVAILABLE);
  216     }
  217 
  218     q = par;
  219     newcmdbuf[0] = '\0';
  220     isexec = false;
  221 
  222     while (*q != '\0')
  223     {
  224         /*
  225         **  Strip off a leading pathname on the command name.  For
  226         **  example, change /usr/ucb/vacation to vacation.
  227         */
  228 
  229         /* strip leading spaces */
  230         while (*q != '\0' && isascii(*q) && isspace(*q))
  231             q++;
  232         if (*q == '\0')
  233         {
  234             if (isexec)
  235             {
  236                 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
  237                              "%s: missing command to exec\n",
  238                              prg);
  239 #ifndef DEBUG
  240                 syslog(LOG_CRIT, "uid %d: missing command to exec", (int) getuid());
  241 #endif /* ! DEBUG */
  242                 exit(EX_UNAVAILABLE);
  243             }
  244             break;
  245         }
  246 
  247         /* find the end of the command name */
  248         p = strpbrk(q, " \t");
  249         if (p == NULL)
  250             cmd = &q[strlen(q)];
  251         else
  252         {
  253             *p = '\0';
  254             cmd = p;
  255         }
  256         /* search backwards for last / (allow for 0200 bit) */
  257         while (cmd > q)
  258         {
  259             if ((*--cmd & 0177) == '/')
  260             {
  261                 cmd++;
  262                 break;
  263             }
  264         }
  265         /* cmd now points at final component of path name */
  266 
  267         /* allow a few shell builtins */
  268         if (strcmp(q, "exec") == 0 && p != NULL)
  269         {
  270             addcmd("exec ", false, strlen("exec "));
  271 
  272             /* test _next_ arg */
  273             q = ++p;
  274             isexec = true;
  275             continue;
  276         }
  277         else if (strcmp(q, "exit") == 0 || strcmp(q, "echo") == 0)
  278         {
  279             addcmd(cmd, false, strlen(cmd));
  280 
  281             /* test following chars */
  282         }
  283         else
  284         {
  285             char cmdbuf[MAXPATHLEN];
  286 
  287             /*
  288             **  Check to see if the command name is legal.
  289             */
  290 
  291             if (sm_strlcpyn(cmdbuf, sizeof cmdbuf, 3, CMDDIR,
  292                     "/", cmd) >= sizeof cmdbuf)
  293             {
  294                 /* too long */
  295                 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
  296                              "%s: \"%s\" not available for sendmail programs (filename too long)\n",
  297                               prg, cmd);
  298                 if (p != NULL)
  299                     *p = ' ';
  300 #ifndef DEBUG
  301                 syslog(LOG_CRIT, "uid %d: attempt to use \"%s\" (filename too long)",
  302                        (int) getuid(), cmd);
  303 #endif /* ! DEBUG */
  304                 exit(EX_UNAVAILABLE);
  305             }
  306 
  307 #ifdef DEBUG
  308             (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
  309                          "Trying %s\n", cmdbuf);
  310 #endif /* DEBUG */
  311             if (stat(cmdbuf, &st) < 0)
  312             {
  313                 /* can't stat it */
  314                 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
  315                              "%s: \"%s\" not available for sendmail programs (stat failed)\n",
  316                               prg, cmd);
  317                 if (p != NULL)
  318                     *p = ' ';
  319 #ifndef DEBUG
  320                 syslog(LOG_CRIT, "uid %d: attempt to use \"%s\" (stat failed)",
  321                        (int) getuid(), cmd);
  322 #endif /* ! DEBUG */
  323                 exit(EX_UNAVAILABLE);
  324             }
  325             if (!S_ISREG(st.st_mode)
  326 #ifdef S_ISLNK
  327                 && !S_ISLNK(st.st_mode)
  328 #endif /* S_ISLNK */
  329                )
  330             {
  331                 /* can't stat it */
  332                 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
  333                              "%s: \"%s\" not available for sendmail programs (not a file)\n",
  334                               prg, cmd);
  335                 if (p != NULL)
  336                     *p = ' ';
  337 #ifndef DEBUG
  338                 syslog(LOG_CRIT, "uid %d: attempt to use \"%s\" (not a file)",
  339                        (int) getuid(), cmd);
  340 #endif /* ! DEBUG */
  341                 exit(EX_UNAVAILABLE);
  342             }
  343             if (access(cmdbuf, X_OK) < 0)
  344             {
  345                 /* oops....  crack attack possiblity */
  346                 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
  347                              "%s: \"%s\" not available for sendmail programs\n",
  348                               prg, cmd);
  349                 if (p != NULL)
  350                     *p = ' ';
  351 #ifndef DEBUG
  352                 syslog(LOG_CRIT, "uid %d: attempt to use \"%s\"",
  353                        (int) getuid(), cmd);
  354 #endif /* ! DEBUG */
  355                 exit(EX_UNAVAILABLE);
  356             }
  357 
  358             /*
  359             **  Create the actual shell input.
  360             */
  361 
  362             addcmd(cmd, true, strlen(cmd));
  363         }
  364         isexec = false;
  365 
  366         if (p != NULL)
  367             *p = ' ';
  368         else
  369             break;
  370 
  371         r = strpbrk(p, specialbuf);
  372         if (r == NULL)
  373         {
  374             addcmd(p, false, strlen(p));
  375             break;
  376         }
  377 #if ALLOWSEMI
  378         if (*r == ';')
  379         {
  380             addcmd(p, false,  r - p + 1);
  381             q = r + 1;
  382             continue;
  383         }
  384 #endif /* ALLOWSEMI */
  385         if ((*r == '&' && *(r + 1) == '&') ||
  386             (*r == '|' && *(r + 1) == '|'))
  387         {
  388             addcmd(p, false,  r - p + 2);
  389             q = r + 2;
  390             continue;
  391         }
  392 
  393         (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
  394                      "%s: cannot use %c in command\n", prg, *r);
  395 #ifndef DEBUG
  396         syslog(LOG_CRIT, "uid %d: attempt to use %c in command: %s",
  397                (int) getuid(), *r, par);
  398 #endif /* ! DEBUG */
  399         exit(EX_UNAVAILABLE);
  400     }
  401     if (isexec)
  402     {
  403         (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
  404                      "%s: missing command to exec\n", prg);
  405 #ifndef DEBUG
  406         syslog(LOG_CRIT, "uid %d: missing command to exec",
  407                (int) getuid());
  408 #endif /* ! DEBUG */
  409         exit(EX_UNAVAILABLE);
  410     }
  411     /* make sure we created something */
  412     if (newcmdbuf[0] == '\0')
  413     {
  414         (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
  415                      "Usage: %s -c command\n", prg);
  416 #ifndef DEBUG
  417         syslog(LOG_ERR, "usage");
  418 #endif /* ! DEBUG */
  419         exit(EX_USAGE);
  420     }
  421 
  422     /*
  423     **  Now invoke the shell
  424     */
  425 
  426 #ifdef DEBUG
  427     (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s\n", newcmdbuf);
  428 #endif /* DEBUG */
  429     (void) execle("/bin/sh", "/bin/sh", "-c", newcmdbuf,
  430               (char *)NULL, newenv);
  431     save_errno = errno;
  432 #ifndef DEBUG
  433     syslog(LOG_CRIT, "Cannot exec /bin/sh: %s", sm_errstring(errno));
  434 #endif /* ! DEBUG */
  435     errno = save_errno;
  436     sm_perror("/bin/sh");
  437     exit(EX_OSFILE);
  438     /* NOTREACHED */
  439     return EX_OSFILE;
  440 }