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 <set> 18 19 #include <cstdint> 20 #include <errno.h> 21 #include <stdio.h> 22 #include <stdlib.h> 23 #include <string.h> 24 25 #define LOG_TAG "FirewallController" 26 #define LOG_NDEBUG 0 27 28 #include <android-base/strings.h> 29 #include <android-base/stringprintf.h> 30 #include <cutils/log.h> 31 32 #include "Controllers.h" 33 #include "FirewallController.h" 34 #include "NetdConstants.h" 35 #include "bpf/BpfUtils.h" 36 37 using android::base::Join; 38 using android::base::StringAppendF; 39 using android::base::StringPrintf; 40 using android::bpf::DOZABLE_UID_MAP_PATH; 41 using android::bpf::POWERSAVE_UID_MAP_PATH; 42 using android::bpf::STANDBY_UID_MAP_PATH; 43 using android::net::gCtls; 44 45 auto FirewallController::execIptablesRestore = ::execIptablesRestore; 46 47 const char* FirewallController::TABLE = "filter"; 48 49 const char* FirewallController::LOCAL_INPUT = "fw_INPUT"; 50 const char* FirewallController::LOCAL_OUTPUT = "fw_OUTPUT"; 51 const char* FirewallController::LOCAL_FORWARD = "fw_FORWARD"; 52 53 const char* FirewallController::LOCAL_DOZABLE = "fw_dozable"; 54 const char* FirewallController::LOCAL_STANDBY = "fw_standby"; 55 const char* FirewallController::LOCAL_POWERSAVE = "fw_powersave"; 56 57 // ICMPv6 types that are required for any form of IPv6 connectivity to work. Note that because the 58 // fw_dozable chain is called from both INPUT and OUTPUT, this includes both packets that we need 59 // to be able to send (e.g., RS, NS), and packets that we need to receive (e.g., RA, NA). 60 const char* FirewallController::ICMPV6_TYPES[] = { 61 "packet-too-big", 62 "router-solicitation", 63 "router-advertisement", 64 "neighbour-solicitation", 65 "neighbour-advertisement", 66 "redirect", 67 }; 68 69 bool getBpfOwnerStatus() { 70 return gCtls->trafficCtrl.checkBpfStatsEnable(); 71 } 72 73 FirewallController::FirewallController(void) { 74 // If no rules are set, it's in BLACKLIST mode 75 mFirewallType = BLACKLIST; 76 mIfaceRules = {}; 77 } 78 79 int FirewallController::setupIptablesHooks(void) { 80 int res = 0; 81 mUseBpfOwnerMatch = getBpfOwnerStatus(); 82 if (mUseBpfOwnerMatch) { 83 return res; 84 } 85 res |= createChain(LOCAL_DOZABLE, getFirewallType(DOZABLE)); 86 res |= createChain(LOCAL_STANDBY, getFirewallType(STANDBY)); 87 res |= createChain(LOCAL_POWERSAVE, getFirewallType(POWERSAVE)); 88 return res; 89 } 90 91 int FirewallController::enableFirewall(FirewallType ftype) { 92 int res = 0; 93 if (mFirewallType != ftype) { 94 // flush any existing rules 95 disableFirewall(); 96 97 if (ftype == WHITELIST) { 98 // create default rule to drop all traffic 99 std::string command = 100 "*filter\n" 101 "-A fw_INPUT -j DROP\n" 102 "-A fw_OUTPUT -j REJECT\n" 103 "-A fw_FORWARD -j REJECT\n" 104 "COMMIT\n"; 105 res = execIptablesRestore(V4V6, command.c_str()); 106 } 107 108 // Set this after calling disableFirewall(), since it defaults to WHITELIST there 109 mFirewallType = ftype; 110 } 111 return res; 112 } 113 114 int FirewallController::disableFirewall(void) { 115 mFirewallType = WHITELIST; 116 mIfaceRules.clear(); 117 118 // flush any existing rules 119 std::string command = 120 "*filter\n" 121 ":fw_INPUT -\n" 122 ":fw_OUTPUT -\n" 123 ":fw_FORWARD -\n" 124 "COMMIT\n"; 125 126 return execIptablesRestore(V4V6, command.c_str()); 127 } 128 129 int FirewallController::enableChildChains(ChildChain chain, bool enable) { 130 int res = 0; 131 const char* name; 132 switch(chain) { 133 case DOZABLE: 134 name = LOCAL_DOZABLE; 135 break; 136 case STANDBY: 137 name = LOCAL_STANDBY; 138 break; 139 case POWERSAVE: 140 name = LOCAL_POWERSAVE; 141 break; 142 default: 143 return res; 144 } 145 146 if (mUseBpfOwnerMatch) { 147 return gCtls->trafficCtrl.toggleUidOwnerMap(chain, enable); 148 } 149 150 std::string command = "*filter\n"; 151 for (const char *parent : { LOCAL_INPUT, LOCAL_OUTPUT }) { 152 StringAppendF(&command, "%s %s -j %s\n", (enable ? "-A" : "-D"), parent, name); 153 } 154 StringAppendF(&command, "COMMIT\n"); 155 156 return execIptablesRestore(V4V6, command); 157 } 158 159 int FirewallController::isFirewallEnabled(void) { 160 // TODO: verify that rules are still in place near top 161 return -1; 162 } 163 164 int FirewallController::setInterfaceRule(const char* iface, FirewallRule rule) { 165 if (mFirewallType == BLACKLIST) { 166 // Unsupported in BLACKLIST mode 167 return -1; 168 } 169 170 if (!isIfaceName(iface)) { 171 errno = ENOENT; 172 return -1; 173 } 174 175 // Only delete rules if we actually added them, because otherwise our iptables-restore 176 // processes will terminate with "no such rule" errors and cause latency penalties while we 177 // spin up new ones. 178 const char* op; 179 if (rule == ALLOW && mIfaceRules.find(iface) == mIfaceRules.end()) { 180 op = "-I"; 181 mIfaceRules.insert(iface); 182 } else if (rule == DENY && mIfaceRules.find(iface) != mIfaceRules.end()) { 183 op = "-D"; 184 mIfaceRules.erase(iface); 185 } else { 186 return 0; 187 } 188 189 std::string command = Join(std::vector<std::string> { 190 "*filter", 191 StringPrintf("%s fw_INPUT -i %s -j RETURN", op, iface), 192 StringPrintf("%s fw_OUTPUT -o %s -j RETURN", op, iface), 193 "COMMIT\n" 194 }, "\n"); 195 return execIptablesRestore(V4V6, command); 196 } 197 198 FirewallType FirewallController::getFirewallType(ChildChain chain) { 199 switch(chain) { 200 case DOZABLE: 201 return WHITELIST; 202 case STANDBY: 203 return BLACKLIST; 204 case POWERSAVE: 205 return WHITELIST; 206 case NONE: 207 return mFirewallType; 208 default: 209 return BLACKLIST; 210 } 211 } 212 213 int FirewallController::setUidRule(ChildChain chain, int uid, FirewallRule rule) { 214 const char* op; 215 const char* target; 216 FirewallType firewallType = getFirewallType(chain); 217 if (firewallType == WHITELIST) { 218 target = "RETURN"; 219 // When adding, insert RETURN rules at the front, before the catch-all DROP at the end. 220 op = (rule == ALLOW)? "-I" : "-D"; 221 } else { // BLACKLIST mode 222 target = "DROP"; 223 // When adding, append DROP rules at the end, after the RETURN rule that matches TCP RSTs. 224 op = (rule == DENY)? "-A" : "-D"; 225 } 226 227 std::vector<std::string> chainNames; 228 switch(chain) { 229 case DOZABLE: 230 chainNames = { LOCAL_DOZABLE }; 231 break; 232 case STANDBY: 233 chainNames = { LOCAL_STANDBY }; 234 break; 235 case POWERSAVE: 236 chainNames = { LOCAL_POWERSAVE }; 237 break; 238 case NONE: 239 chainNames = { LOCAL_INPUT, LOCAL_OUTPUT }; 240 break; 241 default: 242 ALOGW("Unknown child chain: %d", chain); 243 return -1; 244 } 245 if (mUseBpfOwnerMatch) { 246 return gCtls->trafficCtrl.changeUidOwnerRule(chain, uid, rule, firewallType); 247 } 248 249 std::string command = "*filter\n"; 250 for (std::string chainName : chainNames) { 251 StringAppendF(&command, "%s %s -m owner --uid-owner %d -j %s\n", 252 op, chainName.c_str(), uid, target); 253 } 254 StringAppendF(&command, "COMMIT\n"); 255 256 return execIptablesRestore(V4V6, command); 257 } 258 259 int FirewallController::createChain(const char* chain, FirewallType type) { 260 static const std::vector<int32_t> NO_UIDS; 261 return replaceUidChain(chain, type == WHITELIST, NO_UIDS); 262 } 263 264 /* static */ 265 std::string FirewallController::makeCriticalCommands(IptablesTarget target, const char* chainName) { 266 // Allow ICMPv6 packets necessary to make IPv6 connectivity work. http://b/23158230 . 267 std::string commands; 268 if (target == V6) { 269 for (size_t i = 0; i < ARRAY_SIZE(ICMPV6_TYPES); i++) { 270 StringAppendF(&commands, "-A %s -p icmpv6 --icmpv6-type %s -j RETURN\n", 271 chainName, ICMPV6_TYPES[i]); 272 } 273 } 274 return commands; 275 } 276 277 std::string FirewallController::makeUidRules(IptablesTarget target, const char *name, 278 bool isWhitelist, const std::vector<int32_t>& uids) { 279 std::string commands; 280 StringAppendF(&commands, "*filter\n:%s -\n", name); 281 282 // Whitelist chains have UIDs at the beginning, and new UIDs are added with '-I'. 283 if (isWhitelist) { 284 for (auto uid : uids) { 285 StringAppendF(&commands, "-A %s -m owner --uid-owner %d -j RETURN\n", name, uid); 286 } 287 288 // Always whitelist system UIDs. 289 StringAppendF(&commands, 290 "-A %s -m owner --uid-owner %d-%d -j RETURN\n", name, 0, MAX_SYSTEM_UID); 291 292 // This rule inverts the match for all UIDs; ie, if there is no UID match here, 293 // there is no socket to be found 294 StringAppendF(&commands, 295 "-A %s -m owner ! --uid-owner %d-%u -j RETURN\n", name, 0, UINT32_MAX-1); 296 297 // Always whitelist traffic with protocol ESP, or no known socket - required for IPSec 298 StringAppendF(&commands, "-A %s -p esp -j RETURN\n", name); 299 } 300 301 // Always allow networking on loopback. 302 StringAppendF(&commands, "-A %s -i lo -j RETURN\n", name); 303 StringAppendF(&commands, "-A %s -o lo -j RETURN\n", name); 304 305 // Allow TCP RSTs so we can cleanly close TCP connections of apps that no longer have network 306 // access. Both incoming and outgoing RSTs are allowed. 307 StringAppendF(&commands, "-A %s -p tcp --tcp-flags RST RST -j RETURN\n", name); 308 309 if (isWhitelist) { 310 commands.append(makeCriticalCommands(target, name)); 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 331 int FirewallController::replaceUidChain( 332 const char *name, bool isWhitelist, const std::vector<int32_t>& uids) { 333 if (mUseBpfOwnerMatch) { 334 return gCtls->trafficCtrl.replaceUidOwnerMap(name, isWhitelist, uids); 335 } 336 std::string commands4 = makeUidRules(V4, name, isWhitelist, uids); 337 std::string commands6 = makeUidRules(V6, name, isWhitelist, uids); 338 return execIptablesRestore(V4, commands4.c_str()) | execIptablesRestore(V6, commands6.c_str()); 339 } 340