helper: Optionally add back setuid support

If you don't have userns support in your kernel you can use this.
The future lies with userns though, so it is the default.
This commit is contained in:
Alexander Larsson
2015-05-29 10:46:10 +02:00
parent 31692b6ab2
commit 50b3de3728
3 changed files with 138 additions and 28 deletions

View File

@@ -132,6 +132,17 @@ xdg_dbus_proxy_SOURCES = \
xdg_dbus_proxy_LDADD = $(BASE_LIBS) libglnx.la
xdg_dbus_proxy_CFLAGS = $(BASE_CFLAGS) -I$(srcdir)/libglnx
install-exec-hook:
if DISABLE_USERNS
if PRIV_MODE_SETUID
$(SUDO_BIN) chown root $(DESTDIR)$(bindir)/xdg-app-helper
$(SUDO_BIN) chmod u+s $(DESTDIR)$(bindir)/xdg-app-helper
else
if PRIV_MODE_FILECAPS
$(SUDO_BIN) setcap cap_sys_admin+ep $(DESTDIR)$(bindir)/xdg-app-helper
endif
endif
endif
completiondir = $(datadir)/bash-completion/completions
completion_DATA = completion/xdg-app

View File

@@ -61,6 +61,33 @@ PKG_CHECK_MODULES(OSTREE, [libgsystem >= 2015.1 ostree-1 >= 2015.3])
AC_SUBST(OSTREE_CFLAGS)
AC_SUBST(OSTREE_LIBS)
AC_ARG_ENABLE([userns],
AC_HELP_STRING([--disable-userns],
[Disable User namespaces (requires setuid/setcaps)]),
[],
[enable_userns=yes])
AM_CONDITIONAL(DISABLE_USERNS, test "x$enable_userns" = "xno")
if test "x$enable_userns" = "xno"; then
AC_DEFINE([DISABLE_USERNS], [1],
[Define if not using user namespaces])
fi
AC_ARG_WITH(priv-mode,
AS_HELP_STRING([--with-priv-mode=setuid/caps/none],
[How to set privilege-raising during install (only needed if userns disabled)]),
[],
[with_priv_mode="setuid"])
AM_CONDITIONAL(PRIV_MODE_FILECAPS, test "x$with_priv_mode" = "xcaps")
AM_CONDITIONAL(PRIV_MODE_SETUID, test "x$with_priv_mode" = "xsetuid")
AM_CONDITIONAL(PRIV_MODE_FILECAPS, test "x$with_priv_mode" = "xcaps")
AC_ARG_ENABLE(sudo,
AS_HELP_STRING([--enable-sudo],[Use sudo to set setuid flags on binaries during install (only needed if userns disabled)]),
[SUDO_BIN="sudo"], [SUDO_BIN=""])
AC_SUBST([SUDO_BIN])
AC_ARG_ENABLE(documentation,
AC_HELP_STRING([--enable-documentation], [Build documentation]),,
enable_documentation=yes)

View File

