Run firewalld as a regular user, in a PID namespace.

Now that we have ambient capabilities, we don't need to run firewalld
as root.

BUG=chromium:715678
TEST=platform_Firewall
TEST=readlink /proc/`pgrep firewalld`/ns/pid
TEST=pid:[4026532158]
TEST=readlink /proc/1/ns/pid
TEST=pid:[4026531836]
CQ-DEPEND=CL:494127

Change-Id: I5e65c56886e8d57bb261edb171ff16dd931d7f1d
Reviewed-on: https://chromium-review.googlesource.com/488701
Commit-Ready: Jorge Lucangeli Obes <jorgelo@chromium.org>
Tested-by: Jorge Lucangeli Obes <jorgelo@chromium.org>
Reviewed-by: Jorge Lucangeli Obes <jorgelo@chromium.org>
Reviewed-by: Mike Frysinger <vapier@chromium.org>
diff --git a/dbus/org.chromium.Firewalld.conf b/dbus/org.chromium.Firewalld.conf
index 000b74e..82da784 100644
--- a/dbus/org.chromium.Firewalld.conf
+++ b/dbus/org.chromium.Firewalld.conf
@@ -2,7 +2,7 @@
  "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
  "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
 <busconfig>
-  <policy user="root">
+  <policy user="firewall">
     <allow own="org.chromium.Firewalld"/>
   </policy>
 
diff --git a/firewalld.conf b/firewalld.conf
index bfa521a..d7d9b28 100644
--- a/firewalld.conf
+++ b/firewalld.conf
@@ -21,4 +21,5 @@
 stop on stopping system-services
 respawn
 
-exec firewalld
+# Keep CAP_NET_ADMIN|CAP_NET_RAW, run in a new PID/mount namespace.
+exec /sbin/minijail0 -u firewall -g firewall -pvr -n -c 3000 --ambient -- /usr/bin/firewalld
diff --git a/iptables.cc b/iptables.cc
index 93d2f95..13bb697 100644
--- a/iptables.cc
+++ b/iptables.cc
@@ -35,9 +35,6 @@
 const char kIPv4[] = "IPv4";
 const char kIPv6[] = "IPv6";
 
-const uint64_t kIpTablesCapMask =
-    CAP_TO_MASK(CAP_NET_ADMIN) | CAP_TO_MASK(CAP_NET_RAW);
-
 // Interface names must be shorter than 'IFNAMSIZ' chars.
 // See http://man7.org/linux/man-pages/man7/netdevice.7.html
 // 'IFNAMSIZ' is 16 in recent kernels.
@@ -333,7 +330,7 @@
   argv.push_back("-w");  // Wait for xtables lock.
 
   // Use CAP_NET_ADMIN|CAP_NET_RAW.
