Launch dhcpcd using Minijail.

dhcpcd runs as root and listens on the network. Launch it using Minijail
so that we can run it as a regular user, mitigating the risk of an eventual
compromise.

Add a mock Minijail wrapper for unittesting.

BUG=chromium-os:28336
TEST=dhcp_config_unittest
TEST=network_netperf2
TEST=Manual connection to ethernet, GoogleGuest, Google-A.
CQ-DEPEND=I243e02c82f70c6a3469ca712e539ec9fb6e3e4d4

Change-Id: I14c4e843eba478ed39b10fa4fcb0e25eb3186c1a
Reviewed-on: https://gerrit.chromium.org/gerrit/20414
Commit-Ready: Jorge Lucangeli Obes <jorgelo@chromium.org>
Reviewed-by: Jorge Lucangeli Obes <jorgelo@chromium.org>
Tested-by: Jorge Lucangeli Obes <jorgelo@chromium.org>
diff --git a/dhcp_config.cc b/dhcp_config.cc
index e1f610d..ab58d1a 100644
--- a/dhcp_config.cc
+++ b/dhcp_config.cc
@@ -17,6 +17,7 @@
 #include "shill/event_dispatcher.h"
 #include "shill/glib.h"
 #include "shill/ip_address.h"
+#include "shill/minijail.h"
 #include "shill/proxy_factory.h"
 #include "shill/scope_logger.h"
 
@@ -42,6 +43,7 @@
 const char DHCPConfig::kDHCPCDPathFormatPID[] =
     "var/run/dhcpcd/dhcpcd-%s.pid";
 const int DHCPConfig::kDHCPTimeoutSeconds = 30;
+const char DHCPConfig::kDHCPCDUser[] = "dhcp";
 const int DHCPConfig::kMinMTU = 576;
 const char DHCPConfig::kReasonBound[] = "BOUND";
 const char DHCPConfig::kReasonFail[] = "FAIL";
@@ -72,7 +74,8 @@
       root_("/"),
       weak_ptr_factory_(this),
       dispatcher_(dispatcher),
-      glib_(glib) {
+      glib_(glib),
+      minijail_(Minijail::GetInstance()) {
   SLOG(DHCP, 2) << __func__ << ": " << device_name;
   if (lease_file_suffix_.empty()) {
     lease_file_suffix_ = device_name;
@@ -175,18 +178,18 @@
   }
   args.push_back(const_cast<char *>(interface_arg.c_str()));
   args.push_back(NULL);
-  char *envp[1] = { NULL };
+
+  struct minijail *jail = minijail_->New();
+  minijail_->DropRoot(jail, kDHCPCDUser);
+  minijail_->UseCapabilities(jail,
+                             CAP_TO_MASK(CAP_NET_BIND_SERVICE) |
+                             CAP_TO_MASK(CAP_NET_BROADCAST) |
+                             CAP_TO_MASK(CAP_NET_ADMIN) |
+                             CAP_TO_MASK(CAP_NET_RAW));
 
   CHECK(!pid_);
-  if (!glib_->SpawnAsync(NULL,
-                         args.data(),
-                         envp,
-                         G_SPAWN_DO_NOT_REAP_CHILD,
-                         NULL,
-                         NULL,
-                         &pid_,
-                         NULL)) {
-    LOG(ERROR) << "Unable to spawn " << kDHCPCDPath;
+  if (!minijail_->RunAndDestroy(jail, args, &pid_)) {
+    LOG(ERROR) << "Unable to spawn " << kDHCPCDPath << " in a jail.";
     return false;
   }
   LOG(INFO) << "Spawned " << kDHCPCDPath << " with pid: " << pid_;
@@ -320,10 +323,7 @@
     glib_->SourceRemove(child_watch_tag_);
     child_watch_tag_ = 0;
   }
-  if (pid_) {
-    glib_->SpawnClosePID(pid_);
-    pid_ = 0;
-  }
+  pid_ = 0;
   proxy_.reset();
   if (lease_file_suffix_ == device_name()) {
     // If the lease file suffix was left as default, clean it up at exit.