xref: /aosp_15_r20/external/selinux/libselinux/src/android/android_device.c (revision 2d543d20722ada2425b5bdab9d0d1d29470e7bba)
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/system_properties.h>
15 #include <sys/types.h>
16 #include <sys/vfs.h>
17 #include <sys/xattr.h>
18 #include <unistd.h>
19 
20 #include <log/log.h>
21 #include <packagelistparser/packagelistparser.h>
22 #include <private/android_filesystem_config.h>
23 #include <selinux/android.h>
24 #include <selinux/context.h>
25 #include <selinux/selinux.h>
26 
27 #include "android_internal.h"
28 #include "callbacks.h"
29 #include "label_internal.h"
30 #include "selinux_internal.h"
31 
selinux_android_context_with_level(const char * context,char ** newContext,uid_t userid,uid_t appid)32 int selinux_android_context_with_level(const char * context,
33 				       char ** newContext,
34 				       uid_t userid,
35 				       uid_t appid)
36 {
37 	int rc = -2;
38 
39 	enum levelFrom levelFrom;
40 	if (userid == (uid_t) -1) {
41 		levelFrom = (appid == (uid_t) -1) ? LEVELFROM_NONE : LEVELFROM_APP;
42 	} else {
43 		levelFrom = (appid == (uid_t) -1) ? LEVELFROM_USER : LEVELFROM_ALL;
44 	}
45 
46 	context_t ctx = context_new(context);
47 	if (!ctx) {
48 		goto out;
49 	}
50 
51 	int res = set_range_from_level(ctx, levelFrom, userid, appid);
52 	if (res != 0) {
53 		rc = res;
54 		goto out;
55 	}
56 
57 	const char * newString = context_str(ctx);
58 	if (!newString) {
59 		goto out;
60 	}
61 
62 	char * newCopied = strdup(newString);
63 	if (!newCopied) {
64 		goto out;
65 	}
66 
67 	*newContext = newCopied;
68 	rc = 0;
69 
70 out:
71 	context_free(ctx);
72 	return rc;
73 }
74 
selinux_android_setcon(const char * con)75 int selinux_android_setcon(const char *con)
76 {
77 	int ret = setcon(con);
78 	if (ret)
79 		return ret;
80 	/*
81 	  System properties must be reinitialized after setcon() otherwise the
82 	  previous property files will be leaked since mmap()'ed regions are not
83 	  closed as a result of setcon().
84 	*/
85 	return __system_properties_init();
86 }
87 
selinux_android_setcontext(uid_t uid,bool isSystemServer,const char * seinfo,const char * pkgname)88 int selinux_android_setcontext(uid_t uid,
89 			       bool isSystemServer,
90 			       const char *seinfo,
91 			       const char *pkgname)
92 {
93 	char *orig_ctx_str = NULL;
94 	const char *ctx_str = NULL;
95 	context_t ctx = NULL;
96 	int rc = -1;
97 
98 	if (is_selinux_enabled() <= 0)
99 		return 0;
100 
101 	rc = getcon(&orig_ctx_str);
102 	if (rc)
103 		goto err;
104 
105 	ctx = context_new(orig_ctx_str);
106 	if (!ctx)
107 		goto oom;
108 
109 	rc = seapp_context_lookup(SEAPP_DOMAIN, uid, isSystemServer, seinfo, pkgname, ctx);
110 	if (rc == -1)
111 		goto err;
112 	else if (rc == -2)
113 		goto oom;
114 
115 	ctx_str = context_str(ctx);
116 	if (!ctx_str)
117 		goto oom;
118 
119 	rc = security_check_context(ctx_str);
120 	if (rc < 0)
121 		goto err;
122 
123 	if (strcmp(ctx_str, orig_ctx_str)) {
124 		rc = selinux_android_setcon(ctx_str);
125 		if (rc < 0)
126 			goto err;
127 	}
128 
129 	rc = 0;
130 out:
131 	freecon(orig_ctx_str);
132 	context_free(ctx);
133 	return rc;
134 err:
135 	if (isSystemServer)
136 		selinux_log(SELINUX_ERROR,
137 				"%s:  Error setting context for system server: %s\n",
138 				__FUNCTION__, strerror(errno));
139 	else
140 		selinux_log(SELINUX_ERROR,
141 				"%s:  Error setting context for app with uid %d, seinfo %s: %s\n",
142 				__FUNCTION__, uid, seinfo, strerror(errno));
143 
144 	rc = -1;
145 	goto out;
146 oom:
147 	selinux_log(SELINUX_ERROR, "%s:  Out of memory\n", __FUNCTION__);
148 	rc = -1;
149 	goto out;
150 }
151 
152 static struct selabel_handle *fc_sehandle = NULL;
153 
file_context_init(void)154 static void file_context_init(void)
155 {
156 	if (!fc_sehandle)
157 		fc_sehandle = selinux_android_file_context_handle();
158 }
159 
160 static pthread_once_t fc_once = PTHREAD_ONCE_INIT;
161 
162 #define PKGTAB_SIZE 256
163 /* Hash table for pkg_info. It uses the package name as key. In case of
164  * collision, the next entry is the private_data attribute */
165 static struct pkg_info *pkgTab[PKGTAB_SIZE];
166 
167 /* Returns a hash based on the package name */
pkghash(const char * pkgname)168 static unsigned int pkghash(const char *pkgname)
169 {
170 	unsigned int h = 7;
171 	for (; *pkgname; pkgname++) {
172 		h = h * 31 + *pkgname;
173 	}
174 	return h & (PKGTAB_SIZE - 1);
175 }
176 
177 /* Adds the pkg_info entry to the hash table */
pkg_parse_callback(pkg_info * info,void * userdata)178 static bool pkg_parse_callback(pkg_info *info, void *userdata) {
179 
180 	(void) userdata;
181 
182 	unsigned int hash = pkghash(info->name);
183 	if (pkgTab[hash])
184 		/* Collision. Prepend the entry. */
185 		info->private_data = pkgTab[hash];
186 	pkgTab[hash] = info;
187 	return true;
188 }
189 
190 /* Initialize the pkg_info hash table */
package_info_init(void)191 static void package_info_init(void)
192 {
193 
194 	bool rc = packagelist_parse(pkg_parse_callback, NULL);
195 	if (!rc) {
196 		selinux_log(SELINUX_ERROR, "SELinux: Could NOT parse package list\n");
197 		return;
198 	}
199 
200 #if DEBUG
201 	{
202 		unsigned int hash, buckets, entries, chainlen, longestchain;
203 		struct pkg_info *info = NULL;
204 
205 		buckets = entries = longestchain = 0;
206 		for (hash = 0; hash < PKGTAB_SIZE; hash++) {
207 			if (pkgTab[hash]) {
208 				buckets++;
209 				chainlen = 0;
210 				for (info = pkgTab[hash]; info; info = (pkg_info *)info->private_data) {
211 					chainlen++;
212 					selinux_log(SELINUX_INFO, "%s:	name=%s uid=%u debuggable=%s dataDir=%s seinfo=%s\n",
213 								__FUNCTION__,
214 								info->name, info->uid, info->debuggable ? "true" : "false", info->data_dir, info->seinfo);
215 				}
216 				entries += chainlen;
217 				if (longestchain < chainlen)
218 					longestchain = chainlen;
219 			}
220 		}
221 		selinux_log(SELINUX_INFO, "SELinux:  %d pkg entries and %d/%d buckets used, longest chain %d\n", entries, buckets, PKGTAB_SIZE, longestchain);
222 	}
223 #endif
224 
225 }
226 
227 static pthread_once_t pkg_once = PTHREAD_ONCE_INIT;
228 
229 /* Returns the pkg_info for a package with a specific name */
package_info_lookup(const char * name)230 struct pkg_info *package_info_lookup(const char *name)
231 {
232 	struct pkg_info *info;
233 	unsigned int hash;
234 
235 	__selinux_once(pkg_once, package_info_init);
236 
237 	hash = pkghash(name);
238 	for (info = pkgTab[hash]; info; info = (pkg_info *)info->private_data) {
239 		if (!strcmp(name, info->name))
240 			return info;
241 	}
242 	return NULL;
243 }
244 
245 #define USER_PROFILE_PATH "/data/misc/profiles/cur/*"
246 
pkgdir_selabel_lookup(const char * pathname,const char * seinfo,uid_t uid,char ** secontextp)247 static int pkgdir_selabel_lookup(const char *pathname,
248 				 const char *seinfo,
249 				 uid_t uid,
250 				 char **secontextp)
251 {
252 	char *pkgname = NULL;
253 	struct pkg_info *info = NULL;
254 	const char *orig_ctx_str = *secontextp;
255 	const char *ctx_str = NULL;
256 	context_t ctx = NULL;
257 	int rc = 0;
258 	unsigned int userid_from_path = 0;
259 
260 	rc = extract_pkgname_and_userid(pathname, &pkgname, &userid_from_path);
261 	if (rc) {
262 		/* Invalid path, we skip it */
263 		if (rc == -1) {
264 			return 0;
265 		}
266 		return rc;
267 	}
268 
269 	if (!seinfo) {
270 		info = package_info_lookup(pkgname);
271 		if (!info) {
272 			selinux_log(SELINUX_WARNING, "SELinux:	Could not look up information for package %s, cannot restorecon %s.\n",
273 						pkgname, pathname);
274 			free(pkgname);
275 			return -1;
276 		}
277 		// info->uid only contains the appid and not the userid.
278 		info->uid += userid_from_path * AID_USER_OFFSET;
279 	}
280 
281 	ctx = context_new(orig_ctx_str);
282 	if (!ctx)
283 		goto err;
284 
285 	rc = seapp_context_lookup(SEAPP_TYPE, info ? info->uid : uid, 0,
286 				  info ? info->seinfo : seinfo, info ? info->name : pkgname, ctx);
287 	if (rc < 0)
288 		goto err;
289 
290 	ctx_str = context_str(ctx);
291 	if (!ctx_str)
292 		goto err;
293 
294 	if (!strcmp(ctx_str, orig_ctx_str))
295 		goto out;
296 
297 	rc = security_check_context(ctx_str);
298 	if (rc < 0)
299 		goto err;
300 
301 	freecon(*secontextp);
302 	*secontextp = strdup(ctx_str);
303 	if (!(*secontextp))
304 		goto err;
305 
306 	rc = 0;
307 
308 out:
309 	free(pkgname);
310 	context_free(ctx);
311 	return rc;
312 err:
313 	selinux_log(SELINUX_ERROR, "%s:  Error looking up context for path %s, pkgname %s, seinfo %s, uid %u: %s\n",
314 				__FUNCTION__, pathname, pkgname, info ? info->seinfo : seinfo,
315 				info ? info->uid : uid, strerror(errno));
316 	rc = -1;
317 	goto out;
318 }
319 
320 #define RESTORECON_PARTIAL_MATCH_DIGEST  "security.sehash"
321 
restorecon_sb(const char * pathname,const struct stat * sb,bool nochange,bool verbose,const char * seinfo,uid_t uid)322 static int restorecon_sb(const char *pathname,
323 			 const struct stat *sb,
324 			 bool nochange,
325 			 bool verbose,
326 			 const char *seinfo,
327 			 uid_t uid)
328 {
329 	char *secontext = NULL;
330 	char *oldsecontext = NULL;
331 	int rc = 0;
332 
333 	if (selabel_lookup(fc_sehandle, &secontext, pathname, sb->st_mode) < 0)
334 		return 0;  /* no match, but not an error */
335 
336 	if (lgetfilecon(pathname, &oldsecontext) < 0)
337 		goto err;
338 
339 	/*
340 	 * For subdirectories of /data/data or /data/user, we ignore selabel_lookup()
341 	 * and use pkgdir_selabel_lookup() instead. Files within those directories
342 	 * have different labeling rules, based off of /seapp_contexts, and
343 	 * installd is responsible for managing these labels instead of init.
344 	 */
345 	if (is_app_data_path(pathname)) {
346 		if (pkgdir_selabel_lookup(pathname, seinfo, uid, &secontext) < 0)
347 			goto err;
348 	}
349 
350 	if (strcmp(oldsecontext, secontext) != 0) {
351 		if (verbose)
352 			selinux_log(SELINUX_INFO,
353 						"SELinux:  Relabeling %s from %s to %s.\n", pathname, oldsecontext, secontext);
354 		if (!nochange) {
355 			if (lsetfilecon(pathname, secontext) < 0)
356 				goto err;
357 		}
358 	}
359 
360 	rc = 0;
361 
362 out:
363 	freecon(oldsecontext);
364 	freecon(secontext);
365 	return rc;
366 
367 err:
368 	selinux_log(SELINUX_ERROR,
369 				"SELinux: Could not set context for %s:  %s\n",
370 				pathname, strerror(errno));
371 	rc = -1;
372 	goto out;
373 }
374 
375 #define SYS_PATH "/sys"
376 #define SYS_PREFIX SYS_PATH "/"
377 
378 struct dir_hash_node {
379 	char* path;
380 	uint8_t digest[SHA1_HASH_SIZE];
381 	struct dir_hash_node *next;
382 };
383 
384 // Returns true if the digest of all partial matched contexts is the same as the one
385 // saved by setxattr. Otherwise returns false and constructs a dir_hash_node with the
386 // newly calculated digest.
check_context_match_for_dir(const char * pathname,struct dir_hash_node ** new_node,bool force,int error)387 static bool check_context_match_for_dir(const char *pathname,
388 					struct dir_hash_node **new_node,
389 					bool force, int error)
390 {
391 	uint8_t read_digest[SHA1_HASH_SIZE];
392 	ssize_t read_size = getxattr(pathname, RESTORECON_PARTIAL_MATCH_DIGEST,
393 					 read_digest, SHA1_HASH_SIZE);
394 	uint8_t calculated_digest[SHA1_HASH_SIZE];
395 	bool status = selabel_hash_all_partial_matches(fc_sehandle, pathname,
396 						       calculated_digest);
397 
398 	if (!new_node) {
399 		return false;
400 	}
401 	*new_node = NULL;
402 	if (!force && status && read_size == SHA1_HASH_SIZE &&
403 		memcmp(read_digest, calculated_digest, SHA1_HASH_SIZE) == 0) {
404 		return true;
405 	}
406 
407 	// Save the digest of all matched contexts for the current directory.
408 	if (!error && status) {
409 		*new_node = calloc(1, sizeof(struct dir_hash_node));
410 		if (*new_node == NULL) {
411 			selinux_log(SELINUX_ERROR,
412 						"SELinux: %s: Out of memory\n", __func__);
413 			return false;
414 		}
415 
416 		(*new_node)->path = strdup(pathname);
417 		if ((*new_node)->path == NULL) {
418 			selinux_log(SELINUX_ERROR,
419 						"SELinux: %s: Out of memory\n", __func__);
420 			free(*new_node);
421 			*new_node = NULL;
422 			return false;
423 		}
424 		memcpy((*new_node)->digest, calculated_digest, SHA1_HASH_SIZE);
425 		(*new_node)->next = NULL;
426 	}
427 
428 	return false;
429 }
430 
selinux_android_restorecon_common(const char * pathname_orig,const char * seinfo,uid_t uid,unsigned int flags)431 static int selinux_android_restorecon_common(const char* pathname_orig,
432 					     const char *seinfo,
433 					     uid_t uid,
434 					     unsigned int flags)
435 {
436 	bool nochange = (flags & SELINUX_ANDROID_RESTORECON_NOCHANGE) ? true : false;
437 	bool verbose = (flags & SELINUX_ANDROID_RESTORECON_VERBOSE) ? true : false;
438 	bool recurse = (flags & SELINUX_ANDROID_RESTORECON_RECURSE) ? true : false;
439 	bool force = (flags & SELINUX_ANDROID_RESTORECON_FORCE) ? true : false;
440 	bool datadata = (flags & SELINUX_ANDROID_RESTORECON_DATADATA) ? true : false;
441 	bool skipce = (flags & SELINUX_ANDROID_RESTORECON_SKIPCE) ? true : false;
442 	bool cross_filesystems = (flags & SELINUX_ANDROID_RESTORECON_CROSS_FILESYSTEMS) ? true : false;
443 	bool setrestoreconlast = (flags & SELINUX_ANDROID_RESTORECON_SKIP_SEHASH) ? false : true;
444 	bool issys;
445 	struct stat sb;
446 	struct statfs sfsb;
447 	FTS *fts;
448 	FTSENT *ftsent;
449 	char *pathname = NULL, *pathdnamer = NULL, *pathdname, *pathbname;
450 	char * paths[2] = { NULL , NULL };
451 	int ftsflags = FTS_NOCHDIR | FTS_PHYSICAL;
452 	int error, sverrno;
453 	struct dir_hash_node *current = NULL;
454 	struct dir_hash_node *head = NULL;
455 
456 	if (!cross_filesystems) {
457 		ftsflags |= FTS_XDEV;
458 	}
459 
460 	if (is_selinux_enabled() <= 0) {
461 		selinux_log(SELINUX_WARNING, "SELinux: SELinux is disabled, skipping restorecon");
462 		return 0;
463 	}
464 
465 	__selinux_once(fc_once, file_context_init);
466 
467 	if (!fc_sehandle)
468 		return 0;
469 
470 	/*
471 	 * Convert passed-in pathname to canonical pathname by resolving realpath of
472 	 * containing dir, then appending last component name.
473 	 */
474 	pathbname = basename(pathname_orig);
475 	if (!strcmp(pathbname, "/") || !strcmp(pathbname, ".") || !strcmp(pathbname, "..")) {
476 		pathname = realpath(pathname_orig, NULL);
477 		if (!pathname)
478 			goto realpatherr;
479 	} else {
480 		pathdname = dirname(pathname_orig);
481 		pathdnamer = realpath(pathdname, NULL);
482 		if (!pathdnamer)
483 			goto realpatherr;
484 		if (!strcmp(pathdnamer, "/"))
485 			error = asprintf(&pathname, "/%s", pathbname);
486 		else
487 			error = asprintf(&pathname, "%s/%s", pathdnamer, pathbname);
488 		if (error < 0)
489 			goto oom;
490 	}
491 
492 	paths[0] = pathname;
493 	issys = (!strcmp(pathname, SYS_PATH)
494 			|| !strncmp(pathname, SYS_PREFIX, sizeof(SYS_PREFIX)-1)) ? true : false;
495 
496 	if (!recurse) {
497 		if (lstat(pathname, &sb) < 0) {
498 			error = -1;
499 			goto cleanup;
500 		}
501 
502 		error = restorecon_sb(pathname, &sb, nochange, verbose, seinfo, uid);
503 		goto cleanup;
504 	}
505 
506 	/*
507 	 * Ignore saved partial match digest on /data/data or /data/user
508 	 * since their labeling is based on seapp_contexts and seinfo
509 	 * assignments rather than file_contexts and is managed by
510 	 * installd rather than init.
511 	 */
512 	if (is_app_data_path(pathname))
513 		setrestoreconlast = false;
514 
515 	/* Also ignore on /sys since it is regenerated on each boot regardless. */
516 	if (issys)
517 		setrestoreconlast = false;
518 
519 	/* Ignore files on in-memory filesystems */
520 	if (statfs(pathname, &sfsb) == 0) {
521 		if (sfsb.f_type == RAMFS_MAGIC || sfsb.f_type == TMPFS_MAGIC)
522 			setrestoreconlast = false;
523 	}
524 
525 	fts = fts_open(paths, ftsflags, NULL);
526 	if (!fts) {
527 		error = -1;
528 		goto cleanup;
529 	}
530 
531 	error = 0;
532 	while ((ftsent = fts_read(fts)) != NULL) {
533 		switch (ftsent->fts_info) {
534 		case FTS_DC:
535 			selinux_log(SELINUX_ERROR,
536 						"SELinux:  Directory cycle on %s.\n", ftsent->fts_path);
537 			errno = ELOOP;
538 			error = -1;
539 			goto out;
540 		case FTS_DP:
541 			continue;
542 		case FTS_DNR:
543 			selinux_log(SELINUX_ERROR,
544 						"SELinux:  Could not read %s: %s.\n", ftsent->fts_path, strerror(errno));
545 			fts_set(fts, ftsent, FTS_SKIP);
546 			continue;
547 		case FTS_NS:
548 			selinux_log(SELINUX_ERROR,
549 						"SELinux:  Could not stat %s: %s.\n", ftsent->fts_path, strerror(errno));
550 			fts_set(fts, ftsent, FTS_SKIP);
551 			continue;
552 		case FTS_ERR:
553 			selinux_log(SELINUX_ERROR,
554 						"SELinux:  Error on %s: %s.\n", ftsent->fts_path, strerror(errno));
555 			fts_set(fts, ftsent, FTS_SKIP);
556 			continue;
557 		case FTS_D:
558 			if (issys && !selabel_partial_match(fc_sehandle, ftsent->fts_path)) {
559 				fts_set(fts, ftsent, FTS_SKIP);
560 				continue;
561 			}
562 
563 			if (!datadata && !fnmatch(USER_PROFILE_PATH, ftsent->fts_path, FNM_PATHNAME)) {
564 				// Don't label this directory, vold takes care of that, but continue below it.
565 				continue;
566 			}
567 
568 			if (setrestoreconlast) {
569 				struct dir_hash_node* new_node = NULL;
570 				if (check_context_match_for_dir(ftsent->fts_path, &new_node, force, error)) {
571 					selinux_log(SELINUX_INFO,
572 								"SELinux: Skipping restorecon on directory(%s)\n",
573 								ftsent->fts_path);
574 					fts_set(fts, ftsent, FTS_SKIP);
575 					continue;
576 				}
577 				if (new_node) {
578 					if (!current) {
579 						current = new_node;
580 						head = current;
581 					} else {
582 						current->next = new_node;
583 						current = current->next;
584 					}
585 				}
586 			}
587 
588 			if (skipce && is_credential_encrypted_path(ftsent->fts_path)) {
589 				// Don't label anything below this directory.
590 				fts_set(fts, ftsent, FTS_SKIP);
591 				// but fall through and make sure we label the directory itself
592 			}
593 
594 			if (!datadata && is_app_data_path(ftsent->fts_path)) {
595 				// Don't label anything below this directory.
596 				fts_set(fts, ftsent, FTS_SKIP);
597 				// but fall through and make sure we label the directory itself
598 			}
599 			/* fall through */
600 		default:
601 			error |= restorecon_sb(ftsent->fts_path, ftsent->fts_statp, nochange, verbose, seinfo, uid);
602 			break;
603 		}
604 	}
605 
606 	// Labeling successful. Write the partial match digests for subdirectories.
607 	// TODO: Write the digest upon FTS_DP if no error occurs in its descents.
608 	if (setrestoreconlast && !nochange && !error) {
609 		current = head;
610 		while (current != NULL) {
611 			if (setxattr(current->path, RESTORECON_PARTIAL_MATCH_DIGEST, current->digest,
612 					SHA1_HASH_SIZE, 0) < 0) {
613 				selinux_log(SELINUX_ERROR,
614 							"SELinux:  setxattr failed: %s:  %s\n",
615 							current->path,
616 							strerror(errno));
617 			}
618 			current = current->next;
619 		}
620 	}
621 
622 out:
623 	sverrno = errno;
624 	(void) fts_close(fts);
625 	errno = sverrno;
626 cleanup:
627 	free(pathdnamer);
628 	free(pathname);
629 	current = head;
630 	while (current != NULL) {
631 		struct dir_hash_node *next = current->next;
632 		free(current->path);
633 		free(current);
634 		current = next;
635 	}
636 	return error;
637 oom:
638 	sverrno = errno;
639 	selinux_log(SELINUX_ERROR, "%s:  Out of memory\n", __FUNCTION__);
640 	errno = sverrno;
641 	error = -1;
642 	goto cleanup;
643 realpatherr:
644 	sverrno = errno;
645 	selinux_log(SELINUX_ERROR, "SELinux: Could not get canonical path for %s restorecon: %s.\n",
646 			pathname_orig, strerror(errno));
647 	errno = sverrno;
648 	error = -1;
649 	goto cleanup;
650 }
651 
selinux_android_restorecon(const char * file,unsigned int flags)652 int selinux_android_restorecon(const char *file, unsigned int flags)
653 {
654 	return selinux_android_restorecon_common(file, NULL, -1, flags);
655 }
656 
selinux_android_restorecon_pkgdir(const char * pkgdir,const char * seinfo,uid_t uid,unsigned int flags)657 int selinux_android_restorecon_pkgdir(const char *pkgdir,
658 				      const char *seinfo,
659 				      uid_t uid,
660 				      unsigned int flags)
661 {
662 	return selinux_android_restorecon_common(pkgdir, seinfo, uid, flags | SELINUX_ANDROID_RESTORECON_DATADATA);
663 }
664 
665 
selinux_android_set_sehandle(const struct selabel_handle * hndl)666 void selinux_android_set_sehandle(const struct selabel_handle *hndl)
667 {
668 	fc_sehandle = (struct selabel_handle *) hndl;
669 }
670 
selinux_android_load_policy()671 int selinux_android_load_policy()
672 {
673 	selinux_log(SELINUX_ERROR, "selinux_android_load_policy is not implemented\n");
674 	return -1;
675 }
676 
selinux_android_load_policy_from_fd(int fd,const char * description)677 int selinux_android_load_policy_from_fd(int fd __attribute__((unused)), const char *description __attribute__((unused)))
678 {
679 	selinux_log(SELINUX_ERROR, "selinux_android_load_policy_from_fd is not implemented\n");
680 	return -1;
681 }
682