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