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 <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 
     31 using android::base::StringAppendF;
     32 
     33 auto FirewallController::execIptables = ::execIptables;
     34 auto FirewallController::execIptablesSilently = ::execIptables;
     35 auto FirewallController::execIptablesRestore = ::execIptablesRestore;
     36 
     37 const char* FirewallController::TABLE = "filter";
     38 
     39 const char* FirewallController::LOCAL_INPUT = "fw_INPUT";
     40 const char* FirewallController::LOCAL_OUTPUT = "fw_OUTPUT";
     41 const char* FirewallController::LOCAL_FORWARD = "fw_FORWARD";
     42 
     43 const char* FirewallController::LOCAL_DOZABLE = "fw_dozable";
     44 const char* FirewallController::LOCAL_STANDBY = "fw_standby";
     45 const 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).
     50 const char* FirewallController::ICMPV6_TYPES[] = {
     51     "packet-too-big",
     52     "router-solicitation",
     53     "router-advertisement",
     54     "neighbour-solicitation",
     55     "neighbour-advertisement",
     56     "redirect",
     57 };
     58 
     59 FirewallController::FirewallController(void) {
     60     // If no rules are set, it's in BLACKLIST mode
     61     mFirewallType = BLACKLIST;
     62 }
     63 
     64 int FirewallController::setupIptablesHooks(void) {
     65     int res = 0;
     66     // child chains are created but not attached, they will be attached explicitly.
     67     FirewallType firewallType = getFirewallType(DOZABLE);
     68     res |= createChain(LOCAL_DOZABLE, LOCAL_INPUT, firewallType);
     69 
     70     firewallType = getFirewallType(STANDBY);
     71     res |= createChain(LOCAL_STANDBY, LOCAL_INPUT, firewallType);
     72 
     73     firewallType = getFirewallType(POWERSAVE);
     74     res |= createChain(LOCAL_POWERSAVE, LOCAL_INPUT, firewallType);
     75 
     76     return res;
     77 }
     78 
     79 int FirewallController::enableFirewall(FirewallType ftype) {
     80     int res = 0;
     81     if (mFirewallType != ftype) {
     82         // flush any existing rules
     83         disableFirewall();
     84 
     85         if (ftype == WHITELIST) {
     86             // create default rule to drop all traffic
     87             res |= execIptables(V4V6, "-A", LOCAL_INPUT, "-j", "DROP", NULL);
     88             res |= execIptables(V4V6, "-A", LOCAL_OUTPUT, "-j", "REJECT", NULL);
     89             res |= execIptables(V4V6, "-A", LOCAL_FORWARD, "-j", "REJECT", NULL);
     90         }
     91 
     92         // Set this after calling disableFirewall(), since it defaults to WHITELIST there
     93         mFirewallType = ftype;
     94     }
     95     return res;
     96 }
     97 
     98 int FirewallController::disableFirewall(void) {
     99     int res = 0;
    100 
    101     mFirewallType = WHITELIST;
    102 
    103     // flush any existing rules
    104     res |= execIptables(V4V6, "-F", LOCAL_INPUT, NULL);
    105     res |= execIptables(V4V6, "-F", LOCAL_OUTPUT, NULL);
    106     res |= execIptables(V4V6, "-F", LOCAL_FORWARD, NULL);
    107 
    108     return res;
    109 }
    110 
    111 int FirewallController::enableChildChains(ChildChain chain, bool enable) {
    112     int res = 0;
    113     const char* name;
    114     switch(chain) {
    115         case DOZABLE:
    116             name = LOCAL_DOZABLE;
    117             break;
    118         case STANDBY:
    119             name = LOCAL_STANDBY;
    120             break;
    121         case POWERSAVE:
    122             name = LOCAL_POWERSAVE;
    123             break;
    124         default:
    125             return res;
    126     }
    127 
    128     if (enable) {
    129         res |= attachChain(name, LOCAL_INPUT);
    130         res |= attachChain(name, LOCAL_OUTPUT);
    131     } else {
    132         res |= detachChain(name, LOCAL_INPUT);
    133         res |= detachChain(name, LOCAL_OUTPUT);
    134     }
    135     return res;
    136 }
    137 
    138 int FirewallController::isFirewallEnabled(void) {
    139     // TODO: verify that rules are still in place near top
    140     return -1;
    141 }
    142 
    143 int FirewallController::setInterfaceRule(const char* iface, FirewallRule rule) {
    144     if (mFirewallType == BLACKLIST) {
    145         // Unsupported in BLACKLIST mode
    146         return -1;
    147     }
    148 
    149     if (!isIfaceName(iface)) {
    150         errno = ENOENT;
    151         return -1;
    152     }
    153 
    154     const char* op;
    155     if (rule == ALLOW) {
    156         op = "-I";
    157     } else {
    158         op = "-D";
    159     }
    160 
    161     int res = 0;
    162     res |= execIptables(V4V6, op, LOCAL_INPUT, "-i", iface, "-j", "RETURN", NULL);
    163     res |= execIptables(V4V6, op, LOCAL_OUTPUT, "-o", iface, "-j", "RETURN", NULL);
    164     return res;
    165 }
    166 
    167 int FirewallController::setEgressSourceRule(const char* addr, FirewallRule rule) {
    168     if (mFirewallType == BLACKLIST) {
    169         // Unsupported in BLACKLIST mode
    170         return -1;
    171     }
    172 
    173     IptablesTarget target = V4;
    174     if (strchr(addr, ':')) {
    175         target = V6;
    176     }
    177 
    178     const char* op;
    179     if (rule == ALLOW) {
    180         op = "-I";
    181     } else {
    182         op = "-D";
    183     }
    184 
    185     int res = 0;
    186     res |= execIptables(target, op, LOCAL_INPUT, "-d", addr, "-j", "RETURN", NULL);
    187     res |= execIptables(target, op, LOCAL_OUTPUT, "-s", addr, "-j", "RETURN", NULL);
    188     return res;
    189 }
    190 
    191 int FirewallController::setEgressDestRule(const char* addr, int protocol, int port,
    192         FirewallRule rule) {
    193     if (mFirewallType == BLACKLIST) {
    194         // Unsupported in BLACKLIST mode
    195         return -1;
    196     }
    197 
    198     IptablesTarget target = V4;
    199     if (strchr(addr, ':')) {
    200         target = V6;
    201     }
    202 
    203     char protocolStr[16];
    204     sprintf(protocolStr, "%d", protocol);
    205 
    206     char portStr[16];
    207     sprintf(portStr, "%d", port);
    208 
    209     const char* op;
    210     if (rule == ALLOW) {
    211         op = "-I";
    212     } else {
    213         op = "-D";
    214     }
    215 
    216     int res = 0;
    217     res |= execIptables(target, op, LOCAL_INPUT, "-s", addr, "-p", protocolStr,
    218             "--sport", portStr, "-j", "RETURN", NULL);
    219     res |= execIptables(target, op, LOCAL_OUTPUT, "-d", addr, "-p", protocolStr,
    220             "--dport", portStr, "-j", "RETURN", NULL);
    221     return res;
    222 }
    223 
    224 FirewallType FirewallController::getFirewallType(ChildChain chain) {
    225     switch(chain) {
    226         case DOZABLE:
    227             return WHITELIST;
    228         case STANDBY:
    229             return BLACKLIST;
    230         case POWERSAVE:
    231             return WHITELIST;
    232         case NONE:
    233             return mFirewallType;
    234         default:
    235             return BLACKLIST;
    236     }
    237 }
    238 
    239 int FirewallController::setUidRule(ChildChain chain, int uid, FirewallRule rule) {
    240     char uidStr[16];
    241     sprintf(uidStr, "%d", uid);
    242 
    243     const char* op;
    244     const char* target;
    245     FirewallType firewallType = getFirewallType(chain);
    246     if (firewallType == WHITELIST) {
    247         target = "RETURN";
    248         // When adding, insert RETURN rules at the front, before the catch-all DROP at the end.
    249         op = (rule == ALLOW)? "-I" : "-D";
    250     } else { // BLACKLIST mode
    251         target = "DROP";
    252         // When adding, append DROP rules at the end, after the RETURN rule that matches TCP RSTs.
    253         op = (rule == DENY)? "-A" : "-D";
    254     }
    255 
    256     int res = 0;
    257     switch(chain) {
    258         case DOZABLE:
    259             res |= execIptables(V4V6, op, LOCAL_DOZABLE, "-m", "owner", "--uid-owner",
    260                     uidStr, "-j", target, NULL);
    261             break;
    262         case STANDBY:
    263             res |= execIptables(V4V6, op, LOCAL_STANDBY, "-m", "owner", "--uid-owner",
    264                     uidStr, "-j", target, NULL);
    265             break;
    266         case POWERSAVE:
    267             res |= execIptables(V4V6, op, LOCAL_POWERSAVE, "-m", "owner", "--uid-owner",
    268                     uidStr, "-j", target, NULL);
    269             break;
    270         case NONE:
    271             res |= execIptables(V4V6, op, LOCAL_INPUT, "-m", "owner", "--uid-owner", uidStr,
    272                     "-j", target, NULL);
    273             res |= execIptables(V4V6, op, LOCAL_OUTPUT, "-m", "owner", "--uid-owner", uidStr,
    274                     "-j", target, NULL);
    275             break;
    276         default:
    277             ALOGW("Unknown child chain: %d", chain);
    278             break;
    279     }
    280     return res;
    281 }
    282 
    283 int FirewallController::attachChain(const char* childChain, const char* parentChain) {
    284     return execIptables(V4V6, "-t", TABLE, "-A", parentChain, "-j", childChain, NULL);
    285 }
    286 
    287 int FirewallController::detachChain(const char* childChain, const char* parentChain) {
    288     return execIptables(V4V6, "-t", TABLE, "-D", parentChain, "-j", childChain, NULL);
    289 }
    290 
    291 int FirewallController::createChain(const char* childChain,
    292         const char* parentChain, FirewallType type) {
    293     execIptablesSilently(V4V6, "-t", TABLE, "-D", parentChain, "-j", childChain, NULL);
    294     std::vector<int32_t> uids;
    295     return replaceUidChain(childChain, type == WHITELIST, uids);
    296 }
    297 
    298 std::string FirewallController::makeUidRules(IptablesTarget target, const char *name,
    299         bool isWhitelist, const std::vector<int32_t>& uids) {
    300     std::string commands;
    301     StringAppendF(&commands, "*filter\n:%s -\n", name);
    302 
    303     // Allow TCP RSTs so we can cleanly close TCP connections of apps that no longer have network
    304     // access. Both incoming and outgoing RSTs are allowed.
    305     StringAppendF(&commands, "-A %s -p tcp --tcp-flags RST RST -j RETURN\n", name);
    306 
    307     if (isWhitelist) {
    308         // Allow ICMPv6 packets necessary to make IPv6 connectivity work. http://b/23158230 .
    309         if (target == V6) {
    310             for (size_t i = 0; i < ARRAY_SIZE(ICMPV6_TYPES); i++) {
    311                 StringAppendF(&commands, "-A %s -p icmpv6 --icmpv6-type %s -j RETURN\n",
    312                        name, ICMPV6_TYPES[i]);
    313             }
    314         }
    315 
    316         // Always whitelist system UIDs.
    317         StringAppendF(&commands,
    318                 "-A %s -m owner --uid-owner %d-%d -j RETURN\n", name, 0, MAX_SYSTEM_UID);
    319     }
    320 
    321     // Whitelist or blacklist the specified UIDs.
    322     const char *action = isWhitelist ? "RETURN" : "DROP";
    323     for (auto uid : uids) {
    324         StringAppendF(&commands, "-A %s -m owner --uid-owner %d -j %s\n", name, uid, action);
    325     }
    326 
    327     // If it's a whitelist chain, add a default DROP at the end. This is not necessary for a
    328     // blacklist chain, because all user-defined chains implicitly RETURN at the end.
    329     if (isWhitelist) {
    330         StringAppendF(&commands, "-A %s -j DROP\n", name);
    331     }
    332 
    333     StringAppendF(&commands, "COMMIT\n\x04");  // EOT.
    334 
    335     return commands;
    336 }
    337 
    338 int FirewallController::replaceUidChain(
    339         const char *name, bool isWhitelist, const std::vector<int32_t>& uids) {
    340    std::string commands4 = makeUidRules(V4, name, isWhitelist, uids);
    341    std::string commands6 = makeUidRules(V6, name, isWhitelist, uids);
    342    return execIptablesRestore(V4, commands4.c_str()) | execIptablesRestore(V6, commands6.c_str());
    343 }
    344