httpd-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Randy Terbush <ra...@zyzzyva.com>
Subject SetUID once again
Date Sat, 23 Mar 1996 23:54:13 GMT
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 <http://www.apache.org/>.
+  *
+  */
+ 
+ 
+ #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);



Mime
View raw message