Received: by taz.hyperreal.com (8.6.12/8.6.5) id PAA11487; Tue, 4 Jun 1996 15:49:03 -0700 Received: from sierra.zyzzyva.com by taz.hyperreal.com (8.6.12/8.6.5) with ESMTP id PAA11474; Tue, 4 Jun 1996 15:48:58 -0700 Received: from zyzzyva.com (localhost [127.0.0.1]) by sierra.zyzzyva.com (8.7.5/8.6.11) with ESMTP id RAA25606 for ; Tue, 4 Jun 1996 17:48:55 -0500 (CDT) Message-Id: <199606042248.RAA25606@sierra.zyzzyva.com> To: new-httpd@hyperreal.com Subject: Re: Authentication In-reply-to: rst's message of Tue, 04 Jun 1996 16:42:41 -0400. <199606042042.QAA08936@volterra.ai.mit.edu> X-uri: http://www.zyzzyva.com/ Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Date: Tue, 04 Jun 1996 17:48:55 -0500 From: Randy Terbush Sender: owner-new-httpd@apache.org Precedence: bulk Reply-To: new-httpd@hyperreal.com I have the following sucgi.c working on a test port of my main server (Solaris). Things seem to be working well. DOC_ROOT now restricts execution to within this path. The CGI program passed in cannot contain *any* '/' characters. This makes things secure enough to put me at ease (but then, I'm easy). Other checks include: * file and directory owner must match * file must not be writable by anyone but the owner * directory cannot be writable by anyone but the owner * requests for execution of CGI as root are denied * request for execution of CGI as group 0 are denied Todo: * portability issue with setrlimit() call * code up a linage test using kvm_getproc() for paranoid souls * add the ability to set owner execution on per/directory basis * ?? #define HTTPD_USER "www" #define LOG_EXEC "/www/var/log/cgi.log" #define DOC_ROOT "/disks/sd1/www/docs" #include #include #include #include #include #include #include #include #include #include #include #include static void err_output (const char *fmt, va_list ap) { time_t timevar; struct tm *lt; FILE *log; if ((log = fopen (LOG_EXEC, "a")) == NULL) { fprintf (stderr, "failed to open log file\n"); perror ("fopen"); } time (&timevar); lt = localtime (&timevar); fprintf (log, "[%.2d:%.2d:%.2d %.2d-%.2d-%.2d]: ", lt->tm_hour, lt->tm_min, lt->tm_sec, lt->tm_mday, lt->tm_mon, lt->tm_year); vfprintf (log, fmt, ap); fflush (log); fclose (log); return; } void log_err (const char *fmt, ...) { #ifdef LOG_EXEC va_list ap; fprintf (stderr, "%d\n", __LINE__); va_start (ap, fmt); fprintf (stderr, "%d\n", __LINE__); err_output (fmt, ap); fprintf (stderr, "%d\n", __LINE__); va_end(ap); #endif fprintf (stderr, "%d\n", __LINE__); return; } int main(int argc, char *argv[], char **env) { int doclen; uid_t uid, euid; 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) { log_err ("invalid uid: (%ld)\n", uid); 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); } cwd = getcwd (buf, MAXPATHLEN); log_err ("%s\n", cwd); doclen = strlen (DOC_ROOT); if (strncmp (cwd, DOC_ROOT, doclen)) { log_err ("invalid command (%s)\n", cmd); exit(105); } if ((stat (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 ((stat (cmd, &prg_info)) && !(S_ISLNK(prg_info.st_mode))) { log_err ("cannot stat program: (%s)\n", cmd); exit (106); } 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 (108); } pw = getpwnam (server_uid); gr = getgrnam (server_gid); if (pw->pw_uid != dir_info.st_uid && gr->gr_gid != dir_info.st_gid) { log_err ("server uid/gid (%ld/%ld) and directory uid/gid (%ld/%ld) mismatch\n", pw->pw_uid, gr->gr_gid, dir_info.st_uid, dir_info.st_gid); exit (109); } if (pw->pw_uid == 0) { log_err ("cannot run as uid 0 (%s)\n", cmd); exit(110); } if (gr->gr_gid == 0) { log_err ("cannot run as gid 0 (%s)\n", cmd); exit(112); } if ((setuid (pw->pw_uid)) != 0) { fprintf (stderr, "failed to setuid (%ld: %s)\n", pw->pw_uid, cmd); exit(111); } if ((setgid (gr->gr_gid)) != 0) { fprintf (stderr, "failed to setgid (%d: %s)\n", gr->gr_gid, cmd); exit(113); } execve (cmd, &argv[4], env); fprintf (stderr, "exec failed (%s)\n", cmd); exit(255); }