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) |
| 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 <sys/types.h> |
| 19 | #include <sys/socket.h> |
Danny Baumann | ed55b0e | 2013-11-22 16:43:04 +0100 | [diff] [blame] | 20 | #include <sys/uio.h> |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 21 | #include <sys/un.h> |
| 22 | #include <sys/wait.h> |
| 23 | #include <sys/select.h> |
| 24 | #include <sys/time.h> |
| 25 | #include <unistd.h> |
| 26 | #include <limits.h> |
| 27 | #include <fcntl.h> |
| 28 | #include <stdio.h> |
| 29 | #include <stdlib.h> |
| 30 | #include <getopt.h> |
| 31 | #include <stdint.h> |
| 32 | #include <pwd.h> |
| 33 | #include <sys/stat.h> |
| 34 | #include <stdarg.h> |
| 35 | #include <sys/types.h> |
Diogo Ferreira | 9d0f234 | 2014-12-16 18:18:28 +0000 | [diff] [blame] | 36 | #include <log/log.h> |
Ricardo Cerqueira | b2ffaf2 | 2015-01-04 03:07:14 +0000 | [diff] [blame] | 37 | #include <private/android_filesystem_config.h> |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 38 | |
| 39 | #include "su.h" |
| 40 | #include "utils.h" |
Diogo Ferreira | 590ddd2 | 2014-12-16 10:27:31 +0000 | [diff] [blame] | 41 | #include "binder/pm-wrapper.h" |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 42 | |
Koushik Dutta | c200cf7 | 2013-07-26 21:35:23 -0700 | [diff] [blame] | 43 | extern int is_daemon; |
| 44 | extern int daemon_from_uid; |
| 45 | extern int daemon_from_pid; |
| 46 | |
Koushik Dutta | 5f2c658 | 2013-07-30 15:38:48 -0700 | [diff] [blame] | 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 { |
Marcos Marado | 2b782d1 | 2014-12-11 20:41:24 +0000 | [diff] [blame] | 55 | if ((pid = fork())) |
Koushik Dutta | 5f2c658 | 2013-07-30 15:38:48 -0700 | [diff] [blame] | 56 | exit(0); |
| 57 | return 0; |
| 58 | } |
| 59 | } |
| 60 | |
Koushik Dutta | 045c68a | 2013-02-17 17:03:56 -0800 | [diff] [blame] | 61 | static int from_init(struct su_initiator *from) { |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 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 | |
Marcos Marado | f465a0a | 2014-02-01 22:58:19 +0000 | [diff] [blame] | 72 | if (is_daemon) { |
| 73 | from->uid = daemon_from_uid; |
| 74 | from->pid = daemon_from_pid; |
| 75 | } |
| 76 | |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 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) { |
Daniel Micay | c9d292e | 2015-11-08 16:52:17 -0500 | [diff] [blame] | 106 | if (strlcpy(from->args, argv_rest, sizeof(from->args)) >= sizeof(from->args)) { |
| 107 | ALOGE("argument too long"); |
| 108 | return -1; |
| 109 | } |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 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 | |
Daniel Micay | c9d292e | 2015-11-08 16:52:17 -0500 | [diff] [blame] | 126 | if (strlcpy(from->bin, argv0, sizeof(from->bin)) >= sizeof(from->bin)) { |
| 127 | ALOGE("binary path too long"); |
| 128 | return -1; |
| 129 | } |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 130 | |
Koushik Dutta | a194322 | 2013-02-22 00:24:54 -0800 | [diff] [blame] | 131 | struct passwd *pw; |
| 132 | pw = getpwuid(from->uid); |
| 133 | if (pw && pw->pw_name) { |
Daniel Micay | c9d292e | 2015-11-08 16:52:17 -0500 | [diff] [blame] | 134 | if (strlcpy(from->name, pw->pw_name, sizeof(from->name)) >= sizeof(from->name)) { |
| 135 | ALOGE("name too long"); |
| 136 | return -1; |
| 137 | } |
Koushik Dutta | a194322 | 2013-02-22 00:24:54 -0800 | [diff] [blame] | 138 | } |
| 139 | |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 140 | return 0; |
| 141 | } |
| 142 | |
Koushik Dutta | 045c68a | 2013-02-17 17:03:56 -0800 | [diff] [blame] | 143 | static void populate_environment(const struct su_context *ctx) { |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 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); |
Koushik Dutta | 379371b | 2013-03-11 17:30:53 -0700 | [diff] [blame] | 152 | if (ctx->to.shell) |
| 153 | setenv("SHELL", ctx->to.shell, 1); |
| 154 | else |
| 155 | setenv("SHELL", DEFAULT_SHELL, 1); |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 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 | |
Koushik Dutta | 045c68a | 2013-02-17 17:03:56 -0800 | [diff] [blame] | 163 | void set_identity(unsigned int uid) { |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 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 | |
Koushik Dutta | 045c68a | 2013-02-17 17:03:56 -0800 | [diff] [blame] | 182 | static void usage(int status) { |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 183 | FILE *stream = (status == EXIT_SUCCESS) ? stdout : stderr; |
| 184 | |
| 185 | fprintf(stream, |
| 186 | "Usage: su [options] [--] [-] [LOGIN] [--] [args...]\n\n" |
| 187 | "Options:\n" |
Koushik Dutta | c7adb38 | 2013-07-29 20:59:41 -0700 | [diff] [blame] | 188 | " --daemon start the su daemon agent\n" |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 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 | |
Koushik Dutta | 045c68a | 2013-02-17 17:03:56 -0800 | [diff] [blame] | 201 | static __attribute__ ((noreturn)) void deny(struct su_context *ctx) { |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 202 | char *cmd = get_command(&ctx->to); |
Diogo Ferreira | 4aaba1e | 2014-12-16 17:58:55 +0000 | [diff] [blame] | 203 | 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] | 204 | fprintf(stderr, "%s\n", strerror(EACCES)); |
| 205 | exit(EXIT_FAILURE); |
| 206 | } |
| 207 | |
Diogo Ferreira | 9d64665 | 2014-12-17 12:24:46 +0000 | [diff] [blame] | 208 | static __attribute__ ((noreturn)) void allow(struct su_context *ctx, const char *packageName) { |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 209 | char *arg0; |
| 210 | int argc, err; |
| 211 | |
| 212 | umask(ctx->umask); |
Koushik Dutta | 379371b | 2013-03-11 17:30:53 -0700 | [diff] [blame] | 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; |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 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) \ |
Koushik Dutta | 379371b | 2013-03-11 17:30:53 -0700 | [diff] [blame] | 251 | (argc + (arg) < ctx->to.argc) ? " " : "", \ |
| 252 | (argc + (arg) < ctx->to.argc) ? ctx->to.argv[argc + (arg)] : "" |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 253 | |
Diogo Ferreira | 4aaba1e | 2014-12-16 17:58:55 +0000 | [diff] [blame] | 254 | ALOGD("%u %s executing %u %s using binary %s : %s%s%s%s%s%s%s%s%s%s%s%s%s%s", |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 255 | ctx->from.uid, ctx->from.bin, |
Koushik Dutta | 379371b | 2013-03-11 17:30:53 -0700 | [diff] [blame] | 256 | ctx->to.uid, get_command(&ctx->to), binary, |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 257 | arg0, PARG(0), PARG(1), PARG(2), PARG(3), PARG(4), PARG(5), |
| 258 | (ctx->to.optind + 6 < ctx->to.argc) ? " ..." : ""); |
| 259 | |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 260 | ctx->to.argv[--argc] = arg0; |
Diogo Ferreira | 9d64665 | 2014-12-17 12:24:46 +0000 | [diff] [blame] | 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 | } |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 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 | */ |
Koushik Dutta | 045c68a | 2013-02-17 17:03:56 -0800 | [diff] [blame] | 288 | int access_disabled(const struct su_initiator *from) { |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 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) { |
Diogo Ferreira | 4aaba1e | 2014-12-16 17:58:55 +0000 | [diff] [blame] | 304 | ALOGE("Root access is disabled on non-debug builds"); |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 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) |
Shareef Ali | a1229fc | 2015-01-04 18:58:43 -0600 | [diff] [blame] | 312 | memcpy(enabled, "0", 2); |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 313 | else |
| 314 | memcpy(enabled, data, len + 1); |
| 315 | free(data); |
| 316 | } else |
Shareef Ali | a1229fc | 2015-01-04 18:58:43 -0600 | [diff] [blame] | 317 | memcpy(enabled, "0", 2); |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 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 ) { |
Diogo Ferreira | 4aaba1e | 2014-12-16 17:58:55 +0000 | [diff] [blame] | 323 | ALOGE("Apps root access is disabled by system setting - " |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 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 ) { |
Diogo Ferreira | 4aaba1e | 2014-12-16 17:58:55 +0000 | [diff] [blame] | 331 | ALOGE("Shell root access is disabled by a system setting - " |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 332 | "enable it under settings -> developer options"); |
| 333 | return 1; |
| 334 | } |
Koushik Dutta | c200cf7 | 2013-07-26 21:35:23 -0700 | [diff] [blame] | 335 | |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 336 | } |
| 337 | return 0; |
| 338 | } |
| 339 | |
Kevin Cernekee | b27031e | 2013-11-23 15:02:58 -0800 | [diff] [blame] | 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 | |
Koushik Dutta | 045c68a | 2013-02-17 17:03:56 -0800 | [diff] [blame] | 362 | int main(int argc, char *argv[]) { |
Daniel Micay | 4fb98c1 | 2015-11-08 19:06:51 -0500 | [diff] [blame] | 363 | if (getuid() != geteuid()) { |
| 364 | ALOGE("must not be a setuid binary"); |
| 365 | return 1; |
| 366 | } |
| 367 | |
Koushik Dutta | 8829fc3 | 2013-11-21 09:19:47 -0800 | [diff] [blame] | 368 | return su_main(argc, argv, 1); |
| 369 | } |
| 370 | |
| 371 | int su_main(int argc, char *argv[], int need_client) { |
Koushik Dutta | c200cf7 | 2013-07-26 21:35:23 -0700 | [diff] [blame] | 372 | // start up in daemon mode if prompted |
| 373 | if (argc == 2 && strcmp(argv[1], "--daemon") == 0) { |
| 374 | return run_daemon(); |
| 375 | } |
| 376 | |
Marcos Marado | f465a0a | 2014-02-01 22:58:19 +0000 | [diff] [blame] | 377 | int ppid = getppid(); |
Kevin Cernekee | b27031e | 2013-11-23 15:02:58 -0800 | [diff] [blame] | 378 | fork_for_samsung(); |
| 379 | |
Koushik Dutta | 8066f2c | 2013-02-16 19:59:10 -0800 | [diff] [blame] | 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 | |
Diogo Ferreira | 4aaba1e | 2014-12-16 17:58:55 +0000 | [diff] [blame] | 418 | ALOGD("su invoked."); |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 419 | |
| 420 | struct su_context ctx = { |
| 421 | .from = { |
| 422 | .pid = -1, |
| 423 | .uid = 0, |
| 424 | .bin = "", |
| 425 | .args = "", |
Koushik Dutta | a194322 | 2013-02-22 00:24:54 -0800 | [diff] [blame] | 426 | .name = "", |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 427 | }, |
| 428 | .to = { |
| 429 | .uid = AID_ROOT, |
| 430 | .login = 0, |
| 431 | .keepenv = 0, |
Koushik Dutta | 379371b | 2013-03-11 17:30:53 -0700 | [diff] [blame] | 432 | .shell = NULL, |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 433 | .command = NULL, |
| 434 | .argv = argv, |
| 435 | .argc = argc, |
| 436 | .optind = 0, |
Koushik Dutta | a194322 | 2013-02-22 00:24:54 -0800 | [diff] [blame] | 437 | .name = "", |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 438 | }, |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 439 | }; |
Luca Stefani | 2b2cde2 | 2017-01-22 23:09:15 +0100 | [diff] [blame] | 440 | int c; |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 441 | struct option long_opts[] = { |
| 442 | { "command", required_argument, NULL, 'c' }, |
| 443 | { "help", no_argument, NULL, 'h' }, |
| 444 | { "login", no_argument, NULL, 'l' }, |
| 445 | { "preserve-environment", no_argument, NULL, 'p' }, |
| 446 | { "shell", required_argument, NULL, 's' }, |
| 447 | { "version", no_argument, NULL, 'v' }, |
| 448 | { NULL, 0, NULL, 0 }, |
| 449 | }; |
| 450 | |
Diogo Ferreira | 9d0f234 | 2014-12-16 18:18:28 +0000 | [diff] [blame] | 451 | while ((c = getopt_long(argc, argv, "+c:hlmps:Vv", long_opts, NULL)) != -1) { |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 452 | switch(c) { |
| 453 | case 'c': |
Koushik Dutta | 379371b | 2013-03-11 17:30:53 -0700 | [diff] [blame] | 454 | ctx.to.shell = DEFAULT_SHELL; |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 455 | ctx.to.command = optarg; |
| 456 | break; |
| 457 | case 'h': |
| 458 | usage(EXIT_SUCCESS); |
| 459 | break; |
| 460 | case 'l': |
| 461 | ctx.to.login = 1; |
| 462 | break; |
| 463 | case 'm': |
| 464 | case 'p': |
| 465 | ctx.to.keepenv = 1; |
| 466 | break; |
| 467 | case 's': |
| 468 | ctx.to.shell = optarg; |
| 469 | break; |
| 470 | case 'V': |
| 471 | printf("%d\n", VERSION_CODE); |
| 472 | exit(EXIT_SUCCESS); |
| 473 | case 'v': |
| 474 | printf("%s\n", VERSION); |
| 475 | exit(EXIT_SUCCESS); |
| 476 | default: |
| 477 | /* Bionic getopt_long doesn't terminate its error output by newline */ |
| 478 | fprintf(stderr, "\n"); |
| 479 | usage(2); |
| 480 | } |
| 481 | } |
Koushik Dutta | 8829fc3 | 2013-11-21 09:19:47 -0800 | [diff] [blame] | 482 | |
| 483 | if (need_client) { |
Daniel Micay | f40b068 | 2015-11-14 18:28:34 -0500 | [diff] [blame] | 484 | // attempt to connect to daemon... |
| 485 | ALOGD("starting daemon client %d %d", getuid(), geteuid()); |
| 486 | return connect_daemon(argc, argv, ppid); |
Koushik Dutta | 8829fc3 | 2013-11-21 09:19:47 -0800 | [diff] [blame] | 487 | } |
| 488 | |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 489 | if (optind < argc && !strcmp(argv[optind], "-")) { |
| 490 | ctx.to.login = 1; |
| 491 | optind++; |
| 492 | } |
| 493 | /* username or uid */ |
| 494 | if (optind < argc && strcmp(argv[optind], "--")) { |
| 495 | struct passwd *pw; |
| 496 | pw = getpwnam(argv[optind]); |
| 497 | if (!pw) { |
| 498 | char *endptr; |
| 499 | |
| 500 | /* It seems we shouldn't do this at all */ |
| 501 | errno = 0; |
| 502 | ctx.to.uid = strtoul(argv[optind], &endptr, 10); |
| 503 | if (errno || *endptr) { |
Diogo Ferreira | 4aaba1e | 2014-12-16 17:58:55 +0000 | [diff] [blame] | 504 | ALOGE("Unknown id: %s\n", argv[optind]); |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 505 | fprintf(stderr, "Unknown id: %s\n", argv[optind]); |
| 506 | exit(EXIT_FAILURE); |
| 507 | } |
| 508 | } else { |
| 509 | ctx.to.uid = pw->pw_uid; |
Daniel Micay | c9d292e | 2015-11-08 16:52:17 -0500 | [diff] [blame] | 510 | if (pw->pw_name) { |
| 511 | if (strlcpy(ctx.to.name, pw->pw_name, sizeof(ctx.to.name)) >= sizeof(ctx.to.name)) { |
| 512 | ALOGE("name too long"); |
| 513 | exit(EXIT_FAILURE); |
| 514 | } |
| 515 | } |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 516 | } |
| 517 | optind++; |
| 518 | } |
| 519 | if (optind < argc && !strcmp(argv[optind], "--")) { |
| 520 | optind++; |
| 521 | } |
| 522 | ctx.to.optind = optind; |
| 523 | |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 524 | if (from_init(&ctx.from) < 0) { |
| 525 | deny(&ctx); |
| 526 | } |
Koushik Dutta | c200cf7 | 2013-07-26 21:35:23 -0700 | [diff] [blame] | 527 | |
Diogo Ferreira | 590ddd2 | 2014-12-16 10:27:31 +0000 | [diff] [blame] | 528 | ALOGE("SU from: %s", ctx.from.name); |
| 529 | |
Koushik Dutta | 62843e1 | 2013-03-11 14:39:55 -0700 | [diff] [blame] | 530 | // the latter two are necessary for stock ROMs like note 2 which do dumb things with su, or crash otherwise |
Koushik Dutta | 379371b | 2013-03-11 17:30:53 -0700 | [diff] [blame] | 531 | if (ctx.from.uid == AID_ROOT) { |
Diogo Ferreira | 4aaba1e | 2014-12-16 17:58:55 +0000 | [diff] [blame] | 532 | ALOGD("Allowing root/system/radio."); |
Diogo Ferreira | 9d64665 | 2014-12-17 12:24:46 +0000 | [diff] [blame] | 533 | allow(&ctx, NULL); |
Koushik Dutta | 75c720a | 2013-03-04 16:16:41 -0800 | [diff] [blame] | 534 | } |
| 535 | |
Koushik Dutta | 33b3f16 | 2013-03-03 22:41:48 -0800 | [diff] [blame] | 536 | // check if superuser is disabled completely |
| 537 | if (access_disabled(&ctx.from)) { |
Diogo Ferreira | 4aaba1e | 2014-12-16 17:58:55 +0000 | [diff] [blame] | 538 | ALOGD("access_disabled"); |
Koushik Dutta | 33b3f16 | 2013-03-03 22:41:48 -0800 | [diff] [blame] | 539 | deny(&ctx); |
| 540 | } |
| 541 | |
Koushik Dutta | 62843e1 | 2013-03-11 14:39:55 -0700 | [diff] [blame] | 542 | // autogrant shell at this point |
| 543 | if (ctx.from.uid == AID_SHELL) { |
Diogo Ferreira | 4aaba1e | 2014-12-16 17:58:55 +0000 | [diff] [blame] | 544 | ALOGD("Allowing shell."); |
Diogo Ferreira | 9d64665 | 2014-12-17 12:24:46 +0000 | [diff] [blame] | 545 | allow(&ctx, NULL); |
Koushik Dutta | 62843e1 | 2013-03-11 14:39:55 -0700 | [diff] [blame] | 546 | } |
| 547 | |
Diogo Ferreira | 9d64665 | 2014-12-17 12:24:46 +0000 | [diff] [blame] | 548 | const char *packageName = resolve_package_name(ctx.from.uid); |
| 549 | if (!appops_start_op_su(ctx.from.uid, packageName)) { |
Diogo Ferreira | 4aaba1e | 2014-12-16 17:58:55 +0000 | [diff] [blame] | 550 | ALOGD("Allowing via appops."); |
Diogo Ferreira | 9d64665 | 2014-12-17 12:24:46 +0000 | [diff] [blame] | 551 | allow(&ctx, packageName); |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 552 | } |
Diogo Ferreira | 910c841 | 2014-12-16 17:38:44 +0000 | [diff] [blame] | 553 | |
Diogo Ferreira | 4aaba1e | 2014-12-16 17:58:55 +0000 | [diff] [blame] | 554 | ALOGE("Allow chain exhausted, denying request"); |
Diogo Ferreira | 910c841 | 2014-12-16 17:38:44 +0000 | [diff] [blame] | 555 | deny(&ctx); |
Koushik Dutta | 05258fd | 2013-02-16 19:20:58 -0800 | [diff] [blame] | 556 | } |