#include "config.h" #define __USE_MISC #define _SVID_SOURCE #define _DEFAULT_SOURCE # ifdef __linux__ # include # endif # ifdef HAVE_ENVIRON # define _GNU_SOURCE 1 # endif #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_ALLOCA_H # include #endif #include #ifdef HAVE_PRCTL # include #elif defined(HAVE_PROCCTL) # include #endif #define E_UTIL_SUID_ERR(args...) do { fprintf(stderr, "E_SUID_ERR: "); fprintf(stderr, ##args); } while (0) static inline int e_setuid_setup(uid_t *caller_uid, gid_t *caller_gid, char **caller_user, char **caller_group) { struct passwd *pwent; struct group *grent; static char buf[PATH_MAX]; *caller_uid = getuid(); *caller_gid = getgid(); pwent = getpwuid(*caller_uid); if (!pwent) { E_UTIL_SUID_ERR("Unable to obtain passwd entry for calling user\n"); return 31; } if (!pwent->pw_name) { E_UTIL_SUID_ERR("Blank username for user\n"); return 32; } *caller_user = strdup(pwent->pw_name); if (!(*caller_user)) { E_UTIL_SUID_ERR("Unable to allocate memory for username\n"); return 33; } grent = getgrgid(*caller_gid); if (!grent) { E_UTIL_SUID_ERR("Unable to obtain group entry for calling group\n"); return 34; } if (!grent->gr_name) { E_UTIL_SUID_ERR("Blank groupname for group\n"); return 35; } *caller_group = strdup(grent->gr_name); if (!(*caller_group)) { E_UTIL_SUID_ERR("Unable to allocate memory for groupname\n"); return 36; } if (setuid(0) != 0) { E_UTIL_SUID_ERR("Unable to assume root user privileges\n"); return 37; } if (setgid(0) != 0) { E_UTIL_SUID_ERR("Unable to assume root group privileges\n"); return 38; } pwent = getpwuid(getuid()); if (!pwent) { E_UTIL_SUID_ERR("Unable to obtain passwd entry\n"); return 39; } if (!pwent->pw_dir) { E_UTIL_SUID_ERR("No home dir for root\n"); return 40; } if (strlen(pwent->pw_dir) > (sizeof(buf) - 8)) { E_UTIL_SUID_ERR("Root homedir too long\n"); return 41; } if (pwent->pw_dir[0] != '/') { E_UTIL_SUID_ERR("Root homedir %s is not a full path\n", pwent->pw_dir); return 42; } if (!realpath(pwent->pw_dir, buf)) { E_UTIL_SUID_ERR("Root homedir %s does not resolve\n", pwent->pw_dir); return 43; } snprintf(buf, sizeof(buf), "HOME=%s", pwent->pw_dir); if (putenv(buf) == -1) { E_UTIL_SUID_ERR("Unable to set $HOME environment\n"); return 44; } // change CWD to / to avoid path search dlopens finding libs in ./ if (chdir("/") != 0) { E_UTIL_SUID_ERR("Unable to change working dir to /\n"); return 45; } // die with parent - special as this is setuid #ifdef HAVE_PRCTL prctl(PR_SET_PDEATHSIG, SIGTERM); #elif defined(HAVE_PROCCTL) int sig = SIGTERM; procctl(P_PID, 0, PROC_PDEATHSIG_CTL, &sig); #endif #ifdef HAVE_UNSETENV # define NOENV(x) unsetenv(x) // pass 1 - just nuke known dangerous env vars brutally if possible via // unsetenv(). if you don't have unsetenv... there's pass 2 and 3 NOENV("IFS"); NOENV("CDPATH"); NOENV("LOCALDOMAIN"); NOENV("RES_OPTIONS"); NOENV("HOSTALIASES"); NOENV("NLSPATH"); NOENV("PATH_LOCALE"); NOENV("COLORTERM"); NOENV("LANG"); NOENV("LANGUAGE"); NOENV("LINGUAS"); NOENV("TERM"); NOENV("LD_PRELOAD"); NOENV("LD_LIBRARY_PATH"); NOENV("SHLIB_PATH"); NOENV("LIBPATH"); NOENV("AUTHSTATE"); NOENV("DYLD_*"); NOENV("KRB_CONF*"); NOENV("KRBCONFDIR"); NOENV("KRBTKFILE"); NOENV("KRB5_CONFIG*"); NOENV("KRB5_KTNAME"); NOENV("VAR_ACE"); NOENV("USR_ACE"); NOENV("DLC_ACE"); NOENV("TERMINFO"); NOENV("TERMINFO_DIRS"); NOENV("TERMPATH"); NOENV("TERMCAP"); NOENV("ENV"); NOENV("BASH_ENV"); NOENV("PS4"); NOENV("GLOBIGNORE"); NOENV("SHELLOPTS"); NOENV("JAVA_TOOL_OPTIONS"); NOENV("PERLIO_DEBUG"); NOENV("PERLLIB"); NOENV("PERL5LIB"); NOENV("PERL5OPT"); NOENV("PERL5DB"); NOENV("FPATH"); NOENV("NULLCMD"); NOENV("READNULLCMD"); NOENV("ZDOTDIR"); NOENV("TMPPREFIX"); NOENV("PYTHONPATH"); NOENV("PYTHONHOME"); NOENV("PYTHONINSPECT"); NOENV("RUBYLIB"); NOENV("RUBYOPT"); # ifdef HAVE_ENVIRON if (environ) { int i, again; // go over environment array again and again... safely do { again = 0; // walk through and find first entry that we don't like for (i = 0; environ[i]; i++) { // if it begins with any of these, it's possibly nasty if ((!strncmp(environ[i], "LD_", 3)) || (!strncmp(environ[i], "_RLD_", 5)) || (!strncmp(environ[i], "LC_", 3)) || (!strncmp(environ[i], "LDR_", 3))) { // unset it char *tmp, *p; tmp = strdup(environ[i]); if (!tmp) abort(); p = strchr(tmp, '='); if (!p) abort(); *p = 0; NOENV(tmp); free(tmp); // and mark our do to try again from the start in case // unsetenv changes environ ptr again = 1; break; } } } while (again); } # endif #endif // pass 2 - clear entire environment so it doesn't exist at all. if you // can't do this... you're possibly in trouble... but the worst is still // fixed in pass 3 #ifdef HAVE_CLEARENV clearenv(); #else # ifdef HAVE_ENVIRON environ = NULL; # endif #endif // pass 3 - set path and ifs to minimal defaults putenv("PATH=/bin:/usr/bin:/sbin:/usr/sbin"); putenv("IFS= \t\n"); return 0; }