From 1f4d95296acf34a93128332441782a80c10845b4 Mon Sep 17 00:00:00 2001 From: David 'Digit' Turner Date: Tue, 2 Mar 2010 18:05:23 -0800 Subject: [PATCH] Add 'run-as' command implementation as set-uid program. Typical usage is 'run-as ' to run in the data directory, and the user id, of if, and only if is the name of an installed and debuggable application. This relies on the /data/system/packages.list file generated by the PackageManager service. BEWARE: This is intended to be available on production devices ! --- include/private/android_filesystem_config.h | 5 +- run-as/Android.mk | 12 + run-as/NOTICE | 190 ++++++++ run-as/package.c | 471 ++++++++++++++++++++ run-as/package.h | 41 ++ run-as/run-as.c | 178 ++++++++ 6 files changed, 896 insertions(+), 1 deletion(-) create mode 100644 run-as/Android.mk create mode 100644 run-as/NOTICE create mode 100644 run-as/package.c create mode 100644 run-as/package.h create mode 100644 run-as/run-as.c diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h index f2a5fe1..4cab96a 100644 --- a/include/private/android_filesystem_config.h +++ b/include/private/android_filesystem_config.h @@ -169,7 +169,7 @@ static struct fs_path_config android_files[] = { * Do not change. */ { 02755, AID_ROOT, AID_NET_RAW, "system/bin/ping" }, { 02750, AID_ROOT, AID_INET, "system/bin/netcfg" }, - /* the following four files are INTENTIONALLY set-uid, but they + /* the following five files are INTENTIONALLY set-uid, but they * are NOT included on user builds. */ { 06755, AID_ROOT, AID_ROOT, "system/xbin/su" }, { 06755, AID_ROOT, AID_ROOT, "system/xbin/librank" }, @@ -177,6 +177,9 @@ static struct fs_path_config android_files[] = { { 06755, AID_ROOT, AID_ROOT, "system/xbin/procmem" }, { 06755, AID_ROOT, AID_ROOT, "system/xbin/tcpdump" }, { 04770, AID_ROOT, AID_RADIO, "system/bin/pppd-ril" }, + /* the following file is INTENTIONALLY set-uid, and IS included + * in user builds. */ + { 06750, AID_ROOT, AID_SHELL, "system/bin/run-as" }, { 00755, AID_ROOT, AID_SHELL, "system/bin/*" }, { 00755, AID_ROOT, AID_SHELL, "system/xbin/*" }, { 00750, AID_ROOT, AID_SHELL, "sbin/*" }, diff --git a/run-as/Android.mk b/run-as/Android.mk new file mode 100644 index 0000000..326f5af --- /dev/null +++ b/run-as/Android.mk @@ -0,0 +1,12 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= run-as.c package.c + +LOCAL_MODULE:= run-as + +LOCAL_FORCE_STATIC_EXECUTABLE := true + +LOCAL_STATIC_LIBRARIES := libc + +include $(BUILD_EXECUTABLE) diff --git a/run-as/NOTICE b/run-as/NOTICE new file mode 100644 index 0000000..c5b1efa --- /dev/null +++ b/run-as/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-2008, The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/run-as/package.c b/run-as/package.c new file mode 100644 index 0000000..46f8239 --- /dev/null +++ b/run-as/package.c @@ -0,0 +1,471 @@ +/* +** +** Copyright 2010, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +#include +#include +#include +#include +#include +#include "package.h" + +/* + * WARNING WARNING WARNING WARNING + * + * The following code runs as root on production devices, before + * the run-as command has dropped the uid/gid. Hence be very + * conservative and keep in mind the following: + * + * - Performance does not matter here, clarity and safety of the code + * does however. Documentation is a must. + * + * - Avoid calling C library functions with complex implementations + * like malloc() and printf(). You want to depend on simple system + * calls instead, which behaviour is not going to be altered in + * unpredictible ways by environment variables or system properties. + * + * - Do not trust user input and/or the filesystem whenever possible. + * + */ + +/* The file containing the list of installed packages on the system */ +#define PACKAGES_LIST_FILE "/data/system/packages.list" + +/* This should be large enough to hold the content of the package database file */ +#define PACKAGES_LIST_BUFFER_SIZE 8192 + +/* Copy 'srclen' string bytes from 'src' into buffer 'dst' of size 'dstlen' + * This function always zero-terminate the destination buffer unless + * 'dstlen' is 0, even in case of overflow. + */ +static void +string_copy(char* dst, size_t dstlen, const char* src, size_t srclen) +{ + const char* srcend = src + srclen; + const char* dstend = dst + dstlen; + + if (dstlen == 0) + return; + + dstend--; /* make room for terminating zero */ + + while (dst < dstend && src < srcend && *src != '\0') + *dst++ = *src++; + + *dst = '\0'; /* zero-terminate result */ +} + +/* Read up to 'buffsize' bytes into 'buff' from the file + * named 'filename'. Return byte length on success, or -1 + * on error. + */ +static int +read_file(const char* filename, char* buff, size_t buffsize) +{ + int fd, len, old_errno; + + /* check the input buffer size */ + if (buffsize >= INT_MAX) { + errno = EINVAL; + return -1; + } + + /* open the file for reading */ + do { + fd = open(filename, O_RDONLY); + } while (fd < 0 && errno == EINTR); + + if (fd < 0) + return -1; + + /* read the content */ + do { + len = read(fd, buff, buffsize); + } while (len < 0 && errno == EINTR); + + /* close the file, preserve old errno for better diagnostics */ + old_errno = errno; + close(fd); + errno = old_errno; + + return len; +} + +/* Check that a given directory: + * - exists + * - is owned by a given uid/gid + * - is a real directory, not a symlink + * - isn't readable or writable by others + * + * Return 0 on success, or -1 on error. + * errno is set to EINVAL in case of failed check. + */ +static int +check_directory_ownership(const char* path, uid_t uid) +{ + int ret; + struct stat st; + + do { + ret = lstat(path, &st); + } while (ret < 0 && errno == EINTR); + + if (ret < 0) + return -1; + + /* must be a real directory, not a symlink */ + if (!S_ISDIR(st.st_mode)) + goto BAD; + + /* must be owned by specific uid/gid */ + if (st.st_uid != uid || st.st_gid != uid) + goto BAD; + + /* must not be readable or writable by others */ + if ((st.st_mode & (S_IROTH|S_IWOTH)) != 0) + goto BAD; + + /* everything ok */ + return 0; + +BAD: + errno = EINVAL; + return -1; +} + +/* This function is used to check the data directory path for safety. + * We check that every sub-directory is owned by the 'system' user + * and exists and is not a symlink. We also check that the full directory + * path is properly owned by the user ID. + * + * Return 0 on success, -1 on error. + */ +int +check_data_path(const char* dataPath, uid_t uid) +{ + int nn; + + /* the path should be absolute */ + if (dataPath[0] != '/') { + errno = EINVAL; + return -1; + } + + /* look for all sub-paths, we do that by finding + * directory separators in the input path and + * checking each sub-path independently + */ + for (nn = 1; dataPath[nn] != '\0'; nn++) + { + char subpath[PATH_MAX]; + + /* skip non-separator characters */ + if (dataPath[nn] != '/') + continue; + + /* handle trailing separator case */ + if (dataPath[nn+1] == '\0') { + break; + } + + /* found a separator, check that dataPath is not too long. */ + if (nn >= (int)(sizeof subpath)) { + errno = EINVAL; + return -1; + } + + /* reject any '..' subpath */ + if (nn >= 3 && + dataPath[nn-3] == '/' && + dataPath[nn-2] == '.' && + dataPath[nn-1] == '.') { + errno = EINVAL; + return -1; + } + + /* copy to 'subpath', then check ownership */ + memcpy(subpath, dataPath, nn); + subpath[nn] = '\0'; + + if (check_directory_ownership(subpath, AID_SYSTEM) < 0) + return -1; + } + + /* All sub-paths were checked, now verify that the full data + * directory is owned by the application uid + */ + if (check_directory_ownership(dataPath, uid) < 0) + return -1; + + /* all clear */ + return 0; +} + +/* Return TRUE iff a character is a space or tab */ +static inline int +is_space(char c) +{ + return (c == ' ' || c == '\t'); +} + +/* Skip any space or tab character from 'p' until 'end' is reached. + * Return new position. + */ +static const char* +skip_spaces(const char* p, const char* end) +{ + while (p < end && is_space(*p)) + p++; + + return p; +} + +/* Skip any non-space and non-tab character from 'p' until 'end'. + * Return new position. + */ +static const char* +skip_non_spaces(const char* p, const char* end) +{ + while (p < end && !is_space(*p)) + p++; + + return p; +} + +/* Find the first occurence of 'ch' between 'p' and 'end' + * Return its position, or 'end' if none is found. + */ +static const char* +find_first(const char* p, const char* end, char ch) +{ + while (p < end && *p != ch) + p++; + + return p; +} + +/* Check that the non-space string starting at 'p' and eventually + * ending at 'end' equals 'name'. Return new position (after name) + * on success, or NULL on failure. + * + * This function fails is 'name' is NULL, empty or contains any space. + */ +static const char* +compare_name(const char* p, const char* end, const char* name) +{ + /* 'name' must not be NULL or empty */ + if (name == NULL || name[0] == '\0' || p == end) + return NULL; + + /* compare characters to those in 'name', excluding spaces */ + while (*name) { + /* note, we don't check for *p == '\0' since + * it will be caught in the next conditional. + */ + if (p >= end || is_space(*p)) + goto BAD; + + if (*p != *name) + goto BAD; + + p++; + name++; + } + + /* must be followed by end of line or space */ + if (p < end && !is_space(*p)) + goto BAD; + + return p; + +BAD: + return NULL; +} + +/* Parse one or more whitespace characters starting from '*pp' + * until 'end' is reached. Updates '*pp' on exit. + * + * Return 0 on success, -1 on failure. + */ +static int +parse_spaces(const char** pp, const char* end) +{ + const char* p = *pp; + + if (p >= end || !is_space(*p)) { + errno = EINVAL; + return -1; + } + p = skip_spaces(p, end); + *pp = p; + return 0; +} + +/* Parse a positive decimal number starting from '*pp' until 'end' + * is reached. Adjust '*pp' on exit. Return decimal value or -1 + * in case of error. + * + * If the value is larger than INT_MAX, -1 will be returned, + * and errno set to EOVERFLOW. + * + * If '*pp' does not start with a decimal digit, -1 is returned + * and errno set to EINVAL. + */ +static int +parse_positive_decimal(const char** pp, const char* end) +{ + const char* p = *pp; + int value = 0; + int overflow = 0; + + if (p >= end || *p < '0' || *p > '9') { + errno = EINVAL; + return -1; + } + + while (p < end) { + int ch = *p; + unsigned d = (unsigned)(ch - '0'); + int val2; + + if (d >= 10U) /* d is unsigned, no lower bound check */ + break; + + val2 = value*10 + (int)d; + if (val2 < value) + overflow = 1; + value = val2; + p++; + } + *pp = p; + + if (overflow) { + errno = EOVERFLOW; + value = -1; + } + return value; + +BAD: + *pp = p; + return -1; +} + +/* Read the system's package database and extract information about + * 'pkgname'. Return 0 in case of success, or -1 in case of error. + * + * If the package is unknown, return -1 and set errno to ENOENT + * If the package database is corrupted, return -1 and set errno to EINVAL + */ +int +get_package_info(const char* pkgName, PackageInfo *info) +{ + static char buffer[PACKAGES_LIST_BUFFER_SIZE]; + int buffer_len; + const char* p; + const char* buffer_end; + int result; + + info->uid = 0; + info->isDebuggable = 0; + info->dataDir[0] = '\0'; + + buffer_len = read_file(PACKAGES_LIST_FILE, buffer, sizeof buffer); + if (buffer_len < 0) + return -1; + + p = buffer; + buffer_end = buffer + buffer_len; + + /* expect the following format on each line of the control file: + * + * + * + * where: + * is the package's name + * is the application-specific user Id (decimal) + * is 1 if the package is debuggable, or 0 otherwise + * is the path to the package's data directory (e.g. /data/data/com.example.foo) + * + * The file is generated in com.android.server.PackageManagerService.Settings.writeLP() + */ + + while (p < buffer_end) { + /* find end of current line and start of next one */ + const char* end = find_first(p, buffer_end, '\n'); + const char* next = (end < buffer_end) ? end + 1 : buffer_end; + const char* q; + int uid, debugFlag; + + /* first field is the package name */ + p = compare_name(p, end, pkgName); + if (p == NULL) + goto NEXT_LINE; + + /* skip spaces */ + if (parse_spaces(&p, end) < 0) + goto BAD_FORMAT; + + /* second field is the pid */ + uid = parse_positive_decimal(&p, end); + if (uid < 0) + return -1; + + info->uid = (uid_t) uid; + + /* skip spaces */ + if (parse_spaces(&p, end) < 0) + goto BAD_FORMAT; + + /* third field is debug flag (0 or 1) */ + debugFlag = parse_positive_decimal(&p, end); + switch (debugFlag) { + case 0: + info->isDebuggable = 0; + break; + case 1: + info->isDebuggable = 1; + break; + default: + goto BAD_FORMAT; + } + + /* skip spaces */ + if (parse_spaces(&p, end) < 0) + goto BAD_FORMAT; + + /* fourth field is data directory path and must not contain + * spaces. + */ + q = skip_non_spaces(p, end); + if (q == p) + goto BAD_FORMAT; + + string_copy(info->dataDir, sizeof info->dataDir, p, q - p); + + /* Ignore the rest */ + return 0; + + NEXT_LINE: + p = next; + } + + /* the package is unknown */ + errno = ENOENT; + return -1; + +BAD_FORMAT: + errno = EINVAL; + return -1; +} diff --git a/run-as/package.h b/run-as/package.h new file mode 100644 index 0000000..852af06 --- /dev/null +++ b/run-as/package.h @@ -0,0 +1,41 @@ +/* +** +** Copyright 2010, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +#ifndef RUN_AS_PACKAGE_H +#define RUN_AS_PACKAGE_H + +#include +#include + +typedef enum { + PACKAGE_IS_DEBUGGABLE = 0, + PACKAGE_IS_NOT_DEBUGGABLE, + PACKAGE_IS_UNKNOWN, +} PackageStatus; + +typedef struct { + uid_t uid; + char isDebuggable; + char dataDir[PATH_MAX]; +} PackageInfo; + +/* see documentation in package.c for these functiosn */ + +extern int get_package_info(const char* packageName, PackageInfo* info); + +extern int check_data_path(const char* dataDir, uid_t uid); + +#endif /* RUN_AS_PACKAGE_H */ diff --git a/run-as/run-as.c b/run-as/run-as.c new file mode 100644 index 0000000..d2a44e1 --- /dev/null +++ b/run-as/run-as.c @@ -0,0 +1,178 @@ +/* +** +** Copyright 2010, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#define PROGNAME "run-as" +#define LOG_TAG PROGNAME + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "package.h" + +/* + * WARNING WARNING WARNING WARNING + * + * This program runs as set-uid root on Android production devices. + * Be very conservative when modifying it to avoid any serious + * security issue. Keep in mind the following: + * + * - This program should only run for the 'root' or 'shell' users + * + * - Statically link against the C library, and avoid anything that + * is more complex than simple system calls until the uid/gid has + * been dropped to that of a normal user or you are sure to exit. + * + * This avoids depending on environment variables, system properties + * and other external factors that may affect the C library in + * unpredictable ways. + * + * - Do not trust user input and/or the filesystem whenever possible. + * + * Read README.TXT for more details. + * + * + * + * The purpose of this program is to run a command as a specific + * application user-id. Typical usage is: + * + * run-as + * + * The 'run-as' binary is setuid, but will check the following: + * + * - that it is invoked from the 'shell' or 'root' user (abort otherwise) + * - that '' is the name of an installed and debuggable package + * - that the package's data directory is well-formed (see package.c) + * + * If so, it will cd to the package's data directory, drop to the application's + * user id / group id then run the command there. + * + * This can be useful for a number of different things on production devices: + * + * - Allow application developers to look at their own applicative data + * during development. + * + * - Run the 'gdbserver' binary executable to allow native debugging + */ + +static void +usage(void) +{ + const char* str = "Usage: " PROGNAME " []\n\n"; + write(1, str, strlen(str)); + exit(1); +} + + +static void +panic(const char* format, ...) +{ + va_list args; + + fprintf(stderr, "%s: ", PROGNAME); + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + exit(1); +} + + +int main(int argc, char **argv) +{ + const char* pkgname; + int myuid, uid, gid; + PackageInfo info; + + /* check arguments */ + if (argc < 2) + usage(); + + /* check userid of caller - must be 'shell' or 'root' */ + myuid = getuid(); + if (myuid != AID_SHELL && myuid != AID_ROOT) { + panic("only 'shell' or 'root' users can run this program\n"); + } + + /* retrieve package information from system */ + pkgname = argv[1]; + if (get_package_info(pkgname, &info) < 0) { + panic("Package '%s' is unknown\n", pkgname); + return 1; + } + + /* reject system packages */ + if (info.uid < AID_APP) { + panic("Package '%s' is not an application\n", pkgname); + return 1; + } + + /* reject any non-debuggable package */ + if (!info.isDebuggable) { + panic("Package '%s' is not debuggable\n", pkgname); + return 1; + } + + /* check that the data directory path is valid */ + if (check_data_path(info.dataDir, info.uid) < 0) { + panic("Package '%s' has corrupt installation\n", pkgname); + return 1; + } + + /* then move to it */ + { + int ret; + do { + ret = chdir(info.dataDir); + } while (ret < 0 && errno == EINTR); + + if (ret < 0) { + panic("Could not cd to package's data directory: %s\n", strerror(errno)); + return 1; + } + } + + /* Ensure that we change all real/effective/saved IDs at the + * same time to avoid nasty surprises. + */ + uid = gid = info.uid; + if(setresgid(gid,gid,gid) || setresuid(uid,uid,uid)) { + panic("Permission denied\n"); + return 1; + } + + /* User specified command for exec. */ + if (argc >= 3 ) { + if (execvp(argv[2], argv+2) < 0) { + panic("exec failed for %s Error:%s\n", argv[2], strerror(errno)); + return -errno; + } + } + + /* Default exec shell. */ + execlp("/system/bin/sh", "sh", NULL); + + panic("exec failed\n"); + return 1; +}