11f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner/* 21f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner** 31f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner** Copyright 2010, The Android Open Source Project 41f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner** 51f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner** Licensed under the Apache License, Version 2.0 (the "License"); 61f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner** you may not use this file except in compliance with the License. 71f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner** You may obtain a copy of the License at 81f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner** 91f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner** http://www.apache.org/licenses/LICENSE-2.0 101f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner** 111f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner** Unless required by applicable law or agreed to in writing, software 121f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner** distributed under the License is distributed on an "AS IS" BASIS, 131f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 141f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner** See the License for the specific language governing permissions and 151f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner** limitations under the License. 161f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner*/ 171f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner 18db5334ad039dab3ec2b50e9a259c97ce882acc0dMark Salyzyn#define PROGNAME "run-as" 19db5334ad039dab3ec2b50e9a259c97ce882acc0dMark Salyzyn#define LOG_TAG PROGNAME 201f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner 21db5334ad039dab3ec2b50e9a259c97ce882acc0dMark Salyzyn#include <dirent.h> 22db5334ad039dab3ec2b50e9a259c97ce882acc0dMark Salyzyn#include <errno.h> 23a372f6f24192f28e66b9e73885aca34839112a66Elliott Hughes#include <paths.h> 24a372f6f24192f28e66b9e73885aca34839112a66Elliott Hughes#include <pwd.h> 25db5334ad039dab3ec2b50e9a259c97ce882acc0dMark Salyzyn#include <stdarg.h> 261f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner#include <stdio.h> 271f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner#include <stdlib.h> 281f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner#include <string.h> 29db5334ad039dab3ec2b50e9a259c97ce882acc0dMark Salyzyn#include <sys/capability.h> 30db5334ad039dab3ec2b50e9a259c97ce882acc0dMark Salyzyn#include <sys/cdefs.h> 311f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner#include <sys/stat.h> 32db5334ad039dab3ec2b50e9a259c97ce882acc0dMark Salyzyn#include <sys/types.h> 331f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner#include <time.h> 34db5334ad039dab3ec2b50e9a259c97ce882acc0dMark Salyzyn#include <unistd.h> 351f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner 361f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner#include <private/android_filesystem_config.h> 37db5334ad039dab3ec2b50e9a259c97ce882acc0dMark Salyzyn#include <selinux/android.h> 38db5334ad039dab3ec2b50e9a259c97ce882acc0dMark Salyzyn 391f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner#include "package.h" 401f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner 411f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner/* 421f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner * WARNING WARNING WARNING WARNING 431f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner * 44b0739c662db6a19b49c0912b865edb2853156bdaAlex Klyubin * This program runs with CAP_SETUID and CAP_SETGID capabilities on Android 45b0739c662db6a19b49c0912b865edb2853156bdaAlex Klyubin * production devices. Be very conservative when modifying it to avoid any 46b0739c662db6a19b49c0912b865edb2853156bdaAlex Klyubin * serious security issue. Keep in mind the following: 471f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner * 481f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner * - This program should only run for the 'root' or 'shell' users 491f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner * 50b2d8f896b6ef081c1af263dd13d91d2f435de0faNick Kralevich * - Avoid anything that is more complex than simple system calls 51b2d8f896b6ef081c1af263dd13d91d2f435de0faNick Kralevich * until the uid/gid has been dropped to that of a normal user 52b2d8f896b6ef081c1af263dd13d91d2f435de0faNick Kralevich * or you are sure to exit. 531f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner * 541f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner * This avoids depending on environment variables, system properties 551f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner * and other external factors that may affect the C library in 561f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner * unpredictable ways. 571f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner * 581f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner * - Do not trust user input and/or the filesystem whenever possible. 591f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner * 601f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner * Read README.TXT for more details. 611f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner * 621f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner * 631f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner * 641f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner * The purpose of this program is to run a command as a specific 651f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner * application user-id. Typical usage is: 661f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner * 671f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner * run-as <package-name> <command> <args> 681f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner * 69b0739c662db6a19b49c0912b865edb2853156bdaAlex Klyubin * The 'run-as' binary is installed with CAP_SETUID and CAP_SETGID file 70b0739c662db6a19b49c0912b865edb2853156bdaAlex Klyubin * capabilities, but will check the following: 711f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner * 721f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner * - that it is invoked from the 'shell' or 'root' user (abort otherwise) 731f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner * - that '<package-name>' is the name of an installed and debuggable package 741f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner * - that the package's data directory is well-formed (see package.c) 751f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner * 76b0739c662db6a19b49c0912b865edb2853156bdaAlex Klyubin * If so, it will drop to the application's user id / group id, cd to the 77b0739c662db6a19b49c0912b865edb2853156bdaAlex Klyubin * package's data directory, then run the command there. 78b0739c662db6a19b49c0912b865edb2853156bdaAlex Klyubin * 79b0739c662db6a19b49c0912b865edb2853156bdaAlex Klyubin * NOTE: In the future it might not be possible to cd to the package's data 80b0739c662db6a19b49c0912b865edb2853156bdaAlex Klyubin * directory under that package's user id / group id, in which case this 81b0739c662db6a19b49c0912b865edb2853156bdaAlex Klyubin * utility will need to be changed accordingly. 821f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner * 831f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner * This can be useful for a number of different things on production devices: 841f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner * 851f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner * - Allow application developers to look at their own applicative data 861f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner * during development. 871f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner * 881f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner * - Run the 'gdbserver' binary executable to allow native debugging 891f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner */ 901f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner 91db5334ad039dab3ec2b50e9a259c97ce882acc0dMark Salyzyn__noreturn static void 921f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turnerpanic(const char* format, ...) 931f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner{ 941f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner va_list args; 95db5334ad039dab3ec2b50e9a259c97ce882acc0dMark Salyzyn int e = errno; 961f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner 971f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner fprintf(stderr, "%s: ", PROGNAME); 981f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner va_start(args, format); 991f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner vfprintf(stderr, format, args); 1001f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner va_end(args); 101db5334ad039dab3ec2b50e9a259c97ce882acc0dMark Salyzyn exit(e ? -e : 1); 1021f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner} 1031f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner 104db5334ad039dab3ec2b50e9a259c97ce882acc0dMark Salyzynstatic void 105db5334ad039dab3ec2b50e9a259c97ce882acc0dMark Salyzynusage(void) 106db5334ad039dab3ec2b50e9a259c97ce882acc0dMark Salyzyn{ 107a08d313bb87279d2a203cded92669638e3458f5fOleksiy Vyalov panic("Usage:\n " PROGNAME " <package-name> [--user <uid>] <command> [<args>]\n"); 108db5334ad039dab3ec2b50e9a259c97ce882acc0dMark Salyzyn} 1091f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner 1101f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turnerint main(int argc, char **argv) 1111f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner{ 1121f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner const char* pkgname; 113a08d313bb87279d2a203cded92669638e3458f5fOleksiy Vyalov uid_t myuid, uid, gid, userAppId = 0; 114a08d313bb87279d2a203cded92669638e3458f5fOleksiy Vyalov int commandArgvOfs = 2, userId = 0; 1151f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner PackageInfo info; 116db5334ad039dab3ec2b50e9a259c97ce882acc0dMark Salyzyn struct __user_cap_header_struct capheader; 117db5334ad039dab3ec2b50e9a259c97ce882acc0dMark Salyzyn struct __user_cap_data_struct capdata[2]; 1181f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner 1191f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner /* check arguments */ 120db5334ad039dab3ec2b50e9a259c97ce882acc0dMark Salyzyn if (argc < 2) { 1211f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner usage(); 122db5334ad039dab3ec2b50e9a259c97ce882acc0dMark Salyzyn } 1231f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner 1241f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner /* check userid of caller - must be 'shell' or 'root' */ 1251f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner myuid = getuid(); 1261f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner if (myuid != AID_SHELL && myuid != AID_ROOT) { 1271f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner panic("only 'shell' or 'root' users can run this program\n"); 1281f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner } 1291f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner 130db5334ad039dab3ec2b50e9a259c97ce882acc0dMark Salyzyn memset(&capheader, 0, sizeof(capheader)); 131db5334ad039dab3ec2b50e9a259c97ce882acc0dMark Salyzyn memset(&capdata, 0, sizeof(capdata)); 132db5334ad039dab3ec2b50e9a259c97ce882acc0dMark Salyzyn capheader.version = _LINUX_CAPABILITY_VERSION_3; 133db5334ad039dab3ec2b50e9a259c97ce882acc0dMark Salyzyn capdata[CAP_TO_INDEX(CAP_SETUID)].effective |= CAP_TO_MASK(CAP_SETUID); 134db5334ad039dab3ec2b50e9a259c97ce882acc0dMark Salyzyn capdata[CAP_TO_INDEX(CAP_SETGID)].effective |= CAP_TO_MASK(CAP_SETGID); 135db5334ad039dab3ec2b50e9a259c97ce882acc0dMark Salyzyn capdata[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID); 136db5334ad039dab3ec2b50e9a259c97ce882acc0dMark Salyzyn capdata[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID); 137db5334ad039dab3ec2b50e9a259c97ce882acc0dMark Salyzyn 138db5334ad039dab3ec2b50e9a259c97ce882acc0dMark Salyzyn if (capset(&capheader, &capdata[0]) < 0) { 139db5334ad039dab3ec2b50e9a259c97ce882acc0dMark Salyzyn panic("Could not set capabilities: %s\n", strerror(errno)); 140db5334ad039dab3ec2b50e9a259c97ce882acc0dMark Salyzyn } 141db5334ad039dab3ec2b50e9a259c97ce882acc0dMark Salyzyn 1421f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner pkgname = argv[1]; 143a08d313bb87279d2a203cded92669638e3458f5fOleksiy Vyalov 144a08d313bb87279d2a203cded92669638e3458f5fOleksiy Vyalov /* get user_id from command line if provided */ 145a08d313bb87279d2a203cded92669638e3458f5fOleksiy Vyalov if ((argc >= 4) && !strcmp(argv[2], "--user")) { 146a08d313bb87279d2a203cded92669638e3458f5fOleksiy Vyalov userId = atoi(argv[3]); 147a08d313bb87279d2a203cded92669638e3458f5fOleksiy Vyalov if (userId < 0) 148a08d313bb87279d2a203cded92669638e3458f5fOleksiy Vyalov panic("Negative user id %d is provided\n", userId); 149a08d313bb87279d2a203cded92669638e3458f5fOleksiy Vyalov commandArgvOfs += 2; 150a08d313bb87279d2a203cded92669638e3458f5fOleksiy Vyalov } 151a08d313bb87279d2a203cded92669638e3458f5fOleksiy Vyalov 152a08d313bb87279d2a203cded92669638e3458f5fOleksiy Vyalov /* retrieve package information from system (does setegid) */ 153a08d313bb87279d2a203cded92669638e3458f5fOleksiy Vyalov if (get_package_info(pkgname, userId, &info) < 0) { 1541f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner panic("Package '%s' is unknown\n", pkgname); 1551f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner } 1561f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner 157a08d313bb87279d2a203cded92669638e3458f5fOleksiy Vyalov /* verify that user id is not too big. */ 158a08d313bb87279d2a203cded92669638e3458f5fOleksiy Vyalov if ((UID_MAX - info.uid) / AID_USER < (uid_t)userId) { 159a08d313bb87279d2a203cded92669638e3458f5fOleksiy Vyalov panic("User id %d is too big\n", userId); 160a08d313bb87279d2a203cded92669638e3458f5fOleksiy Vyalov } 161a08d313bb87279d2a203cded92669638e3458f5fOleksiy Vyalov 162a08d313bb87279d2a203cded92669638e3458f5fOleksiy Vyalov /* calculate user app ID. */ 163a08d313bb87279d2a203cded92669638e3458f5fOleksiy Vyalov userAppId = (AID_USER * userId) + info.uid; 164a08d313bb87279d2a203cded92669638e3458f5fOleksiy Vyalov 1651f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner /* reject system packages */ 166a08d313bb87279d2a203cded92669638e3458f5fOleksiy Vyalov if (userAppId < AID_APP) { 1671f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner panic("Package '%s' is not an application\n", pkgname); 1681f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner } 1691f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner 1701f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner /* reject any non-debuggable package */ 1711f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner if (!info.isDebuggable) { 1721f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner panic("Package '%s' is not debuggable\n", pkgname); 1731f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner } 1741f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner 1751f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner /* check that the data directory path is valid */ 176a08d313bb87279d2a203cded92669638e3458f5fOleksiy Vyalov if (check_data_path(info.dataDir, userAppId) < 0) { 1771f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner panic("Package '%s' has corrupt installation\n", pkgname); 1781f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner } 1791f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner 1801f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner /* Ensure that we change all real/effective/saved IDs at the 1811f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner * same time to avoid nasty surprises. 1821f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner */ 183a08d313bb87279d2a203cded92669638e3458f5fOleksiy Vyalov uid = gid = userAppId; 1841f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner if(setresgid(gid,gid,gid) || setresuid(uid,uid,uid)) { 1851f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner panic("Permission denied\n"); 186db5334ad039dab3ec2b50e9a259c97ce882acc0dMark Salyzyn } 187db5334ad039dab3ec2b50e9a259c97ce882acc0dMark Salyzyn 188db5334ad039dab3ec2b50e9a259c97ce882acc0dMark Salyzyn /* Required if caller has uid and gid all non-zero */ 189db5334ad039dab3ec2b50e9a259c97ce882acc0dMark Salyzyn memset(&capdata, 0, sizeof(capdata)); 190db5334ad039dab3ec2b50e9a259c97ce882acc0dMark Salyzyn if (capset(&capheader, &capdata[0]) < 0) { 191db5334ad039dab3ec2b50e9a259c97ce882acc0dMark Salyzyn panic("Could not clear all capabilities: %s\n", strerror(errno)); 1921f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner } 1931f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner 194fced3ded831cb084121b10a78c12de99c89004aaRobert Craig if (selinux_android_setcontext(uid, 0, info.seinfo, pkgname) < 0) { 195db5334ad039dab3ec2b50e9a259c97ce882acc0dMark Salyzyn panic("Could not set SELinux security context: %s\n", strerror(errno)); 1964ead8beac8fe59b01ad1e5670713b99e7f841b9bStephen Smalley } 1974ead8beac8fe59b01ad1e5670713b99e7f841b9bStephen Smalley 198a372f6f24192f28e66b9e73885aca34839112a66Elliott Hughes // cd into the data directory, and set $HOME correspondingly. 199db5334ad039dab3ec2b50e9a259c97ce882acc0dMark Salyzyn if (TEMP_FAILURE_RETRY(chdir(info.dataDir)) < 0) { 200db5334ad039dab3ec2b50e9a259c97ce882acc0dMark Salyzyn panic("Could not cd to package's data directory: %s\n", strerror(errno)); 201b0739c662db6a19b49c0912b865edb2853156bdaAlex Klyubin } 202a372f6f24192f28e66b9e73885aca34839112a66Elliott Hughes setenv("HOME", info.dataDir, 1); 203a372f6f24192f28e66b9e73885aca34839112a66Elliott Hughes 204a372f6f24192f28e66b9e73885aca34839112a66Elliott Hughes // Reset parts of the environment, like su would. 205a372f6f24192f28e66b9e73885aca34839112a66Elliott Hughes setenv("PATH", _PATH_DEFPATH, 1); 206a372f6f24192f28e66b9e73885aca34839112a66Elliott Hughes unsetenv("IFS"); 207a372f6f24192f28e66b9e73885aca34839112a66Elliott Hughes 208a372f6f24192f28e66b9e73885aca34839112a66Elliott Hughes // Set the user-specific parts for this user. 209a372f6f24192f28e66b9e73885aca34839112a66Elliott Hughes struct passwd* pw = getpwuid(uid); 210a372f6f24192f28e66b9e73885aca34839112a66Elliott Hughes setenv("LOGNAME", pw->pw_name, 1); 211a372f6f24192f28e66b9e73885aca34839112a66Elliott Hughes setenv("SHELL", pw->pw_shell, 1); 212a372f6f24192f28e66b9e73885aca34839112a66Elliott Hughes setenv("USER", pw->pw_name, 1); 213b0739c662db6a19b49c0912b865edb2853156bdaAlex Klyubin 2141f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner /* User specified command for exec. */ 215a08d313bb87279d2a203cded92669638e3458f5fOleksiy Vyalov if ((argc >= commandArgvOfs + 1) && 216a08d313bb87279d2a203cded92669638e3458f5fOleksiy Vyalov (execvp(argv[commandArgvOfs], argv+commandArgvOfs) < 0)) { 217a08d313bb87279d2a203cded92669638e3458f5fOleksiy Vyalov panic("exec failed for %s: %s\n", argv[commandArgvOfs], strerror(errno)); 2181f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner } 2191f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner 2201f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner /* Default exec shell. */ 2211f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner execlp("/system/bin/sh", "sh", NULL); 2221f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner 223db5334ad039dab3ec2b50e9a259c97ce882acc0dMark Salyzyn panic("exec failed: %s\n", strerror(errno)); 2241f4d95296acf34a93128332441782a80c10845b4David 'Digit' Turner} 225