apmanager: run daemon inside minijail

Run apmanager inside a minijail with limited privileges and system calls
through seccomp filter.

BUG=chromium:442186
TEST=Verify AP services with client connectiviy on arm (peach_pit),
     x86 (x86-alex), and amd64 (stumpy) platforms.
CQ-DEPEND=CL:236097

Change-Id: I10b2b0c6943cad134028894505d54e2ca4993a26
Reviewed-on: https://chromium-review.googlesource.com/236098
Tested-by: Zeping Qiu <zqiu@chromium.org>
Reviewed-by: Jorge Lucangeli Obes <jorgelo@chromium.org>
Commit-Queue: Zeping Qiu <zqiu@chromium.org>
Trybot-Ready: Zeping Qiu <zqiu@chromium.org>
diff --git a/dhcp_server.cc b/dhcp_server.cc
index c60bd32..0a895a9 100644
--- a/dhcp_server.cc
+++ b/dhcp_server.cc
@@ -10,6 +10,8 @@
 #include <base/files/file_util.h>
 #include <base/strings/stringprintf.h>
 
+#include "apmanager/daemon.h"
+
 using std::string;
 
 namespace apmanager {
@@ -105,11 +107,9 @@
   // terminated. Configure dnsmasq to run in "foreground" so no extra process
   // will be spawned.
   config += "keep-in-foreground\n";
-  // TODO(zqiu): by default, dnsmasq process will be started under "nobody".
-  // Set the user to "root" for now, since both the daemon and hostapd are
-  // running under "root". Update the user once we switch the daemon and
-  // hostapd over to "apmanager" user.
-  config += "user=root\n";
+  // Explicitly set the user to apmanager. If not set, dnsmasq will default to
+  // run as "nobody".
+  base::StringAppendF(&config, "user=%s\n", Daemon::kAPManagerUserName);
   base::StringAppendF(
       &config, "dhcp-range=%s,%s\n", address_low.c_str(), address_high.c_str());
   base::StringAppendF(&config, "interface=%s\n", interface_name_.c_str());
diff --git a/dhcp_server_unittest.cc b/dhcp_server_unittest.cc
index ba13631..4bc133b 100644
--- a/dhcp_server_unittest.cc
+++ b/dhcp_server_unittest.cc
@@ -30,7 +30,7 @@
       "bind-interfaces\n"
       "log-dhcp\n"
       "keep-in-foreground\n"
-      "user=root\n"
+      "user=apmanager\n"
       "dhcp-range=192.168.1.1,192.168.1.128\n"
       "interface=test_interface\n"
       "dhcp-leasefile=/tmp/dhcpd-1.leases\n";
diff --git a/init/apmanager-seccomp-amd64.policy b/init/apmanager-seccomp-amd64.policy
new file mode 100644
index 0000000..2ebd303
--- /dev/null
+++ b/init/apmanager-seccomp-amd64.policy
@@ -0,0 +1,84 @@
+# Tested on stumpy board
+getegid: 1
+geteuid: 1
+getgid: 1
+getpid: 1
+getresgid: 1
+getresuid: 1
+gettid: 1
+getuid: 1
+setgroups: 1
+setresgid: 1
+setresuid: 1
+
+clock_getres: 1
+clock_gettime: 1
+nanosleep: 1
+alarm: 1
+
+connect: 1
+bind: 1
+getsockname: 1
+pipe: 1
+recvfrom: 1
+recvmsg: 1
+sendmsg: 1
+select: 1
+sendto: 1
+setsockopt: 1
+socket: 1
+socketpair: 1
+
+close: 1
+creat: 1
+ioctl: 1
+open: 1
+prctl: 1
+read: 1
+write: 1
+arch_prctl: 1
+capget: 1
+
+brk: 1
+dup2: 1
+clone: 1
+fork: 1
+mmap: 1
+munmap: 1
+
+fcntl: 1
+fstat: 1
+fsync: 1
+ftruncate: 1
+lseek: 1
+stat: 1
+
+futex: 1
+
+exit: 1
+exit_group: 1
+kill: 1
+rt_sigaction: 1
+rt_sigprocmask: 1
+rt_sigreturn: 1
+signalfd4: 1
+tkill: 1
+
+epoll_create: 1
+epoll_ctl: 1
+epoll_wait: 1
+poll: 1
+wait4: 1
+
+chdir: 1
+readlink: 1
+umask: 1
+
+set_robust_list: 1
+set_tid_address: 1
+
+execve: 1
+mprotect: 1
+access: 1
+getrlimit: 1
+unlink: 1
\ No newline at end of file
diff --git a/init/apmanager-seccomp-arm.policy b/init/apmanager-seccomp-arm.policy
new file mode 100644
index 0000000..19777c1
--- /dev/null
+++ b/init/apmanager-seccomp-arm.policy
@@ -0,0 +1,78 @@
+# Tested on peach_pit board
+socket: 1
+setsockopt: 1
+bind: 1
+clock_gettime: 1
+_newselect: 1
+recvfrom: 1
+epoll_ctl: 1
+gettid: 1
+write: 1
+epoll_wait: 1
+read: 1
+open: 1
+futex: 1
+brk: 1
+fstat64: 1
+mmap2: 1
+close: 1
+munmap: 1
+sendmsg: 1
+poll: 1
+recvmsg: 1
+fork: 1
+clone: 1
+ioctl: 1
+rt_sigprocmask: 1
+rt_sigaction: 1
+rt_sigreturn: 1
+sigreturn: 1
+connect: 1
+sendto: 1
+creat: 1
+access: 1
+set_robust_list: 1
+set_tid_address: 1
+wait4: 1
+exit: 1
+exit_group: 1
+epoll_create: 1
+
+fcntl64: 1
+prctl: 1
+capget: 1
+capset: 1
+dup2: 1
+
+getpid: 1
+getuid32: 1
+setgroups32: 1
+setresgid32: 1
+setresuid32: 1
+setresgid32: 1
+setresuid32: 1
+setitimer: 1
+mprotect: 1
+stat64: 1
+send: 1
+_llseek: 1
+signalfd4: 1
+execve: 1
+
+getsockname: 1
+readlink: 1
+gettimeofday: 1
+
+restart_syscall: 1
+uname: 1
+ARM_set_tls: 1
+ugetrlimit: 1
+kill: 1
+nanosleep: 1
+
+umask: 1
+pipe: 1
+chdir: 1
+ftruncate64: 1
+fsync: 1
+unlink: 1
\ No newline at end of file
diff --git a/init/apmanager-seccomp-mips.policy b/init/apmanager-seccomp-mips.policy
new file mode 100644
index 0000000..b683c04
--- /dev/null
+++ b/init/apmanager-seccomp-mips.policy
@@ -0,0 +1 @@
+# Stub for now until we have mips hw.
diff --git a/init/apmanager-seccomp-x86.policy b/init/apmanager-seccomp-x86.policy
new file mode 100644
index 0000000..122a897
--- /dev/null
+++ b/init/apmanager-seccomp-x86.policy
@@ -0,0 +1,65 @@
+# Tested on x86-alex board
+clock_gettime: 1
+_newselect: 1
+epoll_ctl: 1
+gettid: 1
+write: 1
+gettimeofday: 1
+epoll_wait: 1
+read: 1
+open: 1
+brk: 1
+fstat64: 1
+mmap2: 1
+close: 1
+munmap: 1
+poll: 1
+rt_sigprocmask: 1
+clone: 1
+signalfd4: 1
+ioctl: 1
+set_robust_list: 1
+fork: 1
+stat64: 1
+execve: 1
+kill: 1
+fcntl64: 1
+access: 1
+mprotect: 1
+waitpid: 1
+set_thread_area: 1
+set_tid_address: 1
+futex: 1
+rt_sigaction: 1
+ugetrlimit: 1
+uname: 1
+readlink: 1
+nanosleep: 1
+restart_syscall: 1
+exit_group: 1
+alarm: 1
+sigreturn: 1
+umask: 1
+_llseek: 1
+capget: 1
+pipe: 1
+chdir: 1
+getuid32: 1
+dup2: 1
+getpid: 1
+stat64: 1
+ftruncate64: 1
+fsync: 1
+prctl: 1
+capset: 1
+getresgid32: 1
+getresuid32: 1
+geteuid32: 1
+getgid32: 1
+getegid32: 1
+setresgid32: 1
+setresuid32: 1
+tgkill: 1
+time: 1
+epoll_create: 1
+socketcall: 1
\ No newline at end of file
diff --git a/init/apmanager.conf b/init/apmanager.conf
index 06dd54f..ad06be0 100644
--- a/init/apmanager.conf
+++ b/init/apmanager.conf
@@ -7,6 +7,7 @@
 
 start on starting system-services
 stop on stopping system-services
+expect fork
 
 env APMANAGER_LOG_LEVEL=0
 
diff --git a/main.cc b/main.cc
index b6c860a..d5fdb55 100644
--- a/main.cc
+++ b/main.cc
@@ -36,6 +36,7 @@
 
 const char kLoggerCommand[] = "/usr/bin/logger";
 const char kLoggerUser[] = "syslog";
+const char kSeccompFilePath[] = "/usr/share/policy/apmanager-seccomp.policy";
 
 }  // namespace
 
