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 #include <errno.h>
     18 #include <fcntl.h>
     19 #include <unistd.h>
     20 #include <sys/stat.h>
     21 #include <private/android_filesystem_config.h>
     22 #include "package.h"
     23 
     24 /*
     25  *  WARNING WARNING WARNING WARNING
     26  *
     27  *  The following code runs as root on production devices, before
     28  *  the run-as command has dropped the uid/gid. Hence be very
     29  *  conservative and keep in mind the following:
     30  *
     31  *  - Performance does not matter here, clarity and safety of the code
     32  *    does however. Documentation is a must.
     33  *
     34  *  - Avoid calling C library functions with complex implementations
     35  *    like malloc() and printf(). You want to depend on simple system
     36  *    calls instead, which behaviour is not going to be altered in
     37  *    unpredictible ways by environment variables or system properties.
     38  *
     39  *  - Do not trust user input and/or the filesystem whenever possible.
     40  *
     41  */
     42 
     43 /* The file containing the list of installed packages on the system */
     44 #define PACKAGES_LIST_FILE  "/data/system/packages.list"
     45 
     46 /* This should be large enough to hold the content of the package database file */
     47 #define PACKAGES_LIST_BUFFER_SIZE  8192
     48 
     49 /* Copy 'srclen' string bytes from 'src' into buffer 'dst' of size 'dstlen'
     50  * This function always zero-terminate the destination buffer unless
     51  * 'dstlen' is 0, even in case of overflow.
     52  */
     53 static void
     54 string_copy(char* dst, size_t dstlen, const char* src, size_t srclen)
     55 {
     56     const char* srcend = src + srclen;
     57     const char* dstend = dst + dstlen;
     58 
     59     if (dstlen == 0)
     60         return;
     61 
     62     dstend--; /* make room for terminating zero */
     63 
     64     while (dst < dstend && src < srcend && *src != '\0')
     65         *dst++ = *src++;
     66 
     67     *dst = '\0'; /* zero-terminate result */
     68 }
     69 
     70 /* Read up to 'buffsize' bytes into 'buff' from the file
     71  * named 'filename'. Return byte length on success, or -1
     72  * on error.
     73  */
     74 static int
     75 read_file(const char* filename, char* buff, size_t buffsize)
     76 {
     77     int  fd, len, old_errno;
     78 
     79     /* check the input buffer size */
     80     if (buffsize >= INT_MAX) {
     81         errno = EINVAL;
     82         return -1;
     83     }
     84 
     85     /* open the file for reading */
     86     do {
     87         fd = open(filename, O_RDONLY);
     88     } while (fd < 0 && errno == EINTR);
     89 
     90     if (fd < 0)
     91         return -1;
     92 
     93     /* read the content */
     94     do {
     95         len = read(fd, buff, buffsize);
     96     } while (len < 0 && errno == EINTR);
     97 
     98     /* close the file, preserve old errno for better diagnostics */
     99     old_errno = errno;
    100     close(fd);
    101     errno = old_errno;
    102 
    103     return len;
    104 }
    105 
    106 /* Check that a given directory:
    107  * - exists
    108  * - is owned by a given uid/gid
    109  * - is a real directory, not a symlink
    110  * - isn't readable or writable by others
    111  *
    112  * Return 0 on success, or -1 on error.
    113  * errno is set to EINVAL in case of failed check.
    114  */
    115 static int
    116 check_directory_ownership(const char* path, uid_t uid)
    117 {
    118     int ret;
    119     struct stat st;
    120 
    121     do {
    122         ret = lstat(path, &st);
    123     } while (ret < 0 && errno == EINTR);
    124 
    125     if (ret < 0)
    126         return -1;
    127 
    128     /* must be a real directory, not a symlink */
    129     if (!S_ISDIR(st.st_mode))
    130         goto BAD;
    131 
    132     /* must be owned by specific uid/gid */
    133     if (st.st_uid != uid || st.st_gid != uid)
    134         goto BAD;
    135 
    136     /* must not be readable or writable by others */
    137     if ((st.st_mode & (S_IROTH|S_IWOTH)) != 0)
    138         goto BAD;
    139 
    140     /* everything ok */
    141     return 0;
    142 
    143 BAD:
    144     errno = EINVAL;
    145     return -1;
    146 }
    147 
    148 /* This function is used to check the data directory path for safety.
    149  * We check that every sub-directory is owned by the 'system' user
    150  * and exists and is not a symlink. We also check that the full directory
    151  * path is properly owned by the user ID.
    152  *
    153  * Return 0 on success, -1 on error.
    154  */
    155 int
    156 check_data_path(const char* dataPath, uid_t  uid)
    157 {
    158     int  nn;
    159 
    160     /* the path should be absolute */
    161     if (dataPath[0] != '/') {
    162         errno = EINVAL;
    163         return -1;
    164     }
    165 
    166     /* look for all sub-paths, we do that by finding
    167      * directory separators in the input path and
    168      * checking each sub-path independently
    169      */
    170     for (nn = 1; dataPath[nn] != '\0'; nn++)
    171     {
    172         char subpath[PATH_MAX];
    173 
    174         /* skip non-separator characters */
    175         if (dataPath[nn] != '/')
    176             continue;
    177 
    178         /* handle trailing separator case */
    179         if (dataPath[nn+1] == '\0') {
    180             break;
    181         }
    182 
    183         /* found a separator, check that dataPath is not too long. */
    184         if (nn >= (int)(sizeof subpath)) {
    185             errno = EINVAL;
    186             return -1;
    187         }
    188 
    189         /* reject any '..' subpath */
    190         if (nn >= 3               &&
    191             dataPath[nn-3] == '/' &&
    192             dataPath[nn-2] == '.' &&
    193             dataPath[nn-1] == '.') {
    194             errno = EINVAL;
    195             return -1;
    196         }
    197 
    198         /* copy to 'subpath', then check ownership */
    199         memcpy(subpath, dataPath, nn);
    200         subpath[nn] = '\0';
    201 
    202         if (check_directory_ownership(subpath, AID_SYSTEM) < 0)
    203             return -1;
    204     }
    205 
    206     /* All sub-paths were checked, now verify that the full data
    207      * directory is owned by the application uid
    208      */
    209     if (check_directory_ownership(dataPath, uid) < 0)
    210         return -1;
    211 
    212     /* all clear */
    213     return 0;
    214 }
    215 
    216 /* Return TRUE iff a character is a space or tab */
    217 static inline int
    218 is_space(char c)
    219 {
    220     return (c == ' ' || c == '\t');
    221 }
    222 
    223 /* Skip any space or tab character from 'p' until 'end' is reached.
    224  * Return new position.
    225  */
    226 static const char*
    227 skip_spaces(const char*  p, const char*  end)
    228 {
    229     while (p < end && is_space(*p))
    230         p++;
    231 
    232     return p;
    233 }
    234 
    235 /* Skip any non-space and non-tab character from 'p' until 'end'.
    236  * Return new position.
    237  */
    238 static const char*
    239 skip_non_spaces(const char* p, const char* end)
    240 {
    241     while (p < end && !is_space(*p))
    242         p++;
    243 
    244     return p;
    245 }
    246 
    247 /* Find the first occurence of 'ch' between 'p' and 'end'
    248  * Return its position, or 'end' if none is found.
    249  */
    250 static const char*
    251 find_first(const char* p, const char* end, char ch)
    252 {
    253     while (p < end && *p != ch)
    254         p++;
    255 
    256     return p;
    257 }
    258 
    259 /* Check that the non-space string starting at 'p' and eventually
    260  * ending at 'end' equals 'name'. Return new position (after name)
    261  * on success, or NULL on failure.
    262  *
    263  * This function fails is 'name' is NULL, empty or contains any space.
    264  */
    265 static const char*
    266 compare_name(const char* p, const char* end, const char* name)
    267 {
    268     /* 'name' must not be NULL or empty */
    269     if (name == NULL || name[0] == '\0' || p == end)
    270         return NULL;
    271 
    272     /* compare characters to those in 'name', excluding spaces */
    273     while (*name) {
    274         /* note, we don't check for *p == '\0' since
    275          * it will be caught in the next conditional.
    276          */
    277         if (p >= end || is_space(*p))
    278             goto BAD;
    279 
    280         if (*p != *name)
    281             goto BAD;
    282 
    283         p++;
    284         name++;
    285     }
    286 
    287     /* must be followed by end of line or space */
    288     if (p < end && !is_space(*p))
    289         goto BAD;
    290 
    291     return p;
    292 
    293 BAD:
    294     return NULL;
    295 }
    296 
    297 /* Parse one or more whitespace characters starting from '*pp'
    298  * until 'end' is reached. Updates '*pp' on exit.
    299  *
    300  * Return 0 on success, -1 on failure.
    301  */
    302 static int
    303 parse_spaces(const char** pp, const char* end)
    304 {
    305     const char* p = *pp;
    306 
    307     if (p >= end || !is_space(*p)) {
    308         errno = EINVAL;
    309         return -1;
    310     }
    311     p   = skip_spaces(p, end);
    312     *pp = p;
    313     return 0;
    314 }
    315 
    316 /* Parse a positive decimal number starting from '*pp' until 'end'
    317  * is reached. Adjust '*pp' on exit. Return decimal value or -1
    318  * in case of error.
    319  *
    320  * If the value is larger than INT_MAX, -1 will be returned,
    321  * and errno set to EOVERFLOW.
    322  *
    323  * If '*pp' does not start with a decimal digit, -1 is returned
    324  * and errno set to EINVAL.
    325  */
    326 static int
    327 parse_positive_decimal(const char** pp, const char* end)
    328 {
    329     const char* p = *pp;
    330     int value = 0;
    331     int overflow = 0;
    332 
    333     if (p >= end || *p < '0' || *p > '9') {
    334         errno = EINVAL;
    335         return -1;
    336     }
    337 
    338     while (p < end) {
    339         int      ch = *p;
    340         unsigned d  = (unsigned)(ch - '0');
    341         int      val2;
    342 
    343         if (d >= 10U) /* d is unsigned, no lower bound check */
    344             break;
    345 
    346         val2 = value*10 + (int)d;
    347         if (val2 < value)
    348             overflow = 1;
    349         value = val2;
    350         p++;
    351     }
    352     *pp = p;
    353 
    354     if (overflow) {
    355         errno = EOVERFLOW;
    356         value = -1;
    357     }
    358     return value;
    359 
    360 BAD:
    361     *pp = p;
    362     return -1;
    363 }
    364 
    365 /* Read the system's package database and extract information about
    366  * 'pkgname'. Return 0 in case of success, or -1 in case of error.
    367  *
    368  * If the package is unknown, return -1 and set errno to ENOENT
    369  * If the package database is corrupted, return -1 and set errno to EINVAL
    370  */
    371 int
    372 get_package_info(const char* pkgName, PackageInfo *info)
    373 {
    374     static char  buffer[PACKAGES_LIST_BUFFER_SIZE];
    375     int          buffer_len;
    376     const char*  p;
    377     const char*  buffer_end;
    378     int          result;
    379 
    380     info->uid          = 0;
    381     info->isDebuggable = 0;
    382     info->dataDir[0]   = '\0';
    383 
    384     buffer_len = read_file(PACKAGES_LIST_FILE, buffer, sizeof buffer);
    385     if (buffer_len < 0)
    386         return -1;
    387 
    388     p          = buffer;
    389     buffer_end = buffer + buffer_len;
    390 
    391     /* expect the following format on each line of the control file:
    392      *
    393      *  <pkgName> <uid> <debugFlag> <dataDir>
    394      *
    395      * where:
    396      *  <pkgName>    is the package's name
    397      *  <uid>        is the application-specific user Id (decimal)
    398      *  <debugFlag>  is 1 if the package is debuggable, or 0 otherwise
    399      *  <dataDir>    is the path to the package's data directory (e.g. /data/data/com.example.foo)
    400      *
    401      * The file is generated in com.android.server.PackageManagerService.Settings.writeLP()
    402      */
    403 
    404     while (p < buffer_end) {
    405         /* find end of current line and start of next one */
    406         const char*  end  = find_first(p, buffer_end, '\n');
    407         const char*  next = (end < buffer_end) ? end + 1 : buffer_end;
    408         const char*  q;
    409         int          uid, debugFlag;
    410 
    411         /* first field is the package name */
    412         p = compare_name(p, end, pkgName);
    413         if (p == NULL)
    414             goto NEXT_LINE;
    415 
    416         /* skip spaces */
    417         if (parse_spaces(&p, end) < 0)
    418             goto BAD_FORMAT;
    419 
    420         /* second field is the pid */
    421         uid = parse_positive_decimal(&p, end);
    422         if (uid < 0)
    423             return -1;
    424 
    425         info->uid = (uid_t) uid;
    426 
    427         /* skip spaces */
    428         if (parse_spaces(&p, end) < 0)
    429             goto BAD_FORMAT;
    430 
    431         /* third field is debug flag (0 or 1) */
    432         debugFlag = parse_positive_decimal(&p, end);
    433         switch (debugFlag) {
    434         case 0:
    435             info->isDebuggable = 0;
    436             break;
    437         case 1:
    438             info->isDebuggable = 1;
    439             break;
    440         default:
    441             goto BAD_FORMAT;
    442         }
    443 
    444         /* skip spaces */
    445         if (parse_spaces(&p, end) < 0)
    446             goto BAD_FORMAT;
    447 
    448         /* fourth field is data directory path and must not contain
    449          * spaces.
    450          */
    451         q = skip_non_spaces(p, end);
    452         if (q == p)
    453             goto BAD_FORMAT;
    454 
    455         string_copy(info->dataDir, sizeof info->dataDir, p, q - p);
    456 
    457         /* Ignore the rest */
    458         return 0;
    459 
    460     NEXT_LINE:
    461         p = next;
    462     }
    463 
    464     /* the package is unknown */
    465     errno = ENOENT;
    466     return -1;
    467 
    468 BAD_FORMAT:
    469     errno = EINVAL;
    470     return -1;
    471 }
    472