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