From bab2b503f62faf1a1464ae14b973733f2b9e2977 Mon Sep 17 00:00:00 2001 From: "Carsten Haitzler (Rasterman)" Date: Fri, 1 May 2020 18:13:23 +0100 Subject: [PATCH] setuid setup - move to single shared file to stop cnp of code --- src/bin/e_ckpasswd_main.c | 46 +++--- src/bin/e_util_suid.h | 252 ++++++++++++++++++++++++++++++++ src/bin/system/e_system.h | 7 - src/bin/system/e_system_main.c | 258 +-------------------------------- src/bin/system/meson.build | 2 +- 5 files changed, 278 insertions(+), 287 deletions(-) create mode 100644 src/bin/e_util_suid.h diff --git a/src/bin/e_ckpasswd_main.c b/src/bin/e_ckpasswd_main.c index 0128d68b8..cbfe119d0 100644 --- a/src/bin/e_ckpasswd_main.c +++ b/src/bin/e_ckpasswd_main.c @@ -1,8 +1,6 @@ -#include "config.h" +#include "e_util_suid.h" -#define __USE_MISC -#define _SVID_SOURCE -#define _DEFAULT_SOURCE +#include "config.h" #include #include @@ -10,23 +8,28 @@ #include #include #include -#include #ifdef HAVE_ALLOCA_H -#include +# include #endif +#include #include #include #include +uid_t uid = -1; // uid of person running me +gid_t gid = -1; // gid of person running me +char *user_name = NULL; +char *group_name = NULL; + #if defined(__OpenBSD__) static int -_check_auth(uid_t uid, const char *guess) +_check_auth(uid_t id, const char *guess) { struct passwd *pwent; - pwent = getpwuid_shadow(uid); + pwent = getpwuid_shadow(id); if (!pwent) return -1; if (!pwent->pw_passwd) return -1; @@ -41,9 +44,9 @@ _check_auth(uid_t uid, const char *guess) #include static int -_check_auth(uid_t uid, const char *pw) +_check_auth(uid_t id, const char *pw) { - struct passwd *pwent = getpwuid(uid); + struct passwd *pwent = getpwuid(id); if (!pwent) return -1; if (!pwent->pw_passwd) return -1; @@ -102,7 +105,7 @@ _conv_cb(int num, const struct pam_message **msg, struct pam_response **resp, vo } static int -_check_auth(uid_t uid, const char *pw) +_check_auth(uid_t id, const char *pw) { Authinfo ai; struct passwd *pwent; @@ -114,7 +117,7 @@ _check_auth(uid_t uid, const char *pw) int pamerr; struct pam_conv conv; - pwent = getpwuid(uid); + pwent = getpwuid(id); if (!pwent) return -1; user = pwent->pw_name; if (!user) return -1; @@ -155,7 +158,7 @@ _check_auth(uid_t uid, const char *pw) #else static int -_check_auth(uid_t uid, const char *pw) +_check_auth(uid_t id, const char *pw) { return -1; } @@ -251,7 +254,6 @@ int main(int argc, char **argv) { ssize_t rd; - uid_t id; char pw[4096], *p; int polkit_mode = 0; char polkit_cookie[4096]; @@ -266,9 +268,6 @@ main(int argc, char **argv) if (!strcmp(argv[1], "pw")) polkit_mode = 0; else if (!strcmp(argv[1], "pk")) polkit_mode = 1; - // get uid who ran this - id = getuid(); - // read passwd from stdin if (polkit_mode == 0) { @@ -363,15 +362,10 @@ main(int argc, char **argv) } } - // If we are setuid root then try become root - we can work without though - // if pam etc. can work without being root - if (setuid(0) != 0) - fprintf(stderr, - "Warning. Can't become user root. If password auth requires root then this will fail\n"); - if (setgid(0) != 0) - fprintf(stderr, - "Warning. Can't become group root. If password auth requires root then this will fail\n"); - if (_check_auth(id, pw) == 0) + // ok to fail - auth will just possibly fail then + e_setuid_setup(&uid, &gid, &user_name, &group_name); + + if (_check_auth(uid, pw) == 0) { fprintf(stderr, "Password OK\n"); if (polkit_mode == 1) diff --git a/src/bin/e_util_suid.h b/src/bin/e_util_suid.h new file mode 100644 index 000000000..128888ac8 --- /dev/null +++ b/src/bin/e_util_suid.h @@ -0,0 +1,252 @@ +#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 +#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 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; +} + diff --git a/src/bin/system/e_system.h b/src/bin/system/e_system.h index 472dbde3b..00fc636ff 100644 --- a/src/bin/system/e_system.h +++ b/src/bin/system/e_system.h @@ -57,7 +57,6 @@ void *alloca (size_t); # include # include # include -# include # include # include # include @@ -76,12 +75,6 @@ void *alloca (size_t); # include #endif -#ifdef HAVE_PRCTL -# include -#elif defined(HAVE_PROCCTL) -# include -#endif - # ifndef _POSIX_HOST_NAME_MAX # define _POSIX_HOST_NAME_MAX 255 # endif diff --git a/src/bin/system/e_system_main.c b/src/bin/system/e_system_main.c index 85f1dec36..0607415b3 100644 --- a/src/bin/system/e_system_main.c +++ b/src/bin/system/e_system_main.c @@ -1,3 +1,5 @@ +#include "e_util_suid.h" + #include "e_system.h" Eina_Bool alert_backlight_reset = EINA_FALSE; @@ -114,257 +116,6 @@ deny: return 0; } -static void -setuid_setup(void) -{ - struct passwd *pwent; - struct group *grent; - static char buf[PATH_MAX]; - - uid = getuid(); - gid = getgid(); - - pwent = getpwuid(uid); - if (!pwent) - { - ERR("Unable to obtain passwd entry for calling user\n"); - exit(31); - } - if (!pwent->pw_name) - { - ERR("Blank username for user\n"); - exit(32); - } - user_name = strdup(pwent->pw_name); - if (!user_name) - { - ERR("Unable to allocate memory for username\n"); - exit(33); - } - grent = getgrgid(gid); - if (!grent) - { - ERR("Unable to obtain group entry for calling group\n"); - exit(34); - } - if (!grent->gr_name) - { - ERR("Blank groupname for group\n"); - exit(35); - } - group_name = strdup(grent->gr_name); - if (!group_name) - { - ERR("Unable to allocate memory for groupname\n"); - exit(36); - } - - if (setuid(0) != 0) - { - ERR("Unable to assume root user privileges\n"); - exit(37); - } - if (setgid(0) != 0) - { - ERR("Unable to assume root group privileges\n"); - exit(38); - } - - pwent = getpwuid(getuid()); - if (!pwent) - { - ERR("Unable to obtain passwd entry\n"); - exit(39); - } - if (!pwent->pw_dir) - { - ERR("No home dir for root\n"); - exit(40); - } - if (strlen(pwent->pw_dir) > (sizeof(buf) - 8)) - { - ERR("Root homedir too long\n"); - exit(41); - } - if (pwent->pw_dir[0] != '/') - { - ERR("Root homedir %s is not a full path\n", pwent->pw_dir); - exit(42); - } - if (!realpath(pwent->pw_dir, buf)) - { - ERR("Root homedir %s does not resolve\n", pwent->pw_dir); - exit(43); - } - snprintf(buf, sizeof(buf), "HOME=%s", pwent->pw_dir); - if (putenv(buf) == -1) - { - ERR("Unable to set $HOME environment\n"); - exit(44); - } - - // change CWD to / to avoid path search dlopens finding libs in ./ - if (chdir("/") != 0) - { - ERR("Unable to change working dir to /\n"); - exit(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) - { - Eina_Bool again; - // go over environment array again and again... safely - do - { - again = EINA_FALSE; - // 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 = EINA_TRUE; - 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"); -} - -// no singleton mode - this is not really a bonus, just painful, so disable -// but keep code aroun for future possible use -/* -static void -_cb_die(void *data EINA_UNUSED, Ecore_Thread *th EINA_UNUSED) -{ - char buf[256]; - int f; - - snprintf(buf, sizeof(buf), "/var/run/enlightenment_sys-%u.lck", uid); - f = open(buf, O_RDONLY); - if (f < 0) exit(0); - exit(0); -} - -static void -singleton_setup(void) -{ // only one per uid - kill existing and replace... - char buf[256]; - int f; - mode_t um; - - snprintf(buf, sizeof(buf), "/var/run/enlightenment_sys-%u.lck", uid); - f = open(buf, O_WRONLY | O_NONBLOCK); - if (f >= 0) - { - if (write(f, buf, 1) == 1) - ERR("Replacing previous enlightenment_system\n"); - close(f); - } - unlink(buf); - um = umask(0); - mkfifo(buf, S_IRUSR | S_IWUSR); - umask(um); - - ecore_thread_feedback_run(_cb_die, NULL, NULL, NULL, NULL, EINA_TRUE); -} -*/ - static Eina_Bool _cb_idle_enterer(void *data EINA_UNUSED) { @@ -380,7 +131,7 @@ int main(int argc EINA_UNUSED, const char **argv EINA_UNUSED) { const char *s; - int systems = 0; + int ok, systems = 0; // special mode to reset all newly found bl devices to max on // discovery because we were run by the e alert crash handler and @@ -388,7 +139,8 @@ main(int argc EINA_UNUSED, const char **argv EINA_UNUSED) s = getenv("E_ALERT_BACKLIGHT_RESET"); if ((s) && (s[0] == '1')) alert_backlight_reset = EINA_TRUE; - setuid_setup(); + ok = e_setuid_setup(&uid, &gid, &user_name, &group_name); + if (ok != 0) exit(ok); ecore_app_no_system_modules(); diff --git a/src/bin/system/meson.build b/src/bin/system/meson.build index 64683b96d..0db4ca49c 100644 --- a/src/bin/system/meson.build +++ b/src/bin/system/meson.build @@ -11,7 +11,7 @@ src = [ 'e_system.h', ] executable('enlightenment_system', src, - include_directories: include_directories('../../..'), + include_directories: include_directories('..', '../../..'), dependencies : [ dep_eina, dep_ecore, dep_ecore_file, dep_m, dep_dl, dep_eet, dep_eeze,