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