Check preinstalled app's partition

Bug: 280547417
Test: boot pixel and cuttlefish
Change-Id: I6ed125eff392020ace6686514e0a102dab1fb10f
diff --git a/libselinux/src/android/android.c b/libselinux/src/android/android.c
index ab0a15f..4e33059 100644
--- a/libselinux/src/android/android.c
+++ b/libselinux/src/android/android.c
@@ -94,11 +94,26 @@
 		const path_alts_t *path_sets,
 		const char* paths[MAX_CONTEXT_PATHS])
 {
+	return find_existing_files_with_partitions(
+		path_sets,
+		paths,
+		NULL
+	);
+}
+
+size_t find_existing_files_with_partitions(
+		const path_alts_t *path_sets,
+		const char* paths[MAX_CONTEXT_PATHS],
+		const char* partitions[MAX_CONTEXT_PATHS])
+{
 	size_t i, j, len = 0;
 	for (i = 0; i < MAX_CONTEXT_PATHS; i++) {
 		for (j = 0; j < MAX_ALT_CONTEXT_PATHS; j++) {
 			const char* file = path_sets->paths[i][j];
 			if (file && access(file, R_OK) != -1) {
+				if (partitions) {
+					partitions[len] = path_sets->partitions[i];
+				}
 				paths[len++] = file;
 				/* Within each set, only the first valid entry is used */
 				break;
diff --git a/libselinux/src/android/android_internal.h b/libselinux/src/android/android_internal.h
index 411ce48..4960e72 100644
--- a/libselinux/src/android/android_internal.h
+++ b/libselinux/src/android/android_internal.h
@@ -17,6 +17,7 @@
 #define MAX_ALT_CONTEXT_PATHS 2
 typedef struct path_alts {
 	const char *paths[MAX_CONTEXT_PATHS][MAX_ALT_CONTEXT_PATHS];
+	const char *partitions[MAX_CONTEXT_PATHS];
 } path_alts_t;
 
 /* Within each set of files, adds the first file that is accessible to `paths`.
@@ -25,6 +26,14 @@
 	const path_alts_t *path_sets,
 	const char *paths[MAX_CONTEXT_PATHS]);
 
+/* Within each set of files, adds the first file that is accessible to `paths`.
+ * Returns the number of accessible files. Also returns the partitions where
+ * the files exist. */
+size_t find_existing_files_with_partitions(
+	const path_alts_t *path_sets,
+	const char *paths[MAX_CONTEXT_PATHS],
+	const char *partitions[MAX_CONTEXT_PATHS]);
+
 /* Converts an array of file paths into an array of options for selabel_open.
  * opts must be at least as large as paths. */
 void paths_to_opts(
diff --git a/libselinux/src/android/android_seapp.c b/libselinux/src/android/android_seapp.c
index 391165d..d97a5c8 100644
--- a/libselinux/src/android/android_seapp.c
+++ b/libselinux/src/android/android_seapp.c
@@ -48,8 +48,8 @@
 	}
 }};
 
-/* Locations for the seapp_contexts files. For each partition, only the first
- * existing entry will be used (for example, if
+/* Locations for the seapp_contexts files, and corresponding partitions. For
+ * each partition, only the first existing entry will be used (for example, if
  * /system/etc/selinux/plat_seapp_contexts exists, /plat_seapp_contexts will be
  * ignored).
  */
@@ -77,6 +77,13 @@
 		"/odm/etc/selinux/odm_seapp_contexts",
 		"/odm_seapp_contexts"
 	}
+}, .partitions= {
+	"system",
+	"system", // regard APEX sepolicy as system
+	"system_ext",
+	"product",
+	"vendor",
+	"odm"
 }};
 
 /* Returns a handle for the file contexts backend, initialized with the Android
@@ -141,6 +148,7 @@
 	char *type;
 	char *level;
 	enum levelFrom levelFrom;
+	const char* partition;
 };
 
 static void free_seapp_context(struct seapp_context *s)
@@ -303,8 +311,9 @@
 	size_t i, len, files_len = 0;
 	int ret;
 	const char* seapp_contexts_files[MAX_CONTEXT_PATHS];
+	const char* seapp_contexts_partitions[MAX_CONTEXT_PATHS];
 
-	files_len = find_existing_files(context_paths, seapp_contexts_files);
+	files_len = find_existing_files_with_partitions(context_paths, seapp_contexts_files, seapp_contexts_partitions);
 
 	/* Reset the current entries */
 	free_seapp_contexts();
@@ -555,6 +564,7 @@
 				goto err;
 			}
 
+			cur->partition = seapp_contexts_partitions[i];
 			seapp_contexts[nspec] = cur;
 			nspec++;
 			lineno++;
@@ -643,6 +653,7 @@
 #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)
 {
@@ -664,6 +675,39 @@
 	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;
+}
+
+static bool is_platform(const char *partition) {
+	// system, system_ext, product are regarded as "platform", whereas vendor
+	// and odm are regarded as vendor.
+	if (strcmp(partition, "system") == 0) return true;
+	if (strcmp(partition, "system_ext") == 0) return true;
+	if (strcmp(partition, "product") == 0) return true;
+	return false;
+}
+
+static bool check_preinstalled_app_partition(const char *spec, const char *app) {
+	// We forbid system/system_ext/product installed apps from being labeled with vendor sepolicy.
+	return !is_platform(spec) && is_platform(app);
+}
+
+
 static int seinfo_parse(char *dest, const char *src, size_t size)
 {
 	size_t len;
@@ -742,6 +786,8 @@
 	bool isSdkSandboxNext = false;
 	int32_t targetSdkVersion = 0;
 	bool fromRunAs = false;
+	bool isPreinstalledApp = false;
+	char partition[BUFSIZ];
 	char parsedseinfo[BUFSIZ];
 
 	if (seinfo) {
@@ -753,6 +799,7 @@
 		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) {
 			selinux_log(SELINUX_ERROR,
 					"%s:  Invalid targetSdkVersion passed for app with uid %d, seinfo %s, name %s\n",
@@ -849,6 +896,14 @@
 		}
 
 		if (cur->levelFrom != LEVELFROM_NONE) {
+			if (isPreinstalledApp
+					&& !check_preinstalled_app_partition(cur->partition, partition)) {
+				// TODO(b/280547417): make this an error after fixing violations
+				selinux_log(SELINUX_ERROR,
+					"%s:  App %s preinstalled to %s can't be labeled with %s sepolicy",
+					__FUNCTION__, pkgname, partition, cur->partition);
+			}
+
 			int res = set_range_from_level(ctx, cur->levelFrom, userid, appid);
 			if (res != 0) {
 				return res;