Home | History | Annotate | Download | only in server
      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