blob: a84e5b000c22c208f7b3cff851b4b94bf507a18e [file] [log] [blame]
Luis Hector Chaveze97a4b92017-11-02 14:17:43 -07001/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <cutils/uevent.h>
18
19#include <errno.h>
20#include <stdint.h>
21#include <stdio.h>
22#include <string.h>
23#include <strings.h>
24#include <sys/socket.h>
25#include <sys/un.h>
26#include <unistd.h>
27
28#include <linux/netlink.h>
29
30#include <fstream>
31
32#include <private/android_filesystem_config.h>
33
34namespace {
35
36// Returns the uid of root in the current user namespace.
37// Returns AID_OVERFLOWUID if the root user is not mapped in the current
38// namespace.
39// Returns 0 if the kernel is not user namespace-aware (for backwards
40// compatibility) or if AID_OVERFLOWUID could not be validated to match what the
41// kernel would return.
42uid_t GetRootUid() {
43 constexpr uid_t kParentRootUid = 0;
44
45 std::ifstream uid_map_file("/proc/self/uid_map");
46 if (!uid_map_file) {
47 // The kernel does not support user namespaces.
48 return kParentRootUid;
49 }
50
51 uid_t current_namespace_uid, parent_namespace_uid;
52 uint32_t length;
53 while (uid_map_file >> current_namespace_uid >> parent_namespace_uid >> length) {
54 // Since kParentRootUid is 0, it should be the first entry in the mapped
55 // range.
56 if (parent_namespace_uid != kParentRootUid || length < 1) continue;
57 return current_namespace_uid;
58 }
59
60 // Sanity check: verify that the overflow UID is the one to be returned by
61 // the kernel.
62 std::ifstream overflowuid_file("/proc/sys/kernel/overflowuid");
63 if (!overflowuid_file) {
64 // It's better to return 0 in case we cannot make sure that the overflow
65 // UID matches.
66 return kParentRootUid;
67 }
68 uid_t kernel_overflow_uid;
69 if (!(overflowuid_file >> kernel_overflow_uid) || kernel_overflow_uid != AID_OVERFLOWUID)
70 return kParentRootUid;
71
72 // root is unmapped, use the kernel "overflow" uid.
73 return AID_OVERFLOWUID;
74}
75
76} // namespace
77
78extern "C" {
79
80/**
81 * Like recv(), but checks that messages actually originate from the kernel.
82 */
83ssize_t uevent_kernel_multicast_recv(int socket, void* buffer, size_t length) {
84 uid_t uid = -1;
85 return uevent_kernel_multicast_uid_recv(socket, buffer, length, &uid);
86}
87
88/**
89 * Like the above, but passes a uid_t in by pointer. In the event that this
90 * fails due to a bad uid check, the uid_t will be set to the uid of the
91 * socket's peer.
92 *
93 * If this method rejects a netlink message from outside the kernel, it
94 * returns -1, sets errno to EIO, and sets "user" to the UID associated with the
95 * message. If the peer UID cannot be determined, "user" is set to -1."
96 */
97ssize_t uevent_kernel_multicast_uid_recv(int socket, void* buffer, size_t length, uid_t* uid) {
98 return uevent_kernel_recv(socket, buffer, length, true, uid);
99}
100
101ssize_t uevent_kernel_recv(int socket, void* buffer, size_t length, bool require_group, uid_t* uid) {
102 static const uid_t root_uid = GetRootUid();
103 struct iovec iov = {buffer, length};
104 struct sockaddr_nl addr;
105 char control[CMSG_SPACE(sizeof(struct ucred))];
106 struct msghdr hdr = {
107 &addr, sizeof(addr), &iov, 1, control, sizeof(control), 0,
108 };
109 struct ucred* cred;
110
111 *uid = -1;
112 ssize_t n = recvmsg(socket, &hdr, 0);
113 if (n <= 0) {
114 return n;
115 }
116
117 struct cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr);
118 if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) {
119 /* ignoring netlink message with no sender credentials */
120 goto out;
121 }
122
123 cred = (struct ucred*)CMSG_DATA(cmsg);
124 *uid = cred->uid;
125 if (cred->uid != root_uid) {
126 /* ignoring netlink message from non-root user */
127 goto out;
128 }
129
130 if (addr.nl_pid != 0) {
131 /* ignore non-kernel */
132 goto out;
133 }
134 if (require_group && addr.nl_groups == 0) {
135 /* ignore unicast messages when requested */
136 goto out;
137 }
138
139 return n;
140
141out:
142 /* clear residual potentially malicious data */
143 bzero(buffer, length);
144 errno = EIO;
145 return -1;
146}
147
148int uevent_open_socket(int buf_sz, bool passcred) {
149 struct sockaddr_nl addr;
150 int on = passcred;
151 int s;
152
153 memset(&addr, 0, sizeof(addr));
154 addr.nl_family = AF_NETLINK;
155 addr.nl_pid = getpid();
156 addr.nl_groups = 0xffffffff;
157
158 s = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT);
159 if (s < 0) return -1;
160
161 /* buf_sz should be less than net.core.rmem_max for this to succeed */
162 if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &buf_sz, sizeof(buf_sz)) < 0) {
163 close(s);
164 return -1;
165 }
166
167 setsockopt(s, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
168
169 if (bind(s, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
170 close(s);
171 return -1;
172 }
173
174 return s;
175}
176
177} // extern "C"