sepolicy: add version_policy tool and version non-platform policy.

In order to support platform changes without simultaneous updates from
non-platform components, the platform and non-platform policies must be
split.  In order to provide a guarantee that policy written for
non-platform objects continues to provide the same access, all types
exposed to non-platform policy are versioned by converting them and the
policy using them into attributes.

This change performs that split, the subsequent versioning and also
generates a mapping file to glue the different policy components
together.

Test: Device boots and runs.
Bug: 31369363
Change-Id: Ibfd3eb077bd9b8e2ff3b2e6a0ca87e44d78b1317
diff --git a/tools/Android.mk b/tools/Android.mk
index 023c454..1948b7a 100644
--- a/tools/Android.mk
+++ b/tools/Android.mk
@@ -46,4 +46,17 @@
 
 include $(BUILD_HOST_EXECUTABLE)
 
+###################################
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := version_policy
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS := -Wall -Werror
+LOCAL_SRC_FILES := version_policy.c
+LOCAL_SHARED_LIBRARIES := libsepol
+LOCAL_CXX_STL := none
+
+include $(BUILD_HOST_EXECUTABLE)
+
+
 include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tools/version_policy.c b/tools/version_policy.c
new file mode 100644
index 0000000..74c9c73
--- /dev/null
+++ b/tools/version_policy.c
@@ -0,0 +1,184 @@
+/*
+ * version_policy.c - Takes the given public platform policy, a private policy
+ * and a version number to produced a combined "versioned" policy file.
+ */
+#include <errno.h>
+#include <getopt.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <cil/android.h>
+#include <cil/cil.h>
+#include <cil/cil_write_ast.h>
+
+void __attribute__ ((noreturn)) static usage(char *prog) {
+	printf("Usage: %s [OPTION]...\n", prog);
+	printf("\n");
+	printf("Options:\n");
+	printf("  -b, --base=<file>          (req'd) base policy for versioning.\n");
+	printf("  -m, --mapping              generate cil version  mapping from base policy\n");
+	printf("  -n, --number               (req'd) version number to use.\n");
+	printf("  -o, --output=<file>        write cil policy to <file>\n");
+	printf("  -t, --tgt_policy           policy to be versioned according to base policy\n");
+	printf("  -h, --help                 display usage information\n");
+	exit(1);
+}
+
+/*
+ * read_cil_file - Initialize db and parse CIL input file.
+ */
+static int read_cil_file(struct cil_db **db, char *path) {
+	int rc = SEPOL_ERR;
+	FILE *file;
+	struct stat filedata;
+	uint32_t file_size;
+	char *buff = NULL;
+
+	cil_db_init(db);
+	file = fopen(path, "re");
+	if (!file) {
+		fprintf(stderr, "Could not open file: %s\n", path);
+		goto file_err;
+	}
+	rc = stat(path, &filedata);
+	if (rc == -1) {
+		fprintf(stderr, "Could not stat file: %s - %s\n", path, strerror(errno));
+		goto err;
+	}
+	file_size = filedata.st_size;
+	buff = malloc(file_size);
+	if (buff == NULL) {
+		fprintf(stderr, "OOM!\n");
+		rc = SEPOL_ERR;
+		goto err;
+	}
+	rc = fread(buff, file_size, 1, file);
+	if (rc != 1) {
+		fprintf(stderr, "Failure reading file: %s\n", path);
+		rc = SEPOL_ERR;
+		goto err;
+	}
+	fclose(file);
+	file = NULL;
+
+	/* creates parse_tree */
+	rc = cil_add_file(*db, path, buff, file_size);
+	if (rc != SEPOL_OK) {
+		fprintf(stderr, "Failure adding %s to parse tree\n", path);
+		goto err;
+	}
+	free(buff);
+
+	return SEPOL_OK;
+err:
+	free(buff);
+	fclose(file);
+file_err:
+	cil_db_destroy(db);
+	return rc;
+}
+
+int main(int argc, char *argv[])
+{
+	int opt_char;
+	int opt_index = 0;
+	int rc = SEPOL_ERR;
+	bool mapping = false;
+	char *base = NULL;
+	char *tgt_policy = NULL;
+	char *num = NULL;
+	char *output = NULL;
+	struct cil_db *base_db = NULL;
+	struct cil_db *out_db = NULL;
+
+	static struct option long_opts[] = {
+		{"help", no_argument, 0, 'h'},
+		{"base", required_argument, 0, 'b'},
+		{"mapping", no_argument, 0, 'm'},
+		{"number", required_argument, 0, 'n'},
+		{"output", required_argument, 0, 'o'},
+		{"tgt_policy", required_argument, 0, 't'},
+		{0, 0, 0, 0}
+	};
+
+	while (1) {
+		opt_char = getopt_long(argc, argv, "b:mn:o:t:h", long_opts, &opt_index);
+		if (opt_char == -1) {
+			break;
+		}
+		switch (opt_char) {
+		case 'b':
+			base = strdup(optarg);
+			break;
+		case 'm':
+			mapping = true;
+			break;
+		case 'n':
+			num = strdup(optarg);
+			break;
+		case 'o':
+			output = strdup(optarg);
+			break;
+		case 't':
+			tgt_policy = strdup(optarg);
+			break;
+		case 'h':
+			usage(argv[0]);
+		default:
+			fprintf(stderr, "Unsupported option: %s\n", optarg);
+			usage(argv[0]);
+		}
+	}
+	if (optind < argc) {
+		fprintf(stderr, "Unknown arguments supplied\n");
+		usage(argv[0]);
+	}
+	if (num == NULL || base == NULL || (mapping == false && tgt_policy == NULL)) {
+		fprintf(stderr, "Please specify required arguments\n");
+		usage(argv[0]);
+	}
+
+	if (mapping && tgt_policy) {
+		fprintf(stderr, "Please select only one mode between --mapping and --tgt_policy\n");
+		usage(argv[0]);
+	}
+
+	/* gimme all the details */
+	cil_set_log_level(CIL_INFO);
+
+	/* read platform policy */
+	rc = read_cil_file(&base_db, base);
+	if (rc != SEPOL_OK) {
+		goto exit;
+	}
+
+	if (mapping) {
+		rc = cil_android_attrib_mapping(&out_db, base_db, num);
+		if (rc != SEPOL_OK)
+			goto exit;
+	} else {
+		/* read target policy, ready for manipulation */
+		rc = read_cil_file(&out_db, tgt_policy);
+		if (rc != SEPOL_OK) {
+			goto exit;
+		}
+		/* attributize the target policy */
+		rc = cil_android_attributize(out_db, base_db, num);
+		if (rc != SEPOL_OK) {
+			goto exit;
+		}
+	}
+	rc = cil_write_ast(out_db, output);
+	if (rc != SEPOL_OK) {
+		goto exit;
+	}
+
+exit:
+	free(base);
+	free(tgt_policy);
+	free(num);
+	free(output);
+	cil_db_destroy(&base_db);
+	cil_db_destroy(&out_db);
+	return rc;
+}