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