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 <errno.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 
22 #define LOG_TAG "FirewallController"
23 #define LOG_NDEBUG 0
24 
25 #include <android-base/stringprintf.h>
26 #include <cutils/log.h>
27 
28 #include "NetdConstants.h"
29 #include "FirewallController.h"
30 
31 using android::base::StringAppendF;
32 
33 auto FirewallController::execIptables = ::execIptables;
34 auto FirewallController::execIptablesSilently = ::execIptablesSilently;
35 auto FirewallController::execIptablesRestore = ::execIptablesRestore;
36 
37 const char* FirewallController::TABLE = "filter";
38 
39 const char* FirewallController::LOCAL_INPUT = "fw_INPUT";
40 const char* FirewallController::LOCAL_OUTPUT = "fw_OUTPUT";
41 const char* FirewallController::LOCAL_FORWARD = "fw_FORWARD";
42 
43 const char* FirewallController::LOCAL_DOZABLE = "fw_dozable";
44 const char* FirewallController::LOCAL_STANDBY = "fw_standby";
45 const char* FirewallController::LOCAL_POWERSAVE = "fw_powersave";
46 
47 // ICMPv6 types that are required for any form of IPv6 connectivity to work. Note that because the
48 // fw_dozable chain is called from both INPUT and OUTPUT, this includes both packets that we need
49 // to be able to send (e.g., RS, NS), and packets that we need to receive (e.g., RA, NA).
50 const char* FirewallController::ICMPV6_TYPES[] = {
51     "packet-too-big",
52     "router-solicitation",
53     "router-advertisement",
54     "neighbour-solicitation",
55     "neighbour-advertisement",
56     "redirect",
57 };
58 
FirewallController(void)59 FirewallController::FirewallController(void) {
60     // If no rules are set, it's in BLACKLIST mode
61     mFirewallType = BLACKLIST;
62 }
63 
setupIptablesHooks(void)64 int FirewallController::setupIptablesHooks(void) {
65     int res = 0;
66     res |= createChain(LOCAL_DOZABLE, getFirewallType(DOZABLE));
67     res |= createChain(LOCAL_STANDBY, getFirewallType(STANDBY));
68     res |= createChain(LOCAL_POWERSAVE, getFirewallType(POWERSAVE));
69     return res;
70 }
71 
enableFirewall(FirewallType ftype)72 int FirewallController::enableFirewall(FirewallType ftype) {
73     int res = 0;
74     if (mFirewallType != ftype) {
75         // flush any existing rules
76         disableFirewall();
77 
78         if (ftype == WHITELIST) {
79             // create default rule to drop all traffic
80             res |= execIptables(V4V6, "-A", LOCAL_INPUT, "-j", "DROP", NULL);
81             res |= execIptables(V4V6, "-A", LOCAL_OUTPUT, "-j", "REJECT", NULL);
82             res |= execIptables(V4V6, "-A", LOCAL_FORWARD, "-j", "REJECT", NULL);
83         }
84 
85         // Set this after calling disableFirewall(), since it defaults to WHITELIST there
86         mFirewallType = ftype;
87     }
88     return res;
89 }
90 
disableFirewall(void)91 int FirewallController::disableFirewall(void) {
92     int res = 0;
93 
94     mFirewallType = WHITELIST;
95 
96     // flush any existing rules
97     res |= execIptables(V4V6, "-F", LOCAL_INPUT, NULL);
98     res |= execIptables(V4V6, "-F", LOCAL_OUTPUT, NULL);
99     res |= execIptables(V4V6, "-F", LOCAL_FORWARD, NULL);
100 
101     return res;
102 }
103 
enableChildChains(ChildChain chain,bool enable)104 int FirewallController::enableChildChains(ChildChain chain, bool enable) {
105     int res = 0;
106     const char* name;
107     switch(chain) {
108         case DOZABLE:
109             name = LOCAL_DOZABLE;
110             break;
111         case STANDBY:
112             name = LOCAL_STANDBY;
113             break;
114         case POWERSAVE:
115             name = LOCAL_POWERSAVE;
116             break;
117         default:
118             return res;
119     }
120 
121     std::string command = "*filter\n";
122     for (const char *parent : { LOCAL_INPUT, LOCAL_OUTPUT }) {
123         StringAppendF(&command, "%s %s -j %s\n", (enable ? "-A" : "-D"), parent, name);
124     }
125     StringAppendF(&command, "COMMIT\n");
126 
127     return execIptablesRestore(V4V6, command);
128 }
129 
isFirewallEnabled(void)130 int FirewallController::isFirewallEnabled(void) {
131     // TODO: verify that rules are still in place near top
132     return -1;
133 }
134 
setInterfaceRule(const char * iface,FirewallRule rule)135 int FirewallController::setInterfaceRule(const char* iface, FirewallRule rule) {
136     if (mFirewallType == BLACKLIST) {
137         // Unsupported in BLACKLIST mode
138         return -1;
139     }
140 
141     if (!isIfaceName(iface)) {
142         errno = ENOENT;
143         return -1;
144     }
145 
146     const char* op;
147     if (rule == ALLOW) {
148         op = "-I";
149     } else {
150         op = "-D";
151     }
152 
153     int res = 0;
154     res |= execIptables(V4V6, op, LOCAL_INPUT, "-i", iface, "-j", "RETURN", NULL);
155     res |= execIptables(V4V6, op, LOCAL_OUTPUT, "-o", iface, "-j", "RETURN", NULL);
156     return res;
157 }
158 
setEgressSourceRule(const char * addr,FirewallRule rule)159 int FirewallController::setEgressSourceRule(const char* addr, FirewallRule rule) {
160     if (mFirewallType == BLACKLIST) {
161         // Unsupported in BLACKLIST mode
162         return -1;
163     }
164 
165     IptablesTarget target = V4;
166     if (strchr(addr, ':')) {
167         target = V6;
168     }
169 
170     const char* op;
171     if (rule == ALLOW) {
172         op = "-I";
173     } else {
174         op = "-D";
175     }
176 
177     int res = 0;
178     res |= execIptables(target, op, LOCAL_INPUT, "-d", addr, "-j", "RETURN", NULL);
179     res |= execIptables(target, op, LOCAL_OUTPUT, "-s", addr, "-j", "RETURN", NULL);
180     return res;
181 }
182 
setEgressDestRule(const char * addr,int protocol,int port,FirewallRule rule)183 int FirewallController::setEgressDestRule(const char* addr, int protocol, int port,
184         FirewallRule rule) {
185     if (mFirewallType == BLACKLIST) {
186         // Unsupported in BLACKLIST mode
187         return -1;
188     }
189 
190     IptablesTarget target = V4;
191     if (strchr(addr, ':')) {
192         target = V6;
193     }
194 
195     char protocolStr[16];
196     sprintf(protocolStr, "%d", protocol);
197 
198     char portStr[16];
199     sprintf(portStr, "%d", port);
200 
201     const char* op;
202     if (rule == ALLOW) {
203         op = "-I";
204     } else {
205         op = "-D";
206     }
207 
208     int res = 0;
209     res |= execIptables(target, op, LOCAL_INPUT, "-s", addr, "-p", protocolStr,
210             "--sport", portStr, "-j", "RETURN", NULL);
211     res |= execIptables(target, op, LOCAL_OUTPUT, "-d", addr, "-p", protocolStr,
212             "--dport", portStr, "-j", "RETURN", NULL);
213     return res;
214 }
215 
getFirewallType(ChildChain chain)216 FirewallType FirewallController::getFirewallType(ChildChain chain) {
217     switch(chain) {
218         case DOZABLE:
219             return WHITELIST;
220         case STANDBY:
221             return BLACKLIST;
222         case POWERSAVE:
223             return WHITELIST;
224         case NONE:
225             return mFirewallType;
226         default:
227             return BLACKLIST;
228     }
229 }
230 
setUidRule(ChildChain chain,int uid,FirewallRule rule)231 int FirewallController::setUidRule(ChildChain chain, int uid, FirewallRule rule) {
232     const char* op;
233     const char* target;
234     FirewallType firewallType = getFirewallType(chain);
235     if (firewallType == WHITELIST) {
236         target = "RETURN";
237         // When adding, insert RETURN rules at the front, before the catch-all DROP at the end.
238         op = (rule == ALLOW)? "-I" : "-D";
239     } else { // BLACKLIST mode
240         target = "DROP";
241         // When adding, append DROP rules at the end, after the RETURN rule that matches TCP RSTs.
242         op = (rule == DENY)? "-A" : "-D";
243     }
244 
245     std::vector<std::string> chainNames;
246     switch(chain) {
247         case DOZABLE:
248             chainNames = { LOCAL_DOZABLE };
249             break;
250         case STANDBY:
251             chainNames = { LOCAL_STANDBY };
252             break;
253         case POWERSAVE:
254             chainNames = { LOCAL_POWERSAVE };
255             break;
256         case NONE:
257             chainNames = { LOCAL_INPUT, LOCAL_OUTPUT };
258             break;
259         default:
260             ALOGW("Unknown child chain: %d", chain);
261             return -1;
262     }
263 
264     std::string command = "*filter\n";
265     for (std::string chainName : chainNames) {
266         StringAppendF(&command, "%s %s -m owner --uid-owner %d -j %s\n",
267                       op, chainName.c_str(), uid, target);
268     }
269     StringAppendF(&command, "COMMIT\n");
270 
271     return execIptablesRestore(V4V6, command);
272 }
273 
createChain(const char * chain,FirewallType type)274 int FirewallController::createChain(const char* chain, FirewallType type) {
275     static const std::vector<int32_t> NO_UIDS;
276     return replaceUidChain(chain, type == WHITELIST, NO_UIDS);
277 }
278 
makeUidRules(IptablesTarget target,const char * name,bool isWhitelist,const std::vector<int32_t> & uids)279 std::string FirewallController::makeUidRules(IptablesTarget target, const char *name,
280         bool isWhitelist, const std::vector<int32_t>& uids) {
281     std::string commands;
282     StringAppendF(&commands, "*filter\n:%s -\n", name);
283 
284     // Whitelist chains have UIDs at the beginning, and new UIDs are added with '-I'.
285     if (isWhitelist) {
286         for (auto uid : uids) {
287             StringAppendF(&commands, "-A %s -m owner --uid-owner %d -j RETURN\n", name, uid);
288         }
289 
290         // Always whitelist system UIDs.
291         StringAppendF(&commands,
292                 "-A %s -m owner --uid-owner %d-%d -j RETURN\n", name, 0, MAX_SYSTEM_UID);
293     }
294 
295     // Always allow networking on loopback.
296     StringAppendF(&commands, "-A %s -i lo -j RETURN\n", name);
297     StringAppendF(&commands, "-A %s -o lo -j RETURN\n", name);
298 
299     // Allow TCP RSTs so we can cleanly close TCP connections of apps that no longer have network
300     // access. Both incoming and outgoing RSTs are allowed.
301     StringAppendF(&commands, "-A %s -p tcp --tcp-flags RST RST -j RETURN\n", name);
302 
303     if (isWhitelist) {
304         // Allow ICMPv6 packets necessary to make IPv6 connectivity work. http://b/23158230 .
305         if (target == V6) {
306             for (size_t i = 0; i < ARRAY_SIZE(ICMPV6_TYPES); i++) {
307                 StringAppendF(&commands, "-A %s -p icmpv6 --icmpv6-type %s -j RETURN\n",
308                        name, ICMPV6_TYPES[i]);
309             }
310         }
311     }
312 
313     // Blacklist chains have UIDs at the end, and new UIDs are added with '-A'.
314     if (!isWhitelist) {
315         for (auto uid : uids) {
316             StringAppendF(&commands, "-A %s -m owner --uid-owner %d -j DROP\n", name, uid);
317         }
318     }
319 
320     // If it's a whitelist chain, add a default DROP at the end. This is not necessary for a
321     // blacklist chain, because all user-defined chains implicitly RETURN at the end.
322     if (isWhitelist) {
323         StringAppendF(&commands, "-A %s -j DROP\n", name);
324     }
325 
326     StringAppendF(&commands, "COMMIT\n");
327 
328     return commands;
329 }
330 
replaceUidChain(const char * name,bool isWhitelist,const std::vector<int32_t> & uids)331 int FirewallController::replaceUidChain(
332         const char *name, bool isWhitelist, const std::vector<int32_t>& uids) {
333    std::string commands4 = makeUidRules(V4, name, isWhitelist, uids);
334    std::string commands6 = makeUidRules(V6, name, isWhitelist, uids);
335    return execIptablesRestore(V4, commands4.c_str()) | execIptablesRestore(V6, commands6.c_str());
336 }
337