From 4b31539ef26e9d8c5604ca60f44222d751b0564b Mon Sep 17 00:00:00 2001 From: Adam Shanks Date: Wed, 15 Jun 2011 11:32:51 +0100 Subject: [PATCH 01/71] Do not use _mktemp --- su.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/su.c b/su.c index c498f94..5eb6542 100644 --- a/su.c +++ b/su.c @@ -24,7 +24,7 @@ #include -extern char* _mktemp(char*); /* mktemp doesn't link right. Don't ask me why. */ +//extern char* _mktemp(char*); /* mktemp doesn't link right. Don't ask me why. */ #include "su.h" @@ -146,7 +146,7 @@ static int socket_create_temp() memset(&sun, 0, sizeof(sun)); sun.sun_family = AF_LOCAL; strcpy(socket_path_buf, socket_path_template); - socket_path = _mktemp(socket_path_buf); + socket_path = mktemp(socket_path_buf); snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", socket_path); if (bind(fd, (struct sockaddr*)&sun, sizeof(sun)) < 0) { From 9b15727135225aaca8699f9caef4222f70637403 Mon Sep 17 00:00:00 2001 From: Adam Shanks Date: Wed, 15 Jun 2011 11:33:40 +0100 Subject: [PATCH 02/71] Fix Android.mk --- Android.mk | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Android.mk b/Android.mk index b23b09b..a17aaf5 100644 --- a/Android.mk +++ b/Android.mk @@ -6,8 +6,13 @@ LOCAL_SRC_FILES := su.c activity.cpp LOCAL_C_INCLUDES += external/sqlite/dist -LOCAL_SHARED_LIBRARIES := liblog libsqlite libandroid_runtime - +LOCAL_SHARED_LIBRARIES := \ + liblog \ + libsqlite \ + libcutils \ + libbinder \ + libutils LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) +LOCAL_MODULE_TAGS := eng,debug include $(BUILD_EXECUTABLE) From 91cd65c3d91967ddd2d095891735e0848b64cb71 Mon Sep 17 00:00:00 2001 From: Adam Shanks Date: Wed, 15 Jun 2011 11:34:34 +0100 Subject: [PATCH 03/71] Bump version --- su.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/su.h b/su.h index 2052eab..2d4daf5 100644 --- a/su.h +++ b/su.h @@ -12,7 +12,7 @@ #define DEFAULT_COMMAND "/system/bin/sh" -#define VERSION "2.3.1-efg" +#define VERSION "2.3.2-efgh" struct su_initiator { pid_t pid; From f79e166005b7a1f56f44ed63941951830cbb3abc Mon Sep 17 00:00:00 2001 From: Adam Shanks Date: Tue, 27 Sep 2011 13:15:51 +0100 Subject: [PATCH 04/71] 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 From 186b86252e165dbf259e4a7800ea87a41e44740a Mon Sep 17 00:00:00 2001 From: Koushik Dutta Date: Thu, 6 Oct 2011 11:48:45 +0800 Subject: [PATCH 05/71] shell uid always gets root. Change-Id: Ic1d1a0eccf7fae96243c19bf8abc207b5b991200 --- su.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/su.c b/su.c index be44319..7be5530 100644 --- a/su.c +++ b/su.c @@ -355,7 +355,7 @@ int main(int argc, char *argv[]) deny(); } - if (su_from.uid == AID_ROOT) + if (su_from.uid == AID_ROOT || su_from.uid == AID_SHELL) allow(shell); if (stat(REQUESTOR_DATA_PATH, &st) < 0) { From 00f1bb547ef219b690a440920c7bc29be2a1d348 Mon Sep 17 00:00:00 2001 From: HomerSp Date: Tue, 18 Oct 2011 19:14:06 +0200 Subject: [PATCH 06/71] Possible fix for db crash --- su.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) mode change 100644 => 100755 su.c diff --git a/su.c b/su.c old mode 100644 new mode 100755 index 7be5530..add619a --- a/su.c +++ b/su.c @@ -376,6 +376,12 @@ int main(int argc, char *argv[]) chown(REQUESTOR_CACHE_PATH, req_uid, req_uid); } + + setgroups(0, NULL); + setegid(st.st_gid); + seteuid(st.st_uid); + + LOGE("sudb - Opening database"); db = database_init(); if (!db) { @@ -398,7 +404,7 @@ int main(int argc, char *argv[]) case DB_INTERACTIVE: break; default: deny(); } - + socket_serv_fd = socket_create_temp(req_uid); if (socket_serv_fd < 0) { deny(); From 49427f6fa3d1c89666013ec0f6f7e547ad150b7f Mon Sep 17 00:00:00 2001 From: "Gleb O. Raiko" Date: Sat, 22 Oct 2011 17:33:02 +0400 Subject: [PATCH 07/71] Fix potential buffer overflow when getting a shell from the command line --- su.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/su.c b/su.c index add619a..7ece49d 100755 --- a/su.c +++ b/su.c @@ -318,7 +318,8 @@ int main(int argc, char *argv[]) } } else if (!strcmp(argv[i], "-s") || !strcmp(argv[i], "--shell")) { if (++i < argc) { - strcpy(shell, argv[i]); + strncpy(shell, argv[i], sizeof(shell)); + shell[sizeof(shell) - 1] = 0; } else { usage(); } From a0cc2b931f54f063b9669fec49c18837bb7a7eba Mon Sep 17 00:00:00 2001 From: Adam Shanks Date: Sat, 22 Oct 2011 14:52:11 +0100 Subject: [PATCH 08/71] Fix file mode: --- su.c | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 su.c diff --git a/su.c b/su.c old mode 100755 new mode 100644 From 67394c6bbc56cc41523c1cc8712f77883a6408c3 Mon Sep 17 00:00:00 2001 From: Adam Shanks Date: Sat, 22 Oct 2011 16:10:46 +0100 Subject: [PATCH 09/71] bump verison --- su.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/su.h b/su.h index 36866ac..bd5b06a 100644 --- a/su.h +++ b/su.h @@ -32,8 +32,8 @@ #define SOCKET_PATH_TEMPLATE REQUESTOR_CACHE_PATH "/.socketXXXXXX" -#define VERSION "3.0" -#define VERSION_CODE 11 +#define VERSION "3.0.1" +#define VERSION_CODE 12 #define DATABASE_VERSION 6 From a1855463d26d78a47cd00355e33ba9a4ec473f9a Mon Sep 17 00:00:00 2001 From: "Gleb O. Raiko" Date: Sat, 22 Oct 2011 19:19:05 +0400 Subject: [PATCH 10/71] Fix db double close --- su.c | 1 + 1 file changed, 1 insertion(+) diff --git a/su.c b/su.c index 7ece49d..8a18b46 100755 --- a/su.c +++ b/su.c @@ -396,6 +396,7 @@ int main(int argc, char *argv[]) // Close the database, we're done with it. If it stays open, // it will cause problems sqlite3_close(db); + db = NULL; LOGE("sudb - Database closed"); } From 263b17e0eed2d98afc64b660c31bed668045bc86 Mon Sep 17 00:00:00 2001 From: Adam Shanks Date: Sat, 22 Oct 2011 19:42:30 +0100 Subject: [PATCH 11/71] Clean up whitespace --- su.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/su.c b/su.c index 8a18b46..bb87a19 100644 --- a/su.c +++ b/su.c @@ -377,11 +377,9 @@ int main(int argc, char *argv[]) chown(REQUESTOR_CACHE_PATH, req_uid, req_uid); } - setgroups(0, NULL); setegid(st.st_gid); seteuid(st.st_uid); - LOGE("sudb - Opening database"); db = database_init(); From 233b1d0aa95e8c43fd7f02b91ac047e2e32a8aa5 Mon Sep 17 00:00:00 2001 From: Adam Shanks Date: Wed, 14 Dec 2011 20:13:39 +0000 Subject: [PATCH 12/71] bump version --- su.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/su.h b/su.h index bd5b06a..6bf7259 100644 --- a/su.h +++ b/su.h @@ -32,8 +32,8 @@ #define SOCKET_PATH_TEMPLATE REQUESTOR_CACHE_PATH "/.socketXXXXXX" -#define VERSION "3.0.1" -#define VERSION_CODE 12 +#define VERSION "3.0.3" +#define VERSION_CODE 14 #define DATABASE_VERSION 6 From a9a303f79cf7ef2027d09578bc607e7eafb4b37a Mon Sep 17 00:00:00 2001 From: Adam Shanks Date: Wed, 14 Dec 2011 20:13:39 +0000 Subject: [PATCH 13/71] bump version --- su.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/su.h b/su.h index bd5b06a..6bf7259 100644 --- a/su.h +++ b/su.h @@ -32,8 +32,8 @@ #define SOCKET_PATH_TEMPLATE REQUESTOR_CACHE_PATH "/.socketXXXXXX" -#define VERSION "3.0.1" -#define VERSION_CODE 12 +#define VERSION "3.0.3" +#define VERSION_CODE 14 #define DATABASE_VERSION 6 From 0123ee3421ee5ac95b0af1e3af4d22ef0c46914b Mon Sep 17 00:00:00 2001 From: "Gleb O. Raiko" Date: Sun, 23 Oct 2011 17:18:52 +0400 Subject: [PATCH 14/71] Report error from read rather than successful completion of close --- su.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/su.c b/su.c index bb87a19..4094af0 100644 --- a/su.c +++ b/su.c @@ -71,6 +71,7 @@ static int from_init(struct su_initiator *from) int fd; ssize_t len; int i; + int err; from->uid = getuid(); from->pid = getppid(); @@ -83,9 +84,10 @@ static int from_init(struct su_initiator *from) return -1; } len = read(fd, args, sizeof(args)); + err = errno; close(fd); if (len < 0 || len == sizeof(args)) { - PLOGE("Reading command line"); + PLOGEV("Reading command line", err); return -1; } From 2e17b69cef62fead9770e1c6ff06d5f789e3fca9 Mon Sep 17 00:00:00 2001 From: "Gleb O. Raiko" Date: Sun, 23 Oct 2011 17:31:27 +0400 Subject: [PATCH 15/71] Remove setgroups in allow() supplementary groups have been dropped already at this time (introduced by commit 00f1bb547ef219b690a440920c7bc29be2a1d348) --- su.c | 1 - 1 file changed, 1 deletion(-) diff --git a/su.c b/su.c index 4094af0..8e7f488 100644 --- a/su.c +++ b/su.c @@ -289,7 +289,6 @@ static void allow(char *shell) strcpy(shell , "/system/bin/sh"); } exe = strrchr (shell, '/') + 1; - 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, From d5984c9ba86ea7a09389ebe6da81dad91fb7edca Mon Sep 17 00:00:00 2001 From: "Gleb O. Raiko" Date: Sun, 30 Oct 2011 00:32:54 +0400 Subject: [PATCH 16/71] Always free result table if sqlite3_get_table succeeds --- db.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/db.c b/db.c index c338ee9..ca56f92 100644 --- a/db.c +++ b/db.c @@ -48,7 +48,7 @@ int database_check(sqlite3 *db, struct su_initiator *from, struct su_request *to char *zErrmsg; char **result; int nrow,ncol; - int allow; + int allow = DB_INTERACTIVE; struct timeval tv; sqlite3_snprintf( @@ -70,7 +70,7 @@ int database_check(sqlite3 *db, struct su_initiator *from, struct su_request *to } if (nrow == 0 || ncol != 3) - return DB_INTERACTIVE; + goto out; if (strcmp(result[0], "_id") == 0 && strcmp(result[2], "allow") == 0) { if (strcmp(result[5], "1") == 0) { @@ -80,10 +80,10 @@ int database_check(sqlite3 *db, struct su_initiator *from, struct su_request *to } else { allow = DB_DENY; } - return allow; } +out: sqlite3_free_table(result); - return DB_INTERACTIVE; + return allow; } From 4c6ea930caf9ac3609f3c19f11d768fadedd3f70 Mon Sep 17 00:00:00 2001 From: "Gleb O. Raiko" Date: Sun, 30 Oct 2011 14:43:23 +0400 Subject: [PATCH 17/71] Clean up sources --- db.c | 4 +--- su.c | 1 - su.h | 4 ---- 3 files changed, 1 insertion(+), 8 deletions(-) diff --git a/db.c b/db.c index ca56f92..ce4ae72 100644 --- a/db.c +++ b/db.c @@ -28,8 +28,7 @@ sqlite3 *database_init() { sqlite3 *db; - int version, rc, databaseStatus = 0; - char *zErrMsg = 0; + int rc; rc = sqlite3_open_v2(REQUESTOR_DATABASE_PATH, &db, SQLITE_OPEN_READONLY, NULL); if ( rc ) { @@ -49,7 +48,6 @@ int database_check(sqlite3 *db, struct su_initiator *from, struct su_request *to char **result; int nrow,ncol; int allow = DB_INTERACTIVE; - struct timeval tv; sqlite3_snprintf( sizeof(sql), sql, diff --git a/su.c b/su.c index 8e7f488..5d57480 100644 --- a/su.c +++ b/su.c @@ -224,7 +224,6 @@ 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); diff --git a/su.h b/su.h index 6bf7259..b8086be 100644 --- a/su.h +++ b/su.h @@ -24,10 +24,6 @@ #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 SOCKET_PATH_TEMPLATE REQUESTOR_CACHE_PATH "/.socketXXXXXX" From 7d97cfea964c68c9c51a77750f6c7aa86fab66c3 Mon Sep 17 00:00:00 2001 From: "Gleb O. Raiko" Date: Sat, 3 Dec 2011 14:44:51 +0400 Subject: [PATCH 18/71] Fix potential security flaw in creation of sockets/directories Previous code created a file object with two steps: the creation itself with default umask/mode and setting necessary permissions then. This approach is known to lead to a race condition when malicious process can open an object before permissions is set. The patch sets creation mask (mask) to 027, thus denying any access from others. Also, the patch removes all dead code which is not needed after changes mentioned. --- su.c | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/su.c b/su.c index 5d57480..9bbd439 100644 --- a/su.c +++ b/su.c @@ -146,10 +146,10 @@ static void cleanup_signal(int sig) exit(sig); } -static int socket_create_temp(unsigned req_uid) +static int socket_create_temp(void) { static char buf[PATH_MAX]; - int fd, err; + int fd; struct sockaddr_un sun; @@ -176,18 +176,6 @@ static int socket_create_temp(unsigned req_uid) } } - if (chmod(sun.sun_path, 0600) < 0) { - PLOGE("chmod(socket)"); - unlink(sun.sun_path); - return -1; - } - - if (chown(sun.sun_path, req_uid, req_uid) < 0) { - PLOGE("chown(socket)"); - unlink(sun.sun_path); - return -1; - } - if (listen(fd, 1) < 0) { PLOGE("listen"); return -1; @@ -276,12 +264,13 @@ static void deny(void) exit(EXIT_FAILURE); } -static void allow(char *shell) +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, "")) { @@ -308,6 +297,7 @@ int main(int argc, char *argv[]) char buf[64], shell[PATH_MAX], *result; int i, dballow; unsigned req_uid; + mode_t orig_umask; for (i = 1; i < argc; i++) { if (!strcmp(argv[i], "-c") || !strcmp(argv[i], "--command")) { @@ -356,8 +346,10 @@ int main(int argc, char *argv[]) deny(); } + orig_umask = umask(027); + if (su_from.uid == AID_ROOT || su_from.uid == AID_SHELL) - allow(shell); + allow(shell, orig_umask); if (stat(REQUESTOR_DATA_PATH, &st) < 0) { PLOGE("stat"); @@ -373,7 +365,7 @@ int main(int argc, char *argv[]) req_uid = st.st_uid; - if (mkdir(REQUESTOR_CACHE_PATH, 0771) >= 0) { + if (mkdir(REQUESTOR_CACHE_PATH, 0770) >= 0) { chown(REQUESTOR_CACHE_PATH, req_uid, req_uid); } @@ -400,12 +392,12 @@ int main(int argc, char *argv[]) switch (dballow) { case DB_DENY: deny(); - case DB_ALLOW: allow(shell); + case DB_ALLOW: allow(shell, orig_umask); case DB_INTERACTIVE: break; default: deny(); } - socket_serv_fd = socket_create_temp(req_uid); + socket_serv_fd = socket_create_temp(); if (socket_serv_fd < 0) { deny(); } @@ -432,7 +424,7 @@ int main(int argc, char *argv[]) if (!strcmp(result, "DENY")) { deny(); } else if (!strcmp(result, "ALLOW")) { - allow(shell); + allow(shell, orig_umask); } else { LOGE("unknown response from Superuser Requestor: %s", result); deny(); From 1fe89fff7b3b4539b2437238ee3032b19cf7e16e Mon Sep 17 00:00:00 2001 From: "Gleb O. Raiko" Date: Sun, 23 Oct 2011 17:30:45 +0400 Subject: [PATCH 19/71] Remove all occurences of req_uid --- su.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/su.c b/su.c index 9bbd439..9bf46bc 100644 --- a/su.c +++ b/su.c @@ -296,7 +296,6 @@ int main(int argc, char *argv[]) static int socket_serv_fd = -1; char buf[64], shell[PATH_MAX], *result; int i, dballow; - unsigned req_uid; mode_t orig_umask; for (i = 1; i < argc; i++) { @@ -363,10 +362,8 @@ int main(int argc, char *argv[]) deny(); } - req_uid = st.st_uid; - if (mkdir(REQUESTOR_CACHE_PATH, 0770) >= 0) { - chown(REQUESTOR_CACHE_PATH, req_uid, req_uid); + chown(REQUESTOR_CACHE_PATH, st.st_uid, st.st_gid); } setgroups(0, NULL); From 9934e8700af90dac6cf28ca5bc098c1d3384ab2f Mon Sep 17 00:00:00 2001 From: Adam Shanks Date: Sun, 18 Dec 2011 13:21:55 +0000 Subject: [PATCH 20/71] Fix for Android 4.0.3 from shakalaca on github --- activity.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/activity.cpp b/activity.cpp index c361e41..c92e316 100644 --- a/activity.cpp +++ b/activity.cpp @@ -75,6 +75,10 @@ int send_intent(struct su_initiator *from, struct su_request *to, const char *so // 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(); From 070fc98bf5e82526d9a558a76109b6a900c99795 Mon Sep 17 00:00:00 2001 From: Adam Shanks Date: Sun, 18 Dec 2011 13:22:41 +0000 Subject: [PATCH 21/71] bump version --- su.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/su.h b/su.h index b8086be..01e7666 100644 --- a/su.h +++ b/su.h @@ -28,8 +28,8 @@ #define SOCKET_PATH_TEMPLATE REQUESTOR_CACHE_PATH "/.socketXXXXXX" -#define VERSION "3.0.3" -#define VERSION_CODE 14 +#define VERSION "3.0.3.2" +#define VERSION_CODE 15 #define DATABASE_VERSION 6 From e0329cee5f18171ac7a4e89e42a0bb763a1f30a8 Mon Sep 17 00:00:00 2001 From: git-core Date: Fri, 16 Dec 2011 22:32:17 +0400 Subject: [PATCH 22/71] Inherit the environment of the caller process for a new process image --- su.c | 35 +++++++++++++++++++++++++++++++++-- su.h | 4 ++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/su.c b/su.c index 9bf46bc..bb8e5b2 100644 --- a/su.c +++ b/su.c @@ -57,6 +57,8 @@ static struct su_initiator su_from = { .uid = 0, .bin = "", .args = "", + .env = "", + .envp = { NULL, }, }; static struct su_request su_to = { @@ -72,6 +74,7 @@ static int from_init(struct su_initiator *from) ssize_t len; int i; int err; + size_t j; from->uid = getuid(); from->pid = getppid(); @@ -126,6 +129,30 @@ static int from_init(struct su_initiator *from) strncpy(from->bin, argv0, sizeof(from->bin)); from->bin[sizeof(from->bin)-1] = '\0'; + /* Get the environment of the calling process */ + snprintf(path, sizeof(path), "/proc/%u/environ", from->pid); + fd = open(path, O_RDONLY); + if (fd < 0) { + PLOGE("Opening environment"); + goto out; + } + len = read(fd, from->env, sizeof(from->env)); + close(fd); + if (len < 0 || len == sizeof(from->env)) { + PLOGE("Reading environment"); + goto out; + } + from->env[len] = '\0'; + + from->envp[0] = &from->env[0]; + for (i = 0, j = 0; i < len && j < ARRAY_SIZE(from->envp); i++) { + if (from->env[i] == '\0') { + from->envp[++j] = &from->env[i + 1]; + } + } + from->envp[j] = NULL; + +out: return 0; } @@ -269,6 +296,7 @@ static void allow(char *shell, mode_t mask) struct su_initiator *from = &su_from; struct su_request *to = &su_to; char *exe = NULL; + char **envp = environ; umask(mask); send_intent(&su_from, &su_to, "", 1, 1); @@ -277,14 +305,17 @@ static void allow(char *shell, mode_t mask) strcpy(shell , "/system/bin/sh"); } exe = strrchr (shell, '/') + 1; + if (from->envp[0]) { + envp = from->envp; + } 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); + execle(shell, exe, "-c", to->command, (char*)NULL, envp); } else { - execl(shell, exe, "-", (char*)NULL); + execle(shell, exe, "-", (char*)NULL, envp); } PLOGE("exec"); exit(EXIT_SUCCESS); diff --git a/su.h b/su.h index 01e7666..1bb13e9 100644 --- a/su.h +++ b/su.h @@ -38,6 +38,8 @@ struct su_initiator { unsigned uid; char bin[PATH_MAX]; char args[4096]; + char env[ARG_MAX]; + char *envp[512]; }; struct su_request { @@ -65,4 +67,6 @@ extern int send_intent(struct su_initiator *from, struct su_request *to, const c #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)) +#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0])) + #endif From 8fb8756b737cd92be893a841061bab1ee42ed2f9 Mon Sep 17 00:00:00 2001 From: "Gleb O. Raiko" Date: Sun, 23 Oct 2011 17:18:52 +0400 Subject: [PATCH 23/71] Report error from read rather than successful completion of close --- su.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/su.c b/su.c index bb87a19..4094af0 100644 --- a/su.c +++ b/su.c @@ -71,6 +71,7 @@ static int from_init(struct su_initiator *from) int fd; ssize_t len; int i; + int err; from->uid = getuid(); from->pid = getppid(); @@ -83,9 +84,10 @@ static int from_init(struct su_initiator *from) return -1; } len = read(fd, args, sizeof(args)); + err = errno; close(fd); if (len < 0 || len == sizeof(args)) { - PLOGE("Reading command line"); + PLOGEV("Reading command line", err); return -1; } From 8cee79fe1377ea5a891f624d2509137a06e240c1 Mon Sep 17 00:00:00 2001 From: "Gleb O. Raiko" Date: Sun, 23 Oct 2011 17:31:27 +0400 Subject: [PATCH 24/71] Remove setgroups in allow() supplementary groups have been dropped already at this time (introduced by commit 00f1bb547ef219b690a440920c7bc29be2a1d348) --- su.c | 1 - 1 file changed, 1 deletion(-) diff --git a/su.c b/su.c index 4094af0..8e7f488 100644 --- a/su.c +++ b/su.c @@ -289,7 +289,6 @@ static void allow(char *shell) strcpy(shell , "/system/bin/sh"); } exe = strrchr (shell, '/') + 1; - 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, From 7e47a9941b539215504ff74a3486beaf72382a29 Mon Sep 17 00:00:00 2001 From: "Gleb O. Raiko" Date: Sun, 30 Oct 2011 00:32:54 +0400 Subject: [PATCH 25/71] Always free result table if sqlite3_get_table succeeds --- db.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/db.c b/db.c index c338ee9..ca56f92 100644 --- a/db.c +++ b/db.c @@ -48,7 +48,7 @@ int database_check(sqlite3 *db, struct su_initiator *from, struct su_request *to char *zErrmsg; char **result; int nrow,ncol; - int allow; + int allow = DB_INTERACTIVE; struct timeval tv; sqlite3_snprintf( @@ -70,7 +70,7 @@ int database_check(sqlite3 *db, struct su_initiator *from, struct su_request *to } if (nrow == 0 || ncol != 3) - return DB_INTERACTIVE; + goto out; if (strcmp(result[0], "_id") == 0 && strcmp(result[2], "allow") == 0) { if (strcmp(result[5], "1") == 0) { @@ -80,10 +80,10 @@ int database_check(sqlite3 *db, struct su_initiator *from, struct su_request *to } else { allow = DB_DENY; } - return allow; } +out: sqlite3_free_table(result); - return DB_INTERACTIVE; + return allow; } From 77cf83727acdf4da4aeaf8f8e292496d67944a4a Mon Sep 17 00:00:00 2001 From: "Gleb O. Raiko" Date: Sun, 30 Oct 2011 14:43:23 +0400 Subject: [PATCH 26/71] Clean up sources --- db.c | 4 +--- su.c | 1 - su.h | 4 ---- 3 files changed, 1 insertion(+), 8 deletions(-) diff --git a/db.c b/db.c index ca56f92..ce4ae72 100644 --- a/db.c +++ b/db.c @@ -28,8 +28,7 @@ sqlite3 *database_init() { sqlite3 *db; - int version, rc, databaseStatus = 0; - char *zErrMsg = 0; + int rc; rc = sqlite3_open_v2(REQUESTOR_DATABASE_PATH, &db, SQLITE_OPEN_READONLY, NULL); if ( rc ) { @@ -49,7 +48,6 @@ int database_check(sqlite3 *db, struct su_initiator *from, struct su_request *to char **result; int nrow,ncol; int allow = DB_INTERACTIVE; - struct timeval tv; sqlite3_snprintf( sizeof(sql), sql, diff --git a/su.c b/su.c index 8e7f488..5d57480 100644 --- a/su.c +++ b/su.c @@ -224,7 +224,6 @@ 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); diff --git a/su.h b/su.h index 6bf7259..b8086be 100644 --- a/su.h +++ b/su.h @@ -24,10 +24,6 @@ #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 SOCKET_PATH_TEMPLATE REQUESTOR_CACHE_PATH "/.socketXXXXXX" From b1e2851a450b2a618ef1dc37086f322ce50c4080 Mon Sep 17 00:00:00 2001 From: "Gleb O. Raiko" Date: Sat, 3 Dec 2011 14:44:51 +0400 Subject: [PATCH 27/71] Fix potential security flaw in creation of sockets/directories Previous code created a file object with two steps: the creation itself with default umask/mode and setting necessary permissions then. This approach is known to lead to a race condition when malicious process can open an object before permissions is set. The patch sets creation mask (mask) to 027, thus denying any access from others. Also, the patch removes all dead code which is not needed after changes mentioned. --- su.c | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/su.c b/su.c index 5d57480..9bbd439 100644 --- a/su.c +++ b/su.c @@ -146,10 +146,10 @@ static void cleanup_signal(int sig) exit(sig); } -static int socket_create_temp(unsigned req_uid) +static int socket_create_temp(void) { static char buf[PATH_MAX]; - int fd, err; + int fd; struct sockaddr_un sun; @@ -176,18 +176,6 @@ static int socket_create_temp(unsigned req_uid) } } - if (chmod(sun.sun_path, 0600) < 0) { - PLOGE("chmod(socket)"); - unlink(sun.sun_path); - return -1; - } - - if (chown(sun.sun_path, req_uid, req_uid) < 0) { - PLOGE("chown(socket)"); - unlink(sun.sun_path); - return -1; - } - if (listen(fd, 1) < 0) { PLOGE("listen"); return -1; @@ -276,12 +264,13 @@ static void deny(void) exit(EXIT_FAILURE); } -static void allow(char *shell) +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, "")) { @@ -308,6 +297,7 @@ int main(int argc, char *argv[]) char buf[64], shell[PATH_MAX], *result; int i, dballow; unsigned req_uid; + mode_t orig_umask; for (i = 1; i < argc; i++) { if (!strcmp(argv[i], "-c") || !strcmp(argv[i], "--command")) { @@ -356,8 +346,10 @@ int main(int argc, char *argv[]) deny(); } + orig_umask = umask(027); + if (su_from.uid == AID_ROOT || su_from.uid == AID_SHELL) - allow(shell); + allow(shell, orig_umask); if (stat(REQUESTOR_DATA_PATH, &st) < 0) { PLOGE("stat"); @@ -373,7 +365,7 @@ int main(int argc, char *argv[]) req_uid = st.st_uid; - if (mkdir(REQUESTOR_CACHE_PATH, 0771) >= 0) { + if (mkdir(REQUESTOR_CACHE_PATH, 0770) >= 0) { chown(REQUESTOR_CACHE_PATH, req_uid, req_uid); } @@ -400,12 +392,12 @@ int main(int argc, char *argv[]) switch (dballow) { case DB_DENY: deny(); - case DB_ALLOW: allow(shell); + case DB_ALLOW: allow(shell, orig_umask); case DB_INTERACTIVE: break; default: deny(); } - socket_serv_fd = socket_create_temp(req_uid); + socket_serv_fd = socket_create_temp(); if (socket_serv_fd < 0) { deny(); } @@ -432,7 +424,7 @@ int main(int argc, char *argv[]) if (!strcmp(result, "DENY")) { deny(); } else if (!strcmp(result, "ALLOW")) { - allow(shell); + allow(shell, orig_umask); } else { LOGE("unknown response from Superuser Requestor: %s", result); deny(); From ae5862bc8a28d48423572c9838dca3b582774abe Mon Sep 17 00:00:00 2001 From: "Gleb O. Raiko" Date: Sun, 23 Oct 2011 17:30:45 +0400 Subject: [PATCH 28/71] Remove all occurences of req_uid --- su.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/su.c b/su.c index 9bbd439..9bf46bc 100644 --- a/su.c +++ b/su.c @@ -296,7 +296,6 @@ int main(int argc, char *argv[]) static int socket_serv_fd = -1; char buf[64], shell[PATH_MAX], *result; int i, dballow; - unsigned req_uid; mode_t orig_umask; for (i = 1; i < argc; i++) { @@ -363,10 +362,8 @@ int main(int argc, char *argv[]) deny(); } - req_uid = st.st_uid; - if (mkdir(REQUESTOR_CACHE_PATH, 0770) >= 0) { - chown(REQUESTOR_CACHE_PATH, req_uid, req_uid); + chown(REQUESTOR_CACHE_PATH, st.st_uid, st.st_gid); } setgroups(0, NULL); From 4b22bdcb2c650f949ef5a82a3b5b966333a45a4f Mon Sep 17 00:00:00 2001 From: Adam Shanks Date: Sun, 18 Dec 2011 13:21:55 +0000 Subject: [PATCH 29/71] Fix for Android 4.0.3 from shakalaca on github --- activity.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/activity.cpp b/activity.cpp index c361e41..c92e316 100644 --- a/activity.cpp +++ b/activity.cpp @@ -75,6 +75,10 @@ int send_intent(struct su_initiator *from, struct su_request *to, const char *so // 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(); From c46a3db32523f31dd869160440ca15ecbfb077e9 Mon Sep 17 00:00:00 2001 From: Adam Shanks Date: Sun, 18 Dec 2011 13:22:41 +0000 Subject: [PATCH 30/71] bump version --- su.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/su.h b/su.h index b8086be..01e7666 100644 --- a/su.h +++ b/su.h @@ -28,8 +28,8 @@ #define SOCKET_PATH_TEMPLATE REQUESTOR_CACHE_PATH "/.socketXXXXXX" -#define VERSION "3.0.3" -#define VERSION_CODE 14 +#define VERSION "3.0.3.2" +#define VERSION_CODE 15 #define DATABASE_VERSION 6 From b9a681d14e47badaecd0cccf1fc79f806bdcea6a Mon Sep 17 00:00:00 2001 From: git-core Date: Fri, 16 Dec 2011 23:25:08 +0400 Subject: [PATCH 31/71] Use /dev as storage for cache instead of /data/data /dev is tmpfs, so it's more suitable for storing temporary files than /data which is usualy internal flash memory on Android. Also, set uid and gid of the requestor cache unconditionally. Superuser app can be removed and, then, installed again with different uid. PackageManager doesn't protect us from this things anymore since the cache is in /dev now. --- su.c | 5 ++--- su.h | 5 +++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/su.c b/su.c index 9bf46bc..ccb7b07 100644 --- a/su.c +++ b/su.c @@ -362,9 +362,8 @@ int main(int argc, char *argv[]) deny(); } - if (mkdir(REQUESTOR_CACHE_PATH, 0770) >= 0) { - chown(REQUESTOR_CACHE_PATH, st.st_uid, st.st_gid); - } + mkdir(REQUESTOR_CACHE_PATH, 0770); + chown(REQUESTOR_CACHE_PATH, st.st_uid, st.st_gid); setgroups(0, NULL); setegid(st.st_gid); diff --git a/su.h b/su.h index 01e7666..7ef7e00 100644 --- a/su.h +++ b/su.h @@ -18,8 +18,9 @@ #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 "com.noshufou.android.su" +#define REQUESTOR_DATA_PATH "/data/data/" REQUESTOR +#define REQUESTOR_CACHE_PATH "/dev/" REQUESTOR #define REQUESTOR_DATABASES_PATH REQUESTOR_DATA_PATH "/databases" #define REQUESTOR_DATABASE_PATH REQUESTOR_DATABASES_PATH "/permissions.sqlite" From fb9f063fbd48a9d26de6ee340a7d2a346bf50e39 Mon Sep 17 00:00:00 2001 From: git-core Date: Sat, 17 Dec 2011 14:49:05 +0400 Subject: [PATCH 32/71] Preliminary fix for security issue reported via private email This commit will be dropped in the near future. The fix has to be reimplemented. The details of the issue and the reporter info will be published later. --- su.c | 77 ++++++++++++++++++++++++++++++++++++++++++++++-------------- su.h | 1 + 2 files changed, 60 insertions(+), 18 deletions(-) diff --git a/su.c b/su.c index ccb7b07..e1477ae 100644 --- a/su.c +++ b/su.c @@ -209,26 +209,53 @@ static int socket_accept(int serv_fd) return fd; } -static int socket_receive_result(int serv_fd, char *result, ssize_t result_len) +static int socket_send_request(int fd, struct su_initiator *from, struct su_request *to) +{ + size_t len; + size_t bin_size, cmd_size; + +#define write_token(fd, data) \ +do { \ + uint32_t __data = htonl(data); \ + size_t __count = sizeof(__data); \ + size_t __len = write((fd), &__data, __count); \ + if (__len != __count) { \ + PLOGE("write(" #data ")"); \ + return -1; \ + } \ +} while (0) + + write_token(fd, PROTO_VERSION); + write_token(fd, PATH_MAX); + write_token(fd, ARG_MAX); + write_token(fd, from->uid); + write_token(fd, to->uid); + bin_size = strlen(from->bin) + 1; + write_token(fd, bin_size); + len = write(fd, from->bin, bin_size); + if (len != bin_size) { + PLOGE("write(bin)"); + return -1; + } + cmd_size = strlen(to->command) + 1; + write_token(fd, cmd_size); + len = write(fd, to->command, cmd_size); + if (len != cmd_size) { + PLOGE("write(cmd)"); + return -1; + } + return 0; +} + +static int socket_receive_result(int 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; - } + len = read(fd, result, result_len-1); + if (len < 0) { + PLOGE("read(result)"); + return -1; } - result[len] = '\0'; return 0; @@ -293,7 +320,7 @@ static void allow(char *shell, mode_t mask) int main(int argc, char *argv[]) { struct stat st; - static int socket_serv_fd = -1; + int socket_serv_fd, fd; char buf[64], shell[PATH_MAX], *result; int i, dballow; mode_t orig_umask; @@ -408,15 +435,29 @@ int main(int argc, char *argv[]) deny(); } - if (socket_receive_result(socket_serv_fd, buf, sizeof(buf)) < 0) { + fd = socket_accept(socket_serv_fd); + if (fd < 0) { + deny(); + } + if (socket_send_request(fd, &su_from, &su_to)) { + deny(); + } + if (socket_receive_result(fd, buf, sizeof(buf))) { deny(); } + close(fd); close(socket_serv_fd); socket_cleanup(); result = buf; +#define SOCKET_RESPONSE "socket:" + if (strncmp(result, SOCKET_RESPONSE, sizeof(SOCKET_RESPONSE) - 1)) + LOGW("SECURITY RISK: Requestor still receives credentials in intent"); + else + result += sizeof(SOCKET_RESPONSE) - 1; + if (!strcmp(result, "DENY")) { deny(); } else if (!strcmp(result, "ALLOW")) { diff --git a/su.h b/su.h index 7ef7e00..8090ff8 100644 --- a/su.h +++ b/su.h @@ -33,6 +33,7 @@ #define VERSION_CODE 15 #define DATABASE_VERSION 6 +#define PROTO_VERSION 0 struct su_initiator { pid_t pid; From 73a49647c561997e1791c8d644086521f41aadd8 Mon Sep 17 00:00:00 2001 From: git-core Date: Sat, 17 Dec 2011 17:30:28 +0400 Subject: [PATCH 33/71] Move all code that deals with database in db.c --- db.c | 26 ++++++++++++++++++++++++-- su.c | 25 +------------------------ su.h | 2 ++ 3 files changed, 27 insertions(+), 26 deletions(-) diff --git a/db.c b/db.c index ce4ae72..c66576f 100644 --- a/db.c +++ b/db.c @@ -25,7 +25,7 @@ // { int* pint; pint=(int*)data; ++(*pint); } -sqlite3 *database_init() +static sqlite3 *db_init() { sqlite3 *db; int rc; @@ -41,7 +41,7 @@ sqlite3 *database_init() return db; } -int database_check(sqlite3 *db, struct su_initiator *from, struct su_request *to) +static int db_check(sqlite3 *db, struct su_initiator *from, struct su_request *to) { char sql[4096]; char *zErrmsg; @@ -85,3 +85,25 @@ int database_check(sqlite3 *db, struct su_initiator *from, struct su_request *to return allow; } + +int database_check(struct su_initiator *from, struct su_request *to) +{ + sqlite3 *db; + int dballow; + + LOGE("sudb - Opening database"); + db = db_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 + return DB_INTERACTIVE; + } + + LOGE("sudb - Database opened"); + dballow = db_check(db, from, to); + // Close the database, we're done with it. If it stays open, it will cause problems + sqlite3_close(db); + LOGE("sudb - Database closed"); + return dballow; +} diff --git a/su.c b/su.c index e1477ae..fffcac7 100644 --- a/su.c +++ b/su.c @@ -39,18 +39,12 @@ #include #include -#include - #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, @@ -137,7 +131,6 @@ static void socket_cleanup(void) static void cleanup(void) { socket_cleanup(); - if (db) sqlite3_close(db); } static void cleanup_signal(int sig) @@ -396,23 +389,7 @@ int main(int argc, char *argv[]) setegid(st.st_gid); seteuid(st.st_uid); - LOGE("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 { - 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); - db = NULL; - LOGE("sudb - Database closed"); - } - + dballow = database_check(&su_from, &su_to); switch (dballow) { case DB_DENY: deny(); case DB_ALLOW: allow(shell, orig_umask); diff --git a/su.h b/su.h index 8090ff8..985a5e0 100644 --- a/su.h +++ b/su.h @@ -53,6 +53,8 @@ enum { DB_ALLOW }; +extern int database_check(struct su_initiator*, struct su_request*); + extern int send_intent(struct su_initiator *from, struct su_request *to, const char *socket_path, int allow, int type); #if 0 From cbcc7692bf2be0fbdce5598b61f36276d46b5794 Mon Sep 17 00:00:00 2001 From: git-core Date: Sun, 18 Dec 2011 14:02:39 +0400 Subject: [PATCH 34/71] Don't use mktemp After access to the su cache was secured (commit 7d97cfea964c68c9c51a77750f6c7aa86fab66c3), we can use simple name convention for the socket object. Name .socket is simple enough and doesn't require mktemp which is blamed by gcc on every linkage. --- su.c | 45 ++++++++++++++++++++++----------------------- su.h | 2 -- 2 files changed, 22 insertions(+), 25 deletions(-) diff --git a/su.c b/su.c index fffcac7..9221bb9 100644 --- a/su.c +++ b/su.c @@ -41,10 +41,8 @@ #include "su.h" -//extern char* _mktemp(char*); /* mktemp doesn't link right. Don't ask me why. */ - /* Still lazt, will fix this */ -static char *socket_path = NULL; +static char socket_path[PATH_MAX]; static struct su_initiator su_from = { .pid = -1, @@ -139,11 +137,9 @@ static void cleanup_signal(int sig) exit(sig); } -static int socket_create_temp(void) +static int socket_create_temp(char *path, size_t len) { - static char buf[PATH_MAX]; int fd; - struct sockaddr_un sun; fd = socket(AF_LOCAL, SOCK_STREAM, 0); @@ -152,29 +148,32 @@ static int socket_create_temp(void) 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 { - break; - } + memset(&sun, 0, sizeof(sun)); + sun.sun_family = AF_LOCAL; + snprintf(path, len, "%s/.socket%d", REQUESTOR_CACHE_PATH, getpid()); + snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", path); + + /* + * Delete the socket to protect from situations when + * something bad occured previously and the kernel reused pid from that process. + * Small probability, isn't it. + */ + unlink(sun.sun_path); + + if (bind(fd, (struct sockaddr*)&sun, sizeof(sun)) < 0) { + PLOGE("bind"); + goto err; } if (listen(fd, 1) < 0) { PLOGE("listen"); - return -1; + goto err; } return fd; +err: + close(fd); + return -1; } static int socket_accept(int serv_fd) @@ -397,7 +396,7 @@ int main(int argc, char *argv[]) default: deny(); } - socket_serv_fd = socket_create_temp(); + socket_serv_fd = socket_create_temp(socket_path, sizeof(socket_path)); if (socket_serv_fd < 0) { deny(); } diff --git a/su.h b/su.h index 985a5e0..043ed0f 100644 --- a/su.h +++ b/su.h @@ -27,8 +27,6 @@ #define DEFAULT_COMMAND "/system/bin/sh" -#define SOCKET_PATH_TEMPLATE REQUESTOR_CACHE_PATH "/.socketXXXXXX" - #define VERSION "3.0.3.2" #define VERSION_CODE 15 From 815edac93007df1aa0eaff77d92d0a4b681feb4f Mon Sep 17 00:00:00 2001 From: git-core Date: Mon, 19 Dec 2011 23:59:31 +0400 Subject: [PATCH 35/71] Merge legacy branch --- Android.mk | 16 +++++++++------- activity.cpp | 6 ++++++ su.h | 8 +++++++- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/Android.mk b/Android.mk index d19d0e0..8a43c01 100644 --- a/Android.mk +++ b/Android.mk @@ -4,17 +4,19 @@ include $(CLEAR_VARS) LOCAL_MODULE := su LOCAL_SRC_FILES := su.c db.c activity.cpp +SU_SHARED_LIBRARIES := liblog libsqlite +ifeq ($(PLATFORM_SDK_VERSION),4) + LOCAL_CFLAGS += -DSU_LEGACY_BUILD + SU_SHARED_LIBRARIES += libandroid_runtime +else + SU_SHARED_LIBRARIES += libcutils libbinder libutils + LOCAL_MODULE_TAGS := debug,eng +endif LOCAL_C_INCLUDES += external/sqlite/dist -LOCAL_SHARED_LIBRARIES := \ - liblog \ - libsqlite \ - libcutils \ - libbinder \ - libutils \ +LOCAL_SHARED_LIBRARIES := $(SU_SHARED_LIBRARIES) LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) -LOCAL_MODULE_TAGS := debug,eng include $(BUILD_EXECUTABLE) diff --git a/activity.cpp b/activity.cpp index c92e316..28139cb 100644 --- a/activity.cpp +++ b/activity.cpp @@ -17,9 +17,15 @@ #include #include +#ifdef SU_LEGACY_BUILD +#include +#include +#include +#else #include #include #include +#endif #include #include diff --git a/su.h b/su.h index 043ed0f..8ca8bb9 100644 --- a/su.h +++ b/su.h @@ -27,7 +27,13 @@ #define DEFAULT_COMMAND "/system/bin/sh" -#define VERSION "3.0.3.2" +#ifdef SU_LEGACY_BUILD +#define VERSION_EXTRA "l" +#else +#define VERSION_EXTRA "" +#endif + +#define VERSION "3.0.3.2" VERSION_EXTRA #define VERSION_CODE 15 #define DATABASE_VERSION 6 From cf50c028d866b2742e277c9efa14564084d629e4 Mon Sep 17 00:00:00 2001 From: git-core Date: Sun, 25 Dec 2011 20:52:23 +0400 Subject: [PATCH 36/71] Fix off-by-one bug when calculating the bundle size Remember position in parcel before adding bundle signature. The old code supposed to work by chance: the resolved type was NULL (so only -1 as int32 was stored in the parcel) and redundant -1 (as int32) was added after the resolved type. Thus, the former was actually a null string in the bundle and the latter acted the part of the (null) resolved type. Don't put garbage at the end of intent (after sticky which is supposed to be the last parameter in an intent). Again, it seemed to work before due to Android stops to parse intent just after it gets all required parameters. --- activity.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/activity.cpp b/activity.cpp index 28139cb..c79ddae 100644 --- a/activity.cpp +++ b/activity.cpp @@ -87,8 +87,8 @@ int send_intent(struct su_initiator *from, struct su_request *to, const char *so } { /* Extras */ data.writeInt32(-1); /* dummy, will hold length */ - int oldPos = data.dataPosition(); data.writeInt32(0x4C444E42); // 'B' 'N' 'D' 'L' + int oldPos = data.dataPosition(); { /* writeMapInternal */ data.writeInt32(7); /* writeMapInternal - size */ @@ -128,15 +128,13 @@ int send_intent(struct su_initiator *from, struct su_request *to, const char *so data.writeInt32(VERSION_CODE); } int newPos = data.dataPosition(); - data.setDataPosition(oldPos - 4); + data.setDataPosition(oldPos - 8); 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 */ @@ -146,7 +144,6 @@ int send_intent(struct su_initiator *from, struct su_request *to, const char *so 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; From 983d8e040bcddcc8a73e583a565183644629bcd7 Mon Sep 17 00:00:00 2001 From: git-core Date: Mon, 26 Dec 2011 23:58:56 +0400 Subject: [PATCH 37/71] Reorder the code which stores intent into parcel for clarity Data themself aren't changed actually, only comments around the code --- activity.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/activity.cpp b/activity.cpp index c79ddae..1eac868 100644 --- a/activity.cpp +++ b/activity.cpp @@ -76,11 +76,11 @@ int send_intent(struct su_initiator *from, struct su_request *to, const char *so 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); + data.writeInt32(0); /* Rect - the bounds of the sender */ } + data.writeInt32(0); /* Categories - size */ if (sdk_version >= 15) { // added in IceCreamSandwich 4.0.3 data.writeInt32(0); /* Selector */ From 57d607f73c3e056bcecc505b6630ebb780d55e38 Mon Sep 17 00:00:00 2001 From: git-core Date: Wed, 28 Dec 2011 10:36:20 +0400 Subject: [PATCH 38/71] Use DEFAULT_COMMAND intead of hardcoded name for default shell --- su.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/su.c b/su.c index 9221bb9..1df9f6b 100644 --- a/su.c +++ b/su.c @@ -293,7 +293,7 @@ static void allow(char *shell, mode_t mask) send_intent(&su_from, &su_to, "", 1, 1); if (!strcmp(shell, "")) { - strcpy(shell , "/system/bin/sh"); + strcpy(shell , DEFAULT_COMMAND); } exe = strrchr (shell, '/') + 1; setresgid(to->uid, to->uid, to->uid); From b3c87082c8d6db1543f7cf0df7e31a85f5c61592 Mon Sep 17 00:00:00 2001 From: git-core Date: Wed, 28 Dec 2011 10:41:28 +0400 Subject: [PATCH 39/71] Fix segmentation fault when shell passed via -s doesn't contain / --- su.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/su.c b/su.c index 1df9f6b..6cf79ac 100644 --- a/su.c +++ b/su.c @@ -295,7 +295,8 @@ static void allow(char *shell, mode_t mask) if (!strcmp(shell, "")) { strcpy(shell , DEFAULT_COMMAND); } - exe = strrchr (shell, '/') + 1; + exe = strrchr (shell, '/'); + exe = (exe) ? exe + 1 : shell; 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, From da868645214a5026a606461be9f7421cb20f8c1f Mon Sep 17 00:00:00 2001 From: git-core Date: Wed, 28 Dec 2011 15:08:11 +0400 Subject: [PATCH 40/71] Exit with false status if exec(3) fails ... ... and show errno from exec(3) to the user --- su.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/su.c b/su.c index 6cf79ac..06a857a 100644 --- a/su.c +++ b/su.c @@ -288,6 +288,7 @@ static void allow(char *shell, mode_t mask) struct su_initiator *from = &su_from; struct su_request *to = &su_to; char *exe = NULL; + int err; umask(mask); send_intent(&su_from, &su_to, "", 1, 1); @@ -306,8 +307,10 @@ static void allow(char *shell, mode_t mask) } else { execl(shell, exe, "-", (char*)NULL); } + err = errno; PLOGE("exec"); - exit(EXIT_SUCCESS); + fprintf(stderr, "Cannot execute %s: %s\n", shell, strerror(err)); + exit(EXIT_FAILURE); } int main(int argc, char *argv[]) From fc7479fab2d9d85c7c1f0bf1fee523a69d477a40 Mon Sep 17 00:00:00 2001 From: git-core Date: Wed, 28 Dec 2011 15:11:39 +0400 Subject: [PATCH 41/71] Be compatible with other implementation of su o use getopt_long(3), so -l, --login can be allowed anywhere in the argument list o add -m and -p which do nothing o su -h outputs usage in stdout and exits with 0 while su with invalid options outputs usage in stderr and exits with 2 now --- su.c | 119 ++++++++++++++++++++++++++++++++--------------------------- 1 file changed, 65 insertions(+), 54 deletions(-) diff --git a/su.c b/su.c index 06a857a..5cd80aa 100644 --- a/su.c +++ b/su.c @@ -28,11 +28,10 @@ #include #include #include - #include #include #include - +#include #include #include @@ -253,23 +252,22 @@ static int socket_receive_result(int fd, char *result, ssize_t result_len) return 0; } -static void usage(void) +static void usage(int status) { - 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); + FILE *stream = (status == EXIT_SUCCESS) ? stdout : stderr; + + fprintf(stream, + "Usage: su [options] [LOGIN]\n\n" + "Options:\n" + " -c, --command COMMAND pass COMMAND to the invoked shell\n" + " -h, --help display this help message and exit\n" + " -, -l, --login, -m, -p,\n" + " --preserve-environment do nothing, kept for compatibility\n" + " -s, --shell SHELL use SHELL instead of the default " DEFAULT_COMMAND "\n" + " -v, --version display version number and exit\n" + " -V display version code and exit,\n" + " this is used almost exclusively by Superuser.apk\n"); + exit(status); } static void deny(void) @@ -293,8 +291,8 @@ static void allow(char *shell, mode_t mask) umask(mask); send_intent(&su_from, &su_to, "", 1, 1); - if (!strcmp(shell, "")) { - strcpy(shell , DEFAULT_COMMAND); + if (!shell) { + shell = DEFAULT_COMMAND; } exe = strrchr (shell, '/'); exe = (exe) ? exe + 1 : shell; @@ -317,48 +315,61 @@ int main(int argc, char *argv[]) { struct stat st; int socket_serv_fd, fd; - char buf[64], shell[PATH_MAX], *result; - int i, dballow; + char buf[64], *shell = NULL, *result; + int c, 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; + struct option long_opts[] = { + { "command", required_argument, NULL, 'c' }, + { "help", no_argument, NULL, 'h' }, + { "login", no_argument, NULL, 'l' }, + { "preserve-environment", no_argument, NULL, 'p' }, + { "shell", required_argument, NULL, 's' }, + { "version", no_argument, NULL, 'v' }, + { NULL, 0, NULL, 0 }, + }; + + while ((c = getopt_long(argc, argv, "c:hlmps:Vv", long_opts, NULL)) != -1) { + switch(c) { + case 'c': + su_to.command = optarg; break; - } else { + case 'h': + usage(EXIT_SUCCESS); + break; + case 'l': /* for compatibility */ + case 'm': + case 'p': + break; + case 's': + shell = optarg; break; + case 'V': + printf("%d\n", VERSION_CODE); + exit(EXIT_SUCCESS); + case 'v': + printf("%s\n", VERSION); + exit(EXIT_SUCCESS); + default: + /* Bionic getopt_long doesn't terminate its error output by newline */ + fprintf(stderr, "\n"); + usage(2); } } - if (i < argc-1) { - usage(); + if (optind < argc && !strcmp(argv[optind], "-")) { + optind++; + } + /* + * Other su implementations pass the remaining args to the shell. + * -- maybe implement this later + */ + if (optind < argc - 1) { + usage(2); } - if (i == argc-1) { + if (optind == argc - 1) { struct passwd *pw; - pw = getpwnam(argv[i]); + pw = getpwnam(argv[optind]); if (!pw) { - su_to.uid = atoi(argv[i]); + su_to.uid = atoi(argv[optind]); } else { su_to.uid = pw->pw_uid; } From 0e769ccf82253becb41060d0c481a35a06a991e6 Mon Sep 17 00:00:00 2001 From: git-core Date: Wed, 28 Dec 2011 16:07:39 +0400 Subject: [PATCH 42/71] Don't allow garbage instead of real uid in the command line Replace atoi(3) by strtoul(3), the latter allows to check parse errors --- su.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/su.c b/su.c index 5cd80aa..e35cf10 100644 --- a/su.c +++ b/su.c @@ -369,7 +369,16 @@ int main(int argc, char *argv[]) struct passwd *pw; pw = getpwnam(argv[optind]); if (!pw) { - su_to.uid = atoi(argv[optind]); + char *endptr; + + /* It seems we shouldn't do this at all */ + errno = 0; + su_to.uid = strtoul(argv[optind], &endptr, 10); + if (errno || *endptr) { + LOGE("Unknown id: %s\n", argv[optind]); + fprintf(stderr, "Unknown id: %s\n", argv[optind]); + exit(EXIT_FAILURE); + } } else { su_to.uid = pw->pw_uid; } From 94d02f2347451a5199c3afe6bc7d4e37c417a3db Mon Sep 17 00:00:00 2001 From: Adam Shanks Date: Thu, 5 Jan 2012 19:12:13 +0000 Subject: [PATCH 43/71] Bump version --- su.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/su.h b/su.h index 8ca8bb9..e3e3e7c 100644 --- a/su.h +++ b/su.h @@ -33,8 +33,8 @@ #define VERSION_EXTRA "" #endif -#define VERSION "3.0.3.2" VERSION_EXTRA -#define VERSION_CODE 15 +#define VERSION "3.1" VERSION_EXTRA +#define VERSION_CODE 16 #define DATABASE_VERSION 6 #define PROTO_VERSION 0 From bc9bbecfe978de88aa25e8fdad80e3e4f1eb8b62 Mon Sep 17 00:00:00 2001 From: git-core Date: Sat, 14 Jan 2012 19:23:52 +0400 Subject: [PATCH 44/71] Check syscall return codes --- su.c | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/su.c b/su.c index e35cf10..ea6e2f2 100644 --- a/su.c +++ b/su.c @@ -296,8 +296,14 @@ static void allow(char *shell, mode_t mask) } exe = strrchr (shell, '/'); exe = (exe) ? exe + 1 : shell; - setresgid(to->uid, to->uid, to->uid); - setresuid(to->uid, to->uid, to->uid); + if (setresgid(to->uid, to->uid, to->uid)) { + PLOGE("setresgid (%u)", to->uid); + exit(EXIT_FAILURE); + } + if (setresuid(to->uid, to->uid, to->uid)) { + PLOGE("setresuid (%u)", to->uid); + exit(EXIT_FAILURE); + } 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)) { @@ -406,11 +412,23 @@ int main(int argc, char *argv[]) } mkdir(REQUESTOR_CACHE_PATH, 0770); - chown(REQUESTOR_CACHE_PATH, st.st_uid, st.st_gid); + if (chown(REQUESTOR_CACHE_PATH, st.st_uid, st.st_gid)) { + PLOGE("chown (%s, %ld, %ld)", REQUESTOR_CACHE_PATH, st.st_uid, st.st_gid); + deny(); + } - setgroups(0, NULL); - setegid(st.st_gid); - seteuid(st.st_uid); + if (setgroups(0, NULL)) { + PLOGE("setgroups"); + deny(); + } + if (setegid(st.st_gid)) { + PLOGE("setegid (%lu)", st.st_gid); + deny(); + } + if (seteuid(st.st_uid)) { + PLOGE("seteuid (%lu)", st.st_uid); + deny(); + } dballow = database_check(&su_from, &su_to); switch (dballow) { From c29f8d42acbe6e6fd2e31b047d86268460e2fdf5 Mon Sep 17 00:00:00 2001 From: git-core Date: Tue, 17 Jan 2012 23:11:51 +0400 Subject: [PATCH 45/71] Remove the communication socket if Ctrl+C (SIGINT) or Ctrl+\ (SIGQUIT) are pressed --- su.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/su.c b/su.c index ea6e2f2..6ab292e 100644 --- a/su.c +++ b/su.c @@ -446,6 +446,8 @@ int main(int argc, char *argv[]) signal(SIGHUP, cleanup_signal); signal(SIGPIPE, cleanup_signal); signal(SIGTERM, cleanup_signal); + signal(SIGQUIT, cleanup_signal); + signal(SIGINT, cleanup_signal); signal(SIGABRT, cleanup_signal); atexit(cleanup); From aa74885fd9f2193c895c043aeebb58121fa7c80b Mon Sep 17 00:00:00 2001 From: git-core Date: Tue, 17 Jan 2012 23:26:09 +0400 Subject: [PATCH 46/71] Prepare to implement argument passing from su command line to a callee --- su.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/su.c b/su.c index 6ab292e..e0e3531 100644 --- a/su.c +++ b/su.c @@ -286,6 +286,8 @@ static void allow(char *shell, mode_t mask) struct su_initiator *from = &su_from; struct su_request *to = &su_to; char *exe = NULL; + char *argv[4]; + int argc = 0; int err; umask(mask); @@ -306,11 +308,15 @@ static void allow(char *shell, mode_t mask) } LOGD("%u %s executing %u %s using shell %s : %s", from->uid, from->bin, to->uid, to->command, shell, exe); + argv[argc++] = exe; if (strcmp(to->command, DEFAULT_COMMAND)) { - execl(shell, exe, "-c", to->command, (char*)NULL); + argv[argc++] = "-c"; + argv[argc++] = to->command; } else { - execl(shell, exe, "-", (char*)NULL); + argv[argc++] = "-"; } + argv[argc] = NULL; + execv(shell, argv); err = errno; PLOGE("exec"); fprintf(stderr, "Cannot execute %s: %s\n", shell, strerror(err)); From e39e1e30c9786e94bbd318fb50e8a2d43b8e7a86 Mon Sep 17 00:00:00 2001 From: git-core Date: Sat, 21 Jan 2012 19:48:05 +0400 Subject: [PATCH 47/71] Accurately handle -c option --- su.c | 8 +++++--- su.h | 1 + 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/su.c b/su.c index e0e3531..949b94a 100644 --- a/su.c +++ b/su.c @@ -52,6 +52,7 @@ static struct su_initiator su_from = { static struct su_request su_to = { .uid = AID_ROOT, + .doshell = 1, .command = DEFAULT_COMMAND, }; @@ -309,11 +310,11 @@ static void allow(char *shell, mode_t mask) LOGD("%u %s executing %u %s using shell %s : %s", from->uid, from->bin, to->uid, to->command, shell, exe); argv[argc++] = exe; - if (strcmp(to->command, DEFAULT_COMMAND)) { + if (to->doshell) { + argv[argc++] = "-"; + } else { argv[argc++] = "-c"; argv[argc++] = to->command; - } else { - argv[argc++] = "-"; } argv[argc] = NULL; execv(shell, argv); @@ -344,6 +345,7 @@ int main(int argc, char *argv[]) switch(c) { case 'c': su_to.command = optarg; + su_to.doshell = 0; break; case 'h': usage(EXIT_SUCCESS); diff --git a/su.h b/su.h index e3e3e7c..d6e4acf 100644 --- a/su.h +++ b/su.h @@ -48,6 +48,7 @@ struct su_initiator { struct su_request { unsigned uid; + int doshell; char *command; }; From 69c1b43ae913b51efe1296441a6899daafe7f8e3 Mon Sep 17 00:00:00 2001 From: git-core Date: Sat, 21 Jan 2012 20:09:29 +0400 Subject: [PATCH 48/71] Return special exit code when terminating by signal So user could distinguish between SIGHUP and other abnormal terminations at least --- su.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/su.c b/su.c index 949b94a..a5a3e67 100644 --- a/su.c +++ b/su.c @@ -134,7 +134,7 @@ static void cleanup(void) static void cleanup_signal(int sig) { socket_cleanup(); - exit(sig); + exit(128 + sig); } static int socket_create_temp(char *path, size_t len) From ac59b39d0a5f4f34ecc8cab5c0de922f5d9cd0ef Mon Sep 17 00:00:00 2001 From: git-core Date: Sat, 21 Jan 2012 22:55:02 +0400 Subject: [PATCH 49/71] Handle -l option --- su.c | 19 +++++++++++++++++-- su.h | 1 + 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/su.c b/su.c index a5a3e67..6ee8cd2 100644 --- a/su.c +++ b/su.c @@ -52,6 +52,7 @@ static struct su_initiator su_from = { static struct su_request su_to = { .uid = AID_ROOT, + .login = 0, .doshell = 1, .command = DEFAULT_COMMAND, }; @@ -299,6 +300,17 @@ static void allow(char *shell, mode_t mask) } exe = strrchr (shell, '/'); exe = (exe) ? exe + 1 : shell; + if (to->login) { + int s = strlen(exe) + 2; + char *p = malloc(s); + + if (!p) + exit(EXIT_FAILURE); + + *p = '-'; + strcpy(p + 1, exe); + exe = p; + } if (setresgid(to->uid, to->uid, to->uid)) { PLOGE("setresgid (%u)", to->uid); exit(EXIT_FAILURE); @@ -350,8 +362,10 @@ int main(int argc, char *argv[]) case 'h': usage(EXIT_SUCCESS); break; - case 'l': /* for compatibility */ - case 'm': + case 'l': + su_to.login = 1; + break; + case 'm': /* for compatibility */ case 'p': break; case 's': @@ -370,6 +384,7 @@ int main(int argc, char *argv[]) } } if (optind < argc && !strcmp(argv[optind], "-")) { + su_to.login = 1; optind++; } /* diff --git a/su.h b/su.h index d6e4acf..884bb61 100644 --- a/su.h +++ b/su.h @@ -48,6 +48,7 @@ struct su_initiator { struct su_request { unsigned uid; + int login; int doshell; char *command; }; From 418ec8b69d3923122bbed2bf8066964166d5cf79 Mon Sep 17 00:00:00 2001 From: git-core Date: Sun, 29 Jan 2012 17:15:55 +0400 Subject: [PATCH 50/71] Pass the rest of arguments in the command line to a callee Thus, someone may invoke su as su -s shell uid args... For example, su -s /system/xbin/busybox root mount -o rw,remount /system --- activity.cpp | 5 ++- db.c | 2 +- su.c | 103 +++++++++++++++++++++++++++------------------------ su.h | 8 +++- 4 files changed, 65 insertions(+), 53 deletions(-) diff --git a/activity.cpp b/activity.cpp index 1eac868..c52689d 100644 --- a/activity.cpp +++ b/activity.cpp @@ -110,7 +110,10 @@ int send_intent(struct su_initiator *from, struct su_request *to, const char *so data.writeInt32(VAL_STRING); data.writeString16(String16("desired_cmd")); data.writeInt32(VAL_STRING); - data.writeString16(String16(to->command)); + if (to->command) + data.writeString16(String16(to->command)); + else + data.writeString16(String16(to->shell)); data.writeInt32(VAL_STRING); data.writeString16(String16("socket")); diff --git a/db.c b/db.c index c66576f..6b29b60 100644 --- a/db.c +++ b/db.c @@ -52,7 +52,7 @@ static int db_check(sqlite3 *db, struct su_initiator *from, struct su_request *t 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 + (unsigned)from->uid, to->uid, (to->command) ? to->command : to->shell ); if (strlen(sql) >= sizeof(sql)-1) diff --git a/su.c b/su.c index 6ee8cd2..414a00f 100644 --- a/su.c +++ b/su.c @@ -53,8 +53,11 @@ static struct su_initiator su_from = { static struct su_request su_to = { .uid = AID_ROOT, .login = 0, - .doshell = 1, - .command = DEFAULT_COMMAND, + .shell = DEFAULT_SHELL, + .command = NULL, + .argv = NULL, + .argc = 0, + .optind = 0, }; static int from_init(struct su_initiator *from) @@ -206,6 +209,7 @@ static int socket_send_request(int fd, struct su_initiator *from, struct su_requ { size_t len; size_t bin_size, cmd_size; + char *cmd; #define write_token(fd, data) \ do { \ @@ -230,9 +234,10 @@ do { \ PLOGE("write(bin)"); return -1; } - cmd_size = strlen(to->command) + 1; + cmd = (to->command) ? to->command : to->shell; + cmd_size = strlen(cmd) + 1; write_token(fd, cmd_size); - len = write(fd, to->command, cmd_size); + len = write(fd, cmd, cmd_size); if (len != cmd_size) { PLOGE("write(cmd)"); return -1; @@ -259,13 +264,13 @@ static void usage(int status) FILE *stream = (status == EXIT_SUCCESS) ? stdout : stderr; fprintf(stream, - "Usage: su [options] [LOGIN]\n\n" + "Usage: su [options] [--] [-] [LOGIN] [--] [args...]\n\n" "Options:\n" " -c, --command COMMAND pass COMMAND to the invoked shell\n" " -h, --help display this help message and exit\n" " -, -l, --login, -m, -p,\n" " --preserve-environment do nothing, kept for compatibility\n" - " -s, --shell SHELL use SHELL instead of the default " DEFAULT_COMMAND "\n" + " -s, --shell SHELL use SHELL instead of the default " DEFAULT_SHELL "\n" " -v, --version display version number and exit\n" " -V display version code and exit,\n" " this is used almost exclusively by Superuser.apk\n"); @@ -276,40 +281,36 @@ static void deny(void) { struct su_initiator *from = &su_from; struct su_request *to = &su_to; + char *cmd = (to->command) ? to->command : to->shell; send_intent(&su_from, &su_to, "", 0, 1); - LOGW("request rejected (%u->%u %s)", from->uid, to->uid, to->command); + LOGW("request rejected (%u->%u %s)", from->uid, to->uid, cmd); fprintf(stderr, "%s\n", strerror(EACCES)); exit(EXIT_FAILURE); } -static void allow(char *shell, mode_t mask) +static void allow(void) { struct su_initiator *from = &su_from; struct su_request *to = &su_to; - char *exe = NULL; - char *argv[4]; - int argc = 0; - int err; + char *arg0; + int argc, err; - umask(mask); + umask(to->umask); send_intent(&su_from, &su_to, "", 1, 1); - if (!shell) { - shell = DEFAULT_COMMAND; - } - exe = strrchr (shell, '/'); - exe = (exe) ? exe + 1 : shell; + arg0 = strrchr (to->shell, '/'); + arg0 = (arg0) ? arg0 + 1 : to->shell; if (to->login) { - int s = strlen(exe) + 2; + int s = strlen(arg0) + 2; char *p = malloc(s); if (!p) exit(EXIT_FAILURE); *p = '-'; - strcpy(p + 1, exe); - exe = p; + strcpy(p + 1, arg0); + arg0 = p; } if (setresgid(to->uid, to->uid, to->uid)) { PLOGE("setresgid (%u)", to->uid); @@ -319,20 +320,25 @@ static void allow(char *shell, mode_t mask) PLOGE("setresuid (%u)", to->uid); exit(EXIT_FAILURE); } - LOGD("%u %s executing %u %s using shell %s : %s", from->uid, from->bin, - to->uid, to->command, shell, exe); - argv[argc++] = exe; - if (to->doshell) { - argv[argc++] = "-"; - } else { - argv[argc++] = "-c"; - argv[argc++] = to->command; + +#define PARG(arg) (to->optind + (arg) < to->argc) ? " " : "", \ + (to->optind + (arg) < to->argc) ? to->argv[to->optind + (arg)] : "" + LOGD("%u %s executing %u %s using shell %s : %s%s%s%s%s%s%s%s%s%s%s%s%s%s", + from->uid, from->bin, + to->uid, (to->command) ? to->command : to->shell, to->shell, + arg0, PARG(0), PARG(1), PARG(2), PARG(3), PARG(4), PARG(5), + (to->optind + 6 < to->argc) ? " ..." : ""); + + argc = to->optind; + if (to->command) { + to->argv[--argc] = to->command; + to->argv[--argc] = "-c"; } - argv[argc] = NULL; - execv(shell, argv); + to->argv[--argc] = arg0; + execv(to->shell, to->argv + argc); err = errno; PLOGE("exec"); - fprintf(stderr, "Cannot execute %s: %s\n", shell, strerror(err)); + fprintf(stderr, "Cannot execute %s: %s\n", to->shell, strerror(err)); exit(EXIT_FAILURE); } @@ -340,9 +346,8 @@ int main(int argc, char *argv[]) { struct stat st; int socket_serv_fd, fd; - char buf[64], *shell = NULL, *result; + char buf[64], *result; int c, dballow; - mode_t orig_umask; struct option long_opts[] = { { "command", required_argument, NULL, 'c' }, { "help", no_argument, NULL, 'h' }, @@ -353,11 +358,10 @@ int main(int argc, char *argv[]) { NULL, 0, NULL, 0 }, }; - while ((c = getopt_long(argc, argv, "c:hlmps:Vv", long_opts, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "+c:hlmps:Vv", long_opts, NULL)) != -1) { switch(c) { case 'c': su_to.command = optarg; - su_to.doshell = 0; break; case 'h': usage(EXIT_SUCCESS); @@ -369,7 +373,7 @@ int main(int argc, char *argv[]) case 'p': break; case 's': - shell = optarg; + su_to.shell = optarg; break; case 'V': printf("%d\n", VERSION_CODE); @@ -387,14 +391,8 @@ int main(int argc, char *argv[]) su_to.login = 1; optind++; } - /* - * Other su implementations pass the remaining args to the shell. - * -- maybe implement this later - */ - if (optind < argc - 1) { - usage(2); - } - if (optind == argc - 1) { + /* username or uid */ + if (optind < argc && strcmp(argv[optind], "--")) { struct passwd *pw; pw = getpwnam(argv[optind]); if (!pw) { @@ -411,16 +409,23 @@ int main(int argc, char *argv[]) } else { su_to.uid = pw->pw_uid; } + optind++; + } + if (optind < argc && !strcmp(argv[optind], "--")) { + optind++; } + su_to.argv = argv; + su_to.argc = argc; + su_to.optind = optind; if (from_init(&su_from) < 0) { deny(); } - orig_umask = umask(027); + su_to.umask = umask(027); if (su_from.uid == AID_ROOT || su_from.uid == AID_SHELL) - allow(shell, orig_umask); + allow(); if (stat(REQUESTOR_DATA_PATH, &st) < 0) { PLOGE("stat"); @@ -456,7 +461,7 @@ int main(int argc, char *argv[]) dballow = database_check(&su_from, &su_to); switch (dballow) { case DB_DENY: deny(); - case DB_ALLOW: allow(shell, orig_umask); + case DB_ALLOW: allow(); case DB_INTERACTIVE: break; default: deny(); } @@ -504,7 +509,7 @@ int main(int argc, char *argv[]) if (!strcmp(result, "DENY")) { deny(); } else if (!strcmp(result, "ALLOW")) { - allow(shell, orig_umask); + allow(); } else { LOGE("unknown response from Superuser Requestor: %s", result); deny(); diff --git a/su.h b/su.h index 884bb61..2f8bef7 100644 --- a/su.h +++ b/su.h @@ -25,7 +25,7 @@ #define REQUESTOR_DATABASES_PATH REQUESTOR_DATA_PATH "/databases" #define REQUESTOR_DATABASE_PATH REQUESTOR_DATABASES_PATH "/permissions.sqlite" -#define DEFAULT_COMMAND "/system/bin/sh" +#define DEFAULT_SHELL "/system/bin/sh" #ifdef SU_LEGACY_BUILD #define VERSION_EXTRA "l" @@ -49,8 +49,12 @@ struct su_initiator { struct su_request { unsigned uid; int login; - int doshell; + mode_t umask; + char *shell; char *command; + char **argv; + int argc; + int optind; }; enum { From 4eb80175f98569f7b9f3facb4f9e184c293cb93d Mon Sep 17 00:00:00 2001 From: git-core Date: Sun, 29 Jan 2012 20:18:52 +0400 Subject: [PATCH 51/71] Combine su_initiator and su_request into su_context --- activity.cpp | 14 ++--- db.c | 8 +-- su.c | 172 +++++++++++++++++++++++++-------------------------- su.h | 11 +++- 4 files changed, 103 insertions(+), 102 deletions(-) diff --git a/activity.cpp b/activity.cpp index c52689d..0328063 100644 --- a/activity.cpp +++ b/activity.cpp @@ -46,7 +46,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 allow, int type) +int send_intent(struct su_context *ctx, 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"); @@ -95,25 +95,25 @@ int send_intent(struct su_initiator *from, struct su_request *to, const char *so data.writeInt32(VAL_STRING); data.writeString16(String16("caller_uid")); data.writeInt32(VAL_INTEGER); - data.writeInt32(from->uid); + data.writeInt32(ctx->from.uid); data.writeInt32(VAL_STRING); data.writeString16(String16("caller_bin")); data.writeInt32(VAL_STRING); - data.writeString16(String16(from->bin)); + data.writeString16(String16(ctx->from.bin)); data.writeInt32(VAL_STRING); data.writeString16(String16("desired_uid")); data.writeInt32(VAL_INTEGER); - data.writeInt32(to->uid); + data.writeInt32(ctx->to.uid); data.writeInt32(VAL_STRING); data.writeString16(String16("desired_cmd")); data.writeInt32(VAL_STRING); - if (to->command) - data.writeString16(String16(to->command)); + if (ctx->to.command) + data.writeString16(String16(ctx->to.command)); else - data.writeString16(String16(to->shell)); + data.writeString16(String16(ctx->to.shell)); data.writeInt32(VAL_STRING); data.writeString16(String16("socket")); diff --git a/db.c b/db.c index 6b29b60..7d192ee 100644 --- a/db.c +++ b/db.c @@ -41,7 +41,7 @@ static sqlite3 *db_init() return db; } -static int db_check(sqlite3 *db, struct su_initiator *from, struct su_request *to) +static int db_check(sqlite3 *db, struct su_context *ctx) { char sql[4096]; char *zErrmsg; @@ -52,7 +52,7 @@ static int db_check(sqlite3 *db, struct su_initiator *from, struct su_request *t 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) ? to->command : to->shell + ctx->from.uid, ctx->to.uid, (ctx->to.command) ? ctx->to.command : ctx->to.shell ); if (strlen(sql) >= sizeof(sql)-1) @@ -86,7 +86,7 @@ static int db_check(sqlite3 *db, struct su_initiator *from, struct su_request *t return allow; } -int database_check(struct su_initiator *from, struct su_request *to) +int database_check(struct su_context *ctx) { sqlite3 *db; int dballow; @@ -101,7 +101,7 @@ int database_check(struct su_initiator *from, struct su_request *to) } LOGE("sudb - Database opened"); - dballow = db_check(db, from, to); + dballow = db_check(db, ctx); // Close the database, we're done with it. If it stays open, it will cause problems sqlite3_close(db); LOGE("sudb - Database closed"); diff --git a/su.c b/su.c index 414a00f..b8ddc70 100644 --- a/su.c +++ b/su.c @@ -43,23 +43,6 @@ /* Still lazt, will fix this */ static char socket_path[PATH_MAX]; -static struct su_initiator su_from = { - .pid = -1, - .uid = 0, - .bin = "", - .args = "", -}; - -static struct su_request su_to = { - .uid = AID_ROOT, - .login = 0, - .shell = DEFAULT_SHELL, - .command = NULL, - .argv = NULL, - .argc = 0, - .optind = 0, -}; - static int from_init(struct su_initiator *from) { char path[PATH_MAX], exe[PATH_MAX]; @@ -205,7 +188,7 @@ static int socket_accept(int serv_fd) return fd; } -static int socket_send_request(int fd, struct su_initiator *from, struct su_request *to) +static int socket_send_request(int fd, struct su_context *ctx) { size_t len; size_t bin_size, cmd_size; @@ -225,16 +208,16 @@ do { \ write_token(fd, PROTO_VERSION); write_token(fd, PATH_MAX); write_token(fd, ARG_MAX); - write_token(fd, from->uid); - write_token(fd, to->uid); - bin_size = strlen(from->bin) + 1; + write_token(fd, ctx->from.uid); + write_token(fd, ctx->to.uid); + bin_size = strlen(ctx->from.bin) + 1; write_token(fd, bin_size); - len = write(fd, from->bin, bin_size); + len = write(fd, ctx->from.bin, bin_size); if (len != bin_size) { PLOGE("write(bin)"); return -1; } - cmd = (to->command) ? to->command : to->shell; + cmd = (ctx->to.command) ? ctx->to.command : ctx->to.shell; cmd_size = strlen(cmd) + 1; write_token(fd, cmd_size); len = write(fd, cmd, cmd_size); @@ -277,31 +260,27 @@ static void usage(int status) exit(status); } -static void deny(void) +static void deny(struct su_context *ctx) { - struct su_initiator *from = &su_from; - struct su_request *to = &su_to; - char *cmd = (to->command) ? to->command : to->shell; + char *cmd = (ctx->to.command) ? ctx->to.command : ctx->to.shell; - send_intent(&su_from, &su_to, "", 0, 1); - LOGW("request rejected (%u->%u %s)", from->uid, to->uid, cmd); + send_intent(ctx, "", 0, 1); + LOGW("request rejected (%u->%u %s)", ctx->from.uid, ctx->to.uid, cmd); fprintf(stderr, "%s\n", strerror(EACCES)); exit(EXIT_FAILURE); } -static void allow(void) +static void allow(struct su_context *ctx) { - struct su_initiator *from = &su_from; - struct su_request *to = &su_to; char *arg0; int argc, err; - umask(to->umask); - send_intent(&su_from, &su_to, "", 1, 1); + umask(ctx->umask); + send_intent(ctx, "", 1, 1); - arg0 = strrchr (to->shell, '/'); - arg0 = (arg0) ? arg0 + 1 : to->shell; - if (to->login) { + arg0 = strrchr (ctx->to.shell, '/'); + arg0 = (arg0) ? arg0 + 1 : ctx->to.shell; + if (ctx->to.login) { int s = strlen(arg0) + 2; char *p = malloc(s); @@ -312,38 +291,57 @@ static void allow(void) strcpy(p + 1, arg0); arg0 = p; } - if (setresgid(to->uid, to->uid, to->uid)) { - PLOGE("setresgid (%u)", to->uid); + if (setresgid(ctx->to.uid, ctx->to.uid, ctx->to.uid)) { + PLOGE("setresgid (%u)", ctx->to.uid); exit(EXIT_FAILURE); } - if (setresuid(to->uid, to->uid, to->uid)) { - PLOGE("setresuid (%u)", to->uid); + if (setresuid(ctx->to.uid, ctx->to.uid, ctx->to.uid)) { + PLOGE("setresuid (%u)", ctx->to.uid); exit(EXIT_FAILURE); } -#define PARG(arg) (to->optind + (arg) < to->argc) ? " " : "", \ - (to->optind + (arg) < to->argc) ? to->argv[to->optind + (arg)] : "" +#define PARG(arg) \ + (ctx->to.optind + (arg) < ctx->to.argc) ? " " : "", \ + (ctx->to.optind + (arg) < ctx->to.argc) ? ctx->to.argv[ctx->to.optind + (arg)] : "" + LOGD("%u %s executing %u %s using shell %s : %s%s%s%s%s%s%s%s%s%s%s%s%s%s", - from->uid, from->bin, - to->uid, (to->command) ? to->command : to->shell, to->shell, + ctx->from.uid, ctx->from.bin, + ctx->to.uid, (ctx->to.command) ? ctx->to.command : ctx->to.shell, ctx->to.shell, arg0, PARG(0), PARG(1), PARG(2), PARG(3), PARG(4), PARG(5), - (to->optind + 6 < to->argc) ? " ..." : ""); + (ctx->to.optind + 6 < ctx->to.argc) ? " ..." : ""); - argc = to->optind; - if (to->command) { - to->argv[--argc] = to->command; - to->argv[--argc] = "-c"; + argc = ctx->to.optind; + if (ctx->to.command) { + ctx->to.argv[--argc] = ctx->to.command; + ctx->to.argv[--argc] = "-c"; } - to->argv[--argc] = arg0; - execv(to->shell, to->argv + argc); + ctx->to.argv[--argc] = arg0; + execv(ctx->to.shell, ctx->to.argv + argc); err = errno; PLOGE("exec"); - fprintf(stderr, "Cannot execute %s: %s\n", to->shell, strerror(err)); + fprintf(stderr, "Cannot execute %s: %s\n", ctx->to.shell, strerror(err)); exit(EXIT_FAILURE); } int main(int argc, char *argv[]) { + struct su_context ctx = { + .from = { + .pid = -1, + .uid = 0, + .bin = "", + .args = "", + }, + .to = { + .uid = AID_ROOT, + .login = 0, + .shell = DEFAULT_SHELL, + .command = NULL, + .argv = argv, + .argc = argc, + .optind = 0, + }, + }; struct stat st; int socket_serv_fd, fd; char buf[64], *result; @@ -361,19 +359,19 @@ int main(int argc, char *argv[]) while ((c = getopt_long(argc, argv, "+c:hlmps:Vv", long_opts, NULL)) != -1) { switch(c) { case 'c': - su_to.command = optarg; + ctx.to.command = optarg; break; case 'h': usage(EXIT_SUCCESS); break; case 'l': - su_to.login = 1; + ctx.to.login = 1; break; case 'm': /* for compatibility */ case 'p': break; case 's': - su_to.shell = optarg; + ctx.to.shell = optarg; break; case 'V': printf("%d\n", VERSION_CODE); @@ -388,7 +386,7 @@ int main(int argc, char *argv[]) } } if (optind < argc && !strcmp(argv[optind], "-")) { - su_to.login = 1; + ctx.to.login = 1; optind++; } /* username or uid */ @@ -400,75 +398,73 @@ int main(int argc, char *argv[]) /* It seems we shouldn't do this at all */ errno = 0; - su_to.uid = strtoul(argv[optind], &endptr, 10); + ctx.to.uid = strtoul(argv[optind], &endptr, 10); if (errno || *endptr) { LOGE("Unknown id: %s\n", argv[optind]); fprintf(stderr, "Unknown id: %s\n", argv[optind]); exit(EXIT_FAILURE); } } else { - su_to.uid = pw->pw_uid; + ctx.to.uid = pw->pw_uid; } optind++; } if (optind < argc && !strcmp(argv[optind], "--")) { optind++; } - su_to.argv = argv; - su_to.argc = argc; - su_to.optind = optind; + ctx.to.optind = optind; - if (from_init(&su_from) < 0) { - deny(); + if (from_init(&ctx.from) < 0) { + deny(&ctx); } - su_to.umask = umask(027); + ctx.umask = umask(027); - if (su_from.uid == AID_ROOT || su_from.uid == AID_SHELL) - allow(); + if (ctx.from.uid == AID_ROOT || ctx.from.uid == AID_SHELL) + allow(&ctx); if (stat(REQUESTOR_DATA_PATH, &st) < 0) { PLOGE("stat"); - deny(); + deny(&ctx); } 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(); + deny(&ctx); } mkdir(REQUESTOR_CACHE_PATH, 0770); if (chown(REQUESTOR_CACHE_PATH, st.st_uid, st.st_gid)) { PLOGE("chown (%s, %ld, %ld)", REQUESTOR_CACHE_PATH, st.st_uid, st.st_gid); - deny(); + deny(&ctx); } if (setgroups(0, NULL)) { PLOGE("setgroups"); - deny(); + deny(&ctx); } if (setegid(st.st_gid)) { PLOGE("setegid (%lu)", st.st_gid); - deny(); + deny(&ctx); } if (seteuid(st.st_uid)) { PLOGE("seteuid (%lu)", st.st_uid); - deny(); + deny(&ctx); } - dballow = database_check(&su_from, &su_to); + dballow = database_check(&ctx); switch (dballow) { - case DB_DENY: deny(); - case DB_ALLOW: allow(); + case DB_DENY: deny(&ctx); + case DB_ALLOW: allow(&ctx); case DB_INTERACTIVE: break; - default: deny(); + default: deny(&ctx); } socket_serv_fd = socket_create_temp(socket_path, sizeof(socket_path)); if (socket_serv_fd < 0) { - deny(); + deny(&ctx); } signal(SIGHUP, cleanup_signal); @@ -479,19 +475,19 @@ int main(int argc, char *argv[]) signal(SIGABRT, cleanup_signal); atexit(cleanup); - if (send_intent(&su_from, &su_to, socket_path, -1, 0) < 0) { - deny(); + if (send_intent(&ctx, socket_path, -1, 0) < 0) { + deny(&ctx); } fd = socket_accept(socket_serv_fd); if (fd < 0) { - deny(); + deny(&ctx); } - if (socket_send_request(fd, &su_from, &su_to)) { - deny(); + if (socket_send_request(fd, &ctx)) { + deny(&ctx); } if (socket_receive_result(fd, buf, sizeof(buf))) { - deny(); + deny(&ctx); } close(fd); @@ -507,14 +503,14 @@ int main(int argc, char *argv[]) result += sizeof(SOCKET_RESPONSE) - 1; if (!strcmp(result, "DENY")) { - deny(); + deny(&ctx); } else if (!strcmp(result, "ALLOW")) { - allow(); + allow(&ctx); } else { LOGE("unknown response from Superuser Requestor: %s", result); - deny(); + deny(&ctx); } - deny(); + deny(&ctx); return -1; } diff --git a/su.h b/su.h index 2f8bef7..f5e1970 100644 --- a/su.h +++ b/su.h @@ -49,7 +49,6 @@ struct su_initiator { struct su_request { unsigned uid; int login; - mode_t umask; char *shell; char *command; char **argv; @@ -57,15 +56,21 @@ struct su_request { int optind; }; +struct su_context { + struct su_initiator from; + struct su_request to; + mode_t umask; +}; + enum { DB_INTERACTIVE, DB_DENY, DB_ALLOW }; -extern int database_check(struct su_initiator*, struct su_request*); +extern int database_check(struct su_context *ctx); -extern int send_intent(struct su_initiator *from, struct su_request *to, const char *socket_path, int allow, int type); +extern int send_intent(struct su_context *ctx, const char *socket_path, int allow, int type); #if 0 #undef LOGE From 6f6eb26839780ca56b5254745c7cae4d6dae9a26 Mon Sep 17 00:00:00 2001 From: git-core Date: Sun, 29 Jan 2012 20:48:21 +0400 Subject: [PATCH 52/71] Move frequently used code snippet in get_command() --- activity.cpp | 5 +---- db.c | 2 +- su.c | 6 +++--- su.h | 5 +++++ 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/activity.cpp b/activity.cpp index 0328063..a192751 100644 --- a/activity.cpp +++ b/activity.cpp @@ -110,10 +110,7 @@ int send_intent(struct su_context *ctx, const char *socket_path, int allow, int data.writeInt32(VAL_STRING); data.writeString16(String16("desired_cmd")); data.writeInt32(VAL_STRING); - if (ctx->to.command) - data.writeString16(String16(ctx->to.command)); - else - data.writeString16(String16(ctx->to.shell)); + data.writeString16(String16(get_command(&ctx->to))); data.writeInt32(VAL_STRING); data.writeString16(String16("socket")); diff --git a/db.c b/db.c index 7d192ee..07b353c 100644 --- a/db.c +++ b/db.c @@ -52,7 +52,7 @@ static int db_check(sqlite3 *db, struct su_context *ctx) sqlite3_snprintf( sizeof(sql), sql, "SELECT _id,name,allow FROM apps WHERE uid=%u AND exec_uid=%u AND exec_cmd='%q';", - ctx->from.uid, ctx->to.uid, (ctx->to.command) ? ctx->to.command : ctx->to.shell + ctx->from.uid, ctx->to.uid, get_command(&ctx->to) ); if (strlen(sql) >= sizeof(sql)-1) diff --git a/su.c b/su.c index b8ddc70..d8ed29b 100644 --- a/su.c +++ b/su.c @@ -217,7 +217,7 @@ do { \ PLOGE("write(bin)"); return -1; } - cmd = (ctx->to.command) ? ctx->to.command : ctx->to.shell; + cmd = get_command(&ctx->to); cmd_size = strlen(cmd) + 1; write_token(fd, cmd_size); len = write(fd, cmd, cmd_size); @@ -262,7 +262,7 @@ static void usage(int status) static void deny(struct su_context *ctx) { - char *cmd = (ctx->to.command) ? ctx->to.command : ctx->to.shell; + char *cmd = get_command(&ctx->to); send_intent(ctx, "", 0, 1); LOGW("request rejected (%u->%u %s)", ctx->from.uid, ctx->to.uid, cmd); @@ -306,7 +306,7 @@ static void allow(struct su_context *ctx) LOGD("%u %s executing %u %s using shell %s : %s%s%s%s%s%s%s%s%s%s%s%s%s%s", ctx->from.uid, ctx->from.bin, - ctx->to.uid, (ctx->to.command) ? ctx->to.command : ctx->to.shell, ctx->to.shell, + ctx->to.uid, get_command(&ctx->to), ctx->to.shell, arg0, PARG(0), PARG(1), PARG(2), PARG(3), PARG(4), PARG(5), (ctx->to.optind + 6 < ctx->to.argc) ? " ..." : ""); diff --git a/su.h b/su.h index f5e1970..ae51920 100644 --- a/su.h +++ b/su.h @@ -72,6 +72,11 @@ extern int database_check(struct su_context *ctx); extern int send_intent(struct su_context *ctx, const char *socket_path, int allow, int type); +static inline char *get_command(struct su_request *to) +{ + return (to->command) ? to->command : to->shell; +} + #if 0 #undef LOGE #define LOGE(fmt,args...) fprintf(stderr, fmt , ## args ) From 95fe23798e918ad5b246e2eff42bb6ae841bdf40 Mon Sep 17 00:00:00 2001 From: git-core Date: Sun, 29 Jan 2012 20:59:21 +0400 Subject: [PATCH 53/71] Call property_get() only once property_get() iterates over potentially unbound array of properties. There is no good for calling it several times per su invocation. --- activity.cpp | 13 +++---------- su.c | 12 ++++++++++++ su.h | 1 + 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/activity.cpp b/activity.cpp index a192751..f0ee97d 100644 --- a/activity.cpp +++ b/activity.cpp @@ -31,8 +31,6 @@ extern "C" { #include "su.h" -#include -#include } using namespace android; @@ -48,11 +46,6 @@ static const int START_SUCCESS = 0; int send_intent(struct su_context *ctx, 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 sm = defaultServiceManager(); sp am = sm->checkService(String16("activity")); assert(am != NULL); @@ -71,17 +64,17 @@ int send_intent(struct su_context *ctx, const char *socket_path, int allow, int data.writeInt32(NULL_TYPE_ID); /* Uri - data */ data.writeString16(NULL, 0); /* type */ data.writeInt32(0); /* flags */ - if (sdk_version >= 4) { + if (ctx->sdk_version >= 4) { // added in donut data.writeString16(NULL, 0); /* package name - DONUT ONLY, NOT IN CUPCAKE. */ } data.writeString16(NULL, 0); /* ComponentName - package */ - if (sdk_version >= 7) { + if (ctx->sdk_version >= 7) { // added in eclair rev 7 data.writeInt32(0); /* Rect - the bounds of the sender */ } data.writeInt32(0); /* Categories - size */ - if (sdk_version >= 15) { + if (ctx->sdk_version >= 15) { // added in IceCreamSandwich 4.0.3 data.writeInt32(0); /* Selector */ } diff --git a/su.c b/su.c index d8ed29b..43f64f4 100644 --- a/su.c +++ b/su.c @@ -36,6 +36,7 @@ #include #include +#include #include #include "su.h" @@ -43,6 +44,15 @@ /* Still lazt, will fix this */ static char socket_path[PATH_MAX]; + +static inline int get_sdk_version(void) +{ + char sdk_version_prop[PROPERTY_VALUE_MAX]; + + property_get("ro.build.version.sdk", sdk_version_prop, "0"); + return atoi(sdk_version_prop); +} + static int from_init(struct su_initiator *from) { char path[PATH_MAX], exe[PATH_MAX]; @@ -414,6 +424,8 @@ int main(int argc, char *argv[]) } ctx.to.optind = optind; + ctx.sdk_version = get_sdk_version(); + if (from_init(&ctx.from) < 0) { deny(&ctx); } diff --git a/su.h b/su.h index ae51920..de8a8a4 100644 --- a/su.h +++ b/su.h @@ -60,6 +60,7 @@ struct su_context { struct su_initiator from; struct su_request to; mode_t umask; + int sdk_version; }; enum { From 73e06ac2df0f2f3d4753cd8ad03ec88240bdc5d6 Mon Sep 17 00:00:00 2001 From: git-core Date: Sun, 29 Jan 2012 21:37:51 +0400 Subject: [PATCH 54/71] Pass last parameter to send_intent() as pointer to the action name --- activity.cpp | 9 +++------ su.c | 6 +++--- su.h | 7 ++++++- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/activity.cpp b/activity.cpp index f0ee97d..c140fa7 100644 --- a/activity.cpp +++ b/activity.cpp @@ -44,7 +44,8 @@ static const int VAL_INTEGER = 1; static const int START_SUCCESS = 0; -int send_intent(struct su_context *ctx, const char *socket_path, int allow, int type) +int send_intent(struct su_context *ctx, + const char *socket_path, int allow, const char *action) { sp sm = defaultServiceManager(); sp am = sm->checkService(String16("activity")); @@ -56,11 +57,7 @@ int send_intent(struct su_context *ctx, const char *socket_path, int allow, int 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.writeString16(String16(action)); /* action */ data.writeInt32(NULL_TYPE_ID); /* Uri - data */ data.writeString16(NULL, 0); /* type */ data.writeInt32(0); /* flags */ diff --git a/su.c b/su.c index 43f64f4..7903f20 100644 --- a/su.c +++ b/su.c @@ -274,7 +274,7 @@ static void deny(struct su_context *ctx) { char *cmd = get_command(&ctx->to); - send_intent(ctx, "", 0, 1); + send_intent(ctx, "", 0, ACTION_RESULT); LOGW("request rejected (%u->%u %s)", ctx->from.uid, ctx->to.uid, cmd); fprintf(stderr, "%s\n", strerror(EACCES)); exit(EXIT_FAILURE); @@ -286,7 +286,7 @@ static void allow(struct su_context *ctx) int argc, err; umask(ctx->umask); - send_intent(ctx, "", 1, 1); + send_intent(ctx, "", 1, ACTION_RESULT); arg0 = strrchr (ctx->to.shell, '/'); arg0 = (arg0) ? arg0 + 1 : ctx->to.shell; @@ -487,7 +487,7 @@ int main(int argc, char *argv[]) signal(SIGABRT, cleanup_signal); atexit(cleanup); - if (send_intent(&ctx, socket_path, -1, 0) < 0) { + if (send_intent(&ctx, socket_path, -1, ACTION_REQUEST) < 0) { deny(&ctx); } diff --git a/su.h b/su.h index de8a8a4..c03b9aa 100644 --- a/su.h +++ b/su.h @@ -25,6 +25,10 @@ #define REQUESTOR_DATABASES_PATH REQUESTOR_DATA_PATH "/databases" #define REQUESTOR_DATABASE_PATH REQUESTOR_DATABASES_PATH "/permissions.sqlite" +/* intent actions */ +#define ACTION_REQUEST REQUESTOR ".REQUEST" +#define ACTION_RESULT REQUESTOR ".RESULT" + #define DEFAULT_SHELL "/system/bin/sh" #ifdef SU_LEGACY_BUILD @@ -71,7 +75,8 @@ enum { extern int database_check(struct su_context *ctx); -extern int send_intent(struct su_context *ctx, const char *socket_path, int allow, int type); +extern int send_intent(struct su_context *ctx, + const char *socket_path, int allow, const char *action); static inline char *get_command(struct su_request *to) { From 067da4eadd7e892915e133dcae295af90f998bb7 Mon Sep 17 00:00:00 2001 From: git-core Date: Sun, 29 Jan 2012 21:50:31 +0400 Subject: [PATCH 55/71] Provide LOG_TAG for db too --- su.c | 2 -- su.h | 5 +++++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/su.c b/su.c index 7903f20..51f5130 100644 --- a/su.c +++ b/su.c @@ -15,8 +15,6 @@ ** limitations under the License. */ -#define LOG_TAG "su" - #include #include #include diff --git a/su.h b/su.h index c03b9aa..e32665d 100644 --- a/su.h +++ b/su.h @@ -18,6 +18,11 @@ #ifndef SU_h #define SU_h 1 +#ifdef LOG_TAG +#undef LOG_TAG +#endif +#define LOG_TAG "su" + #define REQUESTOR "com.noshufou.android.su" #define REQUESTOR_DATA_PATH "/data/data/" REQUESTOR #define REQUESTOR_CACHE_PATH "/dev/" REQUESTOR From 33529968515ec0c8d2dc73804091d02331454631 Mon Sep 17 00:00:00 2001 From: git-core Date: Sun, 29 Jan 2012 22:00:27 +0400 Subject: [PATCH 56/71] constantify su_context* arg & small cosmetic fixes --- activity.cpp | 2 +- db.c | 8 +++----- su.c | 6 +++--- su.h | 16 ++++++++-------- 4 files changed, 15 insertions(+), 17 deletions(-) diff --git a/activity.cpp b/activity.cpp index c140fa7..9f3f930 100644 --- a/activity.cpp +++ b/activity.cpp @@ -44,7 +44,7 @@ static const int VAL_INTEGER = 1; static const int START_SUCCESS = 0; -int send_intent(struct su_context *ctx, +int send_intent(const struct su_context *ctx, const char *socket_path, int allow, const char *action) { sp sm = defaultServiceManager(); diff --git a/db.c b/db.c index 07b353c..f570d2e 100644 --- a/db.c +++ b/db.c @@ -23,9 +23,7 @@ #include "su.h" -// { int* pint; pint=(int*)data; ++(*pint); } - -static sqlite3 *db_init() +static sqlite3 *db_init(void) { sqlite3 *db; int rc; @@ -41,7 +39,7 @@ static sqlite3 *db_init() return db; } -static int db_check(sqlite3 *db, struct su_context *ctx) +static int db_check(sqlite3 *db, const struct su_context *ctx) { char sql[4096]; char *zErrmsg; @@ -86,7 +84,7 @@ static int db_check(sqlite3 *db, struct su_context *ctx) return allow; } -int database_check(struct su_context *ctx) +int database_check(const struct su_context *ctx) { sqlite3 *db; int dballow; diff --git a/su.c b/su.c index 51f5130..d86e4a1 100644 --- a/su.c +++ b/su.c @@ -196,7 +196,7 @@ static int socket_accept(int serv_fd) return fd; } -static int socket_send_request(int fd, struct su_context *ctx) +static int socket_send_request(int fd, const struct su_context *ctx) { size_t len; size_t bin_size, cmd_size; @@ -268,7 +268,7 @@ static void usage(int status) exit(status); } -static void deny(struct su_context *ctx) +static void deny(const struct su_context *ctx) { char *cmd = get_command(&ctx->to); @@ -278,7 +278,7 @@ static void deny(struct su_context *ctx) exit(EXIT_FAILURE); } -static void allow(struct su_context *ctx) +static void allow(const struct su_context *ctx) { char *arg0; int argc, err; diff --git a/su.h b/su.h index e32665d..0ebf1ba 100644 --- a/su.h +++ b/su.h @@ -78,26 +78,26 @@ enum { DB_ALLOW }; -extern int database_check(struct su_context *ctx); +extern int database_check(const struct su_context *ctx); -extern int send_intent(struct su_context *ctx, +extern int send_intent(const struct su_context *ctx, const char *socket_path, int allow, const char *action); -static inline char *get_command(struct su_request *to) +static inline char *get_command(const struct su_request *to) { return (to->command) ? to->command : to->shell; } #if 0 #undef LOGE -#define LOGE(fmt,args...) fprintf(stderr, fmt , ## args ) +#define LOGE(fmt,args...) fprintf(stderr, fmt, ##args) #undef LOGD -#define LOGD(fmt,args...) fprintf(stderr, fmt , ## args ) +#define LOGD(fmt,args...) fprintf(stderr, fmt, ##args) #undef LOGW -#define LOGW(fmt,args...) fprintf(stderr, fmt , ## args ) +#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)) +#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 From c97414b9d2f7bb080818d49a47a3b9fe0aea2543 Mon Sep 17 00:00:00 2001 From: git-core Date: Mon, 20 Feb 2012 23:29:00 +0400 Subject: [PATCH 57/71] Revert "Inherit the environment of the caller process for a new process image" This reverts commit e0329cee5f18171ac7a4e89e42a0bb763a1f30a8. --- su.c | 34 +--------------------------------- su.h | 4 ---- 2 files changed, 1 insertion(+), 37 deletions(-) diff --git a/su.c b/su.c index 5bcee92..d86e4a1 100644 --- a/su.c +++ b/su.c @@ -59,7 +59,6 @@ static int from_init(struct su_initiator *from) ssize_t len; int i; int err; - size_t j; from->uid = getuid(); from->pid = getppid(); @@ -114,31 +113,6 @@ static int from_init(struct su_initiator *from) strncpy(from->bin, argv0, sizeof(from->bin)); from->bin[sizeof(from->bin)-1] = '\0'; - /* Get the environment of the calling process */ - snprintf(path, sizeof(path), "/proc/%u/environ", from->pid); - fd = open(path, O_RDONLY); - if (fd < 0) { - PLOGE("Opening environment"); - goto out; - } - len = read(fd, from->env, sizeof(from->env)); - err = errno; - close(fd); - if (len < 0 || len == sizeof(from->env)) { - PLOGEV("Reading environment", err); - goto out; - } - from->env[len] = '\0'; - - from->envp[0] = &from->env[0]; - for (i = 0, j = 0; i < len && j < ARRAY_SIZE(from->envp); i++) { - if (from->env[i] == '\0') { - from->envp[++j] = &from->env[i + 1]; - } - } - from->envp[j] = NULL; - -out: return 0; } @@ -307,7 +281,6 @@ static void deny(const struct su_context *ctx) static void allow(const struct su_context *ctx) { char *arg0; - char * const* envp = environ; int argc, err; umask(ctx->umask); @@ -326,9 +299,6 @@ static void allow(const struct su_context *ctx) strcpy(p + 1, arg0); arg0 = p; } - if (ctx->from.envp[0]) { - envp = ctx->from.envp; - } if (setresgid(ctx->to.uid, ctx->to.uid, ctx->to.uid)) { PLOGE("setresgid (%u)", ctx->to.uid); exit(EXIT_FAILURE); @@ -354,7 +324,7 @@ static void allow(const struct su_context *ctx) ctx->to.argv[--argc] = "-c"; } ctx->to.argv[--argc] = arg0; - execve(ctx->to.shell, ctx->to.argv + argc, envp); + execv(ctx->to.shell, ctx->to.argv + argc); err = errno; PLOGE("exec"); fprintf(stderr, "Cannot execute %s: %s\n", ctx->to.shell, strerror(err)); @@ -369,8 +339,6 @@ int main(int argc, char *argv[]) .uid = 0, .bin = "", .args = "", - .env = "", - .envp = { NULL }, }, .to = { .uid = AID_ROOT, diff --git a/su.h b/su.h index d32e7e2..0ebf1ba 100644 --- a/su.h +++ b/su.h @@ -53,8 +53,6 @@ struct su_initiator { unsigned uid; char bin[PATH_MAX]; char args[4096]; - char env[ARG_MAX]; - char *envp[512]; }; struct su_request { @@ -102,6 +100,4 @@ static inline char *get_command(const struct su_request *to) #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)) -#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0])) - #endif From 8afac1d25701618cd32ad2bc5dce01e3a77a5604 Mon Sep 17 00:00:00 2001 From: git-core Date: Sat, 25 Feb 2012 23:55:55 +0400 Subject: [PATCH 58/71] Setup environment for a new process image, implement -p/-m options --- su.c | 30 +++++++++++++++++++++++++++--- su.h | 1 + 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/su.c b/su.c index d86e4a1..8ec38c3 100644 --- a/su.c +++ b/su.c @@ -116,6 +116,24 @@ static int from_init(struct su_initiator *from) return 0; } +static void populate_environment(const struct su_context *ctx) +{ + struct passwd *pw; + + if (ctx->to.keepenv) + return; + + pw = getpwuid(ctx->to.uid); + if (pw) { + setenv("HOME", pw->pw_dir, 1); + setenv("SHELL", ctx->to.shell, 1); + if (ctx->to.login || ctx->to.uid) { + setenv("USER", pw->pw_name, 1); + setenv("LOGNAME", pw->pw_name, 1); + } + } +} + static void socket_cleanup(void) { unlink(socket_path); @@ -259,8 +277,9 @@ static void usage(int status) "Options:\n" " -c, --command COMMAND pass COMMAND to the invoked shell\n" " -h, --help display this help message and exit\n" - " -, -l, --login, -m, -p,\n" - " --preserve-environment do nothing, kept for compatibility\n" + " -, -l, --login pretend the shell to be a login shell\n" + " -m, -p,\n" + " --preserve-environment do not change environment variables\n" " -s, --shell SHELL use SHELL instead of the default " DEFAULT_SHELL "\n" " -v, --version display version number and exit\n" " -V display version code and exit,\n" @@ -299,6 +318,9 @@ static void allow(const struct su_context *ctx) strcpy(p + 1, arg0); arg0 = p; } + + populate_environment(ctx); + if (setresgid(ctx->to.uid, ctx->to.uid, ctx->to.uid)) { PLOGE("setresgid (%u)", ctx->to.uid); exit(EXIT_FAILURE); @@ -343,6 +365,7 @@ int main(int argc, char *argv[]) .to = { .uid = AID_ROOT, .login = 0, + .keepenv = 0, .shell = DEFAULT_SHELL, .command = NULL, .argv = argv, @@ -375,8 +398,9 @@ int main(int argc, char *argv[]) case 'l': ctx.to.login = 1; break; - case 'm': /* for compatibility */ + case 'm': case 'p': + ctx.to.keepenv = 1; break; case 's': ctx.to.shell = optarg; diff --git a/su.h b/su.h index 0ebf1ba..7f215c7 100644 --- a/su.h +++ b/su.h @@ -58,6 +58,7 @@ struct su_initiator { struct su_request { unsigned uid; int login; + int keepenv; char *shell; char *command; char **argv; From f39dbf0effb2e4b110ae9a9a5d930044ea275280 Mon Sep 17 00:00:00 2001 From: git-core Date: Sat, 3 Mar 2012 12:15:07 +0400 Subject: [PATCH 59/71] Change euid back to root before finally set a new id Fixes a bug reported in a comment to the issue #9 against su-binary (see https://github.com/ChainsDD/su-binary/issues/9#issuecomment-4220404 for details). Thanks to Erik Pilsits (epilsits@gmail.com) for the report. Add the euid change before populate_environment() anticipating we have to merge the commit "Inherit LD_LIBRARY_PATH ...", which needs root credentials in order to successfully open /proc//environ. --- su.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/su.c b/su.c index 8ec38c3..71e008f 100644 --- a/su.c +++ b/su.c @@ -319,6 +319,15 @@ static void allow(const struct su_context *ctx) arg0 = p; } + /* + * Set effective uid back to root, otherwise setres[ug]id will fail + * if ctx->to.uid isn't root. + */ + if (seteuid(0)) { + PLOGE("seteuid (root)"); + exit(EXIT_FAILURE); + } + populate_environment(ctx); if (setresgid(ctx->to.uid, ctx->to.uid, ctx->to.uid)) { From 382d1e37c1fec3d58f49a1ccb9cf63de046965df Mon Sep 17 00:00:00 2001 From: Adam Shanks Date: Mon, 4 Jun 2012 20:17:44 +0100 Subject: [PATCH 60/71] Use am to send intents rather than a binder transaction. This commit is by koush and is mostly copied from the CyanogenMod fork of su. By using am instead of a binder transaction, we can guarantee compatibility with current and future versions of Android. --- activity.c | 76 +++++++++++++++++++++++++++ activity.cpp | 142 --------------------------------------------------- su.c | 11 ---- su.h | 1 - 4 files changed, 76 insertions(+), 154 deletions(-) create mode 100644 activity.c delete mode 100644 activity.cpp diff --git a/activity.c b/activity.c new file mode 100644 index 0000000..10649f1 --- /dev/null +++ b/activity.c @@ -0,0 +1,76 @@ +/* +** 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 "su.h" + +int send_intent(const struct su_context *ctx, + const char *socket_path, int allow, const char *action) +{ + char command[PATH_MAX]; + + sprintf(command, "/system/bin/am broadcast -a %s --es socket %s --ei version_code %d > /dev/null", + action, socket_path, VERSION_CODE); + + // before sending the intent, make sure the (uid and euid) and (gid and egid) match, + // otherwise LD_LIBRARY_PATH is wiped in Android 4.0+. + // Also, sanitize all secure environment variables (from linker_environ.c in linker). + + /* The same list than GLibc at this point */ + static const char* const unsec_vars[] = { + "GCONV_PATH", + "GETCONF_DIR", + "HOSTALIASES", + "LD_AUDIT", + "LD_DEBUG", + "LD_DEBUG_OUTPUT", + "LD_DYNAMIC_WEAK", + "LD_LIBRARY_PATH", + "LD_ORIGIN_PATH", + "LD_PRELOAD", + "LD_PROFILE", + "LD_SHOW_AUXV", + "LD_USE_LOAD_BIAS", + "LOCALDOMAIN", + "LOCPATH", + "MALLOC_TRACE", + "MALLOC_CHECK_", + "NIS_PATH", + "NLSPATH", + "RESOLV_HOST_CONF", + "RES_OPTIONS", + "TMPDIR", + "TZDIR", + "LD_AOUT_LIBRARY_PATH", + "LD_AOUT_PRELOAD", + // not listed in linker, used due to system() call + "IFS", + }; + const char* const* cp = unsec_vars; + const char* const* endp = cp + sizeof(unsec_vars)/sizeof(unsec_vars[0]); + while (cp < endp) { + unsetenv(*cp); + cp++; + } + + // sane value so "am" works + setenv("LD_LIBRARY_PATH", "/vendor/lib:/system/lib", 1); + setegid(getgid()); + seteuid(getuid()); + return system(command); +} diff --git a/activity.cpp b/activity.cpp deleted file mode 100644 index 9f3f930..0000000 --- a/activity.cpp +++ /dev/null @@ -1,142 +0,0 @@ -/* -** 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 -#ifdef SU_LEGACY_BUILD -#include -#include -#include -#else -#include -#include -#include -#endif -#include -#include - -extern "C" { -#include "su.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(const struct su_context *ctx, - const char *socket_path, int allow, const char *action) -{ - sp sm = defaultServiceManager(); - sp am = sm->checkService(String16("activity")); - assert(am != NULL); - - Parcel data, reply; - data.writeInterfaceToken(String16("android.app.IActivityManager")); - - data.writeStrongBinder(NULL); /* caller */ - - /* intent */ - data.writeString16(String16(action)); /* action */ - data.writeInt32(NULL_TYPE_ID); /* Uri - data */ - data.writeString16(NULL, 0); /* type */ - data.writeInt32(0); /* flags */ - if (ctx->sdk_version >= 4) { - // added in donut - data.writeString16(NULL, 0); /* package name - DONUT ONLY, NOT IN CUPCAKE. */ - } - data.writeString16(NULL, 0); /* ComponentName - package */ - if (ctx->sdk_version >= 7) { - // added in eclair rev 7 - data.writeInt32(0); /* Rect - the bounds of the sender */ - } - data.writeInt32(0); /* Categories - size */ - if (ctx->sdk_version >= 15) { - // added in IceCreamSandwich 4.0.3 - data.writeInt32(0); /* Selector */ - } - { /* Extras */ - data.writeInt32(-1); /* dummy, will hold length */ - data.writeInt32(0x4C444E42); // 'B' 'N' 'D' 'L' - int oldPos = data.dataPosition(); - { /* writeMapInternal */ - data.writeInt32(7); /* writeMapInternal - size */ - - data.writeInt32(VAL_STRING); - data.writeString16(String16("caller_uid")); - data.writeInt32(VAL_INTEGER); - data.writeInt32(ctx->from.uid); - - data.writeInt32(VAL_STRING); - data.writeString16(String16("caller_bin")); - data.writeInt32(VAL_STRING); - data.writeString16(String16(ctx->from.bin)); - - data.writeInt32(VAL_STRING); - data.writeString16(String16("desired_uid")); - data.writeInt32(VAL_INTEGER); - data.writeInt32(ctx->to.uid); - - data.writeInt32(VAL_STRING); - data.writeString16(String16("desired_cmd")); - data.writeInt32(VAL_STRING); - data.writeString16(String16(get_command(&ctx->to))); - - 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 - 8); - data.writeInt32(newPos - oldPos); /* length */ - data.setDataPosition(newPos); - } - - data.writeString16(NULL, 0); /* resolvedType */ - - 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 */ - - status_t ret = am->transact(BROADCAST_INTENT_TRANSACTION, data, &reply); - if (ret < START_SUCCESS) return -1; - - return 0; -} diff --git a/su.c b/su.c index 71e008f..993e017 100644 --- a/su.c +++ b/su.c @@ -42,15 +42,6 @@ /* Still lazt, will fix this */ static char socket_path[PATH_MAX]; - -static inline int get_sdk_version(void) -{ - char sdk_version_prop[PROPERTY_VALUE_MAX]; - - property_get("ro.build.version.sdk", sdk_version_prop, "0"); - return atoi(sdk_version_prop); -} - static int from_init(struct su_initiator *from) { char path[PATH_MAX], exe[PATH_MAX]; @@ -455,8 +446,6 @@ int main(int argc, char *argv[]) } ctx.to.optind = optind; - ctx.sdk_version = get_sdk_version(); - if (from_init(&ctx.from) < 0) { deny(&ctx); } diff --git a/su.h b/su.h index 7f215c7..e0bc3c8 100644 --- a/su.h +++ b/su.h @@ -70,7 +70,6 @@ struct su_context { struct su_initiator from; struct su_request to; mode_t umask; - int sdk_version; }; enum { From d86184a90e78016fd66f558cd12d54a979b7fb46 Mon Sep 17 00:00:00 2001 From: Adam Shanks Date: Mon, 4 Jun 2012 20:23:36 +0100 Subject: [PATCH 61/71] Build su as static. With so many different versions of Android, using a dynamically linked binary is just not practical, since things can be wildly different on different versions. --- Android.mk | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/Android.mk b/Android.mk index 8a43c01..eb32dc1 100644 --- a/Android.mk +++ b/Android.mk @@ -2,21 +2,17 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := su -LOCAL_SRC_FILES := su.c db.c activity.cpp - -SU_SHARED_LIBRARIES := liblog libsqlite -ifeq ($(PLATFORM_SDK_VERSION),4) - LOCAL_CFLAGS += -DSU_LEGACY_BUILD - SU_SHARED_LIBRARIES += libandroid_runtime -else - SU_SHARED_LIBRARIES += libcutils libbinder libutils - LOCAL_MODULE_TAGS := debug,eng -endif +LOCAL_SRC_FILES := su.c db.c activity.c LOCAL_C_INCLUDES += external/sqlite/dist -LOCAL_SHARED_LIBRARIES := $(SU_SHARED_LIBRARIES) +LOCAL_STATIC_LIBRARIES := \ + liblog \ + libsqlite \ + libc \ LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) +LOCAL_MODULE_TAGS := eng debug +LOCAL_FORCE_STATIC_EXECUTABLE := true -include $(BUILD_EXECUTABLE) +include $(BUILD_EXECUTABLE) \ No newline at end of file From 8bf97db843429917c5d72f94c5213d2fe00b3e3d Mon Sep 17 00:00:00 2001 From: Adam Shanks Date: Mon, 4 Jun 2012 21:26:30 +0100 Subject: [PATCH 62/71] Send information required for the result service in the intent --- activity.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/activity.c b/activity.c index 10649f1..fb2c79c 100644 --- a/activity.c +++ b/activity.c @@ -24,8 +24,8 @@ int send_intent(const struct su_context *ctx, { char command[PATH_MAX]; - sprintf(command, "/system/bin/am broadcast -a %s --es socket %s --ei version_code %d > /dev/null", - action, socket_path, VERSION_CODE); + sprintf(command, "/system/bin/am broadcast -a '%s' --es socket '%s' --ei caller_uid '%d' --ei allow '%d' --ei version_code '%d' > /dev/null", + action, socket_path, ctx->from.uid, allow, VERSION_CODE); // before sending the intent, make sure the (uid and euid) and (gid and egid) match, // otherwise LD_LIBRARY_PATH is wiped in Android 4.0+. From 538413f2b709edceb62943222e07e9a9dce75163 Mon Sep 17 00:00:00 2001 From: Adam Shanks Date: Sun, 1 Jul 2012 02:30:25 +0100 Subject: [PATCH 63/71] CM specific changes to allow disabling of root access from system settings --- Android.mk | 2 +- su.c | 53 +++++++++++++++++++++++++-- su.h | 4 +-- utils.c | 103 +++++++++++++++++++++++++++++++++++++++++++++++++++++ utils.h | 24 +++++++++++++ 5 files changed, 181 insertions(+), 5 deletions(-) create mode 100644 utils.c create mode 100644 utils.h diff --git a/Android.mk b/Android.mk index eb32dc1..0f14405 100644 --- a/Android.mk +++ b/Android.mk @@ -2,7 +2,7 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := su -LOCAL_SRC_FILES := su.c db.c activity.c +LOCAL_SRC_FILES := su.c db.c activity.c utils.c LOCAL_C_INCLUDES += external/sqlite/dist diff --git a/su.c b/su.c index 993e017..d36eaed 100644 --- a/su.c +++ b/su.c @@ -38,6 +38,7 @@ #include #include "su.h" +#include "utils.h" /* Still lazt, will fix this */ static char socket_path[PATH_MAX]; @@ -375,8 +376,10 @@ int main(int argc, char *argv[]) }; struct stat st; int socket_serv_fd, fd; - char buf[64], *result; - int c, dballow; + char buf[64], *result, debuggable[PROPERTY_VALUE_MAX]; + char enabled[PROPERTY_VALUE_MAX], build_type[PROPERTY_VALUE_MAX]; + char cm_version[PROPERTY_VALUE_MAX];; + int c, dballow, len; struct option long_opts[] = { { "command", required_argument, NULL, 'c' }, { "help", no_argument, NULL, 'h' }, @@ -386,6 +389,8 @@ int main(int argc, char *argv[]) { "version", no_argument, NULL, 'v' }, { NULL, 0, NULL, 0 }, }; + char *data; + unsigned sz; while ((c = getopt_long(argc, argv, "+c:hlmps:Vv", long_opts, NULL)) != -1) { switch(c) { @@ -450,8 +455,52 @@ int main(int argc, char *argv[]) deny(&ctx); } + // we can't simply use the property service, since we aren't launched from init and + // can't trust the location of the property workspace. find the properties ourselves. + data = read_file("/default.prop", &sz); + get_property(data, debuggable, "ro.debuggable", "0"); + free(data); + + data = read_file("/system/build.prop", &sz); + get_property(data, cm_version, "ro.cm.version", ""); + get_property(data, build_type, "ro.build.type", ""); + free(data); + + data = read_file("/data/property/persist.sys.root_access", &sz); + if (data != NULL) { + len = strlen(data); + if (len >= PROPERTY_VALUE_MAX) + memcpy(enabled, "1", 2); + else + memcpy(enabled, data, len + 1); + free(data); + } else + memcpy(enabled, "1", 2); + ctx.umask = umask(027); + // CyanogenMod-specific behavior + if (strlen(cm_version) > 0) { + // only allow su on debuggable builds + if (strcmp("1", debuggable) != 0) { + LOGE("Root access is disabled on non-debug builds"); + deny(&ctx); + } + + // enforce persist.sys.root_access on non-eng builds + if (strcmp("eng", build_type) != 0 && + (atoi(enabled) & 1) != 1 ) { + LOGE("Root access is disabled by system setting - enable it under settings -> developer options"); + deny(&ctx); + } + + // disallow su in a shell if appropriate + if (ctx.from.uid == AID_SHELL && (atoi(enabled) == 1)) { + LOGE("Root access is disabled by a system setting - enable it under settings -> developer options"); + deny(&ctx); + } + } + if (ctx.from.uid == AID_ROOT || ctx.from.uid == AID_SHELL) allow(&ctx); diff --git a/su.h b/su.h index e0bc3c8..8dc93d1 100644 --- a/su.h +++ b/su.h @@ -42,8 +42,8 @@ #define VERSION_EXTRA "" #endif -#define VERSION "3.1" VERSION_EXTRA -#define VERSION_CODE 16 +#define VERSION "3.1.1" VERSION_EXTRA +#define VERSION_CODE 17 #define DATABASE_VERSION 6 #define PROTO_VERSION 0 diff --git a/utils.c b/utils.c new file mode 100644 index 0000000..ec6444b --- /dev/null +++ b/utils.c @@ -0,0 +1,103 @@ +/* +** Copyright 2012, The CyanogenMod 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 +** +** 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 + +#include +#include +#include +#include + +/* reads a file, making sure it is terminated with \n \0 */ +char* read_file(const char *fn, unsigned *_sz) +{ + char *data; + int sz; + int fd; + + data = 0; + fd = open(fn, O_RDONLY); + if(fd < 0) return 0; + + sz = lseek(fd, 0, SEEK_END); + if(sz < 0) goto oops; + + if(lseek(fd, 0, SEEK_SET) != 0) goto oops; + + data = (char*) malloc(sz + 2); + if(data == 0) goto oops; + + if(read(fd, data, sz) != sz) goto oops; + close(fd); + data[sz] = '\n'; + data[sz+1] = 0; + if(_sz) *_sz = sz; + return data; + +oops: + close(fd); + if(data != 0) free(data); + return 0; +} + +int get_property(const char *data, char *found, const char *searchkey, const char *not_found) +{ + char *key, *value, *eol, *sol, *tmp; + if (data == NULL) goto defval; + int matched = 0; + sol = strdup(data); + while((eol = strchr(sol, '\n'))) { + key = sol; + *eol++ = 0; + sol = eol; + + value = strchr(key, '='); + if(value == 0) continue; + *value++ = 0; + + while(isspace(*key)) key++; + if(*key == '#') continue; + tmp = value - 2; + while((tmp > key) && isspace(*tmp)) *tmp-- = 0; + + while(isspace(*value)) value++; + tmp = eol - 2; + while((tmp > value) && isspace(*tmp)) *tmp-- = 0; + + if (strncmp(searchkey, key, strlen(searchkey)) == 0) { + matched = 1; + break; + } + } + int len; + if (matched) { + len = strlen(value); + if (len >= PROPERTY_VALUE_MAX) + return -1; + memcpy(found, value, len + 1); + } else goto defval; + return len; + +defval: + len = strlen(not_found); + memcpy(found, not_found, len + 1); + return len; +} \ No newline at end of file diff --git a/utils.h b/utils.h new file mode 100644 index 0000000..92e1ace --- /dev/null +++ b/utils.h @@ -0,0 +1,24 @@ +/* +** Copyright 2012, The CyanogenMod 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 +** +** 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 _UTILS_H_ +#define _UTILS_H_ + +/* reads a file, making sure it is terminated with \n \0 */ +char* read_file(const char *fn, unsigned *_sz); + +int get_property(const char *data, char *found, const char *searchkey, const char *not_found); +#endif \ No newline at end of file From ec37525297010b47d4109e7971c6de0b9040f847 Mon Sep 17 00:00:00 2001 From: Adam Shanks Date: Sat, 7 Jul 2012 21:43:39 +0100 Subject: [PATCH 64/71] Use flat files for stored apps --- Android.mk | 3 -- db.c | 105 ++++++++++++++--------------------------------------- su.h | 6 +-- 3 files changed, 31 insertions(+), 83 deletions(-) diff --git a/Android.mk b/Android.mk index 0f14405..7bd51bb 100644 --- a/Android.mk +++ b/Android.mk @@ -4,11 +4,8 @@ include $(CLEAR_VARS) LOCAL_MODULE := su LOCAL_SRC_FILES := su.c db.c activity.c utils.c -LOCAL_C_INCLUDES += external/sqlite/dist - LOCAL_STATIC_LIBRARIES := \ liblog \ - libsqlite \ libc \ LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) diff --git a/db.c b/db.c index f570d2e..52f9673 100644 --- a/db.c +++ b/db.c @@ -15,93 +15,44 @@ */ #include -#include +#include #include #include -#include - #include "su.h" -static sqlite3 *db_init(void) -{ - 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; -} - -static int db_check(sqlite3 *db, const struct su_context *ctx) +int database_check(const struct su_context *ctx) { - 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';", - ctx->from.uid, ctx->to.uid, get_command(&ctx->to) - ); - - 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"); + FILE *fp; + char allow = '-'; + char *filename = malloc(snprintf(NULL, 0, "%s/%u-%u", REQUESTOR_STORED_PATH, ctx->from.uid, ctx->to.uid) + 1); + sprintf(filename, "%s/%u-%u", REQUESTOR_STORED_PATH, ctx->from.uid, ctx->to.uid); + if ((fp = fopen(filename, "r"))) { + LOGD("Found file"); + char cmd[PATH_MAX]; + fgets(cmd, sizeof(cmd), fp); + int last = strlen(cmd) - 1; + LOGD("this is the last character %u of the string", cmd[5]); + if (cmd[last] == '\n') { + cmd[last] = '\0'; } - 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; + LOGD("Comparing %c %s, %u to %s", cmd[last - 2], cmd, last, get_command(&ctx->to)); + if (strcmp(cmd, get_command(&ctx->to)) == 0) { + allow = fgetc(fp); } + fclose(fp); + } else if ((fp = fopen(REQUESTOR_STORED_DEFAULT, "r"))) { + LOGD("Using default"); + allow = fgetc(fp); + fclose(fp); } + free(filename); -out: - sqlite3_free_table(result); - - return allow; -} - -int database_check(const struct su_context *ctx) -{ - sqlite3 *db; - int dballow; - - LOGE("sudb - Opening database"); - db = db_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 + if (allow == '1') { + return DB_ALLOW; + } else if (allow == '0') { + return DB_DENY; + } else { return DB_INTERACTIVE; } - - LOGE("sudb - Database opened"); - dballow = db_check(db, ctx); - // Close the database, we're done with it. If it stays open, it will cause problems - sqlite3_close(db); - LOGE("sudb - Database closed"); - return dballow; } diff --git a/su.h b/su.h index 8dc93d1..53bca8b 100644 --- a/su.h +++ b/su.h @@ -25,10 +25,10 @@ #define REQUESTOR "com.noshufou.android.su" #define REQUESTOR_DATA_PATH "/data/data/" REQUESTOR -#define REQUESTOR_CACHE_PATH "/dev/" REQUESTOR +#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 REQUESTOR_STORED_PATH REQUESTOR_DATA_PATH "/files/stored" +#define REQUESTOR_STORED_DEFAULT REQUESTOR_STORED_PATH "/default" /* intent actions */ #define ACTION_REQUEST REQUESTOR ".REQUEST" From ff77b4978a421265c62e12f9551f3e101894705e Mon Sep 17 00:00:00 2001 From: Adam Shanks Date: Sat, 7 Jul 2012 21:43:56 +0100 Subject: [PATCH 65/71] Update version --- su.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/su.h b/su.h index 53bca8b..4cc5485 100644 --- a/su.h +++ b/su.h @@ -42,8 +42,8 @@ #define VERSION_EXTRA "" #endif -#define VERSION "3.1.1" VERSION_EXTRA -#define VERSION_CODE 17 +#define VERSION "3.2" VERSION_EXTRA +#define VERSION_CODE 18 #define DATABASE_VERSION 6 #define PROTO_VERSION 0 From 15ea3553bfc2a2d8eb1ce25d45dd4033807481ce Mon Sep 17 00:00:00 2001 From: Adam Shanks Date: Sun, 8 Jul 2012 07:49:57 +0100 Subject: [PATCH 66/71] Revert "Update version" This reverts commit ff77b4978a421265c62e12f9551f3e101894705e. --- su.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/su.h b/su.h index 4cc5485..53bca8b 100644 --- a/su.h +++ b/su.h @@ -42,8 +42,8 @@ #define VERSION_EXTRA "" #endif -#define VERSION "3.2" VERSION_EXTRA -#define VERSION_CODE 18 +#define VERSION "3.1.1" VERSION_EXTRA +#define VERSION_CODE 17 #define DATABASE_VERSION 6 #define PROTO_VERSION 0 From 588c414d4a99f60010dcc7d4c31d8410c5028aac Mon Sep 17 00:00:00 2001 From: Adam Shanks Date: Sun, 8 Jul 2012 07:49:57 +0100 Subject: [PATCH 67/71] Revert "Use flat files for stored apps" This reverts commit ec37525297010b47d4109e7971c6de0b9040f847. --- Android.mk | 3 ++ db.c | 105 +++++++++++++++++++++++++++++++++++++++-------------- su.h | 6 +-- 3 files changed, 83 insertions(+), 31 deletions(-) diff --git a/Android.mk b/Android.mk index 7bd51bb..0f14405 100644 --- a/Android.mk +++ b/Android.mk @@ -4,8 +4,11 @@ include $(CLEAR_VARS) LOCAL_MODULE := su LOCAL_SRC_FILES := su.c db.c activity.c utils.c +LOCAL_C_INCLUDES += external/sqlite/dist + LOCAL_STATIC_LIBRARIES := \ liblog \ + libsqlite \ libc \ LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) diff --git a/db.c b/db.c index 52f9673..f570d2e 100644 --- a/db.c +++ b/db.c @@ -15,44 +15,93 @@ */ #include -#include +#include #include #include +#include + #include "su.h" -int database_check(const struct su_context *ctx) +static sqlite3 *db_init(void) +{ + 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; +} + +static int db_check(sqlite3 *db, const struct su_context *ctx) { - FILE *fp; - char allow = '-'; - char *filename = malloc(snprintf(NULL, 0, "%s/%u-%u", REQUESTOR_STORED_PATH, ctx->from.uid, ctx->to.uid) + 1); - sprintf(filename, "%s/%u-%u", REQUESTOR_STORED_PATH, ctx->from.uid, ctx->to.uid); - if ((fp = fopen(filename, "r"))) { - LOGD("Found file"); - char cmd[PATH_MAX]; - fgets(cmd, sizeof(cmd), fp); - int last = strlen(cmd) - 1; - LOGD("this is the last character %u of the string", cmd[5]); - if (cmd[last] == '\n') { - cmd[last] = '\0'; + 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';", + ctx->from.uid, ctx->to.uid, get_command(&ctx->to) + ); + + 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"); } - LOGD("Comparing %c %s, %u to %s", cmd[last - 2], cmd, last, get_command(&ctx->to)); - if (strcmp(cmd, get_command(&ctx->to)) == 0) { - allow = fgetc(fp); + 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; } - fclose(fp); - } else if ((fp = fopen(REQUESTOR_STORED_DEFAULT, "r"))) { - LOGD("Using default"); - allow = fgetc(fp); - fclose(fp); } - free(filename); - if (allow == '1') { - return DB_ALLOW; - } else if (allow == '0') { - return DB_DENY; - } else { +out: + sqlite3_free_table(result); + + return allow; +} + +int database_check(const struct su_context *ctx) +{ + sqlite3 *db; + int dballow; + + LOGE("sudb - Opening database"); + db = db_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 return DB_INTERACTIVE; } + + LOGE("sudb - Database opened"); + dballow = db_check(db, ctx); + // Close the database, we're done with it. If it stays open, it will cause problems + sqlite3_close(db); + LOGE("sudb - Database closed"); + return dballow; } diff --git a/su.h b/su.h index 53bca8b..8dc93d1 100644 --- a/su.h +++ b/su.h @@ -25,10 +25,10 @@ #define REQUESTOR "com.noshufou.android.su" #define REQUESTOR_DATA_PATH "/data/data/" REQUESTOR -#define REQUESTOR_CACHE_PATH REQUESTOR_DATA_PATH "/cache" +#define REQUESTOR_CACHE_PATH "/dev/" REQUESTOR -#define REQUESTOR_STORED_PATH REQUESTOR_DATA_PATH "/files/stored" -#define REQUESTOR_STORED_DEFAULT REQUESTOR_STORED_PATH "/default" +#define REQUESTOR_DATABASES_PATH REQUESTOR_DATA_PATH "/databases" +#define REQUESTOR_DATABASE_PATH REQUESTOR_DATABASES_PATH "/permissions.sqlite" /* intent actions */ #define ACTION_REQUEST REQUESTOR ".REQUEST" From d74df1719920dcd4b56f44851cf6a1f0131de4d8 Mon Sep 17 00:00:00 2001 From: Adam Shanks Date: Sun, 8 Jul 2012 08:27:26 +0100 Subject: [PATCH 68/71] Revert "Revert "Use flat files for stored apps"" This reverts commit 588c414d4a99f60010dcc7d4c31d8410c5028aac. --- Android.mk | 3 -- db.c | 105 ++++++++++++++--------------------------------------- su.h | 6 +-- 3 files changed, 31 insertions(+), 83 deletions(-) diff --git a/Android.mk b/Android.mk index 0f14405..7bd51bb 100644 --- a/Android.mk +++ b/Android.mk @@ -4,11 +4,8 @@ include $(CLEAR_VARS) LOCAL_MODULE := su LOCAL_SRC_FILES := su.c db.c activity.c utils.c -LOCAL_C_INCLUDES += external/sqlite/dist - LOCAL_STATIC_LIBRARIES := \ liblog \ - libsqlite \ libc \ LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) diff --git a/db.c b/db.c index f570d2e..52f9673 100644 --- a/db.c +++ b/db.c @@ -15,93 +15,44 @@ */ #include -#include +#include #include #include -#include - #include "su.h" -static sqlite3 *db_init(void) -{ - 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; -} - -static int db_check(sqlite3 *db, const struct su_context *ctx) +int database_check(const struct su_context *ctx) { - 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';", - ctx->from.uid, ctx->to.uid, get_command(&ctx->to) - ); - - 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"); + FILE *fp; + char allow = '-'; + char *filename = malloc(snprintf(NULL, 0, "%s/%u-%u", REQUESTOR_STORED_PATH, ctx->from.uid, ctx->to.uid) + 1); + sprintf(filename, "%s/%u-%u", REQUESTOR_STORED_PATH, ctx->from.uid, ctx->to.uid); + if ((fp = fopen(filename, "r"))) { + LOGD("Found file"); + char cmd[PATH_MAX]; + fgets(cmd, sizeof(cmd), fp); + int last = strlen(cmd) - 1; + LOGD("this is the last character %u of the string", cmd[5]); + if (cmd[last] == '\n') { + cmd[last] = '\0'; } - 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; + LOGD("Comparing %c %s, %u to %s", cmd[last - 2], cmd, last, get_command(&ctx->to)); + if (strcmp(cmd, get_command(&ctx->to)) == 0) { + allow = fgetc(fp); } + fclose(fp); + } else if ((fp = fopen(REQUESTOR_STORED_DEFAULT, "r"))) { + LOGD("Using default"); + allow = fgetc(fp); + fclose(fp); } + free(filename); -out: - sqlite3_free_table(result); - - return allow; -} - -int database_check(const struct su_context *ctx) -{ - sqlite3 *db; - int dballow; - - LOGE("sudb - Opening database"); - db = db_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 + if (allow == '1') { + return DB_ALLOW; + } else if (allow == '0') { + return DB_DENY; + } else { return DB_INTERACTIVE; } - - LOGE("sudb - Database opened"); - dballow = db_check(db, ctx); - // Close the database, we're done with it. If it stays open, it will cause problems - sqlite3_close(db); - LOGE("sudb - Database closed"); - return dballow; } diff --git a/su.h b/su.h index 8dc93d1..53bca8b 100644 --- a/su.h +++ b/su.h @@ -25,10 +25,10 @@ #define REQUESTOR "com.noshufou.android.su" #define REQUESTOR_DATA_PATH "/data/data/" REQUESTOR -#define REQUESTOR_CACHE_PATH "/dev/" REQUESTOR +#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 REQUESTOR_STORED_PATH REQUESTOR_DATA_PATH "/files/stored" +#define REQUESTOR_STORED_DEFAULT REQUESTOR_STORED_PATH "/default" /* intent actions */ #define ACTION_REQUEST REQUESTOR ".REQUEST" From f215759188d086177c512c20340a5e93259c3eb9 Mon Sep 17 00:00:00 2001 From: Adam Shanks Date: Sun, 8 Jul 2012 08:27:34 +0100 Subject: [PATCH 69/71] Revert "Revert "Update version"" This reverts commit 15ea3553bfc2a2d8eb1ce25d45dd4033807481ce. --- su.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/su.h b/su.h index 53bca8b..4cc5485 100644 --- a/su.h +++ b/su.h @@ -42,8 +42,8 @@ #define VERSION_EXTRA "" #endif -#define VERSION "3.1.1" VERSION_EXTRA -#define VERSION_CODE 17 +#define VERSION "3.2" VERSION_EXTRA +#define VERSION_CODE 18 #define DATABASE_VERSION 6 #define PROTO_VERSION 0 From 4baab01407630c4d8462d99d021780d5aa71be11 Mon Sep 17 00:00:00 2001 From: Adam Shanks Date: Sun, 8 Jul 2012 09:07:32 +0100 Subject: [PATCH 70/71] Prevent compiler warnings --- activity.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/activity.c b/activity.c index fb2c79c..47fae4d 100644 --- a/activity.c +++ b/activity.c @@ -16,6 +16,8 @@ */ #include +#include +#include #include "su.h" From e40811c1e9ab12596995e41c42b0887ecb1205c9 Mon Sep 17 00:00:00 2001 From: shadiberck <143373925+shadiberck@users.noreply.github.com> Date: Mon, 15 Apr 2024 14:39:04 -0500 Subject: [PATCH 71/71] Update README --- README | 1 + 1 file changed, 1 insertion(+) diff --git a/README b/README index f1bc7a2..27d8f58 100644 --- a/README +++ b/README @@ -17,3 +17,4 @@ Branches for this repo support the following Android versions: Branches marked with '-dev' are in work and should probably not be used. I will push to those channels as I develop new versions so they may be broken from time to time. * I have yet to personally test superuser on any honeycomb devices, therefore I cannot guarantee it's compatibility +Run