• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  65536
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
string_copy(char * dst,size_t dstlen,const char * src,size_t srclen)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
read_file(const char * filename,char * buff,size_t buffsize)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
check_directory_ownership(const char * path,uid_t uid)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
check_data_path(const char * dataPath,uid_t uid)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
is_space(char c)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*
skip_spaces(const char * p,const char * end)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*
skip_non_spaces(const char * p,const char * end)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*
find_first(const char * p,const char * end,char ch)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*
compare_name(const char * p,const char * end,const char * name)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
parse_spaces(const char ** pp,const char * end)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
parse_positive_decimal(const char ** pp,const char * end)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
get_package_info(const char * pkgName,PackageInfo * info)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