├── .gitignore ├── binder ├── pm-wrapper.h ├── appops-wrapper.cpp └── pm-wrapper.c ├── superuser.rc ├── LICENSE ├── Android.mk ├── utils.h ├── su.h ├── utils.c ├── pts.h ├── pts.c ├── su.c └── daemon.c /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | armeabi 3 | x86 4 | obj 5 | local.properties 6 | gen 7 | .DS_Store 8 | .settings 9 | libs 10 | -------------------------------------------------------------------------------- /binder/pm-wrapper.h: -------------------------------------------------------------------------------- 1 | #ifndef _HAS_PM_WRAPPER_H 2 | #define _HAS_PM_WRAPPER_H 3 | 4 | const char* resolve_package_name(int uid); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /superuser.rc: -------------------------------------------------------------------------------- 1 | # su daemon 2 | service su_daemon /system/xbin/su --daemon 3 | user root 4 | group root 5 | disabled 6 | seclabel u:r:sudaemon:s0 7 | 8 | on property:persist.sys.root_access=0 9 | stop su_daemon 10 | 11 | on property:persist.sys.root_access=2 12 | start su_daemon 13 | 14 | on property:persist.sys.root_access=1 15 | start su_daemon 16 | 17 | on property:persist.sys.root_access=3 18 | start su_daemon 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2013 Koushik Dutta (2013) 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /Android.mk: -------------------------------------------------------------------------------- 1 | # Root AOSP source makefile 2 | # su is built here, and 3 | LOCAL_PATH := $(call my-dir) 4 | 5 | include $(CLEAR_VARS) 6 | 7 | LOCAL_MODULE := su 8 | LOCAL_MODULE_TAGS := optional 9 | LOCAL_WHOLE_STATIC_LIBRARIES := libcutils libutils libbinder liblog 10 | LOCAL_SRC_FILES := su.c daemon.c utils.c pts.c 11 | LOCAL_SRC_FILES += binder/appops-wrapper.cpp binder/pm-wrapper.c 12 | LOCAL_CFLAGS += -Werror 13 | LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) 14 | 15 | LOCAL_INIT_RC := superuser.rc 16 | 17 | include $(BUILD_EXECUTABLE) 18 | 19 | SYMLINKS := $(addprefix $(TARGET_OUT)/bin/,su) 20 | $(SYMLINKS): 21 | @echo "Symlink: $@ -> /system/xbin/su" 22 | @mkdir -p $(dir $@) 23 | @rm -rf $@ 24 | $(hide) ln -sf ../xbin/su $@ 25 | 26 | # We need this so that the installed files could be picked up based on the 27 | # local module name 28 | ALL_MODULES.$(LOCAL_MODULE).INSTALLED := \ 29 | $(ALL_MODULES.$(LOCAL_MODULE).INSTALLED) $(SYMLINKS) 30 | 31 | -------------------------------------------------------------------------------- /binder/appops-wrapper.cpp: -------------------------------------------------------------------------------- 1 | #define LOG_TAG "su" 2 | 3 | #include 4 | #include 5 | 6 | using namespace android; 7 | 8 | extern "C" { 9 | 10 | int appops_start_op_su(int uid, const char *pkgName) { 11 | ALOGD("Checking whether app [uid:%d, pkgName: %s] is allowed to be root", uid, pkgName); 12 | AppOpsManager *ops = new AppOpsManager(); 13 | 14 | int mode = ops->startOp(AppOpsManager::OP_SU, uid, String16(pkgName)); 15 | 16 | switch (mode) { 17 | case AppOpsManager::MODE_ALLOWED: 18 | ALOGD("Privilege elevation allowed by appops"); 19 | return 0; 20 | default: 21 | ALOGD("Privilege elevation denied by appops"); 22 | return 1; 23 | } 24 | 25 | delete ops; 26 | } 27 | 28 | void appops_finish_op_su(int uid, const char *pkgName) { 29 | ALOGD("Finishing su operation for app [uid:%d, pkgName: %s]", uid, pkgName); 30 | AppOpsManager *ops = new AppOpsManager(); 31 | ops->finishOp(AppOpsManager::OP_SU, uid, String16(pkgName)); 32 | delete ops; 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** Copyright 2012, The CyanogenMod Project 3 | ** 4 | ** Licensed under the Apache License, Version 2.0 (the "License"); 5 | ** you may not use this file except in compliance with the License. 6 | ** You may obtain a copy of the License at 7 | ** 8 | ** http://www.apache.org/licenses/LICENSE-2.0 9 | ** 10 | ** Unless required by applicable law or agreed to in writing, software 11 | ** distributed under the License is distributed on an "AS IS" BASIS, 12 | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | ** See the License for the specific language governing permissions and 14 | ** limitations under the License. 15 | */ 16 | 17 | #ifndef _UTILS_H_ 18 | #define _UTILS_H_ 19 | 20 | #ifndef PROPERTY_VALUE_MAX 21 | #define PROPERTY_VALUE_MAX 92 22 | #endif 23 | 24 | /* reads a file, making sure it is terminated with \n \0 */ 25 | extern char* read_file(const char *fn); 26 | 27 | extern int get_property(const char *data, char *found, const char *searchkey, 28 | const char *not_found); 29 | extern int check_property(const char *data, const char *prefix); 30 | #endif 31 | -------------------------------------------------------------------------------- /binder/pm-wrapper.c: -------------------------------------------------------------------------------- 1 | #include "../utils.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define PACKAGE_LIST_PATH "/data/system/packages.list" 9 | #define PACKAGE_NAME_MAX_LEN (1<<16) 10 | 11 | /* Tries to resolve a package name from a uid via the packages list file. 12 | * 13 | * If there is no matching uid, it will return an empty string which can 14 | * be resolved by appops in some cases (i.e. apps with uid = 0, uid = AID_SHELL). 15 | * 16 | * Since packages may share UID, this function will return the first present 17 | * in packages.list. 18 | */ 19 | const char* resolve_package_name(int uid) { 20 | char *packages = read_file(PACKAGE_LIST_PATH); 21 | 22 | if (packages == NULL) { 23 | goto notfound; 24 | } 25 | 26 | char *p = packages; 27 | while (*p) { 28 | char *line_end = strstr(p, "\n"); 29 | if (line_end == NULL) 30 | break; 31 | 32 | char *token; 33 | char *pkgName = strtok_r(p, " ", &token); 34 | if (pkgName != NULL) { 35 | char *pkgUid = strtok_r(NULL, " ", &token); 36 | if (pkgUid != NULL) { 37 | char *endptr; 38 | errno = 0; 39 | int pkgUidInt = strtoul(pkgUid, &endptr, 10); 40 | if ((errno == 0 && endptr != NULL && !(*endptr)) && pkgUidInt == uid) 41 | return strdup(pkgName); 42 | } 43 | } 44 | p = ++line_end; 45 | } 46 | free(packages); 47 | 48 | notfound: 49 | return ""; 50 | } 51 | -------------------------------------------------------------------------------- /su.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** Copyright 2010, Adam Shanks (@ChainsDD) 3 | ** Copyright 2008, Zinx Verituse (@zinxv) 4 | ** 5 | ** Licensed under the Apache License, Version 2.0 (the "License"); 6 | ** you may not use this file except in compliance with the License. 7 | ** You may obtain a copy of the License at 8 | ** 9 | ** http://www.apache.org/licenses/LICENSE-2.0 10 | ** 11 | ** Unless required by applicable law or agreed to in writing, software 12 | ** distributed under the License is distributed on an "AS IS" BASIS, 13 | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | ** See the License for the specific language governing permissions and 15 | ** limitations under the License. 16 | */ 17 | 18 | #ifndef SU_h 19 | #define SU_h 1 20 | 21 | #ifdef LOG_TAG 22 | #undef LOG_TAG 23 | #endif 24 | #define LOG_TAG "su" 25 | 26 | // CyanogenMod-specific behavior 27 | #define CM_ROOT_ACCESS_DISABLED 0 28 | #define CM_ROOT_ACCESS_APPS_ONLY 1 29 | #define CM_ROOT_ACCESS_ADB_ONLY 2 30 | #define CM_ROOT_ACCESS_APPS_AND_ADB 3 31 | 32 | #define DAEMON_SOCKET_PATH "/dev/socket/su-daemon/" 33 | 34 | #define DEFAULT_SHELL "/system/bin/sh" 35 | 36 | #define xstr(a) str(a) 37 | #define str(a) #a 38 | 39 | #ifndef VERSION_CODE 40 | #define VERSION_CODE 16 41 | #endif 42 | #define VERSION xstr(VERSION_CODE) " cm-su" 43 | 44 | #define PROTO_VERSION 1 45 | 46 | struct su_initiator { 47 | pid_t pid; 48 | unsigned uid; 49 | unsigned user; 50 | char name[64]; 51 | char bin[PATH_MAX]; 52 | char args[4096]; 53 | }; 54 | 55 | struct su_request { 56 | unsigned uid; 57 | char name[64]; 58 | int login; 59 | int keepenv; 60 | char *shell; 61 | char *command; 62 | char **argv; 63 | int argc; 64 | int optind; 65 | }; 66 | 67 | struct su_context { 68 | struct su_initiator from; 69 | struct su_request to; 70 | mode_t umask; 71 | char sock_path[PATH_MAX]; 72 | }; 73 | 74 | typedef enum { 75 | INTERACTIVE = 0, 76 | DENY = 1, 77 | ALLOW = 2, 78 | } policy_t; 79 | 80 | extern void set_identity(unsigned int uid); 81 | 82 | static inline char *get_command(const struct su_request *to) 83 | { 84 | if (to->command) 85 | return to->command; 86 | if (to->shell) 87 | return to->shell; 88 | char* ret = to->argv[to->optind]; 89 | if (ret) 90 | return ret; 91 | return DEFAULT_SHELL; 92 | } 93 | 94 | int appops_start_op_su(int uid, const char *pkgName); 95 | int appops_finish_op_su(int uid, const char *pkgName); 96 | 97 | int run_daemon(); 98 | int connect_daemon(int argc, char *argv[], int ppid); 99 | int su_main(int argc, char *argv[], int need_client); 100 | // for when you give zero fucks about the state of the child process. 101 | // this version of fork understands you don't care about the child. 102 | // deadbeat dad fork. 103 | int fork_zero_fucks(); 104 | 105 | #ifndef LOG_NDEBUG 106 | #define LOG_NDEBUG 1 107 | #endif 108 | 109 | #include 110 | #include 111 | #define PLOGE(fmt,args...) ALOGE(fmt " failed with %d: %s", ##args, errno, strerror(errno)) 112 | #define PLOGEV(fmt,err,args...) ALOGE(fmt " failed with %d: %s", ##args, err, strerror(err)) 113 | 114 | #endif 115 | -------------------------------------------------------------------------------- /utils.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** Copyright 2012, The CyanogenMod Project 3 | ** 4 | ** Licensed under the Apache License, Version 2.0 (the "License"); 5 | ** you may not use this file except in compliance with the License. 6 | ** You may obtain a copy of the License at 7 | ** 8 | ** http://www.apache.org/licenses/LICENSE-2.0 9 | ** 10 | ** Unless required by applicable law or agreed to in writing, software 11 | ** distributed under the License is distributed on an "AS IS" BASIS, 12 | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | ** See the License for the specific language governing permissions and 14 | ** limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "utils.h" 29 | 30 | /* reads a file, making sure it is terminated with \n \0 */ 31 | char* read_file(const char *fn) 32 | { 33 | struct stat st; 34 | char *data = NULL; 35 | 36 | int fd = open(fn, O_RDONLY); 37 | if (fd < 0) return data; 38 | 39 | if (fstat(fd, &st)) goto oops; 40 | 41 | data = malloc(st.st_size + 2); 42 | if (!data) goto oops; 43 | 44 | if (read(fd, data, st.st_size) != st.st_size) goto oops; 45 | close(fd); 46 | data[st.st_size] = '\n'; 47 | data[st.st_size + 1] = 0; 48 | return data; 49 | 50 | oops: 51 | close(fd); 52 | if (data) free(data); 53 | return NULL; 54 | } 55 | 56 | int get_property(const char *data, char *found, const char *searchkey, const char *not_found) 57 | { 58 | char *key, *value, *eol, *sol, *tmp; 59 | if (data == NULL) goto defval; 60 | int matched = 0; 61 | char *dup = strdup(data); 62 | 63 | sol = dup; 64 | while((eol = strchr(sol, '\n'))) { 65 | key = sol; 66 | *eol++ = 0; 67 | sol = eol; 68 | 69 | value = strchr(key, '='); 70 | if(value == 0) continue; 71 | *value++ = 0; 72 | 73 | while(isspace(*key)) key++; 74 | if(*key == '#') continue; 75 | tmp = value - 2; 76 | while((tmp > key) && isspace(*tmp)) *tmp-- = 0; 77 | 78 | while(isspace(*value)) value++; 79 | tmp = eol - 2; 80 | while((tmp > value) && isspace(*tmp)) *tmp-- = 0; 81 | 82 | if (strncmp(searchkey, key, strlen(searchkey)) == 0) { 83 | matched = 1; 84 | break; 85 | } 86 | } 87 | free(dup); 88 | int len; 89 | if (matched) { 90 | len = strlen(value); 91 | if (len >= PROPERTY_VALUE_MAX) 92 | return -1; 93 | memcpy(found, value, len + 1); 94 | } else goto defval; 95 | return len; 96 | 97 | defval: 98 | len = strlen(not_found); 99 | memcpy(found, not_found, len + 1); 100 | return len; 101 | } 102 | 103 | /* 104 | * Fast version of get_property which purpose is to check 105 | * whether the property with given prefix exists. 106 | * 107 | * Assume nobody is stupid enough to put a propery with prefix ro.cm.version 108 | * in his build.prop on a non-CM ROM and comment it out. 109 | */ 110 | int check_property(const char *data, const char *prefix) 111 | { 112 | if (!data) 113 | return 0; 114 | return strstr(data, prefix) != NULL; 115 | } 116 | -------------------------------------------------------------------------------- /pts.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013, Tan Chee Eng (@tan-ce) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /* 18 | * pts.h 19 | * 20 | * Manages the pseudo-terminal driver on Linux/Android and provides some 21 | * helper functions to handle raw input mode and terminal window resizing 22 | */ 23 | 24 | #ifndef _PTS_H_ 25 | #define _PTS_H_ 26 | 27 | /** 28 | * pts_open 29 | * 30 | * Opens a pts device and returns the name of the slave tty device. 31 | * 32 | * Arguments 33 | * slave_name the name of the slave device 34 | * slave_name_size the size of the buffer passed via slave_name 35 | * 36 | * Return Values 37 | * on failure either -2 or -1 (errno set) is returned. 38 | * on success, the file descriptor of the master device is returned. 39 | */ 40 | int pts_open(char *slave_name, size_t slave_name_size); 41 | 42 | /** 43 | * set_stdin_raw 44 | * 45 | * Changes stdin to raw unbuffered mode, disables echo, 46 | * auto carriage return, etc. 47 | * 48 | * Return Value 49 | * on failure -1, and errno is set 50 | * on success 0 51 | */ 52 | int set_stdin_raw(void); 53 | 54 | /** 55 | * restore_stdin 56 | * 57 | * Restore termios on stdin to the state it was before 58 | * set_stdin_raw() was called. If set_stdin_raw() was 59 | * never called, does nothing and doesn't return an error. 60 | * 61 | * This function is async-safe. 62 | * 63 | * Return Value 64 | * on failure, -1 and errno is set 65 | * on success, 0 66 | */ 67 | int restore_stdin(void); 68 | 69 | /** 70 | * watch_sigwinch_async 71 | * 72 | * After calling this function, if the application receives 73 | * SIGWINCH, the terminal window size will be read from 74 | * "input" and set on "output". 75 | * 76 | * NOTE: This function blocks SIGWINCH and spawns a thread. 77 | * 78 | * Arguments 79 | * master A file descriptor of the TTY window size to follow 80 | * slave A file descriptor of the TTY window size which is 81 | * to be set on SIGWINCH 82 | * 83 | * Return Value 84 | * on failure, -1 and errno will be set. In this case, no 85 | * thread has been spawned and SIGWINCH will not be 86 | * blocked. 87 | * on success, 0 88 | */ 89 | int watch_sigwinch_async(int master, int slave); 90 | 91 | /** 92 | * watch_sigwinch_cleanup 93 | * 94 | * Cause the SIGWINCH watcher thread to terminate 95 | */ 96 | void watch_sigwinch_cleanup(void); 97 | 98 | /** 99 | * pump_stdin_async 100 | * 101 | * Forward data from STDIN to the given FD 102 | * in a seperate thread 103 | */ 104 | void pump_stdin_async(int outfd); 105 | 106 | /** 107 | * pump_stdout_blocking 108 | * 109 | * Forward data from the FD to STDOUT. 110 | * Returns when the remote end of the FD closes. 111 | * 112 | * Before returning, restores stdin settings. 113 | */ 114 | void pump_stdout_blocking(int infd); 115 | 116 | #endif 117 | -------------------------------------------------------------------------------- /pts.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013, Tan Chee Eng (@tan-ce) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /* 18 | * pts.c 19 | * 20 | * Manages the pseudo-terminal driver on Linux/Android and provides some 21 | * helper functions to handle raw input mode and terminal window resizing 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include "pts.h" 34 | 35 | /** 36 | * Helper functions 37 | */ 38 | // Ensures all the data is written out 39 | static int write_blocking(int fd, char *buf, size_t bufsz) { 40 | ssize_t ret, written; 41 | 42 | written = 0; 43 | do { 44 | ret = write(fd, buf + written, bufsz - written); 45 | if (ret == -1) return -1; 46 | written += ret; 47 | } while (written < (ssize_t)bufsz); 48 | 49 | return 0; 50 | } 51 | 52 | /** 53 | * Pump data from input FD to output FD. If close_output is 54 | * true, then close the output FD when we're done. 55 | */ 56 | static void pump_ex(int input, int output, int close_output) { 57 | char buf[4096]; 58 | int len; 59 | while ((len = read(input, buf, 4096)) > 0) { 60 | if (write_blocking(output, buf, len) == -1) break; 61 | } 62 | close(input); 63 | if (close_output) close(output); 64 | } 65 | 66 | /** 67 | * Pump data from input FD to output FD. Will close the 68 | * output FD when done. 69 | */ 70 | static void pump(int input, int output) { 71 | pump_ex(input, output, 1); 72 | } 73 | 74 | static void* pump_thread(void* data) { 75 | int* files = (int*)data; 76 | int input = files[0]; 77 | int output = files[1]; 78 | pump(input, output); 79 | free(data); 80 | return NULL; 81 | } 82 | 83 | static void pump_async(int input, int output) { 84 | pthread_t writer; 85 | int* files = (int*)malloc(sizeof(int) * 2); 86 | if (files == NULL) { 87 | exit(-1); 88 | } 89 | files[0] = input; 90 | files[1] = output; 91 | pthread_create(&writer, NULL, pump_thread, files); 92 | } 93 | 94 | 95 | /** 96 | * pts_open 97 | * 98 | * Opens a pts device and returns the name of the slave tty device. 99 | * 100 | * Arguments 101 | * slave_name the name of the slave device 102 | * slave_name_size the size of the buffer passed via slave_name 103 | * 104 | * Return Values 105 | * on failure either -2 or -1 (errno set) is returned. 106 | * on success, the file descriptor of the master device is returned. 107 | */ 108 | int pts_open(char *slave_name, size_t slave_name_size) { 109 | int fdm; 110 | char sn_tmp[slave_name_size]; 111 | 112 | // Open master ptmx device 113 | fdm = open("/dev/ptmx", O_RDWR); 114 | if (fdm == -1) return -1; 115 | 116 | // Get the slave name 117 | if (ptsname_r(fdm, sn_tmp, slave_name_size) != 0) { 118 | close(fdm); 119 | return -2; 120 | } 121 | 122 | if (strlcpy(slave_name, sn_tmp, slave_name_size) >= slave_name_size) { 123 | return -1; 124 | } 125 | 126 | // Grant, then unlock 127 | if (grantpt(fdm) == -1) { 128 | close(fdm); 129 | return -1; 130 | } 131 | if (unlockpt(fdm) == -1) { 132 | close(fdm); 133 | return -1; 134 | } 135 | 136 | return fdm; 137 | } 138 | 139 | // Stores the previous termios of stdin 140 | static struct termios old_stdin; 141 | static int stdin_is_raw = 0; 142 | 143 | /** 144 | * set_stdin_raw 145 | * 146 | * Changes stdin to raw unbuffered mode, disables echo, 147 | * auto carriage return, etc. 148 | * 149 | * Return Value 150 | * on failure -1, and errno is set 151 | * on success 0 152 | */ 153 | int set_stdin_raw(void) { 154 | struct termios new_termios; 155 | 156 | // Save the current stdin termios 157 | if (tcgetattr(STDIN_FILENO, &old_stdin) < 0) { 158 | return -1; 159 | } 160 | 161 | // Start from the current settings 162 | new_termios = old_stdin; 163 | 164 | // Make the terminal like an SSH or telnet client 165 | new_termios.c_iflag |= IGNPAR; 166 | new_termios.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF); 167 | new_termios.c_lflag &= ~(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL); 168 | new_termios.c_oflag &= ~OPOST; 169 | new_termios.c_cc[VMIN] = 1; 170 | new_termios.c_cc[VTIME] = 0; 171 | 172 | if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &new_termios) < 0) { 173 | return -1; 174 | } 175 | 176 | stdin_is_raw = 1; 177 | 178 | return 0; 179 | } 180 | 181 | /** 182 | * restore_stdin 183 | * 184 | * Restore termios on stdin to the state it was before 185 | * set_stdin_raw() was called. If set_stdin_raw() was 186 | * never called, does nothing and doesn't return an error. 187 | * 188 | * This function is async-safe. 189 | * 190 | * Return Value 191 | * on failure, -1 and errno is set 192 | * on success, 0 193 | */ 194 | int restore_stdin(void) { 195 | if (!stdin_is_raw) return 0; 196 | 197 | if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &old_stdin) < 0) { 198 | return -1; 199 | } 200 | 201 | stdin_is_raw = 0; 202 | 203 | return 0; 204 | } 205 | 206 | // Flag indicating whether the sigwinch watcher should terminate. 207 | static volatile int closing_time = 0; 208 | 209 | /** 210 | * Thread process. Wait for a SIGWINCH to be received, then update 211 | * the terminal size. 212 | */ 213 | static void *watch_sigwinch(void *data) { 214 | sigset_t winch; 215 | int sig; 216 | int master = ((int *)data)[0]; 217 | int slave = ((int *)data)[1]; 218 | 219 | sigemptyset(&winch); 220 | sigaddset(&winch, SIGWINCH); 221 | 222 | do { 223 | // Wait for a SIGWINCH 224 | sigwait(&winch, &sig); 225 | 226 | if (closing_time) break; 227 | 228 | // Get the new terminal size 229 | struct winsize w; 230 | if (ioctl(master, TIOCGWINSZ, &w) == -1) { 231 | continue; 232 | } 233 | 234 | // Set the new terminal size 235 | ioctl(slave, TIOCSWINSZ, &w); 236 | 237 | } while (1); 238 | 239 | free(data); 240 | return NULL; 241 | } 242 | 243 | /** 244 | * watch_sigwinch_async 245 | * 246 | * After calling this function, if the application receives 247 | * SIGWINCH, the terminal window size will be read from 248 | * "input" and set on "output". 249 | * 250 | * NOTE: This function blocks SIGWINCH and spawns a thread. 251 | * NOTE 2: This function must be called before any of the 252 | * pump functions. 253 | * 254 | * Arguments 255 | * master A file descriptor of the TTY window size to follow 256 | * slave A file descriptor of the TTY window size which is 257 | * to be set on SIGWINCH 258 | * 259 | * Return Value 260 | * on failure, -1 and errno will be set. In this case, no 261 | * thread has been spawned and SIGWINCH will not be 262 | * blocked. 263 | * on success, 0 264 | */ 265 | int watch_sigwinch_async(int master, int slave) { 266 | pthread_t watcher; 267 | int *files = (int *) malloc(sizeof(int) * 2); 268 | if (files == NULL) { 269 | return -1; 270 | } 271 | 272 | // Block SIGWINCH so sigwait can later receive it 273 | sigset_t winch; 274 | sigemptyset(&winch); 275 | sigaddset(&winch, SIGWINCH); 276 | if (sigprocmask(SIG_BLOCK, &winch, NULL) == -1) { 277 | free(files); 278 | return -1; 279 | } 280 | 281 | // Initialize some variables, then start the thread 282 | closing_time = 0; 283 | files[0] = master; 284 | files[1] = slave; 285 | int ret = pthread_create(&watcher, NULL, &watch_sigwinch, files); 286 | if (ret != 0) { 287 | free(files); 288 | errno = ret; 289 | return -1; 290 | } 291 | 292 | // Set the initial terminal size 293 | raise(SIGWINCH); 294 | return 0; 295 | } 296 | 297 | /** 298 | * watch_sigwinch_cleanup 299 | * 300 | * Cause the SIGWINCH watcher thread to terminate 301 | */ 302 | void watch_sigwinch_cleanup(void) { 303 | closing_time = 1; 304 | raise(SIGWINCH); 305 | } 306 | 307 | /** 308 | * pump_stdin_async 309 | * 310 | * Forward data from STDIN to the given FD 311 | * in a seperate thread 312 | */ 313 | void pump_stdin_async(int outfd) { 314 | // Put stdin into raw mode 315 | set_stdin_raw(); 316 | 317 | // Pump data from stdin to the PTY 318 | pump_async(STDIN_FILENO, outfd); 319 | } 320 | 321 | /** 322 | * pump_stdout_blocking 323 | * 324 | * Forward data from the FD to STDOUT. 325 | * Returns when the remote end of the FD closes. 326 | * 327 | * Before returning, restores stdin settings. 328 | */ 329 | void pump_stdout_blocking(int infd) { 330 | // Pump data from stdout to PTY 331 | pump_ex(infd, STDOUT_FILENO, 0 /* Don't close output when done */); 332 | 333 | // Cleanup 334 | restore_stdin(); 335 | watch_sigwinch_cleanup(); 336 | } 337 | -------------------------------------------------------------------------------- /su.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** Copyright 2010, Adam Shanks (@ChainsDD) 3 | ** Copyright 2008, Zinx Verituse (@zinxv) 4 | ** 5 | ** Licensed under the Apache License, Version 2.0 (the "License"); 6 | ** you may not use this file except in compliance with the License. 7 | ** You may obtain a copy of the License at 8 | ** 9 | ** http://www.apache.org/licenses/LICENSE-2.0 10 | ** 11 | ** Unless required by applicable law or agreed to in writing, software 12 | ** distributed under the License is distributed on an "AS IS" BASIS, 13 | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | ** See the License for the specific language governing permissions and 15 | ** limitations under the License. 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | #include "su.h" 40 | #include "utils.h" 41 | #include "binder/pm-wrapper.h" 42 | 43 | extern int is_daemon; 44 | extern int daemon_from_uid; 45 | extern int daemon_from_pid; 46 | 47 | int fork_zero_fucks() { 48 | int pid = fork(); 49 | if (pid) { 50 | int status; 51 | waitpid(pid, &status, 0); 52 | return pid; 53 | } 54 | else { 55 | if ((pid = fork())) 56 | exit(0); 57 | return 0; 58 | } 59 | } 60 | 61 | static int from_init(struct su_initiator *from) { 62 | char path[PATH_MAX], exe[PATH_MAX]; 63 | char args[4096], *argv0, *argv_rest; 64 | int fd; 65 | ssize_t len; 66 | int i; 67 | int err; 68 | 69 | from->uid = getuid(); 70 | from->pid = getppid(); 71 | 72 | if (is_daemon) { 73 | from->uid = daemon_from_uid; 74 | from->pid = daemon_from_pid; 75 | } 76 | 77 | /* Get the command line */ 78 | snprintf(path, sizeof(path), "/proc/%u/cmdline", from->pid); 79 | fd = open(path, O_RDONLY); 80 | if (fd < 0) { 81 | PLOGE("Opening command line"); 82 | return -1; 83 | } 84 | len = read(fd, args, sizeof(args)); 85 | err = errno; 86 | close(fd); 87 | if (len < 0 || len == sizeof(args)) { 88 | PLOGEV("Reading command line", err); 89 | return -1; 90 | } 91 | 92 | argv0 = args; 93 | argv_rest = NULL; 94 | for (i = 0; i < len; i++) { 95 | if (args[i] == '\0') { 96 | if (!argv_rest) { 97 | argv_rest = &args[i+1]; 98 | } else { 99 | args[i] = ' '; 100 | } 101 | } 102 | } 103 | args[len] = '\0'; 104 | 105 | if (argv_rest) { 106 | if (strlcpy(from->args, argv_rest, sizeof(from->args)) >= sizeof(from->args)) { 107 | ALOGE("argument too long"); 108 | return -1; 109 | } 110 | } else { 111 | from->args[0] = '\0'; 112 | } 113 | 114 | /* If this isn't app_process, use the real path instead of argv[0] */ 115 | snprintf(path, sizeof(path), "/proc/%u/exe", from->pid); 116 | len = readlink(path, exe, sizeof(exe)); 117 | if (len < 0) { 118 | PLOGE("Getting exe path"); 119 | return -1; 120 | } 121 | exe[len] = '\0'; 122 | if (strcmp(exe, "/system/bin/app_process")) { 123 | argv0 = exe; 124 | } 125 | 126 | if (strlcpy(from->bin, argv0, sizeof(from->bin)) >= sizeof(from->bin)) { 127 | ALOGE("binary path too long"); 128 | return -1; 129 | } 130 | 131 | struct passwd *pw; 132 | pw = getpwuid(from->uid); 133 | if (pw && pw->pw_name) { 134 | if (strlcpy(from->name, pw->pw_name, sizeof(from->name)) >= sizeof(from->name)) { 135 | ALOGE("name too long"); 136 | return -1; 137 | } 138 | } 139 | 140 | return 0; 141 | } 142 | 143 | static void populate_environment(const struct su_context *ctx) { 144 | struct passwd *pw; 145 | 146 | if (ctx->to.keepenv) 147 | return; 148 | 149 | pw = getpwuid(ctx->to.uid); 150 | if (pw) { 151 | setenv("HOME", pw->pw_dir, 1); 152 | if (ctx->to.shell) 153 | setenv("SHELL", ctx->to.shell, 1); 154 | else 155 | setenv("SHELL", DEFAULT_SHELL, 1); 156 | if (ctx->to.login || ctx->to.uid) { 157 | setenv("USER", pw->pw_name, 1); 158 | setenv("LOGNAME", pw->pw_name, 1); 159 | } 160 | } 161 | } 162 | 163 | void set_identity(unsigned int uid) { 164 | /* 165 | * Set effective uid back to root, otherwise setres[ug]id will fail 166 | * if uid isn't root. 167 | */ 168 | if (seteuid(0)) { 169 | PLOGE("seteuid (root)"); 170 | exit(EXIT_FAILURE); 171 | } 172 | if (setresgid(uid, uid, uid)) { 173 | PLOGE("setresgid (%u)", uid); 174 | exit(EXIT_FAILURE); 175 | } 176 | if (setresuid(uid, uid, uid)) { 177 | PLOGE("setresuid (%u)", uid); 178 | exit(EXIT_FAILURE); 179 | } 180 | } 181 | 182 | static void usage(int status) { 183 | FILE *stream = (status == EXIT_SUCCESS) ? stdout : stderr; 184 | 185 | fprintf(stream, 186 | "Usage: su [options] [--] [-] [LOGIN] [--] [args...]\n\n" 187 | "Options:\n" 188 | " --daemon start the su daemon agent\n" 189 | " -c, --command COMMAND pass COMMAND to the invoked shell\n" 190 | " -h, --help display this help message and exit\n" 191 | " -, -l, --login pretend the shell to be a login shell\n" 192 | " -m, -p,\n" 193 | " --preserve-environment do not change environment variables\n" 194 | " -s, --shell SHELL use SHELL instead of the default " DEFAULT_SHELL "\n" 195 | " -v, --version display version number and exit\n" 196 | " -V display version code and exit,\n" 197 | " this is used almost exclusively by Superuser.apk\n"); 198 | exit(status); 199 | } 200 | 201 | static __attribute__ ((noreturn)) void deny(struct su_context *ctx) { 202 | char *cmd = get_command(&ctx->to); 203 | ALOGW("request rejected (%u->%u %s)", ctx->from.uid, ctx->to.uid, cmd); 204 | fprintf(stderr, "%s\n", strerror(EACCES)); 205 | exit(EXIT_FAILURE); 206 | } 207 | 208 | static __attribute__ ((noreturn)) void allow(struct su_context *ctx, const char *packageName) { 209 | char *arg0; 210 | int argc, err; 211 | 212 | umask(ctx->umask); 213 | 214 | char *binary; 215 | argc = ctx->to.optind; 216 | if (ctx->to.command) { 217 | binary = ctx->to.shell; 218 | ctx->to.argv[--argc] = ctx->to.command; 219 | ctx->to.argv[--argc] = "-c"; 220 | } 221 | else if (ctx->to.shell) { 222 | binary = ctx->to.shell; 223 | } 224 | else { 225 | if (ctx->to.argv[argc]) { 226 | binary = ctx->to.argv[argc++]; 227 | } 228 | else { 229 | binary = DEFAULT_SHELL; 230 | } 231 | } 232 | 233 | arg0 = strrchr (binary, '/'); 234 | arg0 = (arg0) ? arg0 + 1 : binary; 235 | if (ctx->to.login) { 236 | int s = strlen(arg0) + 2; 237 | char *p = malloc(s); 238 | 239 | if (!p) 240 | exit(EXIT_FAILURE); 241 | 242 | *p = '-'; 243 | strcpy(p + 1, arg0); 244 | arg0 = p; 245 | } 246 | 247 | populate_environment(ctx); 248 | set_identity(ctx->to.uid); 249 | 250 | #define PARG(arg) \ 251 | (argc + (arg) < ctx->to.argc) ? " " : "", \ 252 | (argc + (arg) < ctx->to.argc) ? ctx->to.argv[argc + (arg)] : "" 253 | 254 | ALOGD("%u %s executing %u %s using binary %s : %s%s%s%s%s%s%s%s%s%s%s%s%s%s", 255 | ctx->from.uid, ctx->from.bin, 256 | ctx->to.uid, get_command(&ctx->to), binary, 257 | arg0, PARG(0), PARG(1), PARG(2), PARG(3), PARG(4), PARG(5), 258 | (ctx->to.optind + 6 < ctx->to.argc) ? " ..." : ""); 259 | 260 | ctx->to.argv[--argc] = arg0; 261 | 262 | int pid = fork(); 263 | if (!pid) { 264 | execvp(binary, ctx->to.argv + argc); 265 | err = errno; 266 | PLOGE("exec"); 267 | fprintf(stderr, "Cannot execute %s: %s\n", binary, strerror(err)); 268 | exit(EXIT_FAILURE); 269 | } else { 270 | int status; 271 | 272 | ALOGD("Waiting for pid %d.", pid); 273 | waitpid(pid, &status, 0); 274 | if (packageName) { 275 | appops_finish_op_su(ctx->from.uid, packageName); 276 | } 277 | exit(status); 278 | } 279 | } 280 | 281 | /* 282 | * CyanogenMod-specific behavior 283 | * 284 | * we can't simply use the property service, since we aren't launched from init 285 | * and can't trust the location of the property workspace. 286 | * Find the properties ourselves. 287 | */ 288 | int access_disabled(const struct su_initiator *from) { 289 | char *data; 290 | char build_type[PROPERTY_VALUE_MAX]; 291 | char debuggable[PROPERTY_VALUE_MAX], enabled[PROPERTY_VALUE_MAX]; 292 | size_t len; 293 | 294 | data = read_file("/system/build.prop"); 295 | if (check_property(data, "ro.cm.version")) { 296 | get_property(data, build_type, "ro.build.type", ""); 297 | free(data); 298 | 299 | data = read_file("/default.prop"); 300 | get_property(data, debuggable, "ro.debuggable", "0"); 301 | free(data); 302 | /* only allow su on debuggable builds */ 303 | if (strcmp("1", debuggable) != 0) { 304 | ALOGE("Root access is disabled on non-debug builds"); 305 | return 1; 306 | } 307 | 308 | data = read_file("/data/property/persist.sys.root_access"); 309 | if (data != NULL) { 310 | len = strlen(data); 311 | if (len >= PROPERTY_VALUE_MAX) 312 | memcpy(enabled, "0", 2); 313 | else 314 | memcpy(enabled, data, len + 1); 315 | free(data); 316 | } else 317 | memcpy(enabled, "0", 2); 318 | 319 | /* enforce persist.sys.root_access on non-eng builds for apps */ 320 | if (strcmp("eng", build_type) != 0 && 321 | from->uid != AID_SHELL && from->uid != AID_ROOT && 322 | (atoi(enabled) & CM_ROOT_ACCESS_APPS_ONLY) != CM_ROOT_ACCESS_APPS_ONLY ) { 323 | ALOGE("Apps root access is disabled by system setting - " 324 | "enable it under settings -> developer options"); 325 | return 1; 326 | } 327 | 328 | /* disallow su in a shell if appropriate */ 329 | if (from->uid == AID_SHELL && 330 | (atoi(enabled) & CM_ROOT_ACCESS_ADB_ONLY) != CM_ROOT_ACCESS_ADB_ONLY ) { 331 | ALOGE("Shell root access is disabled by a system setting - " 332 | "enable it under settings -> developer options"); 333 | return 1; 334 | } 335 | 336 | } 337 | return 0; 338 | } 339 | 340 | static void fork_for_samsung(void) 341 | { 342 | // Samsung CONFIG_SEC_RESTRICT_SETUID wants the parent process to have 343 | // EUID 0, or else our setresuid() calls will be denied. So make sure 344 | // all such syscalls are executed by a child process. 345 | int rv; 346 | 347 | switch (fork()) { 348 | case 0: 349 | return; 350 | case -1: 351 | PLOGE("fork"); 352 | exit(1); 353 | default: 354 | if (wait(&rv) < 0) { 355 | exit(1); 356 | } else { 357 | exit(WEXITSTATUS(rv)); 358 | } 359 | } 360 | } 361 | 362 | int main(int argc, char *argv[]) { 363 | if (getuid() != geteuid()) { 364 | ALOGE("must not be a setuid binary"); 365 | return 1; 366 | } 367 | 368 | return su_main(argc, argv, 1); 369 | } 370 | 371 | int su_main(int argc, char *argv[], int need_client) { 372 | // start up in daemon mode if prompted 373 | if (argc == 2 && strcmp(argv[1], "--daemon") == 0) { 374 | return run_daemon(); 375 | } 376 | 377 | int ppid = getppid(); 378 | fork_for_samsung(); 379 | 380 | // Sanitize all secure environment variables (from linker_environ.c in AOSP linker). 381 | /* The same list than GLibc at this point */ 382 | static const char* const unsec_vars[] = { 383 | "GCONV_PATH", 384 | "GETCONF_DIR", 385 | "HOSTALIASES", 386 | "LD_AUDIT", 387 | "LD_DEBUG", 388 | "LD_DEBUG_OUTPUT", 389 | "LD_DYNAMIC_WEAK", 390 | "LD_LIBRARY_PATH", 391 | "LD_ORIGIN_PATH", 392 | "LD_PRELOAD", 393 | "LD_PROFILE", 394 | "LD_SHOW_AUXV", 395 | "LD_USE_LOAD_BIAS", 396 | "LOCALDOMAIN", 397 | "LOCPATH", 398 | "MALLOC_TRACE", 399 | "MALLOC_CHECK_", 400 | "NIS_PATH", 401 | "NLSPATH", 402 | "RESOLV_HOST_CONF", 403 | "RES_OPTIONS", 404 | "TMPDIR", 405 | "TZDIR", 406 | "LD_AOUT_LIBRARY_PATH", 407 | "LD_AOUT_PRELOAD", 408 | // not listed in linker, used due to system() call 409 | "IFS", 410 | }; 411 | const char* const* cp = unsec_vars; 412 | const char* const* endp = cp + sizeof(unsec_vars)/sizeof(unsec_vars[0]); 413 | while (cp < endp) { 414 | unsetenv(*cp); 415 | cp++; 416 | } 417 | 418 | ALOGD("su invoked."); 419 | 420 | struct su_context ctx = { 421 | .from = { 422 | .pid = -1, 423 | .uid = 0, 424 | .bin = "", 425 | .args = "", 426 | .name = "", 427 | }, 428 | .to = { 429 | .uid = AID_ROOT, 430 | .login = 0, 431 | .keepenv = 0, 432 | .shell = NULL, 433 | .command = NULL, 434 | .argv = argv, 435 | .argc = argc, 436 | .optind = 0, 437 | .name = "", 438 | }, 439 | }; 440 | int c, socket_serv_fd, fd; 441 | char buf[64], *result; 442 | policy_t dballow; 443 | struct option long_opts[] = { 444 | { "command", required_argument, NULL, 'c' }, 445 | { "help", no_argument, NULL, 'h' }, 446 | { "login", no_argument, NULL, 'l' }, 447 | { "preserve-environment", no_argument, NULL, 'p' }, 448 | { "shell", required_argument, NULL, 's' }, 449 | { "version", no_argument, NULL, 'v' }, 450 | { NULL, 0, NULL, 0 }, 451 | }; 452 | 453 | while ((c = getopt_long(argc, argv, "+c:hlmps:Vv", long_opts, NULL)) != -1) { 454 | switch(c) { 455 | case 'c': 456 | ctx.to.shell = DEFAULT_SHELL; 457 | ctx.to.command = optarg; 458 | break; 459 | case 'h': 460 | usage(EXIT_SUCCESS); 461 | break; 462 | case 'l': 463 | ctx.to.login = 1; 464 | break; 465 | case 'm': 466 | case 'p': 467 | ctx.to.keepenv = 1; 468 | break; 469 | case 's': 470 | ctx.to.shell = optarg; 471 | break; 472 | case 'V': 473 | printf("%d\n", VERSION_CODE); 474 | exit(EXIT_SUCCESS); 475 | case 'v': 476 | printf("%s\n", VERSION); 477 | exit(EXIT_SUCCESS); 478 | default: 479 | /* Bionic getopt_long doesn't terminate its error output by newline */ 480 | fprintf(stderr, "\n"); 481 | usage(2); 482 | } 483 | } 484 | 485 | if (need_client) { 486 | // attempt to connect to daemon... 487 | ALOGD("starting daemon client %d %d", getuid(), geteuid()); 488 | return connect_daemon(argc, argv, ppid); 489 | } 490 | 491 | if (optind < argc && !strcmp(argv[optind], "-")) { 492 | ctx.to.login = 1; 493 | optind++; 494 | } 495 | /* username or uid */ 496 | if (optind < argc && strcmp(argv[optind], "--")) { 497 | struct passwd *pw; 498 | pw = getpwnam(argv[optind]); 499 | if (!pw) { 500 | char *endptr; 501 | 502 | /* It seems we shouldn't do this at all */ 503 | errno = 0; 504 | ctx.to.uid = strtoul(argv[optind], &endptr, 10); 505 | if (errno || *endptr) { 506 | ALOGE("Unknown id: %s\n", argv[optind]); 507 | fprintf(stderr, "Unknown id: %s\n", argv[optind]); 508 | exit(EXIT_FAILURE); 509 | } 510 | } else { 511 | ctx.to.uid = pw->pw_uid; 512 | if (pw->pw_name) { 513 | if (strlcpy(ctx.to.name, pw->pw_name, sizeof(ctx.to.name)) >= sizeof(ctx.to.name)) { 514 | ALOGE("name too long"); 515 | exit(EXIT_FAILURE); 516 | } 517 | } 518 | } 519 | optind++; 520 | } 521 | if (optind < argc && !strcmp(argv[optind], "--")) { 522 | optind++; 523 | } 524 | ctx.to.optind = optind; 525 | 526 | if (from_init(&ctx.from) < 0) { 527 | deny(&ctx); 528 | } 529 | 530 | ALOGE("SU from: %s", ctx.from.name); 531 | 532 | // the latter two are necessary for stock ROMs like note 2 which do dumb things with su, or crash otherwise 533 | if (ctx.from.uid == AID_ROOT) { 534 | ALOGD("Allowing root/system/radio."); 535 | allow(&ctx, NULL); 536 | } 537 | 538 | // check if superuser is disabled completely 539 | if (access_disabled(&ctx.from)) { 540 | ALOGD("access_disabled"); 541 | deny(&ctx); 542 | } 543 | 544 | // autogrant shell at this point 545 | if (ctx.from.uid == AID_SHELL) { 546 | ALOGD("Allowing shell."); 547 | allow(&ctx, NULL); 548 | } 549 | 550 | const char *packageName = resolve_package_name(ctx.from.uid); 551 | if (!appops_start_op_su(ctx.from.uid, packageName)) { 552 | ALOGD("Allowing via appops."); 553 | allow(&ctx, packageName); 554 | } 555 | 556 | ALOGE("Allow chain exhausted, denying request"); 557 | deny(&ctx); 558 | } 559 | -------------------------------------------------------------------------------- /daemon.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** Copyright 2010, Adam Shanks (@ChainsDD) 3 | ** Copyright 2008, Zinx Verituse (@zinxv) 4 | ** 5 | ** Licensed under the Apache License, Version 2.0 (the "License"); 6 | ** you may not use this file except in compliance with the License. 7 | ** You may obtain a copy of the License at 8 | ** 9 | ** http://www.apache.org/licenses/LICENSE-2.0 10 | ** 11 | ** Unless required by applicable law or agreed to in writing, software 12 | ** distributed under the License is distributed on an "AS IS" BASIS, 13 | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | ** See the License for the specific language governing permissions and 15 | ** limitations under the License. 16 | */ 17 | 18 | #define _GNU_SOURCE /* for unshare() */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | 45 | #include 46 | 47 | #include "su.h" 48 | #include "utils.h" 49 | #include "pts.h" 50 | 51 | int is_daemon = 0; 52 | int daemon_from_uid = 0; 53 | int daemon_from_pid = 0; 54 | 55 | // Constants for the atty bitfield 56 | #define ATTY_IN 1 57 | #define ATTY_OUT 2 58 | #define ATTY_ERR 4 59 | 60 | /* 61 | * Receive a file descriptor from a Unix socket. 62 | * Contributed by @mkasick 63 | * 64 | * Returns the file descriptor on success, or -1 if a file 65 | * descriptor was not actually included in the message 66 | * 67 | * On error the function terminates by calling exit(-1) 68 | */ 69 | static int recv_fd(int sockfd) { 70 | // Need to receive data from the message, otherwise don't care about it. 71 | char iovbuf; 72 | 73 | struct iovec iov = { 74 | .iov_base = &iovbuf, 75 | .iov_len = 1, 76 | }; 77 | 78 | char cmsgbuf[CMSG_SPACE(sizeof(int))]; 79 | 80 | struct msghdr msg = { 81 | .msg_iov = &iov, 82 | .msg_iovlen = 1, 83 | .msg_control = cmsgbuf, 84 | .msg_controllen = sizeof(cmsgbuf), 85 | }; 86 | 87 | if (recvmsg(sockfd, &msg, MSG_WAITALL) != 1) { 88 | goto error; 89 | } 90 | 91 | // Was a control message actually sent? 92 | switch (msg.msg_controllen) { 93 | case 0: 94 | // No, so the file descriptor was closed and won't be used. 95 | return -1; 96 | case sizeof(cmsgbuf): 97 | // Yes, grab the file descriptor from it. 98 | break; 99 | default: 100 | goto error; 101 | } 102 | 103 | struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); 104 | 105 | if (cmsg == NULL || 106 | cmsg->cmsg_len != CMSG_LEN(sizeof(int)) || 107 | cmsg->cmsg_level != SOL_SOCKET || 108 | cmsg->cmsg_type != SCM_RIGHTS) { 109 | error: 110 | ALOGE("unable to read fd"); 111 | exit(-1); 112 | } 113 | 114 | return *(int *)CMSG_DATA(cmsg); 115 | } 116 | 117 | /* 118 | * Send a file descriptor through a Unix socket. 119 | * Contributed by @mkasick 120 | * 121 | * On error the function terminates by calling exit(-1) 122 | * 123 | * fd may be -1, in which case the dummy data is sent, 124 | * but no control message with the FD is sent. 125 | */ 126 | static void send_fd(int sockfd, int fd) { 127 | // Need to send some data in the message, this will do. 128 | struct iovec iov = { 129 | .iov_base = "", 130 | .iov_len = 1, 131 | }; 132 | 133 | struct msghdr msg = { 134 | .msg_iov = &iov, 135 | .msg_iovlen = 1, 136 | }; 137 | 138 | char cmsgbuf[CMSG_SPACE(sizeof(int))]; 139 | 140 | if (fd != -1) { 141 | // Is the file descriptor actually open? 142 | if (fcntl(fd, F_GETFD) == -1) { 143 | if (errno != EBADF) { 144 | goto error; 145 | } 146 | // It's closed, don't send a control message or sendmsg will EBADF. 147 | } else { 148 | // It's open, send the file descriptor in a control message. 149 | msg.msg_control = cmsgbuf; 150 | msg.msg_controllen = sizeof(cmsgbuf); 151 | 152 | struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); 153 | 154 | cmsg->cmsg_len = CMSG_LEN(sizeof(int)); 155 | cmsg->cmsg_level = SOL_SOCKET; 156 | cmsg->cmsg_type = SCM_RIGHTS; 157 | 158 | *(int *)CMSG_DATA(cmsg) = fd; 159 | } 160 | } 161 | 162 | if (sendmsg(sockfd, &msg, 0) != 1) { 163 | error: 164 | PLOGE("unable to send fd"); 165 | exit(-1); 166 | } 167 | } 168 | 169 | static int read_int(int fd) { 170 | int val; 171 | int len = read(fd, &val, sizeof(int)); 172 | if (len != sizeof(int)) { 173 | ALOGE("unable to read int: %d", len); 174 | exit(-1); 175 | } 176 | return val; 177 | } 178 | 179 | static void write_int(int fd, int val) { 180 | int written = write(fd, &val, sizeof(int)); 181 | if (written != sizeof(int)) { 182 | PLOGE("unable to write int"); 183 | exit(-1); 184 | } 185 | } 186 | 187 | static char* read_string(int fd) { 188 | int len = read_int(fd); 189 | if (len > PATH_MAX || len < 0) { 190 | ALOGE("invalid string length %d", len); 191 | exit(-1); 192 | } 193 | char* val = malloc(sizeof(char) * (len + 1)); 194 | if (val == NULL) { 195 | ALOGE("unable to malloc string"); 196 | exit(-1); 197 | } 198 | val[len] = '\0'; 199 | int amount = read(fd, val, len); 200 | if (amount != len) { 201 | ALOGE("unable to read string"); 202 | exit(-1); 203 | } 204 | return val; 205 | } 206 | 207 | static void write_string(int fd, char* val) { 208 | int len = strlen(val); 209 | write_int(fd, len); 210 | int written = write(fd, val, len); 211 | if (written != len) { 212 | PLOGE("unable to write string"); 213 | exit(-1); 214 | } 215 | } 216 | 217 | static void mount_emulated_storage(int user_id) { 218 | const char *emulated_source = getenv("EMULATED_STORAGE_SOURCE"); 219 | const char *emulated_target = getenv("EMULATED_STORAGE_TARGET"); 220 | const char* legacy = getenv("EXTERNAL_STORAGE"); 221 | 222 | if (!emulated_source || !emulated_target) { 223 | // No emulated storage is present 224 | return; 225 | } 226 | 227 | // Create a second private mount namespace for our process 228 | if (unshare(CLONE_NEWNS) < 0) { 229 | PLOGE("unshare"); 230 | return; 231 | } 232 | 233 | if (mount("rootfs", "/", NULL, MS_SLAVE | MS_REC, NULL) < 0) { 234 | PLOGE("mount rootfs as slave"); 235 | return; 236 | } 237 | 238 | // /mnt/shell/emulated -> /storage/emulated 239 | if (mount(emulated_source, emulated_target, NULL, MS_BIND, NULL) < 0) { 240 | PLOGE("mount emulated storage"); 241 | } 242 | 243 | char target_user[PATH_MAX]; 244 | snprintf(target_user, PATH_MAX, "%s/%d", emulated_target, user_id); 245 | 246 | // /mnt/shell/emulated/ -> /storage/emulated/legacy 247 | if (mount(target_user, legacy, NULL, MS_BIND | MS_REC, NULL) < 0) { 248 | PLOGE("mount legacy path"); 249 | } 250 | } 251 | 252 | static int run_daemon_child(int infd, int outfd, int errfd, int argc, char** argv) { 253 | if (-1 == dup2(outfd, STDOUT_FILENO)) { 254 | PLOGE("dup2 child outfd"); 255 | exit(-1); 256 | } 257 | 258 | if (-1 == dup2(errfd, STDERR_FILENO)) { 259 | PLOGE("dup2 child errfd"); 260 | exit(-1); 261 | } 262 | 263 | if (-1 == dup2(infd, STDIN_FILENO)) { 264 | PLOGE("dup2 child infd"); 265 | exit(-1); 266 | } 267 | 268 | close(infd); 269 | close(outfd); 270 | close(errfd); 271 | 272 | return su_main(argc, argv, 0); 273 | } 274 | 275 | static int daemon_accept(int fd) { 276 | char mypath[PATH_MAX], remotepath[PATH_MAX]; 277 | int caller_is_self = 0; 278 | 279 | is_daemon = 1; 280 | int pid = read_int(fd); 281 | int child_result; 282 | ALOGD("remote pid: %d", pid); 283 | char *pts_slave = read_string(fd); 284 | ALOGD("remote pts_slave: %s", pts_slave); 285 | daemon_from_pid = read_int(fd); 286 | ALOGV("remote req pid: %d", daemon_from_pid); 287 | 288 | struct ucred credentials; 289 | socklen_t ucred_length = sizeof(struct ucred); 290 | /* fill in the user data structure */ 291 | if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &credentials, &ucred_length)) { 292 | ALOGE("could obtain credentials from unix domain socket"); 293 | exit(-1); 294 | } 295 | 296 | daemon_from_uid = credentials.uid; 297 | 298 | int mount_storage = read_int(fd); 299 | // The the FDs for each of the streams 300 | int infd = recv_fd(fd); 301 | int outfd = recv_fd(fd); 302 | int errfd = recv_fd(fd); 303 | 304 | int argc = read_int(fd); 305 | if (argc < 0 || argc > 512) { 306 | ALOGE("unable to allocate args: %d", argc); 307 | exit(-1); 308 | } 309 | ALOGV("remote args: %d", argc); 310 | char** argv = (char**)malloc(sizeof(char*) * (argc + 1)); 311 | argv[argc] = NULL; 312 | int i; 313 | for (i = 0; i < argc; i++) { 314 | argv[i] = read_string(fd); 315 | } 316 | 317 | // ack 318 | write_int(fd, 1); 319 | 320 | // Fork the child process. The fork has to happen before calling 321 | // setsid() and opening the pseudo-terminal so that the parent 322 | // is not affected 323 | int child = fork(); 324 | if (child < 0) { 325 | for (i = 0; i < argc; i++) { 326 | free(argv[i]); 327 | } 328 | free(argv); 329 | 330 | // fork failed, send a return code and bail out 331 | PLOGE("unable to fork"); 332 | write(fd, &child, sizeof(int)); 333 | close(fd); 334 | return child; 335 | } 336 | 337 | if (child != 0) { 338 | for (i = 0; i < argc; i++) { 339 | free(argv[i]); 340 | } 341 | free(argv); 342 | 343 | // In parent, wait for the child to exit, and send the exit code 344 | // across the wire. 345 | int status, code; 346 | 347 | free(pts_slave); 348 | 349 | ALOGD("waiting for child exit"); 350 | if (waitpid(child, &status, 0) > 0) { 351 | code = WEXITSTATUS(status); 352 | } 353 | else { 354 | code = -1; 355 | } 356 | 357 | // Is the file descriptor actually open? 358 | if (fcntl(fd, F_GETFD) == -1) { 359 | if (errno != EBADF) { 360 | goto error; 361 | } 362 | } 363 | 364 | // Pass the return code back to the client 365 | ALOGD("sending code"); 366 | if (write(fd, &code, sizeof(int)) != sizeof(int)) { 367 | PLOGE("unable to write exit code"); 368 | } 369 | 370 | close(fd); 371 | error: 372 | ALOGD("child exited"); 373 | return code; 374 | } 375 | 376 | // We are in the child now 377 | // Close the unix socket file descriptor 378 | close (fd); 379 | 380 | // Become session leader 381 | if (setsid() == (pid_t) -1) { 382 | PLOGE("setsid"); 383 | } 384 | 385 | int ptsfd; 386 | if (pts_slave[0]) { 387 | // Opening the TTY has to occur after the 388 | // fork() and setsid() so that it becomes 389 | // our controlling TTY and not the daemon's 390 | ptsfd = open(pts_slave, O_RDWR); 391 | if (ptsfd == -1) { 392 | PLOGE("open(pts_slave) daemon"); 393 | exit(-1); 394 | } 395 | 396 | struct stat st; 397 | if (fstat(ptsfd, &st)) { 398 | PLOGE("failed to stat pts_slave"); 399 | exit(-1); 400 | } 401 | 402 | if (st.st_uid != credentials.uid) { 403 | PLOGE("caller doesn't own proposed PTY"); 404 | exit(-1); 405 | } 406 | 407 | if (!S_ISCHR(st.st_mode)) { 408 | PLOGE("proposed PTY isn't a chardev"); 409 | exit(-1); 410 | } 411 | 412 | if (infd < 0) { 413 | ALOGD("daemon: stdin using PTY"); 414 | infd = ptsfd; 415 | } 416 | if (outfd < 0) { 417 | ALOGD("daemon: stdout using PTY"); 418 | outfd = ptsfd; 419 | } 420 | if (errfd < 0) { 421 | ALOGD("daemon: stderr using PTY"); 422 | errfd = ptsfd; 423 | } 424 | } else { 425 | // TODO: Check system property, if PTYs are disabled, 426 | // made infd the CTTY using: 427 | // ioctl(infd, TIOCSCTTY, 1); 428 | } 429 | free(pts_slave); 430 | 431 | if (mount_storage) { 432 | mount_emulated_storage(multiuser_get_user_id(daemon_from_uid)); 433 | } 434 | 435 | child_result = run_daemon_child(infd, outfd, errfd, argc, argv); 436 | for (i = 0; i < argc; i++) { 437 | free(argv[i]); 438 | } 439 | free(argv); 440 | return child_result; 441 | } 442 | 443 | int run_daemon() { 444 | if (getuid() != 0 || getgid() != 0) { 445 | PLOGE("daemon requires root. uid/gid not root"); 446 | return -1; 447 | } 448 | 449 | int fd; 450 | struct sockaddr_un sun; 451 | 452 | fd = socket(AF_LOCAL, SOCK_STREAM, 0); 453 | if (fd < 0) { 454 | PLOGE("socket"); 455 | return -1; 456 | } 457 | if (fcntl(fd, F_SETFD, FD_CLOEXEC)) { 458 | PLOGE("fcntl FD_CLOEXEC"); 459 | goto err; 460 | } 461 | 462 | memset(&sun, 0, sizeof(sun)); 463 | sun.sun_family = AF_LOCAL; 464 | sprintf(sun.sun_path, "%s/su-daemon", DAEMON_SOCKET_PATH); 465 | 466 | /* 467 | * Delete the socket to protect from situations when 468 | * something bad occured previously and the kernel reused pid from that process. 469 | * Small probability, isn't it. 470 | */ 471 | unlink(sun.sun_path); 472 | unlink(DAEMON_SOCKET_PATH); 473 | 474 | int previous_umask = umask(027); 475 | mkdir(DAEMON_SOCKET_PATH, 0711); 476 | 477 | if (bind(fd, (struct sockaddr*)&sun, sizeof(sun)) < 0) { 478 | PLOGE("daemon bind"); 479 | goto err; 480 | } 481 | 482 | chmod(DAEMON_SOCKET_PATH, 0711); 483 | chmod(sun.sun_path, 0666); 484 | 485 | umask(previous_umask); 486 | 487 | if (listen(fd, 10) < 0) { 488 | PLOGE("daemon listen"); 489 | goto err; 490 | } 491 | 492 | int client; 493 | while ((client = accept(fd, NULL, NULL)) > 0) { 494 | if (fork_zero_fucks() == 0) { 495 | close(fd); 496 | return daemon_accept(client); 497 | } 498 | else { 499 | close(client); 500 | } 501 | } 502 | 503 | ALOGE("daemon exiting"); 504 | err: 505 | close(fd); 506 | return -1; 507 | } 508 | 509 | // List of signals which cause process termination 510 | static int quit_signals[] = { SIGALRM, SIGHUP, SIGPIPE, SIGQUIT, SIGTERM, SIGINT, 0 }; 511 | 512 | static void sighandler(__attribute__ ((unused)) int sig) { 513 | restore_stdin(); 514 | 515 | // Assume we'll only be called before death 516 | // See note before sigaction() in set_stdin_raw() 517 | // 518 | // Now, close all standard I/O to cause the pumps 519 | // to exit so we can continue and retrieve the exit 520 | // code 521 | close(STDIN_FILENO); 522 | close(STDOUT_FILENO); 523 | close(STDERR_FILENO); 524 | 525 | // Put back all the default handlers 526 | struct sigaction act; 527 | int i; 528 | 529 | memset(&act, '\0', sizeof(act)); 530 | act.sa_handler = SIG_DFL; 531 | for (i = 0; quit_signals[i]; i++) { 532 | if (sigaction(quit_signals[i], &act, NULL) < 0) { 533 | PLOGE("Error removing signal handler"); 534 | continue; 535 | } 536 | } 537 | } 538 | 539 | /** 540 | * Setup signal handlers trap signals which should result in program termination 541 | * so that we can restore the terminal to its normal state and retrieve the 542 | * return code. 543 | */ 544 | static void setup_sighandlers(void) { 545 | struct sigaction act; 546 | int i; 547 | 548 | // Install the termination handlers 549 | // Note: we're assuming that none of these signal handlers are already trapped. 550 | // If they are, we'll need to modify this code to save the previous handler and 551 | // call it after we restore stdin to its previous state. 552 | memset(&act, '\0', sizeof(act)); 553 | act.sa_handler = &sighandler; 554 | for (i = 0; quit_signals[i]; i++) { 555 | if (sigaction(quit_signals[i], &act, NULL) < 0) { 556 | PLOGE("Error installing signal handler"); 557 | continue; 558 | } 559 | } 560 | } 561 | 562 | int connect_daemon(int argc, char *argv[], int ppid) { 563 | int ptmx = -1; 564 | char pts_slave[PATH_MAX]; 565 | 566 | struct sockaddr_un sun; 567 | 568 | // Open a socket to the daemon 569 | int socketfd = socket(AF_LOCAL, SOCK_STREAM, 0); 570 | if (socketfd < 0) { 571 | PLOGE("socket"); 572 | exit(-1); 573 | } 574 | if (fcntl(socketfd, F_SETFD, FD_CLOEXEC)) { 575 | PLOGE("fcntl FD_CLOEXEC"); 576 | exit(-1); 577 | } 578 | 579 | memset(&sun, 0, sizeof(sun)); 580 | sun.sun_family = AF_LOCAL; 581 | sprintf(sun.sun_path, "%s/su-daemon", DAEMON_SOCKET_PATH); 582 | 583 | if (0 != connect(socketfd, (struct sockaddr*)&sun, sizeof(sun))) { 584 | PLOGE("connect"); 585 | exit(-1); 586 | } 587 | 588 | ALOGV("connecting client %d", getpid()); 589 | 590 | int mount_storage = getenv("MOUNT_EMULATED_STORAGE") != NULL; 591 | 592 | // Determine which one of our streams are attached to a TTY 593 | int atty = 0; 594 | 595 | // TODO: Check a system property and never use PTYs if 596 | // the property is set. 597 | if (isatty(STDIN_FILENO)) atty |= ATTY_IN; 598 | if (isatty(STDOUT_FILENO)) atty |= ATTY_OUT; 599 | if (isatty(STDERR_FILENO)) atty |= ATTY_ERR; 600 | 601 | if (atty) { 602 | // We need a PTY. Get one. 603 | ptmx = pts_open(pts_slave, sizeof(pts_slave)); 604 | if (ptmx < 0) { 605 | PLOGE("pts_open"); 606 | exit(-1); 607 | } 608 | } else { 609 | pts_slave[0] = '\0'; 610 | } 611 | 612 | // Send some info to the daemon, starting with our PID 613 | write_int(socketfd, getpid()); 614 | // Send the slave path to the daemon 615 | // (This is "" if we're not using PTYs) 616 | write_string(socketfd, pts_slave); 617 | // Parent PID 618 | write_int(socketfd, ppid); 619 | write_int(socketfd, mount_storage); 620 | 621 | // Send stdin 622 | if (atty & ATTY_IN) { 623 | // Using PTY 624 | send_fd(socketfd, -1); 625 | } else { 626 | send_fd(socketfd, STDIN_FILENO); 627 | } 628 | 629 | // Send stdout 630 | if (atty & ATTY_OUT) { 631 | // Forward SIGWINCH 632 | watch_sigwinch_async(STDOUT_FILENO, ptmx); 633 | 634 | // Using PTY 635 | send_fd(socketfd, -1); 636 | } else { 637 | send_fd(socketfd, STDOUT_FILENO); 638 | } 639 | 640 | // Send stderr 641 | if (atty & ATTY_ERR) { 642 | // Using PTY 643 | send_fd(socketfd, -1); 644 | } else { 645 | send_fd(socketfd, STDERR_FILENO); 646 | } 647 | 648 | // Number of command line arguments 649 | write_int(socketfd, mount_storage ? argc - 1 : argc); 650 | 651 | // Command line arguments 652 | int i; 653 | for (i = 0; i < argc; i++) { 654 | if (i == 1 && mount_storage) { 655 | continue; 656 | } 657 | write_string(socketfd, argv[i]); 658 | } 659 | 660 | // Wait for acknowledgement from daemon 661 | read_int(socketfd); 662 | 663 | if (atty & ATTY_IN) { 664 | setup_sighandlers(); 665 | pump_stdin_async(ptmx); 666 | } 667 | if (atty & ATTY_OUT) { 668 | pump_stdout_blocking(ptmx); 669 | } 670 | 671 | // Get the exit code 672 | int code = read_int(socketfd); 673 | close(socketfd); 674 | ALOGD("client exited %d", code); 675 | 676 | return code; 677 | } 678 | --------------------------------------------------------------------------------