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
This commit is contained in:
parent
0d323c12df
commit
e97a4b9799
|
@ -112,7 +112,7 @@ cc_library {
|
|||
"properties.cpp",
|
||||
"qtaguid.c",
|
||||
"trace-dev.c",
|
||||
"uevent.c",
|
||||
"uevent.cpp",
|
||||
],
|
||||
sanitize: {
|
||||
misc_undefined: ["integer"],
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -17,7 +17,8 @@
|
|||
#include <cutils/uevent.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <sys/socket.h>
|
||||
|
@ -26,11 +27,60 @@
|
|||
|
||||
#include <linux/netlink.h>
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include <private/android_filesystem_config.h>
|
||||
|
||||
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 @@ out:
|
|||
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"
|
Loading…
Reference in New Issue