OSDN Git Service

su: import CyanogenMod's version for Superuser
authorChih-Wei Huang <cwhuang@linux.org.tw>
Wed, 8 Feb 2012 08:05:59 +0000 (16:05 +0800)
committerChih-Wei Huang <cwhuang@linux.org.tw>
Wed, 8 Feb 2012 08:05:59 +0000 (16:05 +0800)
From git://github.com/CyanogenMod/android_system_su

su/Android.mk
su/activity.cpp [new file with mode: 0644]
su/db.c [new file with mode: 0644]
su/su.c
su/su.h [new file with mode: 0644]

index 0593cc9..d458a05 100644 (file)
@@ -1,15 +1,23 @@
-LOCAL_PATH:= $(call my-dir)
+LOCAL_PATH := $(call my-dir)
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES:= su.c
+LOCAL_SRC_FILES := \
+    activity.cpp \
+    db.c \
+    su.c\
 
-LOCAL_MODULE:= su
+LOCAL_MODULE := su
 
-LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_C_INCLUDES += external/sqlite/dist
 
-LOCAL_STATIC_LIBRARIES := libc
+LOCAL_SHARED_LIBRARIES := \
+    liblog \
+    libsqlite \
+    libcutils \
+    libbinder \
+    libutils \
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
-LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE_TAGS := optional
 
 include $(BUILD_EXECUTABLE)
