diff mbox series

[1/3] libsepol: add support for xperms in conditional policies

Message ID 20241023172044.98572-1-cgoettsche@seltendoof.de (mailing list archive)
State Accepted
Commit 438b16d17799
Delegated to: Petr Lautrbach
Headers show
Series [1/3] libsepol: add support for xperms in conditional policies | expand

Commit Message

Christian Göttsche Oct. 23, 2024, 5:20 p.m. UTC
From: Christian Göttsche <cgzones@googlemail.com>

Add support for extended permission rules in conditional policies by
adding a new policy version and adjusting writing and validating
policies accordingly.

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/include/sepol/policydb/policydb.h | 12 +++++-
 libsepol/src/policydb.c                    | 21 ++++++++++
 libsepol/src/policydb_validate.c           |  4 +-
 libsepol/src/write.c                       | 46 +++++++++++++++-------
 4 files changed, 64 insertions(+), 19 deletions(-)

Comments

James Carter Oct. 23, 2024, 9:03 p.m. UTC | #1
On Wed, Oct 23, 2024 at 1:21 PM Christian Göttsche
<cgoettsche@seltendoof.de> wrote:
>
> From: Christian Göttsche <cgzones@googlemail.com>
>
> Add support for extended permission rules in conditional policies by
> adding a new policy version and adjusting writing and validating
> policies accordingly.


Some changes are going to be needed to the assertion checking for this.

optional work as expected.
boolean if's work if everything is in one branch, but problems occur
if they are not.

A couple of examples that fail

if bb04 {
  allow tp41 tpx : clx ioctl;
} else {
  allowxperm tp41 tpx : clx ioctl 0x1101;
}
neverallowxperm tp41 tpx : clx ioctl 0x4101;
This should be an assertion failure, but is not.

allow tp51 tpx : clx ioctl;
if bb05 {
  allowxperm tp51 tpx : clx ioctl 0x1101;
} else {
  allowxperm tp51 tpx : clx ioctl 0x1101;
}
neverallowxperm tp51 tpx : clx ioctl 0x4101;
This should not be an assertion failure, but is.

Thanks,
Jim