-  return ExecvNonRoot(argv, kIpTablesCapMask) == 0;
+  return RunInMinijail(argv) == 0;
 }
 
 bool IpTables::DeleteAcceptRule(const std::string& executable_path,
@@ -357,7 +354,7 @@
   argv.push_back("-w");  // Wait for xtables lock.
 
   // Use CAP_NET_ADMIN|CAP_NET_RAW.
-  return ExecvNonRoot(argv, kIpTablesCapMask) == 0;
+  return RunInMinijail(argv) == 0;
 }
 
 bool IpTables::ApplyMasqueradeWithExecutable(const std::string& interface,
@@ -378,7 +375,7 @@
   argv.push_back("-w");  // Wait for xtables lock
 
   // Use CAP_NET_ADMIN|CAP_NET_RAW.
-  if (ExecvNonRoot(argv, kIpTablesCapMask) != 0) {
+  if (RunInMinijail(argv)) {
     LOG(ERROR) << (add ? "Adding" : "Removing")
                << " masquerade failed for interface " << interface
                << " using '" << executable_path << "'";
@@ -405,7 +402,7 @@
   argv.push_back("--clamp-mss-to-pmtu");
   argv.push_back("-w");  // Wait for xtables lock
 
-  if (ExecvNonRoot(argv, kIpTablesCapMask) != 0) {
+  if (RunInMinijail(argv)) {
     LOG(ERROR) << (add ? "Adding" : "Removing")
                << " tcpmss rule failed for interface " << interface
                << " using '" << executable_path << "'";
@@ -434,7 +431,7 @@
   argv.push_back("-w");  // Wait for xtables lock
 
   // Use CAP_NET_ADMIN|CAP_NET_RAW.
-  bool success = ExecvNonRoot(argv, kIpTablesCapMask) == 0;
+  bool success = RunInMinijail(argv) == 0;
 
   if (!success) {
       LOG(ERROR) << (add ? "Adding" : "Removing")
@@ -457,7 +454,7 @@
   argv.push_back("table");
   argv.push_back(kTableIdForUserTraffic);
 
-  bool success = ExecvNonRoot(argv, kIpTablesCapMask) == 0;
+  bool success = RunInMinijail(argv) == 0;
 
   if (!success) {
     LOG(ERROR) << (add ? "Adding" : "Removing")
@@ -468,16 +465,9 @@
   return success;
 }
 
-int IpTables::ExecvNonRoot(const std::vector<std::string>& argv,
-                           uint64_t capmask) {
+int IpTables::RunInMinijail(const std::vector<std::string>& argv) {
   brillo::Minijail* m = brillo::Minijail::GetInstance();
   minijail* jail = m->New();
-#if !defined(__ANDROID__)
-  // TODO(garnold) This needs to be re-enabled once we figure out which
-  // unprivileged user we want to use.
-  CHECK(m->DropRoot(jail, kUnprivilegedUser, kUnprivilegedUser));
-#endif  // __ANDROID__
-  m->UseCapabilities(jail, capmask);
 
   std::vector<char*> args;
   for (const auto& arg : argv) {
diff --git a/iptables.h b/iptables.h
index beca79c..d53b90f 100644
--- a/iptables.h
+++ b/iptables.h
@@ -115,8 +115,9 @@
   bool ApplyRuleForUserTrafficWithVersion(const std::string& ip_version,
                                           bool add);
 
-  virtual int ExecvNonRoot(const std::vector<std::string>& argv,
-                           uint64_t capmask);
+  // Even though firewalld runs as a regular user, it can still add other
+  // restrictions when launching 'iptables'.
+  virtual int RunInMinijail(const std::vector<std::string>& argv);
 
   // Keep track of firewall holes to avoid adding redundant firewall rules.
   std::set<Hole> tcp_holes_;
diff --git a/iptables_unittest.cc b/iptables_unittest.cc
index bb3cff6..41319a4 100644
--- a/iptables_unittest.cc
+++ b/iptables_unittest.cc
@@ -31,19 +31,19 @@
  protected:
   void SetMockExpectations(MockIpTables* iptables, bool success) {
     // Empty criterion matches all commands.
-    iptables->SetExecvNonRootFailCriterion(std::vector<std::string>(),
-                                           true, success);
+    iptables->SetRunInMinijailFailCriterion(std::vector<std::string>(),
+                                            true, success);
   }
 
   void SetMockExpectationsPerExecutable(MockIpTables* iptables,
                                         bool ip4_success,
                                         bool ip6_success) {
     if (!ip4_success)
-      iptables->SetExecvNonRootFailCriterion(
+      iptables->SetRunInMinijailFailCriterion(
           std::vector<std::string>({kIpTablesPath}), true /* repeat */,
                                    false /* omit_failure */);
     if (!ip6_success)
-      iptables->SetExecvNonRootFailCriterion(
+      iptables->SetRunInMinijailFailCriterion(
           std::vector<std::string>({kIp6TablesPath}), true, false);
   }
 
@@ -54,7 +54,7 @@
 TEST_F(IpTablesTest, Port0Fails) {
   MockIpTables mock_iptables;
   // Don't fail on anything.
-  int id = mock_iptables.SetExecvNonRootFailCriterion(
+  int id = mock_iptables.SetRunInMinijailFailCriterion(
       std::vector<std::string>(), true, true);
 
   // Try to punch hole for TCP port 0, port 0 is not a valid port.
@@ -63,7 +63,7 @@
   EXPECT_FALSE(mock_iptables.PunchUdpHole(0, "iface"));
 
   // We should not be adding any rules for port 0.
-  EXPECT_TRUE(mock_iptables.GetExecvNonRootCriterionMatchCount(id) == 0);
+  EXPECT_TRUE(mock_iptables.GetRunInMinijailCriterionMatchCount(id) == 0);
 }
 
 TEST_F(IpTablesTest, ValidInterfaceName) {
@@ -80,7 +80,7 @@
 
 TEST_F(IpTablesTest, InvalidInterfaceName) {
   MockIpTables mock_iptables;
-  int id = mock_iptables.SetExecvNonRootFailCriterion(
+  int id = mock_iptables.SetRunInMinijailFailCriterion(
       std::vector<std::string>(), true, true);
 
   EXPECT_FALSE(mock_iptables.PunchTcpHole(80, "reallylonginterfacename"));
@@ -100,7 +100,7 @@
   EXPECT_FALSE(mock_iptables.PunchUdpHole(53, "enddot."));
 
   // We should not be adding any rules for invalid interface names.
-  EXPECT_TRUE(mock_iptables.GetExecvNonRootCriterionMatchCount(id) == 0);
+  EXPECT_TRUE(mock_iptables.GetRunInMinijailCriterionMatchCount(id) == 0);
 }
 
 TEST_F(IpTablesTest, PunchTcpHoleSucceeds) {
@@ -198,16 +198,16 @@
 
   MockIpTables mock_iptables;
   // Fail on all commands issued for testuser1.
-  int id = mock_iptables.SetExecvNonRootFailCriterion(
+  int id = mock_iptables.SetRunInMinijailFailCriterion(
       std::vector<std::string>({usernames[1], "-A"}), true, false);
-  // Fail on all the calls to ExecvNonRoot issued by the
+  // Fail on all the calls to RunInMinijail issued by the
   // attempt to apply marks for the last user.
   ASSERT_FALSE(
       mock_iptables.ApplyVpnSetup(usernames, interface, add));
   EXPECT_TRUE(mock_iptables.CheckCommandsUndone());
 
   // Should have failed only on the first testuser1 command.
-  EXPECT_TRUE(mock_iptables.GetExecvNonRootCriterionMatchCount(id) == 1);
+  EXPECT_TRUE(mock_iptables.GetRunInMinijailCriterionMatchCount(id) == 1);
 }
 
 TEST_F(IpTablesTest, ApplyVpnSetupAdd_FailureInMasquerade) {
@@ -219,12 +219,12 @@
 
   MockIpTables mock_iptables;
   // Fail on calls adding MASQUERADE.
-  int id = mock_iptables.SetExecvNonRootFailCriterion(
+  int id = mock_iptables.SetRunInMinijailFailCriterion(
       std::vector<std::string>({"MASQUERADE", "-A"}), true, false);
   ASSERT_FALSE(
       mock_iptables.ApplyVpnSetup(usernames, interface, add));
   EXPECT_TRUE(mock_iptables.CheckCommandsUndone());
-  EXPECT_TRUE(mock_iptables.GetExecvNonRootCriterionMatchCount(id) == 1);
+  EXPECT_TRUE(mock_iptables.GetRunInMinijailCriterionMatchCount(id) == 1);
 }
 
 TEST_F(IpTablesTest, ApplyVpnSetupAdd_FailureInRuleForUserTraffic) {
@@ -233,11 +233,11 @@
   const bool add = true;
 
   MockIpTables mock_iptables;
-  int id = mock_iptables.SetExecvNonRootFailCriterion(
+  int id = mock_iptables.SetRunInMinijailFailCriterion(
       std::vector<std::string>({"rule", "add", "fwmark"}), true, false);
   ASSERT_FALSE(mock_iptables.ApplyVpnSetup(usernames, interface, add));
   EXPECT_TRUE(mock_iptables.CheckCommandsUndone());
-  EXPECT_TRUE(mock_iptables.GetExecvNonRootCriterionMatchCount(id) == 1);
+  EXPECT_TRUE(mock_iptables.GetRunInMinijailCriterionMatchCount(id) == 1);
 }
 
 TEST_F(IpTablesTest, ApplyVpnSetupRemove_Success) {
@@ -249,11 +249,11 @@
   // The fail criterions ensure that the ApplyVpnSetup function will delete
   // the rules that are added. In addition to this, test that when all of
   // these commands succeed, ApplyVpnSetup returns true.
-  mock_iptables.SetExecvNonRootFailCriterion(
+  mock_iptables.SetRunInMinijailFailCriterion(
       std::vector<std::string>({"MASQUERADE", "-D"}), true, true);
-  mock_iptables.SetExecvNonRootFailCriterion(
+  mock_iptables.SetRunInMinijailFailCriterion(
       std::vector<std::string>({"--uid-owner", "-D"}), true, true);
-  mock_iptables.SetExecvNonRootFailCriterion(
+  mock_iptables.SetRunInMinijailFailCriterion(
       std::vector<std::string>({"rule", "delete", "fwmark"}), true, true);
 
   ASSERT_TRUE(mock_iptables.ApplyVpnSetup(usernames, interface, remove));
@@ -266,9 +266,9 @@
 
   MockIpTables mock_iptables;
   // Make all removing commands fail.
-  mock_iptables.SetExecvNonRootFailCriterion(
+  mock_iptables.SetRunInMinijailFailCriterion(
       std::vector<std::string>({"delete"}), true, false);
-  mock_iptables.SetExecvNonRootFailCriterion(
+  mock_iptables.SetRunInMinijailFailCriterion(
       std::vector<std::string>({"-D"}), true, false);
   ASSERT_FALSE(mock_iptables.ApplyVpnSetup(usernames, interface, remove));
 }
diff --git a/mock_iptables.cc b/mock_iptables.cc
index 8fe817b..e9a43cf 100644
--- a/mock_iptables.cc
+++ b/mock_iptables.cc
@@ -22,14 +22,14 @@
   PlugAllHoles();
 }
 
-int MockIpTables::SetExecvNonRootFailCriterion(
+int MockIpTables::SetRunInMinijailFailCriterion(
     const std::vector<std::string>& keywords, bool repeat,
     bool omit_failure) {
   match_criteria_.push_back(Criterion{keywords, repeat, omit_failure, 0});
   return match_criteria_.size() - 1;
 }
 
-int MockIpTables::GetExecvNonRootCriterionMatchCount(int id) {
+int MockIpTables::GetRunInMinijailCriterionMatchCount(int id) {
   if (id < 0 || id >= (int) match_criteria_.size())
     return -1;
   return match_criteria_[id].match_count;
@@ -38,7 +38,7 @@
 bool MockIpTables::MatchAndUpdate(const std::vector<std::string>& argv) {
   for (auto& criterion : match_criteria_) {
     bool match = true;
-    // Empty criterion is a catch all -- fail on any ExecvNonRoot.
+    // Empty criterion is a catch all -- fail on any RunInMinijail.
     for (const std::string& keyword : criterion.keywords) {
       match = match &&
         (std::find(argv.begin(), argv.end(), keyword) != argv.end());
@@ -59,8 +59,7 @@
   return false;
 }
 
-int MockIpTables::ExecvNonRoot(
-    const std::vector<std::string>& argv, uint64_t capmask) {
+int MockIpTables::RunInMinijail(const std::vector<std::string>& argv) {
   if (MatchAndUpdate(argv))
     return 1;
   commands_.push_back(argv);
diff --git a/mock_iptables.h b/mock_iptables.h
index cb0b5df..ae88aa8 100644
--- a/mock_iptables.h
+++ b/mock_iptables.h
@@ -29,9 +29,9 @@
   MockIpTables() = default;
   ~MockIpTables() override;
 
-  int SetExecvNonRootFailCriterion(
+  int SetRunInMinijailFailCriterion(
       const std::vector<std::string>& keywords, bool repeat, bool omit_failure);
-  int GetExecvNonRootCriterionMatchCount(int id);
+  int GetRunInMinijailCriterionMatchCount(int id);
   bool CheckCommandsUndone();
 
   // Check if the current command matches a failure rule,
@@ -65,12 +65,12 @@
   // must have in order for it to fail.
   std::vector<Criterion> match_criteria_;
 
-  // A log of commands issued with ExecvNonRoot during the test.
+  // A log of commands issued with RunInMinijail during the test.
   std::vector<std::vector<std::string>> commands_;
 
   // The mock's implementation simply logs the command issued if
   // successful.
-  int ExecvNonRoot(const std::vector<std::string>& argv, uint64_t capmask) override;
+  int RunInMinijail(const std::vector<std::string>& argv) override;
 
   // Given an ip/iptables command, return the command that undoes it's effect.
   std::vector<std::string> GetInverseCommand(const std::vector<std::string>& command);