• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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