/* file: setuidruby.c - setuidruby is a wrapper on ruby which will run ruby as another user - to compile make or, __only__ if you __know__ what you are doing make mode=6777 - the user under which to execute ruby and an optional path to a ruby interpreter must be specified as the first argument to setuidruby. for example: [ahoward@localhost setuidruby-0.0.0]$ cat ./a.rb require "yaml" y 'euid' => Process::euid, 'uid' => Process::uid, 'egid' => Process::egid, 'gid' => Process::gid [ahoward@localhost setuidruby-0.0.0]$ setuidruby ahoward ./a.rb --- egid: 500 gid: 500 euid: 500 uid: 500 [ahoward@localhost setuidruby-0.0.0]$ setuidruby jsherry:/usr/bin/ruby182 ./a.rb --- egid: 510 gid: 510 euid: 509 uid: 509 if the path to ruby, which must be absolute, is not specifed the compile time default of /usr/bin/ruby is used. obviously setuidruby can be used in a shebang line: [ahoward@localhost setuidruby-0.0.0]$ cat b.rb #! /usr/bin/setuidruby ahoward:/usr/bin/ruby190 require "yaml" y 'euid' => Process::euid, 'uid' => Process::uid, 'egid' => Process::egid, 'gid' => Process::gid [ahoward@localhost setuidruby-0.0.0]$ ./b.rb --- egid: 500 gid: 500 euid: 500 uid: 500 - this program could be __very__ dangerous to your system's health: use wisely. you are responsible. */ #include #include #include #include #include #include #include static int xdie (const char *const msg, int err, const char *const file, int lineno) { if (msg) { fprintf (stderr, "msg(%s) strerror(%s) errno(%d) @ %s:%d\n", msg, strerror (errno), errno, file, lineno); } else { fprintf (stderr, "strerror(%s) errno(%d) @ %s:%d\n", strerror (errno), errno, file, lineno); } exit (err); } #define usr_bin_ruby "/usr/bin/ruby" #define die(msg) do{xdie(msg,errno,__FILE__,__LINE__);}while(0) #define usage "setuidruby user[:/path/to/ruby (default /usr/bin/ruby)] [rubyargs]" int main (argc, argv, env) int argc; char **argv; char **env; { char *user; char *ruby; char *cp; struct passwd *entry; uid_t pw_uid; gid_t pw_gid; user = argv[1]; argv++; if (user == NULL) { die (usage); } for (cp = user; *cp && *cp != ':'; cp++); if (!*cp) { ruby = usr_bin_ruby; } else { *cp = '\0'; cp++; ruby = *cp ? cp : usr_bin_ruby; } entry = getpwnam (user); if (entry == NULL) { die ("must set SETUSER or SETUID"); } pw_uid = entry->pw_uid; pw_gid = entry->pw_gid; setregid (pw_gid, pw_gid); setfsgid (pw_gid); setreuid (pw_uid, pw_uid); setfsuid (pw_uid); execv (ruby, argv); die (ruby); }