checkpolicy: libsepol: implement default type policy syntax

We currently have a mechanism in which the default user, role, and range
can be picked up from the source or the target object.  This implements
the same thing for types.  The kernel will override this with type
transition rules and similar.  This is just the default if nothing
specific is given.

Signed-off-by: Eric Paris <eparis@redhat.com>
Acked-by: Dan Walsh <dwalsh@redhat.com>
diff --git a/checkpolicy/policy_define.c b/checkpolicy/policy_define.c
index fc67737..5db1bca 100644
--- a/checkpolicy/policy_define.c
+++ b/checkpolicy/policy_define.c
@@ -415,6 +415,38 @@
 	return 0;
 }
 
+int define_default_type(int which)
+{
+	char *id;
+	class_datum_t *cladatum;
+
+	if (pass == 1) {
+		while ((id = queue_remove(id_queue)))
+			free(id);
+		return 0;
+	}
+
+	while ((id = queue_remove(id_queue))) {
+		if (!is_id_in_scope(SYM_CLASSES, id)) {
+			yyerror2("class %s is not within scope", id);
+			return -1;
+		}
+		cladatum = hashtab_search(policydbp->p_classes.table, id);
+		if (!cladatum) {
+			yyerror2("unknown class %s", id);
+			return -1;
+		}
+		if (cladatum->default_type && cladatum->default_type != which) {
+			yyerror2("conflicting default type information for class %s", id);
+			return -1;
+		}
+		cladatum->default_type = which;
+		free(id);
+	}
+
+	return 0;
+}
+
 int define_default_range(int which)
 {
 	char *id;
diff --git a/checkpolicy/policy_define.h b/checkpolicy/policy_define.h
index ccbe56f..8bfd8f6 100644
--- a/checkpolicy/policy_define.h
+++ b/checkpolicy/policy_define.h
@@ -26,6 +26,7 @@
 int define_class(void);
 int define_default_user(int which);
 int define_default_role(int which);
+int define_default_type(int which);
 int define_default_range(int which);
 int define_common_perms(void);
 int define_compute_type(int which);
diff --git a/checkpolicy/policy_parse.y b/checkpolicy/policy_parse.y
index d92cc32..b40f413 100644
--- a/checkpolicy/policy_parse.y
+++ b/checkpolicy/policy_parse.y
@@ -143,7 +143,7 @@
 %token POLICYCAP
 %token PERMISSIVE
 %token FILESYSTEM
-%token DEFAULT_USER DEFAULT_ROLE DEFAULT_RANGE
+%token DEFAULT_USER DEFAULT_ROLE DEFAULT_TYPE DEFAULT_RANGE
 %token LOW_HIGH LOW HIGH
 
 %left OR
@@ -202,9 +202,11 @@
 			;
 default_rules		: default_user_def
 			| default_role_def
+			| default_type_def
 			| default_range_def
 			| default_rules default_user_def
 			| default_rules default_role_def
+			| default_rules default_type_def
 			| default_rules default_range_def
 			;
 default_user_def	: DEFAULT_USER names SOURCE ';'
@@ -217,6 +219,11 @@
 			| DEFAULT_ROLE names TARGET ';'
 			{if (define_default_role(DEFAULT_TARGET)) return -1; }
 			;
+default_type_def	: DEFAULT_TYPE names SOURCE ';'
+			{if (define_default_type(DEFAULT_SOURCE)) return -1; }
+			| DEFAULT_TYPE names TARGET ';'
+			{if (define_default_type(DEFAULT_TARGET)) return -1; }
+			;
 default_range_def	: DEFAULT_RANGE names SOURCE LOW ';'
 			{if (define_default_range(DEFAULT_SOURCE_LOW)) return -1; }
 			| DEFAULT_RANGE names SOURCE HIGH ';'
diff --git a/checkpolicy/policy_scan.l b/checkpolicy/policy_scan.l
index 62d03f0..bba7667 100644
--- a/checkpolicy/policy_scan.l
+++ b/checkpolicy/policy_scan.l
@@ -229,6 +229,8 @@
 DEFAULT_USER			{ return(DEFAULT_USER); }
 default_role |
 DEFAULT_ROLE			{ return(DEFAULT_ROLE); }
+default_type |
+DEFAULT_TYPE			{ return(DEFAULT_TYPE); }
 default_range |
 DEFAULT_RANGE			{ return(DEFAULT_RANGE); }
 low-high |
diff --git a/libsepol/include/sepol/policydb/policydb.h b/libsepol/include/sepol/policydb/policydb.h
index f53a499..c27275e 100644
--- a/libsepol/include/sepol/policydb/policydb.h
+++ b/libsepol/include/sepol/policydb/policydb.h
@@ -116,6 +116,7 @@
 #define DEFAULT_TARGET		2
 	char default_user;
 	char default_role;
+	char default_type;
 /* Options how a new object range should be decided */
 #define DEFAULT_SOURCE_LOW	1
 #define DEFAULT_SOURCE_HIGH	2
@@ -681,10 +682,11 @@
 #define POLICYDB_VERSION_FILENAME_TRANS	25
 #define POLICYDB_VERSION_ROLETRANS	26
 #define POLICYDB_VERSION_NEW_OBJECT_DEFAULTS	27
+#define POLICYDB_VERSION_DEFAULT_TYPE	28
 
 /* Range of policy versions we understand*/
 #define POLICYDB_VERSION_MIN	POLICYDB_VERSION_BASE
-#define POLICYDB_VERSION_MAX	POLICYDB_VERSION_NEW_OBJECT_DEFAULTS
+#define POLICYDB_VERSION_MAX	POLICYDB_VERSION_DEFAULT_TYPE
 
 /* Module versions and specific changes*/
 #define MOD_POLICYDB_VERSION_BASE		4
@@ -701,9 +703,10 @@
 #define MOD_POLICYDB_VERSION_ROLEATTRIB		13
 #define MOD_POLICYDB_VERSION_TUNABLE_SEP	14
 #define MOD_POLICYDB_VERSION_NEW_OBJECT_DEFAULTS	15
+#define MOD_POLICYDB_VERSION_DEFAULT_TYPE	16
 
 #define MOD_POLICYDB_VERSION_MIN MOD_POLICYDB_VERSION_BASE
-#define MOD_POLICYDB_VERSION_MAX MOD_POLICYDB_VERSION_NEW_OBJECT_DEFAULTS
+#define MOD_POLICYDB_VERSION_MAX MOD_POLICYDB_VERSION_DEFAULT_TYPE
 
 #define POLICYDB_CONFIG_MLS    1
 
diff --git a/libsepol/src/expand.c b/libsepol/src/expand.c
index efb4f93..38def75 100644
--- a/libsepol/src/expand.c
+++ b/libsepol/src/expand.c
@@ -453,6 +453,13 @@
 		}
 		newdatum->default_role = olddatum->default_role;
 	}
