diff --git a/libselinux/src/android/android_internal.h b/libselinux/src/android/android_internal.h index 0f11ce8a..e5d69402 100644 --- a/libselinux/src/android/android_internal.h +++ b/libselinux/src/android/android_internal.h @@ -100,6 +100,25 @@ int set_range_from_level(context_t ctx, enum levelFrom levelFrom, uid_t userid, /* Similar to seapp_context_reload, but does not implicitly load the default * context files. It should only be used for unit tests. */ int seapp_context_reload_internal(const path_alts_t *context_paths); + +#define SEINFO_BUFSIZ 256 +/* A parsed seinfo */ +struct parsed_seinfo { + char base[SEINFO_BUFSIZ]; +#define IS_PRIV_APP (1 << 0) +#define IS_FROM_RUN_AS (1 << 1) +#define IS_EPHEMERAL_APP (1 << 2) +#define IS_ISOLATED_COMPUTE_APP (1 << 3) +#define IS_SDK_SANDBOX_AUDIT (1 << 4) +#define IS_SDK_SANDBOX_NEXT (1 << 5) + int32_t is; + bool isPreinstalledApp; + char partition[SEINFO_BUFSIZ]; + int32_t targetSdkVersion; +}; + +/* Parses an seinfo string. Returns -1 if an error occurred. */ +int parse_seinfo(const char* seinfo, struct parsed_seinfo* info); #ifdef __cplusplus } #endif diff --git a/libselinux/src/android/android_seapp.c b/libselinux/src/android/android_seapp.c index 847c9354..d1f50292 100644 --- a/libselinux/src/android/android_seapp.c +++ b/libselinux/src/android/android_seapp.c @@ -675,51 +675,15 @@ void selinux_android_seapp_context_init(void) { */ #define CAT_MAPPING_MAX_ID (0x1<<16) -#define PRIVILEGED_APP_STR ":privapp" -#define ISOLATED_COMPUTE_APP_STR ":isolatedComputeApp" -#define APPLY_SDK_SANDBOX_AUDIT_RESTRICTIONS_STR ":isSdkSandboxAudit" -#define APPLY_SDK_SANDBOX_NEXT_RESTRICTIONS_STR ":isSdkSandboxNext" -#define EPHEMERAL_APP_STR ":ephemeralapp" -#define TARGETSDKVERSION_STR ":targetSdkVersion=" -#define PARTITION_STR ":partition=" -#define FROM_RUNAS_STR ":fromRunAs" -static int32_t get_app_targetSdkVersion(const char *seinfo) -{ - char *substr = strstr(seinfo, TARGETSDKVERSION_STR); - long targetSdkVersion; - char *endptr; - if (substr != NULL) { - substr = substr + strlen(TARGETSDKVERSION_STR); - if (substr != NULL) { - targetSdkVersion = strtol(substr, &endptr, 10); - if (('\0' != *endptr && ':' != *endptr) - || (targetSdkVersion < 0) || (targetSdkVersion > INT32_MAX)) { - return -1; /* malformed targetSdkVersion value in seinfo */ - } else { - return (int32_t) targetSdkVersion; - } - } - } - return 0; /* default to 0 when targetSdkVersion= is not present in seinfo */ -} - -// returns true if found, false if not found or error -static bool get_partition(const char *seinfo, char partition[], size_t size) -{ - if (size == 0) return false; - - const char *substr = strstr(seinfo, PARTITION_STR); - if (substr == NULL) return false; - - const char *src = substr + strlen(PARTITION_STR); - const char *p = strchr(src, ':'); - size_t len = p ? p - src : strlen(src); - if (len > size - 1) return -1; - strncpy(partition, src, len); - partition[len] = '\0'; - - return true; -} +#define PRIVILEGED_APP_STR "privapp" +#define ISOLATED_COMPUTE_APP_STR "isolatedComputeApp" +#define APPLY_SDK_SANDBOX_AUDIT_RESTRICTIONS_STR "isSdkSandboxAudit" +#define APPLY_SDK_SANDBOX_NEXT_RESTRICTIONS_STR "isSdkSandboxNext" +#define EPHEMERAL_APP_STR "ephemeralapp" +#define TARGETSDKVERSION_STR "targetSdkVersion" +#define PARTITION_STR "partition" +#define FROM_RUNAS_STR "fromRunAs" +#define COMPLETE_STR "complete" static bool is_preinstalled_app_partition_valid(const char *app_policy, const char *app_partition) { // We forbid system/system_ext/product installed apps from being labeled with vendor sepolicy. @@ -727,26 +691,6 @@ static bool is_preinstalled_app_partition_valid(const char *app_policy, const ch return !(is_platform(app_partition) && !is_platform(app_policy)); } - -static int seinfo_parse(char *dest, const char *src, size_t size) -{ - size_t len; - char *p; - - if ((p = strchr(src, ':')) != NULL) - len = p - src; - else - len = strlen(src); - - if (len > size - 1) - return -1; - - strncpy(dest, src, len); - dest[len] = '\0'; - - return 0; -} - /* Sets the categories of ctx based on the level request */ int set_range_from_level(context_t ctx, enum levelFrom levelFrom, uid_t userid, uid_t appid) { @@ -781,6 +725,86 @@ int set_range_from_level(context_t ctx, enum levelFrom levelFrom, uid_t userid, return 0; } +int parse_seinfo(const char* seinfo, struct parsed_seinfo* info) { + char local_seinfo[SEINFO_BUFSIZ]; + + memset(info, 0, sizeof(*info)); + + if (strlen(seinfo) >= SEINFO_BUFSIZ) { + selinux_log(SELINUX_ERROR, "%s: seinfo is too large to be parsed: %zu\n", + __FUNCTION__, strlen(seinfo)); + return -1; + } + strncpy(local_seinfo, seinfo, SEINFO_BUFSIZ); + + char *token; + char *saved_colon_ptr = NULL; + char *saved_equal_ptr; + bool first = true; + for (token = strtok_r(local_seinfo, ":", &saved_colon_ptr); token; token = strtok_r(NULL, ":", &saved_colon_ptr)) { + if (first) { + strncpy(info->base, token, SEINFO_BUFSIZ); + first = false; + continue; + } + if (!strcmp(token, PRIVILEGED_APP_STR)) { + info->is |= IS_PRIV_APP; + continue; + } + if (!strcmp(token, EPHEMERAL_APP_STR)) { + info->is |= IS_EPHEMERAL_APP; + continue; + } + if (!strcmp(token, ISOLATED_COMPUTE_APP_STR)) { + info->is |= IS_ISOLATED_COMPUTE_APP; + continue; + } + if (!strcmp(token, APPLY_SDK_SANDBOX_AUDIT_RESTRICTIONS_STR)) { + info->is |= IS_SDK_SANDBOX_AUDIT; + continue; + } + if (!strcmp(token, APPLY_SDK_SANDBOX_NEXT_RESTRICTIONS_STR)) { + info->is |= IS_SDK_SANDBOX_NEXT; + continue; + } + if (!strcmp(token, FROM_RUNAS_STR)) { + info->is |= IS_FROM_RUN_AS; + continue; + } + if (!strncmp(token, TARGETSDKVERSION_STR, strlen(TARGETSDKVERSION_STR))) { + saved_equal_ptr = NULL; + char *subtoken = strtok_r(token, "=", &saved_equal_ptr); + subtoken = strtok_r(NULL, "=", &saved_equal_ptr); + if (!subtoken) { + selinux_log(SELINUX_ERROR, "%s: Invalid targetSdkVersion: %s in %s\n", + __FUNCTION__, token, seinfo); + return -1; + } + info->targetSdkVersion = strtol(subtoken, NULL, 10); + continue; + } + if (!strncmp(token, PARTITION_STR, strlen(PARTITION_STR))) { + saved_equal_ptr = NULL; + char *subtoken = strtok_r(token, "=", &saved_equal_ptr); + subtoken = strtok_r(NULL, "=", &saved_equal_ptr); + if (!subtoken) { + selinux_log(SELINUX_ERROR, "%s: Invalid partition: %s in %s\n", + __FUNCTION__, token, seinfo); + return -1; + } + info->isPreinstalledApp = true; + strncpy(info->partition, subtoken, strlen(subtoken)); + continue; + } + if (!strcmp(token, COMPLETE_STR)) { + break; + } + selinux_log(SELINUX_WARNING, "%s: Ignoring unknown seinfo field: %s in %s\n", + __FUNCTION__, token, seinfo); + } + return 0; +} + /* * This code is Android specific, bionic guarantees that * calls to non-reentrant getpwuid() are thread safe. @@ -800,35 +824,21 @@ int seapp_context_lookup_internal(enum seapp_kind kind, int i; uid_t userid; uid_t appid; - bool isPrivApp = false; - bool isEphemeralApp = false; - bool isIsolatedComputeApp = false; - bool isSdkSandboxAudit = false; - bool isSdkSandboxNext = false; - int32_t targetSdkVersion = 0; - bool fromRunAs = false; - bool isPreinstalledApp = false; - char partition[BUFSIZ]; - char parsedseinfo[BUFSIZ]; + struct parsed_seinfo info; + memset(&info, 0, sizeof(info)); if (seinfo) { - if (seinfo_parse(parsedseinfo, seinfo, BUFSIZ)) + int ret = parse_seinfo(seinfo, &info); + if (ret) { + selinux_log(SELINUX_ERROR, "%s: Invalid seinfo: %s\n", __FUNCTION__, seinfo); goto err; - isPrivApp = strstr(seinfo, PRIVILEGED_APP_STR) ? true : false; - isEphemeralApp = strstr(seinfo, EPHEMERAL_APP_STR) ? true : false; - isIsolatedComputeApp = strstr(seinfo, ISOLATED_COMPUTE_APP_STR) ? true : false; - isSdkSandboxAudit = strstr(seinfo, APPLY_SDK_SANDBOX_AUDIT_RESTRICTIONS_STR) ? true : false; - isSdkSandboxNext = strstr(seinfo, APPLY_SDK_SANDBOX_NEXT_RESTRICTIONS_STR) ? true : false; - fromRunAs = strstr(seinfo, FROM_RUNAS_STR) ? true : false; - targetSdkVersion = get_app_targetSdkVersion(seinfo); - isPreinstalledApp = get_partition(seinfo, partition, BUFSIZ); - if (targetSdkVersion < 0) { + } + if (info.targetSdkVersion < 0) { selinux_log(SELINUX_ERROR, "%s: Invalid targetSdkVersion passed for app with uid %d, seinfo %s, name %s\n", __FUNCTION__, uid, seinfo, pkgname); goto err; } - seinfo = parsedseinfo; } userid = uid / AID_USER_OFFSET; @@ -858,7 +868,7 @@ int seapp_context_lookup_internal(enum seapp_kind kind, if (cur->isSystemServer != isSystemServer) continue; - if (cur->isEphemeralAppSet && cur->isEphemeralApp != isEphemeralApp) + if (cur->isEphemeralAppSet && cur->isEphemeralApp != ((info.is & IS_EPHEMERAL_APP) != 0)) continue; if (cur->user.str) { @@ -872,7 +882,7 @@ int seapp_context_lookup_internal(enum seapp_kind kind, } if (cur->seinfo) { - if (!seinfo || strcasecmp(seinfo, cur->seinfo)) + if (!seinfo || strcasecmp(info.base, cur->seinfo)) continue; } @@ -889,22 +899,22 @@ int seapp_context_lookup_internal(enum seapp_kind kind, } } - if (cur->isPrivAppSet && cur->isPrivApp != isPrivApp) + if (cur->isPrivAppSet && cur->isPrivApp != ((info.is & IS_PRIV_APP) != 0)) continue; - if (cur->minTargetSdkVersion > targetSdkVersion) + if (cur->minTargetSdkVersion > info.targetSdkVersion) continue; - if (cur->fromRunAs != fromRunAs) + if (cur->fromRunAs != ((info.is & IS_FROM_RUN_AS) != 0)) continue; - if (cur->isIsolatedComputeApp != isIsolatedComputeApp) + if (cur->isIsolatedComputeApp != ((info.is & IS_ISOLATED_COMPUTE_APP) != 0)) continue; - if (cur->isSdkSandboxAudit != isSdkSandboxAudit) + if (cur->isSdkSandboxAudit != ((info.is & IS_SDK_SANDBOX_AUDIT) != 0)) continue; - if (cur->isSdkSandboxNext != isSdkSandboxNext) + if (cur->isSdkSandboxNext != ((info.is & IS_SDK_SANDBOX_NEXT) != 0)) continue; if (kind == SEAPP_TYPE && !cur->type) @@ -930,12 +940,12 @@ int seapp_context_lookup_internal(enum seapp_kind kind, goto oom; } - if (isPreinstalledApp - && !is_preinstalled_app_partition_valid(cur->partition, partition)) { + if (info.isPreinstalledApp + && !is_preinstalled_app_partition_valid(cur->partition, info.partition)) { // TODO(b/280547417): make this an error after fixing violations selinux_log(SELINUX_WARNING, "%s: App %s preinstalled to %s can't be labeled with %s sepolicy", - __FUNCTION__, pkgname, partition, cur->partition); + __FUNCTION__, pkgname, info.partition, cur->partition); } break; diff --git a/libselinux/src/android/android_unittest.cpp b/libselinux/src/android/android_unittest.cpp index 47613928..1eb9056e 100644 --- a/libselinux/src/android/android_unittest.cpp +++ b/libselinux/src/android/android_unittest.cpp @@ -110,3 +110,65 @@ TEST_F(AndroidSELinuxTest, LoadAndLookupSeAppContext) EXPECT_STREQ(context_str(ctx), "u:r:app_data_file:s0:c512,c768"); context_free(ctx); } + +TEST(AndroidSeAppTest, ParseValidSeInfo) +{ + struct parsed_seinfo info; + memset(&info, 0, sizeof(info)); + + string seinfo = "default:privapp:targetSdkVersion=10000:partition=system:complete"; + int ret = parse_seinfo(seinfo.c_str(), &info); + + EXPECT_EQ(ret, 0); + EXPECT_STREQ(info.base, "default"); + EXPECT_EQ(info.targetSdkVersion, 10000); + EXPECT_EQ(info.is, IS_PRIV_APP); + EXPECT_EQ(info.isPreinstalledApp, true); + EXPECT_STREQ(info.partition, "system"); + + seinfo = "platform:ephemeralapp:partition=system:complete"; + ret = parse_seinfo(seinfo.c_str(), &info); + + EXPECT_EQ(ret, 0); + EXPECT_STREQ(info.base, "platform"); + EXPECT_EQ(info.targetSdkVersion, 0); + EXPECT_EQ(info.is, IS_EPHEMERAL_APP); + EXPECT_EQ(info.isPreinstalledApp, true); + EXPECT_STREQ(info.partition, "system"); + + seinfo = "bluetooth"; + ret = parse_seinfo(seinfo.c_str(), &info); + + EXPECT_EQ(ret, 0); + EXPECT_STREQ(info.base, "bluetooth"); + EXPECT_EQ(info.targetSdkVersion, 0); + EXPECT_EQ(info.isPreinstalledApp, false); + EXPECT_EQ(info.is, 0); +} + +TEST(AndroidSeAppTest, ParseInvalidSeInfo) +{ + struct parsed_seinfo info; + + string seinfo = "default:targetSdkVersion:complete"; + int ret = parse_seinfo(seinfo.c_str(), &info); + EXPECT_EQ(ret, -1); + + seinfo = "default:targetSdkVersion=:complete"; + ret = parse_seinfo(seinfo.c_str(), &info); + EXPECT_EQ(ret, -1); +} + +TEST(AndroidSeAppTest, ParseOverflow) +{ + struct parsed_seinfo info; + + string seinfo = std::string(255, 'x'); + int ret = parse_seinfo(seinfo.c_str(), &info); + EXPECT_EQ(ret, 0); + EXPECT_STREQ(info.base, seinfo.c_str()); + + seinfo = std::string(256, 'x'); + ret = parse_seinfo(seinfo.c_str(), &info); + EXPECT_EQ(ret, -1); +}