include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
- activity.cpp \
+ activity.c \
db.c \
su.c\
+ utils.c \
LOCAL_MODULE := su
LOCAL_SHARED_LIBRARIES := \
liblog \
libsqlite \
- libcutils \
- libbinder \
- libutils \
LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
LOCAL_MODULE_TAGS := optional
--- /dev/null
+/*
+** Copyright 2010, Adam Shanks (@ChainsDD)
+** Copyright 2008, Zinx Verituse (@zinxv)
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <unistd.h>
+
+#include "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);
+}
+++ /dev/null
-/*
-** Copyright 2010, Adam Shanks (@ChainsDD)
-** Copyright 2008, Zinx Verituse (@zinxv)
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#include <unistd.h>
-#include <android_runtime/ActivityManager.h>
-#include <binder/IBinder.h>
-#include <binder/IServiceManager.h>
-#include <binder/Parcel.h>
-#include <utils/String8.h>
-#include <assert.h>
-
-extern "C" {
-#include "su.h"
-#include <private/android_filesystem_config.h>
-#include <cutils/properties.h>
-}
-
-using namespace android;
-
-static const int BROADCAST_INTENT_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION + 13;
-
-static const int NULL_TYPE_ID = 0;
-
-static const int VAL_STRING = 0;
-static const int VAL_INTEGER = 1;
-
-static const int START_SUCCESS = 0;
-
-int send_intent(struct su_initiator *from, struct su_request *to, const char *socket_path, int allow, int type)
-{
- char sdk_version_prop[PROPERTY_VALUE_MAX] = "0";
- property_get("ro.build.version.sdk", sdk_version_prop, "0");
-
- int sdk_version = atoi(sdk_version_prop);
-
- sp<IServiceManager> sm = defaultServiceManager();
- sp<IBinder> am = sm->checkService(String16("activity"));
- assert(am != NULL);
-
- Parcel data, reply;
- data.writeInterfaceToken(String16("android.app.IActivityManager"));
-
- data.writeStrongBinder(NULL); /* caller */
-
- /* intent */
- if (type == 0) {
- data.writeString16(String16("com.noshufou.android.su.REQUEST")); /* action */
- } else {
- data.writeString16(String16("com.noshufou.android.su.RESULT")); /* action */
- }
- data.writeInt32(NULL_TYPE_ID); /* Uri - data */
- data.writeString16(NULL, 0); /* type */
- data.writeInt32(0); /* flags */
- if (sdk_version >= 4) {
- // added in donut
- data.writeString16(NULL, 0); /* package name - DONUT ONLY, NOT IN CUPCAKE. */
- }
- data.writeString16(NULL, 0); /* ComponentName - package */
- data.writeInt32(0); /* Categories - size */
- if (sdk_version >= 7) {
- // added in eclair rev 7
- data.writeInt32(0);
- }
- if (sdk_version >= 15) {
- // added in IceCreamSandwich 4.0.3
- data.writeInt32(0); /* Selector */
- }
- { /* Extras */
- data.writeInt32(-1); /* dummy, will hold length */
- int oldPos = data.dataPosition();
- data.writeInt32(0x4C444E42); // 'B' 'N' 'D' 'L'
- { /* writeMapInternal */
- data.writeInt32(7); /* writeMapInternal - size */
-
- data.writeInt32(VAL_STRING);
- data.writeString16(String16("caller_uid"));
- data.writeInt32(VAL_INTEGER);
- data.writeInt32(from->uid);
-
- data.writeInt32(VAL_STRING);
- data.writeString16(String16("caller_bin"));
- data.writeInt32(VAL_STRING);
- data.writeString16(String16(from->bin));
-
- data.writeInt32(VAL_STRING);
- data.writeString16(String16("desired_uid"));
- data.writeInt32(VAL_INTEGER);
- data.writeInt32(to->uid);
-
- data.writeInt32(VAL_STRING);
- data.writeString16(String16("desired_cmd"));
- data.writeInt32(VAL_STRING);
- data.writeString16(String16(to->command));
-
- data.writeInt32(VAL_STRING);
- data.writeString16(String16("socket"));
- data.writeInt32(VAL_STRING);
- data.writeString16(String16(socket_path));
-
- data.writeInt32(VAL_STRING);
- data.writeString16(String16("allow"));
- data.writeInt32(VAL_INTEGER);
- data.writeInt32(allow);
-
- data.writeInt32(VAL_STRING);
- data.writeString16(String16("version_code"));
- data.writeInt32(VAL_INTEGER);
- data.writeInt32(VERSION_CODE);
- }
- int newPos = data.dataPosition();
- data.setDataPosition(oldPos - 4);
- data.writeInt32(newPos - oldPos); /* length */
- data.setDataPosition(newPos);
- }
-
- data.writeString16(NULL, 0); /* resolvedType */
-
- data.writeInt32(-1); /* Not sure what this is for, but it prevents a warning */
-
- data.writeStrongBinder(NULL); /* resultTo */
- data.writeInt32(-1); /* resultCode */
- data.writeString16(NULL, 0); /* resultData */
-
- data.writeInt32(-1); /* resultExtras */
-
- data.writeString16(String16("com.noshufou.android.su.RESPOND")); /* perm */
- data.writeInt32(0); /* serialized */
- data.writeInt32(0); /* sticky */
- data.writeInt32(-1);
-
- status_t ret = am->transact(BROADCAST_INTENT_TRANSACTION, data, &reply);
- if (ret < START_SUCCESS) return -1;
-
- return 0;
-}
#include <private/android_filesystem_config.h>
#include <cutils/log.h>
+#include <cutils/properties.h>
#include <sqlite3.h>
+#include "utils.h"
#include "su.h"
//extern char* _mktemp(char*); /* mktemp doesn't link right. Don't ask me why. */
{
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")) {
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);
--- /dev/null
+/*
+** 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 <unistd.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <endian.h>
+#include <ctype.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <cutils/properties.h>
+
+/* 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;
+}
+
--- /dev/null
+/*
+** 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