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 
IsValidInterfaceName(const std::string & iface)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 
RunForAllArguments(const IpTablesCallback & iptables_cmd,const std::vector<std::string> & arguments,bool add)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 
IpTables()104 IpTables::IpTables() {
105 }
106 
~IpTables()107 IpTables::~IpTables() {
108   // Plug all holes when destructed.
109   PlugAllHoles();
110 }
111 
PunchTcpHole(uint16_t in_port,const std::string & in_interface)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 
PunchUdpHole(uint16_t in_port,const std::string & in_interface)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 
PlugTcpHole(uint16_t in_port,const std::string & in_interface)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 
PlugUdpHole(uint16_t in_port,const std::string & in_interface)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 
RequestVpnSetup(const std::vector<std::string> & usernames,const std::string & interface)128 bool IpTables::RequestVpnSetup(const std::vector<std::string>& usernames,
129                                const std::string& interface) {
130   return ApplyVpnSetup(usernames, interface, true /* add */);
131 }
132 
RemoveVpnSetup(const std::vector<std::string> & usernames,const std::string & interface)133 bool IpTables::RemoveVpnSetup(const std::vector<std::string>& usernames,
134                               const std::string& interface) {
135   return ApplyVpnSetup(usernames, interface, false /* delete */);
136 }
137 
PunchHole(uint16_t port,const std::string & interface,std::set<Hole> * holes,ProtocolEnum protocol)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 
PlugHole(uint16_t port,const std::string & interface,std::set<Hole> * holes,ProtocolEnum protocol)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 
PlugAllHoles()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 
AddAcceptRules(ProtocolEnum protocol,uint16_t port,const std::string & interface)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 
DeleteAcceptRules(ProtocolEnum protocol,uint16_t port,const std::string & interface)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 
ApplyVpnSetup(const std::vector<std::string> & usernames,const std::string & interface,bool add)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 
ApplyMasquerade(const std::string & interface,bool add)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 
ApplyMarkForUserTraffic(const std::string & username,bool add)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 
ApplyRuleForUserTraffic(bool add)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 
AddAcceptRule(const std::string & executable_path,ProtocolEnum protocol,uint16_t port,const std::string & interface)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 
DeleteAcceptRule(const std::string & executable_path,ProtocolEnum protocol,uint16_t port,const std::string & interface)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 
ApplyMasqueradeWithExecutable(const std::string & interface,const std::string & executable_path,bool add)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 
ApplyMarkForUserTrafficWithExecutable(const std::string & username,const std::string & executable_path,bool add)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 
ApplyRuleForUserTrafficWithVersion(const std::string & ip_version,bool add)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 
ExecvNonRoot(const std::vector<std::string> & argv,uint64_t capmask)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