diff --git a/su/activity.cpp b/su/activity.cpp
new file mode 100644 (file)
index 0000000..f58c7d0
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+** Copyright 2010, Adam Shanks (@ChainsDD)
+** Copyright 2008, Zinx Verituse (@zinxv)
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <unistd.h>
+#include <android_runtime/ActivityManager.h>
+#include <binder/IBinder.h>
+#include <binder/IServiceManager.h>
+#include <binder/Parcel.h>
+#include <utils/String8.h>
+#include <assert.h>
+
+extern "C" {
+#include "su.h"
+#include <private/android_filesystem_config.h>
+#include <cutils/properties.h>
+}
+
+using namespace android;
+
+static const int BROADCAST_INTENT_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION + 13;
+
+static const int NULL_TYPE_ID = 0;
+
+static const int VAL_STRING = 0;
+static const int VAL_INTEGER = 1;
+
+static const int START_SUCCESS = 0;
+
+int send_intent(struct su_initiator *from, struct su_request *to, const char *socket_path, int allow, int type)
+{
+    char sdk_version_prop[PROPERTY_VALUE_MAX] = "0";
+    property_get("ro.build.version.sdk", sdk_version_prop, "0");
+
+    int sdk_version = atoi(sdk_version_prop);
+
+    sp<IServiceManager> sm = defaultServiceManager();
+    sp<IBinder> am = sm->checkService(String16("activity"));
+    assert(am != NULL);
+
+    Parcel data, reply;
+    data.writeInterfaceToken(String16("android.app.IActivityManager"));
+
+    data.writeStrongBinder(NULL); /* caller */
+
+    /* intent */
+    if (type == 0) {
+        data.writeString16(String16("com.noshufou.android.su.REQUEST")); /* action */
+    } else {
+        data.writeString16(String16("com.noshufou.android.su.RESULT")); /* action */
+    }
+    data.writeInt32(NULL_TYPE_ID); /* Uri - data */
+    data.writeString16(NULL, 0); /* type */
+    data.writeInt32(0); /* flags */
+    if (sdk_version >= 4) {
+        // added in donut
+        data.writeString16(NULL, 0); /* package name - DONUT ONLY, NOT IN CUPCAKE. */
+    }
+    data.writeString16(NULL, 0); /* ComponentName - package */
+    data.writeInt32(0); /* Categories - size */
+    if (sdk_version >= 7) {
+        // added in eclair rev 7
+        data.writeInt32(0);
+    }
+    if (sdk_version >= 15) {
+        // added in IceCreamSandwich 4.0.3
+        data.writeInt32(0); /* Selector */
+    }
+    { /* Extras */
+        data.writeInt32(-1); /* dummy, will hold length */
+        int oldPos = data.dataPosition();
+        data.writeInt32(0x4C444E42); // 'B' 'N' 'D' 'L'
+        { /* writeMapInternal */
+            data.writeInt32(7); /* writeMapInternal - size */
+
+            data.writeInt32(VAL_STRING);
+            data.writeString16(String16("caller_uid"));
+            data.writeInt32(VAL_INTEGER);
+            data.writeInt32(from->uid);
+
+            data.writeInt32(VAL_STRING);
+            data.writeString16(String16("caller_bin"));
+            data.writeInt32(VAL_STRING);
+            data.writeString16(String16(from->bin));
+
+            data.writeInt32(VAL_STRING);
+            data.writeString16(String16("desired_uid"));
+            data.writeInt32(VAL_INTEGER);
+            data.writeInt32(to->uid);
+
+            data.writeInt32(VAL_STRING);
+            data.writeString16(String16("desired_cmd"));
+            data.writeInt32(VAL_STRING);
+            data.writeString16(String16(to->command));
+
+            data.writeInt32(VAL_STRING);
+            data.writeString16(String16("socket"));
+            data.writeInt32(VAL_STRING);
+            data.writeString16(String16(socket_path));
+
+            data.writeInt32(VAL_STRING);
+            data.writeString16(String16("allow"));
+            data.writeInt32(VAL_INTEGER);
+            data.writeInt32(allow);
+
+            data.writeInt32(VAL_STRING);
+            data.writeString16(String16("version_code"));
+            data.writeInt32(VAL_INTEGER);
+            data.writeInt32(VERSION_CODE);
+        }
+        int newPos = data.dataPosition();
+        data.setDataPosition(oldPos - 4);
+        data.writeInt32(newPos - oldPos); /* length */
+        data.setDataPosition(newPos);
+    }
+
+    data.writeString16(NULL, 0); /* resolvedType */
+
+    data.writeInt32(-1); /* Not sure what this is for, but it prevents a warning */
+
+    data.writeStrongBinder(NULL); /* resultTo */
+    data.writeInt32(-1); /* resultCode */
+    data.writeString16(NULL, 0); /* resultData */
+
+    data.writeInt32(-1); /* resultExtras */
+
+    data.writeString16(String16("com.noshufou.android.su.RESPOND")); /* perm */
+    data.writeInt32(0); /* serialized */
+    data.writeInt32(0); /* sticky */
+    data.writeInt32(-1);
+
+    status_t ret = am->transact(BROADCAST_INTENT_TRANSACTION, data, &reply);
+    if (ret < START_SUCCESS) return -1;
+
+    return 0;
+}
diff --git a/su/db.c b/su/db.c
new file mode 100644 (file)
index 0000000..b3c0b07
--- /dev/null
+++ b/su/db.c
@@ -0,0 +1,87 @@
+/*
+** Copyright 2010, Adam Shanks (@ChainsDD)
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <cutils/log.h>
+
+#include <sqlite3.h>
+
+#include "su.h"
+
+// { int* pint; pint=(int*)data; ++(*pint); }
+
+sqlite3 *database_init()
+{
+    sqlite3 *db;
+    int rc;
+
+    rc = sqlite3_open_v2(REQUESTOR_DATABASE_PATH, &db, SQLITE_OPEN_READONLY, NULL);
+    if ( rc ) {
+        LOGE("Couldn't open database: %s", sqlite3_errmsg(db));
+        return NULL;
+    }
+
+    // Create an automatic busy handler in case the db is locked
+    sqlite3_busy_timeout(db, 1000);
+    return db;
+}
+
+int database_check(sqlite3 *db, struct su_initiator *from, struct su_request *to)
+{
+    char sql[4096];
+    char *zErrmsg;
+    char **result;
+    int nrow,ncol;
+    int allow = DB_INTERACTIVE;
+
+    sqlite3_snprintf(
+        sizeof(sql), sql,
+        "SELECT _id,name,allow FROM apps WHERE uid=%u AND exec_uid=%u AND exec_cmd='%q';",
+        (unsigned)from->uid, to->uid, to->command
+    );
+
+    if (strlen(sql) >= sizeof(sql)-1)
+        return DB_DENY;
+
+    int error = sqlite3_get_table(db, sql, &result, &nrow, &ncol, &zErrmsg);
+    if (error != SQLITE_OK) {
+        LOGE("Database check failed with error message %s", zErrmsg);
+        if (error == SQLITE_BUSY) {
+            LOGE("Specifically, the database is busy");
+        }
+        return DB_DENY;
+    }
+
+    if (nrow == 0 || ncol != 3)
+        goto out;
+
+    if (strcmp(result[0], "_id") == 0 && strcmp(result[2], "allow") == 0) {
+        if (strcmp(result[5], "1") == 0) {
+            allow = DB_ALLOW;
+        } else if (strcmp(result[5], "-1") == 0){
+            allow = DB_INTERACTIVE;
+        } else {
+            allow = DB_DENY;
+        }
+    }
+
+out:
+    sqlite3_free_table(result);
+
+    return allow;
+}
diff --git a/su/su.c b/su/su.c
index cf327b3..f4b35f8 100644 (file)
--- a/su/su.c
+++ b/su/su.c
 /*
+** Copyright 2010, Adam Shanks (@ChainsDD)
+** Copyright 2008, Zinx Verituse (@zinxv)
 **
-** Copyright 2008, The Android Open Source Project
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
 **
-** Licensed under the Apache License, Version 2.0 (the "License"); 
-** you may not use this file except in compliance with the License. 
-** You may obtain a copy of the License at 
+**     http://www.apache.org/licenses/LICENSE-2.0
 **
-**     http://www.apache.org/licenses/LICENSE-2.0 
-**
-** Unless required by applicable law or agreed to in writing, software 
-** distributed under the License is distributed on an "AS IS" BASIS, 
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
-** See the License for the specific language governing permissions and 
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
 ** limitations under the License.
 */
 
 #define LOG_TAG "su"
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
 #include <sys/types.h>
