diff mbox series

[v2] libsepol,checkpolicy: support omitting unused initial sid contexts

Message ID 20200129150104.50720-1-sds@tycho.nsa.gov (mailing list archive)
State Accepted
Headers show
Series [v2] libsepol,checkpolicy: support omitting unused initial sid contexts | expand

Commit Message

Stephen Smalley Jan. 29, 2020, 3:01 p.m. UTC
Remove restrictions in libsepol and checkpolicy that required all
declared initial SIDs to be assigned a context.  With this patch,
it is possible to build and load a policy that drops the sid <sidname>
<context> declarations for the unused initial SIDs.  It is still
required to retain the sid <sidname> declarations (in the flask
definitions) in order to preserve the initial SID ordering/values.
The unused initial SIDs can be renamed, e.g. to add an unused_
prefix or similar, if desired, since the names used in the policy
are not stored in the kernel binary policy.

In CIL policies, the (sid ...) and (sidorder (...)) statements
must be left intact for compatibility but the (sidcontext ...)
statements for the unused initial SIDs can be omitted after this change.

With current kernels, if one removes an unused initial SID context
from policy, builds policy with this change applied and loads the
policy into the kernel, cat /sys/fs/selinux/initial_contexts/<sidname>
will show the unlabeled context.  With the kernel patch to remove unused
initial SIDs, the /sys/fs/selinux/initial_contexts/<sidname>
file will not be created for unused initial SIDs in the first place.

NB If an unused initial SID was assigned a context different from
the unlabeled context in existing policy, then it is not safe to
remove that initial SID context from policy and reload policy on
the running kernel that was booted with the original policy.  This
is because that kernel may have assigned that SID to various kernel
objects already and those objects will then be treated as having
the unlabeled context after the removal.  In refpolicy, examples
of such initial SIDs are the "fs" SID and the "sysctl" SID.  Even
though these initial SIDs are not directly used (in code) by the current
kernel, their contexts are being applied to filesystems and sysctl files by
policy and therefore the SIDs are being assigned to objects.

NB The "sysctl" SID was in use by the kernel up until
commit 8e6c96935fcc1ed3dbebc96fddfef3f2f2395afc ("security/selinux:
fix /proc/sys/ labeling) circa v2.6.39.  Removing its context from
policy will cause sysctl(2) or /proc/sys accesses to end up
performing permission checks against the unlabeled context and
likely encounter denials for kernels < 2.6.39.

Signed-off-by: Stephen Smalley <sds@tycho.nsa.gov>
---
v2 issues informational messages about missing initial SID contexts
so that policy developers can detect unintended inconsistencies,
and ensures that the entries are stripped before writing kernel policy
rather than accepting them when reading.  Something isn't quite right
here though; I see the messages when compiling monolithic policy with
checkpolicy but not with a modular build (even upon make validate) or
semodule -v -i of a modified base module that removes some initial
SID contexts.

 checkpolicy/test/dismod.c     |  4 ++--
 libsepol/cil/src/cil_binary.c |  4 +++-
 libsepol/cil/src/cil_verify.c |  3 +--
 libsepol/src/expand.c         | 24 ++++++++++++------------
 libsepol/src/policydb.c       |  4 ----
 libsepol/src/write.c          | 22 ++++++++++++++++++++--
 6 files changed, 38 insertions(+), 23 deletions(-)

Comments

