blob: 6a9c776781c9d93dc6c6edc4fa597c54eaa84e24 [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";
42
Lorenzo Colittic8683d72015-09-01 16:53:35 +090043// ICMPv6 types that are required for any form of IPv6 connectivity to work. Note that because the
44// fw_dozable chain is called from both INPUT and OUTPUT, this includes both packets that we need
45// to be able to send (e.g., RS, NS), and packets that we need to receive (e.g., RA, NA).
46const char* FirewallController::ICMPV6_TYPES[] = {
47 "packet-too-big",
48 "router-solicitation",
49 "router-advertisement",
50 "neighbour-solicitation",
51 "neighbour-advertisement",
52 "redirect",
53};
54
Lorenzo Colitti89faa342016-02-26 11:38:47 +090055const int MAX_SYSTEM_UID = AID_APP - 1;
56
Jeff Sharkeyd8c64022012-07-13 18:04:07 -070057FirewallController::FirewallController(void) {
Amith Yamasani390e4ea2015-04-25 19:08:57 -070058 // If no rules are set, it's in BLACKLIST mode
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -070059 mFirewallType = BLACKLIST;
Jeff Sharkeyd8c64022012-07-13 18:04:07 -070060}
61
62int FirewallController::setupIptablesHooks(void) {
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -070063 int res = 0;
64 // child chains are created but not attached, they will be attached explicitly.
65 FirewallType firewallType = getFirewallType(DOZABLE);
66 res |= createChain(LOCAL_DOZABLE, LOCAL_INPUT, firewallType);
67
68 firewallType = getFirewallType(STANDBY);
69 res |= createChain(LOCAL_STANDBY, LOCAL_INPUT, firewallType);
70
71 return res;
Jeff Sharkeyd8c64022012-07-13 18:04:07 -070072}
73
Amith Yamasani390e4ea2015-04-25 19:08:57 -070074int FirewallController::enableFirewall(FirewallType ftype) {
Jeff Sharkeyd8c64022012-07-13 18:04:07 -070075 int res = 0;
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -070076 if (mFirewallType != ftype) {
77 // flush any existing rules
78 disableFirewall();
Jeff Sharkeyd8c64022012-07-13 18:04:07 -070079
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -070080 if (ftype == WHITELIST) {
81 // create default rule to drop all traffic
82 res |= execIptables(V4V6, "-A", LOCAL_INPUT, "-j", "DROP", NULL);
83 res |= execIptables(V4V6, "-A", LOCAL_OUTPUT, "-j", "REJECT", NULL);
84 res |= execIptables(V4V6, "-A", LOCAL_FORWARD, "-j", "REJECT", NULL);
85 }
Jeff Sharkeyd8c64022012-07-13 18:04:07 -070086
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -070087 // Set this after calling disableFirewall(), since it defaults to WHITELIST there
88 mFirewallType = ftype;
Amith Yamasani390e4ea2015-04-25 19:08:57 -070089 }
Jeff Sharkeyd8c64022012-07-13 18:04:07 -070090 return res;
91}
92
93int FirewallController::disableFirewall(void) {
94 int res = 0;
95
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -070096 mFirewallType = WHITELIST;
Amith Yamasani390e4ea2015-04-25 19:08:57 -070097
Jeff Sharkeyd8c64022012-07-13 18:04:07 -070098 // flush any existing rules
99 res |= execIptables(V4V6, "-F", LOCAL_INPUT, NULL);
100 res |= execIptables(V4V6, "-F", LOCAL_OUTPUT, NULL);
101 res |= execIptables(V4V6, "-F", LOCAL_FORWARD, NULL);
102
103 return res;
104}
105
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -0700106int FirewallController::enableChildChains(ChildChain chain, bool enable) {
107 int res = 0;
108 const char* name;
109 switch(chain) {
110 case DOZABLE:
111 name = LOCAL_DOZABLE;
112 break;
113 case STANDBY:
114 name = LOCAL_STANDBY;
115 break;
116 default:
117 return res;
118 }
119
120 if (enable) {
121 res |= attachChain(name, LOCAL_INPUT);
122 res |= attachChain(name, LOCAL_OUTPUT);
123 } else {
124 res |= detachChain(name, LOCAL_INPUT);
125 res |= detachChain(name, LOCAL_OUTPUT);
126 }
127 return res;
128}
129
Jeff Sharkeyd8c64022012-07-13 18:04:07 -0700130int FirewallController::isFirewallEnabled(void) {
131 // TODO: verify that rules are still in place near top
132 return -1;
133}
134
135int FirewallController::setInterfaceRule(const char* iface, FirewallRule rule) {
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -0700136 if (mFirewallType == BLACKLIST) {
Amith Yamasani390e4ea2015-04-25 19:08:57 -0700137 // Unsupported in BLACKLIST mode
138 return -1;
139 }
140
JP Abgrall69261cb2014-06-19 18:35:24 -0700141 if (!isIfaceName(iface)) {
142 errno = ENOENT;
143 return -1;
144 }
145
Jeff Sharkeyd8c64022012-07-13 18:04:07 -0700146 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
159int FirewallController::setEgressSourceRule(const char* addr, FirewallRule rule) {
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -0700160 if (mFirewallType == BLACKLIST) {
Amith Yamasani390e4ea2015-04-25 19:08:57 -0700161 // Unsupported in BLACKLIST mode
162 return -1;
163 }
164
Jeff Sharkeyd8c64022012-07-13 18:04:07 -0700165 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
183int FirewallController::setEgressDestRule(const char* addr, int protocol, int port,
184 FirewallRule rule) {
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -0700185 if (mFirewallType == BLACKLIST) {
Amith Yamasani390e4ea2015-04-25 19:08:57 -0700186 // Unsupported in BLACKLIST mode
187 return -1;
188 }
189
Jeff Sharkeyd8c64022012-07-13 18:04:07 -0700190 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
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -0700216FirewallType FirewallController::getFirewallType(ChildChain chain) {
217 switch(chain) {
218 case DOZABLE:
219 return WHITELIST;
220 case STANDBY:
221 return BLACKLIST;
222 case NONE:
223 return mFirewallType;
224 default:
225 return BLACKLIST;
226 }
227}
228
229int FirewallController::setUidRule(ChildChain chain, int uid, FirewallRule rule) {
Jeff Sharkeyd8c64022012-07-13 18:04:07 -0700230 char uidStr[16];
231 sprintf(uidStr, "%d", uid);
232
233 const char* op;
Amith Yamasani390e4ea2015-04-25 19:08:57 -0700234 const char* target;
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -0700235 FirewallType firewallType = getFirewallType(chain);
Amith Yamasani390e4ea2015-04-25 19:08:57 -0700236 if (firewallType == WHITELIST) {
237 target = "RETURN";
238 op = (rule == ALLOW)? "-I" : "-D";
239 } else { // BLACKLIST mode
240 target = "DROP";
241 op = (rule == DENY)? "-I" : "-D";
Jeff Sharkeyd8c64022012-07-13 18:04:07 -0700242 }
243
244 int res = 0;
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -0700245 switch(chain) {
246 case DOZABLE:
247 res |= execIptables(V4V6, op, LOCAL_DOZABLE, "-m", "owner", "--uid-owner",
248 uidStr, "-j", target, NULL);
249 break;
250 case STANDBY:
251 res |= execIptables(V4V6, op, LOCAL_STANDBY, "-m", "owner", "--uid-owner",
252 uidStr, "-j", target, NULL);
253 break;
254 case NONE:
255 res |= execIptables(V4V6, op, LOCAL_INPUT, "-m", "owner", "--uid-owner", uidStr,
256 "-j", target, NULL);
257 res |= execIptables(V4V6, op, LOCAL_OUTPUT, "-m", "owner", "--uid-owner", uidStr,
258 "-j", target, NULL);
259 break;
260 default:
261 ALOGW("Unknown child chain: %d", chain);
262 break;
263 }
264 return res;
265}
266
267int FirewallController::attachChain(const char* childChain, const char* parentChain) {
268 return execIptables(V4V6, "-t", TABLE, "-A", parentChain, "-j", childChain, NULL);
269}
270
271int FirewallController::detachChain(const char* childChain, const char* parentChain) {
272 return execIptables(V4V6, "-t", TABLE, "-D", parentChain, "-j", childChain, NULL);
273}
274
275int FirewallController::createChain(const char* childChain,
276 const char* parentChain, FirewallType type) {
277 // Order is important, otherwise later steps may fail.
278 execIptablesSilently(V4V6, "-t", TABLE, "-D", parentChain, "-j", childChain, NULL);
279 execIptablesSilently(V4V6, "-t", TABLE, "-F", childChain, NULL);
280 execIptablesSilently(V4V6, "-t", TABLE, "-X", childChain, NULL);
281 int res = 0;
282 res |= execIptables(V4V6, "-t", TABLE, "-N", childChain, NULL);
283 if (type == WHITELIST) {
Lorenzo Colittic8683d72015-09-01 16:53:35 +0900284 // Allow ICMPv6 packets necessary to make IPv6 connectivity work. http://b/23158230 .
285 for (size_t i = 0; i < ARRAY_SIZE(ICMPV6_TYPES); i++) {
286 res |= execIptables(V6, "-A", childChain, "-p", "icmpv6", "--icmpv6-type",
287 ICMPV6_TYPES[i], "-j", "RETURN", NULL);
288 }
289
Xiaohui Chenfeb2b612015-06-25 21:19:38 -0700290 // create default white list for system uid range
291 char uidStr[16];
Lorenzo Colitti89faa342016-02-26 11:38:47 +0900292 sprintf(uidStr, "0-%d", MAX_SYSTEM_UID);
Xiaohui Chenfeb2b612015-06-25 21:19:38 -0700293 res |= execIptables(V4V6, "-A", childChain, "-m", "owner", "--uid-owner",
294 uidStr, "-j", "RETURN", NULL);
Lorenzo Colittic8683d72015-09-01 16:53:35 +0900295
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -0700296 // create default rule to drop all traffic
297 res |= execIptables(V4V6, "-A", childChain, "-j", "DROP", NULL);
298 }
Jeff Sharkeyd8c64022012-07-13 18:04:07 -0700299 return res;
300}
Lorenzo Colitti89faa342016-02-26 11:38:47 +0900301
302std::string FirewallController::makeUidRules(
303 const char *name, bool isWhitelist, const std::vector<int32_t>& uids) {
304 const char *action = isWhitelist ? "RETURN" : "DROP";
305 const char *defaultAction = isWhitelist ? "DROP" : "RETURN";
306
307 std::string commands;
308
309 StringAppendF(&commands, "*filter\n:%s -\n", name);
310
311 if (isWhitelist) {
312 // Always whitelist system UIDs.
313 StringAppendF(&commands,
314 "-A %s -m owner --uid-owner %d-%d -j %s\n", name, 0, MAX_SYSTEM_UID, action);
315 }
316
317 for (auto uid : uids) {
318 StringAppendF(&commands, "-A %s -m owner --uid-owner %d -j %s\n", name, uid, action);
319 }
320
321 // If it's a blacklist chain that blacklists nothing, then don't add a default action.
322 if (isWhitelist || uids.size() > 0) {
323 StringAppendF(&commands, "-A %s -j %s\n", name, defaultAction);
324 }
325
326 StringAppendF(&commands, "COMMIT\n\x04"); // EOT.
327
328 return commands;
329}
330
331int FirewallController::replaceUidChain(
332 const char *name, bool isWhitelist, const std::vector<int32_t>& uids) {
333 std::string commands = makeUidRules(name, isWhitelist, uids);
334 return execIptablesRestore(V4V6, commands.c_str());
335}