From e97a4b9799f25b10c50fccb20eb0af69f14fe8dc Mon Sep 17 00:00:00 2001 From: Luis Hector Chavez Date: Thu, 2 Nov 2017 14:17:43 -0700 Subject: [PATCH] libcutils: Make uevent_kernel_* namespace-aware This change adds user namespace-awareness to uevent_kernel_* in libcutils. Instead of assuming that root is always uid 0, it detects whether the uid 0 is mapped in the current user namespace and returns the appropriately mapped uid (or the kernel's "overflowuid" in case it is not mapped). In older kernels, or those where user namespaces are not enabled, this still uses uid 0 for root. Bug: 62378620 Test: bullhead networking still works Test: Android in Chrome OS can now receive netlink-related messages Change-Id: I7ea3454e8f38b9c70c65294d6b2a99e5a88f9d70 --- libcutils/Android.bp | 2 +- .../private/android_filesystem_config.h | 8 ++ libcutils/{uevent.c => uevent.cpp} | 90 ++++++++++++++----- 3 files changed, 76 insertions(+), 24 deletions(-) rename libcutils/{uevent.c => uevent.cpp} (54%) diff --git a/libcutils/Android.bp b/libcutils/Android.bp index cfe8d2931c7..f4833086736 100644 --- a/libcutils/Android.bp +++ b/libcutils/Android.bp @@ -112,7 +112,7 @@ cc_library { "properties.cpp", "qtaguid.c", "trace-dev.c", - "uevent.c", + "uevent.cpp", ], sanitize: { misc_undefined: ["integer"], diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h index 55ece54a52d..2ecf5bce697 100644 --- a/libcutils/include/private/android_filesystem_config.h +++ b/libcutils/include/private/android_filesystem_config.h @@ -170,6 +170,14 @@ #define AID_SHARED_GID_START 50000 /* start of gids for apps in each user to share */ #define AID_SHARED_GID_END 59999 /* end of gids for apps in each user to share */ +/* + * This is a magic number in the kernel and not something that was picked + * arbitrarily. This value is returned whenever a uid that has no mapping in the + * user namespace is returned to userspace: + * https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/linux/highuid.h?h=v4.4#n40 + */ +#define AID_OVERFLOWUID 65534 /* unmapped user in the user namespace */ + #define AID_ISOLATED_START 99000 /* start of uids for fully isolated sandboxed processes */ #define AID_ISOLATED_END 99999 /* end of uids for fully isolated sandboxed processes */ diff --git a/libcutils/uevent.c b/libcutils/uevent.cpp similarity index 54% rename from libcutils/uevent.c rename to libcutils/uevent.cpp index f548dca2f6a..a84e5b000c2 100644 --- a/libcutils/uevent.c +++ b/libcutils/uevent.cpp @@ -17,7 +17,8 @@ #include #include -#include +#include +#include #include #include #include @@ -26,11 +27,60 @@ #include +#include + +#include + +namespace { + +// Returns the uid of root in the current user namespace. +// Returns AID_OVERFLOWUID if the root user is not mapped in the current +// namespace. +// Returns 0 if the kernel is not user namespace-aware (for backwards +// compatibility) or if AID_OVERFLOWUID could not be validated to match what the +// kernel would return. +uid_t GetRootUid() { + constexpr uid_t kParentRootUid = 0; + + std::ifstream uid_map_file("/proc/self/uid_map"); + if (!uid_map_file) { + // The kernel does not support user namespaces. + return kParentRootUid; + } + + uid_t current_namespace_uid, parent_namespace_uid; + uint32_t length; + while (uid_map_file >> current_namespace_uid >> parent_namespace_uid >> length) { + // Since kParentRootUid is 0, it should be the first entry in the mapped + // range. + if (parent_namespace_uid != kParentRootUid || length < 1) continue; + return current_namespace_uid; + } + + // Sanity check: verify that the overflow UID is the one to be returned by + // the kernel. + std::ifstream overflowuid_file("/proc/sys/kernel/overflowuid"); + if (!overflowuid_file) { + // It's better to return 0 in case we cannot make sure that the overflow + // UID matches. + return kParentRootUid; + } + uid_t kernel_overflow_uid; + if (!(overflowuid_file >> kernel_overflow_uid) || kernel_overflow_uid != AID_OVERFLOWUID) + return kParentRootUid; + + // root is unmapped, use the kernel "overflow" uid. + return AID_OVERFLOWUID; +} + +} // namespace + +extern "C" { + /** * Like recv(), but checks that messages actually originate from the kernel. */ -ssize_t uevent_kernel_multicast_recv(int socket, void *buffer, size_t length) -{ +ssize_t uevent_kernel_multicast_recv(int socket, void* buffer, size_t length) { uid_t uid = -1; return uevent_kernel_multicast_uid_recv(socket, buffer, length, &uid); } @@ -44,25 +94,19 @@ ssize_t uevent_kernel_multicast_recv(int socket, void *buffer, size_t length) * returns -1, sets errno to EIO, and sets "user" to the UID associated with the * message. If the peer UID cannot be determined, "user" is set to -1." */ -ssize_t uevent_kernel_multicast_uid_recv(int socket, void *buffer, size_t length, uid_t *uid) -{ +ssize_t uevent_kernel_multicast_uid_recv(int socket, void* buffer, size_t length, uid_t* uid) { return uevent_kernel_recv(socket, buffer, length, true, uid); } -ssize_t uevent_kernel_recv(int socket, void *buffer, size_t length, bool require_group, uid_t *uid) -{ - struct iovec iov = { buffer, length }; +ssize_t uevent_kernel_recv(int socket, void* buffer, size_t length, bool require_group, uid_t* uid) { + static const uid_t root_uid = GetRootUid(); + struct iovec iov = {buffer, length}; struct sockaddr_nl addr; char control[CMSG_SPACE(sizeof(struct ucred))]; struct msghdr hdr = { - &addr, - sizeof(addr), - &iov, - 1, - control, - sizeof(control), - 0, + &addr, sizeof(addr), &iov, 1, control, sizeof(control), 0, }; + struct ucred* cred; *uid = -1; ssize_t n = recvmsg(socket, &hdr, 0); @@ -70,15 +114,15 @@ ssize_t uevent_kernel_recv(int socket, void *buffer, size_t length, bool require return n; } - struct cmsghdr *cmsg = CMSG_FIRSTHDR(&hdr); + struct cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr); if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) { /* ignoring netlink message with no sender credentials */ goto out; } - struct ucred *cred = (struct ucred *)CMSG_DATA(cmsg); + cred = (struct ucred*)CMSG_DATA(cmsg); *uid = cred->uid; - if (cred->uid != 0) { + if (cred->uid != root_uid) { /* ignoring netlink message from non-root user */ goto out; } @@ -101,8 +145,7 @@ ssize_t uevent_kernel_recv(int socket, void *buffer, size_t length, bool require return -1; } -int uevent_open_socket(int buf_sz, bool passcred) -{ +int uevent_open_socket(int buf_sz, bool passcred) { struct sockaddr_nl addr; int on = passcred; int s; @@ -113,8 +156,7 @@ int uevent_open_socket(int buf_sz, bool passcred) addr.nl_groups = 0xffffffff; s = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT); - if(s < 0) - return -1; + if (s < 0) return -1; /* buf_sz should be less than net.core.rmem_max for this to succeed */ if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &buf_sz, sizeof(buf_sz)) < 0) { @@ -124,10 +166,12 @@ int uevent_open_socket(int buf_sz, bool passcred) setsockopt(s, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); - if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + if (bind(s, (struct sockaddr*)&addr, sizeof(addr)) < 0) { close(s); return -1; } return s; } + +} // extern "C"