blob: ca084369de51e758795fcc50d8ea611c074a6b5e [file] [log] [blame]
David 'Digit' Turner1f4d9522010-03-02 18:05:23 -08001/*
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 */
David 'Digit' Turner93d81ef2011-06-06 11:53:07 +020047#define PACKAGES_LIST_BUFFER_SIZE 65536
David 'Digit' Turner1f4d9522010-03-02 18:05:23 -080048
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}