Received: by taz.hyperreal.com (8.6.12/8.6.5) id PAA06301; Sat, 23 Mar 1996 15:54:17 -0800 Received: from sierra.zyzzyva.com by taz.hyperreal.com (8.6.12/8.6.5) with ESMTP id PAA06290; Sat, 23 Mar 1996 15:54:09 -0800 Received: from zyzzyva.com (randy@localhost [127.0.0.1]) by sierra.zyzzyva.com (8.7.4/8.6.11) with ESMTP id RAA02219 for ; Sat, 23 Mar 1996 17:54:14 -0600 (CST) Message-Id: <199603232354.RAA02219@sierra.zyzzyva.com> To: new-httpd@apache.org Subject: SetUID once again X-uri: http://www.zyzzyva.com/ Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Date: Sat, 23 Mar 1996 17:54:13 -0600 From: Randy Terbush Sender: owner-new-httpd@apache.org Precedence: bulk Reply-To: new-httpd@hyperreal.com I'm including for review a set of patches made against the current cvs repository for some changes that were sent to me from Michael Kutzner. The changes implement a feature that I understand is available to CERN users to set execution ID based on directory. I know there have been some that have voiced an oposition to this type of functionality in Apache. I ask that you consider this solution, or offer some other suggestions as to what needs to be done to get this accepted into the source tree. It is a feature that I find I cannot run without. I know that there are other ways to do this with external programs, but none that make it simple enough to offer it as part of a server (IMO). *** Makefile.tmpl.orig Sat Mar 23 15:11:38 1996 --- Makefile.tmpl Sat Mar 23 16:29:53 1996 *************** *** 3,10 **** # This is combined with the information in the "Configuration" file # by the configure script to make the actual Makefile. ! OBJS= alloc.o http_main.o http_core.o http_config.o http_request.o \ ! http_log.o http_protocol.o rfc1413.o util.o util_script.o modules.o buff.o\ md5c.o util_md5.o $(MODULES) .c.o: --- 3,10 ---- # This is combined with the information in the "Configuration" file # by the configure script to make the actual Makefile. ! OBJS= alloc.o http_main.o http_core.o http_config.o http_log.o http_protocol.o \ ! http_request.o http_uid.o rfc1413.o util.o util_script.o modules.o buff.o\ md5c.o util_md5.o $(MODULES) .c.o: *** http_core.c.orig Sat Mar 23 15:11:39 1996 --- http_core.c Sat Mar 23 17:26:30 1996 *************** *** 372,377 **** --- 372,421 ---- return NULL; } + /* + * Reads DirUid and puts it on the at the beginning of the diruidlist + */ + char *set_dir_pid (cmd_parms *cmd, void *d, char *l) + { + char dir[MAXNAMLEN], user[64], group[64], *w; + struct diruidlist *new; + int failure=0, i=0; + user[0]=group[0]='\0'; + + if (l[0]){ + w = getword_conf(cmd->pool, &l); + strcpy(dir, w); + }else failure=1; + + if (l[0]){ + w = getword_conf(cmd->pool, &l); + strcpy(user, w); + while(user[i] && (user[i] != '.')) i++; + if (user[i] == '.'){ + user[i]='\0'; + strcpy(group, &user[i+1]); + }else group[0]=0; + }else failure=1; + + if (failure || (group[0] == '\0')){ + return ("unknown parameters to DirUid"); + } + new=(struct diruidlist *)pcalloc(cmd->pool, (sizeof(struct diruidlist))); + strcpy(new->user, user); + strcpy(new->dir, dir); + strcpy(new->group, group); + new->next=cmd->server->diruid; + cmd->server->diruid=new; + #ifndef DEBUG_PID + { + FILE *dbg = fopen ("/tmp/uri.log", "a"); + fprintf (dbg, "%s, %s, %s\n", dir, user,group); + fclose(dbg); + } + #endif + return NULL; + } + /* access.conf commands... * * The *only* thing that can appear in access.conf at top level is a *************** *** 715,720 **** --- 759,765 ---- { "AuthName", set_string_slot, (void*)XtOffsetOf(core_dir_config, auth_name), OR_AUTHCFG, RAW_ARGS, NULL }, { "Require", require, NULL, OR_AUTHCFG, RAW_ARGS, NULL }, + { "DirUid", set_dir_pid, NULL, RSRC_CONF, RAW_ARGS, NULL }, /* Change UID on Directory to uid.groupid */ /* Old resource config file commands */ *** http_main.c.orig Sat Mar 23 15:11:39 1996 --- http_main.c Sat Mar 23 16:25:48 1996 *************** *** 1001,1012 **** reopen_scoreboard (pconf); update_child_status (child_num, SERVER_READY); - /* Only try to switch if we're running as root */ - if(!geteuid() && setuid(user_id) == -1) { - log_unixerr("setuid", NULL, "unable to change uid", server_conf); - exit (1); - } - #ifdef NEXT setjmp(jmpbuffer); #else --- 1001,1006 ---- *** http_request.c.orig Sat Mar 23 15:11:39 1996 --- http_request.c Sat Mar 23 16:22:40 1996 *************** *** 599,604 **** --- 599,605 ---- void process_request_internal (request_rec *r) { int access_status; + int ret_uid; /* Kludge to be reading the assbackwards field outside of protocol.c, * but we've got to check for this sort of nonsense somewhere... *************** *** 627,632 **** --- 628,640 ---- getparents(r->uri); /* OK --- shrinking transformations... */ } + /* + * Switch effective uid depending on the given uri (Michael Kutzner) + */ + if ((ret_uid=switch_euid(r)) == 1){ + die(SERVER_ERROR, r); + } + if ((access_status = translate_name (r))) { decl_die (access_status, "translate", r); return; *** http_uid.c.orig Sat Mar 23 17:19:20 1996 --- http_uid.c Sat Mar 23 16:23:10 1996 *************** *** 0 **** --- 1,280 ---- + + /* ==================================================================== + * Copyright (c) 1995 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * 5. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + + + #include "httpd.h" + extern char *user_name, *group_name; + #define DEBUG /**/ + extern uid_t user_id; + extern gid_t group_id; + + /* + * switch_euid_default + */ + int switch_euid_default(request_rec *r) + { + /* Switch to default user given in the config file */ + #ifdef DEBUG + fprintf(stderr, "Default user: %s, %d\n", user_name, group_id); + #endif + + /* + * At first, if we're running under a different + * uid than root, we have to switch back to root + */ + if (seteuid(0) == -1){ + if (r){ /* log error if r is given */ + log_reason (pstrcat (r->pool, "unable to change effective uid to root", NULL), + r->uri, + r); + die (SERVER_ERROR, r); /* Better to kill than to risk a security hole */ + } + exit(1); /* if r not given die without error */ + } + /* + * Now switch the effective group id. This has to be done whith uid + * root because I got permission denied otherwise (grr, unix) + */ + if (setegid(group_id) == -1){ + if (r){ + log_reason (pstrcat (r->pool, "unable to change effective gid to default", + NULL), + r->uri, + r); + die (SERVER_ERROR, r); + } + exit(1); + } + /* + * now switch the effective user id to the default user id so we have + * permissions to read and write files the default user has + */ + if (seteuid(user_id) == -1 ){ + if (r){ + log_reason (pstrcat (r->pool, "unable to change effective uid to default", + NULL), + r->uri, + r); + die (SERVER_ERROR, r); + } + exit(1); + } + } + + /* + * switch_euid_name + * switches uid and gid to the given user name und group name + * dies if it can't + */ + int switch_euid_name(request_rec *r, char *name, char *group) + { + uid_t uid=uname2id(name); + gid_t gid; + int g=0; + if (group[0]){ + g=1; + gid=gname2id(group); + } + + /* + * At first, if we're running under a different + * uid as root, we've to switch back to root + */ + if (seteuid(0) == -1){ + log_reason (pstrcat (r->pool, "unable to change effective uid to root", NULL), + r->uri, + r); + die (SERVER_ERROR, r); + exit(1); + } + /* + * if group is given, change the effective gid + */ + if (g){ + if (setegid(gid) == -1){ + log_reason (pstrcat (r->pool, "unable to change effective gid", NULL), + r->uri, + r); + die (SERVER_ERROR, r); + #ifdef DEBUG + fprintf(stderr, "unable to change effective gid to %d, current %d\n", + gid, getegid()); + #endif + exit(1); + } + } + /* + * at last change uid + */ + if (seteuid(uid) == -1){ + log_reason (pstrcat (r->pool, "unable to change effective uid", NULL), + r->uri, + r); + die (SERVER_ERROR, r); + #ifdef DEBUG + fprintf(stderr, "unable to change effective uid to %d, current %d\n", + uid, geteuid()); + #endif + exit(1); + } + } + + /* + * switch_euid + * this function is called from process_request after stripping off + * all '../../'s + * It removes all '//' from the given uri and checks if it fits to a + * given DirUid in the config-File. + * If not it switches to the default user id and group id + */ + int switch_euid(request_rec *r) + { + struct diruidlist *dl; + uid_t uid; + gid_t gid; + int ret=0; + + /* + * strip off all '//' (why is it not done by default??? + */ + int l = 0; + char *name=strdup(r->uri); + + while(name[l]!='\0') { + if(name[l] == '/' && name[l+1] == '/' ){ + register int m=l+1,n; + while((name[n]=name[m])) (++n,++m); + } + else ++l; + } + dl=r->server->diruid; + while (dl && !ret){ + if (!strncmp(name, dl->dir, strlen(dl->dir))){ + /* + * found uri, change uid to the one given after "DirUid Dirname" + */ + #ifdef DEBUG + fprintf(stderr, "Changing: %s, %s, %s\n", dl->dir, dl->user,dl->group); + #endif + switch_euid_name(r, dl->user, dl->group); + ret=2; + } + dl=dl->next; + } + if (ret == 0){ + switch_euid_default(r); + } + free(name); + return(ret); + } + + /* + * switch_uid + * called before executing cgi's, ... because no cgi-binary should be able + * to switch the uid's to another that we want to. + */ + int switch_uid(request_rec *r) + { + uid_t uid; + gid_t gid; + int ret=0; + /* + * At first get the effective uid. This has been changed by switch_euid + * before (in process_request or directly in the log modules) + */ + uid=geteuid(); + gid=getegid(); + /* + * switch back to root + */ + seteuid(0); + /* + * Now switch the real group id + */ + if (setgid(gid) == -1){ + log_reason (pstrcat (r->pool, "unable to change effective uid", NULL), + r->uri, + r); + die (SERVER_ERROR, r); + #ifdef DEBUG + fprintf(stderr, "unable switch gid to %d\n", gid); + #endif + exit(1); + } + /* + * Now switch the real uid. After this point we're not able + * to change the uid back to anything different + * so this process has to be killed after executing this function + * (allways done after executing something, I saw) + */ + if (setuid(uid) == -1){ + log_reason (pstrcat (r->pool, "unable to change effective uid", NULL), + r->uri, + r); + die (SERVER_ERROR, r); + #ifdef DEBUG + fprintf(stderr, "unable to switch userid to %d\n", uid); + #endif + exit(1); + } + return(ret); + } + + void switch_uid_back(request_rec *r, int i) + { + if (i == 2){ + seteuid(0); + setegid(group_id); + seteuid(user_id); + } + } *** httpd.h.orig Sat Mar 23 15:11:40 1996 --- httpd.h Sat Mar 23 17:48:21 1996 *************** *** 382,387 **** --- 382,399 ---- const struct htaccess_result *htaccess; }; + /* + * diruid struct Michael Kutzner + */ + typedef struct diruidlist diruidlist; + + struct diruidlist { + char user[64], /* length's schould not be static I think */ + group[64]; + char dir[MAXNAMLEN]; + struct diruidlist *next; + }; + /* Things which are per connection */ *************** *** 458,463 **** --- 470,476 ---- int timeout; /* Timeout, in seconds, before we give up */ int keep_alive_timeout; /* Seconds we'll wait for another request */ int keep_alive; /* Maximum requests per connection */ + diruidlist *diruid; }; /* These are more like real hosts than virtual hosts */ *************** *** 504,513 **** void str_tolower (char *); int ind (const char *, char); /* Sigh... */ - int rind (const char *, char); - int cfg_getline(char *s, int n, FILE *f); ! /* Misc system hackery */ uid_t uname2id(char *name); --- 517,533 ---- void str_tolower (char *); int ind (const char *, char); /* Sigh... */ int cfg_getline(char *s, int n, FILE *f); ! ! /* ! * prototypes for the uid stuff (Michael Kutzner) ! */ ! void switch_uid_back(request_rec *r, int); ! int switch_uid(request_rec *r); ! int switch_euid(request_rec *r); ! int switch_euid_default(request_rec *r); ! struct diruidlist *diruid; ! /* Misc system hackery */ uid_t uname2id(char *name); *** mod_cgi.c.orig Sat Mar 23 15:11:40 1996 --- mod_cgi.c Sat Mar 23 16:22:40 1996 *************** *** 143,148 **** --- 143,155 ---- */ cleanup_for_exec(); + /* + * The effective user id is know changed depending on the given uri + * Now change the real user id too before starting the cgi + * (so it's not possible to change the uid to root for cgi's) + * Michael Kutzner + */ + switch_uid(r); #ifdef __EMX__ if((!r->args) || (!r->args[0]) || (ind(r->args,'=') >= 0)) { *** mod_include.c.orig Sat Mar 23 15:11:41 1996 --- mod_include.c Sat Mar 23 16:22:41 1996 *************** *** 478,483 **** --- 478,490 ---- fprintf (dbg, "Attempting to exec '%s'\n", s); #endif cleanup_for_exec(); + /* + * The effective user id is know changed depending on the given uri + * Now change the real user id too before starting the cgi + * (so it's not possible to change the uid to root for cgi's) + * Michael Kutzner + */ + switch_uid(0); execle(SHELL_PATH, SHELL_PATH, "-c", s, NULL, create_environment (r->pool, env)); *** mod_log_common.c.orig Sat Mar 23 15:11:41 1996 --- mod_log_common.c Sat Mar 23 16:22:41 1996 *************** *** 108,113 **** --- 108,120 ---- cleanup_for_exec(); signal (SIGHUP, SIG_IGN); + /* + * we're running as root, so we have to switch to default user id + * At first call switch_euid_default, then swtich_uid which set's + * the real uid depending on the effective uid + */ + switch_euid_default(0); /* Switch to default effective user id */ + switch_uid(0); /* And now the real user id */ execl (SHELL_PATH, SHELL_PATH, "-c", (char *)cmd, NULL); fprintf (stderr, "Exec of shell for logging failed!!!\n"); exit (1); *** mod_log_config.c.orig Sat Mar 23 15:11:41 1996 --- mod_log_config.c Sat Mar 23 16:22:41 1996 *************** *** 489,494 **** --- 489,501 ---- cleanup_for_exec(); signal (SIGHUP, SIG_IGN); + /* + * we're running as root, so we have to switch to default user id + * At first call switch_euid_default, then swtich_uid which set's + * the real uid depending on the effective uid + */ + switch_euid_default(0); /* Switch to default effective user id */ + switch_uid(0); /* And now the real user id */ execl (SHELL_PATH, SHELL_PATH, "-c", (char *)cmd, NULL); fprintf (stderr, "Exec of shell for logging failed!!!\n"); exit (1); *** mod_log_referer.c.orig Sat Mar 23 15:11:41 1996 --- mod_log_referer.c Sat Mar 23 16:22:41 1996 *************** *** 121,126 **** --- 121,133 ---- cleanup_for_exec(); signal (SIGHUP, SIG_IGN); + /* + * we're running as root, so we have to switch to default user id + * At first call switch_euid_default, then swtich_uid which set's + * the real uid depending on the effective uid + */ + switch_euid_default(0); /* Switch to default effective user id */ + switch_uid(0); /* And now the real user id */ execl (SHELL_PATH, SHELL_PATH, "-c", (char *)cmd, NULL); fprintf (stderr, "Exec of shell for logging failed!!!\n"); exit (1);