Stephen Smalley Jan. 29, 2020, 3:15 p.m. UTC | #1
On 1/29/20 10:01 AM, Stephen Smalley wrote:
> Remove restrictions in libsepol and checkpolicy that required all
> declared initial SIDs to be assigned a context.  With this patch,
> it is possible to build and load a policy that drops the sid <sidname>
> <context> declarations for the unused initial SIDs.  It is still
> required to retain the sid <sidname> declarations (in the flask
> definitions) in order to preserve the initial SID ordering/values.
> The unused initial SIDs can be renamed, e.g. to add an unused_
> prefix or similar, if desired, since the names used in the policy
> are not stored in the kernel binary policy.
> 
> In CIL policies, the (sid ...) and (sidorder (...)) statements
> must be left intact for compatibility but the (sidcontext ...)
> statements for the unused initial SIDs can be omitted after this change.
> 
> With current kernels, if one removes an unused initial SID context
> from policy, builds policy with this change applied and loads the
> policy into the kernel, cat /sys/fs/selinux/initial_contexts/<sidname>
> will show the unlabeled context.  With the kernel patch to remove unused
> initial SIDs, the /sys/fs/selinux/initial_contexts/<sidname>
> file will not be created for unused initial SIDs in the first place.
> 
> NB If an unused initial SID was assigned a context different from
> the unlabeled context in existing policy, then it is not safe to
> remove that initial SID context from policy and reload policy on
> the running kernel that was booted with the original policy.  This
> is because that kernel may have assigned that SID to various kernel
> objects already and those objects will then be treated as having
> the unlabeled context after the removal.  In refpolicy, examples
> of such initial SIDs are the "fs" SID and the "sysctl" SID.  Even
> though these initial SIDs are not directly used (in code) by the current
> kernel, their contexts are being applied to filesystems and sysctl files by
> policy and therefore the SIDs are being assigned to objects.
> 
> NB The "sysctl" SID was in use by the kernel up until
> commit 8e6c96935fcc1ed3dbebc96fddfef3f2f2395afc ("security/selinux:
> fix /proc/sys/ labeling) circa v2.6.39.  Removing its context from
> policy will cause sysctl(2) or /proc/sys accesses to end up
> performing permission checks against the unlabeled context and
> likely encounter denials for kernels < 2.6.39.
> 
> Signed-off-by: Stephen Smalley <sds@tycho.nsa.gov>
> ---
> v2 issues informational messages about missing initial SID contexts
> so that policy developers can detect unintended inconsistencies,
> and ensures that the entries are stripped before writing kernel policy
> rather than accepting them when reading.  Something isn't quite right
> here though; I see the messages when compiling monolithic policy with
> checkpolicy but not with a modular build (even upon make validate) or
> semodule -v -i of a modified base module that removes some initial
> SID contexts.

Never mind.  For modular build I was getting the messages while 
compiling the base module (just missed it in the output) and for 
semodule I needed to turn up verbosity sufficiently to get informational 
messages out of cil.  So it works as expected AFAICT.
James Carter Feb. 4, 2020, 7:07 p.m. UTC | #2
On 1/29/20 10:01 AM, Stephen Smalley wrote:
> Remove restrictions in libsepol and checkpolicy that required all
> declared initial SIDs to be assigned a context.  With this patch,
> it is possible to build and load a policy that drops the sid <sidname>
> <context> declarations for the unused initial SIDs.  It is still
> required to retain the sid <sidname> declarations (in the flask
> definitions) in order to preserve the initial SID ordering/values.
> The unused initial SIDs can be renamed, e.g. to add an unused_
> prefix or similar, if desired, since the names used in the policy
> are not stored in the kernel binary policy.
> 
> In CIL policies, the (sid ...) and (sidorder (...)) statements
> must be left intact for compatibility but the (sidcontext ...)
> statements for the unused initial SIDs can be omitted after this change.
> 
> With current kernels, if one removes an unused initial SID context
> from policy, builds policy with this change applied and loads the
> policy into the kernel, cat /sys/fs/selinux/initial_contexts/<sidname>
> will show the unlabeled context.  With the kernel patch to remove unused
> initial SIDs, the /sys/fs/selinux/initial_contexts/<sidname>
> file will not be created for unused initial SIDs in the first place.
> 
> NB If an unused initial SID was assigned a context different from
> the unlabeled context in existing policy, then it is not safe to
> remove that initial SID context from policy and reload policy on
> the running kernel that was booted with the original policy.  This
> is because that kernel may have assigned that SID to various kernel
> objects already and those objects will then be treated as having
> the unlabeled context after the removal.  In refpolicy, examples
> of such initial SIDs are the "fs" SID and the "sysctl" SID.  Even
> though these initial SIDs are not directly used (in code) by the current
> kernel, their contexts are being applied to filesystems and sysctl files by
> policy and therefore the SIDs are being assigned to objects.
> 
> NB The "sysctl" SID was in use by the kernel up until
> commit 8e6c96935fcc1ed3dbebc96fddfef3f2f2395afc ("security/selinux:
> fix /proc/sys/ labeling) circa v2.6.39.  Removing its context from
> policy will cause sysctl(2) or /proc/sys accesses to end up
> performing permission checks against the unlabeled context and
> likely encounter denials for kernels < 2.6.39.
> 
> Signed-off-by: Stephen Smalley <sds@tycho.nsa.gov>

