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 <android-base/stringprintf.h> 26#include <cutils/log.h> 27 28#include "NetdConstants.h" 29#include "FirewallController.h" 30 31using android::base::StringAppendF; 32 33auto FirewallController::execIptables = ::execIptables; 34auto FirewallController::execIptablesSilently = ::execIptablesSilently; 35auto FirewallController::execIptablesRestore = ::execIptablesRestore; 36 37const char* FirewallController::TABLE = "filter"; 38 39const char* FirewallController::LOCAL_INPUT = "fw_INPUT"; 40const char* FirewallController::LOCAL_OUTPUT = "fw_OUTPUT"; 41const char* FirewallController::LOCAL_FORWARD = "fw_FORWARD"; 42 43const char* FirewallController::LOCAL_DOZABLE = "fw_dozable"; 44const char* FirewallController::LOCAL_STANDBY = "fw_standby"; 45const char* FirewallController::LOCAL_POWERSAVE = "fw_powersave"; 46 47// ICMPv6 types that are required for any form of IPv6 connectivity to work. Note that because the 48// fw_dozable chain is called from both INPUT and OUTPUT, this includes both packets that we need 49// to be able to send (e.g., RS, NS), and packets that we need to receive (e.g., RA, NA). 50const char* FirewallController::ICMPV6_TYPES[] = { 51 "packet-too-big", 52 "router-solicitation", 53 "router-advertisement", 54 "neighbour-solicitation", 55 "neighbour-advertisement", 56 "redirect", 57}; 58 59FirewallController::FirewallController(void) { 60 // If no rules are set, it's in BLACKLIST mode 61 mFirewallType = BLACKLIST; 62} 63 64int FirewallController::setupIptablesHooks(void) { 65 int res = 0; 66 res |= createChain(LOCAL_DOZABLE, getFirewallType(DOZABLE)); 67 res |= createChain(LOCAL_STANDBY, getFirewallType(STANDBY)); 68 res |= createChain(LOCAL_POWERSAVE, getFirewallType(POWERSAVE)); 69 return res; 70} 71 72int FirewallController::enableFirewall(FirewallType ftype) { 73 int res = 0; 74 if (mFirewallType != ftype) { 75 // flush any existing rules 76 disableFirewall(); 77 78 if (ftype == WHITELIST) { 79 // create default rule to drop all traffic 80 res |= execIptables(V4V6, "-A", LOCAL_INPUT, "-j", "DROP", NULL); 81 res |= execIptables(V4V6, "-A", LOCAL_OUTPUT, "-j", "REJECT", NULL); 82 res |= execIptables(V4V6, "-A", LOCAL_FORWARD, "-j", "REJECT", NULL); 83 } 84 85 // Set this after calling disableFirewall(), since it defaults to WHITELIST there 86 mFirewallType = ftype; 87 } 88 return res; 89} 90 91int FirewallController::disableFirewall(void) { 92 int res = 0; 93 94 mFirewallType = WHITELIST; 95 96 // flush any existing rules 97 res |= execIptables(V4V6, "-F", LOCAL_INPUT, NULL); 98 res |= execIptables(V4V6, "-F", LOCAL_OUTPUT, NULL); 99 res |= execIptables(V4V6, "-F", LOCAL_FORWARD, NULL); 100 101 return res; 102} 103 104int FirewallController::enableChildChains(ChildChain chain, bool enable) { 105 int res = 0; 106 const char* name; 107 switch(chain) { 108 case DOZABLE: 109 name = LOCAL_DOZABLE; 110 break; 111 case STANDBY: 112 name = LOCAL_STANDBY; 113 break; 114 case POWERSAVE: 115 name = LOCAL_POWERSAVE; 116 break; 117 default: 118 return res; 119 } 120 121 std::string command = "*filter\n"; 122 for (const char *parent : { LOCAL_INPUT, LOCAL_OUTPUT }) { 123 StringAppendF(&command, "%s %s -j %s\n", (enable ? "-A" : "-D"), parent, name); 124 } 125 StringAppendF(&command, "COMMIT\n"); 126 127 return execIptablesRestore(V4V6, command); 128} 129 130int 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) { 136 if (mFirewallType == BLACKLIST) { 137 // Unsupported in BLACKLIST mode 138 return -1; 139 } 140 141 if (!isIfaceName(iface)) { 142 errno = ENOENT; 143 return -1; 144 } 145 146 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) { 160 if (mFirewallType == BLACKLIST) { 161 // Unsupported in BLACKLIST mode 162 return -1; 163 } 164 165 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) { 185 if (mFirewallType == BLACKLIST) { 186 // Unsupported in BLACKLIST mode 187 return -1; 188 } 189 190 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 216FirewallType FirewallController::getFirewallType(ChildChain chain) { 217 switch(chain) { 218 case DOZABLE: 219 return WHITELIST; 220 case STANDBY: 221 return BLACKLIST; 222 case POWERSAVE: 223 return WHITELIST; 224 case NONE: 225 return mFirewallType; 226 default: 227 return BLACKLIST; 228 } 229} 230 231int FirewallController::setUidRule(ChildChain chain, int uid, FirewallRule rule) { 232 const char* op; 233 const char* target; 234 FirewallType firewallType = getFirewallType(chain); 235 if (firewallType == WHITELIST) { 236 target = "RETURN"; 237 // When adding, insert RETURN rules at the front, before the catch-all DROP at the end. 238 op = (rule == ALLOW)? "-I" : "-D"; 239 } else { // BLACKLIST mode 240 target = "DROP"; 241 // When adding, append DROP rules at the end, after the RETURN rule that matches TCP RSTs. 242 op = (rule == DENY)? "-A" : "-D"; 243 } 244 245 std::vector<std::string> chainNames; 246 switch(chain) { 247 case DOZABLE: 248 chainNames = { LOCAL_DOZABLE }; 249 break; 250 case STANDBY: 251 chainNames = { LOCAL_STANDBY }; 252 break; 253 case POWERSAVE: 254 chainNames = { LOCAL_POWERSAVE }; 255 break; 256 case NONE: 257 chainNames = { LOCAL_INPUT, LOCAL_OUTPUT }; 258 break; 259 default: 260 ALOGW("Unknown child chain: %d", chain); 261 return -1; 262 } 263 264 std::string command = "*filter\n"; 265 for (std::string chainName : chainNames) { 266 StringAppendF(&command, "%s %s -m owner --uid-owner %d -j %s\n", 267 op, chainName.c_str(), uid, target); 268 } 269 StringAppendF(&command, "COMMIT\n"); 270 271 return execIptablesRestore(V4V6, command); 272} 273 274int FirewallController::createChain(const char* chain, FirewallType type) { 275 static const std::vector<int32_t> NO_UIDS; 276 return replaceUidChain(chain, type == WHITELIST, NO_UIDS); 277} 278 279std::string FirewallController::makeUidRules(IptablesTarget target, const char *name, 280 bool isWhitelist, const std::vector<int32_t>& uids) { 281 std::string commands; 282 StringAppendF(&commands, "*filter\n:%s -\n", name); 283 284 // Whitelist chains have UIDs at the beginning, and new UIDs are added with '-I'. 285 if (isWhitelist) { 286 for (auto uid : uids) { 287 StringAppendF(&commands, "-A %s -m owner --uid-owner %d -j RETURN\n", name, uid); 288 } 289 290 // Always whitelist system UIDs. 291 StringAppendF(&commands, 292 "-A %s -m owner --uid-owner %d-%d -j RETURN\n", name, 0, MAX_SYSTEM_UID); 293 } 294 295 // Always allow networking on loopback. 296 StringAppendF(&commands, "-A %s -i lo -j RETURN\n", name); 297 StringAppendF(&commands, "-A %s -o lo -j RETURN\n", name); 298 299 // Allow TCP RSTs so we can cleanly close TCP connections of apps that no longer have network 300 // access. Both incoming and outgoing RSTs are allowed. 301 StringAppendF(&commands, "-A %s -p tcp --tcp-flags RST RST -j RETURN\n", name); 302 303 if (isWhitelist) { 304 // Allow ICMPv6 packets necessary to make IPv6 connectivity work. http://b/23158230 . 305 if (target == V6) { 306 for (size_t i = 0; i < ARRAY_SIZE(ICMPV6_TYPES); i++) { 307 StringAppendF(&commands, "-A %s -p icmpv6 --icmpv6-type %s -j RETURN\n", 308 name, ICMPV6_TYPES[i]); 309 } 310 } 311 } 312 313 // Blacklist chains have UIDs at the end, and new UIDs are added with '-A'. 314 if (!isWhitelist) { 315 for (auto uid : uids) { 316 StringAppendF(&commands, "-A %s -m owner --uid-owner %d -j DROP\n", name, uid); 317 } 318 } 319 320 // If it's a whitelist chain, add a default DROP at the end. This is not necessary for a 321 // blacklist chain, because all user-defined chains implicitly RETURN at the end. 322 if (isWhitelist) { 323 StringAppendF(&commands, "-A %s -j DROP\n", name); 324 } 325 326 StringAppendF(&commands, "COMMIT\n"); 327 328 return commands; 329} 330 331int FirewallController::replaceUidChain( 332 const char *name, bool isWhitelist, const std::vector<int32_t>& uids) { 333 std::string commands4 = makeUidRules(V4, name, isWhitelist, uids); 334 std::string commands6 = makeUidRules(V6, name, isWhitelist, uids); 335 return execIptablesRestore(V4, commands4.c_str()) | execIptablesRestore(V6, commands6.c_str()); 336} 337