1 /*
2 * Copyright (C) 2012 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 <set>
18
19 #include <errno.h>
20 #include <limits.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <cstdint>
25
26 #define LOG_TAG "FirewallController"
27 #define LOG_NDEBUG 0
28
29 #include <android-base/file.h>
30 #include <android-base/stringprintf.h>
31 #include <android-base/strings.h>
32 #include <log/log.h>
33
34 #include "Controllers.h"
35 #include "FirewallController.h"
36 #include "NetdConstants.h"
37 #include "bpf/BpfUtils.h"
38
39 using android::base::Join;
40 using android::base::ReadFileToString;
41 using android::base::Split;
42 using android::base::StringAppendF;
43 using android::base::StringPrintf;
44 using android::net::gCtls;
45
46 namespace {
47
48 // Default maximum valid uid in a normal root user namespace. The maximum valid uid is used in
49 // rules that exclude all possible UIDs in the namespace in order to match packets that have
50 // no socket associated with them.
51 constexpr const uid_t kDefaultMaximumUid = UID_MAX - 1; // UID_MAX defined as UINT_MAX
52
53 // Proc file containing the uid mapping for the user namespace of the current process.
54 const char kUidMapProcFile[] = "/proc/self/uid_map";
55
56 } // namespace
57
58 namespace android {
59 namespace net {
60
61 auto FirewallController::execIptablesRestore = ::execIptablesRestore;
62
63 const char* FirewallController::TABLE = "filter";
64
65 const char* FirewallController::LOCAL_INPUT = "fw_INPUT";
66 const char* FirewallController::LOCAL_OUTPUT = "fw_OUTPUT";
67 const char* FirewallController::LOCAL_FORWARD = "fw_FORWARD";
68
69 const char* FirewallController::LOCAL_DOZABLE = "fw_dozable";
70 const char* FirewallController::LOCAL_STANDBY = "fw_standby";
71 const char* FirewallController::LOCAL_POWERSAVE = "fw_powersave";
72 const char* FirewallController::LOCAL_RESTRICTED = "fw_restricted";
73
74 // ICMPv6 types that are required for any form of IPv6 connectivity to work. Note that because the
75 // fw_dozable chain is called from both INPUT and OUTPUT, this includes both packets that we need
76 // to be able to send (e.g., RS, NS), and packets that we need to receive (e.g., RA, NA).
77 const char* FirewallController::ICMPV6_TYPES[] = {
78 "packet-too-big",
79 "router-solicitation",
80 "router-advertisement",
81 "neighbour-solicitation",
82 "neighbour-advertisement",
83 "redirect",
84 };
85
FirewallController(void)86 FirewallController::FirewallController(void) : mMaxUid(discoverMaximumValidUid(kUidMapProcFile)) {
87 // If no rules are set, it's in DENYLIST mode
88 mFirewallType = DENYLIST;
89 mIfaceRules = {};
90 }
91
setupIptablesHooks(void)92 int FirewallController::setupIptablesHooks(void) {
93 int res = 0;
94 // mUseBpfOwnerMatch should be removed, but it is still depended upon by test code.
95 mUseBpfOwnerMatch = true;
96 if (mUseBpfOwnerMatch) {
97 return res;
98 }
99 res |= createChain(LOCAL_DOZABLE, getFirewallType(DOZABLE));
100 res |= createChain(LOCAL_STANDBY, getFirewallType(STANDBY));
101 res |= createChain(LOCAL_POWERSAVE, getFirewallType(POWERSAVE));
102 res |= createChain(LOCAL_RESTRICTED, getFirewallType(RESTRICTED));
103 return res;
104 }
105
setFirewallType(FirewallType ftype)106 int FirewallController::setFirewallType(FirewallType ftype) {
107 int res = 0;
108 if (mFirewallType != ftype) {
109 // flush any existing rules
110 resetFirewall();
111
112 if (ftype == ALLOWLIST) {
113 // create default rule to drop all traffic
114 std::string command =
115 "*filter\n"
116 "-A fw_INPUT -j DROP\n"
117 "-A fw_OUTPUT -j REJECT\n"
118 "-A fw_FORWARD -j REJECT\n"
119 "COMMIT\n";
120 res = execIptablesRestore(V4V6, command.c_str());
121 }
122
123 // Set this after calling disableFirewall(), since it defaults to ALLOWLIST there
124 mFirewallType = ftype;
125 }
126 return res ? -EREMOTEIO : 0;
127 }
128
resetFirewall(void)129 int FirewallController::resetFirewall(void) {
130 mFirewallType = ALLOWLIST;
131 mIfaceRules.clear();
132
133 // flush any existing rules
134 std::string command =
135 "*filter\n"
136 ":fw_INPUT -\n"
137 ":fw_OUTPUT -\n"
138 ":fw_FORWARD -\n"
139 "COMMIT\n";
140
141 return (execIptablesRestore(V4V6, command.c_str()) == 0) ? 0 : -EREMOTEIO;
142 }
143
enableChildChains(ChildChain chain,bool enable)144 int FirewallController::enableChildChains(ChildChain chain, bool enable) {
145 int res = 0;
146 const char* name;
147 switch(chain) {
148 case DOZABLE:
149 name = LOCAL_DOZABLE;
150 break;
151 case STANDBY:
152 name = LOCAL_STANDBY;
153 break;
154 case POWERSAVE:
155 name = LOCAL_POWERSAVE;
156 break;
157 case RESTRICTED:
158 name = LOCAL_RESTRICTED;
159 break;
160 default:
161 return res;
162 }
163
164 if (mUseBpfOwnerMatch) {
165 return gCtls->trafficCtrl.toggleUidOwnerMap(chain, enable);
166 }
167
168 std::string command = "*filter\n";
169 for (const char *parent : { LOCAL_INPUT, LOCAL_OUTPUT }) {
170 StringAppendF(&command, "%s %s -j %s\n", (enable ? "-A" : "-D"), parent, name);
171 }
172 StringAppendF(&command, "COMMIT\n");
173
174 return execIptablesRestore(V4V6, command);
175 }
176
isFirewallEnabled(void)177 int FirewallController::isFirewallEnabled(void) {
178 // TODO: verify that rules are still in place near top
179 return -1;
180 }
181
setInterfaceRule(const char * iface,FirewallRule rule)182 int FirewallController::setInterfaceRule(const char* iface, FirewallRule rule) {
183 if (mFirewallType == DENYLIST) {
184 // Unsupported in DENYLIST mode
185 return -EINVAL;
186 }
187
188 if (!isIfaceName(iface)) {
189 errno = ENOENT;
190 return -ENOENT;
191 }
192
193 // Only delete rules if we actually added them, because otherwise our iptables-restore
194 // processes will terminate with "no such rule" errors and cause latency penalties while we
195 // spin up new ones.
196 const char* op;
197 if (rule == ALLOW && mIfaceRules.find(iface) == mIfaceRules.end()) {
198 op = "-I";
199 mIfaceRules.insert(iface);
200 } else if (rule == DENY && mIfaceRules.find(iface) != mIfaceRules.end()) {
201 op = "-D";
202 mIfaceRules.erase(iface);
203 } else {
204 return 0;
205 }
206
207 std::string command = Join(std::vector<std::string> {
208 "*filter",
209 StringPrintf("%s fw_INPUT -i %s -j RETURN", op, iface),
210 StringPrintf("%s fw_OUTPUT -o %s -j RETURN", op, iface),
211 "COMMIT\n"
212 }, "\n");
213 return (execIptablesRestore(V4V6, command) == 0) ? 0 : -EREMOTEIO;
214 }
215
getFirewallType(ChildChain chain)216 FirewallType FirewallController::getFirewallType(ChildChain chain) {
217 switch(chain) {
218 case DOZABLE:
219 return ALLOWLIST;
220 case STANDBY:
221 return DENYLIST;
222 case POWERSAVE:
223 return ALLOWLIST;
224 case RESTRICTED:
225 return ALLOWLIST;
226 case NONE:
227 return mFirewallType;
228 default:
229 return DENYLIST;
230 }
231 }
232
setUidRule(ChildChain chain,int uid,FirewallRule rule)233 int FirewallController::setUidRule(ChildChain chain, int uid, FirewallRule rule) {
234 const char* op;
235 const char* target;
236 FirewallType firewallType = getFirewallType(chain);
237 if (firewallType == ALLOWLIST) {
238 target = "RETURN";
239 // When adding, insert RETURN rules at the front, before the catch-all DROP at the end.
240 op = (rule == ALLOW)? "-I" : "-D";
241 } else { // DENYLIST mode
242 target = "DROP";
243 // When adding, append DROP rules at the end, after the RETURN rule that matches TCP RSTs.
244 op = (rule == DENY)? "-A" : "-D";
245 }
246
247 std::vector<std::string> chainNames;
248 switch(chain) {
249 case DOZABLE:
250 chainNames = {LOCAL_DOZABLE};
251 break;
252 case STANDBY:
253 chainNames = {LOCAL_STANDBY};
254 break;
255 case POWERSAVE:
256 chainNames = {LOCAL_POWERSAVE};
257 break;
258 case RESTRICTED:
259 chainNames = {LOCAL_RESTRICTED};
260 break;
261 case NONE:
262 chainNames = {LOCAL_INPUT, LOCAL_OUTPUT};
263 break;
264 default:
265 ALOGW("Unknown child chain: %d", chain);
266 return -EINVAL;
267 }
268 if (mUseBpfOwnerMatch) {
269 return gCtls->trafficCtrl.changeUidOwnerRule(chain, uid, rule, firewallType);
270 }
271
272 std::string command = "*filter\n";
273 for (const std::string& chainName : chainNames) {
274 StringAppendF(&command, "%s %s -m owner --uid-owner %d -j %s\n",
275 op, chainName.c_str(), uid, target);
276 }
277 StringAppendF(&command, "COMMIT\n");
278
279 return (execIptablesRestore(V4V6, command) == 0) ? 0 : -EREMOTEIO;
280 }
281
createChain(const char * chain,FirewallType type)282 int FirewallController::createChain(const char* chain, FirewallType type) {
283 static const std::vector<int32_t> NO_UIDS;
284 return replaceUidChain(chain, type == ALLOWLIST, NO_UIDS);
285 }
286
287 /* static */
makeCriticalCommands(IptablesTarget target,const char * chainName)288 std::string FirewallController::makeCriticalCommands(IptablesTarget target, const char* chainName) {
289 // Allow ICMPv6 packets necessary to make IPv6 connectivity work. http://b/23158230 .
290 std::string commands;
291 if (target == V6) {
292 for (size_t i = 0; i < ARRAY_SIZE(ICMPV6_TYPES); i++) {
293 StringAppendF(&commands, "-A %s -p icmpv6 --icmpv6-type %s -j RETURN\n",
294 chainName, ICMPV6_TYPES[i]);
295 }
296 }
297 return commands;
298 }
299
makeUidRules(IptablesTarget target,const char * name,bool isAllowlist,const std::vector<int32_t> & uids)300 std::string FirewallController::makeUidRules(IptablesTarget target, const char* name,
301 bool isAllowlist, const std::vector<int32_t>& uids) {
302 std::string commands;
303 StringAppendF(&commands, "*filter\n:%s -\n", name);
304
305 // Allowlist chains have UIDs at the beginning, and new UIDs are added with '-I'.
306 if (isAllowlist) {
307 for (auto uid : uids) {
308 StringAppendF(&commands, "-A %s -m owner --uid-owner %d -j RETURN\n", name, uid);
309 }
310
311 // Always allowlist system UIDs.
312 StringAppendF(&commands,
313 "-A %s -m owner --uid-owner %d-%d -j RETURN\n", name, 0, MAX_SYSTEM_UID);
314
315 // This rule inverts the match for all UIDs; ie, if there is no UID match here,
316 // there is no socket to be found
317 StringAppendF(&commands,
318 "-A %s -m owner ! --uid-owner %d-%u -j RETURN\n", name, 0, mMaxUid);
319
320 // Always allowlist traffic with protocol ESP, or no known socket - required for IPSec
321 StringAppendF(&commands, "-A %s -p esp -j RETURN\n", name);
322 }
323
324 // Always allow networking on loopback.
325 StringAppendF(&commands, "-A %s -i lo -j RETURN\n", name);
326 StringAppendF(&commands, "-A %s -o lo -j RETURN\n", name);
327
328 // Allow TCP RSTs so we can cleanly close TCP connections of apps that no longer have network
329 // access. Both incoming and outgoing RSTs are allowed.
330 StringAppendF(&commands, "-A %s -p tcp --tcp-flags RST RST -j RETURN\n", name);
331
332 if (isAllowlist) {
333 commands.append(makeCriticalCommands(target, name));
334 }
335
336 // Denylist chains have UIDs at the end, and new UIDs are added with '-A'.
337 if (!isAllowlist) {
338 for (auto uid : uids) {
339 StringAppendF(&commands, "-A %s -m owner --uid-owner %d -j DROP\n", name, uid);
340 }
341 }
342
343 // If it's an allowlist chain, add a default DROP at the end. This is not necessary for a
344 // denylist chain, because all user-defined chains implicitly RETURN at the end.
345 if (isAllowlist) {
346 StringAppendF(&commands, "-A %s -j DROP\n", name);
347 }
348
349 StringAppendF(&commands, "COMMIT\n");
350
351 return commands;
352 }
353
replaceUidChain(const std::string & name,bool isAllowlist,const std::vector<int32_t> & uids)354 int FirewallController::replaceUidChain(const std::string& name, bool isAllowlist,
355 const std::vector<int32_t>& uids) {
356 if (mUseBpfOwnerMatch) {
357 return gCtls->trafficCtrl.replaceUidOwnerMap(name, isAllowlist, uids);
358 }
359 std::string commands4 = makeUidRules(V4, name.c_str(), isAllowlist, uids);
360 std::string commands6 = makeUidRules(V6, name.c_str(), isAllowlist, uids);
361 return execIptablesRestore(V4, commands4.c_str()) | execIptablesRestore(V6, commands6.c_str());
362 }
363
364 /* static */
discoverMaximumValidUid(const std::string & fileName)365 uid_t FirewallController::discoverMaximumValidUid(const std::string& fileName) {
366 std::string content;
367 if (!ReadFileToString(fileName, &content, false)) {
368 // /proc/self/uid_map only exists if a uid mapping has been set.
369 ALOGD("Could not read %s, max uid defaulting to %u", fileName.c_str(), kDefaultMaximumUid);
370 return kDefaultMaximumUid;
371 }
372
373 std::vector<std::string> lines = Split(content, "\n");
374 if (lines.empty()) {
375 ALOGD("%s was empty, max uid defaulting to %u", fileName.c_str(), kDefaultMaximumUid);
376 return kDefaultMaximumUid;
377 }
378
379 uint32_t maxUid = 0;
380 for (const auto& line : lines) {
381 if (line.empty()) {
382 continue;
383 }
384
385 // Choose the end of the largest range found in the file.
386 uint32_t start;
387 uint32_t ignored;
388 uint32_t rangeLength;
389 int items = sscanf(line.c_str(), "%u %u %u", &start, &ignored, &rangeLength);
390 if (items != 3) {
391 // uid_map lines must have 3 items, see the man page of 'user_namespaces' for details.
392 ALOGD("Format of %s unrecognized, max uid defaulting to %u", fileName.c_str(),
393 kDefaultMaximumUid);
394 return kDefaultMaximumUid;
395 }
396 maxUid = std::max(maxUid, start + rangeLength - 1);
397 }
398
399 if (maxUid == 0) {
400 ALOGD("No max uid found, max uid defaulting to %u", kDefaultMaximumUid);
401 return kDefaultMaximumUid;
402 }
403
404 return maxUid;
405 }
406
407 } // namespace net
408 } // namespace android
409