Acked-by: James Carter <jwcart2@tycho.nsa.gov>

> ---
> v2 issues informational messages about missing initial SID contexts
> so that policy developers can detect unintended inconsistencies,
> and ensures that the entries are stripped before writing kernel policy
> rather than accepting them when reading.  Something isn't quite right
> here though; I see the messages when compiling monolithic policy with
> checkpolicy but not with a modular build (even upon make validate) or
> semodule -v -i of a modified base module that removes some initial
> SID contexts.
> 
>   checkpolicy/test/dismod.c     |  4 ++--
>   libsepol/cil/src/cil_binary.c |  4 +++-
>   libsepol/cil/src/cil_verify.c |  3 +--
>   libsepol/src/expand.c         | 24 ++++++++++++------------
>   libsepol/src/policydb.c       |  4 ----
>   libsepol/src/write.c          | 22 ++++++++++++++++++++--
>   6 files changed, 38 insertions(+), 23 deletions(-)
> 
> diff --git a/checkpolicy/test/dismod.c b/checkpolicy/test/dismod.c
> index 8d6be2ff9522..3408e9b6b767 100644
> --- a/checkpolicy/test/dismod.c
> +++ b/checkpolicy/test/dismod.c
> @@ -444,8 +444,8 @@ void display_initial_sids(policydb_t * p, FILE * fp)
>   		user = p->p_user_val_to_name[cur->context[0].user - 1];
>   		role = p->p_role_val_to_name[cur->context[0].role - 1];
>   		type = p->p_type_val_to_name[cur->context[0].type - 1];
> -		fprintf(fp, "\t%s: sid %d, context %s:%s:%s\n",
> -			cur->u.name, cur->sid[0], user, role, type);
> +		fprintf(fp, "\tsid %d, context %s:%s:%s\n",
> +			cur->sid[0], user, role, type);
>   	}
>   #if 0
>   	fprintf(fp, "Policy Initial SIDs:\n");
> diff --git a/libsepol/cil/src/cil_binary.c b/libsepol/cil/src/cil_binary.c
> index f8e20d32f9f1..e52b6679c289 100644
> --- a/libsepol/cil/src/cil_binary.c
> +++ b/libsepol/cil/src/cil_binary.c
> @@ -3067,9 +3067,11 @@ int cil_sidorder_to_policydb(policydb_t *pdb, const struct cil_db *db)
>   		struct cil_sid *cil_sid = (struct cil_sid*)curr->data;
>   		struct cil_context *cil_context = cil_sid->context;
>   
> +		/* even if no context, we must preserve initial SID values */
> +		count++;
> +
>   		if (cil_context != NULL) {
>   			ocontext_t *new_ocon = cil_add_ocontext(&pdb->ocontexts[OCON_ISID], &tail);
> -			count++;
>   			new_ocon->sid[0] = count;
>   			new_ocon->u.name = cil_strdup(cil_sid->datum.fqn);
>   			rc = __cil_context_to_sepol_context(pdb, cil_context, &new_ocon->context[0]);
> diff --git a/libsepol/cil/src/cil_verify.c b/libsepol/cil/src/cil_verify.c
> index 018514dc1bb9..d1a6538be651 100644
> --- a/libsepol/cil/src/cil_verify.c
> +++ b/libsepol/cil/src/cil_verify.c
> @@ -439,8 +439,7 @@ int __cil_verify_initsids(struct cil_list *sids)
>   		struct cil_sid *sid = i->data;
>   		if (sid->context == NULL) {
>   			struct cil_tree_node *node = sid->datum.nodes->head->data;
> -			cil_tree_log(node, CIL_ERR, "No context assigned to SID %s declared",sid->datum.name);
> -			rc = SEPOL_ERR;
> +			cil_tree_log(node, CIL_INFO, "No context assigned to SID %s, omitting from policy",sid->datum.name);
>   		}
>   	}
>   
> diff --git a/libsepol/src/expand.c b/libsepol/src/expand.c
> index 796121cf5ad8..5738b598ee39 100644
> --- a/libsepol/src/expand.c
> +++ b/libsepol/src/expand.c
> @@ -2093,6 +2093,12 @@ static int ocontext_copy_xen(expand_state_t *state)
>   	for (i = 0; i < OCON_NUM; i++) {
>   		l = NULL;
>   		for (c = state->base->ocontexts[i]; c; c = c->next) {
> +			if (i == OCON_XEN_ISID && !c->context[0].user) {
> +				INFO(state->handle,
> +				     "No context assigned to SID %s, omitting from policy",
> +				     c->u.name);
> +				continue;
> +			}
>   			n = malloc(sizeof(ocontext_t));
>   			if (!n) {
>   				ERR(state->handle, "Out of memory!");
> @@ -2106,12 +2112,6 @@ static int ocontext_copy_xen(expand_state_t *state)
>   			l = n;
>   			switch (i) {
>   			case OCON_XEN_ISID:
> -				if (c->context[0].user == 0) {
> -					ERR(state->handle,
> -					    "Missing context for %s initial sid",
> -					    c->u.name);
> -					return -1;
> -				}
>   				n->sid[0] = c->sid[0];
>   				break;
>   			case OCON_XEN_PIRQ:
> @@ -2159,6 +2159,12 @@ static int ocontext_copy_selinux(expand_state_t *state)
>   	for (i = 0; i < OCON_NUM; i++) {
>   		l = NULL;
>   		for (c = state->base->ocontexts[i]; c; c = c->next) {
> +			if (i == OCON_ISID && !c->context[0].user) {
> +				INFO(state->handle,
> +				     "No context assigned to SID %s, omitting from policy",
> +				     c->u.name);
> +				continue;
> +			}
>   			n = malloc(sizeof(ocontext_t));
>   			if (!n) {
>   				ERR(state->handle, "Out of memory!");
> @@ -2172,12 +2178,6 @@ static int ocontext_copy_selinux(expand_state_t *state)
>   			l = n;
>   			switch (i) {
>   			case OCON_ISID:
> -				if (c->context[0].user == 0) {
> -					ERR(state->handle,
> -					    "Missing context for %s initial sid",
> -					    c->u.name);
> -					return -1;
> -				}
>   				n->sid[0] = c->sid[0];
>   				break;
>   			case OCON_FS:	/* FALLTHROUGH */
> diff --git a/libsepol/src/policydb.c b/libsepol/src/policydb.c
> index 745e546baa3a..5b289a523a94 100644
> --- a/libsepol/src/policydb.c
> +++ b/libsepol/src/policydb.c
> @@ -1610,10 +1610,6 @@ int policydb_load_isids(policydb_t * p, sidtab_t * s)
>   
>   	head = p->ocontexts[OCON_ISID];
>   	for (c = head; c; c = c->next) {
> -		if (!c->context[0].user) {
> -			ERR(NULL, "SID %s was never defined", c->u.name);
> -			return -1;
> -		}
>   		if (sepol_sidtab_insert(s, c->sid[0], &c->context[0])) {
>   			ERR(NULL, "unable to load initial SID %s", c->u.name);
>   			return -1;
> diff --git a/libsepol/src/write.c b/libsepol/src/write.c
> index 7e634510d038..1fd6a16a248b 100644
> --- a/libsepol/src/write.c
> +++ b/libsepol/src/write.c
> @@ -1293,8 +1293,15 @@ static int ocontext_write_xen(struct policydb_compat_info *info, policydb_t *p,
>   	ocontext_t *c;
>   	for (i = 0; i < info->ocon_num; i++) {
>   		nel = 0;
> -		for (c = p->ocontexts[i]; c; c = c->next)
> +		for (c = p->ocontexts[i]; c; c = c->next) {
> +			if (i == OCON_XEN_ISID && !c->context[0].user) {
> +				INFO(fp->handle,
> +				     "No context assigned to SID %s, omitting from policy",
> +				     c->u.name);
> +				continue;
> +			}
>   			nel++;
> +		}
>   		buf[0] = cpu_to_le32(nel);
>   		items = put_entry(buf, sizeof(uint32_t), 1, fp);
>   		if (items != 1)
> @@ -1302,6 +1309,8 @@ static int ocontext_write_xen(struct policydb_compat_info *info, policydb_t *p,
>   		for (c = p->ocontexts[i]; c; c = c->next) {
>   			switch (i) {
>   			case OCON_XEN_ISID:
> +				if (!c->context[0].user)
> +					break;
>   				buf[0] = cpu_to_le32(c->sid[0]);
>   				items = put_entry(buf, sizeof(uint32_t), 1, fp);
>   				if (items != 1)
> @@ -1392,8 +1401,15 @@ static int ocontext_write_selinux(struct policydb_compat_info *info,
>   	ocontext_t *c;
>   	for (i = 0; i < info->ocon_num; i++) {
>   		nel = 0;
> -		for (c = p->ocontexts[i]; c; c = c->next)
> +		for (c = p->ocontexts[i]; c; c = c->next) {
> +			if (i == OCON_ISID && !c->context[0].user) {
> +				INFO(fp->handle,
> +				     "No context assigned to SID %s, omitting from policy",
> +				     c->u.name);
> +				continue;
> +			}
>   			nel++;
> +		}
>   		buf[0] = cpu_to_le32(nel);
>   		items = put_entry(buf, sizeof(uint32_t), 1, fp);
>   		if (items != 1)
> @@ -1401,6 +1417,8 @@ static int ocontext_write_selinux(struct policydb_compat_info *info,
>   		for (c = p->ocontexts[i]; c; c = c->next) {
>   			switch (i) {
>   			case OCON_ISID:
> +				if (!c->context[0].user)
> +					break;
>   				buf[0] = cpu_to_le32(c->sid[0]);
>   				items = put_entry(buf, sizeof(uint32_t), 1, fp);
>   				if (items != 1)
>
Stephen Smalley Feb. 6, 2020, 3:46 p.m. UTC | #3
On 1/29/20 10:01 AM, Stephen Smalley wrote:
> Remove restrictions in libsepol and checkpolicy that required all
> declared initial SIDs to be assigned a context.  With this patch,
> it is possible to build and load a policy that drops the sid <sidname>
> <context> declarations for the unused initial SIDs.  It is still
> required to retain the sid <sidname> declarations (in the flask
> definitions) in order to preserve the initial SID ordering/values.
> The unused initial SIDs can be renamed, e.g. to add an unused_
> prefix or similar, if desired, since the names used in the policy
> are not stored in the kernel binary policy.
> 
> In CIL policies, the (sid ...) and (sidorder (...)) statements
> must be left intact for compatibility but the (sidcontext ...)
> statements for the unused initial SIDs can be omitted after this change.
> 
> With current kernels, if one removes an unused initial SID context
> from policy, builds policy with this change applied and loads the
> policy into the kernel, cat /sys/fs/selinux/initial_contexts/<sidname>
> will show the unlabeled context.  With the kernel patch to remove unused
> initial SIDs, the /sys/fs/selinux/initial_contexts/<sidname>
> file will not be created for unused initial SIDs in the first place.
> 
> NB If an unused initial SID was assigned a context different from
> the unlabeled context in existing policy, then it is not safe to
> remove that initial SID context from policy and reload policy on
> the running kernel that was booted with the original policy.  This
> is because that kernel may have assigned that SID to various kernel
> objects already and those objects will then be treated as having
> the unlabeled context after the removal.  In refpolicy, examples
> of such initial SIDs are the "fs" SID and the "sysctl" SID.  Even
> though these initial SIDs are not directly used (in code) by the current
> kernel, their contexts are being applied to filesystems and sysctl files by
> policy and therefore the SIDs are being assigned to objects.
> 
> NB The "sysctl" SID was in use by the kernel up until
> commit 8e6c96935fcc1ed3dbebc96fddfef3f2f2395afc ("security/selinux:
> fix /proc/sys/ labeling) circa v2.6.39.  Removing its context from
> policy will cause sysctl(2) or /proc/sys accesses to end up
> performing permission checks against the unlabeled context and
> likely encounter denials for kernels < 2.6.39.
> 
> Signed-off-by: Stephen Smalley <sds@tycho.nsa.gov>
> ---

This is now applied.

[...]
diff mbox series

Patch

diff --git a/checkpolicy/test/dismod.c b/checkpolicy/test/dismod.c
index 8d6be2ff9522..3408e9b6b767 100644
--- a/checkpolicy/test/dismod.c
+++ b/checkpolicy/test/dismod.c
@@ -444,8 +444,8 @@  void display_initial_sids(policydb_t * p, FILE * fp)
 		user = p->p_user_val_to_name[cur->context[0].user - 1];
 		role = p->p_role_val_to_name[cur->context[0].role - 1];
 		type = p->p_type_val_to_name[cur->context[0].type - 1];
-		fprintf(fp, "\t%s: sid %d, context %s:%s:%s\n",
-			cur->u.name, cur->sid[0], user, role, type);
+		fprintf(fp, "\tsid %d, context %s:%s:%s\n",
+			cur->sid[0], user, role, type);
 	}
 #if 0
 	fprintf(fp, "Policy Initial SIDs:\n");
diff --git a/libsepol/cil/src/cil_binary.c b/libsepol/cil/src/cil_binary.c
index f8e20d32f9f1..e52b6679c289 100644
--- a/libsepol/cil/src/cil_binary.c
+++ b/libsepol/cil/src/cil_binary.c
@@ -3067,9 +3067,11 @@  int cil_sidorder_to_policydb(policydb_t *pdb, const struct cil_db *db)
 		struct cil_sid *cil_sid = (struct cil_sid*)curr->data;
 		struct cil_context *cil_context = cil_sid->context;
 
+		/* even if no context, we must preserve initial SID values */
+		count++;
+
 		if (cil_context != NULL) {
 			ocontext_t *new_ocon = cil_add_ocontext(&pdb->ocontexts[OCON_ISID], &tail);
-			count++;
 			new_ocon->sid[0] = count;
 			new_ocon->u.name = cil_strdup(cil_sid->datum.fqn);
 			rc = __cil_context_to_sepol_context(pdb, cil_context, &new_ocon->context[0]);
diff --git a/libsepol/cil/src/cil_verify.c b/libsepol/cil/src/cil_verify.c
index 018514dc1bb9..d1a6538be651 100644
--- a/libsepol/cil/src/cil_verify.c
+++ b/libsepol/cil/src/cil_verify.c
@@ -439,8 +439,7 @@  int __cil_verify_initsids(struct cil_list *sids)
 		struct cil_sid *sid = i->data;
 		if (sid->context == NULL) {
 			struct cil_tree_node *node = sid->datum.nodes->head->data;
-			cil_tree_log(node, CIL_ERR, "No context assigned to SID %s declared",sid->datum.name);
-			rc = SEPOL_ERR;
+			cil_tree_log(node, CIL_INFO, "No context assigned to SID %s, omitting from policy",sid->datum.name);
 		}
 	}
 
diff --git a/libsepol/src/expand.c b/libsepol/src/expand.c
index 796121cf5ad8..5738b598ee39 100644
--- a/libsepol/src/expand.c
+++ b/libsepol/src/expand.c
@@ -2093,6 +2093,12 @@  static int ocontext_copy_xen(expand_state_t *state)
 	for (i = 0; i < OCON_NUM; i++) {
 		l = NULL;
 		for (c = state->base->ocontexts[i]; c; c = c->next) {
+			if (i == OCON_XEN_ISID && !c->context[0].user) {
+				INFO(state->handle,
+				     "No context assigned to SID %s, omitting from policy",
+				     c->u.name);
+				continue;
+			}
 			n = malloc(sizeof(ocontext_t));
 			if (!n) {
 				ERR(state->handle, "Out of memory!");
@@ -2106,12 +2112,6 @@  static int ocontext_copy_xen(expand_state_t *state)
 			l = n;
 			switch (i) {
 			case OCON_XEN_ISID:
-				if (c->context[0].user == 0) {
-					ERR(state->handle,
-					    "Missing context for %s initial sid",
-					    c->u.name);
-					return -1;
-				}
 				n->sid[0] = c->sid[0];
 				break;
 			case OCON_XEN_PIRQ:
@@ -2159,6 +2159,12 @@  static int ocontext_copy_selinux(expand_state_t *state)
 	for (i = 0; i < OCON_NUM; i++) {
 		l = NULL;
 		for (c = state->base->ocontexts[i]; c; c = c->next) {
+			if (i == OCON_ISID && !c->context[0].user) {
+				INFO(state->handle,
+				     "No context assigned to SID %s, omitting from policy",
+				     c->u.name);
+				continue;
+			}
 			n = malloc(sizeof(ocontext_t));
 			if (!n) {
 				ERR(state->handle, "Out of memory!");
@@ -2172,12 +2178,6 @@  static int ocontext_copy_selinux(expand_state_t *state)
 			l = n;
 			switch (i) {
 			case OCON_ISID:
-				if (c->context[0].user == 0) {
-					ERR(state->handle,
-					    "Missing context for %s initial sid",
-					    c->u.name);
-					return -1;
-				}
 				n->sid[0] = c->sid[0];
 				break;
 			case OCON_FS:	/* FALLTHROUGH */
diff --git a/libsepol/src/policydb.c b/libsepol/src/policydb.c
index 745e546baa3a..5b289a523a94 100644
--- a/libsepol/src/policydb.c
+++ b/libsepol/src/policydb.c
@@ -1610,10 +1610,6 @@  int policydb_load_isids(policydb_t * p, sidtab_t * s)
 
 	head = p->ocontexts[OCON_ISID];
 	for (c = head; c; c = c->next) {
-		if (!c->context[0].user) {
-			ERR(NULL, "SID %s was never defined", c->u.name);
-			return -1;
-		}
 		if (sepol_sidtab_insert(s, c->sid[0], &c->context[0])) {
 			ERR(NULL, "unable to load initial SID %s", c->u.name);
 			return -1;
diff --git a/libsepol/src/write.c b/libsepol/src/write.c
index 7e634510d038..1fd6a16a248b 100644
--- a/libsepol/src/write.c
+++ b/libsepol/src/write.c
@@ -1293,8 +1293,15 @@  static int ocontext_write_xen(struct policydb_compat_info *info, policydb_t *p,
 	ocontext_t *c;
 	for (i = 0; i < info->ocon_num; i++) {
 		nel = 0;
-		for (c = p->ocontexts[i]; c; c = c->next)
+		for (c = p->ocontexts[i]; c; c = c->next) {
+			if (i == OCON_XEN_ISID && !c->context[0].user) {
+				INFO(fp->handle,
+				     "No context assigned to SID %s, omitting from policy",
+				     c->u.name);
+				continue;
+			}
 			nel++;
+		}
 		buf[0] = cpu_to_le32(nel);
 		items = put_entry(buf, sizeof(uint32_t), 1, fp);
 		if (items != 1)
@@ -1302,6 +1309,8 @@  static int ocontext_write_xen(struct policydb_compat_info *info, policydb_t *p,
 		for (c = p->ocontexts[i]; c; c = c->next) {
 			switch (i) {
 			case OCON_XEN_ISID:
+				if (!c->context[0].user)
+					break;
 				buf[0] = cpu_to_le32(c->sid[0]);
 				items = put_entry(buf, sizeof(uint32_t), 1, fp);
 				if (items != 1)
@@ -1392,8 +1401,15 @@  static int ocontext_write_selinux(struct policydb_compat_info *info,
 	ocontext_t *c;
 	for (i = 0; i < info->ocon_num; i++) {
 		nel = 0;
-		for (c = p->ocontexts[i]; c; c = c->next)
+		for (c = p->ocontexts[i]; c; c = c->next) {
+			if (i == OCON_ISID && !c->context[0].user) {
+				INFO(fp->handle,
+				     "No context assigned to SID %s, omitting from policy",
+				     c->u.name);
+				continue;
+			}
 			nel++;
+		}
 		buf[0] = cpu_to_le32(nel);
 		items = put_entry(buf, sizeof(uint32_t), 1, fp);
 		if (items != 1)
@@ -1401,6 +1417,8 @@  static int ocontext_write_selinux(struct policydb_compat_info *info,
 		for (c = p->ocontexts[i]; c; c = c->next) {
 			switch (i) {
 			case OCON_ISID:
+				if (!c->context[0].user)
+					break;
 				buf[0] = cpu_to_le32(c->sid[0]);
 				items = put_entry(buf, sizeof(uint32_t), 1, fp);
 				if (items != 1)