home *** CD-ROM | disk | FTP | other *** search
/ CD Actual 25 / CDROM25.iso / Share / linux / apache / contrib / patches / 1.3 / suexecflex.patch < prev    next >
Encoding:
Internet Message Format  |  1998-06-11  |  32.7 KB

  1. From: Gary Shea <shea@gtsdesign.com>
  2. To: dgaudet@arctic.com
  3. Cc: pvanhaes@be.oracle.com, shea@gtsdesign.com, apbugs@apache.org
  4. Subject: Re: suexec/1769: suexec too limited -- need per-directory control, more permissive directory structures
  5. Date: Fri, 6 Feb 1998 13:30:23 -0700 (MST)
  6.  
  7.  As promised, patches.  These patches were inspired by, and to some
  8.  extent stolen from, Philippe Vanhaesendonck's (pvanhaes@be.oracle.com) 
  9.  mod_cgi_sugid patches.  Philippe's code/concept functioned entirely
  10.  in mod_cgi and is no longer workable in 1.3b3 (I've not looked at
  11.  1.2.* since 1.2b10), but is the source of the UserID and GroupID
  12.  directive support code.
  13.  
  14.  I have hacked mod_cgi to directly request a uid/gid from call_exec()
  15.  if there is an applicable UserID and/or GroupID.  call_exec() respects
  16.  directly requested uid/gid's absolutely, but if none is specified, it'll
  17.  still detect ~user uri's and pass the appropriate uid/gid to suexec.
  18.  Thus it's identical from the point of view of mod_include, but can
  19.  still give mod_cgi what it wants/expects..
  20.  
  21.  suexec is heavily hacked to suck up a configuration file more or less
  22.  akin to standard apache config files, but I didn't attempt to use
  23.  apache code -- it's my proof of concept I guess, easier to code it
  24.  for testing than to work out integrating the apache code.  As a result
  25.  the config files are quite picky about case, etc..  Also there
  26.  are some holes still insofar as what happens if some of the
  27.  directives are not specified.  I won't bother to fix those holes
  28.  unless someone else wants to use this code.
  29.  
  30.  The configuration file takes three directives at present;
  31.  everything else remains compiled in.  Here's an example. 
  32.  
  33.  UserDirSuffix: public_html/cgi-bin
  34.  UserDirectories: on
  35.  Directory: /users/src/a13b3/htd3/cgi-bin
  36.  Directory: /users/src/a13b3/htd2/cgi-bin
  37.  Directory: /users/src/a13b3/htd5/cgi-bin
  38.  
  39.  I have not yet converted suexec to a simple forking server sitting
  40.  on a Unix socket, but if ther are adverse effects from startup time,
  41.  I will.  Since most of what I run are large Perl codes that take close
  42.  to a second to compile, that's unlikely!
  43.  
  44.  Here's a clip of a configuration file which uses the UserID and GroupID
  45.  directives:
  46.  
  47.  ScriptAlias /htd2/cgi-bin /users/src/a13b3/htd2/cgi-bin
  48.  <Location /htd2/cgi-bin>
  49.  <Limit GET POST>
  50.  UserId          shea
  51.  GroupId         users
  52.  </Limit>
  53.  </Location>
  54.  
  55.  And.... HEEEEEEEEEEERE'S the PATCHES!
  56.  
  57.  
  58.  diff -c --exclude=*.[oa] --recursive apache_1.3b3/src/Configuration a13b3/src/Configuration
  59.  *** apache_1.3b3/src/Configuration     Wed Nov 19 17:49:57 1997
  60.  --- a13b3/src/Configuration    Wed Feb  4 22:48:13 1998
  61.  ***************
  62.  *** 41,47 ****
  63.    # Settings here have priority; If not set, Configure will attempt to guess
  64.    # the C compiler, looking for gcc first, then cc.
  65.    #
  66.  ! EXTRA_CFLAGS=
  67.    EXTRA_LDFLAGS=
  68.    EXTRA_LIBS=
  69.    EXTRA_INCLUDES=
  70.  --- 41,47 ----
  71.    # Settings here have priority; If not set, Configure will attempt to guess
  72.    # the C compiler, looking for gcc first, then cc.
  73.    #
  74.  ! EXTRA_CFLAGS=-DHTTPD_ROOT=\"/users/src/a13b3\" -DDEBUG_CGI -DDEBUG_SUGID_CONFIG  -DSUEXEC_BIN=\"/users/src/a13b3/sbin/suexec\" 
  75.    EXTRA_LDFLAGS=
  76.    EXTRA_LIBS=
  77.    EXTRA_INCLUDES=
  78.  ***************
  79.  *** 181,192 ****
  80.    ## STATUS=yes (see the Rules section near the start of this file) to allow
  81.    ## full status information.  Check conf/access.conf on how to enable this.
  82.    
  83.  ! # AddModule modules/standard/mod_status.o
  84.    
  85.    ## The Info module displays configuration information for the server and 
  86.    ## all included modules. It's very useful for debugging.
  87.    
  88.  ! # AddModule         modules/standard/mod_info.o
  89.    
  90.    ## mod_include translates server-side include (SSI) statements in text files.
  91.    ## mod_autoindex handles requests for directories which have no index file
  92.  --- 181,192 ----
  93.    ## STATUS=yes (see the Rules section near the start of this file) to allow
  94.    ## full status information.  Check conf/access.conf on how to enable this.
  95.    
  96.  ! AddModule modules/standard/mod_status.o
  97.    
  98.    ## The Info module displays configuration information for the server and 
  99.    ## all included modules. It's very useful for debugging.
  100.    
  101.  ! AddModule         modules/standard/mod_info.o
  102.    
  103.    ## mod_include translates server-side include (SSI) statements in text files.
  104.    ## mod_autoindex handles requests for directories which have no index file
  105.  diff -c --exclude=*.[oa] --recursive apache_1.3b3/src/main/util_script.c a13b3/src/main/util_script.c
  106.  *** apache_1.3b3/src/main/util_script.c        Sun Nov 16 08:45:22 1997
  107.  --- a13b3/src/main/util_script.c       Fri Feb  6 08:47:03 1998
  108.  ***************
  109.  *** 559,567 ****
  110.    #endif
  111.    
  112.    
  113.  ! API_EXPORT(int) call_exec(request_rec *r, char *argv0, char **env, int shellcmd)
  114.    {
  115.        int pid = 0;
  116.    #if defined(RLIMIT_CPU)  || defined(RLIMIT_NPROC) || \
  117.        defined(RLIMIT_DATA) || defined(RLIMIT_VMEM)
  118.    
  119.  --- 559,569 ----
  120.    #endif
  121.    
  122.    
  123.  ! API_EXPORT(int) call_exec(request_rec *r, char *argv0, char **env, int shellcmd, uid_t req_uid, gid_t req_gid)
  124.    {
  125.        int pid = 0;
  126.  +     int change_ids = 0; /* Fork suexec or (by default) the target command? */
  127.  +     char *execuser, *grpname; /* Only used if change_ids gets set.  Ick. */
  128.    #if defined(RLIMIT_CPU)  || defined(RLIMIT_NPROC) || \
  129.        defined(RLIMIT_DATA) || defined(RLIMIT_VMEM)
  130.    
  131.  ***************
  132.  *** 778,840 ****
  133.         return (pid);
  134.        }
  135.    #else
  136.  !     if (suexec_enabled &&
  137.  !      ((r->server->server_uid != user_id) ||
  138.  !       (r->server->server_gid != group_id) ||
  139.  !       (!strncmp("/~", r->uri, 2)))) {
  140.  ! 
  141.  !      char *execuser, *grpname;
  142.  !      struct passwd *pw;
  143.  !      struct group *gr;
  144.  ! 
  145.  !      if (!strncmp("/~", r->uri, 2)) {
  146.  !          gid_t user_gid;
  147.  !          char *username = pstrdup(r->pool, r->uri + 2);
  148.  !          int pos = ind(username, '/');
  149.    
  150.  !          if (pos >= 0)
  151.  !              username[pos] = '\0';
  152.    
  153.  !          if ((pw = getpwnam(username)) == NULL) {
  154.                 aplog_error(APLOG_MARK, APLOG_ERR, r->server,
  155.  !                          "getpwnam: invalid username %s", username);
  156.  !              return (pid);
  157.  !          }
  158.  !          execuser = pstrcat(r->pool, "~", pw->pw_name, NULL);
  159.  !          user_gid = pw->pw_gid;
  160.  ! 
  161.  !          if ((gr = getgrgid(user_gid)) == NULL) {
  162.  !              if ((grpname = palloc(r->pool, 16)) == NULL)
  163.  !                  return (pid);
  164.  !              else
  165.  !                  ap_snprintf(grpname, 16, "%ld", (long) user_gid);
  166.  !          }
  167.  !          else
  168.  !              grpname = gr->gr_name;
  169.  !      }
  170.  !      else {
  171.  !          if ((pw = getpwuid(r->server->server_uid)) == NULL) {
  172.  !              aplog_error(APLOG_MARK, APLOG_ERR, r->server,
  173.  !                          "getpwuid: invalid userid %ld",
  174.  !                          (long) r->server->server_uid);
  175.                 return (pid);
  176.             }
  177.             execuser = pstrdup(r->pool, pw->pw_name);
  178.    
  179.  !          if ((gr = getgrgid(r->server->server_gid)) == NULL) {
  180.                 aplog_error(APLOG_MARK, APLOG_ERR, r->server,
  181.  !                          "getgrgid: invalid groupid %ld",
  182.  !                          (long) r->server->server_gid);
  183.                 return (pid);
  184.             }
  185.  !          grpname = gr->gr_name;
  186.         }
  187.    
  188.         if (shellcmd)
  189.  !          execle(SUEXEC_BIN, SUEXEC_BIN, execuser, grpname, argv0, NULL, env);
  190.    
  191.         else if ((!r->args) || (!r->args[0]) || (ind(r->args, '=') >= 0))
  192.  !          execle(SUEXEC_BIN, SUEXEC_BIN, execuser, grpname, argv0, NULL, env);
  193.    
  194.         else {
  195.             execve(SUEXEC_BIN,
  196.  --- 780,849 ----
  197.         return (pid);
  198.        }
  199.    #else
  200.  !     if (suexec_enabled) {
  201.  !      uid_t target_uid;
  202.  !      gid_t target_gid;
  203.  ! 
  204.  !      if (req_uid > 0 || req_gid > 0) {
  205.  !          /*
  206.  !          *  In the cgi module, the user id and group id are requested
  207.  !          *  independently, so provide sensible defaults in case one
  208.  !          *  is not specified.
  209.  !          */
  210.  !          target_uid = req_uid > 0 ? req_uid : r->server->server_uid;
  211.  !          target_gid = req_gid > 0 ? req_gid : r->server->server_gid;
  212.  !      } else if (strncmp("/~", r->uri, 2) == 0) {
  213.  !             struct passwd *pw;
  214.  !             char *username = pstrdup(r->pool, r->uri + 2);
  215.  !             int pos = ind(username, '/');
  216.  ! 
  217.  !             if (pos >= 0)
  218.  !                 username[pos] = '\0';
  219.  ! 
  220.  !             if ((pw = getpwnam(username)) == NULL) {
  221.  !                 aplog_error(APLOG_MARK, APLOG_ERR, r->server,
  222.  !                             "getpwnam: invalid username %s", username);
  223.  !                 exit (0);
  224.  !             }
  225.  !             target_uid = pw->pw_uid;
  226.  !             target_gid = pw->pw_gid;
  227.  !      } else {
  228.  !          target_uid = r->server->server_uid;
  229.  !          target_gid = r->server->server_gid;
  230.  !      }
  231.  ! 
  232.  !      if (target_uid != user_id || target_gid != group_id) {
  233.  !          struct passwd *pw;
  234.  !          struct group *gr;
  235.    
  236.  !          change_ids = 1;
  237.    
  238.  !          if ((pw = getpwuid(target_uid)) == NULL) {
  239.                 aplog_error(APLOG_MARK, APLOG_ERR, r->server,
  240.  !                          "getpwuid: invalid userid %ld",
  241.  !                          (long) target_uid);
  242.                 return (pid);
  243.             }
  244.             execuser = pstrdup(r->pool, pw->pw_name);
  245.    
  246.  !          if ((gr = getgrgid(target_gid)) == NULL) {
  247.                 aplog_error(APLOG_MARK, APLOG_ERR, r->server,
  248.  !                          "getgrgid: invalid groupid %ld",
  249.  !                          (long) target_gid);
  250.                 return (pid);
  251.             }
  252.  !          grpname = pstrdup(r->pool, gr->gr_name);
  253.         }
  254.  +     }
  255.    
  256.  +     if (change_ids) {
  257.         if (shellcmd)
  258.  !          execle(SUEXEC_BIN,
  259.  !                 SUEXEC_BIN, execuser, grpname, argv0, NULL, env);
  260.    
  261.         else if ((!r->args) || (!r->args[0]) || (ind(r->args, '=') >= 0))
  262.  !          execle(SUEXEC_BIN,
  263.  !                 SUEXEC_BIN, execuser, grpname, argv0, NULL, env);
  264.    
  265.         else {
  266.             execve(SUEXEC_BIN,
  267.  ***************
  268.  *** 842,849 ****
  269.                                argv0, r->args),
  270.                    env);
  271.         }
  272.  !     }
  273.  !     else {
  274.         if (shellcmd)
  275.             execle(SHELL_PATH, SHELL_PATH, "-c", argv0, NULL, env);
  276.    
  277.  --- 851,857 ----
  278.                                argv0, r->args),
  279.                    env);
  280.         }
  281.  !     } else {
  282.         if (shellcmd)
  283.             execle(SHELL_PATH, SHELL_PATH, "-c", argv0, NULL, env);
  284.    
  285.  diff -c --exclude=*.[oa] --recursive apache_1.3b3/src/main/util_script.h a13b3/src/main/util_script.h
  286.  *** apache_1.3b3/src/main/util_script.h        Wed Oct 22 14:29:53 1997
  287.  --- a13b3/src/main/util_script.h       Fri Feb  6 08:48:53 1998
  288.  ***************
  289.  *** 67,70 ****
  290.    API_EXPORT(int) scan_script_header_err(request_rec *r, FILE *f, char *buffer);
  291.    API_EXPORT(int) scan_script_header_err_buff(request_rec *r, BUFF *f, char *buffer);
  292.    API_EXPORT(void) send_size(size_t size, request_rec *r);
  293.  ! API_EXPORT(int) call_exec(request_rec *r, char *argv0, char **env, int shellcmd);
  294.  --- 67,70 ----
  295.    API_EXPORT(int) scan_script_header_err(request_rec *r, FILE *f, char *buffer);
  296.    API_EXPORT(int) scan_script_header_err_buff(request_rec *r, BUFF *f, char *buffer);
  297.    API_EXPORT(void) send_size(size_t size, request_rec *r);
  298.  ! API_EXPORT(int) call_exec(request_rec *r, char *argv0, char **env, int shellcmd, uid_t request_uid, gid_t request_gid);
  299.  Only in a13b3/src/modules: CVS
  300.  Only in a13b3/src/modules: Makefile
  301.  Only in a13b3/src/modules/standard: CVS
  302.  Only in a13b3/src/modules/standard: Makefile
  303.  diff -c --exclude=*.[oa] --recursive apache_1.3b3/src/modules/standard/mod_cgi.c a13b3/src/modules/standard/mod_cgi.c
  304.  *** apache_1.3b3/src/modules/standard/mod_cgi.c        Fri Nov  7 18:20:02 1997
  305.  --- a13b3/src/modules/standard/mod_cgi.c       Fri Feb  6 08:47:43 1998
  306.  ***************
  307.  *** 99,104 ****
  308.  --- 99,111 ----
  309.        int bufbytes;
  310.    } cgi_server_conf;
  311.    
  312.  + typedef struct
  313.  + {
  314.  +     uid_t userid;
  315.  +     gid_t groupid;
  316.  + } cgi_dir_conf;
  317.  + 
  318.  + 
  319.    static void *create_cgi_config(pool *p, server_rec *s)
  320.    {
  321.        cgi_server_conf *c =
  322.  ***************
  323.  *** 118,123 ****
  324.  --- 125,144 ----
  325.        return overrides->logname ? overrides : base;
  326.    }
  327.    
  328.  + void *create_cgi_dir_config (pool *p, char *dummy)
  329.  + {
  330.  +     cgi_dir_conf *c = (cgi_dir_conf *) palloc (p,sizeof(cgi_dir_conf));
  331.  + 
  332.  + #   ifdef DEBUG_SUGID_CONFIG
  333.  +       fprintf(stderr,"Create dir config: %s\n",dummy ? dummy : "<NULL>");
  334.  + #   endif
  335.  + 
  336.  +     c->userid = 0;
  337.  +     c->groupid = 0;
  338.  + 
  339.  +     return (void *) c;
  340.  + }
  341.  + 
  342.    static const char *set_scriptlog(cmd_parms *cmd, void *dummy, char *arg)
  343.    {
  344.        server_rec *s = cmd->server;
  345.  ***************
  346.  *** 148,153 ****
  347.  --- 169,219 ----
  348.        return NULL;
  349.    }
  350.    
  351.  + const char *set_userid(cmd_parms *cmd,cgi_dir_conf *conf, char *user) {
  352.  +     struct passwd *ent;
  353.  + 
  354.  + #   ifdef DEBUG_SUGID_CONFIG
  355.  +       fprintf(stderr,"Add UserId: %s\n",user);
  356.  +       fprintf(stderr,"\tPrevious value: %d\n",conf->userid);
  357.  +       fprintf(stderr,"\tConfig File   : %s\n",cmd->config_file->name);
  358.  +       fprintf(stderr,"\tPath    : %s\n",
  359.  +         cmd->path ? cmd->path : "<NULL>");
  360.  + #   endif
  361.  + 
  362.  +     if (user[0] == '#') {
  363.  +       conf->userid = atoi(&user[1]);
  364.  +       return NULL;
  365.  +     } else if (!(ent = getpwnam(user))) {
  366.  +       return "Invalid User Name";
  367.  +     } else {
  368.  +       conf->userid = ent->pw_uid;
  369.  +       return NULL;
  370.  +     }
  371.  + }
  372.  + 
  373.  + const char *set_groupid(cmd_parms *cmd,cgi_dir_conf *conf, char *grp)
  374.  + {
  375.  +     struct group *ent;
  376.  + 
  377.  + #   ifdef DEBUG_SUGID_CONFIG
  378.  +       fprintf(stderr,"Add GroupId: %s\n",grp);
  379.  +       fprintf(stderr,"\tPrevious value: %d\n",conf->groupid);
  380.  +       fprintf(stderr,"\tConfig File   : %s\n",cmd->config_file->name);
  381.  +       fprintf(stderr,"\tPath    : %s\n",
  382.  +         cmd->path ? cmd->path : "<NULL>");
  383.  + #   endif
  384.  + 
  385.  +     if (grp[0] == '#') {
  386.  +       conf->groupid = atoi(&grp[1]);
  387.  +       return NULL;
  388.  +     } else if (!(ent = getgrnam(grp))) {
  389.  +       return "Invalid Group Name";
  390.  +     } else {
  391.  +       conf->groupid = ent->gr_gid;
  392.  +       return NULL;
  393.  +     }
  394.  + }
  395.  + 
  396.    static command_rec cgi_cmds[] =
  397.    {
  398.        {"ScriptLog", set_scriptlog, NULL, RSRC_CONF, TAKE1,
  399.  ***************
  400.  *** 156,161 ****
  401.  --- 222,231 ----
  402.         "the maximum length (in bytes) of the script debug log"},
  403.        {"ScriptLogBuffer", set_scriptlog_buffer, NULL, RSRC_CONF, TAKE1,
  404.         "the maximum size (in bytes) to record of a POST request"},
  405.  +     { "UserId", set_userid, NULL, RSRC_CONF | ACCESS_CONF, TAKE1,
  406.  +       "a UserName or #UserId"},
  407.  +     { "GroupId", set_groupid, NULL, RSRC_CONF | ACCESS_CONF, TAKE1,
  408.  +       "a GroupName or #GroupId"},
  409.        {NULL}
  410.    };
  411.    
  412.  ***************
  413.  *** 281,286 ****
  414.  --- 351,361 ----
  415.        request_rec *r = cld->r;
  416.        char *argv0 = cld->argv0;
  417.        int child_pid;
  418.  +     cgi_dir_conf *conf = (cgi_dir_conf *)
  419.  +       get_module_config (r->per_dir_config,&cgi_module);
  420.  +     uid_t request_uid = 0;
  421.  +     gid_t request_gid = 0;
  422.  + 
  423.    
  424.    #ifdef DEBUG_CGI
  425.    #ifdef __EMX__
  426.  ***************
  427.  *** 296,303 ****
  428.    
  429.        RAISE_SIGSTOP(CGI_CHILD);
  430.    #ifdef DEBUG_CGI
  431.  !     fprintf(dbg, "Attempting to exec %s as %sCGI child (argv0 = %s)\n",
  432.  !          r->filename, nph ? "NPH " : "", argv0);
  433.    #endif
  434.    
  435.        add_cgi_vars(r);
  436.  --- 371,378 ----
  437.    
  438.        RAISE_SIGSTOP(CGI_CHILD);
  439.    #ifdef DEBUG_CGI
  440.  !     fprintf(dbg, "%s (uri=<%s>) as CGI child (argv0 = %s)\n",
  441.  !          r->filename, r->uri, argv0);
  442.    #endif
  443.    
  444.        add_cgi_vars(r);
  445.  ***************
  446.  *** 309,314 ****
  447.  --- 384,417 ----
  448.         fprintf(dbg, "'%s'\n", env[i]);
  449.    #endif
  450.    
  451.  + 
  452.  + #if ! defined(__EMX__) && ! defined(WIN32)
  453.  +     /*
  454.  +     * See under which uid we will run the request.
  455.  +     * If there's a UserId or GroupId available, use those.
  456.  +     * The request_[ug]id args to call_exec are set non-zero
  457.  +     * to override the per-virtual or overall-server defaults.
  458.  +     * If they're left 0, 0, they will be set (in main/util_script.c)
  459.  +     * to a user's uid/gid if this is a ~user uri and suexec
  460.  +     * is enabled.
  461.  +     */
  462.  +     if (suexec_enabled) {
  463.  +      if (conf) {
  464.  +          if (conf->userid > 0) {
  465.  +              request_uid = conf->userid;
  466.  +          }
  467.  +          if (conf->groupid > 0) {
  468.  +              request_gid = conf->groupid;
  469.  +          }
  470.  +      }
  471.  + #ifdef DEBUG_SUGID_CONFIG
  472.  +      fprintf(dbg, "sugid parameters: request_uid=%d request_gid=%d\n",
  473.  +              request_uid, request_gid);
  474.  + #endif
  475.  + 
  476.  +     }
  477.  + #endif
  478.  + 
  479.        chdir_file(r->filename);
  480.        if (!cld->debug)
  481.         error_log2stderr(r->server);
  482.  ***************
  483.  *** 319,325 ****
  484.    
  485.        cleanup_for_exec();
  486.    
  487.  !     child_pid = call_exec(r, argv0, env, 0);
  488.    #ifdef WIN32
  489.        return (child_pid);
  490.    #else
  491.  --- 422,428 ----
  492.    
  493.        cleanup_for_exec();
  494.    
  495.  !     child_pid = call_exec(r, argv0, env, 0, request_uid, request_gid);
  496.    #ifdef WIN32
  497.        return (child_pid);
  498.    #else
  499.  ***************
  500.  *** 551,557 ****
  501.    {
  502.        STANDARD_MODULE_STUFF,
  503.        NULL,                    /* initializer */
  504.  !     NULL,                    /* dir config creater */
  505.        NULL,                    /* dir merger --- default is to override */
  506.        create_cgi_config,               /* server config */
  507.        merge_cgi_config,                /* merge server config */
  508.  --- 654,660 ----
  509.    {
  510.        STANDARD_MODULE_STUFF,
  511.        NULL,                    /* initializer */
  512.  !     create_cgi_dir_config,   /* dir config creater */
  513.        NULL,                    /* dir merger --- default is to override */
  514.        create_cgi_config,               /* server config */
  515.        merge_cgi_config,                /* merge server config */
  516.  diff -c --exclude=*.[oa] --recursive apache_1.3b3/src/modules/standard/mod_include.c a13b3/src/modules/standard/mod_include.c
  517.  *** apache_1.3b3/src/modules/standard/mod_include.c    Sun Nov  9 13:40:34 1997
  518.  --- a13b3/src/modules/standard/mod_include.c   Fri Feb  6 08:45:57 1998
  519.  ***************
  520.  *** 733,739 ****
  521.    #endif
  522.        cleanup_for_exec();
  523.        /* set shellcmd flag to pass arg to SHELL_PATH */
  524.  !     child_pid = call_exec(r, s, create_environment(r->pool, env), 1);
  525.    #ifdef WIN32
  526.        return (child_pid);
  527.    #else
  528.  --- 733,739 ----
  529.    #endif
  530.        cleanup_for_exec();
  531.        /* set shellcmd flag to pass arg to SHELL_PATH */
  532.  !     child_pid = call_exec(r, s, create_environment(r->pool, env), 1, 0, 0);
  533.    #ifdef WIN32
  534.        return (child_pid);
  535.    #else
  536.  diff -c --exclude=*.[oa] --recursive apache_1.3b3/src/support/suexec.c a13b3/src/support/suexec.c
  537.  *** apache_1.3b3/src/support/suexec.c  Wed Oct 22 14:30:46 1997
  538.  --- a13b3/src/support/suexec.c Fri Feb  6 07:43:58 1998
  539.  ***************
  540.  *** 112,117 ****
  541.  --- 112,118 ----
  542.    
  543.    extern char **environ;
  544.    static FILE *log;
  545.  + static int debug_flag = 1;
  546.    
  547.    char *safe_env_lst[] =
  548.    {
  549.  ***************
  550.  *** 155,160 ****
  551.  --- 156,174 ----
  552.        NULL
  553.    };
  554.    
  555.  + typedef struct dir_list {
  556.  +     char *dir;
  557.  +     struct dir_list *next;
  558.  + } dir_list;
  559.  + 
  560.  + typedef struct {
  561.  +     int user_dir_on;
  562.  +     char *user_dir_suff;
  563.  +     dir_list *dirs[2];
  564.  + } suexec_attribs;
  565.  + 
  566.  + suexec_attribs attr;
  567.  + 
  568.    
  569.    static void err_output(const char *fmt, va_list ap)
  570.    {
  571.  ***************
  572.  *** 194,199 ****
  573.  --- 208,226 ----
  574.        return;
  575.    }
  576.    
  577.  + void debug (const char *fmt,...) {
  578.  + #ifdef LOG_EXEC
  579.  +     if (debug_flag) {
  580.  +      va_list ap;
  581.  + 
  582.  +      va_start(ap, fmt);
  583.  +      err_output(fmt, ap);
  584.  +      va_end(ap);
  585.  +     }
  586.  + #endif /* LOG_EXEC */
  587.  +     return;
  588.  + }
  589.  + 
  590.    void clean_env()
  591.    {
  592.        char pathbuf[512];
  593.  ***************
  594.  *** 231,236 ****
  595.  --- 258,443 ----
  596.        environ = cleanenv;
  597.    }
  598.    
  599.  + void load_setup_file () {
  600.  +     char pathbuf[512];
  601.  +     char linebuf[512];
  602.  +     int linecount = 0;
  603.  +     FILE *fp;
  604.  +     struct stat dir_info;    /* directives directory info holder */
  605.  +     struct stat file_info;   /* directives file info holder */
  606.  + 
  607.  +     attr.user_dir_on = 0;
  608.  +     attr.user_dir_suff = 0;
  609.  +     attr.dirs[0] = attr.dirs[1] = 0;
  610.  + 
  611.  +     /*
  612.  +      * Stat the cwd and verify it is a directory, or error out.
  613.  +      */
  614.  +     if (((lstat(DIRECTIVE_DIR, &dir_info)) != 0)
  615.  +          || !(S_ISDIR(dir_info.st_mode))) {
  616.  +      log_err("cannot stat directory: (%s)\n", DIRECTIVE_DIR);
  617.  +      exit(130);
  618.  +     }
  619.  + 
  620.  +     /*
  621.  +      * Error out if DIRECTIVE_DIR is writable by others.
  622.  +      */
  623.  +     if ((dir_info.st_mode & S_IWOTH) || (dir_info.st_mode & S_IWGRP)) {
  624.  +      log_err("directory is writable by others: (%s)\n", DIRECTIVE_DIR);
  625.  +      exit(131);
  626.  +     }
  627.  + 
  628.  +     strcpy (pathbuf, DIRECTIVE_DIR);
  629.  +     strcat (pathbuf, "/");
  630.  +     strcat (pathbuf, DIRECTIVE_FILENAME);
  631.  + 
  632.  +     /*
  633.  +      * Error out if we cannot stat the directives file.
  634.  +      */
  635.  +     if (((lstat(pathbuf, &file_info)) != 0) || (S_ISLNK(file_info.st_mode))) {
  636.  +      log_err("cannot stat directives file: (%s)\n", pathbuf);
  637.  +      exit(132);
  638.  +     }
  639.  + 
  640.  +     /*
  641.  +      * Error out if the directives file is writable by others.
  642.  +      */
  643.  +     if ((file_info.st_mode & S_IWOTH) || (file_info.st_mode & S_IWGRP)) {
  644.  +      log_err("file is writable by others: (%s)\n", pathbuf);
  645.  +      exit(133);
  646.  +     }
  647.  + 
  648.  +     if ((fp = fopen (pathbuf, "r")) == 0) {
  649.  +      log_err("failed to open directives file: (%s)\n", pathbuf);
  650.  +      exit(134);
  651.  +     }
  652.  + 
  653.  +     while (fgets (linebuf, 512, fp)) {
  654.  +         char *key = linebuf;
  655.  +      char *val;
  656.  +      char *cp;
  657.  +      int len = strlen (linebuf);
  658.  + 
  659.  +      ++linecount;
  660.  + 
  661.  +      /*
  662.  +      *  Get rid of that trailing newline!
  663.  +      *  If there isn't one, then the line is too long.
  664.  +      */
  665.  +      if (linebuf [len - 1] == '\n') {
  666.  +          linebuf [len - 1] = '\0';
  667.  +      } else {
  668.  +          log_err("line %d: line too long: (%s)\n", linecount, linebuf);
  669.  +          exit(135);
  670.  +      }
  671.  + 
  672.  +         /*
  673.  +      *  Directives may not extend across newline boundaries.
  674.  +      *  Honor '#' signs as comment delimiters.
  675.  +      *
  676.  +      *  First strip off all leading white space,
  677.  +      *  determine the directive, isolate the value + trailing stuff,
  678.  +      *  then step back from the tail getting rid of trailing
  679.  +      *  comments and white space.
  680.  +      */
  681.  +      while (*key && isspace (*key)) {
  682.  +          ++key;
  683.  +      }
  684.  +      if (! *key || *key == '#') {
  685.  +          continue;
  686.  +      }
  687.  +      debug ("beginning of key: <%s>\n", key);
  688.  + 
  689.  +      /*
  690.  +      * That should be the beginning of the directive.
  691.  +      * Find the first whitespace or ':' and set it null
  692.  +      * to isolate the directive.
  693.  +      */
  694.  +      cp = key;
  695.  +      while (*cp && *cp != ':' && ! isspace (*cp)) {
  696.  +          ++cp;
  697.  +      }
  698.  +      if (! *cp) {
  699.  +          log_err("line %d: end of directive not found: (%s)\n",
  700.  +              linecount, linebuf);
  701.  +          exit(136);
  702.  +      }
  703.  +      *cp = '\0';
  704.  +      debug ("finished key: <%s>\n", key);
  705.  + 
  706.  +      /*
  707.  +      *  Now find the beginning of the value, which is the
  708.  +      * next non-null, non-colon character.
  709.  +      */
  710.  +      ++cp;
  711.  +      while (*cp && (*cp == ':' || isspace (*cp))) {
  712.  +          ++cp;
  713.  +      }
  714.  +      if (! *cp) {
  715.  +          log_err("line %d: beginning of value not found: (%s)\n",
  716.  +              linecount, linebuf);
  717.  +          exit(137);
  718.  +      }
  719.  +      val = cp;
  720.  +      debug ("beginning of value: <%s>\n", val);
  721.  + 
  722.  +      /*
  723.  +      *  If there's a comment, get rid of it.
  724.  +      *  Then strip off trailing whitespace.
  725.  +      */
  726.  +      if (cp = strchr (val, '#')) {
  727.  +          *cp = '\0';
  728.  +      }
  729.  +      cp = val + strlen (val) - 1;
  730.  +      while (cp > val && isspace (*cp)) {
  731.  +          *cp = '\0';
  732.  +          --cp;
  733.  +      }
  734.  +      debug ("val w/o trailing comments: <%s>\n", val);
  735.  + 
  736.  +      /*
  737.  +      *  Ok, it's a take.
  738.  +      *  Directory directives get tacked onto the
  739.  +      *  dirs list; the others go to their specific
  740.  +      *  suexec_attribs field.
  741.  +      */
  742.  + 
  743.  +      if (strcmp (key, "Directory") == 0) {
  744.  +          dir_list *dl;
  745.  +          if (! (dl = malloc (sizeof (dir_list)))) {
  746.  +              log_err("line %d: out of memory\n", linecount);
  747.  +              exit(138);
  748.  +          }
  749.  +          dl->next = 0;
  750.  +          if (! (dl->dir = strdup (val))) {
  751.  +              log_err("line %d: out of memory\n", linecount);
  752.  +              exit(139);
  753.  +          }
  754.  +          if (! attr.dirs[0]) {
  755.  +              attr.dirs[0] = attr.dirs[1] = dl;
  756.  +          } else {
  757.  +              attr.dirs[1]->next = dl;
  758.  +              attr.dirs[1] = dl;
  759.  +          }
  760.  +      } else if (strcmp (key, "UserDirectories") == 0) {
  761.  +          if (strcmp (val, "on") == 0) {
  762.  +              attr.user_dir_on = 1;
  763.  +          } else {
  764.  +              attr.user_dir_on = 0;
  765.  +          }
  766.  +      } else if (strcmp (key, "UserDirSuffix") == 0) {
  767.  +          if (! (attr.user_dir_suff = strdup (val))) {
  768.  +              log_err("line %d: out of memory\n", linecount);
  769.  +              exit(140);
  770.  +          }
  771.  +      } else {
  772.  +          log_err("line %d: unknown directive: (%s)\n",
  773.  +              linecount, key);
  774.  +          exit(141);
  775.  +      }
  776.  +     }
  777.  + }
  778.  + 
  779.    int main(int argc, char *argv[])
  780.    {
  781.        int userdir = 0;         /* ~userdir flag             */
  782.  ***************
  783.  *** 249,254 ****
  784.  --- 456,462 ----
  785.        struct group *gr;                /* group entry holder        */
  786.        struct stat dir_info;    /* directory info holder     */
  787.        struct stat prg_info;    /* program info holder       */
  788.  +     int dirfound = 0;
  789.    
  790.    
  791.    
  792.  ***************
  793.  *** 264,269 ****
  794.  --- 472,479 ----
  795.        target_uname = argv[1];
  796.        target_gname = argv[2];
  797.        cmd = argv[3];
  798.  +     debug ("uname=<%s> gname=<%s> cmd=<%s>\n",
  799.  +         target_uname, target_gname, cmd);
  800.    
  801.        /*
  802.         * Check existence/validity of the UID of the user
  803.  ***************
  804.  *** 286,313 ****
  805.        }
  806.    
  807.        /*
  808.  !      * Check for a leading '/' (absolute path) in the command to be executed,
  809.  !      * or attempts to back up out of the current directory,
  810.         * to protect against attacks.  If any are
  811.         * found, error out.  Naughty naughty crackers.
  812.         */
  813.  !     if ((cmd[0] == '/') || (!strncmp(cmd, "../", 3))
  814.         || (strstr(cmd, "/../") != NULL)) {
  815.            log_err("invalid command (%s)\n", cmd);
  816.         exit(104);
  817.        }
  818.    
  819.        /*
  820.  -      * Check to see if this is a ~userdir request.  If
  821.  -      * so, set the flag, and remove the '~' from the
  822.  -      * target username.
  823.  -      */
  824.  -     if (!strncmp("~", target_uname, 1)) {
  825.  -      target_uname++;
  826.  -      userdir = 1;
  827.  -     }
  828.  - 
  829.  -     /*
  830.         * Error out if the target username is invalid.
  831.         */
  832.        if ((pw = getpwnam(target_uname)) == NULL) {
  833.  --- 496,512 ----
  834.        }
  835.    
  836.        /*
  837.  !      * Check for attempts to back up out of the current directory,
  838.         * to protect against attacks.  If any are
  839.         * found, error out.  Naughty naughty crackers.
  840.         */
  841.  !     if ((!strncmp(cmd, "../", 3))
  842.         || (strstr(cmd, "/../") != NULL)) {
  843.            log_err("invalid command (%s)\n", cmd);
  844.         exit(104);
  845.        }
  846.    
  847.        /*
  848.         * Error out if the target username is invalid.
  849.         */
  850.        if ((pw = getpwnam(target_uname)) == NULL) {
  851.  ***************
  852.  *** 372,378 ****
  853.         * and setgid() to the target group. If unsuccessful, error out.
  854.         */
  855.        if (((setgid(gid)) != 0) || (initgroups(actual_uname, gid) != 0)) {
  856.  !      log_err("failed to setgid (%ld: %s/%s)\n", gid, cwd, cmd);
  857.         exit(109);
  858.        }
  859.    
  860.  --- 571,577 ----
  861.         * and setgid() to the target group. If unsuccessful, error out.
  862.         */
  863.        if (((setgid(gid)) != 0) || (initgroups(actual_uname, gid) != 0)) {
  864.  !      log_err("failed to setgid (%ld: %s)\n", gid, cmd);
  865.         exit(109);
  866.        }
  867.    
  868.  ***************
  869.  *** 380,394 ****
  870.         * setuid() to the target user.  Error out on fail.
  871.         */
  872.        if ((setuid(uid)) != 0) {
  873.  !      log_err("failed to setuid (%ld: %s/%s)\n", uid, cwd, cmd);
  874.         exit(110);
  875.        }
  876.    
  877.        /*
  878.  !      * Get the current working directory, as well as the proper
  879.  !      * document root (dependant upon whether or not it is a
  880.  !      * ~userdir request).  Error out if we cannot get either one,
  881.  !      * or if the current working directory is not in the docroot.
  882.         * Use chdir()s and getcwd()s to avoid problems with symlinked
  883.         * directories.  Yuck.
  884.         */
  885.  --- 579,590 ----
  886.         * setuid() to the target user.  Error out on fail.
  887.         */
  888.        if ((setuid(uid)) != 0) {
  889.  !      log_err("failed to setuid (%ld: %s)\n", uid, cmd);
  890.         exit(110);
  891.        }
  892.    
  893.        /*
  894.  !      * Get the current working directory.
  895.         * Use chdir()s and getcwd()s to avoid problems with symlinked
  896.         * directories.  Yuck.
  897.         */
  898.  ***************
  899.  *** 396,424 ****
  900.         log_err("cannot get current working directory\n");
  901.         exit(111);
  902.        }
  903.  ! 
  904.  !     if (userdir) {
  905.  !      if (((chdir(target_homedir)) != 0) ||
  906.  !          ((chdir(USERDIR_SUFFIX)) != 0) ||
  907.  !          ((getcwd(dwd, AP_MAXPATH)) == NULL) ||
  908.  !          ((chdir(cwd)) != 0)) {
  909.  !          log_err("cannot get docroot information (%s)\n", target_homedir);
  910.  !          exit(112);
  911.  !      }
  912.  !     }
  913.  !     else {
  914.  !      if (((chdir(DOC_ROOT)) != 0) ||
  915.  !          ((getcwd(dwd, AP_MAXPATH)) == NULL) ||
  916.  !          ((chdir(cwd)) != 0)) {
  917.  !          log_err("cannot get docroot information (%s)\n", DOC_ROOT);
  918.  !          exit(113);
  919.  !      }
  920.  !     }
  921.  ! 
  922.  !     if ((strncmp(cwd, dwd, strlen(dwd))) != 0) {
  923.  !      log_err("command not in docroot (%s/%s)\n", cwd, cmd);
  924.  !      exit(114);
  925.  !     }
  926.    
  927.        /*
  928.         * Stat the cwd and verify it is a directory, or error out.
  929.  --- 592,598 ----
  930.         log_err("cannot get current working directory\n");
  931.         exit(111);
  932.        }
  933.  !     debug ("cwd=<%s>\n", cwd);
  934.    
  935.        /*
  936.         * Stat the cwd and verify it is a directory, or error out.
  937.  ***************
  938.  *** 434,439 ****
  939.  --- 608,664 ----
  940.        if ((dir_info.st_mode & S_IWOTH) || (dir_info.st_mode & S_IWGRP)) {
  941.         log_err("directory is writable by others: (%s)\n", cwd);
  942.         exit(116);
  943.  +     }
  944.  + 
  945.  +     /*
  946.  +      * Load the setup file as late as possible.
  947.  +      */
  948.  +     load_setup_file ();
  949.  + 
  950.  +     /*
  951.  +     *  Search the directory lists to determine if the command
  952.  +     *  is in a legal directory.  The current directory is the
  953.  +     *  directory where the command is supposed to be.  If the
  954.  +     *  current directory is the same as, or a subdirectory of,
  955.  +     *  one of the list directories, then it's ok to exec it.
  956.  +     */
  957.  + 
  958.  +     if (attr.user_dir_on) {
  959.  +      char td[AP_MAXPATH];    /* test directory */
  960.  +         /*
  961.  +      *  Construct the home directory for the specified
  962.  +      *  user.
  963.  +      */
  964.  +      strcpy (td, target_homedir);
  965.  +      strcat (td, "/");
  966.  +      strcat (td, attr.user_dir_suff);
  967.  +      debug ("check cwd=<%s> against user dir <%s>\n", cwd, td);
  968.  +      if (strncmp (cwd, td, strlen (td)) == 0) {
  969.  +          ++dirfound;
  970.  +      }
  971.  +     }
  972.  +     if (! dirfound) {
  973.  +         dir_list *dl = attr.dirs[0];
  974.  +         /*
  975.  +      *  Step through the directories which suexec is
  976.  +      *  allowed to work in, looking for a prefix match
  977.  +      *  with the current directory.
  978.  +      */
  979.  +      while (dl) {
  980.  +          debug ("check cwd=<%s> against <%s>\n", cwd, dl->dir);
  981.  +          if (strncmp (cwd, dl->dir, strlen (dl->dir)) == 0) {
  982.  +              ++dirfound;
  983.  +              break;
  984.  +          } else {
  985.  +              dl = dl->next;
  986.  +          }
  987.  +      }
  988.  +     }
  989.  + 
  990.  +     if (! dirfound) {
  991.  +      log_err("unable to validate directory (%s) for user (%s), "
  992.  +           "group (%s), cmd (%s)\n", cwd, target_uname, target_gname, cmd);
  993.  +      exit(121);
  994.        }
  995.    
  996.        /*
  997.  diff -c --exclude=*.[oa] --recursive apache_1.3b3/src/support/suexec.h a13b3/src/support/suexec.h
  998.  *** apache_1.3b3/src/support/suexec.h  Sat Oct 25 16:35:20 1997
  999.  --- a13b3/src/support/suexec.h Wed Feb  4 23:45:28 1998
  1000.  ***************
  1001.  *** 65,71 ****
  1002.     *               this program.
  1003.     */
  1004.    #ifndef HTTPD_USER
  1005.  ! #define HTTPD_USER "www"
  1006.    #endif
  1007.    
  1008.    /*
  1009.  --- 65,71 ----
  1010.     *               this program.
  1011.     */
  1012.    #ifndef HTTPD_USER
  1013.  ! #define HTTPD_USER "nobody"
  1014.    #endif
  1015.    
  1016.    /*
  1017.  ***************
  1018.  *** 115,121 ****
  1019.     *             debugging purposes.
  1020.     */
  1021.    #ifndef LOG_EXEC
  1022.  ! #define LOG_EXEC "/usr/local/apache/logs/cgi.log"    /* Need me? */
  1023.    #endif
  1024.    
  1025.    /*
  1026.  --- 115,121 ----
  1027.     *             debugging purposes.
  1028.     */
  1029.    #ifndef LOG_EXEC
  1030.  ! #define LOG_EXEC "/users/src/a13b3/logs/cgi.log"     /* Need me? */
  1031.    #endif
  1032.    
  1033.    /*
  1034.  ***************
  1035.  *** 133,138 ****
  1036.  --- 133,154 ----
  1037.     */
  1038.    #ifndef SAFE_PATH
  1039.    #define SAFE_PATH "/usr/local/bin:/usr/bin:/bin"
  1040.  + #endif
  1041.  + 
  1042.  + /*
  1043.  +  * DIRECTIVE_DIR -- Path to the directives file.
  1044.  +  *
  1045.  +  */
  1046.  + #ifndef DIRECTIVE_DIR
  1047.  + #define DIRECTIVE_DIR "/users/src/a13b3/conf"
  1048.  + #endif
  1049.  + 
  1050.  + /*
  1051.  +  * DIRECTIVE_FILENAME -- Name of the directives file.
  1052.  +  *
  1053.  +  */
  1054.  + #ifndef DIRECTIVE_FILENAME
  1055.  + #define DIRECTIVE_FILENAME "suexec.conf"
  1056.    #endif
  1057.    
  1058.    #endif /* _SUEXEC_H */
  1059.  
  1060.