| #include <ctype.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <fnmatch.h> |
| #include <fts.h> |
| #include <libgen.h> |
| #include <limits.h> |
| #include <linux/magic.h> |
| #include <stdbool.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <sys/vfs.h> |
| #include <sys/xattr.h> |
| #include <unistd.h> |
| |
| #include <log/log.h> |
| #include <packagelistparser/packagelistparser.h> |
| #include <private/android_filesystem_config.h> |
| #include <selinux/android.h> |
| #include <selinux/context.h> |
| #include <selinux/selinux.h> |
| |
| #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_ |
| #include <sys/_system_properties.h> |
| |
| #include "android_internal.h" |
| #include "callbacks.h" |
| #include "label_internal.h" |
| #include "selinux_internal.h" |
| |
| int selinux_android_context_with_level(const char * context, |
| char ** newContext, |
| uid_t userid, |
| uid_t appid) |
| { |
| int rc = -2; |
| |
| enum levelFrom levelFrom; |
| if (userid == (uid_t) -1) { |
| levelFrom = (appid == (uid_t) -1) ? LEVELFROM_NONE : LEVELFROM_APP; |
| } else { |
| levelFrom = (appid == (uid_t) -1) ? LEVELFROM_USER : LEVELFROM_ALL; |
| } |
| |
| context_t ctx = context_new(context); |
| if (!ctx) { |
| goto out; |
| } |
| |
| int res = set_range_from_level(ctx, levelFrom, userid, appid); |
| if (res != 0) { |
| rc = res; |
| goto out; |
| } |
| |
| const char * newString = context_str(ctx); |
| if (!newString) { |
| goto out; |
| } |
| |
| char * newCopied = strdup(newString); |
| if (!newCopied) { |
| goto out; |
| } |
| |
| *newContext = newCopied; |
| rc = 0; |
| |
| out: |
| context_free(ctx); |
| return rc; |
| } |
| |
| int selinux_android_setcon(const char *con) |
| { |
| int ret = setcon(con); |
| if (ret) |
| return ret; |
| /* |
| System properties must be reinitialized after setcon() otherwise the |
| previous property files will be leaked since mmap()'ed regions are not |
| closed as a result of setcon(). |
| */ |
| return __system_properties_init(); |
| } |
| |
| int selinux_android_setcontext(uid_t uid, |
| bool isSystemServer, |
| const char *seinfo, |
| const char *pkgname) |
| { |
| char *orig_ctx_str = NULL; |
| const char *ctx_str = NULL; |
| context_t ctx = NULL; |
| int rc = -1; |
| |
| if (is_selinux_enabled() <= 0) |
| return 0; |
| |
| rc = getcon(&orig_ctx_str); |
| if (rc) |
| goto err; |
| |
| ctx = context_new(orig_ctx_str); |
| if (!ctx) |
| goto oom; |
| |
| rc = seapp_context_lookup(SEAPP_DOMAIN, uid, isSystemServer, seinfo, pkgname, ctx); |
| if (rc == -1) |
| goto err; |
| else if (rc == -2) |
| goto oom; |
| |
| ctx_str = context_str(ctx); |
| if (!ctx_str) |
| goto oom; |
| |
| rc = security_check_context(ctx_str); |
| if (rc < 0) |
| goto err; |
| |
| if (strcmp(ctx_str, orig_ctx_str)) { |
| rc = selinux_android_setcon(ctx_str); |
| if (rc < 0) |
| goto err; |
| } |
| |
| rc = 0; |
| out: |
| freecon(orig_ctx_str); |
| context_free(ctx); |
| return rc; |
| err: |
| if (isSystemServer) |
| selinux_log(SELINUX_ERROR, |
| "%s: Error setting context for system server: %s\n", |
| __FUNCTION__, strerror(errno)); |
| else |
| selinux_log(SELINUX_ERROR, |
| "%s: Error setting context for app with uid %d, seinfo %s: %s\n", |
| __FUNCTION__, uid, seinfo, strerror(errno)); |
| |
| rc = -1; |
| goto out; |
| oom: |
| selinux_log(SELINUX_ERROR, "%s: Out of memory\n", __FUNCTION__); |
| rc = -1; |
| goto out; |
| } |
| |
| static struct selabel_handle *fc_sehandle = NULL; |
| |
| static void file_context_init(void) |
| { |
| if (!fc_sehandle) |
| fc_sehandle = selinux_android_file_context_handle(); |
| } |
| |
| static pthread_once_t fc_once = PTHREAD_ONCE_INIT; |
| |
| #define PKGTAB_SIZE 256 |
| /* Hash table for pkg_info. It uses the package name as key. In case of |
| * collision, the next entry is the private_data attribute */ |
| static struct pkg_info *pkgTab[PKGTAB_SIZE]; |
| |
| /* Returns a hash based on the package name */ |
| static unsigned int pkghash(const char *pkgname) |
| { |
| unsigned int h = 7; |
| for (; *pkgname; pkgname++) { |
| h = h * 31 + *pkgname; |
| } |
| return h & (PKGTAB_SIZE - 1); |
| } |
| |
| /* Adds the pkg_info entry to the hash table */ |
| static bool pkg_parse_callback(pkg_info *info, void *userdata) { |
| |
| (void) userdata; |
| |
| unsigned int hash = pkghash(info->name); |
| if (pkgTab[hash]) |
| /* Collision. Prepend the entry. */ |
| info->private_data = pkgTab[hash]; |
| pkgTab[hash] = info; |
| return true; |
| } |
| |
| /* Initialize the pkg_info hash table */ |
| static void package_info_init(void) |
| { |
| |
| bool rc = packagelist_parse(pkg_parse_callback, NULL); |
| if (!rc) { |
| selinux_log(SELINUX_ERROR, "SELinux: Could NOT parse package list\n"); |
| return; |
| } |
| |
| #if DEBUG |
| { |
| unsigned int hash, buckets, entries, chainlen, longestchain; |
| struct pkg_info *info = NULL; |
| |
| buckets = entries = longestchain = 0; |
| for (hash = 0; hash < PKGTAB_SIZE; hash++) { |
| if (pkgTab[hash]) { |
| buckets++; |
| chainlen = 0; |
| for (info = pkgTab[hash]; info; info = (pkg_info *)info->private_data) { |
| chainlen++; |
| selinux_log(SELINUX_INFO, "%s: name=%s uid=%u debuggable=%s dataDir=%s seinfo=%s\n", |
| __FUNCTION__, |
| info->name, info->uid, info->debuggable ? "true" : "false", info->data_dir, info->seinfo); |
| } |
| entries += chainlen; |
| if (longestchain < chainlen) |
| longestchain = chainlen; |
| } |
| } |
| selinux_log(SELINUX_INFO, "SELinux: %d pkg entries and %d/%d buckets used, longest chain %d\n", entries, buckets, PKGTAB_SIZE, longestchain); |
| } |
| #endif |
| |
| } |
| |
| static pthread_once_t pkg_once = PTHREAD_ONCE_INIT; |
| |
| /* Returns the pkg_info for a package with a specific name */ |
| struct pkg_info *package_info_lookup(const char *name) |
| { |
| struct pkg_info *info; |
| unsigned int hash; |
| |
| __selinux_once(pkg_once, package_info_init); |
| |
| hash = pkghash(name); |
| for (info = pkgTab[hash]; info; info = (pkg_info *)info->private_data) { |
| if (!strcmp(name, info->name)) |
| return info; |
| } |
| return NULL; |
| } |
| |
| /* The contents of these paths are encrypted on FBE devices until user |
| * credentials are presented (filenames inside are mangled), so we need |
| * to delay restorecon of those until vold explicitly requests it. */ |
| // NOTE: these paths need to be kept in sync with vold |
| #define DATA_SYSTEM_CE_PATH "/data/system_ce" |
| #define DATA_VENDOR_CE_PATH "/data/vendor_ce" |
| #define DATA_MISC_CE_PATH "/data/misc_ce" |
| #define DATA_MISC_DE_PATH "/data/misc_de" |
| |
| /* The path prefixes of package data directories. */ |
| #define DATA_DATA_PATH "/data/data" |
| #define DATA_USER_PATH "/data/user" |
| #define DATA_USER_DE_PATH "/data/user_de" |
| #define USER_PROFILE_PATH "/data/misc/profiles/cur/*" |
| #define SDK_SANDBOX_DATA_CE_PATH "/data/misc_ce/*/sdksandbox" |
| #define SDK_SANDBOX_DATA_DE_PATH "/data/misc_de/*/sdksandbox" |
| |
| #define EXPAND_MNT_PATH "/mnt/expand/\?\?\?\?\?\?\?\?-\?\?\?\?-\?\?\?\?-\?\?\?\?-\?\?\?\?\?\?\?\?\?\?\?\?" |
| #define EXPAND_USER_PATH EXPAND_MNT_PATH "/user" |
| #define EXPAND_USER_DE_PATH EXPAND_MNT_PATH "/user_de" |
| #define EXPAND_SDK_CE_PATH EXPAND_MNT_PATH "/misc_ce/*/sdksandbox" |
| #define EXPAND_SDK_DE_PATH EXPAND_MNT_PATH "/misc_de/*/sdksandbox" |
| |
| #define DATA_DATA_PREFIX DATA_DATA_PATH "/" |
| #define DATA_USER_PREFIX DATA_USER_PATH "/" |
| #define DATA_USER_DE_PREFIX DATA_USER_DE_PATH "/" |
| #define DATA_MISC_CE_PREFIX DATA_MISC_CE_PATH "/" |
| #define DATA_MISC_DE_PREFIX DATA_MISC_DE_PATH "/" |
| #define EXPAND_MNT_PATH_PREFIX EXPAND_MNT_PATH "/" |
| |
| /* |
| * This method helps in identifying paths that refer to users' app data. Labeling for app data is |
| * based on seapp_contexts and seinfo assignments rather than file_contexts and is managed by |
| * installd rather than by init. |
| */ |
| static bool is_app_data_path(const char *pathname) { |
| int flags = FNM_LEADING_DIR|FNM_PATHNAME; |
| return (!strncmp(pathname, DATA_DATA_PREFIX, sizeof(DATA_DATA_PREFIX)-1) || |
| !strncmp(pathname, DATA_USER_PREFIX, sizeof(DATA_USER_PREFIX)-1) || |
| !strncmp(pathname, DATA_USER_DE_PREFIX, sizeof(DATA_USER_DE_PREFIX)-1) || |
| !fnmatch(EXPAND_USER_PATH, pathname, flags) || |
| !fnmatch(EXPAND_USER_DE_PATH, pathname, flags) || |
| !fnmatch(SDK_SANDBOX_DATA_CE_PATH, pathname, flags) || |
| !fnmatch(SDK_SANDBOX_DATA_DE_PATH, pathname, flags) || |
| !fnmatch(EXPAND_SDK_CE_PATH, pathname, flags) || |
| !fnmatch(EXPAND_SDK_DE_PATH, pathname, flags)); |
| } |
| |
| /* |
| * Extract the userid from a path. |
| * On success, pathname is updated past the userid. |
| * Returns 0 on success, -1 on error |
| */ |
| static int extract_userid(const char **pathname, unsigned int *userid) |
| { |
| char *end = NULL; |
| |
| errno = 0; |
| *userid = strtoul(*pathname, &end, 10); |
| if (errno) { |
| selinux_log(SELINUX_ERROR, "SELinux: Could not parse userid %s: %s.\n", |
| *pathname, strerror(errno)); |
| return -1; |
| } |
| if (*pathname == end) { |
| return -1; |
| } |
| if (*userid > 1000) { |
| return -1; |
| } |
| *pathname = end; |
| return 0; |
| } |
| |
| /* Extract the pkgname and userid from a path. |
| * On success, the caller is responsible for free'ing pkgname. |
| * Returns 0 on success, -1 on invalid path, -2 on error. |
| */ |
| static int extract_pkgname_and_userid(const char *pathname, char **pkgname, unsigned int *userid) |
| { |
| char *end = NULL; |
| |
| if (pkgname == NULL || *pkgname != NULL || userid == NULL) { |
| errno = EINVAL; |
| return -2; |
| } |
| |
| /* Skip directory prefix before package name. */ |
| if (!strncmp(pathname, DATA_DATA_PREFIX, sizeof(DATA_DATA_PREFIX)-1)) { |
| pathname += sizeof(DATA_DATA_PREFIX) - 1; |
| } else if (!strncmp(pathname, DATA_USER_PREFIX, sizeof(DATA_USER_PREFIX)-1)) { |
| pathname += sizeof(DATA_USER_PREFIX) - 1; |
| int rc = extract_userid(&pathname, userid); |
| if (rc) |
| return -1; |
| if (*pathname == '/') |
| pathname++; |
| else |
| return -1; |
| } else if (!strncmp(pathname, DATA_USER_DE_PREFIX, sizeof(DATA_USER_DE_PREFIX)-1)) { |
| pathname += sizeof(DATA_USER_DE_PREFIX) - 1; |
| int rc = extract_userid(&pathname, userid); |
| if (rc) |
| return -1; |
| if (*pathname == '/') |
| pathname++; |
| else |
| return -1; |
| } else if (!fnmatch(EXPAND_USER_PATH, pathname, FNM_LEADING_DIR|FNM_PATHNAME)) { |
| pathname += sizeof(EXPAND_USER_PATH); |
| int rc = extract_userid(&pathname, userid); |
| if (rc) |
| return -1; |
| if (*pathname == '/') |
| pathname++; |
| else |
| return -1; |
| } else if (!fnmatch(EXPAND_USER_DE_PATH, pathname, FNM_LEADING_DIR|FNM_PATHNAME)) { |
| pathname += sizeof(EXPAND_USER_DE_PATH); |
| int rc = extract_userid(&pathname, userid); |
| if (rc) |
| return -1; |
| if (*pathname == '/') |
| pathname++; |
| else |
| return -1; |
| } else if (!strncmp(pathname, DATA_MISC_CE_PREFIX, sizeof(DATA_MISC_CE_PREFIX)-1)) { |
| pathname += sizeof(DATA_MISC_CE_PREFIX) - 1; |
| int rc = extract_userid(&pathname, userid); |
| if (rc) |
| return -1; |
| if (!strncmp(pathname, "/sdksandbox/", sizeof("/sdksandbox/")-1)) |
| pathname += sizeof("/sdksandbox/") - 1; |
| else |
| return -1; |
| } else if (!strncmp(pathname, DATA_MISC_DE_PREFIX, sizeof(DATA_MISC_DE_PREFIX)-1)) { |
| pathname += sizeof(DATA_MISC_DE_PREFIX) - 1; |
| int rc = extract_userid(&pathname, userid); |
| if (rc) |
| return -1; |
| if (!strncmp(pathname, "/sdksandbox/", sizeof("/sdksandbox/")-1)) |
| pathname += sizeof("/sdksandbox/") - 1; |
| else |
| return -1; |
| } else if (!fnmatch(EXPAND_SDK_CE_PATH, pathname, FNM_LEADING_DIR|FNM_PATHNAME)) { |
| pathname += sizeof(EXPAND_MNT_PATH_PREFIX) - 1; |
| pathname += sizeof("misc_ce/") - 1; |
| int rc = extract_userid(&pathname, userid); |
| if (rc) |
| return -1; |
| if (!strncmp(pathname, "/sdksandbox/", sizeof("/sdksandbox/")-1)) |
| pathname += sizeof("/sdksandbox/") - 1; |
| else |
| return -1; |
| } else if (!fnmatch(EXPAND_SDK_DE_PATH, pathname, FNM_LEADING_DIR|FNM_PATHNAME)) { |
| pathname += sizeof(EXPAND_MNT_PATH_PREFIX) - 1; |
| pathname += sizeof("misc_de/") - 1; |
| int rc = extract_userid(&pathname, userid); |
| if (rc) |
| return -1; |
| if (!strncmp(pathname, "/sdksandbox/", sizeof("/sdksandbox/")-1)) |
| pathname += sizeof("/sdksandbox/") - 1; |
| else |
| return -1; |
| } else |
| return -1; |
| |
| if (!(*pathname)) |
| return -1; |
| |
| *pkgname = strdup(pathname); |
| if (!(*pkgname)) |
| return -2; |
| |
| // Trim pkgname. |
| for (end = *pkgname; *end && *end != '/'; end++); |
| *end = '\0'; |
| |
| return 0; |
| } |
| |
| static int pkgdir_selabel_lookup(const char *pathname, |
| const char *seinfo, |
| uid_t uid, |
| char **secontextp) |
| { |
| char *pkgname = NULL; |
| struct pkg_info *info = NULL; |
| const char *orig_ctx_str = *secontextp; |
| const char *ctx_str = NULL; |
| context_t ctx = NULL; |
| int rc = 0; |
| unsigned int userid_from_path = 0; |
| |
| rc = extract_pkgname_and_userid(pathname, &pkgname, &userid_from_path); |
| if (rc) { |
| /* Invalid path, we skip it */ |
| if (rc == -1) { |
| return 0; |
| } |
| return rc; |
| } |
| |
| if (!seinfo) { |
| info = package_info_lookup(pkgname); |
| if (!info) { |
| selinux_log(SELINUX_WARNING, "SELinux: Could not look up information for package %s, cannot restorecon %s.\n", |
| pkgname, pathname); |
| free(pkgname); |
| return -1; |
| } |
| // info->uid only contains the appid and not the userid. |
| info->uid += userid_from_path * AID_USER_OFFSET; |
| } |
| |
| ctx = context_new(orig_ctx_str); |
| if (!ctx) |
| goto err; |
| |
| rc = seapp_context_lookup(SEAPP_TYPE, info ? info->uid : uid, 0, |
| info ? info->seinfo : seinfo, info ? info->name : pkgname, ctx); |
| if (rc < 0) |
| goto err; |
| |
| ctx_str = context_str(ctx); |
| if (!ctx_str) |
| goto err; |
| |
| if (!strcmp(ctx_str, orig_ctx_str)) |
| goto out; |
| |
| rc = security_check_context(ctx_str); |
| if (rc < 0) |
| goto err; |
| |
| freecon(*secontextp); |
| *secontextp = strdup(ctx_str); |
| if (!(*secontextp)) |
| goto err; |
| |
| rc = 0; |
| |
| out: |
| free(pkgname); |
| context_free(ctx); |
| return rc; |
| err: |
| selinux_log(SELINUX_ERROR, "%s: Error looking up context for path %s, pkgname %s, seinfo %s, uid %u: %s\n", |
| __FUNCTION__, pathname, pkgname, info ? info->seinfo : seinfo, |
| info ? info->uid : uid, strerror(errno)); |
| rc = -1; |
| goto out; |
| } |
| |
| #define RESTORECON_PARTIAL_MATCH_DIGEST "security.sehash" |
| |
| static int restorecon_sb(const char *pathname, const struct stat *sb, |
| bool nochange, bool verbose, |
| const char *seinfo, uid_t uid) |
| { |
| char *secontext = NULL; |
| char *oldsecontext = NULL; |
| int rc = 0; |
| |
| if (selabel_lookup(fc_sehandle, &secontext, pathname, sb->st_mode) < 0) |
| return 0; /* no match, but not an error */ |
| |
| if (lgetfilecon(pathname, &oldsecontext) < 0) |
| goto err; |
| |
| /* |
| * For subdirectories of /data/data or /data/user, we ignore selabel_lookup() |
| * and use pkgdir_selabel_lookup() instead. Files within those directories |
| * have different labeling rules, based off of /seapp_contexts, and |
| * installd is responsible for managing these labels instead of init. |
| */ |
| if (is_app_data_path(pathname)) { |
| if (pkgdir_selabel_lookup(pathname, seinfo, uid, &secontext) < 0) |
| goto err; |
| } |
| |
| if (strcmp(oldsecontext, secontext) != 0) { |
| if (verbose) |
| selinux_log(SELINUX_INFO, |
| "SELinux: Relabeling %s from %s to %s.\n", pathname, oldsecontext, secontext); |
| if (!nochange) { |
| if (lsetfilecon(pathname, secontext) < 0) |
| goto err; |
| } |
| } |
| |
| rc = 0; |
| |
| out: |
| freecon(oldsecontext); |
| freecon(secontext); |
| return rc; |
| |
| err: |
| selinux_log(SELINUX_ERROR, |
| "SELinux: Could not set context for %s: %s\n", |
| pathname, strerror(errno)); |
| rc = -1; |
| goto out; |
| } |
| |
| #define SYS_PATH "/sys" |
| #define SYS_PREFIX SYS_PATH "/" |
| |
| struct dir_hash_node { |
| char* path; |
| uint8_t digest[SHA1_HASH_SIZE]; |
| struct dir_hash_node *next; |
| }; |
| |
| // Returns true if the digest of all partial matched contexts is the same as the one |
| // saved by setxattr. Otherwise returns false and constructs a dir_hash_node with the |
| // newly calculated digest. |
| static bool check_context_match_for_dir(const char *pathname, struct dir_hash_node **new_node, |
| bool force, int error) { |
| uint8_t read_digest[SHA1_HASH_SIZE]; |
| ssize_t read_size = getxattr(pathname, RESTORECON_PARTIAL_MATCH_DIGEST, |
| read_digest, SHA1_HASH_SIZE); |
| uint8_t calculated_digest[SHA1_HASH_SIZE]; |
| bool status = selabel_hash_all_partial_matches(fc_sehandle, pathname, |
| calculated_digest); |
| |
| if (!new_node) { |
| return false; |
| } |
| *new_node = NULL; |
| if (!force && status && read_size == SHA1_HASH_SIZE && |
| memcmp(read_digest, calculated_digest, SHA1_HASH_SIZE) == 0) { |
| return true; |
| } |
| |
| // Save the digest of all matched contexts for the current directory. |
| if (!error && status) { |
| *new_node = calloc(1, sizeof(struct dir_hash_node)); |
| if (*new_node == NULL) { |
| selinux_log(SELINUX_ERROR, |
| "SELinux: %s: Out of memory\n", __func__); |
| return false; |
| } |
| |
| (*new_node)->path = strdup(pathname); |
| if ((*new_node)->path == NULL) { |
| selinux_log(SELINUX_ERROR, |
| "SELinux: %s: Out of memory\n", __func__); |
| free(*new_node); |
| *new_node = NULL; |
| return false; |
| } |
| memcpy((*new_node)->digest, calculated_digest, SHA1_HASH_SIZE); |
| (*new_node)->next = NULL; |
| } |
| |
| return false; |
| } |
| |
| static int selinux_android_restorecon_common(const char* pathname_orig, |
| const char *seinfo, |
| uid_t uid, |
| unsigned int flags) |
| { |
| bool nochange = (flags & SELINUX_ANDROID_RESTORECON_NOCHANGE) ? true : false; |
| bool verbose = (flags & SELINUX_ANDROID_RESTORECON_VERBOSE) ? true : false; |
| bool recurse = (flags & SELINUX_ANDROID_RESTORECON_RECURSE) ? true : false; |
| bool force = (flags & SELINUX_ANDROID_RESTORECON_FORCE) ? true : false; |
| bool datadata = (flags & SELINUX_ANDROID_RESTORECON_DATADATA) ? true : false; |
| bool skipce = (flags & SELINUX_ANDROID_RESTORECON_SKIPCE) ? true : false; |
| bool cross_filesystems = (flags & SELINUX_ANDROID_RESTORECON_CROSS_FILESYSTEMS) ? true : false; |
| bool setrestoreconlast = (flags & SELINUX_ANDROID_RESTORECON_SKIP_SEHASH) ? false : true; |
| bool issys; |
| struct stat sb; |
| struct statfs sfsb; |
| FTS *fts; |
| FTSENT *ftsent; |
| char *pathname = NULL, *pathdnamer = NULL, *pathdname, *pathbname; |
| char * paths[2] = { NULL , NULL }; |
| int ftsflags = FTS_NOCHDIR | FTS_PHYSICAL; |
| int error, sverrno; |
| struct dir_hash_node *current = NULL; |
| struct dir_hash_node *head = NULL; |
| |
| if (!cross_filesystems) { |
| ftsflags |= FTS_XDEV; |
| } |
| |
| if (is_selinux_enabled() <= 0) |
| return 0; |
| |
| __selinux_once(fc_once, file_context_init); |
| |
| if (!fc_sehandle) |
| return 0; |
| |
| /* |
| * Convert passed-in pathname to canonical pathname by resolving realpath of |
| * containing dir, then appending last component name. |
| */ |
| pathbname = basename(pathname_orig); |
| if (!strcmp(pathbname, "/") || !strcmp(pathbname, ".") || !strcmp(pathbname, "..")) { |
| pathname = realpath(pathname_orig, NULL); |
| if (!pathname) |
| goto realpatherr; |
| } else { |
| pathdname = dirname(pathname_orig); |
| pathdnamer = realpath(pathdname, NULL); |
| if (!pathdnamer) |
| goto realpatherr; |
| if (!strcmp(pathdnamer, "/")) |
| error = asprintf(&pathname, "/%s", pathbname); |
| else |
| error = asprintf(&pathname, "%s/%s", pathdnamer, pathbname); |
| if (error < 0) |
| goto oom; |
| } |
| |
| paths[0] = pathname; |
| issys = (!strcmp(pathname, SYS_PATH) |
| || !strncmp(pathname, SYS_PREFIX, sizeof(SYS_PREFIX)-1)) ? true : false; |
| |
| if (!recurse) { |
| if (lstat(pathname, &sb) < 0) { |
| error = -1; |
| goto cleanup; |
| } |
| |
| error = restorecon_sb(pathname, &sb, nochange, verbose, seinfo, uid); |
| goto cleanup; |
| } |
| |
| /* |
| * Ignore saved partial match digest on /data/data or /data/user |
| * since their labeling is based on seapp_contexts and seinfo |
| * assignments rather than file_contexts and is managed by |
| * installd rather than init. |
| */ |
| if (is_app_data_path(pathname)) |
| setrestoreconlast = false; |
| |
| /* Also ignore on /sys since it is regenerated on each boot regardless. */ |
| if (issys) |
| setrestoreconlast = false; |
| |
| /* Ignore files on in-memory filesystems */ |
| if (statfs(pathname, &sfsb) == 0) { |
| if (sfsb.f_type == RAMFS_MAGIC || sfsb.f_type == TMPFS_MAGIC) |
| setrestoreconlast = false; |
| } |
| |
| fts = fts_open(paths, ftsflags, NULL); |
| if (!fts) { |
| error = -1; |
| goto cleanup; |
| } |
| |
| error = 0; |
| while ((ftsent = fts_read(fts)) != NULL) { |
| switch (ftsent->fts_info) { |
| case FTS_DC: |
| selinux_log(SELINUX_ERROR, |
| "SELinux: Directory cycle on %s.\n", ftsent->fts_path); |
| errno = ELOOP; |
| error = -1; |
| goto out; |
| case FTS_DP: |
| continue; |
| case FTS_DNR: |
| selinux_log(SELINUX_ERROR, |
| "SELinux: Could not read %s: %s.\n", ftsent->fts_path, strerror(errno)); |
| fts_set(fts, ftsent, FTS_SKIP); |
| continue; |
| case FTS_NS: |
| selinux_log(SELINUX_ERROR, |
| "SELinux: Could not stat %s: %s.\n", ftsent->fts_path, strerror(errno)); |
| fts_set(fts, ftsent, FTS_SKIP); |
| continue; |
| case FTS_ERR: |
| selinux_log(SELINUX_ERROR, |
| "SELinux: Error on %s: %s.\n", ftsent->fts_path, strerror(errno)); |
| fts_set(fts, ftsent, FTS_SKIP); |
| continue; |
| case FTS_D: |
| if (issys && !selabel_partial_match(fc_sehandle, ftsent->fts_path)) { |
| fts_set(fts, ftsent, FTS_SKIP); |
| continue; |
| } |
| |
| if (!datadata && !fnmatch(USER_PROFILE_PATH, ftsent->fts_path, FNM_PATHNAME)) { |
| // Don't label this directory, vold takes care of that, but continue below it. |
| continue; |
| } |
| |
| if (setrestoreconlast) { |
| struct dir_hash_node* new_node = NULL; |
| if (check_context_match_for_dir(ftsent->fts_path, &new_node, force, error)) { |
| selinux_log(SELINUX_INFO, |
| "SELinux: Skipping restorecon on directory(%s)\n", |
| ftsent->fts_path); |
| fts_set(fts, ftsent, FTS_SKIP); |
| continue; |
| } |
| if (new_node) { |
| if (!current) { |
| current = new_node; |
| head = current; |
| } else { |
| current->next = new_node; |
| current = current->next; |
| } |
| } |
| } |
| |
| if (skipce && |
| (!strncmp(ftsent->fts_path, DATA_SYSTEM_CE_PATH, sizeof(DATA_SYSTEM_CE_PATH)-1) || |
| !strncmp(ftsent->fts_path, DATA_MISC_CE_PATH, sizeof(DATA_MISC_CE_PATH)-1) || |
| !strncmp(ftsent->fts_path, DATA_VENDOR_CE_PATH, sizeof(DATA_VENDOR_CE_PATH)-1))) { |
| // Don't label anything below this directory. |
| fts_set(fts, ftsent, FTS_SKIP); |
| // but fall through and make sure we label the directory itself |
| } |
| |
| if (!datadata && is_app_data_path(ftsent->fts_path)) { |
| // Don't label anything below this directory. |
| fts_set(fts, ftsent, FTS_SKIP); |
| // but fall through and make sure we label the directory itself |
| } |
| /* fall through */ |
| default: |
| error |= restorecon_sb(ftsent->fts_path, ftsent->fts_statp, nochange, verbose, seinfo, uid); |
| break; |
| } |
| } |
| |
| // Labeling successful. Write the partial match digests for subdirectories. |
| // TODO: Write the digest upon FTS_DP if no error occurs in its descents. |
| if (setrestoreconlast && !nochange && !error) { |
| current = head; |
| while (current != NULL) { |
| if (setxattr(current->path, RESTORECON_PARTIAL_MATCH_DIGEST, current->digest, |
| SHA1_HASH_SIZE, 0) < 0) { |
| selinux_log(SELINUX_ERROR, |
| "SELinux: setxattr failed: %s: %s\n", |
| current->path, |
| strerror(errno)); |
| } |
| current = current->next; |
| } |
| } |
| |
| out: |
| sverrno = errno; |
| (void) fts_close(fts); |
| errno = sverrno; |
| cleanup: |
| free(pathdnamer); |
| free(pathname); |
| current = head; |
| while (current != NULL) { |
| struct dir_hash_node *next = current->next; |
| free(current->path); |
| free(current); |
| current = next; |
| } |
| return error; |
| oom: |
| sverrno = errno; |
| selinux_log(SELINUX_ERROR, "%s: Out of memory\n", __FUNCTION__); |
| errno = sverrno; |
| error = -1; |
| goto cleanup; |
| realpatherr: |
| sverrno = errno; |
| selinux_log(SELINUX_ERROR, "SELinux: Could not get canonical path for %s restorecon: %s.\n", |
| pathname_orig, strerror(errno)); |
| errno = sverrno; |
| error = -1; |
| goto cleanup; |
| } |
| |
| int selinux_android_restorecon(const char *file, unsigned int flags) |
| { |
| return selinux_android_restorecon_common(file, NULL, -1, flags); |
| } |
| |
| int selinux_android_restorecon_pkgdir(const char *pkgdir, |
| const char *seinfo, |
| uid_t uid, |
| unsigned int flags) |
| { |
| return selinux_android_restorecon_common(pkgdir, seinfo, uid, flags | SELINUX_ANDROID_RESTORECON_DATADATA); |
| } |
| |
| |
| void selinux_android_set_sehandle(const struct selabel_handle *hndl) |
| { |
| fc_sehandle = (struct selabel_handle *) hndl; |
| } |
| |
| int selinux_android_load_policy() |
| { |
| selinux_log(SELINUX_ERROR, "selinux_android_load_policy is not implemented\n"); |
| return -1; |
| } |
| |
| int selinux_android_load_policy_from_fd(int fd __attribute__((unused)), const char *description __attribute__((unused))) |
| { |
| selinux_log(SELINUX_ERROR, "selinux_android_load_policy_from_fd is not implemented\n"); |
| return -1; |
| } |