-#include <dirent.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <limits.h>
+#include <fcntl.h>
 #include <errno.h>
+#include <endian.h>
 
-#include <unistd.h>
-#include <time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
 
+#include <stdint.h>
 #include <pwd.h>
 
 #include <private/android_filesystem_config.h>
+#include <cutils/log.h>
 
-/*
- * SU can be given a specific command to exec. UID _must_ be
- * specified for this (ie argc => 3).
- *
- * Usage:
- * su 1000
- * su 1000 ls -l
- */
-int main(int argc, char **argv)
+#include <sqlite3.h>
+
+#include "su.h"
+
+//extern char* _mktemp(char*); /* mktemp doesn't link right.  Don't ask me why. */
+
+extern sqlite3 *database_init();
+extern int database_check(sqlite3*, struct su_initiator*, struct su_request*);
+
+/* Still lazt, will fix this */
+static char *socket_path = NULL;
+static sqlite3 *db = NULL;
+
+static struct su_initiator su_from = {
+    .pid = -1,
+    .uid = 0,
+    .bin = "",
+    .args = "",
+};
+
+static struct su_request su_to = {
+    .uid = AID_ROOT,
+    .command = DEFAULT_COMMAND,
+};
+
+static int from_init(struct su_initiator *from)
 {
-    struct passwd *pw;
-    int uid, gid, myuid;
+    char path[PATH_MAX], exe[PATH_MAX];
+    char args[4096], *argv0, *argv_rest;
+    int fd;
+    ssize_t len;
+    int i;
+    int err;
+
+    from->uid = getuid();
+    from->pid = getppid();
+
+    /* Get the command line */
+    snprintf(path, sizeof(path), "/proc/%u/cmdline", from->pid);
+    fd = open(path, O_RDONLY);
+    if (fd < 0) {
+        PLOGE("Opening command line");
+        return -1;
+    }
+    len = read(fd, args, sizeof(args));
+    err = errno;
+    close(fd);
+    if (len < 0 || len == sizeof(args)) {
+        PLOGEV("Reading command line", err);
+        return -1;
+    }
 
-    if(argc < 2) {
-        uid = gid = 0;
+    argv0 = args;
+    argv_rest = NULL;
+    for (i = 0; i < len; i++) {
+        if (args[i] == '\0') {
+            if (!argv_rest) {
+                argv_rest = &args[i+1];
+            } else {
+                args[i] = ' ';
+            }
+        }
+    }
+    args[len] = '\0';
+
+    if (argv_rest) {
+        strncpy(from->args, argv_rest, sizeof(from->args));
+        from->args[sizeof(from->args)-1] = '\0';
     } else {
-        pw = getpwnam(argv[1]);
+        from->args[0] = '\0';
+    }
 
-        if(pw == 0) {
-            uid = gid = atoi(argv[1]);
+    /* If this isn't app_process, use the real path instead of argv[0] */
+    snprintf(path, sizeof(path), "/proc/%u/exe", from->pid);
+    len = readlink(path, exe, sizeof(exe));
+    if (len < 0) {
+        PLOGE("Getting exe path");
+        return -1;
+    }
+    exe[len] = '\0';
+    if (strcmp(exe, "/system/bin/app_process")) {
+        argv0 = exe;
+    }
+
+    strncpy(from->bin, argv0, sizeof(from->bin));
+    from->bin[sizeof(from->bin)-1] = '\0';
+
+    return 0;
+}
+
+static void socket_cleanup(void)
+{
+    unlink(socket_path);
+}
+
+static void cleanup(void)
+{
+    socket_cleanup();
+    if (db) sqlite3_close(db);
+}
+
+static void cleanup_signal(int sig)
+{
+    socket_cleanup();
+    exit(sig);
+}
+
+static int socket_create_temp(void)
+{
+    static char buf[PATH_MAX];
+    int fd;
+
+    struct sockaddr_un sun;
+
+    fd = socket(AF_LOCAL, SOCK_STREAM, 0);
+    if (fd < 0) {
+        PLOGE("socket");
+        return -1;
+    }
+
+    for (;;) {
+        memset(&sun, 0, sizeof(sun));
+        sun.sun_family = AF_LOCAL;
+        strcpy(buf, SOCKET_PATH_TEMPLATE);
+        socket_path = mktemp(buf);
+        snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", socket_path);
+
+        if (bind(fd, (struct sockaddr*)&sun, sizeof(sun)) < 0) {
+            if (errno != EADDRINUSE) {
+                PLOGE("bind");
+                return -1;
+            }
         } else {
-            uid = pw->pw_uid;
-            gid = pw->pw_gid;
+            break;
+        }
+    }
+
+    if (listen(fd, 1) < 0) {
+        PLOGE("listen");
+        return -1;
+    }
+
+    return fd;
+}
+
+static int socket_accept(int serv_fd)
+{
+    struct timeval tv;
+    fd_set fds;
+    int fd;
+
+    /* Wait 20 seconds for a connection, then give up. */
+    tv.tv_sec = 20;
+    tv.tv_usec = 0;
+    FD_ZERO(&fds);
+    FD_SET(serv_fd, &fds);
+    if (select(serv_fd + 1, &fds, NULL, NULL, &tv) < 1) {
+        PLOGE("select");
+        return -1;
+    }
+
+    fd = accept(serv_fd, NULL, NULL);
+    if (fd < 0) {
+        PLOGE("accept");
+        return -1;
+    }
+
+    return fd;
+}
+
+static int socket_receive_result(int serv_fd, char *result, ssize_t result_len)
+{
+    ssize_t len;
+
+    for (;;) {
+        int fd = socket_accept(serv_fd);
+        if (fd < 0)
+            return -1;
+
+        len = read(fd, result, result_len-1);
+        if (len < 0) {
+            PLOGE("read(result)");
+            return -1;
+        }
+
+        if (len > 0) {
+            break;
         }
     }
-#if 0
-    /* Until we have something better, only root and the shell can use su. */
-    myuid = getuid();
-    if (myuid != AID_ROOT && myuid != AID_SHELL) {
-        fprintf(stderr,"su: uid %d not allowed to su\n", myuid);
-        return 1;
-    }
-#endif
-    if(setgid(gid) || setuid(uid)) {
-        fprintf(stderr,"su: permission denied\n");
-        return 1;
-    }
-
-    /* User specified command for exec. */
-    if (argc == 3 ) {
-        if (execlp(argv[2], argv[2], NULL) < 0) {
-            fprintf(stderr, "su: exec failed for %s Error:%s\n", argv[2],
-                    strerror(errno));
-            return -errno;
+
+    result[len] = '\0';
+
+    return 0;
+}
+
+static void usage(void)
+{
+    printf("Usage: su [options] [LOGIN]\n\n");
+    printf("Options:\n");
+    printf("  -c, --command COMMAND         pass COMMAND to the invoked shell\n");
+    printf("  -h, --help                    display this help message and exit\n");
+    printf("  -, -l, --login                make the shell a login shell\n");
+    // I'll look more into this to figure out what it's about,
+    // maybe implement it later
+//    printf("  -m, -p,\n");
+//    printf("  --preserve-environment        do not reset environment variables, and\n");
+//    printf("                                keep the same shell\n");
+    printf("  -s, --shell SHELL             use SHELL instead of the default in passwd\n");
+    printf("  -v, --version                 display version number and exit\n");
+    printf("  -V                            display version code and exit. this is\n");
+    printf("                                used almost exclusively by Superuser.apk\n");
+    exit(EXIT_SUCCESS);
+}
+
+static void deny(void)
+{
+    struct su_initiator *from = &su_from;
+    struct su_request *to = &su_to;
+
+    send_intent(&su_from, &su_to, "", 0, 1);
+    LOGW("request rejected (%u->%u %s)", from->uid, to->uid, to->command);
+    fprintf(stderr, "%s\n", strerror(EACCES));
+    exit(EXIT_FAILURE);
+}
+
+static void allow(char *shell, mode_t mask)
+{
+    struct su_initiator *from = &su_from;
+    struct su_request *to = &su_to;
+    char *exe = NULL;
+
+    umask(mask);
+    send_intent(&su_from, &su_to, "", 1, 1);
+
+    if (!strcmp(shell, "")) {
+        strcpy(shell , "/system/bin/sh");
+    }
+    exe = strrchr (shell, '/') + 1;
+    setresgid(to->uid, to->uid, to->uid);
+    setresuid(to->uid, to->uid, to->uid);
+    LOGD("%u %s executing %u %s using shell %s : %s", from->uid, from->bin,
+            to->uid, to->command, shell, exe);
+    if (strcmp(to->command, DEFAULT_COMMAND)) {
+        execl(shell, exe, "-c", to->command, (char*)NULL);
+    } else {
+        execl(shell, exe, "-", (char*)NULL);
+    }
+    PLOGE("exec");
+    exit(EXIT_SUCCESS);
+}
+
+int main(int argc, char *argv[])
+{
+    struct stat st;
+    static int socket_serv_fd = -1;
+    char buf[64], shell[PATH_MAX], *result;
+    int i, dballow;
+    mode_t orig_umask;
+
+    for (i = 1; i < argc; i++) {
+        if (!strcmp(argv[i], "-c") || !strcmp(argv[i], "--command")) {
+            if (++i < argc) {
+                su_to.command = argv[i];
+            } else {
+                usage();
+            }
+        } else if (!strcmp(argv[i], "-s") || !strcmp(argv[i], "--shell")) {
+            if (++i < argc) {
+                strncpy(shell, argv[i], sizeof(shell));
+                shell[sizeof(shell) - 1] = 0;
+            } else {
+                usage();
+            }
+        } else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) {
+            printf("%s\n", VERSION);
+            exit(EXIT_SUCCESS);
+        } else if (!strcmp(argv[i], "-V")) {
+            printf("%d\n", VERSION_CODE);
+            exit(EXIT_SUCCESS);
+        } else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
+            usage();
+        } else if (!strcmp(argv[i], "-") || !strcmp(argv[i], "-l") ||
+                !strcmp(argv[i], "--login")) {
+            ++i;
+            break;
+        } else {
+            break;
         }
-    } else if (argc > 3) {
-        /* Copy the rest of the args from main. */
-        char *exec_args[argc - 1];
-        memset(exec_args, 0, sizeof(exec_args));
-        memcpy(exec_args, &argv[2], sizeof(exec_args));
-        if (execvp(argv[2], exec_args) < 0) {
-            fprintf(stderr, "su: exec failed for %s Error:%s\n", argv[2],
-                    strerror(errno));
-            return -errno;
+    }
+    if (i < argc-1) {
+        usage();
+    }
+    if (i == argc-1) {
+        struct passwd *pw;
+        pw = getpwnam(argv[i]);
+        if (!pw) {
+            su_to.uid = atoi(argv[i]);
+        } else {
+            su_to.uid = pw->pw_uid;
         }
     }
 
-    /* Default exec shell. */
-    execlp("/system/bin/sh", "sh", NULL);
+    if (from_init(&su_from) < 0) {
+        deny();
+    }
+
+    orig_umask = umask(027);
+
+    if (su_from.uid == AID_ROOT || su_from.uid == AID_SHELL)
+        allow(shell, orig_umask);
+
+    if (stat(REQUESTOR_DATA_PATH, &st) < 0) {
+        PLOGE("stat");
+        deny();
+    }
+
+    if (st.st_gid != st.st_uid)
+    {
+        LOGE("Bad uid/gid %d/%d for Superuser Requestor application",
+                (int)st.st_uid, (int)st.st_gid);
+        deny();
+    }
+
+    if (mkdir(REQUESTOR_CACHE_PATH, 0770) >= 0) {
+        chown(REQUESTOR_CACHE_PATH, st.st_uid, st.st_gid);
+    }
+
+    setgroups(0, NULL);
+    setegid(st.st_gid);
+    seteuid(st.st_uid);
+
+    LOGV("sudb - Opening database");
+    db = database_init();
+    if (!db) {
+        LOGE("sudb - Could not open database, prompt user");
+        // if the database could not be opened, we can assume we need to
+        // prompt the user
+        dballow = DB_INTERACTIVE;
+    } else {
+        //LOGD("sudb - Database opened");
+        dballow = database_check(db, &su_from, &su_to);
+        // Close the database, we're done with it. If it stays open,
+        // it will cause problems
+        sqlite3_close(db);
+        db = NULL;
+        LOGV("sudb - Database closed");
+    }
+
+    switch (dballow) {
+        case DB_DENY: deny();
+        case DB_ALLOW: allow(shell, orig_umask);
+        case DB_INTERACTIVE: break;
+        default: deny();
+    }
+
+    socket_serv_fd = socket_create_temp();
+    if (socket_serv_fd < 0) {
+        deny();
+    }
+
+    signal(SIGHUP, cleanup_signal);
+    signal(SIGPIPE, cleanup_signal);
+    signal(SIGTERM, cleanup_signal);
+    signal(SIGABRT, cleanup_signal);
+    atexit(cleanup);
+
+    if (send_intent(&su_from, &su_to, socket_path, -1, 0) < 0) {
+        deny();
+    }
+
+    if (socket_receive_result(socket_serv_fd, buf, sizeof(buf)) < 0) {
+        deny();
+    }
+
+    close(socket_serv_fd);
+    socket_cleanup();
+
+    result = buf;
+
+    if (!strcmp(result, "DENY")) {
+        deny();
+    } else if (!strcmp(result, "ALLOW")) {
+        allow(shell, orig_umask);
+    } else {
+        LOGE("unknown response from Superuser Requestor: %s", result);
+        deny();
+    }
 
-    fprintf(stderr, "su: exec failed\n");
-    return 1;
+    deny();
+    return -1;
 }
diff --git a/su/su.h b/su/su.h
new file mode 100644 (file)
index 0000000..e5474d3
--- /dev/null
+++ b/su/su.h
@@ -0,0 +1,68 @@
+/*
+** Copyright 2010, Adam Shanks (@ChainsDD)
+** Copyright 2008, Zinx Verituse (@zinxv)
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef SU_h
+#define SU_h 1
+
+#define REQUESTOR_DATA_PATH "/data/data/com.noshufou.android.su"
+#define REQUESTOR_CACHE_PATH REQUESTOR_DATA_PATH "/cache"
+
+#define REQUESTOR_DATABASES_PATH REQUESTOR_DATA_PATH "/databases"
+#define REQUESTOR_DATABASE_PATH REQUESTOR_DATABASES_PATH "/permissions.sqlite"
+
+#define DEFAULT_COMMAND "/system/bin/sh"
+
+#define SOCKET_PATH_TEMPLATE REQUESTOR_CACHE_PATH "/.socketXXXXXX"
+
+#define VERSION "3.0.3.2"
+#define VERSION_CODE 15
+
+#define DATABASE_VERSION 6
+
+struct su_initiator {
+    pid_t pid;
+    unsigned uid;
+    char bin[PATH_MAX];
+    char args[4096];
+};
+
+struct su_request {
+    unsigned uid;
+    char *command;
+};
+
+enum {
+    DB_INTERACTIVE,
+    DB_DENY,
+    DB_ALLOW
+};
+
+extern int send_intent(struct su_initiator *from, struct su_request *to, const char *socket_path, int allow, int type);
+
+#if 0
+#undef LOGE
+#define LOGE(fmt,args...) fprintf(stderr, fmt , ## args )
+#undef LOGD
+#define LOGD(fmt,args...) fprintf(stderr, fmt , ## args )
+#undef LOGW
+#define LOGW(fmt,args...) fprintf(stderr, fmt , ## args )
+#endif
+
+#define PLOGE(fmt,args...) LOGE(fmt " failed with %d: %s" , ## args , errno, strerror(errno))
+#define PLOGEV(fmt,err,args...) LOGE(fmt " failed with %d: %s" , ## args , err, strerror(err))
+
+#endif