blob: cf5a7de26965852b690bda416124f0f56588838b [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
25#include <cutils/log.h>
Xiaohui Chenfeb2b612015-06-25 21:19:38 -070026#include <private/android_filesystem_config.h>
Jeff Sharkeyd8c64022012-07-13 18:04:07 -070027
28#include "NetdConstants.h"
29#include "FirewallController.h"
30
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -070031const char* FirewallController::TABLE = "filter";
32
Jeff Sharkeyd8c64022012-07-13 18:04:07 -070033const char* FirewallController::LOCAL_INPUT = "fw_INPUT";
34const char* FirewallController::LOCAL_OUTPUT = "fw_OUTPUT";
35const char* FirewallController::LOCAL_FORWARD = "fw_FORWARD";
36
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -070037const char* FirewallController::LOCAL_DOZABLE = "fw_dozable";
38const char* FirewallController::LOCAL_STANDBY = "fw_standby";
39
Lorenzo Colittic8683d72015-09-01 16:53:35 +090040// ICMPv6 types that are required for any form of IPv6 connectivity to work. Note that because the
41// fw_dozable chain is called from both INPUT and OUTPUT, this includes both packets that we need
42// to be able to send (e.g., RS, NS), and packets that we need to receive (e.g., RA, NA).
43const char* FirewallController::ICMPV6_TYPES[] = {
44 "packet-too-big",
45 "router-solicitation",
46 "router-advertisement",
47 "neighbour-solicitation",
48 "neighbour-advertisement",
49 "redirect",
50};
51
Jeff Sharkeyd8c64022012-07-13 18:04:07 -070052FirewallController::FirewallController(void) {
Amith Yamasani390e4ea2015-04-25 19:08:57 -070053 // If no rules are set, it's in BLACKLIST mode
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -070054 mFirewallType = BLACKLIST;
Jeff Sharkeyd8c64022012-07-13 18:04:07 -070055}
56
57int FirewallController::setupIptablesHooks(void) {
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -070058 int res = 0;
59 // child chains are created but not attached, they will be attached explicitly.
60 FirewallType firewallType = getFirewallType(DOZABLE);
61 res |= createChain(LOCAL_DOZABLE, LOCAL_INPUT, firewallType);
62
63 firewallType = getFirewallType(STANDBY);
64 res |= createChain(LOCAL_STANDBY, LOCAL_INPUT, firewallType);
65
66 return res;
Jeff Sharkeyd8c64022012-07-13 18:04:07 -070067}
68
Amith Yamasani390e4ea2015-04-25 19:08:57 -070069int FirewallController::enableFirewall(FirewallType ftype) {
Jeff Sharkeyd8c64022012-07-13 18:04:07 -070070 int res = 0;
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -070071 if (mFirewallType != ftype) {
72 // flush any existing rules
73 disableFirewall();
Jeff Sharkeyd8c64022012-07-13 18:04:07 -070074
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -070075 if (ftype == WHITELIST) {
76 // create default rule to drop all traffic
77 res |= execIptables(V4V6, "-A", LOCAL_INPUT, "-j", "DROP", NULL);
78 res |= execIptables(V4V6, "-A", LOCAL_OUTPUT, "-j", "REJECT", NULL);
79 res |= execIptables(V4V6, "-A", LOCAL_FORWARD, "-j", "REJECT", NULL);
80 }
Jeff Sharkeyd8c64022012-07-13 18:04:07 -070081
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -070082 // Set this after calling disableFirewall(), since it defaults to WHITELIST there
83 mFirewallType = ftype;
Amith Yamasani390e4ea2015-04-25 19:08:57 -070084 }
Jeff Sharkeyd8c64022012-07-13 18:04:07 -070085 return res;
86}
87
88int FirewallController::disableFirewall(void) {
89 int res = 0;
90
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -070091 mFirewallType = WHITELIST;
Amith Yamasani390e4ea2015-04-25 19:08:57 -070092
Jeff Sharkeyd8c64022012-07-13 18:04:07 -070093 // flush any existing rules
94 res |= execIptables(V4V6, "-F", LOCAL_INPUT, NULL);
95 res |= execIptables(V4V6, "-F", LOCAL_OUTPUT, NULL);
96 res |= execIptables(V4V6, "-F", LOCAL_FORWARD, NULL);
97
98 return res;
99}
100
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -0700101int FirewallController::enableChildChains(ChildChain chain, bool enable) {
102 int res = 0;
103 const char* name;
104 switch(chain) {
105 case DOZABLE:
106 name = LOCAL_DOZABLE;
107 break;
108 case STANDBY:
109 name = LOCAL_STANDBY;
110 break;
111 default:
112 return res;
113 }
114
115 if (enable) {
116 res |= attachChain(name, LOCAL_INPUT);
117 res |= attachChain(name, LOCAL_OUTPUT);
118 } else {
119 res |= detachChain(name, LOCAL_INPUT);
120 res |= detachChain(name, LOCAL_OUTPUT);
121 }
122 return res;
123}
124
Jeff Sharkeyd8c64022012-07-13 18:04:07 -0700125int FirewallController::isFirewallEnabled(void) {
126 // TODO: verify that rules are still in place near top
127 return -1;
128}
129
130int FirewallController::setInterfaceRule(const char* iface, FirewallRule rule) {
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -0700131 if (mFirewallType == BLACKLIST) {
Amith Yamasani390e4ea2015-04-25 19:08:57 -0700132 // Unsupported in BLACKLIST mode
133 return -1;
134 }
135
JP Abgrall69261cb2014-06-19 18:35:24 -0700136 if (!isIfaceName(iface)) {
137 errno = ENOENT;
138 return -1;
139 }
140
Jeff Sharkeyd8c64022012-07-13 18:04:07 -0700141 const char* op;
142 if (rule == ALLOW) {
143 op = "-I";
144 } else {
145 op = "-D";
146 }
147
148 int res = 0;
149 res |= execIptables(V4V6, op, LOCAL_INPUT, "-i", iface, "-j", "RETURN", NULL);
150 res |= execIptables(V4V6, op, LOCAL_OUTPUT, "-o", iface, "-j", "RETURN", NULL);
151 return res;
152}
153
154int FirewallController::setEgressSourceRule(const char* addr, FirewallRule rule) {
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -0700155 if (mFirewallType == BLACKLIST) {
Amith Yamasani390e4ea2015-04-25 19:08:57 -0700156 // Unsupported in BLACKLIST mode
157 return -1;
158 }
159
Jeff Sharkeyd8c64022012-07-13 18:04:07 -0700160 IptablesTarget target = V4;
161 if (strchr(addr, ':')) {
162 target = V6;
163 }
164
165 const char* op;
166 if (rule == ALLOW) {
167 op = "-I";
168 } else {
169 op = "-D";
170 }
171
172 int res = 0;
173 res |= execIptables(target, op, LOCAL_INPUT, "-d", addr, "-j", "RETURN", NULL);
174 res |= execIptables(target, op, LOCAL_OUTPUT, "-s", addr, "-j", "RETURN", NULL);
175 return res;
176}
177
178int FirewallController::setEgressDestRule(const char* addr, int protocol, int port,
179 FirewallRule rule) {
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -0700180 if (mFirewallType == BLACKLIST) {
Amith Yamasani390e4ea2015-04-25 19:08:57 -0700181 // Unsupported in BLACKLIST mode
182 return -1;
183 }
184
Jeff Sharkeyd8c64022012-07-13 18:04:07 -0700185 IptablesTarget target = V4;
186 if (strchr(addr, ':')) {
187 target = V6;
188 }
189
190 char protocolStr[16];
191 sprintf(protocolStr, "%d", protocol);
192
193 char portStr[16];
194 sprintf(portStr, "%d", port);
195
196 const char* op;
197 if (rule == ALLOW) {
198 op = "-I";
199 } else {
200 op = "-D";
201 }
202
203 int res = 0;
204 res |= execIptables(target, op, LOCAL_INPUT, "-s", addr, "-p", protocolStr,
205 "--sport", portStr, "-j", "RETURN", NULL);
206 res |= execIptables(target, op, LOCAL_OUTPUT, "-d", addr, "-p", protocolStr,
207 "--dport", portStr, "-j", "RETURN", NULL);
208 return res;
209}
210
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -0700211FirewallType FirewallController::getFirewallType(ChildChain chain) {
212 switch(chain) {
213 case DOZABLE:
214 return WHITELIST;
215 case STANDBY:
216 return BLACKLIST;
217 case NONE:
218 return mFirewallType;
219 default:
220 return BLACKLIST;
221 }
222}
223
224int FirewallController::setUidRule(ChildChain chain, int uid, FirewallRule rule) {
Jeff Sharkeyd8c64022012-07-13 18:04:07 -0700225 char uidStr[16];
226 sprintf(uidStr, "%d", uid);
227
228 const char* op;
Amith Yamasani390e4ea2015-04-25 19:08:57 -0700229 const char* target;
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -0700230 FirewallType firewallType = getFirewallType(chain);
Amith Yamasani390e4ea2015-04-25 19:08:57 -0700231 if (firewallType == WHITELIST) {
232 target = "RETURN";
233 op = (rule == ALLOW)? "-I" : "-D";
234 } else { // BLACKLIST mode
235 target = "DROP";
236 op = (rule == DENY)? "-I" : "-D";
Jeff Sharkeyd8c64022012-07-13 18:04:07 -0700237 }
238
239 int res = 0;
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -0700240 switch(chain) {
241 case DOZABLE:
242 res |= execIptables(V4V6, op, LOCAL_DOZABLE, "-m", "owner", "--uid-owner",
243 uidStr, "-j", target, NULL);
244 break;
245 case STANDBY:
246 res |= execIptables(V4V6, op, LOCAL_STANDBY, "-m", "owner", "--uid-owner",
247 uidStr, "-j", target, NULL);
248 break;
249 case NONE:
250 res |= execIptables(V4V6, op, LOCAL_INPUT, "-m", "owner", "--uid-owner", uidStr,
251 "-j", target, NULL);
252 res |= execIptables(V4V6, op, LOCAL_OUTPUT, "-m", "owner", "--uid-owner", uidStr,
253 "-j", target, NULL);
254 break;
255 default:
256 ALOGW("Unknown child chain: %d", chain);
257 break;
258 }
259 return res;
260}
261
262int FirewallController::attachChain(const char* childChain, const char* parentChain) {
263 return execIptables(V4V6, "-t", TABLE, "-A", parentChain, "-j", childChain, NULL);
264}
265
266int FirewallController::detachChain(const char* childChain, const char* parentChain) {
267 return execIptables(V4V6, "-t", TABLE, "-D", parentChain, "-j", childChain, NULL);
268}
269
270int FirewallController::createChain(const char* childChain,
271 const char* parentChain, FirewallType type) {
272 // Order is important, otherwise later steps may fail.
273 execIptablesSilently(V4V6, "-t", TABLE, "-D", parentChain, "-j", childChain, NULL);
274 execIptablesSilently(V4V6, "-t", TABLE, "-F", childChain, NULL);
275 execIptablesSilently(V4V6, "-t", TABLE, "-X", childChain, NULL);
276 int res = 0;
277 res |= execIptables(V4V6, "-t", TABLE, "-N", childChain, NULL);
278 if (type == WHITELIST) {
Lorenzo Colittic8683d72015-09-01 16:53:35 +0900279 // Allow ICMPv6 packets necessary to make IPv6 connectivity work. http://b/23158230 .
280 for (size_t i = 0; i < ARRAY_SIZE(ICMPV6_TYPES); i++) {
281 res |= execIptables(V6, "-A", childChain, "-p", "icmpv6", "--icmpv6-type",
282 ICMPV6_TYPES[i], "-j", "RETURN", NULL);
283 }
284
Xiaohui Chenfeb2b612015-06-25 21:19:38 -0700285 // create default white list for system uid range
286 char uidStr[16];
287 sprintf(uidStr, "0-%d", AID_APP - 1);
288 res |= execIptables(V4V6, "-A", childChain, "-m", "owner", "--uid-owner",
289 uidStr, "-j", "RETURN", NULL);
Lorenzo Colittic8683d72015-09-01 16:53:35 +0900290
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -0700291 // create default rule to drop all traffic
292 res |= execIptables(V4V6, "-A", childChain, "-j", "DROP", NULL);
293 }
Jeff Sharkeyd8c64022012-07-13 18:04:07 -0700294 return res;
295}