1/* 2** 3** Copyright 2010, The Android Open Source Project 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 PROGNAME "run-as" 19#define LOG_TAG PROGNAME 20 21#include <stdio.h> 22#include <stdlib.h> 23#include <string.h> 24#include <sys/types.h> 25#include <sys/stat.h> 26#include <dirent.h> 27#include <errno.h> 28#include <unistd.h> 29#include <time.h> 30#include <stdarg.h> 31 32#include <private/android_filesystem_config.h> 33#include "package.h" 34 35/* 36 * WARNING WARNING WARNING WARNING 37 * 38 * This program runs as set-uid root on Android production devices. 39 * Be very conservative when modifying it to avoid any serious 40 * security issue. Keep in mind the following: 41 * 42 * - This program should only run for the 'root' or 'shell' users 43 * 44 * - Avoid anything that is more complex than simple system calls 45 * until the uid/gid has been dropped to that of a normal user 46 * or you are sure to exit. 47 * 48 * This avoids depending on environment variables, system properties 49 * and other external factors that may affect the C library in 50 * unpredictable ways. 51 * 52 * - Do not trust user input and/or the filesystem whenever possible. 53 * 54 * Read README.TXT for more details. 55 * 56 * 57 * 58 * The purpose of this program is to run a command as a specific 59 * application user-id. Typical usage is: 60 * 61 * run-as <package-name> <command> <args> 62 * 63 * The 'run-as' binary is setuid, but will check the following: 64 * 65 * - that it is invoked from the 'shell' or 'root' user (abort otherwise) 66 * - that '<package-name>' is the name of an installed and debuggable package 67 * - that the package's data directory is well-formed (see package.c) 68 * 69 * If so, it will cd to the package's data directory, drop to the application's 70 * user id / group id then run the command there. 71 * 72 * This can be useful for a number of different things on production devices: 73 * 74 * - Allow application developers to look at their own applicative data 75 * during development. 76 * 77 * - Run the 'gdbserver' binary executable to allow native debugging 78 */ 79 80static void 81usage(void) 82{ 83 const char* str = "Usage: " PROGNAME " <package-name> <command> [<args>]\n\n"; 84 write(1, str, strlen(str)); 85 exit(1); 86} 87 88 89static void 90panic(const char* format, ...) 91{ 92 va_list args; 93 94 fprintf(stderr, "%s: ", PROGNAME); 95 va_start(args, format); 96 vfprintf(stderr, format, args); 97 va_end(args); 98 exit(1); 99} 100 101 102int main(int argc, char **argv) 103{ 104 const char* pkgname; 105 int myuid, uid, gid; 106 PackageInfo info; 107 108 /* check arguments */ 109 if (argc < 2) 110 usage(); 111 112 /* check userid of caller - must be 'shell' or 'root' */ 113 myuid = getuid(); 114 if (myuid != AID_SHELL && myuid != AID_ROOT) { 115 panic("only 'shell' or 'root' users can run this program\n"); 116 } 117 118 /* retrieve package information from system */ 119 pkgname = argv[1]; 120 if (get_package_info(pkgname, &info) < 0) { 121 panic("Package '%s' is unknown\n", pkgname); 122 return 1; 123 } 124 125 /* reject system packages */ 126 if (info.uid < AID_APP) { 127 panic("Package '%s' is not an application\n", pkgname); 128 return 1; 129 } 130 131 /* reject any non-debuggable package */ 132 if (!info.isDebuggable) { 133 panic("Package '%s' is not debuggable\n", pkgname); 134 return 1; 135 } 136 137 /* check that the data directory path is valid */ 138 if (check_data_path(info.dataDir, info.uid) < 0) { 139 panic("Package '%s' has corrupt installation\n", pkgname); 140 return 1; 141 } 142 143 /* then move to it */ 144 { 145 int ret; 146 do { 147 ret = chdir(info.dataDir); 148 } while (ret < 0 && errno == EINTR); 149 150 if (ret < 0) { 151 panic("Could not cd to package's data directory: %s\n", strerror(errno)); 152 return 1; 153 } 154 } 155 156 /* Ensure that we change all real/effective/saved IDs at the 157 * same time to avoid nasty surprises. 158 */ 159 uid = gid = info.uid; 160 if(setresgid(gid,gid,gid) || setresuid(uid,uid,uid)) { 161 panic("Permission denied\n"); 162 return 1; 163 } 164 165 /* User specified command for exec. */ 166 if (argc >= 3 ) { 167 if (execvp(argv[2], argv+2) < 0) { 168 panic("exec failed for %s Error:%s\n", argv[2], strerror(errno)); 169 return -errno; 170 } 171 } 172 173 /* Default exec shell. */ 174 execlp("/system/bin/sh", "sh", NULL); 175 176 panic("exec failed\n"); 177 return 1; 178} 179