@@ -83,11 +84,15 @@
 }
 
 void DropPrivileges(chromeos::Minijail* minijail) {
-  // TODO(zqiu): Need to figure out the right set of privileges to allow
-  // hostapd to configure interfaces.
   struct minijail* jail = minijail->New();
   minijail->DropRoot(jail, apmanager::Daemon::kAPManagerUserName,
                      apmanager::Daemon::kAPManagerGroupName);
+  // Permissions needed for the daemon and its child processes for managing
+  // network interfaces and binding to network sockets.
+  minijail->UseCapabilities(jail, CAP_TO_MASK(CAP_NET_ADMIN) |
+                                  CAP_TO_MASK(CAP_NET_RAW) |
+                                  CAP_TO_MASK(CAP_NET_BIND_SERVICE));
+  minijail->UseSeccompFilter(jail, kSeccompFilePath);
   minijail_enter(jail);
   minijail->Destroy(jail);
 }
@@ -98,11 +103,9 @@
 
   LOG(INFO) << __func__ << ": Dropping privileges";
 
-  // TODO(zqiu): temporary, until we figure out the exact privileges required
-  // to start required daemons (hostapd and dnsmasq).
   // Now that the daemon has all the resources it needs to run, we can drop
   // privileges further.
-  // DropPrivileges(minijail);
+  DropPrivileges(minijail);
 }
 
 int main(int argc, char* argv[]) {