+	if (olddatum->default_type) {
+		if (newdatum->default_type && olddatum->default_type != newdatum->default_type) {
+			ERR(state->handle, "Found conflicting default type definitions");
+			return SEPOL_ENOTSUP;
+		}
+		newdatum->default_type = olddatum->default_type;
+	}
 	if (olddatum->default_range) {
 		if (newdatum->default_range && olddatum->default_range != newdatum->default_range) {
 			ERR(state->handle, "Found conflicting default range definitions");
diff --git a/libsepol/src/link.c b/libsepol/src/link.c
index defab47..31b955c 100644
--- a/libsepol/src/link.c
+++ b/libsepol/src/link.c
@@ -223,6 +223,13 @@
 		}
 		newdatum->default_role = olddatum->default_role;
 	}
+	if (olddatum->default_type) {
+		if (newdatum->default_type && olddatum->default_type != newdatum->default_type) {
+			ERR(state->handle, "Found conflicting default type definitions");
+			return SEPOL_ENOTSUP;
+		}
+		newdatum->default_type = olddatum->default_type;
+	}
 	if (olddatum->default_range) {
 		if (newdatum->default_range && olddatum->default_range != newdatum->default_range) {
 			ERR(state->handle, "Found conflicting default range definitions");
diff --git a/libsepol/src/policydb.c b/libsepol/src/policydb.c
index f325f87..8ac9948 100644
--- a/libsepol/src/policydb.c
+++ b/libsepol/src/policydb.c
@@ -158,6 +158,13 @@
 	 .target_platform = SEPOL_TARGET_SELINUX,
 	},
 	{
+	 .type = POLICY_KERN,
+	 .version = POLICYDB_VERSION_DEFAULT_TYPE,
+	 .sym_num = SYM_NUM,
+	 .ocon_num = OCON_NODE6 + 1,
+	 .target_platform = SEPOL_TARGET_SELINUX,
+	},
+	{
 	 .type = POLICY_BASE,
 	 .version = MOD_POLICYDB_VERSION_BASE,
 	 .sym_num = SYM_NUM,
@@ -242,6 +249,13 @@
 	 .target_platform = SEPOL_TARGET_SELINUX,
 	},
 	{
+	 .type = POLICY_BASE,
+	 .version = MOD_POLICYDB_VERSION_DEFAULT_TYPE,
+	 .sym_num = SYM_NUM,
+	 .ocon_num = OCON_NODE6 + 1,
+	 .target_platform = SEPOL_TARGET_SELINUX,
+	},
+	{
 	 .type = POLICY_MOD,
 	 .version = MOD_POLICYDB_VERSION_BASE,
 	 .sym_num = SYM_NUM,
@@ -325,6 +339,13 @@
 	 .ocon_num = 0,
 	 .target_platform = SEPOL_TARGET_SELINUX,
 	},
+	{
+	 .type = POLICY_MOD,
+	 .version = MOD_POLICYDB_VERSION_DEFAULT_TYPE,
+	 .sym_num = SYM_NUM,
+	 .ocon_num = 0,
+	 .target_platform = SEPOL_TARGET_SELINUX,
+	},
 };
 
 #if 0
