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