Branch: Tag:

1999-03-27

1999-03-27 22:15:50 by Henrik Grubbström (Grubba) <grubba@grubba.org>

Now with Privs.

Rev: server/base_server/roxen.pike:1.262

1:   /* -  * $Id: roxen.pike,v 1.261 1999/03/11 04:28:29 mast Exp $ +  * $Id: roxen.pike,v 1.262 1999/03/27 22:15:50 grubba Exp $    *    * The Roxen Challenger main program.    *
7:    */      // ABS and suicide systems contributed freely by Francesco Chemolli - constant cvs_version="$Id: roxen.pike,v 1.261 1999/03/11 04:28:29 mast Exp $"; + constant cvs_version="$Id: roxen.pike,v 1.262 1999/03/27 22:15:50 grubba Exp $";      // Some headerfiles   #define IN_ROXEN
25:   inherit "language";       + /* +  * Version information +  */ + constant __roxen_version__ = "1.4"; + constant __roxen_build__ = "37"; +  + #ifdef __NT__ + constant real_version = "Roxen Challenger/"+__roxen_version__+"."+__roxen_build__+" NT"; + #else + constant real_version = "Roxen Challenger/"+__roxen_version__+"."+__roxen_build__; + #endif +  +    // Prototypes for other parts of roxen.   class RequestID   {
74:   };       + /* +  * The privilege changer. +  * +  * Based on privs.pike,v 1.36. +  */ +  + // Some variables used by Privs + #ifdef THREADS + // This mutex is used by Privs + object euid_egid_lock = Thread.Mutex(); + #endif /* THREADS */ +  + int privs_level; +  + static class Privs { + #if efun(seteuid) +  +  int saved_uid; +  int saved_gid; +  +  int new_uid; +  int new_gid; +  + #define LOGP (variables && variables->audit && GLOBVAR(audit)) +  + #if constant(geteuid) && constant(getegid) && constant(seteuid) && constant(setegid) + #define HAVE_EFFECTIVE_USER + #endif +  +  static private string _getcwd() +  { +  if (catch{return(getcwd());}) { +  return("Unknown directory (no x-bit on current directory?)"); +  } +  } +  +  static private string dbt(array t) +  { +  if(!arrayp(t) || (sizeof(t)<2)) return ""; +  return (((t[0]||"Unknown program")-(_getcwd()+"/"))-"base_server/")+":"+t[1]+"\n"; +  } +  + #ifdef THREADS +  mixed mutex_key; // Only one thread may modify the euid/egid at a time. + #endif /* THREADS */ +  +  int p_level; +  +  void create(string reason, int|string|void uid, int|string|void gid) +  { + #ifdef PRIVS_DEBUG +  werror(sprintf("Privs(%O, %O, %O)\n" +  "privs_level: %O\n", +  reason, uid, gid, privs_level)); + #endif /* PRIVS_DEBUG */ +  + #ifdef THREADS + #if constant(roxen_pid) +  if(getpid() == roxen_pid) +  { +  // __disallow_threads(); +  werror("Using Privs ("+reason+") in threaded environment, source is\n "+ +  replace(describe_backtrace(backtrace()), "\n", "\n ")+"\n"); +  } + #endif + #endif + #ifdef HAVE_EFFECTIVE_USER +  array u; +  + #ifdef THREADS +  if (euid_egid_lock) { +  catch { mutex_key = euid_egid_lock->lock(); }; +  } + #endif /* THREADS */ +  +  p_level = privs_level++; +  +  if(getuid()) return; +  +  /* Needs to be here since root-priviliges may be needed to +  * use getpw{uid,nam}. +  */ +  saved_uid = geteuid(); +  saved_gid = getegid(); +  seteuid(0); +  +  /* A string of digits? */ +  if (stringp(uid) && ((int)uid) && +  (replace(uid, ({ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" }), +  ({ "", "", "", "", "", "", "", "", "", "" })) == "")) { +  uid = (int)uid; +  } +  if (stringp(gid) && ((int)gid) && +  (replace(gid, ({ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" }), +  ({ "", "", "", "", "", "", "", "", "", "" })) == "")) { +  gid = (int)gid; +  } +  +  if(!stringp(uid)) { +  u = getpwuid(uid); +  } else { +  u = getpwnam(uid); +  if(u) +  uid = u[2]; +  } +  +  if(u && !gid) gid = u[3]; +  +  if(!u) { +  if (uid && (uid != "root")) { +  if (intp(uid) && (uid >= 60000)) { +  report_debug(sprintf("Privs: User %d is not in the password database.\n" +  "Assuming nobody.\n", uid)); +  // Nobody. +  gid = gid || uid; // Fake a gid also. +  u = ({ "fake-nobody", "x", uid, gid, "A real nobody", "/", "/sbin/sh" }); +  } else { +  error("Unknown user: "+uid+"\n"); +  } +  } else { +  u = ({ "root", "x", 0, gid, "The super-user", "/", "/sbin/sh" }); +  } +  } +  +  if(LOGP) +  report_notice(sprintf("Change to %s(%d):%d privs wanted (%s), from %s", +  (string)u[0], (int)uid, (int)gid, +  (string)reason, +  (string)dbt(backtrace()[-2]))); +  + #if efun(cleargroups) +  catch { cleargroups(); }; + #endif /* cleargroups */ + #if efun(initgroups) +  catch { initgroups(u[0], u[3]); }; + #endif +  gid = gid || getgid(); +  int err = (int)setegid(new_gid = gid); +  if (err < 0) { +  report_debug(sprintf("Privs: WARNING: Failed to set the effective group id to %d!\n" +  "Check that your password database is correct for user %s(%d),\n" +  "and that your group database is correct.\n", +  gid, (string)u[0], (int)uid)); +  int gid2 = gid; + #ifdef HPUX_KLUDGE +  if (gid >= 60000) { +  /* HPUX has doesn't like groups higher than 60000, +  * but has assigned nobody to group 60001 (which isn't even +  * in /etc/group!). +  * +  * HPUX's libc also insists on filling numeric fields it doesn't like +  * with the value 60001! +  */ +  perror("Privs: WARNING: Assuming nobody-group.\n" +  "Trying some alternatives...\n"); +  // Assume we want the nobody group, and try a couple of alternatives +  foreach(({ 60001, 65534, -2 }), gid2) { +  perror("%d... ", gid2); +  if (initgroups(u[0], gid2) >= 0) { +  if ((err = setegid(new_gid = gid2)) >= 0) { +  perror("Success!\n"); +  break; +  } +  } +  } +  } + #endif /* HPUX_KLUDGE */ +  if (err < 0) { +  perror("Privs: Failed\n"); +  throw(({ sprintf("Failed to set EGID to %d\n", gid), backtrace() })); +  } +  perror("Privs: WARNING: Set egid to %d instead of %d.\n", +  gid2, gid); +  gid = gid2; +  } +  if(getgid()!=gid) setgid(gid||getgid()); +  seteuid(new_uid = uid); + #endif /* HAVE_EFFECTIVE_USER */ +  } +  +  void destroy() +  { + #ifdef PRIVS_DEBUG +  werror(sprintf("Privs->destroy()\n" +  "privs_level: %O\n", +  privs_level)); + #endif /* PRIVS_DEBUG */ +  + #ifdef HAVE_EFFECTIVE_USER +  /* Check that we don't increase the privs level */ +  if (p_level >= privs_level) { +  report_error(sprintf("Change back to uid#%d gid#%d from uid#%d gid#%d\n" +  "in wrong order! Saved level:%d Current level:%d\n" +  "Occurs in:\n%s\n", +  saved_uid, saved_gid, new_uid, new_gid, +  p_level, privs_level, +  describe_backtrace(backtrace()))); +  return(0); +  } +  if (p_level != privs_level-1) { +  report_error(sprintf("Change back to uid#%d gid#%d from uid#%d gid#%d\n" +  "Skips privs level. Saved level:%d Current level:%d\n" +  "Occurs in:\n%s\n", +  saved_uid, saved_gid, new_uid, new_gid, +  p_level, privs_level, +  describe_backtrace(backtrace()))); +  } +  privs_level = p_level; +  +  if(LOGP) { +  catch { +  array bt = backtrace(); +  if (sizeof(bt) >= 2) { +  report_notice(sprintf("Change back to uid#%d, from %s\n", saved_uid, +  dbt(bt[-2]))); +  } else { +  report_notice(sprintf("Change back to uid#%d, from backend\n", +  saved_uid)); +  } +  }; +  } +  +  if(getuid()) return; +  + #ifdef DEBUG +  int uid = geteuid(); +  if (uid != new_uid) { +  report_warning(sprintf("Privs: UID #%d differs from expected #%d\n" +  "%s\n", +  uid, new_uid, describe_backtrace(backtrace()))); +  } +  int gid = getegid(); +  if (gid != new_gid) { +  report_warning(sprintf("Privs: GID #%d differs from expected #%d\n" +  "%s\n", +  gid, new_gid, describe_backtrace(backtrace()))); +  } + #endif /* DEBUG */ +  +  seteuid(0); +  array u = getpwuid(saved_uid); + #if efun(cleargroups) +  catch { cleargroups(); }; + #endif /* cleargroups */ +  if(u && (sizeof(u) > 3)) { +  catch { initgroups(u[0], u[3]); }; +  } +  setegid(saved_gid); +  seteuid(saved_uid); + #endif /* HAVE_EFFECTIVE_USER */ +  } + #endif /* efun(seteuid) */ + }; +  + /* Used by read_config.pike, since there seems to be problems with +  * overloading otherwise. +  */ + static object PRIVS(string r, int|string|void u, int|string|void g) + { +  return Privs(r, u, g); + } +  +    // The datashuffler program   #if constant(spider.shuffle) && (defined(THREADS) || defined(__NT__))   constant pipe = (program)"smartpipe";
81:   constant pipe = Pipe.pipe;   #endif    - constant __roxen_version__ = "1.4"; - constant __roxen_build__ = "37"; -  - #ifdef __NT__ - constant real_version = "Roxen Challenger/"+__roxen_version__+"."+__roxen_build__+" NT"; - #else - constant real_version = "Roxen Challenger/"+__roxen_version__+"."+__roxen_build__; - #endif -  +    #if _DEBUG_HTTP_OBJECTS   mapping httpobjects = ([]);   static int idcount;
119:   }      // Locale support - object(Locale.Roxen.standard) default_locale=Locale.Roxen.standard; + object(Locale.Roxen.standard) default_locale=Locale.Roxen.nihongo;   object fonts;   #ifdef THREADS   object locale = thread_local();
144:   private function build_root;   private object root;    - #ifdef THREADS - // This mutex is used by privs.pike - object euid_egid_lock = Thread.Mutex(); -  - #endif /* THREADS */ -  - int privs_level; +    int die_die_die;      void stop_all_modules()
1203:    module_stat_cache = decode_value(Stdio.read_bytes(".module_stat_cache"));    allmodules = decode_value(Stdio.read_bytes(".allmodules"));    }; + #ifndef __NT__ +  if(!getuid()) { +  add_constant("Privs", Privs); +  } else + #endif /* !__NT__ */ +  add_constant("Privs", class{}); +     add_constant("roxen", this_object());    add_constant("RequestID", RequestID);    add_constant("load", load);