From f79e166005b7a1f56f44ed63941951830cbb3abc Mon Sep 17 00:00:00 2001 From: Adam Shanks Date: Tue, 27 Sep 2011 13:15:51 +0100 Subject: [PATCH] Merge master-dev into master --- Android.mk | 8 +- activity.cpp | 38 +++++++- db.c | 89 +++++++++++++++++ su.c | 266 ++++++++++++++++++--------------------------------- su.h | 41 ++++++-- 5 files changed, 257 insertions(+), 185 deletions(-) create mode 100644 db.c diff --git a/Android.mk b/Android.mk index a17aaf5..d19d0e0 100644 --- a/Android.mk +++ b/Android.mk @@ -2,17 +2,19 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := su -LOCAL_SRC_FILES := su.c activity.cpp +LOCAL_SRC_FILES := su.c db.c activity.cpp LOCAL_C_INCLUDES += external/sqlite/dist + LOCAL_SHARED_LIBRARIES := \ liblog \ libsqlite \ libcutils \ libbinder \ - libutils + libutils \ + LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) -LOCAL_MODULE_TAGS := eng,debug +LOCAL_MODULE_TAGS := debug,eng include $(BUILD_EXECUTABLE) diff --git a/activity.cpp b/activity.cpp index 4b21abd..c361e41 100644 --- a/activity.cpp +++ b/activity.cpp @@ -1,3 +1,20 @@ +/* +** 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 #include #include @@ -23,7 +40,7 @@ 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 type) +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"); @@ -43,7 +60,7 @@ int send_intent(struct su_initiator *from, struct su_request *to, const char *so if (type == 0) { data.writeString16(String16("com.noshufou.android.su.REQUEST")); /* action */ } else { - data.writeString16(String16("com.noshufou.android.su.NOTIFICATION")); /* action */ + data.writeString16(String16("com.noshufou.android.su.RESULT")); /* action */ } data.writeInt32(NULL_TYPE_ID); /* Uri - data */ data.writeString16(NULL, 0); /* type */ @@ -63,13 +80,18 @@ int send_intent(struct su_initiator *from, struct su_request *to, const char *so int oldPos = data.dataPosition(); data.writeInt32(0x4C444E42); // 'B' 'N' 'D' 'L' { /* writeMapInternal */ - data.writeInt32(4); /* writeMapInternal - size */ + 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); @@ -84,6 +106,16 @@ int send_intent(struct su_initiator *from, struct su_request *to, const char *so 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); diff --git a/db.c b/db.c new file mode 100644 index 0000000..c338ee9 --- /dev/null +++ b/db.c @@ -0,0 +1,89 @@ +/* +** 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 +#include +#include +#include + +#include + +#include "su.h" + +// { int* pint; pint=(int*)data; ++(*pint); } + +sqlite3 *database_init() +{ + sqlite3 *db; + int version, rc, databaseStatus = 0; + char *zErrMsg = 0; + + 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; + struct timeval tv; + + 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) + return DB_INTERACTIVE; + + 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; + } + return allow; + } + + sqlite3_free_table(result); + + return DB_INTERACTIVE; +} diff --git a/su.c b/su.c index 5eb6542..be44319 100644 --- a/su.c +++ b/su.c @@ -1,3 +1,20 @@ +/* +** 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. +*/ + #define LOG_TAG "su" #include @@ -24,18 +41,15 @@ #include +#include "su.h" + //extern char* _mktemp(char*); /* mktemp doesn't link right. Don't ask me why. */ -#include "su.h" +extern sqlite3 *database_init(); +extern int database_check(sqlite3*, struct su_initiator*, struct su_request*); -/* Ewwww. I'm way too lazy. */ -static const char socket_path_template[PATH_MAX] = REQUESTOR_CACHE_PATH "/.socketXXXXXX"; -static char socket_path_buf[PATH_MAX]; +/* Still lazt, will fix this */ static char *socket_path = NULL; -static int socket_serv_fd = -1; -static char shell[PATH_MAX]; -static unsigned req_uid = 0; - static sqlite3 *db = NULL; static struct su_initiator su_from = { @@ -130,8 +144,9 @@ static void cleanup_signal(int sig) exit(sig); } -static int socket_create_temp() +static int socket_create_temp(unsigned req_uid) { + static char buf[PATH_MAX]; int fd, err; struct sockaddr_un sun; @@ -145,8 +160,8 @@ static int socket_create_temp() for (;;) { memset(&sun, 0, sizeof(sun)); sun.sun_family = AF_LOCAL; - strcpy(socket_path_buf, socket_path_template); - socket_path = mktemp(socket_path_buf); + 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) { @@ -207,6 +222,7 @@ static int socket_accept(int serv_fd) static int socket_receive_result(int serv_fd, char *result, ssize_t result_len) { ssize_t len; + char buf[64]; for (;;) { int fd = socket_accept(serv_fd); @@ -219,8 +235,9 @@ static int socket_receive_result(int serv_fd, char *result, ssize_t result_len) return -1; } - if (len > 0) + if (len > 0) { break; + } } result[len] = '\0'; @@ -228,136 +245,23 @@ static int socket_receive_result(int serv_fd, char *result, ssize_t result_len) return 0; } -static sqlite3 *database_init() +static void usage(void) { - sqlite3 *db; - - if (mkdir(REQUESTOR_DATABASES_PATH, 0771) >= 0) { - chown(REQUESTOR_DATABASES_PATH, req_uid, req_uid); - } - - if (sqlite3_open(REQUESTOR_DATABASE_PATH, &db) != SQLITE_OK) { - LOGE("Couldn't open database"); - return NULL; - } - - if (sqlite3_exec(db, - "PRAGMA journal_mode = delete;", - NULL, - NULL, - NULL - ) != SQLITE_OK) { - LOGE("Could not set journal mode"); - sqlite3_close(db); - return NULL; - } - - chmod(REQUESTOR_DATABASE_PATH, 0660); - chown(REQUESTOR_DATABASE_PATH, req_uid, req_uid); - - if (sqlite3_exec(db, - "CREATE TABLE IF NOT EXISTS apps (_id INTEGER, uid INTEGER, package TEXT, name TEXT, exec_uid INTEGER, exec_cmd TEXT, allow INTEGER, PRIMARY KEY (_id), UNIQUE (uid,exec_uid,exec_cmd));", - NULL, - NULL, - NULL - ) != SQLITE_OK) { - LOGE("Couldn't create apps table"); - sqlite3_close(db); - return NULL; - } - - if (sqlite3_exec(db, - "CREATE TABLE IF NOT EXISTS logs (_id INTEGER, app_id INTEGER, date INTEGER, type INTEGER, PRIMARY KEY (_id));", - NULL, - NULL, - NULL - ) != SQLITE_OK) { - LOGE("Couldn't create logs table"); - sqlite3_close(db); - return NULL; - } - - return db; -} - -enum { - DB_INTERACTIVE, - DB_DENY, - DB_ALLOW -}; - -static 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; - struct timeval tv; - - sqlite3_snprintf( - sizeof(sql), sql, - "SELECT _id,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; - - if (sqlite3_get_table(db, sql, &result, &nrow, &ncol, &zErrmsg) != SQLITE_OK) { - LOGE("Database check failed with error message %s", zErrmsg); - return DB_DENY; - } - - if (nrow == 0 || ncol != 2) - return DB_INTERACTIVE; - - if (strcmp(result[0], "_id") == 0 && strcmp(result[1], "allow") == 0) { - if (strcmp(result[3], "1") == 0) { - allow = DB_ALLOW; - } else { - allow = DB_DENY; - } - gettimeofday(&tv, NULL); - sqlite3_snprintf( - sizeof(sql), sql, - "INSERT OR IGNORE INTO logs (app_id,date,type) VALUES (%s,(%ld*1000)+(%ld/1000),%s);", - result[2], tv.tv_sec, tv.tv_usec, result[3] - ); - sqlite3_exec(db, sql, NULL, NULL, NULL); - return allow; - } - - sqlite3_free_table(result); - - return DB_INTERACTIVE; -} - -static int check_notifications(sqlite3 *db) -{ - char sql[4096]; - char *zErrmsg; - char **result; - int nrow,ncol; - int notifications; - - sqlite3_snprintf( - sizeof(sql), sql, - "SELECT value FROM prefs WHERE key='notifications';" - ); - - if (sqlite3_get_table(db, sql, &result, &nrow, &ncol, &zErrmsg) != SQLITE_OK) { - LOGE("Notifications check failed with error message %s", zErrmsg); - return 0; - } - - if (nrow == 0 || ncol != 1) - return 0; - - if (strcmp(result[0], "value") == 0 && strcmp(result[1], "1") == 0) - return 1; - - return 0; + 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) @@ -365,20 +269,20 @@ 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(-1); + exit(EXIT_FAILURE); } -static void allow(int notifications) +static void allow(char *shell) { struct su_initiator *from = &su_from; struct su_request *to = &su_to; char *exe = NULL; - if (notifications) - send_intent(&su_from, &su_to, "", 1); - + send_intent(&su_from, &su_to, "", 1, 1); + if (!strcmp(shell, "")) { strcpy(shell , "/system/bin/sh"); } @@ -386,40 +290,48 @@ static void allow(int notifications) setgroups(0, NULL); 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); + 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(-1); + exit(EXIT_SUCCESS); } int main(int argc, char *argv[]) { struct stat st; - char buf[64], *result; - int i; - int dballow; + static int socket_serv_fd = -1; + char buf[64], shell[PATH_MAX], *result; + int i, dballow; + unsigned req_uid; for (i = 1; i < argc; i++) { - if (!strcmp(argv[i], "-c")) { + if (!strcmp(argv[i], "-c") || !strcmp(argv[i], "--command")) { if (++i < argc) { su_to.command = argv[i]; } else { - deny(); + usage(); } - } else if (!strcmp(argv[i], "-s")) { + } else if (!strcmp(argv[i], "-s") || !strcmp(argv[i], "--shell")) { if (++i < argc) { strcpy(shell, argv[i]); } else { - deny(); + usage(); } - } else if (!strcmp(argv[i], "-v")) { + } else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) { printf("%s\n", VERSION); - exit(-1); - } else if (!strcmp(argv[i], "-")) { + 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 { @@ -427,7 +339,7 @@ int main(int argc, char *argv[]) } } if (i < argc-1) { - deny(); + usage(); } if (i == argc-1) { struct passwd *pw; @@ -439,10 +351,12 @@ int main(int argc, char *argv[]) } } - from_init(&su_from); + if (from_init(&su_from) < 0) { + deny(); + } if (su_from.uid == AID_ROOT) - allow(0); + allow(shell); if (stat(REQUESTOR_DATA_PATH, &st) < 0) { PLOGE("stat"); @@ -451,35 +365,41 @@ int main(int argc, char *argv[]) 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); + LOGE("Bad uid/gid %d/%d for Superuser Requestor application", + (int)st.st_uid, (int)st.st_gid); deny(); } req_uid = st.st_uid; - if (from_init(&su_from) < 0) { - deny(); - } - if (mkdir(REQUESTOR_CACHE_PATH, 0771) >= 0) { chown(REQUESTOR_CACHE_PATH, req_uid, req_uid); } + LOGE("sudb - Opening database"); db = database_init(); if (!db) { - deny(); + 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 { + LOGE("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); + LOGE("sudb - Database closed"); } - dballow = database_check(db, &su_from, &su_to); - int notifications = check_notifications(db); switch (dballow) { case DB_DENY: deny(); - case DB_ALLOW: allow(notifications); + case DB_ALLOW: allow(shell); case DB_INTERACTIVE: break; default: deny(); } - socket_serv_fd = socket_create_temp(); + socket_serv_fd = socket_create_temp(req_uid); if (socket_serv_fd < 0) { deny(); } @@ -490,7 +410,7 @@ int main(int argc, char *argv[]) signal(SIGABRT, cleanup_signal); atexit(cleanup); - if (send_intent(&su_from, &su_to, socket_path, 0) < 0) { + if (send_intent(&su_from, &su_to, socket_path, -1, 0) < 0) { deny(); } @@ -506,7 +426,7 @@ int main(int argc, char *argv[]) if (!strcmp(result, "DENY")) { deny(); } else if (!strcmp(result, "ALLOW")) { - allow(notifications); + allow(shell); } else { LOGE("unknown response from Superuser Requestor: %s", result); deny(); diff --git a/su.h b/su.h index 2d4daf5..36866ac 100644 --- a/su.h +++ b/su.h @@ -1,18 +1,41 @@ +/* +** 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_PACKAGE "com.noshufou.android.su" -#define REQUESTOR_CLASS "SuRequest" - -#define REQUESTOR_DATA_PATH "/data/data/" REQUESTOR_PACKAGE +#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 APPS_TABLE_DEFINITION "CREATE TABLE IF NOT EXISTS apps (_id INTEGER, uid INTEGER, package TEXT, name TEXT, exec_uid INTEGER, exec_cmd TEXT, allow INTEGER, PRIMARY KEY (_id), UNIQUE (uid,exec_uid,exec_cmd));" +#define LOGS_TABLE_DEFINITION "CREATE TABLE IF NOT EXISTS logs (_id INTEGER, uid INTEGER, name INTEGER, app_id INTEGER, date INTEGER, type INTEGER, PRIMARY KEY (_id));" +#define PREFS_TABLE_DEFINITION "CREATE TABLE IF NOT EXISTS prefs (_id INTEGER, key TEXT, value TEXT, PRIMARY KEY(_id));" + #define DEFAULT_COMMAND "/system/bin/sh" -#define VERSION "2.3.2-efgh" +#define SOCKET_PATH_TEMPLATE REQUESTOR_CACHE_PATH "/.socketXXXXXX" + +#define VERSION "3.0" +#define VERSION_CODE 11 + +#define DATABASE_VERSION 6 struct su_initiator { pid_t pid; @@ -26,7 +49,13 @@ struct su_request { char *command; }; -extern int send_intent(struct su_initiator *from, struct su_request *to, const char *socket_path, int type); +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