simplify the condition for setting the euid of the process. Now it is
always set when we are running as root, the issue was introduced with
the commit 52c081b02c
Changelog: 2018-11-24 - seh - enforce that euid only gets set to ruid if
it currently == 0 (i.e. really was setuid-*root*).
Closes: https://github.com/genuinetools/img/issues/191
Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
Signed-off-by: Serge Hallyn <shallyn@cisco.com>
Commit 1ecca8439d ("new[ug]idmap: not require CAP_SYS_ADMIN in the parent userNS")
does contain a wrong commit message, is lacking an explanation of the
issue, misses some simplifications and hardening features. This commit
tries to rectify this.
In (crazy) environment where all capabilities are dropped from the
capability bounding set apart from CAP_SET{G,U}ID setuid- and
fscaps-based new{g,u}idmap binaries behave differently when writing
complex mappings for an unprivileged user:
1. newuidmap is setuid
unshare -U sleep infinity &
newuidmap $? 0 100000 65536
First file_ns_capable(file, ns, CAP_SYS_ADMIN) is hit. This calls into
cap_capable() and hits the loop
for (;;) {
/* Do we have the necessary capabilities? */
if (ns == cred->user_ns)
return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM;
/*
* If we're already at a lower level than we're looking for,
* we're done searching.
*/
if (ns->level <= cred->user_ns->level)
return -EPERM;
/*
* The owner of the user namespace in the parent of the
* user namespace has all caps.
*/
if ((ns->parent == cred->user_ns) && uid_eq(ns->owner, cred->euid))
return 0;
/*
* If you have a capability in a parent user ns, then you have
* it over all children user namespaces as well.
*/
ns = ns->parent;
}
The first check fails and falls through to the end of the loop and
retrieves the parent user namespace and checks whether CAP_SYS_ADMIN is
available there which isn't.
2. newuidmap has CAP_SETUID as fscaps set
unshare -U sleep infinity &
newuidmap $? 0 100000 65536
The first file_ns_capable() check for CAP_SYS_ADMIN is passed since the
euid has not been changed:
if ((ns->parent == cred->user_ns) && uid_eq(ns->owner, cred->euid))
return 0;
Now new_idmap_permitted() is hit which calls ns_capable(ns->parent,
CAP_SET{G,U}ID). This check passes since CAP_SET{G,U}ID is available in
the parent user namespace.
Now file_ns_capable(file, ns->parent, CAP_SETUID) is hit and the
cap_capable() loop (see above) is entered again. This passes
if (ns == cred->user_ns)
return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM;
since CAP_SET{G,U}ID is available in the parent user namespace. Now the
mapping can be written.
There is no need for this descrepancy between setuid and fscaps based
new{g,u}idmap binaries. The solution is to do a
seteuid() back to the unprivileged uid and PR_SET_KEEPCAPS to keep
CAP_SET{G,U}ID. The seteuid() will cause the
file_ns_capable(file, ns, CAP_SYS_ADMIN) check to pass and the
PR_SET_KEEPCAPS for CAP_SET{G,U}ID will cause the CAP_SET{G,U}ID to
pass.
Fixes: 1ecca8439d ("new[ug]idmap: not require CAP_SYS_ADMIN in the parent userNS")
Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
if the euid!=owner of the userns, the kernel returns EPERM when trying
to write the uidmap and there is no CAP_SYS_ADMIN in the parent
namespace.
Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
The number of ranges should be the ceiling of the number of arguments divided
by three.
Without this fix newuidmap and newgidmap always report and error and fail,
which is very much not what we want.
Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
Signed-off-by: Serge Hallyn <serge.hallyn@ubuntu.com>
In find_new_sub_{u,g}ids, check for min, count and max values.
In idmapping.c:get_map_ranges(), make sure that the value passed
in for ranges did not overflow. Couldn't happen with the current
code, but this is a sanity check for any future potential mis-uses.
Signed-off-by: Serge Hallyn <serge.hallyn@ubuntu.com>