Use hostapd without HIDL for virtual access points

Change the init file to use the new hostapd binary that does not have
HIDL control interface support. This way we can have both HIDL and
non-HIDL hostapd binaries at the same time. The non-HIDL hostapd is used
to provide a virtual acccesss point and the HIDL enabled hostapd is used
to provide WiFi hotspot functionality. Modify the manifest to make sure
that the hostapd HIDL for hotspots is included.

BUG: 74401469
Test: run cts -m CtsNetTestCases
Change-Id: I6ad2af0415c53e50050d7b91c1beb7dd45f4bbdc
diff --git a/init.ranchu.rc b/init.ranchu.rc
index 2193c58..9c23a81 100644
--- a/init.ranchu.rc
+++ b/init.ranchu.rc
@@ -56,9 +56,9 @@
     group root
     disabled
 
-service emu_hostapd /vendor/bin/execns router /vendor/bin/hw/hostapd /vendor/etc/simulated_hostapd.conf
+service emu_hostapd /vendor/bin/execns -u wifi -g wifi router /vendor/bin/hostapd_nohidl /vendor/etc/simulated_hostapd.conf
     user root
-    group root wifi
+    group root wifi net_raw net_admin
     disabled
 
 service dhcpserver /vendor/bin/execns router /vendor/bin/dhcpserver --range 192.168.232.2,192.168.239.254 --gateway 192.168.232.1 --netmask 255.255.248.0 --exclude-interface eth0
diff --git a/manifest.xml b/manifest.xml
index f534912..7e723c4 100644
--- a/manifest.xml
+++ b/manifest.xml
@@ -192,6 +192,15 @@
         </interface>
     </hal>
     <hal format="hidl">
+        <name>android.hardware.wifi.hostapd</name>
+        <transport>hwbinder</transport>
+        <version>1.0</version>
+        <interface>
+            <name>IHostapd</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl">
         <name>android.hardware.wifi.supplicant</name>
         <transport>hwbinder</transport>
         <version>1.0</version>
diff --git a/vendor.mk b/vendor.mk
index 52002b5..411b51e 100644
--- a/vendor.mk
+++ b/vendor.mk
@@ -112,6 +112,7 @@
 	dhcpserver \
 	execns \
 	hostapd \
+	hostapd_nohidl \
 	ipv6proxy \
 	wpa_supplicant \
 
diff --git a/wifi/execns/execns.cpp b/wifi/execns/execns.cpp
index e5506da..770d249 100644
--- a/wifi/execns/execns.cpp
+++ b/wifi/execns/execns.cpp
@@ -19,6 +19,8 @@
 
 #include <errno.h>
 #include <fcntl.h>
+#include <grp.h>
+#include <pwd.h>
 #include <sched.h>
 #include <stdio.h>
 #include <string.h>
@@ -77,7 +79,7 @@
 };
 
 static void printUsage(const char* program) {
-    LOGE("%s <namespace> <program> [options...]", program);
+    LOGE("%s [-u user] [-g group] <namespace> <program> [options...]", program);
 }
 
 static bool isNumericString(const char* str) {
@@ -147,6 +149,34 @@
     return true;
 }
 
+static bool changeUser(const char* user) {
+    struct passwd* pwd = ::getpwnam(user);
+    if (pwd == nullptr) {
+        LOGE("Could not find user '%s'", user);
+        return false;
+    }
+
+    if (::setuid(pwd->pw_uid) != 0) {
+        LOGE("Cannot switch to user '%s': %s", user, strerror(errno));
+        return false;
+    }
+    return true;
+}
+
+static bool changeGroup(const char* group) {
+    struct group* grp = ::getgrnam(group);
+    if (grp == nullptr) {
+        LOGE("Could not find group '%s'", group);
+        return false;
+    }
+
+    if (::setgid(grp->gr_gid) != 0) {
+        LOGE("Cannot switch to group '%s': %s", group, strerror(errno));
+        return false;
+    }
+    return true;
+}
+
 // Append a formatted string to the end of |buffer|. The total size in |buffer|
 // is |size|, including any existing string data. The string to append is
 // specified by |fmt| and any additional arguments required by the format
@@ -220,17 +250,61 @@
  */
 int main(int argc, char* argv[]) {
     isTerminal = isatty(STDOUT_FILENO) != 0;
-    if (argc < 3) {
+
+    // Parse parameters
+    const char* user = nullptr;
+    const char* group = nullptr;
+    int nsArg = -1;
+    int execArg = -1;
+    for (int i = 1; i < argc; ++i) {
+        if (::strcmp(argv[i], "-u") == 0) {
+            if (user || i + 1 >= argc) {
+                LOGE("Missing argument to option -u");
+                return 1;
+            }
+            user = argv[++i];
+        } else if (::strcmp(argv[i], "-g") == 0) {
+            if (group || i + 1 >= argc) {
+                LOGE("Missing argument to option -g");
+                return 1;
+            }
+            group = argv[++i];
+        } else {
+            // Break on the first non-option and treat it as the namespace name
+            nsArg = i;
+            if (i + 1 < argc) {
+                execArg = i + 1;
+            }
+            break;
+        }
+    }
+
+    if (nsArg < 0 || execArg < 0) {
+        // Missing namespace and/or exec arguments
         printUsage(argv[0]);
         return 1;
     }
 
     // First set the new network namespace for this process
-    if (!setNetworkNamespace(argv[1])) {
+    if (!setNetworkNamespace(argv[nsArg])) {
+        return 1;
+    }
+
+    // Changing namespace is the privileged operation, so now we can drop
+    // privileges by changing user and/or group if the user requested it. Note
+    // that it's important to change group first because it must be done as a
+    // privileged user. Otherwise an attacker might be able to restore group
+    // privileges by using the group ID that is saved by setgid when running
+    // as a non-privileged user.
+    if (group && !changeGroup(group)) {
+        return 1;
+    }
+
+    if (user && !changeUser(user)) {
         return 1;
     }
 
     // Now run the command with all the remaining parameters
-    return execCommand(argc - 2, &argv[2]);
+    return execCommand(argc - execArg, &argv[execArg]);
 }