diff --git a/libselinux/src/android/android_internal.h b/libselinux/src/android/android_internal.h index e9405645..e5d69402 100644 --- a/libselinux/src/android/android_internal.h +++ b/libselinux/src/android/android_internal.h @@ -105,7 +105,14 @@ int seapp_context_reload_internal(const path_alts_t *context_paths); /* A parsed seinfo */ struct parsed_seinfo { char base[SEINFO_BUFSIZ]; - char isSelector[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; }; diff --git a/libselinux/src/android/android_seapp.c b/libselinux/src/android/android_seapp.c index 5df5b338..d1f50292 100644 --- a/libselinux/src/android/android_seapp.c +++ b/libselinux/src/android/android_seapp.c @@ -128,14 +128,18 @@ static void free_prefix_str(struct prefix_str *p) struct seapp_context { /* input selectors */ bool isSystemServer; + bool isEphemeralAppSet; + bool isEphemeralApp; struct prefix_str user; char *seinfo; struct prefix_str name; + bool isPrivAppSet; + bool isPrivApp; int32_t minTargetSdkVersion; - /* name of a boolean selector such as isPrivApp, fromRunAs, - * isIsolatedComputeApp, etc. */ - char *isSelector; - bool isSelectorValue; + bool fromRunAs; + bool isIsolatedComputeApp; + bool isSdkSandboxAudit; + bool isSdkSandboxNext; /* outputs */ char *domain; char *type; @@ -149,7 +153,6 @@ static void free_seapp_context(struct seapp_context *s) if (!s) return; - free(s->isSelector); free_prefix_str(&s->user); free(s->seinfo); free_prefix_str(&s->name); @@ -178,6 +181,11 @@ static int seapp_context_cmp(const void *A, const void *B) if (s1->isSystemServer != s2->isSystemServer) return (s1->isSystemServer ? -1 : 1); + /* Give precedence to a specified isEphemeral= over an + * unspecified isEphemeral=. */ + if (s1->isEphemeralAppSet != s2->isEphemeralAppSet) + return (s1->isEphemeralAppSet ? -1 : 1); + /* Give precedence to a specified user= over an unspecified user=. */ if (s1->user.str && !s2->user.str) return -1; @@ -216,12 +224,9 @@ static int seapp_context_cmp(const void *A, const void *B) return (s1->name.len > s2->name.len) ? -1 : 1; } - /* Give precedence to a specified isSelector (e.g., isPrivApp) - * over an unspecified isSelector. */ - if(s1->isSelector && !s2->isSelector) - return -1; - if(!s1->isSelector && s2->isSelector) - return 1; + /* Give precedence to a specified isPrivApp= over an unspecified isPrivApp=. */ + if (s1->isPrivAppSet != s2->isPrivAppSet) + return (s1->isPrivAppSet ? -1 : 1); /* Give precedence to a higher minTargetSdkVersion= over a lower minTargetSdkVersion=. * If unspecified, minTargetSdkVersion has a default value of 0. @@ -231,6 +236,10 @@ static int seapp_context_cmp(const void *A, const void *B) else if (s1->minTargetSdkVersion < s2->minTargetSdkVersion) return 1; + /* Give precedence to fromRunAs=true. */ + if (s1->fromRunAs != s2->fromRunAs) + return (s1->fromRunAs ? -1 : 1); + /* Give precedence to platform side contexts */ bool isS1Platform = is_platform(s1->partition); bool isS2Platform = is_platform(s2->partition); @@ -366,6 +375,16 @@ int seapp_context_reload_internal(const path_alts_t *context_paths) free_seapp_context(cur); goto err; } + } else if (!strcasecmp(name, "isEphemeralApp")) { + cur->isEphemeralAppSet = true; + if (!strcasecmp(value, "true")) + cur->isEphemeralApp = true; + else if (!strcasecmp(value, "false")) + cur->isEphemeralApp = false; + else { + free_seapp_context(cur); + goto err; + } } else if (!strcasecmp(name, "user")) { if (cur->user.str) { free_seapp_context(cur); @@ -466,30 +485,55 @@ int seapp_context_reload_internal(const path_alts_t *context_paths) free_seapp_context(cur); goto oom; } + } else if (!strcasecmp(name, "isPrivApp")) { + cur->isPrivAppSet = true; + if (!strcasecmp(value, "true")) + cur->isPrivApp = true; + else if (!strcasecmp(value, "false")) + cur->isPrivApp = false; + else { + free_seapp_context(cur); + goto err; + } } else if (!strcasecmp(name, "minTargetSdkVersion")) { cur->minTargetSdkVersion = get_minTargetSdkVersion(value); if (cur->minTargetSdkVersion < 0) { free_seapp_context(cur); goto err; } - } else if ((!strncasecmp(name, "is", 2) && strlen(name) > 3) || - !strcasecmp(name, "fromRunAs")) { - if (cur->isSelector) { - selinux_log(SELINUX_ERROR, "%s: isSelector set twice %s", - __FUNCTION__, name); + } else if (!strcasecmp(name, "fromRunAs")) { + if (!strcasecmp(value, "true")) + cur->fromRunAs = true; + else if (!strcasecmp(value, "false")) + cur->fromRunAs = false; + else { free_seapp_context(cur); goto err; } - cur->isSelector = strdup(name); - if (!cur->isSelector) { + } else if (!strcasecmp(name, "isIsolatedComputeApp")) { + if (!strcasecmp(value, "true")) + cur->isIsolatedComputeApp = true; + else if (!strcasecmp(value, "false")) + cur->isIsolatedComputeApp = false; + else { free_seapp_context(cur); goto err; } - if (!strcasecmp(value, "true")) { - cur->isSelectorValue = true; - } else if (!strcasecmp(value, "false")) { - cur->isSelectorValue = false; - } else { + } else if (!strcasecmp(name, "isSdkSandboxAudit")) { + if (!strcasecmp(value, "true")) + cur->isSdkSandboxAudit = true; + else if (!strcasecmp(value, "false")) + cur->isSdkSandboxAudit = false; + else { + free_seapp_context(cur); + goto err; + } + } else if (!strcasecmp(name, "isSdkSandboxNext")) { + if (!strcasecmp(value, "true")) + cur->isSdkSandboxNext = true; + else if (!strcasecmp(value, "false")) + cur->isSdkSandboxNext = false; + else { free_seapp_context(cur); goto err; } @@ -503,9 +547,8 @@ int seapp_context_reload_internal(const path_alts_t *context_paths) break; } - if ((!cur->isSelector || strcasecmp(cur->isSelector, "isPrivApp") || !cur->isSelectorValue) && - cur->name.str && - (!cur->seinfo || !strcmp(cur->seinfo, "default"))) { + if (!cur->isPrivApp && cur->name.str && + (!cur->seinfo || !strcmp(cur->seinfo, "default"))) { selinux_log(SELINUX_ERROR, "%s: No specific seinfo value specified with name=\"%s\", on line %u: insecure configuration!\n", seapp_contexts_files[i], cur->name.str, lineno); free_seapp_context(cur); @@ -539,8 +582,11 @@ int seapp_context_reload_internal(const path_alts_t *context_paths) bool dup = (!s1->user.str || !strcmp(s1->user.str, s2->user.str)) && (!s1->seinfo || !strcmp(s1->seinfo, s2->seinfo)) && (!s1->name.str || !strcmp(s1->name.str, s2->name.str)) && - (!s1->isSelector || !strcmp(s1->isSelector, s2->isSelector)) && - (s1->isSelectorValue == s2->isSelectorValue); + (!s1->isPrivAppSet || s1->isPrivApp == s2->isPrivApp) && + (!s1->isEphemeralAppSet || s1->isEphemeralApp == s2->isEphemeralApp) && + (s1->isIsolatedComputeApp == s2->isIsolatedComputeApp) && + (s1->isSdkSandboxAudit == s2->isSdkSandboxAudit) && + (s1->isSdkSandboxNext == s2->isSdkSandboxNext); if (dup) { selinux_log(SELINUX_ERROR, "seapp_contexts: Duplicated entry\n"); @@ -562,21 +608,27 @@ int seapp_context_reload_internal(const path_alts_t *context_paths) int i; for (i = 0; i < nspec; i++) { cur = seapp_contexts[i]; - selinux_log(SELINUX_INFO, "%s: isSystemServer=%s %s=%s " - "user=%s seinfo=%s name=%s minTargetSdkVersion=%d " - " -> domain=%s type=%s level=%s levelFrom=%s", + selinux_log(SELINUX_INFO, "%s: isSystemServer=%s isEphemeralApp=%s " + "isIsolatedComputeApp=%s isSdkSandboxAudit=%s isSdkSandboxNext=%s " + "user=%s seinfo=%s name=%s isPrivApp=%s minTargetSdkVersion=%d " + "fromRunAs=%s -> domain=%s type=%s level=%s levelFrom=%s", __FUNCTION__, cur->isSystemServer ? "true" : "false", - cur->isSelector ? cur->isSelector : "isX", - cur->isSelector ? (cur->isSelectorValue ? "true" : "false") : "null", + cur->isEphemeralAppSet ? (cur->isEphemeralApp ? "true" : "false") : "null", cur->user.str, cur->seinfo, cur->name.str, + cur->isPrivAppSet ? (cur->isPrivApp ? "true" : "false") : "null", cur->minTargetSdkVersion, + cur->fromRunAs ? "true" : "false", + cur->isIsolatedComputeApp ? "true" : "false", + cur->isSdkSandboxAudit ? "true" : "false", + cur->isSdkSandboxNext ? "true" : "false", cur->domain, cur->type, cur->level, levelFromName[cur->levelFrom]); } } #endif + ret = 0; out: @@ -623,21 +675,16 @@ 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" #define COMPLETE_STR "complete" -/* Any selector from seapp_contexts starting with "is" is automatically mapped - * to an attribute of seinfo. For historical reasons, there are mappings that do - * not follow this convention. */ -static char* selector_mappings[][2] = { - /* seinfo field, seapp_contexts selector */ - {"privapp", "isPrivApp"}, - {"isolatedComputeApp", "isIsolatedComputeApp"}, - {"ephemeralapp", "isEphemeralApp"}, - {"fromRunAs", "fromRunAs"} -}; - 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. // So, either the app shouldn't be platform, or the spec should be platform. @@ -678,8 +725,6 @@ int set_range_from_level(context_t ctx, enum levelFrom levelFrom, uid_t userid, return 0; } -#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) - int parse_seinfo(const char* seinfo, struct parsed_seinfo* info) { char local_seinfo[SEINFO_BUFSIZ]; @@ -702,29 +747,28 @@ int parse_seinfo(const char* seinfo, struct parsed_seinfo* info) { first = false; continue; } - bool matched = false; - for (int i=0; i < ARRAY_SIZE(selector_mappings); i++) { - if (!strcmp(token, selector_mappings[i][0])) { - if(info->isSelector[0]) { - selinux_log(SELINUX_ERROR, "%s: isSelector already populated: %s in %s\n", - __FUNCTION__, token, seinfo); - return -1; - } - strcpy(info->isSelector, selector_mappings[i][1]); - matched = true; - break; - } - } - if (matched) { + if (!strcmp(token, PRIVILEGED_APP_STR)) { + info->is |= IS_PRIV_APP; continue; } - if (!strncmp(token, "is", 2)) { - if(info->isSelector[0]) { - selinux_log(SELINUX_ERROR, "%s: isSelector already populated: %s in %s\n", - __FUNCTION__, token, seinfo); - return -1; - } - strncpy(info->isSelector, token, strlen(token)); + 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))) { @@ -748,6 +792,7 @@ int parse_seinfo(const char* seinfo, struct parsed_seinfo* info) { __FUNCTION__, token, seinfo); return -1; } + info->isPreinstalledApp = true; strncpy(info->partition, subtoken, strlen(subtoken)); continue; } @@ -823,6 +868,9 @@ int seapp_context_lookup_internal(enum seapp_kind kind, if (cur->isSystemServer != isSystemServer) continue; + if (cur->isEphemeralAppSet && cur->isEphemeralApp != ((info.is & IS_EPHEMERAL_APP) != 0)) + continue; + if (cur->user.str) { if (cur->user.is_prefix) { if (strncasecmp(username, cur->user.str, cur->user.len-1)) @@ -851,16 +899,23 @@ int seapp_context_lookup_internal(enum seapp_kind kind, } } + if (cur->isPrivAppSet && cur->isPrivApp != ((info.is & IS_PRIV_APP) != 0)) + continue; + if (cur->minTargetSdkVersion > info.targetSdkVersion) continue; - if (cur->isSelector && cur->isSelectorValue) { - if (!info.isSelector[0]) - continue; - if (strcasecmp(info.isSelector, cur->isSelector)) { - continue; - } - } + if (cur->fromRunAs != ((info.is & IS_FROM_RUN_AS) != 0)) + continue; + + if (cur->isIsolatedComputeApp != ((info.is & IS_ISOLATED_COMPUTE_APP) != 0)) + continue; + + if (cur->isSdkSandboxAudit != ((info.is & IS_SDK_SANDBOX_AUDIT) != 0)) + continue; + + if (cur->isSdkSandboxNext != ((info.is & IS_SDK_SANDBOX_NEXT) != 0)) + continue; if (kind == SEAPP_TYPE && !cur->type) continue; @@ -885,7 +940,7 @@ int seapp_context_lookup_internal(enum seapp_kind kind, goto oom; } - if (info.partition[0] + 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, diff --git a/libselinux/src/android/android_unittest.cpp b/libselinux/src/android/android_unittest.cpp index 72c67a5e..1eb9056e 100644 --- a/libselinux/src/android/android_unittest.cpp +++ b/libselinux/src/android/android_unittest.cpp @@ -11,17 +11,8 @@ using android::base::WriteStringToFile; using std::string; class AndroidSELinuxTest : public ::testing::Test { - protected: - void SetUp() override { - default_seapp_context_ = StringPrintf("%s/seapp_contexts", tdir_.path); - default_seapp_path_alts_ = { .paths = { - { default_seapp_context_.c_str() } - }}; - } - + protected: TemporaryDir tdir_; - string default_seapp_context_; - path_alts_t default_seapp_path_alts_; }; TEST_F(AndroidSELinuxTest, LoadAndLookupServiceContext) @@ -93,13 +84,19 @@ TEST_F(AndroidSELinuxTest, FailLoadingServiceContext) TEST_F(AndroidSELinuxTest, LoadAndLookupSeAppContext) { + string seapp_contexts = + StringPrintf("%s/seapp_contexts", tdir_.path); WriteStringToFile( "# some comment\n" "user=_app seinfo=platform domain=platform_app type=app_data_file levelFrom=user\n", - default_seapp_context_); + seapp_contexts); - EXPECT_EQ(seapp_context_reload_internal(&default_seapp_path_alts_), 0); + const path_alts_t seapp_paths = { .paths = { + { seapp_contexts.c_str() } + }}; + + EXPECT_EQ(seapp_context_reload_internal(&seapp_paths), 0); context_t ctx = context_new("u:r:unknown"); int ret = seapp_context_lookup_internal(SEAPP_DOMAIN, 10001, false, "platform", "com.android.test1", ctx); @@ -114,42 +111,6 @@ TEST_F(AndroidSELinuxTest, LoadAndLookupSeAppContext) context_free(ctx); } -TEST_F(AndroidSELinuxTest, LoadSeAppContextInsecure) -{ - WriteStringToFile( - "user=_app name=com.android.test domain=platform_app type=app_data_file\n", - default_seapp_context_); - - EXPECT_EQ(seapp_context_reload_internal(&default_seapp_path_alts_), -1); - - WriteStringToFile( - "user=_app name=com.android.test isSdkSandbox=true domain=platform_app type=app_data_file\n", - default_seapp_context_); - - EXPECT_EQ(seapp_context_reload_internal(&default_seapp_path_alts_), -1); - - WriteStringToFile( - "user=_app isPrivApp=false name=com.android.test domain=platform_app type=app_data_file\n", - default_seapp_context_); - - EXPECT_EQ(seapp_context_reload_internal(&default_seapp_path_alts_), -1); -} - -TEST_F(AndroidSELinuxTest, LoadSeAppContextInsecureWithSeInfoOrPrivApp) -{ - WriteStringToFile( - "user=_app isPrivApp=true name=com.android.test domain=platform_app type=app_data_file\n", - default_seapp_context_); - - EXPECT_EQ(seapp_context_reload_internal(&default_seapp_path_alts_), 0); - - WriteStringToFile( - "user=_app seinfo=platform name=com.android.test domain=platform_app type=app_data_file\n", - default_seapp_context_); - - EXPECT_EQ(seapp_context_reload_internal(&default_seapp_path_alts_), 0); -} - TEST(AndroidSeAppTest, ParseValidSeInfo) { struct parsed_seinfo info; @@ -161,7 +122,8 @@ TEST(AndroidSeAppTest, ParseValidSeInfo) EXPECT_EQ(ret, 0); EXPECT_STREQ(info.base, "default"); EXPECT_EQ(info.targetSdkVersion, 10000); - EXPECT_STREQ(info.isSelector, "isPrivApp"); + EXPECT_EQ(info.is, IS_PRIV_APP); + EXPECT_EQ(info.isPreinstalledApp, true); EXPECT_STREQ(info.partition, "system"); seinfo = "platform:ephemeralapp:partition=system:complete"; @@ -170,7 +132,8 @@ TEST(AndroidSeAppTest, ParseValidSeInfo) EXPECT_EQ(ret, 0); EXPECT_STREQ(info.base, "platform"); EXPECT_EQ(info.targetSdkVersion, 0); - EXPECT_STREQ(info.isSelector, "isEphemeralApp"); + EXPECT_EQ(info.is, IS_EPHEMERAL_APP); + EXPECT_EQ(info.isPreinstalledApp, true); EXPECT_STREQ(info.partition, "system"); seinfo = "bluetooth"; @@ -179,16 +142,8 @@ TEST(AndroidSeAppTest, ParseValidSeInfo) EXPECT_EQ(ret, 0); EXPECT_STREQ(info.base, "bluetooth"); EXPECT_EQ(info.targetSdkVersion, 0); - EXPECT_STREQ(info.isSelector, ""); - - seinfo = "default:isSdkSandboxNext:partition=system:complete"; - ret = parse_seinfo(seinfo.c_str(), &info); - - EXPECT_EQ(ret, 0); - EXPECT_STREQ(info.base, "default"); - EXPECT_EQ(info.targetSdkVersion, 0); - EXPECT_STREQ(info.isSelector, "isSdkSandboxNext"); - EXPECT_STREQ(info.partition, "system"); + EXPECT_EQ(info.isPreinstalledApp, false); + EXPECT_EQ(info.is, 0); } TEST(AndroidSeAppTest, ParseInvalidSeInfo) @@ -202,14 +157,6 @@ TEST(AndroidSeAppTest, ParseInvalidSeInfo) seinfo = "default:targetSdkVersion=:complete"; ret = parse_seinfo(seinfo.c_str(), &info); EXPECT_EQ(ret, -1); - - seinfo = "default:privapp:ephemeralapp:complete"; - ret = parse_seinfo(seinfo.c_str(), &info); - EXPECT_EQ(ret, -1); - - seinfo = "default:isANewSelector:isAnotherOne:complete"; - ret = parse_seinfo(seinfo.c_str(), &info); - EXPECT_EQ(ret, -1); } TEST(AndroidSeAppTest, ParseOverflow)