@@ -2097,6 +2118,16 @@
 		cladatum->default_range = le32_to_cpu(buf[2]);
 	}
 
+	if ((p->policy_type == POLICY_KERN &&
+	     p->policyvers >= POLICYDB_VERSION_DEFAULT_TYPE) ||
+	    (p->policy_type == POLICY_BASE &&
+	     p->policyvers >= MOD_POLICYDB_VERSION_DEFAULT_TYPE)) {
+		rc = next_entry(buf, fp, sizeof(uint32_t));
+		if (rc < 0)
+			goto bad;
+		cladatum->default_type = le32_to_cpu(buf[0]);
+	}
+
 	if (hashtab_insert(h, key, cladatum))
 		goto bad;
 
diff --git a/libsepol/src/write.c b/libsepol/src/write.c
index 9f5201b..55992f8 100644
--- a/libsepol/src/write.c
+++ b/libsepol/src/write.c
@@ -988,6 +988,16 @@
 			return POLICYDB_ERROR;
 	}
 
+	if ((p->policy_type == POLICY_KERN &&
+	     p->policyvers >= POLICYDB_VERSION_DEFAULT_TYPE) ||
+	    (p->policy_type == POLICY_BASE &&
+	     p->policyvers >= MOD_POLICYDB_VERSION_DEFAULT_TYPE)) {
+		buf[0] = cpu_to_le32(cladatum->default_type);
+		items = put_entry(buf, sizeof(uint32_t), 1, fp);
+		if (items != 1)
+			return POLICYDB_ERROR;
+	}
+
 	return POLICYDB_SUCCESS;
 }