• 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 #define USER_PROFILE_PATH "/data/misc/profiles/cur/*"
248 
pkgdir_selabel_lookup(const char * pathname,const char * seinfo,uid_t uid,char ** secontextp)249 static int pkgdir_selabel_lookup(const char *pathname,
250 				 const char *seinfo,
251 				 uid_t uid,
252 				 char **secontextp)
253 {
254 	char *pkgname = NULL;
255 	struct pkg_info *info = NULL;
256 	const char *orig_ctx_str = *secontextp;
257 	const char *ctx_str = NULL;
258 	context_t ctx = NULL;
259 	int rc = 0;
260 	unsigned int userid_from_path = 0;
261 
262 	rc = extract_pkgname_and_userid(pathname, &pkgname, &userid_from_path);
263 	if (rc) {
264 		/* Invalid path, we skip it */
265 		if (rc == -1) {
266 			return 0;
267 		}
268 		return rc;
269 	}
270 
271 	if (!seinfo) {
272 		info = package_info_lookup(pkgname);
273 		if (!info) {
274 			selinux_log(SELINUX_WARNING, "SELinux:	Could not look up information for package %s, cannot restorecon %s.\n",
275 						pkgname, pathname);
276 			free(pkgname);
277 			return -1;
278 		}
279 		// info->uid only contains the appid and not the userid.
280 		info->uid += userid_from_path * AID_USER_OFFSET;
281 	}
282 
283 	ctx = context_new(orig_ctx_str);
284 	if (!ctx)
285 		goto err;
286 
287 	rc = seapp_context_lookup(SEAPP_TYPE, info ? info->uid : uid, 0,
288 				  info ? info->seinfo : seinfo, info ? info->name : pkgname, ctx);
289 	if (rc < 0)
290 		goto err;
291 
292 	ctx_str = context_str(ctx);
293 	if (!ctx_str)
294 		goto err;
295 
296 	if (!strcmp(ctx_str, orig_ctx_str))
297 		goto out;
298 
299 	rc = security_check_context(ctx_str);
300 	if (rc < 0)
301 		goto err;
302 
303 	freecon(*secontextp);
304 	*secontextp = strdup(ctx_str);
305 	if (!(*secontextp))
306 		goto err;
307 
308 	rc = 0;
309 
310 out:
311 	free(pkgname);
312 	context_free(ctx);
313 	return rc;
314 err:
315 	selinux_log(SELINUX_ERROR, "%s:  Error looking up context for path %s, pkgname %s, seinfo %s, uid %u: %s\n",
316 				__FUNCTION__, pathname, pkgname, info ? info->seinfo : seinfo,
317 				info ? info->uid : uid, strerror(errno));
318 	rc = -1;
319 	goto out;
320 }
321 
322 #define RESTORECON_PARTIAL_MATCH_DIGEST  "security.sehash"
323 
restorecon_sb(const char * pathname,const struct stat * sb,bool nochange,bool verbose,const char * seinfo,uid_t uid)324 static int restorecon_sb(const char *pathname,
325 			 const struct stat *sb,
326 			 bool nochange,
327 			 bool verbose,
328 			 const char *seinfo,
329 			 uid_t uid)
330 {
331 	char *secontext = NULL;
332 	char *oldsecontext = NULL;
333 	int rc = 0;
334 
335 	if (selabel_lookup(fc_sehandle, &secontext, pathname, sb->st_mode) < 0)
336 		return 0;  /* no match, but not an error */
337 
338 	if (lgetfilecon(pathname, &oldsecontext) < 0)
339 		goto err;
340 
341 	/*
342 	 * For subdirectories of /data/data or /data/user, we ignore selabel_lookup()
343 	 * and use pkgdir_selabel_lookup() instead. Files within those directories
344 	 * have different labeling rules, based off of /seapp_contexts, and
345 	 * installd is responsible for managing these labels instead of init.
346 	 */
347 	if (is_app_data_path(pathname)) {
348 		if (pkgdir_selabel_lookup(pathname, seinfo, uid, &secontext) < 0)
349 			goto err;
350 	}
351 
352 	if (strcmp(oldsecontext, secontext) != 0) {
353 		if (verbose)
354 			selinux_log(SELINUX_INFO,
355 						"SELinux:  Relabeling %s from %s to %s.\n", pathname, oldsecontext, secontext);
356 		if (!nochange) {
357 			if (lsetfilecon(pathname, secontext) < 0)
358 				goto err;
359 		}
360 	}
361 
362 	rc = 0;
363 
364 out:
365 	freecon(oldsecontext);
366 	freecon(secontext);
367 	return rc;
368 
369 err:
370 	selinux_log(SELINUX_ERROR,
371 				"SELinux: Could not set context for %s:  %s\n",
372 				pathname, strerror(errno));
373 	rc = -1;
374 	goto out;
375 }
376 
377 #define SYS_PATH "/sys"
378 #define SYS_PREFIX SYS_PATH "/"
379 
380 struct dir_hash_node {
381 	char* path;
382 	uint8_t digest[SHA1_HASH_SIZE];
383 	struct dir_hash_node *next;
384 };
385 
386 // Returns true if the digest of all partial matched contexts is the same as the one
387 // saved by setxattr. Otherwise returns false and constructs a dir_hash_node with the
388 // newly calculated digest.
check_context_match_for_dir(const char * pathname,struct dir_hash_node ** new_node,bool force,int error)389 static bool check_context_match_for_dir(const char *pathname,
390 					struct dir_hash_node **new_node,
391 					bool force, int error)
392 {
393 	uint8_t read_digest[SHA1_HASH_SIZE];
394 	ssize_t read_size = getxattr(pathname, RESTORECON_PARTIAL_MATCH_DIGEST,
395 					 read_digest, SHA1_HASH_SIZE);
396 	uint8_t calculated_digest[SHA1_HASH_SIZE];
397 	bool status = selabel_hash_all_partial_matches(fc_sehandle, pathname,
398 						       calculated_digest);
399 
400 	if (!new_node) {
401 		return false;
402 	}
403 	*new_node = NULL;
404 	if (!force && status && read_size == SHA1_HASH_SIZE &&
405 		memcmp(read_digest, calculated_digest, SHA1_HASH_SIZE) == 0) {
406 		return true;
407 	}
408 
409 	// Save the digest of all matched contexts for the current directory.
410 	if (!error && status) {
411 		*new_node = calloc(1, sizeof(struct dir_hash_node));
412 		if (*new_node == NULL) {
413 			selinux_log(SELINUX_ERROR,
414 						"SELinux: %s: Out of memory\n", __func__);
415 			return false;
416 		}
417 
418 		(*new_node)->path = strdup(pathname);
419 		if ((*new_node)->path == NULL) {
420 			selinux_log(SELINUX_ERROR,
421 						"SELinux: %s: Out of memory\n", __func__);
422 			free(*new_node);
423 			*new_node = NULL;
424 			return false;
425 		}
426 		memcpy((*new_node)->digest, calculated_digest, SHA1_HASH_SIZE);
427 		(*new_node)->next = NULL;
428 	}
429 
430 	return false;
431 }
432 
selinux_android_restorecon_common(const char * pathname_orig,const char * seinfo,uid_t uid,unsigned int flags)433 static int selinux_android_restorecon_common(const char* pathname_orig,
434 					     const char *seinfo,
435 					     uid_t uid,
436 					     unsigned int flags)
437 {
438 	bool nochange = (flags & SELINUX_ANDROID_RESTORECON_NOCHANGE) ? true : false;
439 	bool verbose = (flags & SELINUX_ANDROID_RESTORECON_VERBOSE) ? true : false;
440 	bool recurse = (flags & SELINUX_ANDROID_RESTORECON_RECURSE) ? true : false;
441 	bool force = (flags & SELINUX_ANDROID_RESTORECON_FORCE) ? true : false;
442 	bool datadata = (flags & SELINUX_ANDROID_RESTORECON_DATADATA) ? true : false;
443 	bool skipce = (flags & SELINUX_ANDROID_RESTORECON_SKIPCE) ? true : false;
444 	bool cross_filesystems = (flags & SELINUX_ANDROID_RESTORECON_CROSS_FILESYSTEMS) ? true : false;
445 	bool setrestoreconlast = (flags & SELINUX_ANDROID_RESTORECON_SKIP_SEHASH) ? false : true;
446 	bool issys;
447 	struct stat sb;
448 	struct statfs sfsb;
449 	FTS *fts;
450 	FTSENT *ftsent;
451 	char *pathname = NULL, *pathdnamer = NULL, *pathdname, *pathbname;
452 	char * paths[2] = { NULL , NULL };
453 	int ftsflags = FTS_NOCHDIR | FTS_PHYSICAL;
454 	int error, sverrno;
455 	struct dir_hash_node *current = NULL;
456 	struct dir_hash_node *head = NULL;
457 
458 	if (!cross_filesystems) {
459 		ftsflags |= FTS_XDEV;
460 	}
461 
462 	if (is_selinux_enabled() <= 0) {
463 		selinux_log(SELINUX_WARNING, "SELinux: SELinux is disabled, skipping restorecon");
464 		return 0;
465 	}
466 
467 	__selinux_once(fc_once, file_context_init);
468 
469 	if (!fc_sehandle)
470 		return 0;
471 
472 	/*
473 	 * Convert passed-in pathname to canonical pathname by resolving realpath of
474 	 * containing dir, then appending last component name.
475 	 */
476 	pathbname = basename(pathname_orig);
477 	if (!strcmp(pathbname, "/") || !strcmp(pathbname, ".") || !strcmp(pathbname, "..")) {
478 		pathname = realpath(pathname_orig, NULL);
479 		if (!pathname)
480 			goto realpatherr;
481 	} else {
482 		pathdname = dirname(pathname_orig);
483 		pathdnamer = realpath(pathdname, NULL);
484 		if (!pathdnamer)
485 			goto realpatherr;
486 		if (!strcmp(pathdnamer, "/"))
487 			error = asprintf(&pathname, "/%s", pathbname);
488 		else
489 			error = asprintf(&pathname, "%s/%s", pathdnamer, pathbname);
490 		if (error < 0)
491 			goto oom;
492 	}
493 
494 	paths[0] = pathname;
495 	issys = (!strcmp(pathname, SYS_PATH)
496 			|| !strncmp(pathname, SYS_PREFIX, sizeof(SYS_PREFIX)-1)) ? true : false;
497 
498 	if (!recurse) {
499 		if (lstat(pathname, &sb) < 0) {
500 			error = -1;
501 			goto cleanup;
502 		}
503 
504 		error = restorecon_sb(pathname, &sb, nochange, verbose, seinfo, uid);
505 		goto cleanup;
506 	}
507 
508 	/*
509 	 * Ignore saved partial match digest on /data/data or /data/user
510 	 * since their labeling is based on seapp_contexts and seinfo
511 	 * assignments rather than file_contexts and is managed by
512 	 * installd rather than init.
513 	 */
514 	if (is_app_data_path(pathname))
515 		setrestoreconlast = false;
516 
517 	/* Also ignore on /sys since it is regenerated on each boot regardless. */
518 	if (issys)
519 		setrestoreconlast = false;
520 
521 	/* Ignore files on in-memory filesystems */
522 	if (statfs(pathname, &sfsb) == 0) {
523 		if (sfsb.f_type == RAMFS_MAGIC || sfsb.f_type == TMPFS_MAGIC)
524 			setrestoreconlast = false;
525 	}
526 
527 	fts = fts_open(paths, ftsflags, NULL);
528 	if (!fts) {
529 		error = -1;
530 		goto cleanup;
531 	}
532 
533 	error = 0;
534 	while ((ftsent = fts_read(fts)) != NULL) {
535 		switch (ftsent->fts_info) {
536 		case FTS_DC:
537 			selinux_log(SELINUX_ERROR,
538 						"SELinux:  Directory cycle on %s.\n", ftsent->fts_path);
539 			errno = ELOOP;
540 			error = -1;
541 			goto out;
542 		case FTS_DP:
543 			continue;
544 		case FTS_DNR:
545 			selinux_log(SELINUX_ERROR,
546 						"SELinux:  Could not read %s: %s.\n", ftsent->fts_path, strerror(errno));
547 			fts_set(fts, ftsent, FTS_SKIP);
548 			continue;
549 		case FTS_NS:
550 			selinux_log(SELINUX_ERROR,
551 						"SELinux:  Could not stat %s: %s.\n", ftsent->fts_path, strerror(errno));
552 			fts_set(fts, ftsent, FTS_SKIP);
553 			continue;
554 		case FTS_ERR:
555 			selinux_log(SELINUX_ERROR,
556 						"SELinux:  Error on %s: %s.\n", ftsent->fts_path, strerror(errno));
557 			fts_set(fts, ftsent, FTS_SKIP);
558 			continue;
559 		case FTS_D:
560 			if (issys && !selabel_partial_match(fc_sehandle, ftsent->fts_path)) {
561 				fts_set(fts, ftsent, FTS_SKIP);
562 				continue;
563 			}
564 
565 			if (!datadata && !fnmatch(USER_PROFILE_PATH, ftsent->fts_path, FNM_PATHNAME)) {
566 				// Don't label this directory, vold takes care of that, but continue below it.
567 				continue;
568 			}
569 
570 			if (setrestoreconlast) {
571 				struct dir_hash_node* new_node = NULL;
572 				if (check_context_match_for_dir(ftsent->fts_path, &new_node, force, error)) {
573 					selinux_log(SELINUX_INFO,
574 								"SELinux: Skipping restorecon on directory(%s)\n",
575 								ftsent->fts_path);
576 					fts_set(fts, ftsent, FTS_SKIP);
577 					continue;
578 				}
579 				if (new_node) {
580 					if (!current) {
581 						current = new_node;
582 						head = current;
583 					} else {
584 						current->next = new_node;
585 						current = current->next;
586 					}
587 				}
588 			}
589 
590 			if (skipce && is_credential_encrypted_path(ftsent->fts_path)) {
591 				// Don't label anything below this directory.
592 				fts_set(fts, ftsent, FTS_SKIP);
593 				// but fall through and make sure we label the directory itself
594 			}
595 
596 			if (!datadata && is_app_data_path(ftsent->fts_path)) {
597 				// Don't label anything below this directory.
598 				fts_set(fts, ftsent, FTS_SKIP);
599 				// but fall through and make sure we label the directory itself
600 			}
601 			/* fall through */
602 		default:
603 			error |= restorecon_sb(ftsent->fts_path, ftsent->fts_statp, nochange, verbose, seinfo, uid);
604 			break;
605 		}
606 	}
607 
608 	// Labeling successful. Write the partial match digests for subdirectories.
609 	// TODO: Write the digest upon FTS_DP if no error occurs in its descents.
610 	if (setrestoreconlast && !nochange && !error) {
611 		current = head;
612 		while (current != NULL) {
613 			if (setxattr(current->path, RESTORECON_PARTIAL_MATCH_DIGEST, current->digest,
614 					SHA1_HASH_SIZE, 0) < 0) {
615 				selinux_log(SELINUX_ERROR,
616 							"SELinux:  setxattr failed: %s:  %s\n",
617 							current->path,
618 							strerror(errno));
619 			}
620 			current = current->next;
621 		}
622 	}
623 
624 out:
625 	sverrno = errno;
626 	(void) fts_close(fts);
627 	errno = sverrno;
628 cleanup:
629 	free(pathdnamer);
630 	free(pathname);
631 	current = head;
632 	while (current != NULL) {
633 		struct dir_hash_node *next = current->next;
634 		free(current->path);
635 		free(current);
636 		current = next;
637 	}
638 	return error;
639 oom:
640 	sverrno = errno;
641 	selinux_log(SELINUX_ERROR, "%s:  Out of memory\n", __FUNCTION__);
642 	errno = sverrno;
643 	error = -1;
644 	goto cleanup;
645 realpatherr:
646 	sverrno = errno;
647 	selinux_log(SELINUX_ERROR, "SELinux: Could not get canonical path for %s restorecon: %s.\n",
648 			pathname_orig, strerror(errno));
649 	errno = sverrno;
650 	error = -1;
651 	goto cleanup;
652 }
653 
selinux_android_restorecon(const char * file,unsigned int flags)654 int selinux_android_restorecon(const char *file, unsigned int flags)
655 {
656 	return selinux_android_restorecon_common(file, NULL, -1, flags);
657 }
658 
selinux_android_restorecon_pkgdir(const char * pkgdir,const char * seinfo,uid_t uid,unsigned int flags)659 int selinux_android_restorecon_pkgdir(const char *pkgdir,
660 				      const char *seinfo,
661 				      uid_t uid,
662 				      unsigned int flags)
663 {
664 	return selinux_android_restorecon_common(pkgdir, seinfo, uid, flags | SELINUX_ANDROID_RESTORECON_DATADATA);
665 }
666 
667 
selinux_android_set_sehandle(const struct selabel_handle * hndl)668 void selinux_android_set_sehandle(const struct selabel_handle *hndl)
669 {
670 	fc_sehandle = (struct selabel_handle *) hndl;
671 }
672 
selinux_android_load_policy()673 int selinux_android_load_policy()
674 {
675 	selinux_log(SELINUX_ERROR, "selinux_android_load_policy is not implemented\n");
676 	return -1;
677 }
678 
selinux_android_load_policy_from_fd(int fd,const char * description)679 int selinux_android_load_policy_from_fd(int fd __attribute__((unused)), const char *description __attribute__((unused)))
680 {
681 	selinux_log(SELINUX_ERROR, "selinux_android_load_policy_from_fd is not implemented\n");
682 	return -1;
683 }
684