├── MODULE_LICENSE_APACHE2 ├── Android.mk ├── README ├── utils.h ├── db.c ├── activity.c ├── su.h ├── utils.c ├── NOTICE └── su.c /MODULE_LICENSE_APACHE2: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | include $(CLEAR_VARS) 3 | 4 | LOCAL_MODULE := su 5 | LOCAL_SRC_FILES := su.c db.c activity.c utils.c 6 | 7 | LOCAL_STATIC_LIBRARIES := \ 8 | liblog \ 9 | libc \ 10 | 11 | LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) 12 | LOCAL_MODULE_TAGS := eng debug 13 | LOCAL_FORCE_STATIC_EXECUTABLE := true 14 | 15 | include $(BUILD_EXECUTABLE) -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | This is the su binary for Superuser on Android. 2 | 3 | Any app that requires root access must call this binary in order to be given root access. 4 | 5 | Branches for this repo support the following Android versions: 6 | 7 | - legacy 8 | - cupcake 9 | - donut 10 | 11 | - master 12 | - eclair 13 | - froyo 14 | - gingerbread 15 | - honeycomb* 16 | 17 | 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. 18 | 19 | * I have yet to personally test superuser on any honeycomb devices, therefore I cannot guarantee it's compatibility 20 | -------------------------------------------------------------------------------- /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 | /* reads a file, making sure it is terminated with \n \0 */ 21 | char* read_file(const char *fn, unsigned *_sz); 22 | 23 | int get_property(const char *data, char *found, const char *searchkey, const char *not_found); 24 | #endif -------------------------------------------------------------------------------- /db.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** Copyright 2010, Adam Shanks (@ChainsDD) 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 | 22 | #include "su.h" 23 | 24 | int database_check(const struct su_context *ctx) 25 | { 26 | FILE *fp; 27 | char allow = '-'; 28 | char *filename = malloc(snprintf(NULL, 0, "%s/%u-%u", REQUESTOR_STORED_PATH, ctx->from.uid, ctx->to.uid) + 1); 29 | sprintf(filename, "%s/%u-%u", REQUESTOR_STORED_PATH, ctx->from.uid, ctx->to.uid); 30 | if ((fp = fopen(filename, "r"))) { 31 | ALOGD("Found file"); 32 | char cmd[PATH_MAX]; 33 | fgets(cmd, sizeof(cmd), fp); 34 | int last = strlen(cmd) - 1; 35 | ALOGD("this is the last character %u of the string", cmd[5]); 36 | if (cmd[last] == '\n') { 37 | cmd[last] = '\0'; 38 | } 39 | ALOGD("Comparing %c %s, %u to %s", cmd[last - 2], cmd, last, get_command(&ctx->to)); 40 | if (strcmp(cmd, get_command(&ctx->to)) == 0) { 41 | allow = fgetc(fp); 42 | } 43 | fclose(fp); 44 | } else if ((fp = fopen(REQUESTOR_STORED_DEFAULT, "r"))) { 45 | ALOGD("Using default"); 46 | allow = fgetc(fp); 47 | fclose(fp); 48 | } 49 | free(filename); 50 | 51 | if (allow == '1') { 52 | return DB_ALLOW; 53 | } else if (allow == '0') { 54 | return DB_DENY; 55 | } else { 56 | return DB_INTERACTIVE; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /activity.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 | 20 | #include "su.h" 21 | 22 | int send_intent(const struct su_context *ctx, 23 | const char *socket_path, int allow, const char *action) 24 | { 25 | char command[PATH_MAX]; 26 | 27 | sprintf(command, "/system/bin/am broadcast -a '%s' --es socket '%s' --ei caller_uid '%d' --ei allow '%d' --ei version_code '%d' > /dev/null", 28 | action, socket_path, ctx->from.uid, allow, VERSION_CODE); 29 | 30 | // before sending the intent, make sure the (uid and euid) and (gid and egid) match, 31 | // otherwise LD_LIBRARY_PATH is wiped in Android 4.0+. 32 | // Also, sanitize all secure environment variables (from linker_environ.c in linker). 33 | 34 | /* The same list than GLibc at this point */ 35 | static const char* const unsec_vars[] = { 36 | "GCONV_PATH", 37 | "GETCONF_DIR", 38 | "HOSTALIASES", 39 | "LD_AUDIT", 40 | "LD_DEBUG", 41 | "LD_DEBUG_OUTPUT", 42 | "LD_DYNAMIC_WEAK", 43 | "LD_LIBRARY_PATH", 44 | "LD_ORIGIN_PATH", 45 | "LD_PRELOAD", 46 | "LD_PROFILE", 47 | "LD_SHOW_AUXV", 48 | "LD_USE_LOAD_BIAS", 49 | "LOCALDOMAIN", 50 | "LOCPATH", 51 | "MALLOC_TRACE", 52 | "MALLOC_CHECK_", 53 | "NIS_PATH", 54 | "NLSPATH", 55 | "RESOLV_HOST_CONF", 56 | "RES_OPTIONS", 57 | "TMPDIR", 58 | "TZDIR", 59 | "LD_AOUT_LIBRARY_PATH", 60 | "LD_AOUT_PRELOAD", 61 | // not listed in linker, used due to system() call 62 | "IFS", 63 | }; 64 | const char* const* cp = unsec_vars; 65 | const char* const* endp = cp + sizeof(unsec_vars)/sizeof(unsec_vars[0]); 66 | while (cp < endp) { 67 | unsetenv(*cp); 68 | cp++; 69 | } 70 | 71 | // sane value so "am" works 72 | setenv("LD_LIBRARY_PATH", "/vendor/lib:/system/lib", 1); 73 | setegid(getgid()); 74 | seteuid(getuid()); 75 | return system(command); 76 | } 77 | -------------------------------------------------------------------------------- /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 | #define REQUESTOR "com.noshufou.android.su" 27 | #define REQUESTOR_DATA_PATH "/data/data/" REQUESTOR 28 | #define REQUESTOR_CACHE_PATH REQUESTOR_DATA_PATH "/cache" 29 | 30 | #define REQUESTOR_STORED_PATH REQUESTOR_DATA_PATH "/files/stored" 31 | #define REQUESTOR_STORED_DEFAULT REQUESTOR_STORED_PATH "/default" 32 | 33 | /* intent actions */ 34 | #define ACTION_REQUEST REQUESTOR ".REQUEST" 35 | #define ACTION_RESULT REQUESTOR ".RESULT" 36 | 37 | #define DEFAULT_SHELL "/system/bin/sh" 38 | 39 | #ifdef SU_LEGACY_BUILD 40 | #define VERSION_EXTRA "l" 41 | #else 42 | #define VERSION_EXTRA "" 43 | #endif 44 | 45 | #define VERSION "3.2" VERSION_EXTRA 46 | #define VERSION_CODE 18 47 | 48 | #define DATABASE_VERSION 6 49 | #define PROTO_VERSION 0 50 | 51 | struct su_initiator { 52 | pid_t pid; 53 | unsigned uid; 54 | char bin[PATH_MAX]; 55 | char args[4096]; 56 | }; 57 | 58 | struct su_request { 59 | unsigned uid; 60 | int login; 61 | int keepenv; 62 | char *shell; 63 | char *command; 64 | char **argv; 65 | int argc; 66 | int optind; 67 | }; 68 | 69 | struct su_context { 70 | struct su_initiator from; 71 | struct su_request to; 72 | mode_t umask; 73 | }; 74 | 75 | enum { 76 | DB_INTERACTIVE, 77 | DB_DENY, 78 | DB_ALLOW 79 | }; 80 | 81 | extern int database_check(const struct su_context *ctx); 82 | 83 | extern int send_intent(const struct su_context *ctx, 84 | const char *socket_path, int allow, const char *action); 85 | 86 | static inline char *get_command(const struct su_request *to) 87 | { 88 | return (to->command) ? to->command : to->shell; 89 | } 90 | 91 | #if 0 92 | #undef LOGE 93 | #define LOGE(fmt,args...) fprintf(stderr, fmt, ##args) 94 | #undef LOGD 95 | #define LOGD(fmt,args...) fprintf(stderr, fmt, ##args) 96 | #undef LOGW 97 | #define LOGW(fmt,args...) fprintf(stderr, fmt, ##args) 98 | #endif 99 | 100 | #define PLOGE(fmt,args...) LOGE(fmt " failed with %d: %s", ##args, errno, strerror(errno)) 101 | #define PLOGEV(fmt,err,args...) LOGE(fmt " failed with %d: %s", ##args, err, strerror(err)) 102 | 103 | #endif 104 | -------------------------------------------------------------------------------- /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 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | /* reads a file, making sure it is terminated with \n \0 */ 30 | char* read_file(const char *fn, unsigned *_sz) 31 | { 32 | char *data; 33 | int sz; 34 | int fd; 35 | 36 | data = 0; 37 | fd = open(fn, O_RDONLY); 38 | if(fd < 0) return 0; 39 | 40 | sz = lseek(fd, 0, SEEK_END); 41 | if(sz < 0) goto oops; 42 | 43 | if(lseek(fd, 0, SEEK_SET) != 0) goto oops; 44 | 45 | data = (char*) malloc(sz + 2); 46 | if(data == 0) goto oops; 47 | 48 | if(read(fd, data, sz) != sz) goto oops; 49 | close(fd); 50 | data[sz] = '\n'; 51 | data[sz+1] = 0; 52 | if(_sz) *_sz = sz; 53 | return data; 54 | 55 | oops: 56 | close(fd); 57 | if(data != 0) free(data); 58 | return 0; 59 | } 60 | 61 | int get_property(const char *data, char *found, const char *searchkey, const char *not_found) 62 | { 63 | char *key, *value, *eol, *sol, *tmp; 64 | if (data == NULL) goto defval; 65 | int matched = 0; 66 | sol = strdup(data); 67 | while((eol = strchr(sol, '\n'))) { 68 | key = sol; 69 | *eol++ = 0; 70 | sol = eol; 71 | 72 | value = strchr(key, '='); 73 | if(value == 0) continue; 74 | *value++ = 0; 75 | 76 | while(isspace(*key)) key++; 77 | if(*key == '#') continue; 78 | tmp = value - 2; 79 | while((tmp > key) && isspace(*tmp)) *tmp-- = 0; 80 | 81 | while(isspace(*value)) value++; 82 | tmp = eol - 2; 83 | while((tmp > value) && isspace(*tmp)) *tmp-- = 0; 84 | 85 | if (strncmp(searchkey, key, strlen(searchkey)) == 0) { 86 | matched = 1; 87 | break; 88 | } 89 | } 90 | int len; 91 | if (matched) { 92 | len = strlen(value); 93 | if (len >= PROPERTY_VALUE_MAX) 94 | return -1; 95 | memcpy(found, value, len + 1); 96 | } else goto defval; 97 | return len; 98 | 99 | defval: 100 | len = strlen(not_found); 101 | memcpy(found, not_found, len + 1); 102 | return len; 103 | } -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | 2 | Copyright (c) 2005-2008, The Android Open Source 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 | 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | 13 | 14 | Apache License 15 | Version 2.0, January 2004 16 | http://www.apache.org/licenses/ 17 | 18 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 19 | 20 | 1. Definitions. 21 | 22 | "License" shall mean the terms and conditions for use, reproduction, 23 | and distribution as defined by Sections 1 through 9 of this document. 24 | 25 | "Licensor" shall mean the copyright owner or entity authorized by 26 | the copyright owner that is granting the License. 27 | 28 | "Legal Entity" shall mean the union of the acting entity and all 29 | other entities that control, are controlled by, or are under common 30 | control with that entity. For the purposes of this definition, 31 | "control" means (i) the power, direct or indirect, to cause the 32 | direction or management of such entity, whether by contract or 33 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 34 | outstanding shares, or (iii) beneficial ownership of such entity. 35 | 36 | "You" (or "Your") shall mean an individual or Legal Entity 37 | exercising permissions granted by this License. 38 | 39 | "Source" form shall mean the preferred form for making modifications, 40 | including but not limited to software source code, documentation 41 | source, and configuration files. 42 | 43 | "Object" form shall mean any form resulting from mechanical 44 | transformation or translation of a Source form, including but 45 | not limited to compiled object code, generated documentation, 46 | and conversions to other media types. 47 | 48 | "Work" shall mean the work of authorship, whether in Source or 49 | Object form, made available under the License, as indicated by a 50 | copyright notice that is included in or attached to the work 51 | (an example is provided in the Appendix below). 52 | 53 | "Derivative Works" shall mean any work, whether in Source or Object 54 | form, that is based on (or derived from) the Work and for which the 55 | editorial revisions, annotations, elaborations, or other modifications 56 | represent, as a whole, an original work of authorship. For the purposes 57 | of this License, Derivative Works shall not include works that remain 58 | separable from, or merely link (or bind by name) to the interfaces of, 59 | the Work and Derivative Works thereof. 60 | 61 | "Contribution" shall mean any work of authorship, including 62 | the original version of the Work and any modifications or additions 63 | to that Work or Derivative Works thereof, that is intentionally 64 | submitted to Licensor for inclusion in the Work by the copyright owner 65 | or by an individual or Legal Entity authorized to submit on behalf of 66 | the copyright owner. For the purposes of this definition, "submitted" 67 | means any form of electronic, verbal, or written communication sent 68 | to the Licensor or its representatives, including but not limited to 69 | communication on electronic mailing lists, source code control systems, 70 | and issue tracking systems that are managed by, or on behalf of, the 71 | Licensor for the purpose of discussing and improving the Work, but 72 | excluding communication that is conspicuously marked or otherwise 73 | designated in writing by the copyright owner as "Not a Contribution." 74 | 75 | "Contributor" shall mean Licensor and any individual or Legal Entity 76 | on behalf of whom a Contribution has been received by Licensor and 77 | subsequently incorporated within the Work. 78 | 79 | 2. Grant of Copyright License. Subject to the terms and conditions of 80 | this License, each Contributor hereby grants to You a perpetual, 81 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 82 | copyright license to reproduce, prepare Derivative Works of, 83 | publicly display, publicly perform, sublicense, and distribute the 84 | Work and such Derivative Works in Source or Object form. 85 | 86 | 3. Grant of Patent License. Subject to the terms and conditions of 87 | this License, each Contributor hereby grants to You a perpetual, 88 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 89 | (except as stated in this section) patent license to make, have made, 90 | use, offer to sell, sell, import, and otherwise transfer the Work, 91 | where such license applies only to those patent claims licensable 92 | by such Contributor that are necessarily infringed by their 93 | Contribution(s) alone or by combination of their Contribution(s) 94 | with the Work to which such Contribution(s) was submitted. If You 95 | institute patent litigation against any entity (including a 96 | cross-claim or counterclaim in a lawsuit) alleging that the Work 97 | or a Contribution incorporated within the Work constitutes direct 98 | or contributory patent infringement, then any patent licenses 99 | granted to You under this License for that Work shall terminate 100 | as of the date such litigation is filed. 101 | 102 | 4. Redistribution. You may reproduce and distribute copies of the 103 | Work or Derivative Works thereof in any medium, with or without 104 | modifications, and in Source or Object form, provided that You 105 | meet the following conditions: 106 | 107 | (a) You must give any other recipients of the Work or 108 | Derivative Works a copy of this License; and 109 | 110 | (b) You must cause any modified files to carry prominent notices 111 | stating that You changed the files; and 112 | 113 | (c) You must retain, in the Source form of any Derivative Works 114 | that You distribute, all copyright, patent, trademark, and 115 | attribution notices from the Source form of the Work, 116 | excluding those notices that do not pertain to any part of 117 | the Derivative Works; and 118 | 119 | (d) If the Work includes a "NOTICE" text file as part of its 120 | distribution, then any Derivative Works that You distribute must 121 | include a readable copy of the attribution notices contained 122 | within such NOTICE file, excluding those notices that do not 123 | pertain to any part of the Derivative Works, in at least one 124 | of the following places: within a NOTICE text file distributed 125 | as part of the Derivative Works; within the Source form or 126 | documentation, if provided along with the Derivative Works; or, 127 | within a display generated by the Derivative Works, if and 128 | wherever such third-party notices normally appear. The contents 129 | of the NOTICE file are for informational purposes only and 130 | do not modify the License. You may add Your own attribution 131 | notices within Derivative Works that You distribute, alongside 132 | or as an addendum to the NOTICE text from the Work, provided 133 | that such additional attribution notices cannot be construed 134 | as modifying the License. 135 | 136 | You may add Your own copyright statement to Your modifications and 137 | may provide additional or different license terms and conditions 138 | for use, reproduction, or distribution of Your modifications, or 139 | for any such Derivative Works as a whole, provided Your use, 140 | reproduction, and distribution of the Work otherwise complies with 141 | the conditions stated in this License. 142 | 143 | 5. Submission of Contributions. Unless You explicitly state otherwise, 144 | any Contribution intentionally submitted for inclusion in the Work 145 | by You to the Licensor shall be under the terms and conditions of 146 | this License, without any additional terms or conditions. 147 | Notwithstanding the above, nothing herein shall supersede or modify 148 | the terms of any separate license agreement you may have executed 149 | with Licensor regarding such Contributions. 150 | 151 | 6. Trademarks. This License does not grant permission to use the trade 152 | names, trademarks, service marks, or product names of the Licensor, 153 | except as required for reasonable and customary use in describing the 154 | origin of the Work and reproducing the content of the NOTICE file. 155 | 156 | 7. Disclaimer of Warranty. Unless required by applicable law or 157 | agreed to in writing, Licensor provides the Work (and each 158 | Contributor provides its Contributions) on an "AS IS" BASIS, 159 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 160 | implied, including, without limitation, any warranties or conditions 161 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 162 | PARTICULAR PURPOSE. You are solely responsible for determining the 163 | appropriateness of using or redistributing the Work and assume any 164 | risks associated with Your exercise of permissions under this License. 165 | 166 | 8. Limitation of Liability. In no event and under no legal theory, 167 | whether in tort (including negligence), contract, or otherwise, 168 | unless required by applicable law (such as deliberate and grossly 169 | negligent acts) or agreed to in writing, shall any Contributor be 170 | liable to You for damages, including any direct, indirect, special, 171 | incidental, or consequential damages of any character arising as a 172 | result of this License or out of the use or inability to use the 173 | Work (including but not limited to damages for loss of goodwill, 174 | work stoppage, computer failure or malfunction, or any and all 175 | other commercial damages or losses), even if such Contributor 176 | has been advised of the possibility of such damages. 177 | 178 | 9. Accepting Warranty or Additional Liability. While redistributing 179 | the Work or Derivative Works thereof, You may choose to offer, 180 | and charge a fee for, acceptance of support, warranty, indemnity, 181 | or other liability obligations and/or rights consistent with this 182 | License. However, in accepting such obligations, You may act only 183 | on Your own behalf and on Your sole responsibility, not on behalf 184 | of any other Contributor, and only if You agree to indemnify, 185 | defend, and hold each Contributor harmless for any liability 186 | incurred by, or claims asserted against, such Contributor by reason 187 | of your accepting any such warranty or additional liability. 188 | 189 | END OF TERMS AND CONDITIONS 190 | 191 | -------------------------------------------------------------------------------- /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 | 36 | #include 37 | #include 38 | #include 39 | 40 | #include "su.h" 41 | #include "utils.h" 42 | 43 | /* Still lazt, will fix this */ 44 | static char socket_path[PATH_MAX]; 45 | 46 | static int from_init(struct su_initiator *from) 47 | { 48 | char path[PATH_MAX], exe[PATH_MAX]; 49 | char args[4096], *argv0, *argv_rest; 50 | int fd; 51 | ssize_t len; 52 | int i; 53 | int err; 54 | 55 | from->uid = getuid(); 56 | from->pid = getppid(); 57 | 58 | /* Get the command line */ 59 | snprintf(path, sizeof(path), "/proc/%u/cmdline", from->pid); 60 | fd = open(path, O_RDONLY); 61 | if (fd < 0) { 62 | ALOGE("Opening command line"); 63 | return -1; 64 | } 65 | len = read(fd, args, sizeof(args)); 66 | err = errno; 67 | close(fd); 68 | if (len < 0 || len == sizeof(args)) { 69 | ALOGE("Reading command line", err); 70 | return -1; 71 | } 72 | 73 | argv0 = args; 74 | argv_rest = NULL; 75 | for (i = 0; i < len; i++) { 76 | if (args[i] == '\0') { 77 | if (!argv_rest) { 78 | argv_rest = &args[i+1]; 79 | } else { 80 | args[i] = ' '; 81 | } 82 | } 83 | } 84 | args[len] = '\0'; 85 | 86 | if (argv_rest) { 87 | strncpy(from->args, argv_rest, sizeof(from->args)); 88 | from->args[sizeof(from->args)-1] = '\0'; 89 | } else { 90 | from->args[0] = '\0'; 91 | } 92 | 93 | /* If this isn't app_process, use the real path instead of argv[0] */ 94 | snprintf(path, sizeof(path), "/proc/%u/exe", from->pid); 95 | len = readlink(path, exe, sizeof(exe)); 96 | if (len < 0) { 97 | ALOGE("Getting exe path"); 98 | return -1; 99 | } 100 | exe[len] = '\0'; 101 | if (strcmp(exe, "/system/bin/app_process")) { 102 | argv0 = exe; 103 | } 104 | 105 | strncpy(from->bin, argv0, sizeof(from->bin)); 106 | from->bin[sizeof(from->bin)-1] = '\0'; 107 | 108 | return 0; 109 | } 110 | 111 | static void populate_environment(const struct su_context *ctx) 112 | { 113 | struct passwd *pw; 114 | 115 | if (ctx->to.keepenv) 116 | return; 117 | 118 | pw = getpwuid(ctx->to.uid); 119 | if (pw) { 120 | setenv("HOME", pw->pw_dir, 1); 121 | setenv("SHELL", ctx->to.shell, 1); 122 | if (ctx->to.login || ctx->to.uid) { 123 | setenv("USER", pw->pw_name, 1); 124 | setenv("LOGNAME", pw->pw_name, 1); 125 | } 126 | } 127 | } 128 | 129 | static void socket_cleanup(void) 130 | { 131 | unlink(socket_path); 132 | } 133 | 134 | static void cleanup(void) 135 | { 136 | socket_cleanup(); 137 | } 138 | 139 | static void cleanup_signal(int sig) 140 | { 141 | socket_cleanup(); 142 | exit(128 + sig); 143 | } 144 | 145 | static int socket_create_temp(char *path, size_t len) 146 | { 147 | int fd; 148 | struct sockaddr_un sun; 149 | 150 | fd = socket(AF_LOCAL, SOCK_STREAM, 0); 151 | if (fd < 0) { 152 | ALOGE("socket"); 153 | return -1; 154 | } 155 | 156 | memset(&sun, 0, sizeof(sun)); 157 | sun.sun_family = AF_LOCAL; 158 | snprintf(path, len, "%s/.socket%d", REQUESTOR_CACHE_PATH, getpid()); 159 | snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", path); 160 | 161 | /* 162 | * Delete the socket to protect from situations when 163 | * something bad occured previously and the kernel reused pid from that process. 164 | * Small probability, isn't it. 165 | */ 166 | unlink(sun.sun_path); 167 | 168 | if (bind(fd, (struct sockaddr*)&sun, sizeof(sun)) < 0) { 169 | ALOGE("bind"); 170 | goto err; 171 | } 172 | 173 | if (listen(fd, 1) < 0) { 174 | ALOGE("listen"); 175 | goto err; 176 | } 177 | 178 | return fd; 179 | err: 180 | close(fd); 181 | return -1; 182 | } 183 | 184 | static int socket_accept(int serv_fd) 185 | { 186 | struct timeval tv; 187 | fd_set fds; 188 | int fd; 189 | 190 | /* Wait 20 seconds for a connection, then give up. */ 191 | tv.tv_sec = 20; 192 | tv.tv_usec = 0; 193 | FD_ZERO(&fds); 194 | FD_SET(serv_fd, &fds); 195 | if (select(serv_fd + 1, &fds, NULL, NULL, &tv) < 1) { 196 | ALOGE("select"); 197 | return -1; 198 | } 199 | 200 | fd = accept(serv_fd, NULL, NULL); 201 | if (fd < 0) { 202 | ALOGE("accept"); 203 | return -1; 204 | } 205 | 206 | return fd; 207 | } 208 | 209 | static int socket_send_request(int fd, const struct su_context *ctx) 210 | { 211 | size_t len; 212 | size_t bin_size, cmd_size; 213 | char *cmd; 214 | 215 | #define write_token(fd, data) \ 216 | do { \ 217 | uint32_t __data = htonl(data); \ 218 | size_t __count = sizeof(__data); \ 219 | size_t __len = write((fd), &__data, __count); \ 220 | if (__len != __count) { \ 221 | ALOGE("write(" #data ")"); \ 222 | return -1; \ 223 | } \ 224 | } while (0) 225 | 226 | write_token(fd, PROTO_VERSION); 227 | write_token(fd, PATH_MAX); 228 | write_token(fd, ARG_MAX); 229 | write_token(fd, ctx->from.uid); 230 | write_token(fd, ctx->to.uid); 231 | bin_size = strlen(ctx->from.bin) + 1; 232 | write_token(fd, bin_size); 233 | len = write(fd, ctx->from.bin, bin_size); 234 | if (len != bin_size) { 235 | ALOGE("write(bin)"); 236 | return -1; 237 | } 238 | cmd = get_command(&ctx->to); 239 | cmd_size = strlen(cmd) + 1; 240 | write_token(fd, cmd_size); 241 | len = write(fd, cmd, cmd_size); 242 | if (len != cmd_size) { 243 | ALOGE("write(cmd)"); 244 | return -1; 245 | } 246 | return 0; 247 | } 248 | 249 | static int socket_receive_result(int fd, char *result, ssize_t result_len) 250 | { 251 | ssize_t len; 252 | 253 | len = read(fd, result, result_len-1); 254 | if (len < 0) { 255 | ALOGE("read(result)"); 256 | return -1; 257 | } 258 | result[len] = '\0'; 259 | 260 | return 0; 261 | } 262 | 263 | static void usage(int status) 264 | { 265 | FILE *stream = (status == EXIT_SUCCESS) ? stdout : stderr; 266 | 267 | fprintf(stream, 268 | "Usage: su [options] [--] [-] [LOGIN] [--] [args...]\n\n" 269 | "Options:\n" 270 | " -c, --command COMMAND pass COMMAND to the invoked shell\n" 271 | " -h, --help display this help message and exit\n" 272 | " -, -l, --login pretend the shell to be a login shell\n" 273 | " -m, -p,\n" 274 | " --preserve-environment do not change environment variables\n" 275 | " -s, --shell SHELL use SHELL instead of the default " DEFAULT_SHELL "\n" 276 | " -v, --version display version number and exit\n" 277 | " -V display version code and exit,\n" 278 | " this is used almost exclusively by Superuser.apk\n"); 279 | exit(status); 280 | } 281 | 282 | static void deny(const struct su_context *ctx) 283 | { 284 | char *cmd = get_command(&ctx->to); 285 | 286 | send_intent(ctx, "", 0, ACTION_RESULT); 287 | ALOGW("request rejected (%u->%u %s)", ctx->from.uid, ctx->to.uid, cmd); 288 | fprintf(stderr, "%s\n", strerror(EACCES)); 289 | exit(EXIT_FAILURE); 290 | } 291 | 292 | static void allow(const struct su_context *ctx) 293 | { 294 | char *arg0; 295 | int argc, err; 296 | 297 | umask(ctx->umask); 298 | send_intent(ctx, "", 1, ACTION_RESULT); 299 | 300 | arg0 = strrchr (ctx->to.shell, '/'); 301 | arg0 = (arg0) ? arg0 + 1 : ctx->to.shell; 302 | if (ctx->to.login) { 303 | int s = strlen(arg0) + 2; 304 | char *p = malloc(s); 305 | 306 | if (!p) 307 | exit(EXIT_FAILURE); 308 | 309 | *p = '-'; 310 | strcpy(p + 1, arg0); 311 | arg0 = p; 312 | } 313 | 314 | /* 315 | * Set effective uid back to root, otherwise setres[ug]id will fail 316 | * if ctx->to.uid isn't root. 317 | */ 318 | if (seteuid(0)) { 319 | ALOGE("seteuid (root)"); 320 | exit(EXIT_FAILURE); 321 | } 322 | 323 | populate_environment(ctx); 324 | 325 | if (setresgid(ctx->to.uid, ctx->to.uid, ctx->to.uid)) { 326 | ALOGE("setresgid (%u)", ctx->to.uid); 327 | exit(EXIT_FAILURE); 328 | } 329 | if (setresuid(ctx->to.uid, ctx->to.uid, ctx->to.uid)) { 330 | ALOGE("setresuid (%u)", ctx->to.uid); 331 | exit(EXIT_FAILURE); 332 | } 333 | 334 | #define PARG(arg) \ 335 | (ctx->to.optind + (arg) < ctx->to.argc) ? " " : "", \ 336 | (ctx->to.optind + (arg) < ctx->to.argc) ? ctx->to.argv[ctx->to.optind + (arg)] : "" 337 | 338 | ALOGD("%u %s executing %u %s using shell %s : %s%s%s%s%s%s%s%s%s%s%s%s%s%s", 339 | ctx->from.uid, ctx->from.bin, 340 | ctx->to.uid, get_command(&ctx->to), ctx->to.shell, 341 | arg0, PARG(0), PARG(1), PARG(2), PARG(3), PARG(4), PARG(5), 342 | (ctx->to.optind + 6 < ctx->to.argc) ? " ..." : ""); 343 | 344 | argc = ctx->to.optind; 345 | if (ctx->to.command) { 346 | ctx->to.argv[--argc] = ctx->to.command; 347 | ctx->to.argv[--argc] = "-c"; 348 | } 349 | ctx->to.argv[--argc] = arg0; 350 | execv(ctx->to.shell, ctx->to.argv + argc); 351 | err = errno; 352 | ALOGE("exec"); 353 | fprintf(stderr, "Cannot execute %s: %s\n", ctx->to.shell, strerror(err)); 354 | exit(EXIT_FAILURE); 355 | } 356 | 357 | int main(int argc, char *argv[]) 358 | { 359 | struct su_context ctx = { 360 | .from = { 361 | .pid = -1, 362 | .uid = 0, 363 | .bin = "", 364 | .args = "", 365 | }, 366 | .to = { 367 | .uid = AID_ROOT, 368 | .login = 0, 369 | .keepenv = 0, 370 | .shell = DEFAULT_SHELL, 371 | .command = NULL, 372 | .argv = argv, 373 | .argc = argc, 374 | .optind = 0, 375 | }, 376 | }; 377 | struct stat st; 378 | int socket_serv_fd, fd; 379 | char buf[64], *result, debuggable[PROPERTY_VALUE_MAX]; 380 | char enabled[PROPERTY_VALUE_MAX], build_type[PROPERTY_VALUE_MAX]; 381 | char cm_version[PROPERTY_VALUE_MAX];; 382 | int c, dballow, len; 383 | struct option long_opts[] = { 384 | { "command", required_argument, NULL, 'c' }, 385 | { "help", no_argument, NULL, 'h' }, 386 | { "login", no_argument, NULL, 'l' }, 387 | { "preserve-environment", no_argument, NULL, 'p' }, 388 | { "shell", required_argument, NULL, 's' }, 389 | { "version", no_argument, NULL, 'v' }, 390 | { NULL, 0, NULL, 0 }, 391 | }; 392 | char *data; 393 | unsigned sz; 394 | 395 | while ((c = getopt_long(argc, argv, "+c:hlmps:Vv", long_opts, NULL)) != -1) { 396 | switch(c) { 397 | case 'c': 398 | ctx.to.command = optarg; 399 | break; 400 | case 'h': 401 | usage(EXIT_SUCCESS); 402 | break; 403 | case 'l': 404 | ctx.to.login = 1; 405 | break; 406 | case 'm': 407 | case 'p': 408 | ctx.to.keepenv = 1; 409 | break; 410 | case 's': 411 | ctx.to.shell = optarg; 412 | break; 413 | case 'V': 414 | printf("%d\n", VERSION_CODE); 415 | exit(EXIT_SUCCESS); 416 | case 'v': 417 | printf("%s\n", VERSION); 418 | exit(EXIT_SUCCESS); 419 | default: 420 | /* Bionic getopt_long doesn't terminate its error output by newline */ 421 | fprintf(stderr, "\n"); 422 | usage(2); 423 | } 424 | } 425 | if (optind < argc && !strcmp(argv[optind], "-")) { 426 | ctx.to.login = 1; 427 | optind++; 428 | } 429 | /* username or uid */ 430 | if (optind < argc && strcmp(argv[optind], "--")) { 431 | struct passwd *pw; 432 | pw = getpwnam(argv[optind]); 433 | if (!pw) { 434 | char *endptr; 435 | 436 | /* It seems we shouldn't do this at all */ 437 | errno = 0; 438 | ctx.to.uid = strtoul(argv[optind], &endptr, 10); 439 | if (errno || *endptr) { 440 | ALOGE("Unknown id: %s\n", argv[optind]); 441 | fprintf(stderr, "Unknown id: %s\n", argv[optind]); 442 | exit(EXIT_FAILURE); 443 | } 444 | } else { 445 | ctx.to.uid = pw->pw_uid; 446 | } 447 | optind++; 448 | } 449 | if (optind < argc && !strcmp(argv[optind], "--")) { 450 | optind++; 451 | } 452 | ctx.to.optind = optind; 453 | 454 | if (from_init(&ctx.from) < 0) { 455 | deny(&ctx); 456 | } 457 | 458 | // we can't simply use the property service, since we aren't launched from init and 459 | // can't trust the location of the property workspace. find the properties ourselves. 460 | data = read_file("/default.prop", &sz); 461 | get_property(data, debuggable, "ro.debuggable", "0"); 462 | free(data); 463 | 464 | data = read_file("/system/build.prop", &sz); 465 | get_property(data, cm_version, "ro.cm.version", ""); 466 | get_property(data, build_type, "ro.build.type", ""); 467 | free(data); 468 | 469 | data = read_file("/data/property/persist.sys.root_access", &sz); 470 | if (data != NULL) { 471 | len = strlen(data); 472 | if (len >= PROPERTY_VALUE_MAX) 473 | memcpy(enabled, "1", 2); 474 | else 475 | memcpy(enabled, data, len + 1); 476 | free(data); 477 | } else 478 | memcpy(enabled, "1", 2); 479 | 480 | ctx.umask = umask(027); 481 | 482 | // CyanogenMod-specific behavior 483 | if (strlen(cm_version) > 0) { 484 | // only allow su on debuggable builds 485 | if (strcmp("1", debuggable) != 0) { 486 | ALOGE("Root access is disabled on non-debug builds"); 487 | deny(&ctx); 488 | } 489 | 490 | // enforce persist.sys.root_access on non-eng builds 491 | if (strcmp("eng", build_type) != 0 && 492 | (atoi(enabled) & 1) != 1 ) { 493 | ALOGE("Root access is disabled by system setting - enable it under settings -> developer options"); 494 | deny(&ctx); 495 | } 496 | 497 | // disallow su in a shell if appropriate 498 | if (ctx.from.uid == AID_SHELL && (atoi(enabled) == 1)) { 499 | ALOGE("Root access is disabled by a system setting - enable it under settings -> developer options"); 500 | deny(&ctx); 501 | } 502 | } 503 | 504 | if (ctx.from.uid == AID_ROOT || ctx.from.uid == AID_SHELL) 505 | allow(&ctx); 506 | 507 | if (stat(REQUESTOR_DATA_PATH, &st) < 0) { 508 | ALOGE("stat"); 509 | deny(&ctx); 510 | } 511 | 512 | if (st.st_gid != st.st_uid) 513 | { 514 | ALOGE("Bad uid/gid %d/%d for Superuser Requestor application", 515 | (int)st.st_uid, (int)st.st_gid); 516 | deny(&ctx); 517 | } 518 | 519 | mkdir(REQUESTOR_CACHE_PATH, 0770); 520 | if (chown(REQUESTOR_CACHE_PATH, st.st_uid, st.st_gid)) { 521 | ALOGE("chown (%s, %ld, %ld)", REQUESTOR_CACHE_PATH, st.st_uid, st.st_gid); 522 | deny(&ctx); 523 | } 524 | 525 | if (setgroups(0, NULL)) { 526 | ALOGE("setgroups"); 527 | deny(&ctx); 528 | } 529 | if (setegid(st.st_gid)) { 530 | ALOGE("setegid (%lu)", st.st_gid); 531 | deny(&ctx); 532 | } 533 | if (seteuid(st.st_uid)) { 534 | ALOGE("seteuid (%lu)", st.st_uid); 535 | deny(&ctx); 536 | } 537 | 538 | dballow = database_check(&ctx); 539 | switch (dballow) { 540 | case DB_DENY: deny(&ctx); 541 | case DB_ALLOW: allow(&ctx); 542 | case DB_INTERACTIVE: break; 543 | default: deny(&ctx); 544 | } 545 | 546 | socket_serv_fd = socket_create_temp(socket_path, sizeof(socket_path)); 547 | if (socket_serv_fd < 0) { 548 | deny(&ctx); 549 | } 550 | 551 | signal(SIGHUP, cleanup_signal); 552 | signal(SIGPIPE, cleanup_signal); 553 | signal(SIGTERM, cleanup_signal); 554 | signal(SIGQUIT, cleanup_signal); 555 | signal(SIGINT, cleanup_signal); 556 | signal(SIGABRT, cleanup_signal); 557 | atexit(cleanup); 558 | 559 | if (send_intent(&ctx, socket_path, -1, ACTION_REQUEST) < 0) { 560 | deny(&ctx); 561 | } 562 | 563 | fd = socket_accept(socket_serv_fd); 564 | if (fd < 0) { 565 | deny(&ctx); 566 | } 567 | if (socket_send_request(fd, &ctx)) { 568 | deny(&ctx); 569 | } 570 | if (socket_receive_result(fd, buf, sizeof(buf))) { 571 | deny(&ctx); 572 | } 573 | 574 | close(fd); 575 | close(socket_serv_fd); 576 | socket_cleanup(); 577 | 578 | result = buf; 579 | 580 | #define SOCKET_RESPONSE "socket:" 581 | if (strncmp(result, SOCKET_RESPONSE, sizeof(SOCKET_RESPONSE) - 1)) 582 | ALOGW("SECURITY RISK: Requestor still receives credentials in intent"); 583 | else 584 | result += sizeof(SOCKET_RESPONSE) - 1; 585 | 586 | if (!strcmp(result, "DENY")) { 587 | deny(&ctx); 588 | } else if (!strcmp(result, "ALLOW")) { 589 | allow(&ctx); 590 | } else { 591 | ALOGE("unknown response from Superuser Requestor: %s", result); 592 | deny(&ctx); 593 | } 594 | 595 | deny(&ctx); 596 | return -1; 597 | } 598 | --------------------------------------------------------------------------------