Great Circle Associates Majordomo-Workers
(August 2000)
 

Indexed By Date: [Previous] [Next] Indexed By Thread: [Previous] [Next]

Subject: Re: Mj2: Re: setuid wrappers are insecure?
From: SRE <eckert @ climber . org>
Date: Sun, 27 Aug 2000 23:12:47 -0700
To: Michael Yount <csf @ moscow . com>
Cc: majordomo-workers @ GreatCircle . COM, mj2-dev @ csf . colorado . edu
In-reply-to: <E13T3zD-00009G-00@moscow.com>
References: <Message from SRE <eckert@climber.org><4.3.1.0.20000826213456.00bba7c0@pop.climber.org>

At 08:04 AM 8/27/00, Michael Yount wrote:
>A quick search yesterday turned up a somewhat dated explanation of
>potential linking problems with LD_LIBRARY_PATH and LD_PRELOAD on linux.
>   http://lwn.net/lwn/980212/ldconfusion.html
>I'd be interested to see the modified wrapper.c.  Perhaps
>it contains some useful ideas.

Below is forwarded text and a copy of the suggested C source code.
I believe the issue has evoked religious fervor, so I don't know if
it's worth pursuing. I'm kind of sorry I asked, but will press on
given that there has been one request for information (even though
there has also been a rejection before it has been reviewed).

The "useful idea" you're looking for is either
  - complete control of the environment BEFORE starting the perl script
  - checking of UID/GID to make sure they are as the installer intended

Jason says all shared lib and environment exploits would happen
before your wrapper code gets to execute. Is that true? Then the
wrapper executes a perl script with an unknown environment and
another chance for shared lib exploits. The C file below takes
complete control before the scripts start firing up the perl
processor... where things like the perl module include path are
up for grabs with uncontrolled environment variables. Please
don't take my word for it. I'm just ASKING, not TELLING, and I
am guessing (from the strength of the response) that this question
has been asked before. Just think about it with an open mind, and
I'll believe whatever you and Jason decide. I'm not contributing
much code to Mj2, mostly help files and a bit of sanity checking.

One last thing: Keep in mind that I installed a usable copy of Mj2
WITHOUT EVER BEING ROOT. That wasn't supposed to be possible, but
it's running as we speak. I haven't turned real users loose on it
because I'm doing this sanity check first, but I'm still asking
whether the Mj2 install procedure should recommend running as root
or recommend running as the server (su to the UID that the wrappers
will use when they are executed). It appears, at least on FreeBSD,
that a regular user can create wrappers that setuid to that user
when they are executed by anyone: Is there any reason to be root
when building the wrappers? No answer yet.


>Attached please find a modified copy of "wrapper.c" which came with the
>original majordomo. You can use this to generate a setuid wrapper by
>(example is mj_shell, you need one per program going into /etc/aliases):
>
>1) Compiling like so:
>
>cc -DPROG=\"/path/to/mj_shell\" -DPOSIX_UID=<X> -DPOSIX_GID=<Y> -o mj_shell wrapper.c
>
>where X is the UID and Y is the GID that you want mj_shell to -really- run under.
>
>2) Manually setuid the wrapper to root. 
>
>The wrapper will now execute the specified program with a sane environment vector.


/*
  *  WRAPPER.C - Originally derived from majordomo v1.xx source - See Majordomo v1.xx source
  *  for licensing stuff...as long as you are freeware or open source I don't think there will
  *  be a problem. Don't try to sell this, it's not cool, and very easy to write something similar
  *  from scratch for that purpose. 
  *
  *  Pretty standard stuff here, allows execution of arbitrary programs from /etc/aliases
  *  where YOU pick the setuid owner. Sanitizes environment variables
  *
  *  Modified 01/12/98 - dave@jetcafe.org - add old RUID and RGID to do even more security checks
  *
  */

#include <stdio.h>
#include <sysexits.h>
#include <sys/types.h>
#include <pwd.h>

#if defined(sun) && defined(sparc)
#include <stdlib.h>
#endif


#ifndef STRCHR
#  include <string.h>
#  define STRCHR(s,c) strchr(s,c)
#endif

#ifndef BIN
#  define BIN "/usr/local/bin"
#endif

#ifndef PATH
#  define PATH "PATH=/bin:/usr/bin:/usr/ucb:/usr/local"
#endif

#ifndef HOME
#  define HOME "HOME=/"
#endif

#ifndef SHELL
#  define SHELL "SHELL=/bin/sh"
#endif

#ifndef PROG
#  define PROG "YouDidNotDefineAProgram,Dummy!"
#endif

char * new_env[] = {
     HOME,               /* 0 */
     PATH,               /* 1 */
     SHELL,              /* 2 */
     0,          /* possibly for USER or LOGNAME */
     0,          /* possible for LOGNAME */
     0,          /* possibly for timezone */
     0,          /* other */
     0,          /* other */
     0,          /* other */
     0,          /* other */
     0,          /* other */
     0,          /* other */
     0,          /* other */
     0,          /* other */
     0,          /* other */
     0
};
     
