Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 1 | /* |
| 2 | ** Copyright 2010, Adam Shanks (@ChainsDD) |
| 3 | ** Copyright 2008, Zinx Verituse (@zinxv) |
Bruno Martins | bffcdef | 2018-08-27 18:24:41 +0100 | [diff] [blame] | 4 | ** Copyright 2017-2018, The LineageOS Project |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 5 | ** |
| 6 | ** Licensed under the Apache License, Version 2.0 (the "License"); |
| 7 | ** you may not use this file except in compliance with the License. |
| 8 | ** You may obtain a copy of the License at |
| 9 | ** |
| 10 | ** http://www.apache.org/licenses/LICENSE-2.0 |
| 11 | ** |
| 12 | ** Unless required by applicable law or agreed to in writing, software |
| 13 | ** distributed under the License is distributed on an "AS IS" BASIS, |
| 14 | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 15 | ** See the License for the specific language governing permissions and |
| 16 | ** limitations under the License. |
| 17 | */ |
| 18 | |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 19 | #include <getopt.h> |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 20 | #include <pwd.h> |
Luca Stefani | dd47f31 | 2018-08-26 12:59:23 +0200 | [diff] [blame] | 21 | #include <stdlib.h> |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 22 | #include <sys/stat.h> |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 23 | #include <sys/types.h> |
Luca Stefani | dd47f31 | 2018-08-26 12:59:23 +0200 | [diff] [blame] | 24 | #include <sys/wait.h> |
| 25 | #include <unistd.h> |
| 26 | |
Luca Stefani | 7c86b05 | 2018-08-26 11:50:32 +0200 | [diff] [blame] | 27 | #include <cutils/android_filesystem_config.h> |
| 28 | #include <cutils/properties.h> |
Diogo Ferreira | 9d0f234 | 2014-12-16 18:18:28 +0000 | [diff] [blame] | 29 | #include <log/log.h> |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 30 | |
Luca Stefani | dd47f31 | 2018-08-26 12:59:23 +0200 | [diff] [blame] | 31 | #include "binder/pm-wrapper.h" |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 32 | #include "su.h" |
| 33 | #include "utils.h" |
| 34 | |
Koushik Dutta | c200cf7 | 2013-07-26 21:35:23 -0700 | [diff] [blame] | 35 | extern int is_daemon; |
| 36 | extern int daemon_from_uid; |
| 37 | extern int daemon_from_pid; |
| 38 | |
Koushik Dutta | 5f2c658 | 2013-07-30 15:38:48 -0700 | [diff] [blame] | 39 | int fork_zero_fucks() { |
| 40 | int pid = fork(); |
| 41 | if (pid) { |
| 42 | int status; |
| 43 | waitpid(pid, &status, 0); |
| 44 | return pid; |
Luca Stefani | 5eb52ea | 2018-08-26 11:35:00 +0200 | [diff] [blame] | 45 | } else { |
| 46 | if ((pid = fork())) exit(0); |
Koushik Dutta | 5f2c658 | 2013-07-30 15:38:48 -0700 | [diff] [blame] | 47 | return 0; |
| 48 | } |
| 49 | } |
| 50 | |
Luca Stefani | 5eb52ea | 2018-08-26 11:35:00 +0200 | [diff] [blame] | 51 | static int from_init(struct su_initiator* from) { |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 52 | char path[PATH_MAX], exe[PATH_MAX]; |
| 53 | char args[4096], *argv0, *argv_rest; |
| 54 | int fd; |
| 55 | ssize_t len; |
| 56 | int i; |
| 57 | int err; |
| 58 | |
| 59 | from->uid = getuid(); |
| 60 | from->pid = getppid(); |
| 61 | |
Marcos Marado | f465a0a | 2014-02-01 22:58:19 +0000 | [diff] [blame] | 62 | if (is_daemon) { |
| 63 | from->uid = daemon_from_uid; |
| 64 | from->pid = daemon_from_pid; |
| 65 | } |
| 66 | |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 67 | /* Get the command line */ |
Tom Marshall | beddaf7 | 2018-08-24 19:38:42 +0200 | [diff] [blame] | 68 | snprintf(path, sizeof(path), "/proc/%d/cmdline", from->pid); |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 69 | fd = open(path, O_RDONLY); |
| 70 | if (fd < 0) { |
| 71 | PLOGE("Opening command line"); |
| 72 | return -1; |
| 73 | } |
| 74 | len = read(fd, args, sizeof(args)); |
| 75 | err = errno; |
| 76 | close(fd); |
| 77 | if (len < 0 || len == sizeof(args)) { |
| 78 | PLOGEV("Reading command line", err); |
| 79 | return -1; |
| 80 | } |
| 81 | |
| 82 | argv0 = args; |
| 83 | argv_rest = NULL; |
| 84 | for (i = 0; i < len; i++) { |
| 85 | if (args[i] == '\0') { |
| 86 | if (!argv_rest) { |
Luca Stefani | 5eb52ea | 2018-08-26 11:35:00 +0200 | [diff] [blame] | 87 | argv_rest = &args[i + 1]; |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 88 | } else { |
| 89 | args[i] = ' '; |
| 90 | } |
| 91 | } |
| 92 | } |
| 93 | args[len] = '\0'; |
| 94 | |
| 95 | if (argv_rest) { |
Daniel Micay | c9d292e | 2015-11-08 16:52:17 -0500 | [diff] [blame] | 96 | if (strlcpy(from->args, argv_rest, sizeof(from->args)) >= sizeof(from->args)) { |
| 97 | ALOGE("argument too long"); |
| 98 | return -1; |
| 99 | } |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 100 | } else { |
| 101 | from->args[0] = '\0'; |
| 102 | } |
| 103 | |
| 104 | /* If this isn't app_process, use the real path instead of argv[0] */ |
Tom Marshall | beddaf7 | 2018-08-24 19:38:42 +0200 | [diff] [blame] | 105 | snprintf(path, sizeof(path), "/proc/%d/exe", from->pid); |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 106 | len = readlink(path, exe, sizeof(exe)); |
| 107 | if (len < 0) { |
| 108 | PLOGE("Getting exe path"); |
| 109 | return -1; |
| 110 | } |
| 111 | exe[len] = '\0'; |
Tom Marshall | beddaf7 | 2018-08-24 19:38:42 +0200 | [diff] [blame] | 112 | if (strcmp(exe, "/system/bin/app_process") != 0) { |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 113 | argv0 = exe; |
| 114 | } |
| 115 | |
Daniel Micay | c9d292e | 2015-11-08 16:52:17 -0500 | [diff] [blame] | 116 | if (strlcpy(from->bin, argv0, sizeof(from->bin)) >= sizeof(from->bin)) { |
| 117 | ALOGE("binary path too long"); |
| 118 | return -1; |
| 119 | } |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 120 | |
Luca Stefani | 5eb52ea | 2018-08-26 11:35:00 +0200 | [diff] [blame] | 121 | struct passwd* pw; |
Koushik Dutta | a194322 | 2013-02-22 00:24:54 -0800 | [diff] [blame] | 122 | pw = getpwuid(from->uid); |
| 123 | if (pw && pw->pw_name) { |
Daniel Micay | c9d292e | 2015-11-08 16:52:17 -0500 | [diff] [blame] | 124 | if (strlcpy(from->name, pw->pw_name, sizeof(from->name)) >= sizeof(from->name)) { |
| 125 | ALOGE("name too long"); |
| 126 | return -1; |
| 127 | } |
Koushik Dutta | a194322 | 2013-02-22 00:24:54 -0800 | [diff] [blame] | 128 | } |
| 129 | |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 130 | return 0; |
| 131 | } |
| 132 | |
Luca Stefani | 5eb52ea | 2018-08-26 11:35:00 +0200 | [diff] [blame] | 133 | static void populate_environment(const struct su_context* ctx) { |
| 134 | struct passwd* pw; |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 135 | |
Luca Stefani | 5eb52ea | 2018-08-26 11:35:00 +0200 | [diff] [blame] | 136 | if (ctx->to.keepenv) return; |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 137 | |
| 138 | pw = getpwuid(ctx->to.uid); |
| 139 | if (pw) { |
| 140 | setenv("HOME", pw->pw_dir, 1); |
Koushik Dutta | 379371b | 2013-03-11 17:30:53 -0700 | [diff] [blame] | 141 | if (ctx->to.shell) |
| 142 | setenv("SHELL", ctx->to.shell, 1); |
| 143 | else |
| 144 | setenv("SHELL", DEFAULT_SHELL, 1); |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 145 | if (ctx->to.login || ctx->to.uid) { |
| 146 | setenv("USER", pw->pw_name, 1); |
| 147 | setenv("LOGNAME", pw->pw_name, 1); |
| 148 | } |
| 149 | } |
| 150 | } |
| 151 | |
Koushik Dutta | 045c68a | 2013-02-17 17:03:56 -0800 | [diff] [blame] | 152 | void set_identity(unsigned int uid) { |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 153 | /* |
| 154 | * Set effective uid back to root, otherwise setres[ug]id will fail |
| 155 | * if uid isn't root. |
| 156 | */ |
| 157 | if (seteuid(0)) { |
| 158 | PLOGE("seteuid (root)"); |
| 159 | exit(EXIT_FAILURE); |
| 160 | } |
| 161 | if (setresgid(uid, uid, uid)) { |
| 162 | PLOGE("setresgid (%u)", uid); |
| 163 | exit(EXIT_FAILURE); |
| 164 | } |
| 165 | if (setresuid(uid, uid, uid)) { |
| 166 | PLOGE("setresuid (%u)", uid); |
| 167 | exit(EXIT_FAILURE); |
| 168 | } |
| 169 | } |
| 170 | |
Koushik Dutta | 045c68a | 2013-02-17 17:03:56 -0800 | [diff] [blame] | 171 | static void usage(int status) { |
Luca Stefani | 5eb52ea | 2018-08-26 11:35:00 +0200 | [diff] [blame] | 172 | FILE* stream = (status == EXIT_SUCCESS) ? stdout : stderr; |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 173 | |
| 174 | fprintf(stream, |
Luca Stefani | 5eb52ea | 2018-08-26 11:35:00 +0200 | [diff] [blame] | 175 | "Usage: su [options] [--] [-] [LOGIN] [--] [args...]\n\n" |
| 176 | "Options:\n" |
| 177 | " --daemon start the su daemon agent\n" |
| 178 | " -c, --command COMMAND pass COMMAND to the invoked shell\n" |
| 179 | " -h, --help display this help message and exit\n" |
| 180 | " -, -l, --login pretend the shell to be a login shell\n" |
| 181 | " -m, -p,\n" |
| 182 | " --preserve-environment do not change environment variables\n" |
| 183 | " -s, --shell SHELL use SHELL instead of the default " DEFAULT_SHELL |
| 184 | "\n" |
| 185 | " -v, --version display version number and exit\n" |
| 186 | " -V display version code and exit,\n" |
| 187 | " this is used almost exclusively by Superuser.apk\n"); |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 188 | exit(status); |
| 189 | } |
| 190 | |
Luca Stefani | 5eb52ea | 2018-08-26 11:35:00 +0200 | [diff] [blame] | 191 | static __attribute__((noreturn)) void deny(struct su_context* ctx) { |
| 192 | char* cmd = get_command(&ctx->to); |
Diogo Ferreira | 4aaba1e | 2014-12-16 17:58:55 +0000 | [diff] [blame] | 193 | ALOGW("request rejected (%u->%u %s)", ctx->from.uid, ctx->to.uid, cmd); |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 194 | fprintf(stderr, "%s\n", strerror(EACCES)); |
| 195 | exit(EXIT_FAILURE); |
| 196 | } |
| 197 | |
Luca Stefani | 5eb52ea | 2018-08-26 11:35:00 +0200 | [diff] [blame] | 198 | static __attribute__((noreturn)) void allow(struct su_context* ctx, const char* packageName) { |
| 199 | char* arg0; |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 200 | int argc, err; |
| 201 | |
| 202 | umask(ctx->umask); |
Koushik Dutta | 379371b | 2013-03-11 17:30:53 -0700 | [diff] [blame] | 203 | |
Luca Stefani | 5eb52ea | 2018-08-26 11:35:00 +0200 | [diff] [blame] | 204 | char* binary; |
Koushik Dutta | 379371b | 2013-03-11 17:30:53 -0700 | [diff] [blame] | 205 | argc = ctx->to.optind; |
| 206 | if (ctx->to.command) { |
| 207 | binary = ctx->to.shell; |
| 208 | ctx->to.argv[--argc] = ctx->to.command; |
| 209 | ctx->to.argv[--argc] = "-c"; |
Luca Stefani | 5eb52ea | 2018-08-26 11:35:00 +0200 | [diff] [blame] | 210 | } else if (ctx->to.shell) { |
Koushik Dutta | 379371b | 2013-03-11 17:30:53 -0700 | [diff] [blame] | 211 | binary = ctx->to.shell; |
Luca Stefani | 5eb52ea | 2018-08-26 11:35:00 +0200 | [diff] [blame] | 212 | } else { |
Koushik Dutta | 379371b | 2013-03-11 17:30:53 -0700 | [diff] [blame] | 213 | if (ctx->to.argv[argc]) { |
| 214 | binary = ctx->to.argv[argc++]; |
Luca Stefani | 5eb52ea | 2018-08-26 11:35:00 +0200 | [diff] [blame] | 215 | } else { |
Koushik Dutta | 379371b | 2013-03-11 17:30:53 -0700 | [diff] [blame] | 216 | binary = DEFAULT_SHELL; |
| 217 | } |
| 218 | } |
| 219 | |
Luca Stefani | 5eb52ea | 2018-08-26 11:35:00 +0200 | [diff] [blame] | 220 | arg0 = strrchr(binary, '/'); |
Koushik Dutta | 379371b | 2013-03-11 17:30:53 -0700 | [diff] [blame] | 221 | arg0 = (arg0) ? arg0 + 1 : binary; |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 222 | if (ctx->to.login) { |
| 223 | int s = strlen(arg0) + 2; |
Luca Stefani | 5eb52ea | 2018-08-26 11:35:00 +0200 | [diff] [blame] | 224 | char* p = malloc(s); |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 225 | |
Luca Stefani | 5eb52ea | 2018-08-26 11:35:00 +0200 | [diff] [blame] | 226 | if (!p) exit(EXIT_FAILURE); |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 227 | |
| 228 | *p = '-'; |
| 229 | strcpy(p + 1, arg0); |
| 230 | arg0 = p; |
| 231 | } |
| 232 | |
| 233 | populate_environment(ctx); |
| 234 | set_identity(ctx->to.uid); |
| 235 | |
Luca Stefani | 5eb52ea | 2018-08-26 11:35:00 +0200 | [diff] [blame] | 236 | #define PARG(arg) \ |
| 237 | (argc + (arg) < ctx->to.argc) ? " " : "", \ |
| 238 | (argc + (arg) < ctx->to.argc) ? ctx->to.argv[argc + (arg)] : "" |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 239 | |
Luca Stefani | 5eb52ea | 2018-08-26 11:35:00 +0200 | [diff] [blame] | 240 | ALOGD("%u %s executing %u %s using binary %s : %s%s%s%s%s%s%s%s%s%s%s%s%s%s", ctx->from.uid, |
| 241 | ctx->from.bin, ctx->to.uid, get_command(&ctx->to), binary, arg0, PARG(0), PARG(1), |
| 242 | PARG(2), PARG(3), PARG(4), PARG(5), (ctx->to.optind + 6 < ctx->to.argc) ? " ..." : ""); |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 243 | |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 244 | ctx->to.argv[--argc] = arg0; |
Diogo Ferreira | 9d64665 | 2014-12-17 12:24:46 +0000 | [diff] [blame] | 245 | |
| 246 | int pid = fork(); |
| 247 | if (!pid) { |
| 248 | execvp(binary, ctx->to.argv + argc); |
| 249 | err = errno; |
| 250 | PLOGE("exec"); |
| 251 | fprintf(stderr, "Cannot execute %s: %s\n", binary, strerror(err)); |
| 252 | exit(EXIT_FAILURE); |
| 253 | } else { |
gwzhang | 11730a5 | 2017-09-12 18:59:06 +0800 | [diff] [blame] | 254 | int status, code; |
Diogo Ferreira | 9d64665 | 2014-12-17 12:24:46 +0000 | [diff] [blame] | 255 | |
| 256 | ALOGD("Waiting for pid %d.", pid); |
| 257 | waitpid(pid, &status, 0); |
gwzhang | 11730a5 | 2017-09-12 18:59:06 +0800 | [diff] [blame] | 258 | ALOGD("pid %d returned %d.", pid, status); |
| 259 | code = WIFSIGNALED(status) ? WTERMSIG(status) + 128 : WEXITSTATUS(status); |
| 260 | |
Diogo Ferreira | 9d64665 | 2014-12-17 12:24:46 +0000 | [diff] [blame] | 261 | if (packageName) { |
| 262 | appops_finish_op_su(ctx->from.uid, packageName); |
| 263 | } |
gwzhang | 11730a5 | 2017-09-12 18:59:06 +0800 | [diff] [blame] | 264 | exit(code); |
Diogo Ferreira | 9d64665 | 2014-12-17 12:24:46 +0000 | [diff] [blame] | 265 | } |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 266 | } |
| 267 | |
Luca Stefani | 7c86b05 | 2018-08-26 11:50:32 +0200 | [diff] [blame] | 268 | int access_disabled(const struct su_initiator* from) { |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 269 | char build_type[PROPERTY_VALUE_MAX]; |
Luca Stefani | 7c86b05 | 2018-08-26 11:50:32 +0200 | [diff] [blame] | 270 | int enabled; |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 271 | |
Luca Stefani | 7c86b05 | 2018-08-26 11:50:32 +0200 | [diff] [blame] | 272 | /* Only allow su on debuggable builds */ |
| 273 | if (!property_get_bool("ro.debuggable", false)) { |
MSe1969 | ae77c1a | 2018-06-24 17:00:53 +0200 | [diff] [blame] | 274 | ALOGE("Root access is disabled on non-debug builds"); |
| 275 | return 1; |
| 276 | } |
| 277 | |
Luca Stefani | 7c86b05 | 2018-08-26 11:50:32 +0200 | [diff] [blame] | 278 | /* Enforce persist.sys.root_access on non-eng builds for apps */ |
| 279 | enabled = property_get_int32("persist.sys.root_access", 2); |
| 280 | property_get("ro.build.type", build_type, ""); |
| 281 | if (strcmp("eng", build_type) != 0 && from->uid != AID_SHELL && from->uid != AID_ROOT && |
| 282 | (enabled & LINEAGE_ROOT_ACCESS_APPS_ONLY) != LINEAGE_ROOT_ACCESS_APPS_ONLY) { |
| 283 | ALOGE( |
| 284 | "Apps root access is disabled by system setting - " |
| 285 | "enable it under settings -> developer options"); |
MSe1969 | ae77c1a | 2018-06-24 17:00:53 +0200 | [diff] [blame] | 286 | return 1; |
| 287 | } |
| 288 | |
| 289 | /* disallow su in a shell if appropriate */ |
| 290 | if (from->uid == AID_SHELL && |
Luca Stefani | 7c86b05 | 2018-08-26 11:50:32 +0200 | [diff] [blame] | 291 | (enabled & LINEAGE_ROOT_ACCESS_ADB_ONLY) != LINEAGE_ROOT_ACCESS_ADB_ONLY) { |
| 292 | ALOGE( |
| 293 | "Shell root access is disabled by a system setting - " |
| 294 | "enable it under settings -> developer options"); |
MSe1969 | ae77c1a | 2018-06-24 17:00:53 +0200 | [diff] [blame] | 295 | return 1; |
| 296 | } |
| 297 | |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 298 | return 0; |
| 299 | } |
| 300 | |
Luca Stefani | 5eb52ea | 2018-08-26 11:35:00 +0200 | [diff] [blame] | 301 | int main(int argc, char* argv[]) { |
Daniel Micay | 4fb98c1 | 2015-11-08 19:06:51 -0500 | [diff] [blame] | 302 | if (getuid() != geteuid()) { |
| 303 | ALOGE("must not be a setuid binary"); |
| 304 | return 1; |
| 305 | } |
| 306 | |
Koushik Dutta | 8829fc3 | 2013-11-21 09:19:47 -0800 | [diff] [blame] | 307 | return su_main(argc, argv, 1); |
| 308 | } |
| 309 | |
Luca Stefani | 5eb52ea | 2018-08-26 11:35:00 +0200 | [diff] [blame] | 310 | int su_main(int argc, char* argv[], int need_client) { |
Koushik Dutta | c200cf7 | 2013-07-26 21:35:23 -0700 | [diff] [blame] | 311 | // start up in daemon mode if prompted |
| 312 | if (argc == 2 && strcmp(argv[1], "--daemon") == 0) { |
| 313 | return run_daemon(); |
| 314 | } |
| 315 | |
Marcos Marado | f465a0a | 2014-02-01 22:58:19 +0000 | [diff] [blame] | 316 | int ppid = getppid(); |
Kevin Cernekee | b27031e | 2013-11-23 15:02:58 -0800 | [diff] [blame] | 317 | |
Koushik Dutta | 8066f2c | 2013-02-16 19:59:10 -0800 | [diff] [blame] | 318 | // Sanitize all secure environment variables (from linker_environ.c in AOSP linker). |
| 319 | /* The same list than GLibc at this point */ |
| 320 | static const char* const unsec_vars[] = { |
| 321 | "GCONV_PATH", |
| 322 | "GETCONF_DIR", |
| 323 | "HOSTALIASES", |
| 324 | "LD_AUDIT", |
| 325 | "LD_DEBUG", |
| 326 | "LD_DEBUG_OUTPUT", |
| 327 | "LD_DYNAMIC_WEAK", |
| 328 | "LD_LIBRARY_PATH", |
| 329 | "LD_ORIGIN_PATH", |
| 330 | "LD_PRELOAD", |
| 331 | "LD_PROFILE", |
| 332 | "LD_SHOW_AUXV", |
| 333 | "LD_USE_LOAD_BIAS", |
| 334 | "LOCALDOMAIN", |
| 335 | "LOCPATH", |
| 336 | "MALLOC_TRACE", |
| 337 | "MALLOC_CHECK_", |
| 338 | "NIS_PATH", |
| 339 | "NLSPATH", |
| 340 | "RESOLV_HOST_CONF", |
| 341 | "RES_OPTIONS", |
| 342 | "TMPDIR", |
| 343 | "TZDIR", |
| 344 | "LD_AOUT_LIBRARY_PATH", |
| 345 | "LD_AOUT_PRELOAD", |
| 346 | // not listed in linker, used due to system() call |
| 347 | "IFS", |
| 348 | }; |
Luca Stefani | 5eb52ea | 2018-08-26 11:35:00 +0200 | [diff] [blame] | 349 | const char* const* cp = unsec_vars; |
| 350 | const char* const* endp = cp + sizeof(unsec_vars) / sizeof(unsec_vars[0]); |
Koushik Dutta | 8066f2c | 2013-02-16 19:59:10 -0800 | [diff] [blame] | 351 | while (cp < endp) { |
| 352 | unsetenv(*cp); |
| 353 | cp++; |
| 354 | } |
| 355 | |
Diogo Ferreira | 4aaba1e | 2014-12-16 17:58:55 +0000 | [diff] [blame] | 356 | ALOGD("su invoked."); |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 357 | |
| 358 | struct su_context ctx = { |
Luca Stefani | 5eb52ea | 2018-08-26 11:35:00 +0200 | [diff] [blame] | 359 | .from = |
| 360 | { |
| 361 | .pid = -1, |
| 362 | .uid = 0, |
| 363 | .bin = "", |
| 364 | .args = "", |
| 365 | .name = "", |
| 366 | }, |
| 367 | .to = |
| 368 | { |
| 369 | .uid = AID_ROOT, |
| 370 | .login = 0, |
| 371 | .keepenv = 0, |
| 372 | .shell = NULL, |
| 373 | .command = NULL, |
| 374 | .argv = argv, |
| 375 | .argc = argc, |
| 376 | .optind = 0, |
| 377 | .name = "", |
| 378 | }, |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 379 | }; |
Luca Stefani | bce2976 | 2017-01-22 23:09:15 +0100 | [diff] [blame] | 380 | int c; |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 381 | struct option long_opts[] = { |
Luca Stefani | 5eb52ea | 2018-08-26 11:35:00 +0200 | [diff] [blame] | 382 | {"command", required_argument, NULL, 'c'}, |
| 383 | {"help", no_argument, NULL, 'h'}, |
| 384 | {"login", no_argument, NULL, 'l'}, |
| 385 | {"preserve-environment", no_argument, NULL, 'p'}, |
| 386 | {"shell", required_argument, NULL, 's'}, |
| 387 | {"version", no_argument, NULL, 'v'}, |
| 388 | {NULL, 0, NULL, 0}, |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 389 | }; |
| 390 | |
Diogo Ferreira | 9d0f234 | 2014-12-16 18:18:28 +0000 | [diff] [blame] | 391 | while ((c = getopt_long(argc, argv, "+c:hlmps:Vv", long_opts, NULL)) != -1) { |
Luca Stefani | 5eb52ea | 2018-08-26 11:35:00 +0200 | [diff] [blame] | 392 | switch (c) { |
| 393 | case 'c': |
| 394 | ctx.to.shell = DEFAULT_SHELL; |
| 395 | ctx.to.command = optarg; |
| 396 | break; |
| 397 | case 'h': |
| 398 | usage(EXIT_SUCCESS); |
| 399 | break; |
| 400 | case 'l': |
| 401 | ctx.to.login = 1; |
| 402 | break; |
| 403 | case 'm': |
| 404 | case 'p': |
| 405 | ctx.to.keepenv = 1; |
| 406 | break; |
| 407 | case 's': |
| 408 | ctx.to.shell = optarg; |
| 409 | break; |
| 410 | case 'V': |
| 411 | printf("%d\n", VERSION_CODE); |
| 412 | exit(EXIT_SUCCESS); |
| 413 | case 'v': |
| 414 | printf("%s\n", VERSION); |
| 415 | exit(EXIT_SUCCESS); |
| 416 | default: |
| 417 | /* Bionic getopt_long doesn't terminate its error output by newline */ |
| 418 | fprintf(stderr, "\n"); |
| 419 | usage(2); |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 420 | } |
| 421 | } |
Koushik Dutta | 8829fc3 | 2013-11-21 09:19:47 -0800 | [diff] [blame] | 422 | |
| 423 | if (need_client) { |
Daniel Micay | f40b068 | 2015-11-14 18:28:34 -0500 | [diff] [blame] | 424 | // attempt to connect to daemon... |
| 425 | ALOGD("starting daemon client %d %d", getuid(), geteuid()); |
| 426 | return connect_daemon(argc, argv, ppid); |
Koushik Dutta | 8829fc3 | 2013-11-21 09:19:47 -0800 | [diff] [blame] | 427 | } |
| 428 | |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 429 | if (optind < argc && !strcmp(argv[optind], "-")) { |
| 430 | ctx.to.login = 1; |
| 431 | optind++; |
| 432 | } |
| 433 | /* username or uid */ |
Tom Marshall | beddaf7 | 2018-08-24 19:38:42 +0200 | [diff] [blame] | 434 | if (optind < argc && strcmp(argv[optind], "--") != 0) { |
Luca Stefani | 5eb52ea | 2018-08-26 11:35:00 +0200 | [diff] [blame] | 435 | struct passwd* pw; |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 436 | pw = getpwnam(argv[optind]); |
| 437 | if (!pw) { |
Luca Stefani | 5eb52ea | 2018-08-26 11:35:00 +0200 | [diff] [blame] | 438 | char* endptr; |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 439 | |
| 440 | /* It seems we shouldn't do this at all */ |
| 441 | errno = 0; |
| 442 | ctx.to.uid = strtoul(argv[optind], &endptr, 10); |
| 443 | if (errno || *endptr) { |
Diogo Ferreira | 4aaba1e | 2014-12-16 17:58:55 +0000 | [diff] [blame] | 444 | ALOGE("Unknown id: %s\n", argv[optind]); |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 445 | fprintf(stderr, "Unknown id: %s\n", argv[optind]); |
| 446 | exit(EXIT_FAILURE); |
| 447 | } |
| 448 | } else { |
| 449 | ctx.to.uid = pw->pw_uid; |
Daniel Micay | c9d292e | 2015-11-08 16:52:17 -0500 | [diff] [blame] | 450 | if (pw->pw_name) { |
| 451 | if (strlcpy(ctx.to.name, pw->pw_name, sizeof(ctx.to.name)) >= sizeof(ctx.to.name)) { |
| 452 | ALOGE("name too long"); |
| 453 | exit(EXIT_FAILURE); |
| 454 | } |
| 455 | } |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 456 | } |
| 457 | optind++; |
| 458 | } |
| 459 | if (optind < argc && !strcmp(argv[optind], "--")) { |
| 460 | optind++; |
| 461 | } |
| 462 | ctx.to.optind = optind; |
| 463 | |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 464 | if (from_init(&ctx.from) < 0) { |
| 465 | deny(&ctx); |
| 466 | } |
Koushik Dutta | c200cf7 | 2013-07-26 21:35:23 -0700 | [diff] [blame] | 467 | |
Diogo Ferreira | 590ddd2 | 2014-12-16 10:27:31 +0000 | [diff] [blame] | 468 | ALOGE("SU from: %s", ctx.from.name); |
| 469 | |
Michael Bestas | af63088 | 2019-03-06 23:49:53 +0200 | [diff] [blame] | 470 | if (ctx.from.uid == AID_ROOT) { |
| 471 | ALOGD("Allowing root."); |
| 472 | allow(&ctx, NULL); |
| 473 | } |
| 474 | |
Koushik Dutta | 33b3f16 | 2013-03-03 22:41:48 -0800 | [diff] [blame] | 475 | // check if superuser is disabled completely |
| 476 | if (access_disabled(&ctx.from)) { |
Diogo Ferreira | 4aaba1e | 2014-12-16 17:58:55 +0000 | [diff] [blame] | 477 | ALOGD("access_disabled"); |
Koushik Dutta | 33b3f16 | 2013-03-03 22:41:48 -0800 | [diff] [blame] | 478 | deny(&ctx); |
| 479 | } |
| 480 | |
Koushik Dutta | 62843e1 | 2013-03-11 14:39:55 -0700 | [diff] [blame] | 481 | // autogrant shell at this point |
| 482 | if (ctx.from.uid == AID_SHELL) { |
Diogo Ferreira | 4aaba1e | 2014-12-16 17:58:55 +0000 | [diff] [blame] | 483 | ALOGD("Allowing shell."); |
Diogo Ferreira | 9d64665 | 2014-12-17 12:24:46 +0000 | [diff] [blame] | 484 | allow(&ctx, NULL); |
Koushik Dutta | 62843e1 | 2013-03-11 14:39:55 -0700 | [diff] [blame] | 485 | } |
| 486 | |
Luca Stefani | 5eb52ea | 2018-08-26 11:35:00 +0200 | [diff] [blame] | 487 | char* packageName = resolve_package_name(ctx.from.uid); |
Tom Marshall | beddaf7 | 2018-08-24 19:38:42 +0200 | [diff] [blame] | 488 | if (packageName) { |
| 489 | if (!appops_start_op_su(ctx.from.uid, packageName)) { |
| 490 | ALOGD("Allowing via appops."); |
| 491 | allow(&ctx, packageName); |
| 492 | } |
| 493 | free(packageName); |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 494 | } |
Diogo Ferreira | 910c841 | 2014-12-16 17:38:44 +0000 | [diff] [blame] | 495 | |
Diogo Ferreira | 4aaba1e | 2014-12-16 17:58:55 +0000 | [diff] [blame] | 496 | ALOGE("Allow chain exhausted, denying request"); |
Diogo Ferreira | 910c841 | 2014-12-16 17:38:44 +0000 | [diff] [blame] | 497 | deny(&ctx); |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 498 | } |