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 <cutils/log.h>
26 #include <private/android_filesystem_config.h>
27
28 #include "NetdConstants.h"
29 #include "FirewallController.h"
30
31 const char* FirewallController::TABLE = "filter";
32
33 const char* FirewallController::LOCAL_INPUT = "fw_INPUT";
34 const char* FirewallController::LOCAL_OUTPUT = "fw_OUTPUT";
35 const char* FirewallController::LOCAL_FORWARD = "fw_FORWARD";
36
37 const char* FirewallController::LOCAL_DOZABLE = "fw_dozable";
38 const char* FirewallController::LOCAL_STANDBY = "fw_standby";
39
40 // 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).
43 const char* FirewallController::ICMPV6_TYPES[] = {
44 "packet-too-big",
45 "router-solicitation",
46 "router-advertisement",
47 "neighbour-solicitation",
48 "neighbour-advertisement",
49 "redirect",
50 };
51
FirewallController(void)52 FirewallController::FirewallController(void) {
53 // If no rules are set, it's in BLACKLIST mode
54 mFirewallType = BLACKLIST;
55 }
56
setupIptablesHooks(void)57 int FirewallController::setupIptablesHooks(void) {
58 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;
67 }
68
enableFirewall(FirewallType ftype)69 int FirewallController::enableFirewall(FirewallType ftype) {
70 int res = 0;
71 if (mFirewallType != ftype) {
72 // flush any existing rules
73 disableFirewall();
74
75 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 }
81
82 // Set this after calling disableFirewall(), since it defaults to WHITELIST there
83 mFirewallType = ftype;
84 }
85 return res;
86 }
87
disableFirewall(void)88 int FirewallController::disableFirewall(void) {
89 int res = 0;
90
91 mFirewallType = WHITELIST;
92
93 // 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
enableChildChains(ChildChain chain,bool enable)101 int 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
isFirewallEnabled(void)125 int FirewallController::isFirewallEnabled(void) {
126 // TODO: verify that rules are still in place near top
127 return -1;
128 }
129
setInterfaceRule(const char * iface,FirewallRule rule)130 int FirewallController::setInterfaceRule(const char* iface, FirewallRule rule) {
131 if (mFirewallType == BLACKLIST) {
132 // Unsupported in BLACKLIST mode
133 return -1;
134 }
135
136 if (!isIfaceName(iface)) {
137 errno = ENOENT;
138 return -1;
139 }
140
141 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
setEgressSourceRule(const char * addr,FirewallRule rule)154 int FirewallController::setEgressSourceRule(const char* addr, FirewallRule rule) {
155 if (mFirewallType == BLACKLIST) {
156 // Unsupported in BLACKLIST mode
157 return -1;
158 }
159
160 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
setEgressDestRule(const char * addr,int protocol,int port,FirewallRule rule)178 int FirewallController::setEgressDestRule(const char* addr, int protocol, int port,
179 FirewallRule rule) {
180 if (mFirewallType == BLACKLIST) {
181 // Unsupported in BLACKLIST mode
182 return -1;
183 }
184
185 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
getFirewallType(ChildChain chain)211 FirewallType 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
setUidRule(ChildChain chain,int uid,FirewallRule rule)224 int FirewallController::setUidRule(ChildChain chain, int uid, FirewallRule rule) {
225 char uidStr[16];
226 sprintf(uidStr, "%d", uid);
227
228 const char* op;
229 const char* target;
230 FirewallType firewallType = getFirewallType(chain);
231 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";
237 }
238
239 int res = 0;
240 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
attachChain(const char * childChain,const char * parentChain)262 int FirewallController::attachChain(const char* childChain, const char* parentChain) {
263 return execIptables(V4V6, "-t", TABLE, "-A", parentChain, "-j", childChain, NULL);
264 }
265
detachChain(const char * childChain,const char * parentChain)266 int FirewallController::detachChain(const char* childChain, const char* parentChain) {
267 return execIptables(V4V6, "-t", TABLE, "-D", parentChain, "-j", childChain, NULL);
268 }
269
createChain(const char * childChain,const char * parentChain,FirewallType type)270 int 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) {
279 // 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
285 // 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);
290
291 // create default rule to drop all traffic
292 res |= execIptables(V4V6, "-A", childChain, "-j", "DROP", NULL);
293 }
294 return res;
295 }
296