int new_env_size = 14;                          /* to prevent overflow problems */

main(argc, argv, env)
     int argc;
     char * argv[];
     char * env[];

{
     char * prog, * ticket;
     int e, i;
     struct passwd *userent;

     if (argv[1] == NULL) {
       fprintf(stderr,"usage: complete <arg>\n");
       exit(EX_OSERR);
     }

     if ((prog = (char *) malloc(strlen(BIN) + strlen(PROG) + 2)) == NULL) {
         fprintf(stderr, "%s: error: malloc failed\n", argv[0]);
         exit(EX_OSERR);
     }

     sprintf(prog, "%s/%s", BIN, PROG);

     /*  copy the "USER=" and "LOGNAME=" envariables into the new environment,
      *  if they exist.
      */
     e = 3; /* the first unused slot in new_env[] */
     for (i = 0 ; env[i] != NULL && e <= new_env_size; i++) {
         if ((strncmp(env[i], "USER=", 5) == 0)         ||
             (strncmp(env[i], "TZ=", 3) == 0)           ||
             (strncmp(env[i], "LOGNAME=", 8) == 0)     
             ) {
             new_env[e++] = env[i];
         }
     }

      if ((ticket = (char *) malloc(strlen(argv[1]) + 10)) == NULL) {
         fprintf(stderr, "%s: error: malloc failed\n", argv[0]);
         exit(EX_OSERR);
      }

     /*
      * Add old real and effective ids to env for future use
      */
    userent = getpwuid(getuid());
    if (userent != NULL) {
      char *entry;
      if ((entry = (char *) malloc(strlen(userent->pw_name) + 10)) == NULL) {
         fprintf(stderr, "%s: error: malloc failed\n", argv[0]);
         exit(EX_OSERR);
      }
      sprintf(entry,"RUID=%s",userent->pw_name);
      new_env[e++] = entry;
    }
    userent = getpwuid(geteuid());
    if (userent != NULL) {
      char *entry;
      if ((entry = (char *) malloc(strlen(userent->pw_name) + 10)) == NULL) {
         fprintf(stderr, "%s: error: malloc failed\n", argv[0]);
         exit(EX_OSERR);
      }
      sprintf(entry,"EUID=%s",userent->pw_name);
      new_env[e++] = entry;
    }


#if defined(SETGROUP)
/* renounce any previous group memberships if we are running as root */
     if (geteuid() == 0) { /* Should I exit if this test fails? */
     char *setgroups_used = "setgroups_was_included"; /* give strings a hint */
#if defined(MAIL_GID)
     int groups[] =  { POSIX_GID, MAIL_GID, 0 };
     if (setgroups(2, groups) == -1) {
#else
     int groups[] =  { POSIX_GID, 0 };
     if (setgroups(1, groups) == -1) {
#endif
         extern int errno;

         fprintf(stderr, "%s: error setgroups failed errno %d", argv[0],
                 errno);
         }
}
#endif
           

#ifdef POSIX_GID
     setgid(POSIX_GID);
#else
     setgid(getegid());
#endif

#ifdef POSIX_UID
     setuid(POSIX_UID);
#else
     setuid(geteuid());
#endif

     if ((getuid() != geteuid()) || (getgid() != getegid())) {
         fprintf(stderr, "%s: error: Not running with proper UID and GID.\n", argv[0]);
         fprintf(stderr, "    Make certain that wrapper is installed setuid, and if so,\n");
         fprintf(stderr, "    recompile with POSIX flags.\n");
         exit(EX_SOFTWARE);
     }

     execve(prog, argv, new_env);

     /* the exec should never return */
     fprintf(stderr, "wrapper: Trying to exec %s failed: ", prog);
     perror(NULL);
     fprintf(stderr, "    HOME  is %s,\n", HOME);
     fprintf(stderr, "    PATH  is %s,\n", PATH);
     fprintf(stderr, "    SHELL is %s,\n", SHELL);
     fprintf(stderr, "    PROG  is %s\n", PROG);
     fprintf(stderr, "    BIN   is %s\n", BIN);
     exit(EX_OSERR);
}


SRE

mailto:eckert@climber.org | http://www.climber.org/eckert/
Info on peak climbing email lists mailto:info@climber.org

"A free society is one where it is safe to be unpopular."
   -- Adlai Stevenson




References:
Indexed By Date Previous: Re: Fwd: Re: Fwd: Mj2 Installation
From: Jason L Tibbitts III <tibbs@math.uh.edu>
Next: Re: Fwd: Re: Fwd: Mj2 Installation
From: SRE <eckert@climber.org>
Indexed By Thread Previous: Re: setuid wrappers are insecure?
From: SRE <eckert@climber.org>
Next: V2 where is it?
From: geoff.cox@cableinet.co.uk

Google
 
Search Internet Search www.greatcircle.com