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