httpd-cvs mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Roy Fielding <field...@hyperreal.com>
Subject cvs commit: apache/support suexec.h suexec.c
Date Sun, 24 Nov 1996 09:37:08 GMT
fielding    96/11/24 01:37:08

  Modified:    support   suexec.c
  Added:       support   suexec.h
  Log:
  Removed HAVE_RLIMIT and related rlimit code now that the server handles
  the functionality.  Moved user-defined code to suexec.h.
  Added "DON'T EDIT" warning in code.
  No more "security by obscurity"...comments added at each step.
  
  Submitted by: Jason A. Dour
  Reviewed by: Roy Fielding, Randy Terbush
  
  Revision  Changes    Path
  1.4       +151 -99   apache/support/suexec.c
  
  Index: suexec.c
  ===================================================================
  RCS file: /export/home/cvs/apache/support/suexec.c,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -C3 -r1.3 -r1.4
  *** suexec.c	1996/11/03 21:02:34	1.3
  --- suexec.c	1996/11/24 09:37:07	1.4
  ***************
  *** 53,58 ****
  --- 53,68 ----
    /*
     * suexec.c -- "Wrapper" support program for suEXEC behaviour for Apache
     *
  +  ***********************************************************************
  +  *
  +  * NOTE! : DO NOT edit this code!!!  Unless you know what you are doing,
  +  *         editing this code might open up your system in unexpected 
  +  *         ways to would-be crackers.  Every precaution has been taken 
  +  *         to make this code as safe as possible; alter it at your own
  +  *         risk.
  +  *
  +  ***********************************************************************
  +  *
     * A MotherSoft Product for the Apache WWW server.
     * (http://www.louisville.edu/~jadour01/mothersoft/)
     *
  ***************
  *** 61,66 ****
  --- 71,82 ----
     *    Jason A. Dour (jad@bcc.louisville.edu)
     *    Randy Terbush (randy@zyzzyva.com)
     *
  +  * Version 0.1.0 - Jason A. Dour
  +  *    First beta.  Removed HAVE_RLIMIT and related rlimit code
  +  * now that the server handles the funcitonality.  Moved user-
  +  * defined code to suexec.h.  Added "DON'T EDIT" warning in code.
  +  * No more "security by obscurity"...comments added at each step.
  +  *
     * Version 0.0.3 - Jason A. Dour
     *    Third alpha.  Added NNAME and NGID directives to fix
     * portability problem -- various systems have different
  ***************
  *** 85,130 ****
     */
    
    
  ! /* ********** USER-DEFINED VARIABLES ********** */
  ! 
  ! /*
  !  * HTTPD_USER -- Define as the username under which Apache normally
  !  *               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 DocuemntRoot set for Apache.  This
  !  *             will be the only hierarchy (aside from UserDirs)
  !  *             that can be used for suEXEC behaviour.
  !  */
  ! #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
  ! 
  ! 
  ! 
  ! /* ********** DO NOT EDIT BELOW THIS LINE ********** */
    
    #include <sys/param.h>
    #include <stdlib.h>
  --- 101,107 ----
     */
    
    
  ! #include "suexec.h"
    
    #include <sys/param.h>
    #include <stdlib.h>
  ***************
  *** 137,143 ****
    #include <grp.h>
    #include <time.h>
    #include <sys/stat.h>
  - #include <sys/resource.h>
    
    
    static FILE *log;
  --- 114,119 ----
  ***************
  *** 183,223 ****
    int
    main(int argc, char *argv[], char **env)
    {
  !     int doclen;
  !     int homedir = 0;
  !     uid_t uid;
  !     char *server_uid;
  !     char *server_gid;
  !     char *prog;
  !     char *cmd;
  !     char *cwd;
  !     char *buf = NULL;
  !     struct passwd *pw;
  !     struct group *gr;
  !     struct stat dir_info;
  !     struct stat prg_info;
  !     struct rlimit limits;
    
        
    
        prog = argv[0];
        if (argc < 4)
        {
    	log_err ("too few arguments\n");
    	exit(101);
        }
  !     server_uid = argv[1];
  !     server_gid = argv[2];
        cmd = argv[3];
    
  !     getrlimit ( RLIMIT_NOFILE, &limits );
  !     if (limits.rlim_cur < limits.rlim_max)
  !     {
  !       limits.rlim_cur = 256;
  !       if (setrlimit (RLIMIT_NOFILE, &limits) < 0)
  ! 	log_err ("Cannot exceed hard limit for open files\n");
  !     }
  ! 
        uid = getuid();
        if ((pw = getpwuid (uid)) == NULL)
        {
  --- 159,198 ----
    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          */
  !     char *target_gname;     /* target group name         */
  !     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     */
  !     struct stat prg_info;   /* program info holder       */
    
        
    
  +     /*
  +      * If there are a proper number of arguments, set
  +      * all of them to variables.  Otherwise, error out.
  +      */
        prog = argv[0];
        if (argc < 4)
        {
    	log_err ("too few arguments\n");
    	exit(101);
        }
  !     target_uname = argv[1];
  !     target_gname = argv[2];
        cmd = argv[3];
    
  !     /*
  !      * Check existence/validity of the UID of the user
  !      * running this program.  Error out if invalid.
  !      */
        uid = getuid();
        if ((pw = getpwuid (uid)) == NULL)
        {
  ***************
  *** 225,310 ****
    	exit (102);
        }
        
        if (strcmp (HTTPD_USER, pw->pw_name))
        {
    	log_err ("user mismatch (%s)\n", pw->pw_name);
    	exit (103);
        }
        
        if (strchr (cmd, '/') != (char) NULL )
        {
    	log_err ("invalid command (%s)\n", cmd);
    	exit (104);
        }
    
  !     if (!strncmp( "~", server_uid, 1))
        {
  ! 	server_uid++;
  ! 	homedir = 1;
        }
  ! 
  !     cwd = getcwd (buf, MAXPATHLEN);
  ! 
  !     if (homedir)
        {
    	doclen = strlen (pw->pw_dir);
    	if (strncmp (cwd, pw->pw_dir, doclen))
  ! 	{   
  ! 	    log_err ("invalid command (%s/%s)\n", cwd, cmd);
  ! 	    exit (105);
    	}
        } else {
    	doclen = strlen (DOC_ROOT);
    	if (strncmp (cwd, DOC_ROOT, doclen))
  ! 	{
  ! 	    log_err ("invalid command (%s/%s)\n", cwd, cmd);
  ! 	    exit (105);
    	}
        }
    
        if ( (lstat (cwd, &dir_info))     ||
             !(S_ISDIR(dir_info.st_mode)) )
        {
    	log_err ("cannot stat directory: (%s)\n", cwd);
  ! 	exit (106);
        }
  !     
        if ((dir_info.st_mode & S_IWOTH) || (dir_info.st_mode & S_IWGRP))
        {
    	log_err ("directory is writable by others: (%s)\n", cwd);
  ! 	exit (107);
        }
    
        if ((lstat (cmd, &prg_info)) || (S_ISLNK(prg_info.st_mode)))
        {
    	log_err ("cannot stat program: (%s)\n", cmd);
  ! 	exit (108);
        }
    
        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 (109);
        }
    
        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 (110);
        }
    
  !     if ( (pw = getpwnam (server_uid)) == NULL )
        {
  ! 	log_err ("invalid target user name: (%s)\n", server_uid);
  ! 	exit (111);
        }
    
  !     if ( (gr = getgrnam (server_gid)) == NULL )
        {
  ! 	log_err ("invalid target group name: (%s)\n", server_gid);
  ! 	exit (112);
        }
    
        if ( (pw->pw_uid != dir_info.st_uid) ||
    	 (gr->gr_gid != dir_info.st_gid) ||
    	 (pw->pw_uid != prg_info.st_uid) ||
  --- 200,335 ----
    	exit (102);
        }
        
  +     /*
  +      * Check to see if the user running this program
  +      * is the user allowed to do so as defined in
  +      * suexec.h.  If not the allowed user, error out.
  +      */
        if (strcmp (HTTPD_USER, pw->pw_name))
        {
    	log_err ("user mismatch (%s)\n", pw->pw_name);
    	exit (103);
        }
        
  +     /*
  +      * Check for a '/' in the command to be executed,
  +      * 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);
        }
    
  !     /*
  !      * Check to see if this is a ~userdir request.  If
  !      * so, set the flag, and remove the '~' from the
  !      * target username.
  !      */
  !     if (!strncmp( "~", target_uname, 1))
  !     {
  ! 	target_uname++;
  ! 	userdir = 1;
  !     }
  ! 
  !     /*
  !      * 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);
    	}
        }
    
  +     /*
  +      * Stat the cwd and verify it is a directory, or error out.
  +      */
        if ( (lstat (cwd, &dir_info))     ||
             !(S_ISDIR(dir_info.st_mode)) )
        {
    	log_err ("cannot stat directory: (%s)\n", cwd);
  ! 	exit (107);
        }
  ! 
  !     /*
  !      * Error out if cwd is writable by others.
  !      */
        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);
        }
    
  +     /*
  +      * Error out if we cannot stat the program.
  +      */
        if ((lstat (cmd, &prg_info)) || (S_ISLNK(prg_info.st_mode)))
        {
    	log_err ("cannot stat program: (%s)\n", cmd);
  ! 	exit (109);
        }
    
  +     /*
  +      * Error out if the program is writable by others.
  +      */
        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);
        }
    
  +     /*
  +      * Error out if the file is setuid or setgid.
  +      */
        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) ||
  ***************
  *** 314,352 ****
    		 pw->pw_uid, gr->gr_gid,
    		 dir_info.st_uid, dir_info.st_gid,
    		 prg_info.st_uid, prg_info.st_gid);
  ! 	exit (113);
        }
    
        if (pw->pw_uid == 0)
        {
    	log_err ("cannot run as uid 0 (%s)\n", cmd);
  ! 	exit (114);
        }
  ! 	
        if (gr->gr_gid == 0)
        {
    	log_err ("cannot run as gid 0 (%s)\n", cmd);
  ! 	exit (115);
        }
    
  !     /* log the transaction here to be sure we have an open log before setuid() */
  !     log_err ("uid: (%s) gid: (%s) %s\n", server_uid, server_gid, cmd);
  ! 
        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 (116);
        }
    
        if ((setuid (pw->pw_uid)) != 0)
        {
    	log_err ("failed to setuid (%ld: %s/%s)\n", pw->pw_uid, cwd, cmd);
  ! 	exit (117);
        }
    
        execve (cmd, &argv[3], env);
    
        log_err ("exec failed (%s)\n", cmd);
        exit(255);
    }
  --- 339,404 ----
    		 pw->pw_uid, gr->gr_gid,
    		 dir_info.st_uid, dir_info.st_gid,
    		 prg_info.st_uid, prg_info.st_gid);
  ! 	exit (114);
        }
    
  +     /*
  +      * Error out if attempt is made to execute as root.  Tsk tsk.
  +      */
        if (pw->pw_uid == 0)
        {
    	log_err ("cannot run as uid 0 (%s)\n", cmd);
  ! 	exit (115);
        }
  ! 
  !     /*
  !      * Error out if attempt is made to execute as root group.  Tsk tsk.
  !      */
        if (gr->gr_gid == 0)
        {
    	log_err ("cannot run as gid 0 (%s)\n", cmd);
  ! 	exit (116);
        }
    
  !     /*
  !      * Log the transaction here to be sure we have an open log 
  !      * before we setuid().
  !      */
  !     log_err ("uid: (%s) gid: (%s) %s\n", target_uname, target_gname, cmd);
  ! 
  !     /*
  !      * 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);
        }
    
  +     /*
  +      * setuid() to the target user.  Error out on fail.
  +      */
        if ((setuid (pw->pw_uid)) != 0)
        {
    	log_err ("failed to setuid (%ld: %s/%s)\n", pw->pw_uid, cwd, cmd);
  ! 	exit (118);
        }
    
  +     /*
  +      * Execute the command, replacing our image with its own.
  +      */
        execve (cmd, &argv[3], env);
    
  +     /*
  +      * (I can't help myself...sorry.)
  +      *
  +      * Uh oh.  Still here.  Where's the kaboom?  There was supposed to be an
  +      * EARTH-shattering kaboom!
  +      *
  +      * Oh well, log the failure and error out.
  +      */
        log_err ("exec failed (%s)\n", cmd);
        exit(255);
    }
  
  
  

Mime
View raw message