diff --git a/libselinux/src/android/android.c b/libselinux/src/android/android.c index 83066118..1b78c8f1 100644 --- a/libselinux/src/android/android.c +++ b/libselinux/src/android/android.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -188,6 +189,200 @@ struct selabel_handle* selinux_android_keystore2_key_context_handle(void) return context_handle(SELABEL_CTX_ANDROID_KEYSTORE2_KEY, &keystore2_context_paths, "keystore2"); } +/* 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" + +/* 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 DATA_MISC_DE_PATH "/data/misc_de" +#define DATA_STORAGE_AREA_PATH "/data/storage_area" +#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_STORAGE_AREA_PREFIX DATA_STORAGE_AREA_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 "/" + +bool is_app_data_path(const char *pathname) { + int flags = FNM_LEADING_DIR|FNM_PATHNAME; +#ifdef SELINUX_FLAGS_DATA_DATA_IGNORE + if (!strcmp(pathname, DATA_DATA_PATH)) { + return true; + } +#endif + 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) || + !strncmp(pathname, DATA_STORAGE_AREA_PREFIX, sizeof(DATA_STORAGE_AREA_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)); +} + +bool is_credential_encrypted_path(const char *pathname) { + return !strncmp(pathname, DATA_SYSTEM_CE_PATH, sizeof(DATA_SYSTEM_CE_PATH)-1) || + !strncmp(pathname, DATA_MISC_CE_PATH, sizeof(DATA_MISC_CE_PATH)-1) || + !strncmp(pathname, DATA_VENDOR_CE_PATH, sizeof(DATA_VENDOR_CE_PATH)-1); +} + +/* + * 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; +} + +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 (!strncmp(pathname, DATA_STORAGE_AREA_PREFIX, sizeof(DATA_STORAGE_AREA_PREFIX)-1)) { + pathname += sizeof(DATA_STORAGE_AREA_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 void __selinux_log_callback(bool add_to_event_log, int type, const char *fmt, va_list ap) { int priority; char *strp; diff --git a/libselinux/src/android/android_device.c b/libselinux/src/android/android_device.c index df110739..3759b6ec 100644 --- a/libselinux/src/android/android_device.c +++ b/libselinux/src/android/android_device.c @@ -155,8 +155,8 @@ static struct selabel_handle *fc_sehandle = NULL; static void file_context_init(void) { - if (!fc_sehandle) - fc_sehandle = selinux_android_file_context_handle(); + if (!fc_sehandle) + fc_sehandle = selinux_android_file_context_handle(); } static pthread_once_t fc_once = PTHREAD_ONCE_INIT; @@ -169,59 +169,59 @@ 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); + 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; + (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; + 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; - } + 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; + { + 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); - } + 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 } @@ -231,637 +231,443 @@ 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; + struct pkg_info *info; + unsigned int hash; - __selinux_once(pkg_once, package_info_init); + __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; + 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 DATA_STORAGE_AREA_PATH "/data/storage_area" #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_STORAGE_AREA_PREFIX DATA_STORAGE_AREA_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; -#ifdef SELINUX_FLAGS_DATA_DATA_IGNORE - if (!strcmp(pathname, DATA_DATA_PATH)) { - return true; - } -#endif - 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) || - !strncmp(pathname, DATA_STORAGE_AREA_PREFIX, sizeof(DATA_STORAGE_AREA_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 (!strncmp(pathname, DATA_STORAGE_AREA_PREFIX, sizeof(DATA_STORAGE_AREA_PREFIX)-1)) { - pathname += sizeof(DATA_STORAGE_AREA_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) + 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; + 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; - } + 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; - } + 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; + 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; + 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; + ctx_str = context_str(ctx); + if (!ctx_str) + goto err; - if (!strcmp(ctx_str, orig_ctx_str)) - goto out; + if (!strcmp(ctx_str, orig_ctx_str)) + goto out; - rc = security_check_context(ctx_str); - if (rc < 0) - goto err; + rc = security_check_context(ctx_str); + if (rc < 0) + goto err; - freecon(*secontextp); - *secontextp = strdup(ctx_str); - if (!(*secontextp)) - goto err; + freecon(*secontextp); + *secontextp = strdup(ctx_str); + if (!(*secontextp)) + goto err; - rc = 0; + rc = 0; out: - free(pkgname); - context_free(ctx); - return rc; + 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; + 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) +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; + 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 (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; + 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; - } + /* + * 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; - } - } + 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; + rc = 0; out: - freecon(oldsecontext); - freecon(secontext); - return rc; + 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; + 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; + 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); +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; - } + 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; - } + // 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; - } + (*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; + return false; } static int selinux_android_restorecon_common(const char* pathname_orig, - const char *seinfo, - uid_t uid, - unsigned int flags) + 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; + 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 (!cross_filesystems) { + ftsflags |= FTS_XDEV; + } - if (is_selinux_enabled() <= 0) { - selinux_log(SELINUX_WARNING, "SELinux: SELinux is disabled, skipping restorecon"); - return 0; - } + if (is_selinux_enabled() <= 0) { + selinux_log(SELINUX_WARNING, "SELinux: SELinux is disabled, skipping restorecon"); + return 0; + } - __selinux_once(fc_once, file_context_init); + __selinux_once(fc_once, file_context_init); - if (!fc_sehandle) - return 0; + 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; - } + /* + * 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; + 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; - } + if (!recurse) { + if (lstat(pathname, &sb) < 0) { + error = -1; + goto cleanup; + } - error = restorecon_sb(pathname, &sb, nochange, verbose, seinfo, uid); - 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; + /* + * 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; + /* 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; - } + /* 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; - } + 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; - } + 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 (!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 (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 (skipce && is_credential_encrypted_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 + } - 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; - } - } + 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; - } - } + // 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; + 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; + 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; + 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; + 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); + 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) + const char *seinfo, + uid_t uid, + unsigned int flags) { - return selinux_android_restorecon_common(pkgdir, seinfo, uid, flags | SELINUX_ANDROID_RESTORECON_DATADATA); + 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; + fc_sehandle = (struct selabel_handle *) hndl; } int selinux_android_load_policy() diff --git a/libselinux/src/android/android_internal.h b/libselinux/src/android/android_internal.h index e5d69402..ada11078 100644 --- a/libselinux/src/android/android_internal.h +++ b/libselinux/src/android/android_internal.h @@ -55,6 +55,27 @@ struct selabel_handle* context_handle( const path_alts_t *context_paths, const char* name); +/* + * 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. + */ +bool is_app_data_path(const char *pathname); + +/* + * Determines if a path is Credential Encrypted (CE). + * Some paths are not available when the device first boots (these are protected + * by a credential). They should not be processed by restorecon until decrypted. + * See also the --skip-ce option for restorecon. + */ +bool is_credential_encrypted_path(const char *pathname); + +/* 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. + */ +int extract_pkgname_and_userid(const char *pathname, char **pkgname, unsigned int *userid); + /* The kind of request when looking up an seapp_context. */ enum seapp_kind { /* Returns the SELinux type for the app data directory */ diff --git a/libselinux/src/android/android_unittest.cpp b/libselinux/src/android/android_unittest.cpp index 1eb9056e..28a75247 100644 --- a/libselinux/src/android/android_unittest.cpp +++ b/libselinux/src/android/android_unittest.cpp @@ -172,3 +172,61 @@ TEST(AndroidSeAppTest, ParseOverflow) ret = parse_seinfo(seinfo.c_str(), &info); EXPECT_EQ(ret, -1); } + +TEST(AndroidSELinuxPathTest, IsAppDataPath) +{ + EXPECT_TRUE(is_app_data_path("/data/data")); + EXPECT_TRUE(is_app_data_path("/data/user/0")); + + EXPECT_FALSE(is_app_data_path("/data")); +} + +TEST(AndroidSELinuxPathTest, IsCredentialEncryptedPath) +{ + EXPECT_TRUE(is_credential_encrypted_path("/data/system_ce/0")); + EXPECT_TRUE(is_credential_encrypted_path("/data/system_ce/0/backup")); + EXPECT_TRUE(is_credential_encrypted_path("/data/misc_ce/0")); + EXPECT_TRUE(is_credential_encrypted_path("/data/misc_ce/0/apexdata")); + EXPECT_TRUE(is_credential_encrypted_path("/data/vendor_ce/0")); + EXPECT_TRUE(is_credential_encrypted_path("/data/vendor_ce/0/data")); + + EXPECT_FALSE(is_credential_encrypted_path("/data")); + EXPECT_FALSE(is_credential_encrypted_path("/data/data")); + EXPECT_FALSE(is_credential_encrypted_path("/data/user/0")); +} + +TEST(AndroidSELinuxPathTest, ExtractPkgnameAndUserid) +{ + char *pkgname = NULL; + unsigned int userid; + + EXPECT_EQ(extract_pkgname_and_userid("/data/", &pkgname, &userid), -1); + + char const* path = "/data/user/0/com.android.myapp"; + EXPECT_EQ(extract_pkgname_and_userid(path, &pkgname, &userid), 0); + EXPECT_STREQ("com.android.myapp", pkgname); + EXPECT_EQ(userid, 0); + free(pkgname); + pkgname = NULL; + + path = "/data/user/0/com.android.myapp/som/subdir"; + EXPECT_EQ(extract_pkgname_and_userid(path, &pkgname, &userid), 0); + EXPECT_STREQ("com.android.myapp", pkgname); + EXPECT_EQ(userid, 0); + free(pkgname); + pkgname = NULL; + + path = "/data/data/com.android.myapp2"; + EXPECT_EQ(extract_pkgname_and_userid(path, &pkgname, &userid), 0); + EXPECT_STREQ("com.android.myapp2", pkgname); + EXPECT_EQ(userid, 0); + free(pkgname); + pkgname = NULL; + + path = "/data/misc_de/10/sdksandbox/com.android.myapp3"; + EXPECT_EQ(extract_pkgname_and_userid(path, &pkgname, &userid), 0); + EXPECT_STREQ("com.android.myapp3", pkgname); + EXPECT_EQ(userid, 10); + free(pkgname); + pkgname = NULL; +}