From 1ea6c03c1e65a9d4e275354d50100dc959b19438 Mon Sep 17 00:00:00 2001 From: Chih-Wei Huang Date: Fri, 6 Jul 2012 14:56:29 +0800 Subject: [PATCH] su: update CyanogenMod's latest version Update to commit 5369a56c0e1ddd4d080b0d90db47921d0eded862 but disable static executable since we don't have libsqlite.a. --- su/Android.mk | 6 +-- su/activity.c | 79 ++++++++++++++++++++++++++++++ su/activity.cpp | 149 -------------------------------------------------------- su/su.c | 54 +++++++++++++++++++- su/utils.c | 104 +++++++++++++++++++++++++++++++++++++++ su/utils.h | 24 +++++++++ 6 files changed, 261 insertions(+), 155 deletions(-) create mode 100644 su/activity.c delete mode 100644 su/activity.cpp create mode 100644 su/utils.c create mode 100644 su/utils.h diff --git a/su/Android.mk b/su/Android.mk index d458a054..a5ec9c46 100644 --- a/su/Android.mk +++ b/su/Android.mk @@ -2,9 +2,10 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES := \ - activity.cpp \ + activity.c \ db.c \ su.c\ + utils.c \ LOCAL_MODULE := su @@ -13,9 +14,6 @@ LOCAL_C_INCLUDES += external/sqlite/dist LOCAL_SHARED_LIBRARIES := \ liblog \ libsqlite \ - libcutils \ - libbinder \ - libutils \ LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := optional diff --git a/su/activity.c b/su/activity.c new file mode 100644 index 00000000..ee12f871 --- /dev/null +++ b/su/activity.c @@ -0,0 +1,79 @@ +/* +** 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" +#include "utils.h" + +int send_intent(struct su_initiator *from, struct su_request *to, const char *socket_path, int allow, int type) +{ + char command[PATH_MAX]; + char action[PATH_MAX]; + if (type == 0) { + strcpy(action, "com.noshufou.android.su.REQUEST"); + } else { + strcpy(action, "com.noshufou.android.su.RESULT"); + } + sprintf(command, "/system/bin/am broadcast -a '%s' --es socket '%s' --ei caller_uid '%d' --es caller_bin '%s' --ei desired_uid '%d' --es desired_cmd '%s' --ei allow '%d' --ei version_code '%d' > /dev/null", action, socket_path, from->uid, from->bin, to->uid, to->command, 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+. + // 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); + setresgid(0, 0, 0); + setresuid(0, 0, 0); + return system(command); +} diff --git a/su/activity.cpp b/su/activity.cpp deleted file mode 100644 index f58c7d0e..00000000 --- a/su/activity.cpp +++ /dev/null @@ -1,149 +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 -#include -#include -#include -#include -#include - -extern "C" { -#include "su.h" -#include -#include -} - -using namespace android; - -static const int BROADCAST_INTENT_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION + 13; - -static const int NULL_TYPE_ID = 0; - -static const int VAL_STRING = 0; -static const int VAL_INTEGER = 1; - -static const int START_SUCCESS = 0; - -int send_intent(struct su_initiator *from, struct su_request *to, const char *socket_path, int allow, int type) -{ - char sdk_version_prop[PROPERTY_VALUE_MAX] = "0"; - property_get("ro.build.version.sdk", sdk_version_prop, "0"); - - int sdk_version = atoi(sdk_version_prop); - - sp 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 */ - if (type == 0) { - data.writeString16(String16("com.noshufou.android.su.REQUEST")); /* action */ - } else { - data.writeString16(String16("com.noshufou.android.su.RESULT")); /* action */ - } - data.writeInt32(NULL_TYPE_ID); /* Uri - data */ - data.writeString16(NULL, 0); /* type */ - data.writeInt32(0); /* flags */ - if (sdk_version >= 4) { - // added in donut - data.writeString16(NULL, 0); /* package name - DONUT ONLY, NOT IN CUPCAKE. */ - } - data.writeString16(NULL, 0); /* ComponentName - package */ - data.writeInt32(0); /* Categories - size */ - if (sdk_version >= 7) { - // added in eclair rev 7 - data.writeInt32(0); - } - if (sdk_version >= 15) { - // added in IceCreamSandwich 4.0.3 - data.writeInt32(0); /* Selector */ - } - { /* Extras */ - data.writeInt32(-1); /* dummy, will hold length */ - int oldPos = data.dataPosition(); - data.writeInt32(0x4C444E42); // 'B' 'N' 'D' 'L' - { /* writeMapInternal */ - data.writeInt32(7); /* writeMapInternal - size */ - - data.writeInt32(VAL_STRING); - data.writeString16(String16("caller_uid")); - data.writeInt32(VAL_INTEGER); - data.writeInt32(from->uid); - - data.writeInt32(VAL_STRING); - data.writeString16(String16("caller_bin")); - data.writeInt32(VAL_STRING); - data.writeString16(String16(from->bin)); - - data.writeInt32(VAL_STRING); - data.writeString16(String16("desired_uid")); - data.writeInt32(VAL_INTEGER); - data.writeInt32(to->uid); - - data.writeInt32(VAL_STRING); - data.writeString16(String16("desired_cmd")); - data.writeInt32(VAL_STRING); - data.writeString16(String16(to->command)); - - data.writeInt32(VAL_STRING); - data.writeString16(String16("socket")); - data.writeInt32(VAL_STRING); - data.writeString16(String16(socket_path)); - - data.writeInt32(VAL_STRING); - data.writeString16(String16("allow")); - data.writeInt32(VAL_INTEGER); - data.writeInt32(allow); - - data.writeInt32(VAL_STRING); - data.writeString16(String16("version_code")); - data.writeInt32(VAL_INTEGER); - data.writeInt32(VERSION_CODE); - } - int newPos = data.dataPosition(); - data.setDataPosition(oldPos - 4); - data.writeInt32(newPos - oldPos); /* length */ - data.setDataPosition(newPos); - } - - data.writeString16(NULL, 0); /* resolvedType */ - - data.writeInt32(-1); /* Not sure what this is for, but it prevents a warning */ - - data.writeStrongBinder(NULL); /* resultTo */ - data.writeInt32(-1); /* resultCode */ - data.writeString16(NULL, 0); /* resultData */ - - data.writeInt32(-1); /* resultExtras */ - - data.writeString16(String16("com.noshufou.android.su.RESPOND")); /* perm */ - data.writeInt32(0); /* serialized */ - data.writeInt32(0); /* sticky */ - data.writeInt32(-1); - - status_t ret = am->transact(BROADCAST_INTENT_TRANSACTION, data, &reply); - if (ret < START_SUCCESS) return -1; - - return 0; -} diff --git a/su/su.c b/su/su.c index f4b35f84..773178c4 100644 --- a/su/su.c +++ b/su/su.c @@ -38,9 +38,11 @@ #include #include +#include #include +#include "utils.h" #include "su.h" //extern char* _mktemp(char*); /* mktemp doesn't link right. Don't ask me why. */ @@ -294,9 +296,13 @@ int main(int argc, char *argv[]) { struct stat st; static int socket_serv_fd = -1; - char buf[64], shell[PATH_MAX], *result; - int i, dballow; + char buf[64], shell[PATH_MAX], *result, debuggable[PROPERTY_VALUE_MAX]; + char enabled[PROPERTY_VALUE_MAX], build_type[PROPERTY_VALUE_MAX]; + char cm_version[PROPERTY_VALUE_MAX]; + int i, dballow, len; mode_t orig_umask; + char *data; + unsigned sz; for (i = 1; i < argc; i++) { if (!strcmp(argv[i], "-c") || !strcmp(argv[i], "--command")) { @@ -345,8 +351,52 @@ int main(int argc, char *argv[]) deny(); } + // 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); + orig_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(); + } + + // 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(); + } + + // disallow su in a shell if appropriate + if (su_from.uid == AID_SHELL && (atoi(enabled) == 1)) { + LOGE("Root access is disabled by a system setting - enable it under settings -> developer options"); + deny(); + } + } + if (su_from.uid == AID_ROOT || su_from.uid == AID_SHELL) allow(shell, orig_umask); diff --git a/su/utils.c b/su/utils.c new file mode 100644 index 00000000..54056e07 --- /dev/null +++ b/su/utils.c @@ -0,0 +1,104 @@ +/* +** 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; +} + diff --git a/su/utils.h b/su/utils.h new file mode 100644 index 00000000..a329123d --- /dev/null +++ b/su/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 -- 2.11.0