netd: fix argument interpretation bug

While working around the logwrap() issue, it was replaced with system()
which could lead to various commands getting misinterpreted.

We now use a system() equivalent that doesn't use "sh -c".

Bug:5758556
Change-Id: I2599b526ac34bcfca18d05261286d902d547efda
diff --git a/BandwidthController.cpp b/BandwidthController.cpp
index f0a856e..be3cb28 100644
--- a/BandwidthController.cpp
+++ b/BandwidthController.cpp
@@ -42,6 +42,7 @@
 #include <cutils/properties.h>
 
 extern "C" int logwrap(int argc, const char **argv, int background);
+extern "C" int system_nosh(const char *command);
 
 #include "BandwidthController.h"
 
@@ -185,7 +186,7 @@
     fullCmd.insert(0, iptVer == IptIpV4 ? IPTABLES_PATH : IP6TABLES_PATH);
 
     if (!useLogwrapCall) {
-        res = system(fullCmd.c_str());
+        res = system_nosh(fullCmd.c_str());
     } else {
         if (StrncpyAndCheck(buffer, fullCmd.c_str(), sizeof(buffer))) {
             LOGE("iptables command too long");
diff --git a/NatController.cpp b/NatController.cpp
index 5f6a46a..ed1b095 100644
--- a/NatController.cpp
+++ b/NatController.cpp
@@ -30,7 +30,7 @@
 #include "NatController.h"
 #include "SecondaryTableController.h"
 
-extern "C" int logwrap(int argc, const char **argv, int background);
+extern "C" int system_nosh(const char *command);
 
 static char IPTABLES_PATH[] = "/system/bin/iptables";
 static char IP_PATH[] = "/system/bin/ip";
@@ -55,7 +55,7 @@
     }
 
     asprintf(&buffer, "%s %s", path, cmd);
-    res = system(buffer);
+    res = system_nosh(buffer);
     free(buffer);
     return res;
 }
diff --git a/SecondaryTableController.cpp b/SecondaryTableController.cpp
index 1be1c79..287bba5 100644
--- a/SecondaryTableController.cpp
+++ b/SecondaryTableController.cpp
@@ -31,6 +31,8 @@
 #include <cutils/log.h>
 #include <cutils/properties.h>
 
+extern "C" int system_nosh(const char *command);
+
 #include "ResponseCode.h"
 #include "SecondaryTableController.h"
 
@@ -132,7 +134,7 @@
         free(cmd);
         return -1;
     }
-    ret = system(cmd);
+    ret = system_nosh(cmd);
     free(cmd);
     return ret;
 }
diff --git a/ThrottleController.cpp b/ThrottleController.cpp
index c7dfad8..1ae31b8 100644
--- a/ThrottleController.cpp
+++ b/ThrottleController.cpp
@@ -36,7 +36,7 @@
 
 static char TC_PATH[] = "/system/bin/tc";
 
-extern "C" int logwrap(int argc, const char **argv, int background);
+extern "C" int system_nosh(const char *command);
 extern "C" int ifc_init(void);
 extern "C" int ifc_up(const char *name);
 extern "C" int ifc_down(const char *name);
@@ -53,7 +53,7 @@
     }
 
     asprintf(&buffer, "%s %s", TC_PATH, cmd);
-    res = system(buffer);
+    res = system_nosh(buffer);
     free(buffer);
     return res;
 }
diff --git a/logwrapper.c b/logwrapper.c
index 739f141..d9913f2 100644
--- a/logwrapper.c
+++ b/logwrapper.c
@@ -175,3 +175,66 @@
 
     return 0;
 }
+
+/*
+ * The following is based off of bionic/libc/unistd/system.c with
+ *  modifications to avoid calling /system/bin/sh -c
+ */
+extern char **environ;
+int system_nosh(const char *command)
+{
+    pid_t pid;
+    sig_t intsave, quitsave;
+    sigset_t mask, omask;
+    int pstat;
+    char buffer[255];
+    char *argp[32];
+    char *next = buffer;
+    char *tmp;
+    int i = 0;
+
+    if (!command)           /* just checking... */
+        return(1);
+
+    /*
+     * The command to argp splitting is from code that was
+     * reverted in Change: 11b4e9b2
+     */
+    if (strnlen(buffer, sizeof(buffer) - 1) == sizeof(buffer) - 1) {
+        LOGE("command line too long while processing: %s", command);
+        errno = E2BIG;
+        return -1;
+    }
+    strcpy(buffer, command); // Command len is already checked.
+    while ((tmp = strsep(&next, " "))) {
+        argp[i++] = tmp;
+        if (i == 32) {
+            LOGE("argument overflow while processing: %s", command);
+            errno = E2BIG;
+            return -1;
+        }
+    }
+    argp[i] = NULL;
+
+
+    sigemptyset(&mask);
+    sigaddset(&mask, SIGCHLD);
+    sigprocmask(SIG_BLOCK, &mask, &omask);
+    switch (pid = vfork()) {
+    case -1:                        /* error */
+        sigprocmask(SIG_SETMASK, &omask, NULL);
+        return(-1);
+    case 0:                                 /* child */
+        sigprocmask(SIG_SETMASK, &omask, NULL);
+        execve(argp[0], argp, environ);
+        _exit(127);
+    }
+
+    intsave = (sig_t)  bsd_signal(SIGINT, SIG_IGN);
+    quitsave = (sig_t) bsd_signal(SIGQUIT, SIG_IGN);
+    pid = waitpid(pid, (int *)&pstat, 0);
+    sigprocmask(SIG_SETMASK, &omask, NULL);
+    (void)bsd_signal(SIGINT, intsave);
+    (void)bsd_signal(SIGQUIT, quitsave);
+    return (pid == -1 ? -1 : pstat);
+}