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