randy       96/12/24 10:28:54

  Modified:    src       util_script.c util_script.h
  Log:
  Change create_argv() to accept variable number of arguments allowing
  it to be used with suexec enabled requests. Fixes problem where
  arguments could not be passed via argv[] to the CGI via suexec.
  Reviewed by: Ben Laurie (not tested), Jason Dour (not tested?)
  
  Revision  Changes    Path
  1.31      +38 -20    apache/src/util_script.c
  
  Index: util_script.c
  ===================================================================
  RCS file: /export/home/cvs/apache/src/util_script.c,v
  retrieving revision 1.30
  retrieving revision 1.31
  diff -C3 -r1.30 -r1.31
  *** util_script.c     1996/12/15 22:06:53     1.30
  --- util_script.c     1996/12/24 18:23:17     1.31
  ***************
  *** 72,95 ****
    #define MALFORMED_MESSAGE "malformed header from script. Bad header="
    #define MALFORMED_HEADER_LENGTH_TO_SHOW 30
    
  ! char **create_argv(pool *p, char *av0, const char *args) {
  !     register int x,n;
        char **av;
  !     char *w;
  ! 
  !     for(x=0,n=2;args[x];x++)
  !         if(args[x] == '+') ++n;
    
  !     av = (char **)palloc(p, (n+1)*sizeof(char *));
        av[0] = av0;
  ! 
  !     for(x=1;x<n;x++) {
  !         w = getword_nulls(p, &args, '+');
  !         unescape_url(w);
  !     av[x] = escape_shell_cmd(p, w);
  !         av[x] = w;
        }
  !     av[n] = NULL;
        return av;
    }
    
  --- 72,111 ----
    #define MALFORMED_MESSAGE "malformed header from script. Bad header="
    #define MALFORMED_HEADER_LENGTH_TO_SHOW 30
    
  ! char **create_argv(request_rec *r, char *av0, ...)
  ! {
  !     int idx, slots;
        char **av;
  !     char *t, *arg;
  !     va_list args;
    
  !     if ((av = (char **)palloc(r->pool, ARG_MAX)) == NULL)
  !     log_unixerr("malloc", NULL, "failed to allocate memory for arg list", 
r->server);
  !     
        av[0] = av0;
  !     idx = 1;
  !     
  !     va_start(args, av0);
  !     while ((arg = va_arg(args, char *)) != NULL) {
  !     if ((t = strtok(arg, "+")) == NULL)
  !         break;
  !     
  !     unescape_url(t);
  !     av[idx] = escape_shell_cmd(r->pool, t);
  !     av[idx] = t;
  !     idx++;
  !     
  !     while ((t = strtok(NULL, "+")) != NULL) {
  !         unescape_url(t);
  !         av[idx] = escape_shell_cmd(r->pool, t);
  !         av[idx] = t;
  !         idx++;
  !     }
  !     va_end(args);
        }
  !     va_end(args);
  ! 
  !     av[idx] = NULL;
        return av;
    }
    
  ***************
  *** 480,486 ****
            execv("CMD.EXE", create_argv_cmd(r->pool, argv0, r->args, 
r->filename));
        }
        else
  !         execv(r->filename, create_argv(r->pool, argv0, r->args));
        }
    #else
        if ( suexec_enabled &&
  --- 496,502 ----
            execv("CMD.EXE", create_argv_cmd(r->pool, argv0, r->args, 
r->filename));
        }
        else
  !         execv(r->filename, create_argv(r, argv0, r->args, NULL));
        }
    #else
        if ( suexec_enabled &&
  ***************
  *** 517,526 ****
    
        else if((!r->args) || (!r->args[0]) || (ind(r->args,'=') >= 0))
            execle(SUEXEC_BIN, SUEXEC_BIN, execuser, gr->gr_name, argv0, NULL, 
env);
  !   
  !     else
  !         execle(SUEXEC_BIN, SUEXEC_BIN, execuser, gr->gr_name,
  !                create_argv(r->pool, argv0, r->args), NULL, env);
        }
        else {
        if (shellcmd) 
  --- 533,544 ----
    
        else if((!r->args) || (!r->args[0]) || (ind(r->args,'=') >= 0))
            execle(SUEXEC_BIN, SUEXEC_BIN, execuser, gr->gr_name, argv0, NULL, 
env);
  ! 
  !     else {
  !         execve(SUEXEC_BIN,
  !                create_argv(r, SUEXEC_BIN, execuser, gr->gr_name, argv0, 
r->args, NULL),
  !                env);
  !     }
        }
        else {
        if (shellcmd) 
  ***************
  *** 530,536 ****
            execle(r->filename, argv0, NULL, env);
    
        else
  !         execve(r->filename, create_argv(r->pool, argv0, r->args), env);
        }
    #endif
    }
  --- 548,554 ----
            execle(r->filename, argv0, NULL, env);
    
        else
  !         execve(r->filename, create_argv(r, argv0, r->args, NULL), env);
        }
    #endif
    }
  
  
  
  1.11      +1 -1      apache/src/util_script.h
  
  Index: util_script.h
  ===================================================================
  RCS file: /export/home/cvs/apache/src/util_script.h,v
  retrieving revision 1.10
  retrieving revision 1.11
  diff -C3 -r1.10 -r1.11
  *** util_script.h     1996/12/09 01:00:40     1.10
  --- util_script.h     1996/12/24 18:23:18     1.11
  ***************
  *** 50,56 ****
     *
     */
    
  ! char **create_argv(pool *p, char *av0, const char *args);
    #ifdef __EMX__
    char **create_argv_cmd(pool *p, char *av0, const char *args, char *path);
    #endif
  --- 50,56 ----
     *
     */
    
  ! char **create_argv(request_rec *r, char *av0, ...);
    #ifdef __EMX__
    char **create_argv_cmd(pool *p, char *av0, const char *args, char *path);
    #endif
  
  
  

  Modified:    support   suexec.c suexec.h
  Log:
  1.  symlinked homedirs will kill ~userdirs.
  
      Fixed by the addition of Jim Jagielski's code that he graciously
      beat over Jason and mine's head but did not get through for me.
      We now dowa chdir() to the DOCROOT be that the built one for
      users, or the compiled in DOCROOT. I've resisted the idea of
      a chdir() here, but I see no other way to make this work in a
      situation where the user's homedir taken from the passwd file
      does not agree with the filesystems true mapping. Looking at
      using realpath() or some other similar solution appears to just
      do a chdir() which was the whole point of avoiding this in the
      past. Since opening and reading a directory as root seems to be
      a read-only operation, I see no security whole here. Comments
      welcome.
  
  2.  initgroups() on Linux 2.0.x clobbers gr->grid.
  
      initgroups() call removed from wrapper code until we can
      establish portable fix here.
  
  3.  CGI command lines paramters problems
  
      Code in call_exec() was not properly passing arguments in argv[]
      if you want to pass arguments via '+' separated URL.
      Taking the hint from Jake Buchholz I have changed create_argv()
      to accept a variable number of parameters. This is the only
      change to the server code and is included first in the
      patch below.
  
  4.  pw-pwdir for "docroot check" still the httpd user's pw record.
  
      Major order problem in wrapper code.
  
  5. strchr() returns a char*, not a char
  
  Revision  Changes    Path
  1.7       +52 -61    apache/support/suexec.c
  
  Index: suexec.c
  ===================================================================
  RCS file: /export/home/cvs/apache/support/suexec.c,v
  retrieving revision 1.6
  retrieving revision 1.7
  diff -C3 -r1.6 -r1.7
  *** suexec.c  1996/12/02 18:18:31     1.6
  --- suexec.c  1996/12/24 18:28:52     1.7
  ***************
  *** 115,122 ****
    
    static FILE *log;
    
  ! static void
  ! err_output (const char *fmt, va_list ap)
    {
        time_t timevar;
        struct tm *lt;
  --- 115,121 ----
    
    static FILE *log;
    
  ! static void err_output (const char *fmt, va_list ap)
    {
        time_t timevar;
        struct tm *lt;
  ***************
  *** 140,147 ****
        return;
    }
    
  ! void
  ! log_err (const char *fmt, ...)
    {
    #ifdef LOG_EXEC
        va_list     ap;
  --- 139,145 ----
        return;
    }
    
  ! void log_err (const char *fmt, ...)
    {
    #ifdef LOG_EXEC
        va_list     ap;
  ***************
  *** 153,162 ****
        return;
    }
    
  ! int
  ! main(int argc, char *argv[], char **env)
    {
  !     int doclen;             /* length of the docroot     */
        int userdir = 0;        /* ~userdir flag             */
        uid_t uid;              /* user information          */
        char *target_uname;     /* target user name          */
  --- 151,160 ----
        return;
    }
    
  ! int main(int argc, char *argv[], char **env)
    {
  !     int homelen;        /* length of homedir path */
  !     int cgilen;                 /* length of cgidir path */
        int userdir = 0;        /* ~userdir flag             */
        uid_t uid;              /* user information          */
        char *target_uname;     /* target user name          */
  ***************
  *** 164,170 ****
        char *prog;             /* name of this program      */
        char *cmd;              /* command to be executed    */
        char *cwd;              /* current working directory */
  -     char *buf = NULL;       /* temporary buffer          */
        struct passwd *pw;      /* password entry holder     */
        struct group *gr;       /* group entry holder        */
        struct stat dir_info;   /* directory info holder     */
  --- 162,167 ----
  ***************
  *** 213,219 ****
         * to protect against attacks.  If a '/' is
         * found, error out.  Naughty naughty crackers.
         */
  !     if (strchr (cmd, '/') != (char) NULL )
        {
        log_err ("invalid command (%s)\n", cmd);
        exit (104);
  --- 210,216 ----
         * to protect against attacks.  If a '/' is
         * found, error out.  Naughty naughty crackers.
         */
  !     if ((strchr (cmd, '/')) != NULL )
        {
        log_err ("invalid command (%s)\n", cmd);
        exit (104);
  ***************
  *** 231,262 ****
        }
    
        /*
         * Get the current working directory, as well as
         * the proper document root (dependant upon whether
         * or not it is a ~userdir request.  Error out if
         * we cannot get either one, or if the command is
         * not in the docroot.
         */
  -     if ((cwd = getcwd (buf, MAXPATHLEN)) == NULL)
  -     {
  -         log_err ("cannot get current working directory\n");
  -         exit (105);
  -     }
        if (userdir)
        {
  !     doclen = strlen (pw->pw_dir);
  !     if (strncmp (cwd, pw->pw_dir, doclen))
  !         {   
  !         log_err ("command not in docroot (%s/%s)\n", cwd, cmd);
  !         exit (106);
  !     }
  !     } else {
  !     doclen = strlen (DOC_ROOT);
  !     if (strncmp (cwd, DOC_ROOT, doclen))
  !         {
  !         log_err ("command not in docroot (%s/%s)\n", cwd, cmd);
  !         exit (106);
  !     }
        }
    
        /*
  --- 228,272 ----
        }
    
        /*
  +      * Error out if the target username is invalid.
  +      */
  +     if ((pw = getpwnam (target_uname)) == NULL )
  +     {
  +     log_err ("invalid target user name: (%s)\n", target_uname);
  +     exit (105);
  +     }
  + 
  +     /*
  +      * Error out if the target group name is invalid.
  +      */
  +     if ((gr = getgrnam (target_gname)) == NULL )
  +     {
  +     log_err ("invalid target group name: (%s)\n", target_gname);
  +     exit (106);
  +     }
  + 
  +     /*
         * Get the current working directory, as well as
         * the proper document root (dependant upon whether
         * or not it is a ~userdir request.  Error out if
         * we cannot get either one, or if the command is
         * not in the docroot.
         */
        if (userdir)
        {
  !     homelen = strlen(pw->pw_dir) + 1;
  !     cgilen = strlen(USER_CGI_BIN) + 1;
  !     strncpy(cwd, pw->pw_dir, (homelen < MAXPATHLEN ? homelen : MAXPATHLEN));
  !     homelen = MAXPATHLEN - homelen - 1;     /* get space left */
  !     strncat(cwd, USER_CGI_BIN, (cgilen < homelen ? cgilen : homelen));
  !     }
  !     else
  !     strncpy(cwd, DOC_ROOT, MAXPATHLEN);
  ! 
  ! 
  !     if (!(chdir(cwd))) {
  !     log_err("cannot chdir directory: (%s)\n", cwd);
  !     exit(107);
        }
    
        /*
  ***************
  *** 266,272 ****
             !(S_ISDIR(dir_info.st_mode)) )
        {
        log_err ("cannot stat directory: (%s)\n", cwd);
  !     exit (107);
        }
    
        /*
  --- 276,282 ----
             !(S_ISDIR(dir_info.st_mode)) )
        {
        log_err ("cannot stat directory: (%s)\n", cwd);
  !     exit (108);
        }
    
        /*
  ***************
  *** 275,281 ****
        if ((dir_info.st_mode & S_IWOTH) || (dir_info.st_mode & S_IWGRP))
        {
        log_err ("directory is writable by others: (%s)\n", cwd);
  !     exit (108);
        }
    
        /*
  --- 285,291 ----
        if ((dir_info.st_mode & S_IWOTH) || (dir_info.st_mode & S_IWGRP))
        {
        log_err ("directory is writable by others: (%s)\n", cwd);
  !     exit (109);
        }
    
        /*
  ***************
  *** 284,290 ****
        if ((lstat (cmd, &prg_info)) || (S_ISLNK(prg_info.st_mode)))
        {
        log_err ("cannot stat program: (%s)\n", cmd);
  !     exit (109);
        }
    
        /*
  --- 294,300 ----
        if ((lstat (cmd, &prg_info)) || (S_ISLNK(prg_info.st_mode)))
        {
        log_err ("cannot stat program: (%s)\n", cmd);
  !     exit (110);
        }
    
        /*
  ***************
  *** 293,299 ****
        if ((prg_info.st_mode & S_IWOTH) || (prg_info.st_mode & S_IWGRP))
        {
        log_err ("file is writable by others: (%s/%s)\n", cwd, cmd);
  !     exit (110);
        }
    
        /*
  --- 303,309 ----
        if ((prg_info.st_mode & S_IWOTH) || (prg_info.st_mode & S_IWGRP))
        {
        log_err ("file is writable by others: (%s/%s)\n", cwd, cmd);
  !     exit (111);
        }
    
        /*
  ***************
  *** 302,342 ****
        if ((prg_info.st_mode & S_ISUID) || (prg_info.st_mode & S_ISGID))
        {
        log_err ("file is either setuid or setgid: (%s/%s)\n",cwd,cmd);
  -     exit (111);
  -     }
  - 
  -     /*
  -      * Error out if the target username is invalid.
  -      */
  -     if ( (pw = getpwnam (target_uname)) == NULL )
  -     {
  -     log_err ("invalid target user name: (%s)\n", target_uname);
        exit (112);
        }
    
        /*
  -      * Error out if the target group name is invalid.
  -      */
  -     if ( (gr = getgrnam (target_gname)) == NULL )
  -     {
  -     log_err ("invalid target group name: (%s)\n", target_gname);
  -     exit (113);
  -     }
  - 
  -     /*
         * Error out if the target name/group is different from
         * the name/group of the cwd or the program.
         */
  !     if ( (pw->pw_uid != dir_info.st_uid) ||
  !      (gr->gr_gid != dir_info.st_gid) ||
  !      (pw->pw_uid != prg_info.st_uid) ||
  !      (gr->gr_gid != prg_info.st_gid) )
        {
        log_err ("target uid/gid (%ld/%ld) mismatch with directory (%ld/%ld) or 
program (%ld/%ld)\n",
                 pw->pw_uid, gr->gr_gid,
                 dir_info.st_uid, dir_info.st_gid,
                 prg_info.st_uid, prg_info.st_gid);
  !     exit (114);
        }
    
        /*
  --- 312,334 ----
        if ((prg_info.st_mode & S_ISUID) || (prg_info.st_mode & S_ISGID))
        {
        log_err ("file is either setuid or setgid: (%s/%s)\n",cwd,cmd);
        exit (112);
        }
    
        /*
         * Error out if the target name/group is different from
         * the name/group of the cwd or the program.
         */
  !     if ((pw->pw_uid != dir_info.st_uid) ||
  !     (gr->gr_gid != dir_info.st_gid) ||
  !     (pw->pw_uid != prg_info.st_uid) ||
  !     (gr->gr_gid != prg_info.st_gid))
        {
        log_err ("target uid/gid (%ld/%ld) mismatch with directory (%ld/%ld) or 
program (%ld/%ld)\n",
                 pw->pw_uid, gr->gr_gid,
                 dir_info.st_uid, dir_info.st_gid,
                 prg_info.st_uid, prg_info.st_gid);
  !     exit (113);
        }
    
        /*
  ***************
  *** 345,351 ****
        if (pw->pw_uid == 0)
        {
        log_err ("cannot run as uid 0 (%s)\n", cmd);
  !     exit (115);
        }
    
        /*
  --- 337,343 ----
        if (pw->pw_uid == 0)
        {
        log_err ("cannot run as uid 0 (%s)\n", cmd);
  !     exit (114);
        }
    
        /*
  ***************
  *** 354,360 ****
        if (gr->gr_gid == 0)
        {
        log_err ("cannot run as gid 0 (%s)\n", cmd);
  !     exit (116);
        }
    
        /*
  --- 346,352 ----
        if (gr->gr_gid == 0)
        {
        log_err ("cannot run as gid 0 (%s)\n", cmd);
  !     exit (115);
        }
    
        /*
  ***************
  *** 367,377 ****
         * Initialize the group access list for the target user,
         * and setgid() to the target group. If unsuccessful, error out.
         */
  !     if ( (initgroups (NNAME,NGID) != 0) ||
  !          (setgid (gr->gr_gid)      != 0) )
        {
  !         log_err ("failed to initialize groups or setgid (%ld: %s/%s)\n", 
gr->gr_gid, cwd, cmd);
  !         exit (117);
        }
    
        /*
  --- 359,368 ----
         * Initialize the group access list for the target user,
         * and setgid() to the target group. If unsuccessful, error out.
         */
  !     if ((setgid (gr->gr_gid)) != 0)
        {
  !         log_err ("failed to setgid (%ld: %s/%s)\n", gr->gr_gid, cwd, cmd);
  !         exit (116);
        }
    
        /*
  ***************
  *** 380,386 ****
        if ((setuid (pw->pw_uid)) != 0)
        {
        log_err ("failed to setuid (%ld: %s/%s)\n", pw->pw_uid, cwd, cmd);
  !     exit (118);
        }
    
        /*
  --- 371,377 ----
        if ((setuid (pw->pw_uid)) != 0)
        {
        log_err ("failed to setuid (%ld: %s/%s)\n", pw->pw_uid, cwd, cmd);
  !     exit (117);
        }
    
        /*
  
  
  
  1.4       +19 -1     apache/support/suexec.h
  
  Index: suexec.h
  ===================================================================
  RCS file: /export/home/cvs/apache/support/suexec.h,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -C3 -r1.3 -r1.4
  *** suexec.h  1996/12/02 18:18:31     1.3
  --- suexec.h  1996/12/24 18:28:52     1.4
  ***************
  *** 63,97 ****
     *               runs.  This is the only user allowed to execute
     *               this program.
     */
    #define HTTPD_USER "www"
    
    /*
     * LOG_EXEC -- Define this as a filename if you want all suEXEC
     *             transactions and errors logged for auditing and
     *             debugging purposes.
     */
    #define LOG_EXEC "/usr/local/etc/httpd/logs/cgi.log" /* Need me? */
    
    /*
     * DOC_ROOT -- Define as the DocumentRoot set for Apache.  This
     *             will be the only hierarchy (aside from UserDirs)
     *             that can be used for suEXEC behavior.
     */
    #define DOC_ROOT "/usr/local/etc/httpd/htdocs"
    
    /*
     * NNAME -- Define this as the name for the nobody account
     *          on your operating system.  Most systems will just
     *          need the default 'nobody'.
     */
    #define NNAME "nobody"
    
    /* NGID -- Define this as the *number* for the nogroup group
     *         on your operating system.  Most systems will have
     *         a -1 or -2.  Others might have something above
     *         65000.
     */
    #define NGID -1
  ! 
    
    #endif  /* _SUEXEC_H */
  --- 63,115 ----
     *               runs.  This is the only user allowed to execute
     *               this program.
     */
  + #ifndef HTTPD_USER
    #define HTTPD_USER "www"
  + #endif
    
    /*
     * LOG_EXEC -- Define this as a filename if you want all suEXEC
     *             transactions and errors logged for auditing and
     *             debugging purposes.
     */
  + #ifndef LOG_EXEC
    #define LOG_EXEC "/usr/local/etc/httpd/logs/cgi.log" /* Need me? */
  + #endif
    
    /*
     * DOC_ROOT -- Define as the DocumentRoot set for Apache.  This
     *             will be the only hierarchy (aside from UserDirs)
     *             that can be used for suEXEC behavior.
     */
  + #ifndef DOC_ROOT
    #define DOC_ROOT "/usr/local/etc/httpd/htdocs"
  + #endif
  + 
  + /*
  +  * USER_CGI_BIN -- Define this as the correct cgi-bin directory
  +  *                 for regular users (~user). NOTE: it needs the
  +  *                 initial '/' character
  +  */
  + #ifndef USER_CGI_BIN
  + #define USER_CGI_BIN "/public_html/cgi-bin"
  + #endif
    
    /*
     * NNAME -- Define this as the name for the nobody account
     *          on your operating system.  Most systems will just
     *          need the default 'nobody'.
     */
  + #ifndef NNAME
    #define NNAME "nobody"
  + #endif
    
    /* NGID -- Define this as the *number* for the nogroup group
     *         on your operating system.  Most systems will have
     *         a -1 or -2.  Others might have something above
     *         65000.
     */
  + #ifndef NGID
    #define NGID -1
  ! #endif
    
    #endif  /* _SUEXEC_H */
  
  
  

Reply via email to