package.c revision 1f4d95296acf34a93128332441782a80c10845b4
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 */
53static void
54string_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 */
74static int
75read_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 */
115static int
116check_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
143BAD:
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 */
155int
156check_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 */
217static inline int
218is_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 */
226static const char*
227skip_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 */
238static const char*
239skip_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 */
250static const char*
251find_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 */
265static const char*
266compare_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
293BAD:
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 */
302static int
303parse_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 */
326static int
327parse_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
360BAD:
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 */
371int
372get_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
468BAD_FORMAT:
469    errno = EINVAL;
470    return -1;
471}
472