52c081b02c
Commit1ecca8439d
("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>
45 lines
2.0 KiB
C
45 lines
2.0 KiB
C
/*
|
|
* Copyright (c) 2013 Eric Biederman
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. The name of the copyright holders or contributors may not be used to
|
|
* endorse or promote products derived from this software without
|
|
* specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
|
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#ifndef _IDMAPPING_H_
|
|
#define _IDMAPPING_H_
|
|
|
|
struct map_range {
|
|
unsigned long upper; /* first ID inside the namespace */
|
|
unsigned long lower; /* first ID outside the namespace */
|
|
unsigned long count; /* Length of the inside and outside ranges */
|
|
};
|
|
|
|
extern struct map_range *get_map_ranges(int ranges, int argc, char **argv);
|
|
extern void write_mapping(int proc_dir_fd, int ranges,
|
|
struct map_range *mappings, const char *map_file, uid_t ruid);
|
|
|
|
#endif /* _ID_MAPPING_H_ */
|
|
|