@@ -1526,6 +1526,56 @@ do_init (int event_fd, pid_t initial_pid)
return initial_exit_status;
}
#ifdef DISABLE_USERNS
#define REQUIRED_CAPS (CAP_TO_MASK(CAP_SYS_ADMIN))
static void
acquire_caps (void)
{
struct __user_cap_header_struct hdr;
struct __user_cap_data_struct data;
if (getuid () != geteuid ())
{
/* Tell kernel not clear capabilities when dropping root */
if (prctl (PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0)
die_with_error ("prctl(PR_SET_KEEPCAPS) failed");
/* Drop root uid, but retain the required permitted caps */
if (setuid (getuid ()) < 0)
die_with_error ("unable to drop privs");
}
memset (&hdr, 0, sizeof(hdr));
hdr.version = _LINUX_CAPABILITY_VERSION;
/* Drop all non-require capabilities */
data.effective = REQUIRED_CAPS;
data.permitted = REQUIRED_CAPS;
data.inheritable = 0;
if (capset (&hdr, &data) < 0)
die_with_error ("capset failed");
}
static void
drop_caps (void)
{
struct __user_cap_header_struct hdr;
struct __user_cap_data_struct data;
memset (&hdr, 0, sizeof(hdr));
hdr.version = _LINUX_CAPABILITY_VERSION;
data.effective = 0;
data.permitted = 0;
data.inheritable = 0;
if (capset (&hdr, &data) < 0)
die_with_error ("capset failed");
}
#endif
int
main (int argc,
char **argv)
@@ -1558,13 +1608,17 @@ main (int argc,
bool writable_app = FALSE;
bool writable_exports = FALSE;
char old_cwd[256];
char *uid_map, *gid_map;
int c, i;
pid_t pid;
int event_fd;
int sync_fd = -1;
char *endp;
#ifdef DISABLE_USERNS
/* Get the capabilities we need, drop root */
acquire_caps ();
#endif
/* Never gain any more privs during exec */
if (prctl (PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0)
die_with_error ("prctl(PR_SET_NO_NEW_CAPS) failed");
@@ -1750,7 +1804,10 @@ main (int argc,
block_sigchild (); /* Block before we clone to avoid races */
pid = raw_clone (SIGCHLD | CLONE_NEWNS | CLONE_NEWPID | CLONE_NEWUSER |
pid = raw_clone (SIGCHLD | CLONE_NEWNS | CLONE_NEWPID |
#ifndef DISABLE_USERNS
CLONE_NEWUSER |
#endif
(network ? 0 : CLONE_NEWNET) |
(ipc ? 0 : CLONE_NEWIPC),
NULL);
@@ -1763,22 +1820,27 @@ main (int argc,
exit (0); /* Should not be reached, but better safe... */
}
/* This is a bit hacky, but we need to first map the real uid/gid to
0, otherwise we can't mount the devpts filesystem because root is
not mapped. Later we will create another child user namespace and
map back to the real uid */
uid_map = strdup_printf ("0 %d 1\n", uid);
if (!write_file ("/proc/self/uid_map", uid_map))
die_with_error ("setting up uid map");
free (uid_map);
#ifndef DISABLE_USERNS
{
char *uid_map, *gid_map;
/* This is a bit hacky, but we need to first map the real uid/gid to
0, otherwise we can't mount the devpts filesystem because root is
not mapped. Later we will create another child user namespace and
map back to the real uid */
uid_map = strdup_printf ("0 %d 1\n", uid);
if (!write_file ("/proc/self/uid_map", uid_map))
die_with_error ("setting up uid map");
free (uid_map);
if (!write_file("/proc/self/setgroups", "deny\n"))
die_with_error ("error writing to setgroups");
if (!write_file("/proc/self/setgroups", "deny\n"))
die_with_error ("error writing to setgroups");
gid_map = strdup_printf ("0 %d 1\n", gid);
if (!write_file ("/proc/self/gid_map", gid_map))
die_with_error ("setting up gid map");
free (gid_map);
gid_map = strdup_printf ("0 %d 1\n", gid);
if (!write_file ("/proc/self/gid_map", gid_map))
die_with_error ("setting up gid map");
free (gid_map);
}
#endif
old_umask = umask (0);
@@ -1999,6 +2061,11 @@ main (int argc,
umask (old_umask);
#ifdef DISABLE_USERNS
/* Now we have everything we need CAP_SYS_ADMIN for, so drop it */
drop_caps ();
#endif
if (chdir (old_cwd) < 0)
{
/* If the old cwd is not mapped, go to home */
@@ -2026,20 +2093,25 @@ main (int argc,
free (tz_val);
}
/* Now that devpts is mounted we can create a new userspace and map
our uid 1:1 */
if (unshare (CLONE_NEWUSER))
die_with_error ("unshare user ns");
#ifndef DISABLE_USERNS
{
char *uid_map, *gid_map;
/* Now that devpts is mounted we can create a new userspace and map
our uid 1:1 */
if (unshare (CLONE_NEWUSER))
die_with_error ("unshare user ns");
uid_map = strdup_printf ("%d 0 1\n", uid);
if (!write_file ("/proc/self/uid_map", uid_map))
die_with_error ("setting up uid map");
free (uid_map);
uid_map = strdup_printf ("%d 0 1\n", uid);
if (!write_file ("/proc/self/uid_map", uid_map))
die_with_error ("setting up uid map");
free (uid_map);
gid_map = strdup_printf ("%d 0 1\n", gid);
if (!write_file ("/proc/self/gid_map", gid_map))
die_with_error ("setting up gid map");
free (gid_map);
gid_map = strdup_printf ("%d 0 1\n", gid);
if (!write_file ("/proc/self/gid_map", gid_map))
die_with_error ("setting up gid map");
free (gid_map);
}
#endif
__debug__(("forking for child\n"));