On Thu, 28 Aug 2003, ahoward wrote:

>
> i wrote a little setuid C wrapper which essentially does
>
>   execvp (ruby, argv);
>
> and was suprised that ruby was still aware that it was being run setuid and
> set $SAFE to 1.  is there an easy away around this?  perhpas embedding the
> interpreter?

hate to reply to my own message - but here is the program i'm using, posted to
clr for posterity.  no security flames please! ;-)

----CUT----
/*
  FILE: _ruby.c

  - this program is a wrapper on ruby which will run ruby as another user
  (setuid ruby).

  - it MUST be named 'USERNAME_ruby', for example 'nobody_ruby', and must also
  be a root owned setuid binary

  - compile with

      gcc _ruby.c -o USERNAME_ruby
      sudo su
      chown root USERNAME_ruby
      chgrp root USERNAME_ruby
      chmod 6777 USERNAME_ruby

    for example

      gcc _ruby.c -o nobody_ruby
      sudo su
      chown root nobody_ruby
      chgrp root nobody_ruby
      chmod 6777 nobody_ruby

    obviously the user this ruby would execute as is user 'nobody'

  - this program is known to work with

    * Linux 2.4.18-27.8.0 i686
    * ruby 1.6.7 (2002-03-19) [i386-linux]

  - this program could be VERY dangerous to your system's health: use wisely
*/

#define RUBY "ruby"

#include <stdlib.h>
#include <stdio.h>
#include <pwd.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>


int
die (const char * const msg, int err, const char * const file, int lineno)
{
  fprintf (stderr, "%s @ %s:%d\n", msg ? msg : strerror (err), file, lineno);
  exit (err);
}
#define DIE() (die(strerror(errno),errno,__FILE__,__LINE__))

#define unless(exp) if(!(exp))

int
main (argc, argv, env)
     int argc;
     char **argv;
     char **env;
{
  int n;
  char *ruby;
  char *cp;
  char *name;
  uid_t pw_uid;
  gid_t pw_gid;
  struct passwd *entry;


  /* find basename of executing program */
  for (cp = *argv + strlen (*argv) - 1; cp >= *argv && *cp != '/'; --cp);
  (*cp == '/') && cp++;

  /* make a copy because we will munge it later */
  unless (name = strdup (cp))
    {
      DIE ();
    }

  /* search for XXX component of XXX_ruby */
  for (cp = name; *cp && *cp != '_'; cp++);
  unless (*cp && cp != name && *cp == '_' && (strcmp (cp + 1, "ruby") == 0))
    {
      die ("THIS PROGRAM MUST BE NAMED ${USER}_ruby", EXIT_FAILURE, __FILE__, __LINE__);
    }

  /* we take everything before the '_' as the username */
  *cp = 0;
  unless (entry = getpwnam (name))
    {
      die (name, errno, __FILE__, __LINE__);
    }

  pw_uid = entry->pw_uid;
  pw_gid = entry->pw_gid;

  free (name);

  setregid (pw_gid, pw_gid);
  /* setgid (pw_gid); */
  setfsgid (pw_gid);

  setreuid (pw_uid, pw_uid);
  /* setuid (pw_uid); */
  setfsuid (pw_uid);

  (ruby = getenv ("RUBY")) || (ruby = RUBY);
  execvp (ruby, argv);
  return (EXIT_FAILURE);
}
----CUT----

-a
  ====================================
  | Ara Howard
  | NOAA Forecast Systems Laboratory
  | Information and Technology Services
  | Data Systems Group
  | R/FST 325 Broadway
  | Boulder, CO 80305-3328
  | Email: ara.t.howard / noaa.gov
  | Phone:  303-497-7238
  | Fax:    303-497-7259
  | ~ > ruby -e 'p(%.\x2d\x29..intern)'
  ====================================