1 /* 2 * Copyright (C) 2010 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 * 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 <errno.h> 18 #include <error.h> 19 #include <paths.h> 20 #include <pwd.h> 21 #include <stdlib.h> 22 #include <string.h> 23 #include <sys/capability.h> 24 #include <sys/stat.h> 25 #include <sys/types.h> 26 #include <unistd.h> 27 28 #include <libminijail.h> 29 #include <scoped_minijail.h> 30 31 #include <packagelistparser/packagelistparser.h> 32 #include <private/android_filesystem_config.h> 33 #include <selinux/android.h> 34 35 // The purpose of this program is to run a command as a specific 36 // application user-id. Typical usage is: 37 // 38 // run-as <package-name> <command> <args> 39 // 40 // The 'run-as' binary is installed with CAP_SETUID and CAP_SETGID file 41 // capabilities, but will check the following: 42 // 43 // - that it is invoked from the 'shell' or 'root' user (abort otherwise) 44 // - that '<package-name>' is the name of an installed and debuggable package 45 // - that the package's data directory is well-formed 46 // 47 // If so, it will drop to the application's user id / group id, cd to the 48 // package's data directory, then run the command there. 49 // 50 // This can be useful for a number of different things on production devices: 51 // 52 // - Allow application developers to look at their own application data 53 // during development. 54 // 55 // - Run the 'gdbserver' binary executable to allow native debugging 56 // 57 58 static bool packagelist_parse_callback(pkg_info* this_package, void* userdata) { 59 pkg_info* p = reinterpret_cast<pkg_info*>(userdata); 60 if (strcmp(p->name, this_package->name) == 0) { 61 *p = *this_package; 62 return false; // Stop searching. 63 } 64 packagelist_free(this_package); 65 return true; // Keep searching. 66 } 67 68 static bool check_directory(const char* path, uid_t uid) { 69 struct stat st; 70 if (TEMP_FAILURE_RETRY(lstat(path, &st)) == -1) return false; 71 72 // /data/user/0 is a known safe symlink. 73 if (strcmp("/data/user/0", path) == 0) return true; 74 75 // Must be a real directory, not a symlink. 76 if (!S_ISDIR(st.st_mode)) return false; 77 78 // Must be owned by specific uid/gid. 79 if (st.st_uid != uid || st.st_gid != uid) return false; 80 81 // Must not be readable or writable by others. 82 if ((st.st_mode & (S_IROTH|S_IWOTH)) != 0) return false; 83 84 return true; 85 } 86 87 // This function is used to check the data directory path for safety. 88 // We check that every sub-directory is owned by the 'system' user 89 // and exists and is not a symlink. We also check that the full directory 90 // path is properly owned by the user ID. 91 static bool check_data_path(const char* data_path, uid_t uid) { 92 // The path should be absolute. 93 if (data_path[0] != '/') return false; 94 95 // Look for all sub-paths, we do that by finding 96 // directory separators in the input path and 97 // checking each sub-path independently. 98 for (int nn = 1; data_path[nn] != '\0'; nn++) { 99 char subpath[PATH_MAX]; 100 101 /* skip non-separator characters */ 102 if (data_path[nn] != '/') continue; 103 104 /* handle trailing separator case */ 105 if (data_path[nn+1] == '\0') break; 106 107 /* found a separator, check that data_path is not too long. */ 108 if (nn >= (int)(sizeof subpath)) return false; 109 110 /* reject any '..' subpath */ 111 if (nn >= 3 && 112 data_path[nn-3] == '/' && 113 data_path[nn-2] == '.' && 114 data_path[nn-1] == '.') { 115 return false; 116 } 117 118 /* copy to 'subpath', then check ownership */ 119 memcpy(subpath, data_path, nn); 120 subpath[nn] = '\0'; 121 122 if (!check_directory(subpath, AID_SYSTEM)) return false; 123 } 124 125 // All sub-paths were checked, now verify that the full data 126 // directory is owned by the application uid. 127 return check_directory(data_path, uid); 128 } 129 130 int main(int argc, char* argv[]) { 131 // Check arguments. 132 if (argc < 2) { 133 error(1, 0, "usage: run-as <package-name> [--user <uid>] <command> [<args>]\n"); 134 } 135 136 // This program runs with CAP_SETUID and CAP_SETGID capabilities on Android 137 // production devices. Check user id of caller --- must be 'shell' or 'root'. 138 if (getuid() != AID_SHELL && getuid() != AID_ROOT) { 139 error(1, 0, "only 'shell' or 'root' users can run this program"); 140 } 141 142 char* pkgname = argv[1]; 143 int cmd_argv_offset = 2; 144 145 // Get user_id from command line if provided. 146 int userId = 0; 147 if ((argc >= 4) && !strcmp(argv[2], "--user")) { 148 userId = atoi(argv[3]); 149 if (userId < 0) error(1, 0, "negative user id: %d", userId); 150 cmd_argv_offset += 2; 151 } 152 153 // Retrieve package information from system, switching egid so we can read the file. 154 gid_t old_egid = getegid(); 155 if (setegid(AID_PACKAGE_INFO) == -1) error(1, errno, "setegid(AID_PACKAGE_INFO) failed"); 156 pkg_info info; 157 memset(&info, 0, sizeof(info)); 158 info.name = pkgname; 159 if (!packagelist_parse(packagelist_parse_callback, &info)) { 160 error(1, errno, "packagelist_parse failed"); 161 } 162 if (info.uid == 0) { 163 error(1, 0, "unknown package: %s", pkgname); 164 } 165 if (setegid(old_egid) == -1) error(1, errno, "couldn't restore egid"); 166 167 // Verify that user id is not too big. 168 if ((UID_MAX - info.uid) / AID_USER_OFFSET < (uid_t)userId) { 169 error(1, 0, "user id too big: %d", userId); 170 } 171 172 // Calculate user app ID. 173 uid_t userAppId = (AID_USER_OFFSET * userId) + info.uid; 174 175 // Reject system packages. 176 if (userAppId < AID_APP) { 177 error(1, 0, "package not an application: %s", pkgname); 178 } 179 180 // Reject any non-debuggable package. 181 if (!info.debuggable) { 182 error(1, 0, "package not debuggable: %s", pkgname); 183 } 184 185 // Check that the data directory path is valid. 186 if (!check_data_path(info.data_dir, userAppId)) { 187 error(1, 0, "package has corrupt installation: %s", pkgname); 188 } 189 190 // Ensure that we change all real/effective/saved IDs at the 191 // same time to avoid nasty surprises. 192 uid_t uid = userAppId; 193 uid_t gid = userAppId; 194 ScopedMinijail j(minijail_new()); 195 minijail_change_uid(j.get(), uid); 196 minijail_change_gid(j.get(), gid); 197 minijail_keep_supplementary_gids(j.get()); 198 minijail_enter(j.get()); 199 200 if (selinux_android_setcontext(uid, 0, info.seinfo, pkgname) < 0) { 201 error(1, errno, "couldn't set SELinux security context"); 202 } 203 204 // cd into the data directory, and set $HOME correspondingly. 205 if (TEMP_FAILURE_RETRY(chdir(info.data_dir)) == -1) { 206 error(1, errno, "couldn't chdir to package's data directory"); 207 } 208 setenv("HOME", info.data_dir, 1); 209 210 // Reset parts of the environment, like su would. 211 setenv("PATH", _PATH_DEFPATH, 1); 212 unsetenv("IFS"); 213 214 // Set the user-specific parts for this user. 215 passwd* pw = getpwuid(uid); 216 setenv("LOGNAME", pw->pw_name, 1); 217 setenv("SHELL", pw->pw_shell, 1); 218 setenv("USER", pw->pw_name, 1); 219 220 // User specified command for exec. 221 if ((argc >= cmd_argv_offset + 1) && 222 (execvp(argv[cmd_argv_offset], argv+cmd_argv_offset) == -1)) { 223 error(1, errno, "exec failed for %s", argv[cmd_argv_offset]); 224 } 225 226 // Default exec shell. 227 execlp(_PATH_BSHELL, "sh", NULL); 228 error(1, errno, "exec failed"); 229 } 230