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