setuid setup - move to single shared file to stop cnp of code

This commit is contained in:
Carsten Haitzler 2020-05-01 18:13:23 +01:00
parent 48bcb8d0b3
commit bab2b503f6
5 changed files with 278 additions and 287 deletions

View File

@ -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 <stdio.h>
#include <stdlib.h>
@ -10,23 +8,28 @@
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pwd.h>
#ifdef HAVE_ALLOCA_H
#include <alloca.h>
# include <alloca.h>
#endif
#include <sys/types.h>
#include <Eina.h>
#include <Ecore.h>
#include <Eldbus.h>
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 <security/pam_constants.h>
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)

252
src/bin/e_util_suid.h Normal file
View File

@ -0,0 +1,252 @@
#include "config.h"
#define __USE_MISC
#define _SVID_SOURCE
#define _DEFAULT_SOURCE
# ifdef __linux__
# include <features.h>
# endif
# ifdef HAVE_ENVIRON
# define _GNU_SOURCE 1
# endif
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <sys/resource.h>
#include <limits.h>
#include <pwd.h>
#include <grp.h>
#include <inttypes.h>
#ifdef HAVE_ALLOCA_H
# include <alloca.h>
#endif
#include <sys/types.h>
#include <pwd.h>
#ifdef HAVE_PRCTL
# include <sys/prctl.h>
#elif defined(HAVE_PROCCTL)
# include <sys/procctl.h>
#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;
}

View File

@ -57,7 +57,6 @@ void *alloca (size_t);
# include <ctype.h>
# include <time.h>
# include <dirent.h>
# include <pwd.h>
# include <grp.h>
# include <glob.h>
# include <locale.h>
@ -76,12 +75,6 @@ void *alloca (size_t);
# include <sys/sysctl.h>
#endif
#ifdef HAVE_PRCTL
# include <sys/prctl.h>
#elif defined(HAVE_PROCCTL)
# include <sys/procctl.h>
#endif
# ifndef _POSIX_HOST_NAME_MAX
# define _POSIX_HOST_NAME_MAX 255
# endif

View File

@ -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();

View File

@ -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,