Home | History | Annotate | Download | only in firewalld
      1 // Copyright 2014 The Android Open Source Project
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License");
      4 // you may not use this file except in compliance with the License.
      5 // You may obtain a copy of the License at
      6 //
      7 //      http://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 // Unless required by applicable law or agreed to in writing, software
     10 // distributed under the License is distributed on an "AS IS" BASIS,
     11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 // See the License for the specific language governing permissions and
     13 // limitations under the License.
     14 
     15 #include "iptables.h"
     16 
     17 #include <linux/capability.h>
     18 
     19 #include <string>
     20 #include <vector>
     21 
     22 #include <base/bind.h>
     23 #include <base/bind_helpers.h>
     24 #include <base/callback.h>
     25 #include <base/logging.h>
     26 #include <base/strings/string_number_conversions.h>
     27 #include <base/strings/string_util.h>
     28 #include <base/strings/stringprintf.h>
     29 #include <brillo/minijail/minijail.h>
     30 #include <brillo/process.h>
     31 
     32 namespace {
     33 
     34 using IpTablesCallback = base::Callback<bool(const std::string&, bool)>;
     35 
     36 #if defined(__ANDROID__)
     37 const char kIpTablesPath[] = "/system/bin/iptables";
     38 const char kIp6TablesPath[] = "/system/bin/ip6tables";
     39 const char kIpPath[] = "/system/bin/ip";
     40 #else
     41 const char kIpTablesPath[] = "/sbin/iptables";
     42 const char kIp6TablesPath[] = "/sbin/ip6tables";
     43 const char kIpPath[] = "/bin/ip";
     44 const char kUnprivilegedUser[] = "nobody";
     45 #endif  // __ANDROID__
     46 
     47 const char kIPv4[] = "IPv4";
     48 const char kIPv6[] = "IPv6";
     49 
     50 const uint64_t kIpTablesCapMask =
     51     CAP_TO_MASK(CAP_NET_ADMIN) | CAP_TO_MASK(CAP_NET_RAW);
     52 
     53 // Interface names must be shorter than 'IFNAMSIZ' chars.
     54 // See http://man7.org/linux/man-pages/man7/netdevice.7.html
     55 // 'IFNAMSIZ' is 16 in recent kernels.
     56 // See http://lxr.free-electrons.com/source/include/uapi/linux/if.h#L26
     57 const size_t kInterfaceNameSize = 16;
     58 
     59 const char kMarkForUserTraffic[] = "1";
     60 
     61 const char kTableIdForUserTraffic[] = "1";
     62 
     63 bool IsValidInterfaceName(const std::string& iface) {
     64   // |iface| should be shorter than |kInterfaceNameSize| chars and have only
     65   // alphanumeric characters (embedded hypens and periods are also permitted).
     66   if (iface.length() >= kInterfaceNameSize) {
     67     return false;
     68   }
     69   if (base::StartsWith(iface, "-", base::CompareCase::SENSITIVE) ||
     70       base::EndsWith(iface, "-", base::CompareCase::SENSITIVE) ||
     71       base::StartsWith(iface, ".", base::CompareCase::SENSITIVE) ||
     72       base::EndsWith(iface, ".", base::CompareCase::SENSITIVE)) {
     73     return false;
     74   }
     75   for (auto c : iface) {
     76     if (!std::isalnum(c) && (c != '-') && (c != '.')) {
     77       return false;
     78     }
     79   }
     80   return true;
     81 }
     82 
     83 bool RunForAllArguments(const IpTablesCallback& iptables_cmd,
     84                         const std::vector<std::string>& arguments,
     85                         bool add) {
     86   bool success = true;
     87   for (const auto& argument : arguments) {
     88     if (!iptables_cmd.Run(argument, add)) {
     89       // On failure, only abort if rules are being added.
     90       // If removing a rule fails, attempt the remaining removals but still
     91       // return 'false'.
     92       success = false;
     93       if (add)
     94         break;
     95     }
     96   }
     97   return success;
     98 }
     99 
    100 }  // namespace
    101 
    102 namespace firewalld {
    103 
    104 IpTables::IpTables() {
    105 }
    106 
    107 IpTables::~IpTables() {
    108   // Plug all holes when destructed.
    109   PlugAllHoles();
    110 }
    111 
    112 bool IpTables::PunchTcpHole(uint16_t in_port, const std::string& in_interface) {
    113   return PunchHole(in_port, in_interface, &tcp_holes_, kProtocolTcp);
    114 }
    115 
    116 bool IpTables::PunchUdpHole(uint16_t in_port, const std::string& in_interface) {
    117   return PunchHole(in_port, in_interface, &udp_holes_, kProtocolUdp);
    118 }
    119 
    120 bool IpTables::PlugTcpHole(uint16_t in_port, const std::string& in_interface) {
    121   return PlugHole(in_port, in_interface, &tcp_holes_, kProtocolTcp);
    122 }
    123 
    124 bool IpTables::PlugUdpHole(uint16_t in_port, const std::string& in_interface) {
    125   return PlugHole(in_port, in_interface, &udp_holes_, kProtocolUdp);
    126 }
    127 
    128 bool IpTables::RequestVpnSetup(const std::vector<std::string>& usernames,
    129                                const std::string& interface) {
    130   return ApplyVpnSetup(usernames, interface, true /* add */);
    131 }
    132 
    133 bool IpTables::RemoveVpnSetup(const std::vector<std::string>& usernames,
    134                               const std::string& interface) {
    135   return ApplyVpnSetup(usernames, interface, false /* delete */);
    136 }
    137 
    138 bool IpTables::PunchHole(uint16_t port,
    139                          const std::string& interface,
    140                          std::set<Hole>* holes,
    141                          ProtocolEnum protocol) {
    142   if (port == 0) {
    143     // Port 0 is not a valid TCP/UDP port.
    144     return false;
    145   }
    146 
    147   if (!IsValidInterfaceName(interface)) {
    148     LOG(ERROR) << "Invalid interface name '" << interface << "'";
    149     return false;
    150   }
    151 
    152   Hole hole = std::make_pair(port, interface);
    153   if (holes->find(hole) != holes->end()) {
    154     // We have already punched a hole for |port| on |interface|.
    155     // Be idempotent: do nothing and succeed.
    156     return true;
    157   }
    158 
    159   std::string sprotocol = protocol == kProtocolTcp ? "TCP" : "UDP";
    160   LOG(INFO) << "Punching hole for " << sprotocol << " port " << port
    161             << " on interface '" << interface << "'";
    162   if (!AddAcceptRules(protocol, port, interface)) {
    163     // If the 'iptables' command fails, this method fails.
    164     LOG(ERROR) << "Adding ACCEPT rules failed.";
    165     return false;
    166   }
    167 
    168   // Track the hole we just punched.
    169   holes->insert(hole);
    170 
    171   return true;
    172 }
    173 
    174 bool IpTables::PlugHole(uint16_t port,
    175                         const std::string& interface,
    176                         std::set<Hole>* holes,
    177                         ProtocolEnum protocol) {
    178   if (port == 0) {
    179     // Port 0 is not a valid TCP/UDP port.
    180     return false;
    181   }
    182 
    183   Hole hole = std::make_pair(port, interface);
    184 
    185   if (holes->find(hole) == holes->end()) {
    186     // There is no firewall hole for |port| on |interface|.
    187     // Even though this makes |PlugHole| not idempotent,
    188     // and Punch/Plug not entirely symmetrical, fail. It might help catch bugs.
    189     return false;
    190   }
    191 
    192   std::string sprotocol = protocol == kProtocolTcp ? "TCP" : "UDP";
    193   LOG(INFO) << "Plugging hole for " << sprotocol << " port " << port
    194             << " on interface '" << interface << "'";
    195   if (!DeleteAcceptRules(protocol, port, interface)) {
    196     // If the 'iptables' command fails, this method fails.
    197     LOG(ERROR) << "Deleting ACCEPT rules failed.";
    198     return false;
    199   }
    200 
    201   // Stop tracking the hole we just plugged.
    202   holes->erase(hole);
    203 
    204   return true;
    205 }
    206 
    207 void IpTables::PlugAllHoles() {
    208   // Copy the container so that we can remove elements from the original.
    209   // TCP
    210   std::set<Hole> holes = tcp_holes_;
    211   for (auto hole : holes) {
    212     PlugHole(hole.first /* port */, hole.second /* interface */, &tcp_holes_,
    213              kProtocolTcp);
    214   }
    215 
    216   // UDP
    217   holes = udp_holes_;
    218   for (auto hole : holes) {
    219     PlugHole(hole.first /* port */, hole.second /* interface */, &udp_holes_,
    220              kProtocolUdp);
    221   }
    222 
    223   CHECK(tcp_holes_.size() == 0) << "Failed to plug all TCP holes.";
    224   CHECK(udp_holes_.size() == 0) << "Failed to plug all UDP holes.";
    225 }
    226 
    227 bool IpTables::AddAcceptRules(ProtocolEnum protocol,
    228                               uint16_t port,
    229                               const std::string& interface) {
    230   if (!AddAcceptRule(kIpTablesPath, protocol, port, interface)) {
    231     LOG(ERROR) << "Could not add ACCEPT rule using '" << kIpTablesPath << "'";
    232     return false;
    233   }
    234 
    235   if (AddAcceptRule(kIp6TablesPath, protocol, port, interface)) {
    236     // This worked, record this fact and insist that it works thereafter.
    237     ip6_enabled_ = true;
    238   } else if (ip6_enabled_) {
    239     // It's supposed to work, fail.
    240     LOG(ERROR) << "Could not add ACCEPT rule using '" << kIp6TablesPath
    241                << "', aborting operation.";
    242     DeleteAcceptRule(kIpTablesPath, protocol, port, interface);
    243     return false;
    244   } else {
    245     // It never worked, just ignore it.
    246     LOG(WARNING) << "Could not add ACCEPT rule using '" << kIp6TablesPath
    247                  << "', ignoring.";
    248   }
    249 
    250   return true;
    251 }
    252 
    253 bool IpTables::DeleteAcceptRules(ProtocolEnum protocol,
    254                                  uint16_t port,
    255                                  const std::string& interface) {
    256   bool ip4_success = DeleteAcceptRule(kIpTablesPath, protocol, port,
    257                                       interface);
    258   bool ip6_success = !ip6_enabled_ || DeleteAcceptRule(kIp6TablesPath, protocol,
    259                                                        port, interface);
    260   return ip4_success && ip6_success;
    261 }
    262 
    263 bool IpTables::ApplyVpnSetup(const std::vector<std::string>& usernames,
    264                              const std::string& interface,
    265                              bool add) {
    266   bool success = true;
    267   std::vector<std::string> added_usernames;
    268 
    269   if (!ApplyRuleForUserTraffic(add)) {
    270     if (add) {
    271       ApplyRuleForUserTraffic(false /* remove */);
    272       return false;
    273     }
    274     success = false;
    275   }
    276 
    277   if (!ApplyMasquerade(interface, add)) {
    278     if (add) {
    279       ApplyVpnSetup(added_usernames, interface, false /* remove */);
    280       return false;
    281     }
    282     success = false;
    283   }
    284 
    285   for (const auto& username : usernames) {
    286     if (!ApplyMarkForUserTraffic(username, add)) {
    287       if (add) {
    288         ApplyVpnSetup(added_usernames, interface, false /* remove */);
    289         return false;
    290       }
    291       success = false;
    292     }
    293     if (add) {
    294       added_usernames.push_back(username);
    295     }
    296   }
    297 
    298   return success;
    299 }
    300 
    301 bool IpTables::ApplyMasquerade(const std::string& interface, bool add) {
    302   const IpTablesCallback apply_masquerade =
    303       base::Bind(&IpTables::ApplyMasqueradeWithExecutable,
    304                  base::Unretained(this),
    305                  interface);
    306 
    307   return RunForAllArguments(
    308       apply_masquerade, {kIpTablesPath, kIp6TablesPath}, add);
    309 }
    310 
    311 bool IpTables::ApplyMarkForUserTraffic(const std::string& username, bool add) {
    312   const IpTablesCallback apply_mark =
    313       base::Bind(&IpTables::ApplyMarkForUserTrafficWithExecutable,
    314                  base::Unretained(this),
    315                  username);
    316 
    317   return RunForAllArguments(apply_mark, {kIpTablesPath, kIp6TablesPath}, add);
    318 }
    319 
    320 bool IpTables::ApplyRuleForUserTraffic(bool add) {
    321   const IpTablesCallback apply_rule = base::Bind(
    322       &IpTables::ApplyRuleForUserTrafficWithVersion, base::Unretained(this));
    323 
    324   return RunForAllArguments(apply_rule, {kIPv4, kIPv6}, add);
    325 }
    326 
    327 bool IpTables::AddAcceptRule(const std::string& executable_path,
    328                              ProtocolEnum protocol,
    329                              uint16_t port,
    330                              const std::string& interface) {
    331   std::vector<std::string> argv;
    332   argv.push_back(executable_path);
    333   argv.push_back("-I");  // insert
    334   argv.push_back("INPUT");
    335   argv.push_back("-p");  // protocol
    336   argv.push_back(protocol == kProtocolTcp ? "tcp" : "udp");
    337   argv.push_back("--dport");  // destination port
    338   argv.push_back(std::to_string(port));
    339   if (!interface.empty()) {
    340     argv.push_back("-i");  // interface
    341     argv.push_back(interface);
    342   }
    343   argv.push_back("-j");
    344   argv.push_back("ACCEPT");
    345   argv.push_back("-w");  // Wait for xtables lock.
    346 
    347   // Use CAP_NET_ADMIN|CAP_NET_RAW.
    348   return ExecvNonRoot(argv, kIpTablesCapMask) == 0;
    349 }
    350 
    351 bool IpTables::DeleteAcceptRule(const std::string& executable_path,
    352                                 ProtocolEnum protocol,
    353                                 uint16_t port,
    354                                 const std::string& interface) {
    355   std::vector<std::string> argv;
    356   argv.push_back(executable_path);
    357   argv.push_back("-D");  // delete
    358   argv.push_back("INPUT");
    359   argv.push_back("-p");  // protocol
    360   argv.push_back(protocol == kProtocolTcp ? "tcp" : "udp");
    361   argv.push_back("--dport");  // destination port
    362   argv.push_back(std::to_string(port));
    363   if (interface != "") {
    364     argv.push_back("-i");  // interface
    365     argv.push_back(interface);
    366   }
    367   argv.push_back("-j");
    368   argv.push_back("ACCEPT");
    369   argv.push_back("-w");  // Wait for xtables lock.
    370 
    371   // Use CAP_NET_ADMIN|CAP_NET_RAW.
    372   return ExecvNonRoot(argv, kIpTablesCapMask) == 0;
    373 }
    374 
    375 bool IpTables::ApplyMasqueradeWithExecutable(const std::string& interface,
    376                                              const std::string& executable_path,
    377                                              bool add) {
    378   std::vector<std::string> argv;
    379   argv.push_back(executable_path);
    380   argv.push_back("-t");  // table
    381   argv.push_back("nat");
    382   argv.push_back(add ? "-A" : "-D");  // rule
    383   argv.push_back("POSTROUTING");
    384   argv.push_back("-o");  // output interface
    385   argv.push_back(interface);
    386   argv.push_back("-j");
    387   argv.push_back("MASQUERADE");
    388 
    389   // Use CAP_NET_ADMIN|CAP_NET_RAW.
    390   bool success = ExecvNonRoot(argv, kIpTablesCapMask) == 0;
    391 
    392   if (!success) {
    393     LOG(ERROR) << (add ? "Adding" : "Removing")
    394                << " masquerade failed for interface " << interface
    395                << " using '" << executable_path << "'";
    396   }
    397   return success;
    398 }
    399 
    400 bool IpTables::ApplyMarkForUserTrafficWithExecutable(
    401     const std::string& username, const std::string& executable_path, bool add) {
    402   std::vector<std::string> argv;
    403   argv.push_back(executable_path);
    404   argv.push_back("-t");  // table
    405   argv.push_back("mangle");
    406   argv.push_back(add ? "-A" : "-D");  // rule
    407   argv.push_back("OUTPUT");
    408   argv.push_back("-m");
    409   argv.push_back("owner");
    410   argv.push_back("--uid-owner");
    411   argv.push_back(username);
    412   argv.push_back("-j");
    413   argv.push_back("MARK");
    414   argv.push_back("--set-mark");
    415   argv.push_back(kMarkForUserTraffic);
    416 
    417   // Use CAP_NET_ADMIN|CAP_NET_RAW.
    418   bool success = ExecvNonRoot(argv, kIpTablesCapMask) == 0;
    419 
    420   if (!success) {
    421       LOG(ERROR) << (add ? "Adding" : "Removing")
    422                  << " mark failed for user " << username
    423                  << " using '" << kIpTablesPath << "'";
    424   }
    425   return success;
    426 }
    427 
    428 bool IpTables::ApplyRuleForUserTrafficWithVersion(const std::string& ip_version,
    429                                                   bool add) {
    430   brillo::ProcessImpl ip;
    431   ip.AddArg(kIpPath);
    432   if (ip_version == kIPv6)
    433     ip.AddArg("-6");
    434   ip.AddArg("rule");
    435   ip.AddArg(add ? "add" : "delete");
    436   ip.AddArg("fwmark");
    437   ip.AddArg(kMarkForUserTraffic);
    438   ip.AddArg("table");
    439   ip.AddArg(kTableIdForUserTraffic);
    440 
    441   bool success = ip.Run() == 0;
    442 
    443   if (!success) {
    444     LOG(ERROR) << (add ? "Adding" : "Removing") << " rule for " << ip_version
    445                << " user traffic failed";
    446   }
    447   return success;
    448 }
    449 
    450 int IpTables::ExecvNonRoot(const std::vector<std::string>& argv,
    451                            uint64_t capmask) {
    452   brillo::Minijail* m = brillo::Minijail::GetInstance();
    453   minijail* jail = m->New();
    454 #if !defined(__ANDROID__)
    455   // TODO(garnold) This needs to be re-enabled once we figure out which
    456   // unprivileged user we want to use.
    457   m->DropRoot(jail, kUnprivilegedUser, kUnprivilegedUser);
    458 #endif  // __ANDROID__
    459   m->UseCapabilities(jail, capmask);
    460 
    461   std::vector<char*> args;
    462   for (const auto& arg : argv) {
    463     args.push_back(const_cast<char*>(arg.c_str()));
    464   }
    465   args.push_back(nullptr);
    466 
    467   int status;
    468   bool ran = m->RunSyncAndDestroy(jail, args, &status);
    469   return ran ? status : -1;
    470 }
    471 
    472 }  // namespace firewalld
    473