blob: 1aa56b0cfe5dee87ea6f17d56f3f648e7ebc7fdf [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";
Felipe Leme3f624342016-02-10 18:12:39 -080039const char* FirewallController::LOCAL_POWERSAVE = "fw_powersave";
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -070040
Lorenzo Colittic8683d72015-09-01 16:53:35 +090041// ICMPv6 types that are required for any form of IPv6 connectivity to work. Note that because the
42// fw_dozable chain is called from both INPUT and OUTPUT, this includes both packets that we need
43// to be able to send (e.g., RS, NS), and packets that we need to receive (e.g., RA, NA).
44const char* FirewallController::ICMPV6_TYPES[] = {
45 "packet-too-big",
46 "router-solicitation",
47 "router-advertisement",
48 "neighbour-solicitation",
49 "neighbour-advertisement",
50 "redirect",
51};
52
Jeff Sharkeyd8c64022012-07-13 18:04:07 -070053FirewallController::FirewallController(void) {
Amith Yamasani390e4ea2015-04-25 19:08:57 -070054 // If no rules are set, it's in BLACKLIST mode
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -070055 mFirewallType = BLACKLIST;
Jeff Sharkeyd8c64022012-07-13 18:04:07 -070056}
57
58int FirewallController::setupIptablesHooks(void) {
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -070059 int res = 0;
60 // child chains are created but not attached, they will be attached explicitly.
61 FirewallType firewallType = getFirewallType(DOZABLE);
62 res |= createChain(LOCAL_DOZABLE, LOCAL_INPUT, firewallType);
63
64 firewallType = getFirewallType(STANDBY);
65 res |= createChain(LOCAL_STANDBY, LOCAL_INPUT, firewallType);
66
Felipe Leme3f624342016-02-10 18:12:39 -080067 firewallType = getFirewallType(POWERSAVE);
68 res |= createChain(LOCAL_POWERSAVE, LOCAL_INPUT, firewallType);
69
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -070070 return res;
Jeff Sharkeyd8c64022012-07-13 18:04:07 -070071}
72
Amith Yamasani390e4ea2015-04-25 19:08:57 -070073int FirewallController::enableFirewall(FirewallType ftype) {
Jeff Sharkeyd8c64022012-07-13 18:04:07 -070074 int res = 0;
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -070075 if (mFirewallType != ftype) {
76 // flush any existing rules
77 disableFirewall();
Jeff Sharkeyd8c64022012-07-13 18:04:07 -070078
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -070079 if (ftype == WHITELIST) {
80 // create default rule to drop all traffic
81 res |= execIptables(V4V6, "-A", LOCAL_INPUT, "-j", "DROP", NULL);
82 res |= execIptables(V4V6, "-A", LOCAL_OUTPUT, "-j", "REJECT", NULL);
83 res |= execIptables(V4V6, "-A", LOCAL_FORWARD, "-j", "REJECT", NULL);
84 }
Jeff Sharkeyd8c64022012-07-13 18:04:07 -070085
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -070086 // Set this after calling disableFirewall(), since it defaults to WHITELIST there
87 mFirewallType = ftype;
Amith Yamasani390e4ea2015-04-25 19:08:57 -070088 }
Jeff Sharkeyd8c64022012-07-13 18:04:07 -070089 return res;
90}
91
92int FirewallController::disableFirewall(void) {
93 int res = 0;
94
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -070095 mFirewallType = WHITELIST;
Amith Yamasani390e4ea2015-04-25 19:08:57 -070096
Jeff Sharkeyd8c64022012-07-13 18:04:07 -070097 // flush any existing rules
98 res |= execIptables(V4V6, "-F", LOCAL_INPUT, NULL);
99 res |= execIptables(V4V6, "-F", LOCAL_OUTPUT, NULL);
100 res |= execIptables(V4V6, "-F", LOCAL_FORWARD, NULL);
101
102 return res;
103}
104
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -0700105int FirewallController::enableChildChains(ChildChain chain, bool enable) {
106 int res = 0;
107 const char* name;
108 switch(chain) {
109 case DOZABLE:
110 name = LOCAL_DOZABLE;
111 break;
112 case STANDBY:
113 name = LOCAL_STANDBY;
114 break;
Felipe Leme3f624342016-02-10 18:12:39 -0800115 case POWERSAVE:
116 name = LOCAL_POWERSAVE;
117 break;
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -0700118 default:
119 return res;
120 }
121
122 if (enable) {
123 res |= attachChain(name, LOCAL_INPUT);
124 res |= attachChain(name, LOCAL_OUTPUT);
125 } else {
126 res |= detachChain(name, LOCAL_INPUT);
127 res |= detachChain(name, LOCAL_OUTPUT);
128 }
129 return res;
130}
131
Jeff Sharkeyd8c64022012-07-13 18:04:07 -0700132int FirewallController::isFirewallEnabled(void) {
133 // TODO: verify that rules are still in place near top
134 return -1;
135}
136
137int FirewallController::setInterfaceRule(const char* iface, FirewallRule rule) {
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -0700138 if (mFirewallType == BLACKLIST) {
Amith Yamasani390e4ea2015-04-25 19:08:57 -0700139 // Unsupported in BLACKLIST mode
140 return -1;
141 }
142
JP Abgrall69261cb2014-06-19 18:35:24 -0700143 if (!isIfaceName(iface)) {
144 errno = ENOENT;
145 return -1;
146 }
147
Jeff Sharkeyd8c64022012-07-13 18:04:07 -0700148 const char* op;
149 if (rule == ALLOW) {
150 op = "-I";
151 } else {
152 op = "-D";
153 }
154
155 int res = 0;
156 res |= execIptables(V4V6, op, LOCAL_INPUT, "-i", iface, "-j", "RETURN", NULL);
157 res |= execIptables(V4V6, op, LOCAL_OUTPUT, "-o", iface, "-j", "RETURN", NULL);
158 return res;
159}
160
161int FirewallController::setEgressSourceRule(const char* addr, FirewallRule rule) {
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -0700162 if (mFirewallType == BLACKLIST) {
Amith Yamasani390e4ea2015-04-25 19:08:57 -0700163 // Unsupported in BLACKLIST mode
164 return -1;
165 }
166
Jeff Sharkeyd8c64022012-07-13 18:04:07 -0700167 IptablesTarget target = V4;
168 if (strchr(addr, ':')) {
169 target = V6;
170 }
171
172 const char* op;
173 if (rule == ALLOW) {
174 op = "-I";
175 } else {
176 op = "-D";
177 }
178
179 int res = 0;
180 res |= execIptables(target, op, LOCAL_INPUT, "-d", addr, "-j", "RETURN", NULL);
181 res |= execIptables(target, op, LOCAL_OUTPUT, "-s", addr, "-j", "RETURN", NULL);
182 return res;
183}
184
185int FirewallController::setEgressDestRule(const char* addr, int protocol, int port,
186 FirewallRule rule) {
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -0700187 if (mFirewallType == BLACKLIST) {
Amith Yamasani390e4ea2015-04-25 19:08:57 -0700188 // Unsupported in BLACKLIST mode
189 return -1;
190 }
191
Jeff Sharkeyd8c64022012-07-13 18:04:07 -0700192 IptablesTarget target = V4;
193 if (strchr(addr, ':')) {
194 target = V6;
195 }
196
197 char protocolStr[16];
198 sprintf(protocolStr, "%d", protocol);
199
200 char portStr[16];
201 sprintf(portStr, "%d", port);
202
203 const char* op;
204 if (rule == ALLOW) {
205 op = "-I";
206 } else {
207 op = "-D";
208 }
209
210 int res = 0;
211 res |= execIptables(target, op, LOCAL_INPUT, "-s", addr, "-p", protocolStr,
212 "--sport", portStr, "-j", "RETURN", NULL);
213 res |= execIptables(target, op, LOCAL_OUTPUT, "-d", addr, "-p", protocolStr,
214 "--dport", portStr, "-j", "RETURN", NULL);
215 return res;
216}
217
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -0700218FirewallType FirewallController::getFirewallType(ChildChain chain) {
219 switch(chain) {
220 case DOZABLE:
221 return WHITELIST;
222 case STANDBY:
223 return BLACKLIST;
Felipe Leme3f624342016-02-10 18:12:39 -0800224 case POWERSAVE:
225 return WHITELIST;
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -0700226 case NONE:
227 return mFirewallType;
228 default:
229 return BLACKLIST;
230 }
231}
232
233int FirewallController::setUidRule(ChildChain chain, int uid, FirewallRule rule) {
Jeff Sharkeyd8c64022012-07-13 18:04:07 -0700234 char uidStr[16];
235 sprintf(uidStr, "%d", uid);
236
237 const char* op;
Amith Yamasani390e4ea2015-04-25 19:08:57 -0700238 const char* target;
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -0700239 FirewallType firewallType = getFirewallType(chain);
Amith Yamasani390e4ea2015-04-25 19:08:57 -0700240 if (firewallType == WHITELIST) {
241 target = "RETURN";
242 op = (rule == ALLOW)? "-I" : "-D";
243 } else { // BLACKLIST mode
244 target = "DROP";
245 op = (rule == DENY)? "-I" : "-D";
Jeff Sharkeyd8c64022012-07-13 18:04:07 -0700246 }
247
248 int res = 0;
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -0700249 switch(chain) {
250 case DOZABLE:
251 res |= execIptables(V4V6, op, LOCAL_DOZABLE, "-m", "owner", "--uid-owner",
252 uidStr, "-j", target, NULL);
253 break;
254 case STANDBY:
255 res |= execIptables(V4V6, op, LOCAL_STANDBY, "-m", "owner", "--uid-owner",
256 uidStr, "-j", target, NULL);
257 break;
Felipe Leme3f624342016-02-10 18:12:39 -0800258 case POWERSAVE:
259 res |= execIptables(V4V6, op, LOCAL_POWERSAVE, "-m", "owner", "--uid-owner",
260 uidStr, "-j", target, NULL);
261 break;
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -0700262 case NONE:
263 res |= execIptables(V4V6, op, LOCAL_INPUT, "-m", "owner", "--uid-owner", uidStr,
264 "-j", target, NULL);
265 res |= execIptables(V4V6, op, LOCAL_OUTPUT, "-m", "owner", "--uid-owner", uidStr,
266 "-j", target, NULL);
267 break;
268 default:
269 ALOGW("Unknown child chain: %d", chain);
270 break;
271 }
272 return res;
273}
274
275int FirewallController::attachChain(const char* childChain, const char* parentChain) {
276 return execIptables(V4V6, "-t", TABLE, "-A", parentChain, "-j", childChain, NULL);
277}
278
279int FirewallController::detachChain(const char* childChain, const char* parentChain) {
280 return execIptables(V4V6, "-t", TABLE, "-D", parentChain, "-j", childChain, NULL);
281}
282
283int FirewallController::createChain(const char* childChain,
284 const char* parentChain, FirewallType type) {
285 // Order is important, otherwise later steps may fail.
286 execIptablesSilently(V4V6, "-t", TABLE, "-D", parentChain, "-j", childChain, NULL);
287 execIptablesSilently(V4V6, "-t", TABLE, "-F", childChain, NULL);
288 execIptablesSilently(V4V6, "-t", TABLE, "-X", childChain, NULL);
289 int res = 0;
290 res |= execIptables(V4V6, "-t", TABLE, "-N", childChain, NULL);
291 if (type == WHITELIST) {
Lorenzo Colittic8683d72015-09-01 16:53:35 +0900292 // Allow ICMPv6 packets necessary to make IPv6 connectivity work. http://b/23158230 .
293 for (size_t i = 0; i < ARRAY_SIZE(ICMPV6_TYPES); i++) {
294 res |= execIptables(V6, "-A", childChain, "-p", "icmpv6", "--icmpv6-type",
295 ICMPV6_TYPES[i], "-j", "RETURN", NULL);
296 }
297
Xiaohui Chenfeb2b612015-06-25 21:19:38 -0700298 // create default white list for system uid range
299 char uidStr[16];
300 sprintf(uidStr, "0-%d", AID_APP - 1);
301 res |= execIptables(V4V6, "-A", childChain, "-m", "owner", "--uid-owner",
302 uidStr, "-j", "RETURN", NULL);
Lorenzo Colittic8683d72015-09-01 16:53:35 +0900303
Xiaohui Chen1cdfa9a2015-06-08 16:28:12 -0700304 // create default rule to drop all traffic
305 res |= execIptables(V4V6, "-A", childChain, "-j", "DROP", NULL);
306 }
Jeff Sharkeyd8c64022012-07-13 18:04:07 -0700307 return res;
308}