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