>
> Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
> ---
>  libsepol/include/sepol/policydb/policydb.h | 12 +++++-
>  libsepol/src/policydb.c                    | 21 ++++++++++
>  libsepol/src/policydb_validate.c           |  4 +-
>  libsepol/src/write.c                       | 46 +++++++++++++++-------
>  4 files changed, 64 insertions(+), 19 deletions(-)
>
> diff --git a/libsepol/include/sepol/policydb/policydb.h b/libsepol/include/sepol/policydb/policydb.h
> index f73e21fc..ccff4d71 100644
> --- a/libsepol/include/sepol/policydb/policydb.h
> +++ b/libsepol/include/sepol/policydb/policydb.h
> @@ -759,10 +759,11 @@ extern int policydb_set_target_platform(policydb_t *p, int platform);
>  #define POLICYDB_VERSION_INFINIBAND            31 /* Linux-specific */
>  #define POLICYDB_VERSION_GLBLUB                32
>  #define POLICYDB_VERSION_COMP_FTRANS   33 /* compressed filename transitions */
> +#define POLICYDB_VERSION_COND_XPERMS   34 /* extended permissions in conditional policies */
>
>  /* Range of policy versions we understand*/
>  #define POLICYDB_VERSION_MIN   POLICYDB_VERSION_BASE
> -#define POLICYDB_VERSION_MAX   POLICYDB_VERSION_COMP_FTRANS
> +#define POLICYDB_VERSION_MAX   POLICYDB_VERSION_COND_XPERMS
>
>  /* Module versions and specific changes*/
>  #define MOD_POLICYDB_VERSION_BASE              4
> @@ -785,9 +786,10 @@ extern int policydb_set_target_platform(policydb_t *p, int platform);
>  #define MOD_POLICYDB_VERSION_INFINIBAND                19
>  #define MOD_POLICYDB_VERSION_GLBLUB            20
>  #define MOD_POLICYDB_VERSION_SELF_TYPETRANS    21
> +#define MOD_POLICYDB_VERSION_COND_XPERMS       22
>
>  #define MOD_POLICYDB_VERSION_MIN MOD_POLICYDB_VERSION_BASE
> -#define MOD_POLICYDB_VERSION_MAX MOD_POLICYDB_VERSION_SELF_TYPETRANS
> +#define MOD_POLICYDB_VERSION_MAX MOD_POLICYDB_VERSION_COND_XPERMS
>
>  #define POLICYDB_CONFIG_MLS    1
>
> @@ -801,6 +803,12 @@ extern int policydb_set_target_platform(policydb_t *p, int platform);
>          ((p)->policy_type != POLICY_KERN                       \
>           && (p)->policyvers >= MOD_POLICYDB_VERSION_BOUNDARY))
>
> +#define policydb_has_cond_xperms_feature(p)                    \
> +       (((p)->policy_type == POLICY_KERN                       \
> +         && (p)->policyvers >= POLICYDB_VERSION_COND_XPERMS) ||        \
> +        ((p)->policy_type != POLICY_KERN                       \
> +         && (p)->policyvers >= MOD_POLICYDB_VERSION_COND_XPERMS))
> +
>  /* the config flags related to unknown classes/perms are bits 2 and 3 */
>  #define DENY_UNKNOWN   SEPOL_DENY_UNKNOWN
>  #define REJECT_UNKNOWN SEPOL_REJECT_UNKNOWN
> diff --git a/libsepol/src/policydb.c b/libsepol/src/policydb.c
> index e90ccca1..0747e789 100644
> --- a/libsepol/src/policydb.c
> +++ b/libsepol/src/policydb.c
> @@ -208,6 +208,13 @@ static const struct policydb_compat_info policydb_compat[] = {
>          .ocon_num = OCON_IBENDPORT + 1,
>          .target_platform = SEPOL_TARGET_SELINUX,
>         },
> +       {
> +        .type = POLICY_KERN,
> +        .version = POLICYDB_VERSION_COND_XPERMS,
> +        .sym_num = SYM_NUM,
> +        .ocon_num = OCON_IBENDPORT + 1,
> +        .target_platform = SEPOL_TARGET_SELINUX,
> +       },
>         {
>          .type = POLICY_BASE,
>          .version = MOD_POLICYDB_VERSION_BASE,
> @@ -334,6 +341,13 @@ static const struct policydb_compat_info policydb_compat[] = {
>          .ocon_num = OCON_IBENDPORT + 1,
>          .target_platform = SEPOL_TARGET_SELINUX,
>         },
> +       {
> +        .type = POLICY_BASE,
> +        .version = MOD_POLICYDB_VERSION_COND_XPERMS,
> +        .sym_num = SYM_NUM,
> +        .ocon_num = OCON_IBENDPORT + 1,
> +        .target_platform = SEPOL_TARGET_SELINUX,
> +       },
>         {
>          .type = POLICY_MOD,
>          .version = MOD_POLICYDB_VERSION_BASE,
> @@ -460,6 +474,13 @@ static const struct policydb_compat_info policydb_compat[] = {
>          .ocon_num = 0,
>          .target_platform = SEPOL_TARGET_SELINUX,
>         },
> +       {
> +        .type = POLICY_MOD,
> +        .version = MOD_POLICYDB_VERSION_COND_XPERMS,
> +        .sym_num = SYM_NUM,
> +        .ocon_num = 0,
> +        .target_platform = SEPOL_TARGET_SELINUX,
> +       },
>  };
>
>  #if 0
> diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
> index 5035313b..e021e025 100644
> --- a/libsepol/src/policydb_validate.c
> +++ b/libsepol/src/policydb_validate.c
> @@ -903,7 +903,7 @@ static int validate_avtab_key(const avtab_key_t *key, int conditional, const pol
>         case AVTAB_XPERMS_DONTAUDIT:
>                 if (p->target_platform != SEPOL_TARGET_SELINUX)
>                         goto bad;
> -               if (conditional)
> +               if (conditional && !policydb_has_cond_xperms_feature(p))
>                         goto bad;
>                 break;
>         default:
> @@ -1046,7 +1046,7 @@ static int validate_avrules(sepol_handle_t *handle, const avrule_t *avrule, int
>                 case AVRULE_XPERMS_AUDITALLOW:
>                 case AVRULE_XPERMS_DONTAUDIT:
>                 case AVRULE_XPERMS_NEVERALLOW:
> -                       if (conditional)
> +                       if (conditional && !policydb_has_cond_xperms_feature(p))
>                                 goto bad;
>                         break;
>                 default:
> diff --git a/libsepol/src/write.c b/libsepol/src/write.c
> index a52e2e82..4ef98449 100644
> --- a/libsepol/src/write.c
> +++ b/libsepol/src/write.c
> @@ -56,7 +56,8 @@ struct policy_data {
>  };
>
>  static int avrule_write_list(policydb_t *p,
> -                            avrule_t * avrules, struct policy_file *fp);
> +                            avrule_t * avrules, struct policy_file *fp,
> +                            unsigned conditional);
>
>  static int ebitmap_write(ebitmap_t * e, struct policy_file *fp)
>  {
> @@ -104,7 +105,8 @@ static uint16_t spec_order[] = {
>
>  static int avtab_write_item(policydb_t * p,
>                             avtab_ptr_t cur, struct policy_file *fp,
> -                           unsigned merge, unsigned commit, uint32_t * nel)
> +                           unsigned merge, unsigned commit, unsigned conditional,
> +                           uint32_t * nel)
>  {
>         avtab_ptr_t node;
>         uint8_t buf8;
> @@ -229,14 +231,20 @@ static int avtab_write_item(policydb_t * p,
>                 return POLICYDB_ERROR;
>         if ((p->policyvers < POLICYDB_VERSION_XPERMS_IOCTL) &&
>                         (cur->key.specified & AVTAB_XPERMS)) {
> -               ERR(fp->handle, "policy version %u does not support ioctl extended"
> +               ERR(fp->handle, "policy version %u does not support extended"
>                                 "permissions rules and one was specified", p->policyvers);
>                 return POLICYDB_ERROR;
>         }
>
> +       if (!policydb_has_cond_xperms_feature(p) && (cur->key.specified & AVTAB_XPERMS) && conditional) {
> +               ERR(fp->handle, "policy version %u does not support extended"
> +                               "permissions rules in conditional policies and one was specified", p->policyvers);
> +               return POLICYDB_ERROR;
> +       }
> +
>         if (p->target_platform != SEPOL_TARGET_SELINUX &&
>                         (cur->key.specified & AVTAB_XPERMS)) {
> -               ERR(fp->handle, "Target platform %s does not support ioctl "
> +               ERR(fp->handle, "Target platform %s does not support "
>                                 "extended permissions rules and one was specified",
>                                 policydb_target_strings[p->target_platform]);
>                 return POLICYDB_ERROR;
> @@ -313,7 +321,7 @@ static int avtab_write(struct policydb *p, avtab_t * a, struct policy_file *fp)
>                 for (cur = a->htable[i]; cur; cur = cur->next) {
>                         /* If old format, compute final nel.
>                            If new format, write out the items. */
> -                       if (avtab_write_item(p, cur, fp, 1, !oldvers, &nel)) {
> +                       if (avtab_write_item(p, cur, fp, 1, !oldvers, 0, &nel)) {
>                                 rc = -1;
>                                 goto out;
>                         }
> @@ -332,7 +340,7 @@ static int avtab_write(struct policydb *p, avtab_t * a, struct policy_file *fp)
>                 avtab_reset_merged(a);
>                 for (i = 0; i < a->nslot; i++) {
>                         for (cur = a->htable[i]; cur; cur = cur->next) {
> -                               if (avtab_write_item(p, cur, fp, 1, 1, NULL)) {
> +                               if (avtab_write_item(p, cur, fp, 1, 1, 0, NULL)) {
>                                         rc = -1;
>                                         goto out;
>                                 }
> @@ -795,7 +803,7 @@ static int cond_write_av_list(policydb_t * p,
>
>         for (cur_list = list; cur_list != NULL; cur_list = cur_list->next) {
>                 if (cur_list->node->parse_context)
> -                       if (avtab_write_item(p, cur_list->node, fp, 0, 1, NULL))
> +                       if (avtab_write_item(p, cur_list->node, fp, 0, 1, 1, NULL))
>                                 goto out;
>         }
>
> @@ -846,9 +854,9 @@ static int cond_write_node(policydb_t * p,
>                 if (cond_write_av_list(p, node->false_list, fp) != 0)
>                         return POLICYDB_ERROR;
>         } else {
> -               if (avrule_write_list(p, node->avtrue_list, fp))
> +               if (avrule_write_list(p, node->avtrue_list, fp, 1))
>                         return POLICYDB_ERROR;
> -               if (avrule_write_list(p, node->avfalse_list, fp))
> +               if (avrule_write_list(p, node->avfalse_list, fp, 1))
>                         return POLICYDB_ERROR;
>         }
>
> @@ -1743,7 +1751,7 @@ static int range_write(policydb_t * p, struct policy_file *fp)
>  /************** module writing functions below **************/
>
>  static int avrule_write(policydb_t *p, avrule_t * avrule,
> -                       struct policy_file *fp)
> +                       struct policy_file *fp, unsigned conditional)
>  {
>         size_t items, items2;
>         uint32_t buf[32], len;
> @@ -1801,15 +1809,23 @@ static int avrule_write(policydb_t *p, avrule_t * avrule,
>
>                 if (p->policyvers < MOD_POLICYDB_VERSION_XPERMS_IOCTL) {
>                         ERR(fp->handle,
> -                           "module policy version %u does not support ioctl"
> +                           "module policy version %u does not support"
>                             " extended permissions rules and one was specified",
>                             p->policyvers);
>                         return POLICYDB_ERROR;
>                 }
>
> +               if (conditional && !policydb_has_cond_xperms_feature(p)) {
> +                       ERR(fp->handle,
> +                           "module policy version %u does not support"
> +                           " extended permissions rules in conditional policies and one was specified",
> +                           p->policyvers);
> +                       return POLICYDB_ERROR;
> +               }
> +
>                 if (p->target_platform != SEPOL_TARGET_SELINUX) {
>                         ERR(fp->handle,
> -                           "Target platform %s does not support ioctl"
> +                           "Target platform %s does not support"
>                             " extended permissions rules and one was specified",
>                             policydb_target_strings[p->target_platform]);
>                         return POLICYDB_ERROR;
> @@ -1834,7 +1850,7 @@ static int avrule_write(policydb_t *p, avrule_t * avrule,
>  }
>
>  static int avrule_write_list(policydb_t *p, avrule_t * avrules,
> -                            struct policy_file *fp)
> +                            struct policy_file *fp, unsigned conditional)
>  {
>         uint32_t buf[32], len;
>         avrule_t *avrule;
> @@ -1852,7 +1868,7 @@ static int avrule_write_list(policydb_t *p, avrule_t * avrules,
>
>         avrule = avrules;
>         while (avrule) {
> -               if (avrule_write(p, avrule, fp))
> +               if (avrule_write(p, avrule, fp, conditional))
>                         return POLICYDB_ERROR;
>                 avrule = avrule->next;
>         }
> @@ -2056,7 +2072,7 @@ static int avrule_decl_write(avrule_decl_t * decl, int num_scope_syms,
>                 return POLICYDB_ERROR;
>         }
>         if (cond_write_list(p, decl->cond_list, fp) == -1 ||
> -           avrule_write_list(p, decl->avrules, fp) == -1 ||
> +           avrule_write_list(p, decl->avrules, fp, 0) == -1 ||
>             role_trans_rule_write(p, decl->role_tr_rules, fp) == -1 ||
>             role_allow_rule_write(decl->role_allow_rules, fp) == -1) {
>                 return POLICYDB_ERROR;
> --
> 2.45.2
>
>
diff mbox series

Patch

diff --git a/libsepol/include/sepol/policydb/policydb.h b/libsepol/include/sepol/policydb/policydb.h
index f73e21fc..ccff4d71 100644
--- a/libsepol/include/sepol/policydb/policydb.h
+++ b/libsepol/include/sepol/policydb/policydb.h
@@ -759,10 +759,11 @@  extern int policydb_set_target_platform(policydb_t *p, int platform);
 #define POLICYDB_VERSION_INFINIBAND		31 /* Linux-specific */
 #define POLICYDB_VERSION_GLBLUB		32
 #define POLICYDB_VERSION_COMP_FTRANS	33 /* compressed filename transitions */
+#define POLICYDB_VERSION_COND_XPERMS	34 /* extended permissions in conditional policies */
 
 /* Range of policy versions we understand*/
 #define POLICYDB_VERSION_MIN	POLICYDB_VERSION_BASE
-#define POLICYDB_VERSION_MAX	POLICYDB_VERSION_COMP_FTRANS
+#define POLICYDB_VERSION_MAX	POLICYDB_VERSION_COND_XPERMS
 
 /* Module versions and specific changes*/
 #define MOD_POLICYDB_VERSION_BASE		4
@@ -785,9 +786,10 @@  extern int policydb_set_target_platform(policydb_t *p, int platform);
 #define MOD_POLICYDB_VERSION_INFINIBAND		19
 #define MOD_POLICYDB_VERSION_GLBLUB		20
 #define MOD_POLICYDB_VERSION_SELF_TYPETRANS	21
+#define MOD_POLICYDB_VERSION_COND_XPERMS	22
 
 #define MOD_POLICYDB_VERSION_MIN MOD_POLICYDB_VERSION_BASE
-#define MOD_POLICYDB_VERSION_MAX MOD_POLICYDB_VERSION_SELF_TYPETRANS
+#define MOD_POLICYDB_VERSION_MAX MOD_POLICYDB_VERSION_COND_XPERMS
 
 #define POLICYDB_CONFIG_MLS    1
 
@@ -801,6 +803,12 @@  extern int policydb_set_target_platform(policydb_t *p, int platform);
 	 ((p)->policy_type != POLICY_KERN			\
 	  && (p)->policyvers >= MOD_POLICYDB_VERSION_BOUNDARY))
 
+#define policydb_has_cond_xperms_feature(p)			\
+	(((p)->policy_type == POLICY_KERN			\
+	  && (p)->policyvers >= POLICYDB_VERSION_COND_XPERMS) ||	\
+	 ((p)->policy_type != POLICY_KERN			\
+	  && (p)->policyvers >= MOD_POLICYDB_VERSION_COND_XPERMS))
+
 /* the config flags related to unknown classes/perms are bits 2 and 3 */
 #define DENY_UNKNOWN	SEPOL_DENY_UNKNOWN
 #define REJECT_UNKNOWN	SEPOL_REJECT_UNKNOWN
diff --git a/libsepol/src/policydb.c b/libsepol/src/policydb.c
index e90ccca1..0747e789 100644
--- a/libsepol/src/policydb.c
+++ b/libsepol/src/policydb.c
@@ -208,6 +208,13 @@  static const struct policydb_compat_info policydb_compat[] = {
 	 .ocon_num = OCON_IBENDPORT + 1,
 	 .target_platform = SEPOL_TARGET_SELINUX,
 	},
+	{
+	 .type = POLICY_KERN,
+	 .version = POLICYDB_VERSION_COND_XPERMS,
+	 .sym_num = SYM_NUM,
+	 .ocon_num = OCON_IBENDPORT + 1,
+	 .target_platform = SEPOL_TARGET_SELINUX,
+	},
 	{
 	 .type = POLICY_BASE,
 	 .version = MOD_POLICYDB_VERSION_BASE,
@@ -334,6 +341,13 @@  static const struct policydb_compat_info policydb_compat[] = {
 	 .ocon_num = OCON_IBENDPORT + 1,
 	 .target_platform = SEPOL_TARGET_SELINUX,
 	},
+	{
+	 .type = POLICY_BASE,
+	 .version = MOD_POLICYDB_VERSION_COND_XPERMS,
+	 .sym_num = SYM_NUM,
+	 .ocon_num = OCON_IBENDPORT + 1,
+	 .target_platform = SEPOL_TARGET_SELINUX,
+	},
 	{
 	 .type = POLICY_MOD,
 	 .version = MOD_POLICYDB_VERSION_BASE,
@@ -460,6 +474,13 @@  static const struct policydb_compat_info policydb_compat[] = {
 	 .ocon_num = 0,
 	 .target_platform = SEPOL_TARGET_SELINUX,
 	},
+	{
+	 .type = POLICY_MOD,
+	 .version = MOD_POLICYDB_VERSION_COND_XPERMS,
+	 .sym_num = SYM_NUM,
+	 .ocon_num = 0,
+	 .target_platform = SEPOL_TARGET_SELINUX,
+	},
 };
 
 #if 0
diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
index 5035313b..e021e025 100644
--- a/libsepol/src/policydb_validate.c
+++ b/libsepol/src/policydb_validate.c
@@ -903,7 +903,7 @@  static int validate_avtab_key(const avtab_key_t *key, int conditional, const pol
 	case AVTAB_XPERMS_DONTAUDIT:
 		if (p->target_platform != SEPOL_TARGET_SELINUX)
 			goto bad;
-		if (conditional)
+		if (conditional && !policydb_has_cond_xperms_feature(p))
 			goto bad;
 		break;
 	default:
@@ -1046,7 +1046,7 @@  static int validate_avrules(sepol_handle_t *handle, const avrule_t *avrule, int
 		case AVRULE_XPERMS_AUDITALLOW:
 		case AVRULE_XPERMS_DONTAUDIT:
 		case AVRULE_XPERMS_NEVERALLOW:
-			if (conditional)
+			if (conditional && !policydb_has_cond_xperms_feature(p))
 				goto bad;
 			break;
 		default:
diff --git a/libsepol/src/write.c b/libsepol/src/write.c
index a52e2e82..4ef98449 100644
--- a/libsepol/src/write.c
+++ b/libsepol/src/write.c
@@ -56,7 +56,8 @@  struct policy_data {
 };
 
 static int avrule_write_list(policydb_t *p,
-			     avrule_t * avrules, struct policy_file *fp);
+			     avrule_t * avrules, struct policy_file *fp,
+			     unsigned conditional);
 
 static int ebitmap_write(ebitmap_t * e, struct policy_file *fp)
 {
@@ -104,7 +105,8 @@  static uint16_t spec_order[] = {
 
 static int avtab_write_item(policydb_t * p,
 			    avtab_ptr_t cur, struct policy_file *fp,
-			    unsigned merge, unsigned commit, uint32_t * nel)
+			    unsigned merge, unsigned commit, unsigned conditional,
+			    uint32_t * nel)
 {
 	avtab_ptr_t node;
 	uint8_t buf8;
@@ -229,14 +231,20 @@  static int avtab_write_item(policydb_t * p,
 		return POLICYDB_ERROR;
 	if ((p->policyvers < POLICYDB_VERSION_XPERMS_IOCTL) &&
 			(cur->key.specified & AVTAB_XPERMS)) {
-		ERR(fp->handle, "policy version %u does not support ioctl extended"
+		ERR(fp->handle, "policy version %u does not support extended"
 				"permissions rules and one was specified", p->policyvers);
 		return POLICYDB_ERROR;
 	}
 
+	if (!policydb_has_cond_xperms_feature(p) && (cur->key.specified & AVTAB_XPERMS) && conditional) {
+		ERR(fp->handle, "policy version %u does not support extended"
+				"permissions rules in conditional policies and one was specified", p->policyvers);
+		return POLICYDB_ERROR;
+	}
+
 	if (p->target_platform != SEPOL_TARGET_SELINUX &&
 			(cur->key.specified & AVTAB_XPERMS)) {
-		ERR(fp->handle, "Target platform %s does not support ioctl "
+		ERR(fp->handle, "Target platform %s does not support "
 				"extended permissions rules and one was specified",
 				policydb_target_strings[p->target_platform]);
 		return POLICYDB_ERROR;
@@ -313,7 +321,7 @@  static int avtab_write(struct policydb *p, avtab_t * a, struct policy_file *fp)
 		for (cur = a->htable[i]; cur; cur = cur->next) {
 			/* If old format, compute final nel.
 			   If new format, write out the items. */
-			if (avtab_write_item(p, cur, fp, 1, !oldvers, &nel)) {
+			if (avtab_write_item(p, cur, fp, 1, !oldvers, 0, &nel)) {
 				rc = -1;
 				goto out;
 			}
@@ -332,7 +340,7 @@  static int avtab_write(struct policydb *p, avtab_t * a, struct policy_file *fp)
 		avtab_reset_merged(a);
 		for (i = 0; i < a->nslot; i++) {
 			for (cur = a->htable[i]; cur; cur = cur->next) {
-				if (avtab_write_item(p, cur, fp, 1, 1, NULL)) {
+				if (avtab_write_item(p, cur, fp, 1, 1, 0, NULL)) {
 					rc = -1;
 					goto out;
 				}
@@ -795,7 +803,7 @@  static int cond_write_av_list(policydb_t * p,
 
 	for (cur_list = list; cur_list != NULL; cur_list = cur_list->next) {
 		if (cur_list->node->parse_context)
-			if (avtab_write_item(p, cur_list->node, fp, 0, 1, NULL))
+			if (avtab_write_item(p, cur_list->node, fp, 0, 1, 1, NULL))
 				goto out;
 	}
 
@@ -846,9 +854,9 @@  static int cond_write_node(policydb_t * p,
 		if (cond_write_av_list(p, node->false_list, fp) != 0)
 			return POLICYDB_ERROR;
 	} else {
-		if (avrule_write_list(p, node->avtrue_list, fp))
+		if (avrule_write_list(p, node->avtrue_list, fp, 1))
 			return POLICYDB_ERROR;
-		if (avrule_write_list(p, node->avfalse_list, fp))
+		if (avrule_write_list(p, node->avfalse_list, fp, 1))
 			return POLICYDB_ERROR;
 	}
 
@@ -1743,7 +1751,7 @@  static int range_write(policydb_t * p, struct policy_file *fp)
 /************** module writing functions below **************/
 
 static int avrule_write(policydb_t *p, avrule_t * avrule,
-			struct policy_file *fp)
+			struct policy_file *fp, unsigned conditional)
 {
 	size_t items, items2;
 	uint32_t buf[32], len;
@@ -1801,15 +1809,23 @@  static int avrule_write(policydb_t *p, avrule_t * avrule,
 
 		if (p->policyvers < MOD_POLICYDB_VERSION_XPERMS_IOCTL) {
 			ERR(fp->handle,
-			    "module policy version %u does not support ioctl"
+			    "module policy version %u does not support"
 			    " extended permissions rules and one was specified",
 			    p->policyvers);
 			return POLICYDB_ERROR;
 		}
 
+		if (conditional && !policydb_has_cond_xperms_feature(p)) {
+			ERR(fp->handle,
+			    "module policy version %u does not support"
+			    " extended permissions rules in conditional policies and one was specified",
+			    p->policyvers);
+			return POLICYDB_ERROR;
+		}
+
 		if (p->target_platform != SEPOL_TARGET_SELINUX) {
 			ERR(fp->handle,
-			    "Target platform %s does not support ioctl"
+			    "Target platform %s does not support"
 			    " extended permissions rules and one was specified",
 			    policydb_target_strings[p->target_platform]);
 			return POLICYDB_ERROR;
@@ -1834,7 +1850,7 @@  static int avrule_write(policydb_t *p, avrule_t * avrule,
 }
 
 static int avrule_write_list(policydb_t *p, avrule_t * avrules,
-			     struct policy_file *fp)
+			     struct policy_file *fp, unsigned conditional)
 {
 	uint32_t buf[32], len;
 	avrule_t *avrule;
@@ -1852,7 +1868,7 @@  static int avrule_write_list(policydb_t *p, avrule_t * avrules,
 
 	avrule = avrules;
 	while (avrule) {
-		if (avrule_write(p, avrule, fp))
+		if (avrule_write(p, avrule, fp, conditional))
 			return POLICYDB_ERROR;
 		avrule = avrule->next;
 	}
@@ -2056,7 +2072,7 @@  static int avrule_decl_write(avrule_decl_t * decl, int num_scope_syms,
 		return POLICYDB_ERROR;
 	}
 	if (cond_write_list(p, decl->cond_list, fp) == -1 ||
-	    avrule_write_list(p, decl->avrules, fp) == -1 ||
+	    avrule_write_list(p, decl->avrules, fp, 0) == -1 ||
 	    role_trans_rule_write(p, decl->role_tr_rules, fp) == -1 ||
 	    role_allow_rule_write(decl->role_allow_rules, fp) == -1) {
 		return POLICYDB_ERROR;