Skip to content

Commit

Permalink
Add experimental support for non-privileged chroot on Linux
Browse files Browse the repository at this point in the history
If host kernel supports user namespaces, this allows non-privileged users
to perform chrooted operations such as installations and verification.
With caveats. Only root:root ownership is supported in the namespace, so
packages with other file ownerships will fail to install properly, chown()
fails with -EINVAL similarly to what happens on squashed NFS-mount. We
don't handle that particularly gracefully.

Also add the obligatory disabler, and use it for the test-suite for
now. Only two tests (61 and 342) actually fail because of it, simply
because things are ... different with user namespaces.
  • Loading branch information
pmatilai committed Jan 17, 2019
1 parent 61b0287 commit b4c832c
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 1 deletion.
1 change: 1 addition & 0 deletions configure.ac
Expand Up @@ -761,6 +761,7 @@ AC_CHECK_FUNCS(getauxval)
AC_CHECK_FUNCS(setprogname, [], [], [#include <stdlib.h>])
AC_CHECK_FUNCS(syncfs)
AC_CHECK_FUNCS(sched_getaffinity, [], [], [#include <sched.h>])
AC_CHECK_FUNCS(unshare, [], [], [#include <sched.h>])

AC_MSG_CHECKING([whether __progname is defined])
AC_LINK_IFELSE([AC_LANG_PROGRAM([extern const char *__progname;],
Expand Down
4 changes: 4 additions & 0 deletions lib/poptALL.c
Expand Up @@ -26,6 +26,8 @@ static int _debug = 0;

extern int _rpmds_nopromote;

extern int _rpm_nouserns;

extern int _fsm_debug;

extern int _print_pkts;
Expand Down Expand Up @@ -263,6 +265,8 @@ struct poptOption rpmcliAllPoptTable[] = {
NULL, NULL},
{ "rpmiodebug", '\0', POPT_ARG_VAL|POPT_ARGFLAG_DOC_HIDDEN, &_rpmio_debug, -1,
N_("debug rpmio I/O"), NULL},
{ "nouserns", '\0', POPT_ARG_VAL|POPT_ARGFLAG_DOC_HIDDEN, &_rpm_nouserns, -1,
N_("disable user namespace support"), NULL},
{ "stats", '\0', POPT_ARG_VAL|POPT_ARGFLAG_DOC_HIDDEN, &_rpmts_stats, -1,
NULL, NULL},

Expand Down
59 changes: 59 additions & 0 deletions lib/rpmchroot.c
@@ -1,10 +1,13 @@
#include "system.h"
#include <sched.h>
#include <stdlib.h>
#include <rpm/rpmstring.h>
#include <rpm/rpmlog.h>
#include "lib/rpmchroot.h"
#include "debug.h"

int _rpm_nouserns = 0;

struct rootState_s {
char *rootDir;
int chrootDone;
Expand All @@ -18,6 +21,60 @@ static struct rootState_s rootState = {
.cwd = -1,
};

#if defined(HAVE_UNSHARE) && defined(CLONE_NEWUSER)
/*
* If setgroups file exists (Linux >= 3.19), we need to write "deny" to it,
* otherwise gid_map will fail.
*/
static int deny_setgroups(void)
{
int fd = open("/proc/self/setgroups", O_WRONLY, 0);
int xx = -1;
if (fd >= 0) {
xx = write(fd, "deny\n", strlen("deny\n"));
close (fd);
}
return (xx == -1);
}

static int setup_map(const char *path, unsigned int id, unsigned int oid)
{
int xx = -1;
int fd = open(path, O_WRONLY);
if (fd >= 0) {
char buf[256];
int ret = snprintf(buf, sizeof(buf), "%u %u 1\n", id, oid);
xx = write(fd, buf, ret);
close (fd);
}
return (xx == -1);
}

/*
* Try to become root by creating a user namespace. We don't really care
* if this fails here because in that case chroot() will just fail as it
* normally would.
*/
static void try_become_root(void)
{
static int unshared = 0;
uid_t uid = getuid();
gid_t gid = getgid();
if (!unshared && unshare(CLONE_NEWUSER | CLONE_NEWNS) == 0) {
deny_setgroups();
setup_map("/proc/self/uid_map", 0, uid);
setup_map("/proc/self/gid_map", 0, gid);
unshared = 1;
}
rpmlog(RPMLOG_DEBUG, "user ns: %d original user %d:%d current %d:%d\n",
unshared, uid, gid, getuid(), getgid());
}
#else
static void try_become_root(void)
{
}
#endif

int rpmChrootSet(const char *rootDir)
{
int rc = 0;
Expand All @@ -43,6 +100,8 @@ int rpmChrootSet(const char *rootDir)
rpmlog(RPMLOG_ERR, _("Unable to open current directory: %m\n"));
rc = -1;
}
if (!_rpm_nouserns && rc == 0 && getuid())
try_become_root();
}

return rc;
Expand Down
2 changes: 1 addition & 1 deletion tests/atlocal.in
Expand Up @@ -58,7 +58,7 @@ function rundebug()
function runroot()
{
(unset RPM_CONFIGDIR RPM_POPTEXEC_PATH; cd ${RPMTEST} && \
MAGIC="/magic/magic" FAKECHROOT_BASE="${RPMTEST}" fakechroot "$@" --define "_topdir /build" --noplugins
MAGIC="/magic/magic" FAKECHROOT_BASE="${RPMTEST}" fakechroot "$@" --define "_topdir /build" --noplugins --nouserns
)
}

Expand Down

0 comments on commit b4c832c

Please sign in to comment.