1 #include <ctype.h>
2 #include <errno.h>
3 #include <fcntl.h>
4 #include <fnmatch.h>
5 #include <fts.h>
6 #include <libgen.h>
7 #include <limits.h>
8 #include <linux/magic.h>
9 #include <stdbool.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <sys/stat.h>
14 #include <sys/types.h>
15 #include <sys/vfs.h>
16 #include <sys/xattr.h>
17 #include <unistd.h>
18
19 #include <log/log.h>
20 #include <packagelistparser/packagelistparser.h>
21 #include <private/android_filesystem_config.h>
22 #include <selinux/android.h>
23 #include <selinux/context.h>
24 #include <selinux/selinux.h>
25
26 #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
27 #include <sys/_system_properties.h>
28
29 #include "android_internal.h"
30 #include "callbacks.h"
31 #include "label_internal.h"
32 #include "selinux_internal.h"
33
selinux_android_context_with_level(const char * context,char ** newContext,uid_t userid,uid_t appid)34 int selinux_android_context_with_level(const char * context,
35 char ** newContext,
36 uid_t userid,
37 uid_t appid)
38 {
39 int rc = -2;
40
41 enum levelFrom levelFrom;
42 if (userid == (uid_t) -1) {
43 levelFrom = (appid == (uid_t) -1) ? LEVELFROM_NONE : LEVELFROM_APP;
44 } else {
45 levelFrom = (appid == (uid_t) -1) ? LEVELFROM_USER : LEVELFROM_ALL;
46 }
47
48 context_t ctx = context_new(context);
49 if (!ctx) {
50 goto out;
51 }
52
53 int res = set_range_from_level(ctx, levelFrom, userid, appid);
54 if (res != 0) {
55 rc = res;
56 goto out;
57 }
58
59 const char * newString = context_str(ctx);
60 if (!newString) {
61 goto out;
62 }
63
64 char * newCopied = strdup(newString);
65 if (!newCopied) {
66 goto out;
67 }
68
69 *newContext = newCopied;
70 rc = 0;
71
72 out:
73 context_free(ctx);
74 return rc;
75 }
76
selinux_android_setcon(const char * con)77 int selinux_android_setcon(const char *con)
78 {
79 int ret = setcon(con);
80 if (ret)
81 return ret;
82 /*
83 System properties must be reinitialized after setcon() otherwise the
84 previous property files will be leaked since mmap()'ed regions are not
85 closed as a result of setcon().
86 */
87 return __system_properties_init();
88 }
89
selinux_android_setcontext(uid_t uid,bool isSystemServer,const char * seinfo,const char * pkgname)90 int selinux_android_setcontext(uid_t uid,
91 bool isSystemServer,
92 const char *seinfo,
93 const char *pkgname)
94 {
95 char *orig_ctx_str = NULL;
96 const char *ctx_str = NULL;
97 context_t ctx = NULL;
98 int rc = -1;
99
100 if (is_selinux_enabled() <= 0)
101 return 0;
102
103 rc = getcon(&orig_ctx_str);
104 if (rc)
105 goto err;
106
107 ctx = context_new(orig_ctx_str);
108 if (!ctx)
109 goto oom;
110
111 rc = seapp_context_lookup(SEAPP_DOMAIN, uid, isSystemServer, seinfo, pkgname, ctx);
112 if (rc == -1)
113 goto err;
114 else if (rc == -2)
115 goto oom;
116
117 ctx_str = context_str(ctx);
118 if (!ctx_str)
119 goto oom;
120
121 rc = security_check_context(ctx_str);
122 if (rc < 0)
123 goto err;
124
125 if (strcmp(ctx_str, orig_ctx_str)) {
126 rc = selinux_android_setcon(ctx_str);
127 if (rc < 0)
128 goto err;
129 }
130
131 rc = 0;
132 out:
133 freecon(orig_ctx_str);
134 context_free(ctx);
135 return rc;
136 err:
137 if (isSystemServer)
138 selinux_log(SELINUX_ERROR,
139 "%s: Error setting context for system server: %s\n",
140 __FUNCTION__, strerror(errno));
141 else
142 selinux_log(SELINUX_ERROR,
143 "%s: Error setting context for app with uid %d, seinfo %s: %s\n",
144 __FUNCTION__, uid, seinfo, strerror(errno));
145
146 rc = -1;
147 goto out;
148 oom:
149 selinux_log(SELINUX_ERROR, "%s: Out of memory\n", __FUNCTION__);
150 rc = -1;
151 goto out;
152 }
153
154 static struct selabel_handle *fc_sehandle = NULL;
155
file_context_init(void)156 static void file_context_init(void)
157 {
158 if (!fc_sehandle)
159 fc_sehandle = selinux_android_file_context_handle();
160 }
161
162 static pthread_once_t fc_once = PTHREAD_ONCE_INIT;
163
164 #define PKGTAB_SIZE 256
165 /* Hash table for pkg_info. It uses the package name as key. In case of
166 * collision, the next entry is the private_data attribute */
167 static struct pkg_info *pkgTab[PKGTAB_SIZE];
168
169 /* Returns a hash based on the package name */
pkghash(const char * pkgname)170 static unsigned int pkghash(const char *pkgname)
171 {
172 unsigned int h = 7;
173 for (; *pkgname; pkgname++) {
174 h = h * 31 + *pkgname;
175 }
176 return h & (PKGTAB_SIZE - 1);
177 }
178
179 /* Adds the pkg_info entry to the hash table */
pkg_parse_callback(pkg_info * info,void * userdata)180 static bool pkg_parse_callback(pkg_info *info, void *userdata) {
181
182 (void) userdata;
183
184 unsigned int hash = pkghash(info->name);
185 if (pkgTab[hash])
186 /* Collision. Prepend the entry. */
187 info->private_data = pkgTab[hash];
188 pkgTab[hash] = info;
189 return true;
190 }
191
192 /* Initialize the pkg_info hash table */
package_info_init(void)193 static void package_info_init(void)
194 {
195
196 bool rc = packagelist_parse(pkg_parse_callback, NULL);
197 if (!rc) {
198 selinux_log(SELINUX_ERROR, "SELinux: Could NOT parse package list\n");
199 return;
200 }
201
202 #if DEBUG
203 {
204 unsigned int hash, buckets, entries, chainlen, longestchain;
205 struct pkg_info *info = NULL;
206
207 buckets = entries = longestchain = 0;
208 for (hash = 0; hash < PKGTAB_SIZE; hash++) {
209 if (pkgTab[hash]) {
210 buckets++;
211 chainlen = 0;
212 for (info = pkgTab[hash]; info; info = (pkg_info *)info->private_data) {
213 chainlen++;
214 selinux_log(SELINUX_INFO, "%s: name=%s uid=%u debuggable=%s dataDir=%s seinfo=%s\n",
215 __FUNCTION__,
216 info->name, info->uid, info->debuggable ? "true" : "false", info->data_dir, info->seinfo);
217 }
218 entries += chainlen;
219 if (longestchain < chainlen)
220 longestchain = chainlen;
221 }
222 }
223 selinux_log(SELINUX_INFO, "SELinux: %d pkg entries and %d/%d buckets used, longest chain %d\n", entries, buckets, PKGTAB_SIZE, longestchain);
224 }
225 #endif
226
227 }
228
229 static pthread_once_t pkg_once = PTHREAD_ONCE_INIT;
230
231 /* Returns the pkg_info for a package with a specific name */
package_info_lookup(const char * name)232 struct pkg_info *package_info_lookup(const char *name)
233 {
234 struct pkg_info *info;
235 unsigned int hash;
236
237 __selinux_once(pkg_once, package_info_init);
238
239 hash = pkghash(name);
240 for (info = pkgTab[hash]; info; info = (pkg_info *)info->private_data) {
241 if (!strcmp(name, info->name))
242 return info;
243 }
244 return NULL;
245 }
246
247 /* The contents of these paths are encrypted on FBE devices until user
248 * credentials are presented (filenames inside are mangled), so we need
249 * to delay restorecon of those until vold explicitly requests it. */
250 // NOTE: these paths need to be kept in sync with vold
251 #define DATA_SYSTEM_CE_PATH "/data/system_ce"
252 #define DATA_VENDOR_CE_PATH "/data/vendor_ce"
253 #define DATA_MISC_CE_PATH "/data/misc_ce"
254 #define DATA_MISC_DE_PATH "/data/misc_de"
255
256 /* The path prefixes of package data directories. */
257 #define DATA_DATA_PATH "/data/data"
258 #define DATA_USER_PATH "/data/user"
259 #define DATA_USER_DE_PATH "/data/user_de"
260 #define USER_PROFILE_PATH "/data/misc/profiles/cur/*"
261 #define SDK_SANDBOX_DATA_CE_PATH "/data/misc_ce/*/sdksandbox"
262 #define SDK_SANDBOX_DATA_DE_PATH "/data/misc_de/*/sdksandbox"
263
264 #define EXPAND_MNT_PATH "/mnt/expand/\?\?\?\?\?\?\?\?-\?\?\?\?-\?\?\?\?-\?\?\?\?-\?\?\?\?\?\?\?\?\?\?\?\?"
265 #define EXPAND_USER_PATH EXPAND_MNT_PATH "/user"
266 #define EXPAND_USER_DE_PATH EXPAND_MNT_PATH "/user_de"
267 #define EXPAND_SDK_CE_PATH EXPAND_MNT_PATH "/misc_ce/*/sdksandbox"
268 #define EXPAND_SDK_DE_PATH EXPAND_MNT_PATH "/misc_de/*/sdksandbox"
269
270 #define DATA_DATA_PREFIX DATA_DATA_PATH "/"
271 #define DATA_USER_PREFIX DATA_USER_PATH "/"
272 #define DATA_USER_DE_PREFIX DATA_USER_DE_PATH "/"
273 #define DATA_MISC_CE_PREFIX DATA_MISC_CE_PATH "/"
274 #define DATA_MISC_DE_PREFIX DATA_MISC_DE_PATH "/"
275 #define EXPAND_MNT_PATH_PREFIX EXPAND_MNT_PATH "/"
276
277 /*
278 * This method helps in identifying paths that refer to users' app data. Labeling for app data is
279 * based on seapp_contexts and seinfo assignments rather than file_contexts and is managed by
280 * installd rather than by init.
281 */
is_app_data_path(const char * pathname)282 static bool is_app_data_path(const char *pathname) {
283 int flags = FNM_LEADING_DIR|FNM_PATHNAME;
284 return (!strncmp(pathname, DATA_DATA_PREFIX, sizeof(DATA_DATA_PREFIX)-1) ||
285 !strncmp(pathname, DATA_USER_PREFIX, sizeof(DATA_USER_PREFIX)-1) ||
286 !strncmp(pathname, DATA_USER_DE_PREFIX, sizeof(DATA_USER_DE_PREFIX)-1) ||
287 !fnmatch(EXPAND_USER_PATH, pathname, flags) ||
288 !fnmatch(EXPAND_USER_DE_PATH, pathname, flags) ||
289 !fnmatch(SDK_SANDBOX_DATA_CE_PATH, pathname, flags) ||
290 !fnmatch(SDK_SANDBOX_DATA_DE_PATH, pathname, flags) ||
291 !fnmatch(EXPAND_SDK_CE_PATH, pathname, flags) ||
292 !fnmatch(EXPAND_SDK_DE_PATH, pathname, flags));
293 }
294
295 /*
296 * Extract the userid from a path.
297 * On success, pathname is updated past the userid.
298 * Returns 0 on success, -1 on error
299 */
extract_userid(const char ** pathname,unsigned int * userid)300 static int extract_userid(const char **pathname, unsigned int *userid)
301 {
302 char *end = NULL;
303
304 errno = 0;
305 *userid = strtoul(*pathname, &end, 10);
306 if (errno) {
307 selinux_log(SELINUX_ERROR, "SELinux: Could not parse userid %s: %s.\n",
308 *pathname, strerror(errno));
309 return -1;
310 }
311 if (*pathname == end) {
312 return -1;
313 }
314 if (*userid > 1000) {
315 return -1;
316 }
317 *pathname = end;
318 return 0;
319 }
320
321 /* Extract the pkgname and userid from a path.
322 * On success, the caller is responsible for free'ing pkgname.
323 * Returns 0 on success, -1 on invalid path, -2 on error.
324 */
extract_pkgname_and_userid(const char * pathname,char ** pkgname,unsigned int * userid)325 static int extract_pkgname_and_userid(const char *pathname, char **pkgname, unsigned int *userid)
326 {
327 char *end = NULL;
328
329 if (pkgname == NULL || *pkgname != NULL || userid == NULL) {
330 errno = EINVAL;
331 return -2;
332 }
333
334 /* Skip directory prefix before package name. */
335 if (!strncmp(pathname, DATA_DATA_PREFIX, sizeof(DATA_DATA_PREFIX)-1)) {
336 pathname += sizeof(DATA_DATA_PREFIX) - 1;
337 } else if (!strncmp(pathname, DATA_USER_PREFIX, sizeof(DATA_USER_PREFIX)-1)) {
338 pathname += sizeof(DATA_USER_PREFIX) - 1;
339 int rc = extract_userid(&pathname, userid);
340 if (rc)
341 return -1;
342 if (*pathname == '/')
343 pathname++;
344 else
345 return -1;
346 } else if (!strncmp(pathname, DATA_USER_DE_PREFIX, sizeof(DATA_USER_DE_PREFIX)-1)) {
347 pathname += sizeof(DATA_USER_DE_PREFIX) - 1;
348 int rc = extract_userid(&pathname, userid);
349 if (rc)
350 return -1;
351 if (*pathname == '/')
352 pathname++;
353 else
354 return -1;
355 } else if (!fnmatch(EXPAND_USER_PATH, pathname, FNM_LEADING_DIR|FNM_PATHNAME)) {
356 pathname += sizeof(EXPAND_USER_PATH);
357 int rc = extract_userid(&pathname, userid);
358 if (rc)
359 return -1;
360 if (*pathname == '/')
361 pathname++;
362 else
363 return -1;
364 } else if (!fnmatch(EXPAND_USER_DE_PATH, pathname, FNM_LEADING_DIR|FNM_PATHNAME)) {
365 pathname += sizeof(EXPAND_USER_DE_PATH);
366 int rc = extract_userid(&pathname, userid);
367 if (rc)
368 return -1;
369 if (*pathname == '/')
370 pathname++;
371 else
372 return -1;
373 } else if (!strncmp(pathname, DATA_MISC_CE_PREFIX, sizeof(DATA_MISC_CE_PREFIX)-1)) {
374 pathname += sizeof(DATA_MISC_CE_PREFIX) - 1;
375 int rc = extract_userid(&pathname, userid);
376 if (rc)
377 return -1;
378 if (!strncmp(pathname, "/sdksandbox/", sizeof("/sdksandbox/")-1))
379 pathname += sizeof("/sdksandbox/") - 1;
380 else
381 return -1;
382 } else if (!strncmp(pathname, DATA_MISC_DE_PREFIX, sizeof(DATA_MISC_DE_PREFIX)-1)) {
383 pathname += sizeof(DATA_MISC_DE_PREFIX) - 1;
384 int rc = extract_userid(&pathname, userid);
385 if (rc)
386 return -1;
387 if (!strncmp(pathname, "/sdksandbox/", sizeof("/sdksandbox/")-1))
388 pathname += sizeof("/sdksandbox/") - 1;
389 else
390 return -1;
391 } else if (!fnmatch(EXPAND_SDK_CE_PATH, pathname, FNM_LEADING_DIR|FNM_PATHNAME)) {
392 pathname += sizeof(EXPAND_MNT_PATH_PREFIX) - 1;
393 pathname += sizeof("misc_ce/") - 1;
394 int rc = extract_userid(&pathname, userid);
395 if (rc)
396 return -1;
397 if (!strncmp(pathname, "/sdksandbox/", sizeof("/sdksandbox/")-1))
398 pathname += sizeof("/sdksandbox/") - 1;
399 else
400 return -1;
401 } else if (!fnmatch(EXPAND_SDK_DE_PATH, pathname, FNM_LEADING_DIR|FNM_PATHNAME)) {
402 pathname += sizeof(EXPAND_MNT_PATH_PREFIX) - 1;
403 pathname += sizeof("misc_de/") - 1;
404 int rc = extract_userid(&pathname, userid);
405 if (rc)
406 return -1;
407 if (!strncmp(pathname, "/sdksandbox/", sizeof("/sdksandbox/")-1))
408 pathname += sizeof("/sdksandbox/") - 1;
409 else
410 return -1;
411 } else
412 return -1;
413
414 if (!(*pathname))
415 return -1;
416
417 *pkgname = strdup(pathname);
418 if (!(*pkgname))
419 return -2;
420
421 // Trim pkgname.
422 for (end = *pkgname; *end && *end != '/'; end++);
423 *end = '\0';
424
425 return 0;
426 }
427
pkgdir_selabel_lookup(const char * pathname,const char * seinfo,uid_t uid,char ** secontextp)428 static int pkgdir_selabel_lookup(const char *pathname,
429 const char *seinfo,
430 uid_t uid,
431 char **secontextp)
432 {
433 char *pkgname = NULL;
434 struct pkg_info *info = NULL;
435 const char *orig_ctx_str = *secontextp;
436 const char *ctx_str = NULL;
437 context_t ctx = NULL;
438 int rc = 0;
439 unsigned int userid_from_path = 0;
440
441 rc = extract_pkgname_and_userid(pathname, &pkgname, &userid_from_path);
442 if (rc) {
443 /* Invalid path, we skip it */
444 if (rc == -1) {
445 return 0;
446 }
447 return rc;
448 }
449
450 if (!seinfo) {
451 info = package_info_lookup(pkgname);
452 if (!info) {
453 selinux_log(SELINUX_WARNING, "SELinux: Could not look up information for package %s, cannot restorecon %s.\n",
454 pkgname, pathname);
455 free(pkgname);
456 return -1;
457 }
458 // info->uid only contains the appid and not the userid.
459 info->uid += userid_from_path * AID_USER_OFFSET;
460 }
461
462 ctx = context_new(orig_ctx_str);
463 if (!ctx)
464 goto err;
465
466 rc = seapp_context_lookup(SEAPP_TYPE, info ? info->uid : uid, 0,
467 info ? info->seinfo : seinfo, info ? info->name : pkgname, ctx);
468 if (rc < 0)
469 goto err;
470
471 ctx_str = context_str(ctx);
472 if (!ctx_str)
473 goto err;
474
475 if (!strcmp(ctx_str, orig_ctx_str))
476 goto out;
477
478 rc = security_check_context(ctx_str);
479 if (rc < 0)
480 goto err;
481
482 freecon(*secontextp);
483 *secontextp = strdup(ctx_str);
484 if (!(*secontextp))
485 goto err;
486
487 rc = 0;
488
489 out:
490 free(pkgname);
491 context_free(ctx);
492 return rc;
493 err:
494 selinux_log(SELINUX_ERROR, "%s: Error looking up context for path %s, pkgname %s, seinfo %s, uid %u: %s\n",
495 __FUNCTION__, pathname, pkgname, info ? info->seinfo : seinfo,
496 info ? info->uid : uid, strerror(errno));
497 rc = -1;
498 goto out;
499 }
500
501 #define RESTORECON_PARTIAL_MATCH_DIGEST "security.sehash"
502
restorecon_sb(const char * pathname,const struct stat * sb,bool nochange,bool verbose,const char * seinfo,uid_t uid)503 static int restorecon_sb(const char *pathname, const struct stat *sb,
504 bool nochange, bool verbose,
505 const char *seinfo, uid_t uid)
506 {
507 char *secontext = NULL;
508 char *oldsecontext = NULL;
509 int rc = 0;
510
511 if (selabel_lookup(fc_sehandle, &secontext, pathname, sb->st_mode) < 0)
512 return 0; /* no match, but not an error */
513
514 if (lgetfilecon(pathname, &oldsecontext) < 0)
515 goto err;
516
517 /*
518 * For subdirectories of /data/data or /data/user, we ignore selabel_lookup()
519 * and use pkgdir_selabel_lookup() instead. Files within those directories
520 * have different labeling rules, based off of /seapp_contexts, and
521 * installd is responsible for managing these labels instead of init.
522 */
523 if (is_app_data_path(pathname)) {
524 if (pkgdir_selabel_lookup(pathname, seinfo, uid, &secontext) < 0)
525 goto err;
526 }
527
528 if (strcmp(oldsecontext, secontext) != 0) {
529 if (verbose)
530 selinux_log(SELINUX_INFO,
531 "SELinux: Relabeling %s from %s to %s.\n", pathname, oldsecontext, secontext);
532 if (!nochange) {
533 if (lsetfilecon(pathname, secontext) < 0)
534 goto err;
535 }
536 }
537
538 rc = 0;
539
540 out:
541 freecon(oldsecontext);
542 freecon(secontext);
543 return rc;
544
545 err:
546 selinux_log(SELINUX_ERROR,
547 "SELinux: Could not set context for %s: %s\n",
548 pathname, strerror(errno));
549 rc = -1;
550 goto out;
551 }
552
553 #define SYS_PATH "/sys"
554 #define SYS_PREFIX SYS_PATH "/"
555
556 struct dir_hash_node {
557 char* path;
558 uint8_t digest[SHA1_HASH_SIZE];
559 struct dir_hash_node *next;
560 };
561
562 // Returns true if the digest of all partial matched contexts is the same as the one
563 // saved by setxattr. Otherwise returns false and constructs a dir_hash_node with the
564 // newly calculated digest.
check_context_match_for_dir(const char * pathname,struct dir_hash_node ** new_node,bool force,int error)565 static bool check_context_match_for_dir(const char *pathname, struct dir_hash_node **new_node,
566 bool force, int error) {
567 uint8_t read_digest[SHA1_HASH_SIZE];
568 ssize_t read_size = getxattr(pathname, RESTORECON_PARTIAL_MATCH_DIGEST,
569 read_digest, SHA1_HASH_SIZE);
570 uint8_t calculated_digest[SHA1_HASH_SIZE];
571 bool status = selabel_hash_all_partial_matches(fc_sehandle, pathname,
572 calculated_digest);
573
574 if (!new_node) {
575 return false;
576 }
577 *new_node = NULL;
578 if (!force && status && read_size == SHA1_HASH_SIZE &&
579 memcmp(read_digest, calculated_digest, SHA1_HASH_SIZE) == 0) {
580 return true;
581 }
582
583 // Save the digest of all matched contexts for the current directory.
584 if (!error && status) {
585 *new_node = calloc(1, sizeof(struct dir_hash_node));
586 if (*new_node == NULL) {
587 selinux_log(SELINUX_ERROR,
588 "SELinux: %s: Out of memory\n", __func__);
589 return false;
590 }
591
592 (*new_node)->path = strdup(pathname);
593 if ((*new_node)->path == NULL) {
594 selinux_log(SELINUX_ERROR,
595 "SELinux: %s: Out of memory\n", __func__);
596 free(*new_node);
597 *new_node = NULL;
598 return false;
599 }
600 memcpy((*new_node)->digest, calculated_digest, SHA1_HASH_SIZE);
601 (*new_node)->next = NULL;
602 }
603
604 return false;
605 }
606
selinux_android_restorecon_common(const char * pathname_orig,const char * seinfo,uid_t uid,unsigned int flags)607 static int selinux_android_restorecon_common(const char* pathname_orig,
608 const char *seinfo,
609 uid_t uid,
610 unsigned int flags)
611 {
612 bool nochange = (flags & SELINUX_ANDROID_RESTORECON_NOCHANGE) ? true : false;
613 bool verbose = (flags & SELINUX_ANDROID_RESTORECON_VERBOSE) ? true : false;
614 bool recurse = (flags & SELINUX_ANDROID_RESTORECON_RECURSE) ? true : false;
615 bool force = (flags & SELINUX_ANDROID_RESTORECON_FORCE) ? true : false;
616 bool datadata = (flags & SELINUX_ANDROID_RESTORECON_DATADATA) ? true : false;
617 bool skipce = (flags & SELINUX_ANDROID_RESTORECON_SKIPCE) ? true : false;
618 bool cross_filesystems = (flags & SELINUX_ANDROID_RESTORECON_CROSS_FILESYSTEMS) ? true : false;
619 bool setrestoreconlast = (flags & SELINUX_ANDROID_RESTORECON_SKIP_SEHASH) ? false : true;
620 bool issys;
621 struct stat sb;
622 struct statfs sfsb;
623 FTS *fts;
624 FTSENT *ftsent;
625 char *pathname = NULL, *pathdnamer = NULL, *pathdname, *pathbname;
626 char * paths[2] = { NULL , NULL };
627 int ftsflags = FTS_NOCHDIR | FTS_PHYSICAL;
628 int error, sverrno;
629 struct dir_hash_node *current = NULL;
630 struct dir_hash_node *head = NULL;
631
632 if (!cross_filesystems) {
633 ftsflags |= FTS_XDEV;
634 }
635
636 if (is_selinux_enabled() <= 0)
637 return 0;
638
639 __selinux_once(fc_once, file_context_init);
640
641 if (!fc_sehandle)
642 return 0;
643
644 /*
645 * Convert passed-in pathname to canonical pathname by resolving realpath of
646 * containing dir, then appending last component name.
647 */
648 pathbname = basename(pathname_orig);
649 if (!strcmp(pathbname, "/") || !strcmp(pathbname, ".") || !strcmp(pathbname, "..")) {
650 pathname = realpath(pathname_orig, NULL);
651 if (!pathname)
652 goto realpatherr;
653 } else {
654 pathdname = dirname(pathname_orig);
655 pathdnamer = realpath(pathdname, NULL);
656 if (!pathdnamer)
657 goto realpatherr;
658 if (!strcmp(pathdnamer, "/"))
659 error = asprintf(&pathname, "/%s", pathbname);
660 else
661 error = asprintf(&pathname, "%s/%s", pathdnamer, pathbname);
662 if (error < 0)
663 goto oom;
664 }
665
666 paths[0] = pathname;
667 issys = (!strcmp(pathname, SYS_PATH)
668 || !strncmp(pathname, SYS_PREFIX, sizeof(SYS_PREFIX)-1)) ? true : false;
669
670 if (!recurse) {
671 if (lstat(pathname, &sb) < 0) {
672 error = -1;
673 goto cleanup;
674 }
675
676 error = restorecon_sb(pathname, &sb, nochange, verbose, seinfo, uid);
677 goto cleanup;
678 }
679
680 /*
681 * Ignore saved partial match digest on /data/data or /data/user
682 * since their labeling is based on seapp_contexts and seinfo
683 * assignments rather than file_contexts and is managed by
684 * installd rather than init.
685 */
686 if (is_app_data_path(pathname))
687 setrestoreconlast = false;
688
689 /* Also ignore on /sys since it is regenerated on each boot regardless. */
690 if (issys)
691 setrestoreconlast = false;
692
693 /* Ignore files on in-memory filesystems */
694 if (statfs(pathname, &sfsb) == 0) {
695 if (sfsb.f_type == RAMFS_MAGIC || sfsb.f_type == TMPFS_MAGIC)
696 setrestoreconlast = false;
697 }
698
699 fts = fts_open(paths, ftsflags, NULL);
700 if (!fts) {
701 error = -1;
702 goto cleanup;
703 }
704
705 error = 0;
706 while ((ftsent = fts_read(fts)) != NULL) {
707 switch (ftsent->fts_info) {
708 case FTS_DC:
709 selinux_log(SELINUX_ERROR,
710 "SELinux: Directory cycle on %s.\n", ftsent->fts_path);
711 errno = ELOOP;
712 error = -1;
713 goto out;
714 case FTS_DP:
715 continue;
716 case FTS_DNR:
717 selinux_log(SELINUX_ERROR,
718 "SELinux: Could not read %s: %s.\n", ftsent->fts_path, strerror(errno));
719 fts_set(fts, ftsent, FTS_SKIP);
720 continue;
721 case FTS_NS:
722 selinux_log(SELINUX_ERROR,
723 "SELinux: Could not stat %s: %s.\n", ftsent->fts_path, strerror(errno));
724 fts_set(fts, ftsent, FTS_SKIP);
725 continue;
726 case FTS_ERR:
727 selinux_log(SELINUX_ERROR,
728 "SELinux: Error on %s: %s.\n", ftsent->fts_path, strerror(errno));
729 fts_set(fts, ftsent, FTS_SKIP);
730 continue;
731 case FTS_D:
732 if (issys && !selabel_partial_match(fc_sehandle, ftsent->fts_path)) {
733 fts_set(fts, ftsent, FTS_SKIP);
734 continue;
735 }
736
737 if (!datadata && !fnmatch(USER_PROFILE_PATH, ftsent->fts_path, FNM_PATHNAME)) {
738 // Don't label this directory, vold takes care of that, but continue below it.
739 continue;
740 }
741
742 if (setrestoreconlast) {
743 struct dir_hash_node* new_node = NULL;
744 if (check_context_match_for_dir(ftsent->fts_path, &new_node, force, error)) {
745 selinux_log(SELINUX_INFO,
746 "SELinux: Skipping restorecon on directory(%s)\n",
747 ftsent->fts_path);
748 fts_set(fts, ftsent, FTS_SKIP);
749 continue;
750 }
751 if (new_node) {
752 if (!current) {
753 current = new_node;
754 head = current;
755 } else {
756 current->next = new_node;
757 current = current->next;
758 }
759 }
760 }
761
762 if (skipce &&
763 (!strncmp(ftsent->fts_path, DATA_SYSTEM_CE_PATH, sizeof(DATA_SYSTEM_CE_PATH)-1) ||
764 !strncmp(ftsent->fts_path, DATA_MISC_CE_PATH, sizeof(DATA_MISC_CE_PATH)-1) ||
765 !strncmp(ftsent->fts_path, DATA_VENDOR_CE_PATH, sizeof(DATA_VENDOR_CE_PATH)-1))) {
766 // Don't label anything below this directory.
767 fts_set(fts, ftsent, FTS_SKIP);
768 // but fall through and make sure we label the directory itself
769 }
770
771 if (!datadata && is_app_data_path(ftsent->fts_path)) {
772 // Don't label anything below this directory.
773 fts_set(fts, ftsent, FTS_SKIP);
774 // but fall through and make sure we label the directory itself
775 }
776 /* fall through */
777 default:
778 error |= restorecon_sb(ftsent->fts_path, ftsent->fts_statp, nochange, verbose, seinfo, uid);
779 break;
780 }
781 }
782
783 // Labeling successful. Write the partial match digests for subdirectories.
784 // TODO: Write the digest upon FTS_DP if no error occurs in its descents.
785 if (setrestoreconlast && !nochange && !error) {
786 current = head;
787 while (current != NULL) {
788 if (setxattr(current->path, RESTORECON_PARTIAL_MATCH_DIGEST, current->digest,
789 SHA1_HASH_SIZE, 0) < 0) {
790 selinux_log(SELINUX_ERROR,
791 "SELinux: setxattr failed: %s: %s\n",
792 current->path,
793 strerror(errno));
794 }
795 current = current->next;
796 }
797 }
798
799 out:
800 sverrno = errno;
801 (void) fts_close(fts);
802 errno = sverrno;
803 cleanup:
804 free(pathdnamer);
805 free(pathname);
806 current = head;
807 while (current != NULL) {
808 struct dir_hash_node *next = current->next;
809 free(current->path);
810 free(current);
811 current = next;
812 }
813 return error;
814 oom:
815 sverrno = errno;
816 selinux_log(SELINUX_ERROR, "%s: Out of memory\n", __FUNCTION__);
817 errno = sverrno;
818 error = -1;
819 goto cleanup;
820 realpatherr:
821 sverrno = errno;
822 selinux_log(SELINUX_ERROR, "SELinux: Could not get canonical path for %s restorecon: %s.\n",
823 pathname_orig, strerror(errno));
824 errno = sverrno;
825 error = -1;
826 goto cleanup;
827 }
828
selinux_android_restorecon(const char * file,unsigned int flags)829 int selinux_android_restorecon(const char *file, unsigned int flags)
830 {
831 return selinux_android_restorecon_common(file, NULL, -1, flags);
832 }
833
selinux_android_restorecon_pkgdir(const char * pkgdir,const char * seinfo,uid_t uid,unsigned int flags)834 int selinux_android_restorecon_pkgdir(const char *pkgdir,
835 const char *seinfo,
836 uid_t uid,
837 unsigned int flags)
838 {
839 return selinux_android_restorecon_common(pkgdir, seinfo, uid, flags | SELINUX_ANDROID_RESTORECON_DATADATA);
840 }
841
842
selinux_android_set_sehandle(const struct selabel_handle * hndl)843 void selinux_android_set_sehandle(const struct selabel_handle *hndl)
844 {
845 fc_sehandle = (struct selabel_handle *) hndl;
846 }
847
selinux_android_load_policy()848 int selinux_android_load_policy()
849 {
850 selinux_log(SELINUX_ERROR, "selinux_android_load_policy is not implemented\n");
851 return -1;
852 }
853
selinux_android_load_policy_from_fd(int fd,const char * description)854 int selinux_android_load_policy_from_fd(int fd __attribute__((unused)), const char *description __attribute__((unused)))
855 {
856 selinux_log(SELINUX_ERROR, "selinux_android_load_policy_from_fd is not implemented\n");
857 return -1;
858 }
859