blob: c0c52301cb58a035f75865476e7d6a29ea3fbab2 [file] [log] [blame]
Jeff Sharkeyd8c64022012-07-13 18:04:07 -07001/*
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
Lorenzo Colitti89faa342016-02-26 11:38:47 +090025#include <android-base/stringprintf.h>
Jeff Sharkeyd8c64022012-07-13 18:04:07 -070026#include <cutils/log.h>
Xiaohui Chenfeb2b612015-06-25 21:19:38 -070027#include <private/android_filesystem_config.h>
Jeff Sharkeyd8c64022012-07-13 18:04:07 -070028
29#include "NetdConstants.h"
30#include "FirewallController.h"
31
Lorenzo Colitti89faa342016-02-26 11:38:47 +090032using android::base::StringAppendF;
33
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -070034const char* FirewallController::TABLE = "filter";
35
Jeff Sharkeyd8c64022012-07-13 18:04:07 -070036const char* FirewallController::LOCAL_INPUT = "fw_INPUT";
37const char* FirewallController::LOCAL_OUTPUT = "fw_OUTPUT";
38const char* FirewallController::LOCAL_FORWARD = "fw_FORWARD";
39
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -070040const char* FirewallController::LOCAL_DOZABLE = "fw_dozable";
41const char* FirewallController::LOCAL_STANDBY = "fw_standby";
Felipe Leme3f624342016-02-10 18:12:39 -080042const char* FirewallController::LOCAL_POWERSAVE = "fw_powersave";
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -070043
Lorenzo Colittic8683d72015-09-01 16:53:35 +090044// ICMPv6 types that are required for any form of IPv6 connectivity to work. Note that because the
45// fw_dozable chain is called from both INPUT and OUTPUT, this includes both packets that we need
46// to be able to send (e.g., RS, NS), and packets that we need to receive (e.g., RA, NA).
47const char* FirewallController::ICMPV6_TYPES[] = {
48 "packet-too-big",
49 "router-solicitation",
50 "router-advertisement",
51 "neighbour-solicitation",
52 "neighbour-advertisement",
53 "redirect",
54};
55
Lorenzo Colitti89faa342016-02-26 11:38:47 +090056const int MAX_SYSTEM_UID = AID_APP - 1;
57
Jeff Sharkeyd8c64022012-07-13 18:04:07 -070058FirewallController::FirewallController(void) {
Amith Yamasani390e4ea2015-04-25 19:08:57 -070059 // If no rules are set, it's in BLACKLIST mode
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -070060 mFirewallType = BLACKLIST;
Jeff Sharkeyd8c64022012-07-13 18:04:07 -070061}
62
63int FirewallController::setupIptablesHooks(void) {
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -070064 int res = 0;
65 // child chains are created but not attached, they will be attached explicitly.
66 FirewallType firewallType = getFirewallType(DOZABLE);
67 res |= createChain(LOCAL_DOZABLE, LOCAL_INPUT, firewallType);
68
69 firewallType = getFirewallType(STANDBY);
70 res |= createChain(LOCAL_STANDBY, LOCAL_INPUT, firewallType);
71
Felipe Leme3f624342016-02-10 18:12:39 -080072 firewallType = getFirewallType(POWERSAVE);
73 res |= createChain(LOCAL_POWERSAVE, LOCAL_INPUT, firewallType);
74
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -070075 return res;
Jeff Sharkeyd8c64022012-07-13 18:04:07 -070076}
77
Amith Yamasani390e4ea2015-04-25 19:08:57 -070078int FirewallController::enableFirewall(FirewallType ftype) {
Jeff Sharkeyd8c64022012-07-13 18:04:07 -070079 int res = 0;
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -070080 if (mFirewallType != ftype) {
81 // flush any existing rules
82 disableFirewall();
Jeff Sharkeyd8c64022012-07-13 18:04:07 -070083
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -070084 if (ftype == WHITELIST) {
85 // create default rule to drop all traffic
86 res |= execIptables(V4V6, "-A", LOCAL_INPUT, "-j", "DROP", NULL);
87 res |= execIptables(V4V6, "-A", LOCAL_OUTPUT, "-j", "REJECT", NULL);
88 res |= execIptables(V4V6, "-A", LOCAL_FORWARD, "-j", "REJECT", NULL);
89 }
Jeff Sharkeyd8c64022012-07-13 18:04:07 -070090
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -070091 // Set this after calling disableFirewall(), since it defaults to WHITELIST there
92 mFirewallType = ftype;
Amith Yamasani390e4ea2015-04-25 19:08:57 -070093 }
Jeff Sharkeyd8c64022012-07-13 18:04:07 -070094 return res;
95}
96
97int FirewallController::disableFirewall(void) {
98 int res = 0;
99
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -0700100 mFirewallType = WHITELIST;
Amith Yamasani390e4ea2015-04-25 19:08:57 -0700101
Jeff Sharkeyd8c64022012-07-13 18:04:07 -0700102 // flush any existing rules
103 res |= execIptables(V4V6, "-F", LOCAL_INPUT, NULL);
104 res |= execIptables(V4V6, "-F", LOCAL_OUTPUT, NULL);
105 res |= execIptables(V4V6, "-F", LOCAL_FORWARD, NULL);
106
107 return res;
108}
109
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -0700110int FirewallController::enableChildChains(ChildChain chain, bool enable) {
111 int res = 0;
112 const char* name;
113 switch(chain) {
114 case DOZABLE:
115 name = LOCAL_DOZABLE;
116 break;
117 case STANDBY:
118 name = LOCAL_STANDBY;
119 break;
Felipe Leme3f624342016-02-10 18:12:39 -0800120 case POWERSAVE:
121 name = LOCAL_POWERSAVE;
122 break;
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -0700123 default:
124 return res;
125 }
126
127 if (enable) {
128 res |= attachChain(name, LOCAL_INPUT);
129 res |= attachChain(name, LOCAL_OUTPUT);
130 } else {
131 res |= detachChain(name, LOCAL_INPUT);
132 res |= detachChain(name, LOCAL_OUTPUT);
133 }
134 return res;
135}
136
Jeff Sharkeyd8c64022012-07-13 18:04:07 -0700137int FirewallController::isFirewallEnabled(void) {
138 // TODO: verify that rules are still in place near top
139 return -1;
140}
141
142int FirewallController::setInterfaceRule(const char* iface, FirewallRule rule) {
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -0700143 if (mFirewallType == BLACKLIST) {
Amith Yamasani390e4ea2015-04-25 19:08:57 -0700144 // Unsupported in BLACKLIST mode
145 return -1;
146 }
147
JP Abgrall69261cb2014-06-19 18:35:24 -0700148 if (!isIfaceName(iface)) {
149 errno = ENOENT;
150 return -1;
151 }
152
Jeff Sharkeyd8c64022012-07-13 18:04:07 -0700153 const char* op;
154 if (rule == ALLOW) {
155 op = "-I";
156 } else {
157 op = "-D";
158 }
159
160 int res = 0;
161 res |= execIptables(V4V6, op, LOCAL_INPUT, "-i", iface, "-j", "RETURN", NULL);
162 res |= execIptables(V4V6, op, LOCAL_OUTPUT, "-o", iface, "-j", "RETURN", NULL);
163 return res;
164}
165
166int FirewallController::setEgressSourceRule(const char* addr, FirewallRule rule) {
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -0700167 if (mFirewallType == BLACKLIST) {
Amith Yamasani390e4ea2015-04-25 19:08:57 -0700168 // Unsupported in BLACKLIST mode
169 return -1;
170 }
171
Jeff Sharkeyd8c64022012-07-13 18:04:07 -0700172 IptablesTarget target = V4;
173 if (strchr(addr, ':')) {
174 target = V6;
175 }
176
177 const char* op;
178 if (rule == ALLOW) {
179 op = "-I";
180 } else {
181 op = "-D";
182 }
183
184 int res = 0;
185 res |= execIptables(target, op, LOCAL_INPUT, "-d", addr, "-j", "RETURN", NULL);
186 res |= execIptables(target, op, LOCAL_OUTPUT, "-s", addr, "-j", "RETURN", NULL);
187 return res;
188}
189
190int FirewallController::setEgressDestRule(const char* addr, int protocol, int port,
191 FirewallRule rule) {
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -0700192 if (mFirewallType == BLACKLIST) {
Amith Yamasani390e4ea2015-04-25 19:08:57 -0700193 // Unsupported in BLACKLIST mode
194 return -1;
195 }
196
Jeff Sharkeyd8c64022012-07-13 18:04:07 -0700197 IptablesTarget target = V4;
198 if (strchr(addr, ':')) {
199 target = V6;
200 }
201
202 char protocolStr[16];
203 sprintf(protocolStr, "%d", protocol);
204
205 char portStr[16];
206 sprintf(portStr, "%d", port);
207
208 const char* op;
209 if (rule == ALLOW) {
210 op = "-I";
211 } else {
212 op = "-D";
213 }
214
215 int res = 0;
216 res |= execIptables(target, op, LOCAL_INPUT, "-s", addr, "-p", protocolStr,
217 "--sport", portStr, "-j", "RETURN", NULL);
218 res |= execIptables(target, op, LOCAL_OUTPUT, "-d", addr, "-p", protocolStr,
219 "--dport", portStr, "-j", "RETURN", NULL);
220 return res;
221}
222
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -0700223FirewallType FirewallController::getFirewallType(ChildChain chain) {
224 switch(chain) {
225 case DOZABLE:
226 return WHITELIST;
227 case STANDBY:
228 return BLACKLIST;
Felipe Leme3f624342016-02-10 18:12:39 -0800229 case POWERSAVE:
230 return WHITELIST;
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -0700231 case NONE:
232 return mFirewallType;
233 default:
234 return BLACKLIST;
235 }
236}
237
238int FirewallController::setUidRule(ChildChain chain, int uid, FirewallRule rule) {
Jeff Sharkeyd8c64022012-07-13 18:04:07 -0700239 char uidStr[16];
240 sprintf(uidStr, "%d", uid);
241
242 const char* op;
Amith Yamasani390e4ea2015-04-25 19:08:57 -0700243 const char* target;
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -0700244 FirewallType firewallType = getFirewallType(chain);
Amith Yamasani390e4ea2015-04-25 19:08:57 -0700245 if (firewallType == WHITELIST) {
246 target = "RETURN";
247 op = (rule == ALLOW)? "-I" : "-D";
248 } else { // BLACKLIST mode
249 target = "DROP";
250 op = (rule == DENY)? "-I" : "-D";
Jeff Sharkeyd8c64022012-07-13 18:04:07 -0700251 }
252
253 int res = 0;
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -0700254 switch(chain) {
255 case DOZABLE:
256 res |= execIptables(V4V6, op, LOCAL_DOZABLE, "-m", "owner", "--uid-owner",
257 uidStr, "-j", target, NULL);
258 break;
259 case STANDBY:
260 res |= execIptables(V4V6, op, LOCAL_STANDBY, "-m", "owner", "--uid-owner",
261 uidStr, "-j", target, NULL);
262 break;
Felipe Leme3f624342016-02-10 18:12:39 -0800263 case POWERSAVE:
264 res |= execIptables(V4V6, op, LOCAL_POWERSAVE, "-m", "owner", "--uid-owner",
265 uidStr, "-j", target, NULL);
266 break;
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -0700267 case NONE:
268 res |= execIptables(V4V6, op, LOCAL_INPUT, "-m", "owner", "--uid-owner", uidStr,
269 "-j", target, NULL);
270 res |= execIptables(V4V6, op, LOCAL_OUTPUT, "-m", "owner", "--uid-owner", uidStr,
271 "-j", target, NULL);
272 break;
273 default:
274 ALOGW("Unknown child chain: %d", chain);
275 break;
276 }
277 return res;
278}
279
280int FirewallController::attachChain(const char* childChain, const char* parentChain) {
281 return execIptables(V4V6, "-t", TABLE, "-A", parentChain, "-j", childChain, NULL);
282}
283
284int FirewallController::detachChain(const char* childChain, const char* parentChain) {
285 return execIptables(V4V6, "-t", TABLE, "-D", parentChain, "-j", childChain, NULL);
286}
287
288int FirewallController::createChain(const char* childChain,
289 const char* parentChain, FirewallType type) {
290 // Order is important, otherwise later steps may fail.
291 execIptablesSilently(V4V6, "-t", TABLE, "-D", parentChain, "-j", childChain, NULL);
292 execIptablesSilently(V4V6, "-t", TABLE, "-F", childChain, NULL);
293 execIptablesSilently(V4V6, "-t", TABLE, "-X", childChain, NULL);
294 int res = 0;
295 res |= execIptables(V4V6, "-t", TABLE, "-N", childChain, NULL);
296 if (type == WHITELIST) {
Lorenzo Colittic8683d72015-09-01 16:53:35 +0900297 // Allow ICMPv6 packets necessary to make IPv6 connectivity work. http://b/23158230 .
298 for (size_t i = 0; i < ARRAY_SIZE(ICMPV6_TYPES); i++) {
299 res |= execIptables(V6, "-A", childChain, "-p", "icmpv6", "--icmpv6-type",
300 ICMPV6_TYPES[i], "-j", "RETURN", NULL);
301 }
302
Xiaohui Chenfeb2b612015-06-25 21:19:38 -0700303 // create default white list for system uid range
304 char uidStr[16];
Lorenzo Colitti89faa342016-02-26 11:38:47 +0900305 sprintf(uidStr, "0-%d", MAX_SYSTEM_UID);
Xiaohui Chenfeb2b612015-06-25 21:19:38 -0700306 res |= execIptables(V4V6, "-A", childChain, "-m", "owner", "--uid-owner",
307 uidStr, "-j", "RETURN", NULL);
Lorenzo Colittic8683d72015-09-01 16:53:35 +0900308
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -0700309 // create default rule to drop all traffic
310 res |= execIptables(V4V6, "-A", childChain, "-j", "DROP", NULL);
311 }
Jeff Sharkeyd8c64022012-07-13 18:04:07 -0700312 return res;
313}
Lorenzo Colitti89faa342016-02-26 11:38:47 +0900314
315std::string FirewallController::makeUidRules(
316 const char *name, bool isWhitelist, const std::vector<int32_t>& uids) {
317 const char *action = isWhitelist ? "RETURN" : "DROP";
318 const char *defaultAction = isWhitelist ? "DROP" : "RETURN";
319
320 std::string commands;
321
322 StringAppendF(&commands, "*filter\n:%s -\n", name);
323
324 if (isWhitelist) {
325 // Always whitelist system UIDs.
326 StringAppendF(&commands,
327 "-A %s -m owner --uid-owner %d-%d -j %s\n", name, 0, MAX_SYSTEM_UID, action);
328 }
329
330 for (auto uid : uids) {
331 StringAppendF(&commands, "-A %s -m owner --uid-owner %d -j %s\n", name, uid, action);
332 }
333
334 // If it's a blacklist chain that blacklists nothing, then don't add a default action.
335 if (isWhitelist || uids.size() > 0) {
336 StringAppendF(&commands, "-A %s -j %s\n", name, defaultAction);
337 }
338
339 StringAppendF(&commands, "COMMIT\n\x04"); // EOT.
340
341 return commands;
342}
343
344int FirewallController::replaceUidChain(
345 const char *name, bool isWhitelist, const std::vector<int32_t>& uids) {
346 std::string commands = makeUidRules(name, isWhitelist, uids);
347 return execIptablesRestore(V4V6, commands.c_str());
348}