526 lines
16 KiB
Diff
526 lines
16 KiB
Diff
diff --git a/libsepol/include/sepol/handle.h b/libsepol/include/sepol/handle.h
|
|
index 19be326..115bda1 100644
|
|
--- a/libsepol/include/sepol/handle.h
|
|
+++ b/libsepol/include/sepol/handle.h
|
|
@@ -24,4 +24,11 @@ void sepol_set_expand_consume_base(sepol_handle_t * sh, int consume_base);
|
|
/* Destroy a sepol handle. */
|
|
void sepol_handle_destroy(sepol_handle_t *);
|
|
|
|
+/* Get whether or not needless unused branch of tunables would be preserved */
|
|
+int sepol_get_preserve_tunables(sepol_handle_t * sh);
|
|
+
|
|
+/* Set whether or not to preserve the needless unused branch of tunables,
|
|
+ * 0 is default and discard such branch, 1 preserves them */
|
|
+void sepol_set_preserve_tunables(sepol_handle_t * sh, int preserve_tunables);
|
|
+
|
|
#endif
|
|
diff --git a/libsepol/include/sepol/policydb/conditional.h b/libsepol/include/sepol/policydb/conditional.h
|
|
index a8ed694..48ec106 100644
|
|
--- a/libsepol/include/sepol/policydb/conditional.h
|
|
+++ b/libsepol/include/sepol/policydb/conditional.h
|
|
@@ -77,15 +77,17 @@ typedef struct cond_node {
|
|
/* these true/false lists point into te_avtab when that is used */
|
|
cond_av_list_t *true_list;
|
|
cond_av_list_t *false_list;
|
|
- /* and these are using during parsing and for modules */
|
|
+ /* and these are used during parsing and for modules */
|
|
avrule_t *avtrue_list;
|
|
avrule_t *avfalse_list;
|
|
/* these fields are not written to binary policy */
|
|
unsigned int nbools;
|
|
uint32_t bool_ids[COND_MAX_BOOLS];
|
|
uint32_t expr_pre_comp;
|
|
- /* */
|
|
struct cond_node *next;
|
|
+ /* a tunable conditional, calculated and used at expansion */
|
|
+#define COND_NODE_FLAGS_TUNABLE 0x01
|
|
+ uint32_t flags;
|
|
} cond_node_t;
|
|
|
|
extern int cond_evaluate_expr(policydb_t * p, cond_expr_t * expr);
|
|
diff --git a/libsepol/include/sepol/policydb/policydb.h b/libsepol/include/sepol/policydb/policydb.h
|
|
index 5320bc8..1848a7b 100644
|
|
--- a/libsepol/include/sepol/policydb/policydb.h
|
|
+++ b/libsepol/include/sepol/policydb/policydb.h
|
|
@@ -210,6 +210,8 @@ typedef struct range_trans {
|
|
typedef struct cond_bool_datum {
|
|
symtab_datum_t s;
|
|
int state;
|
|
+#define COND_BOOL_FLAGS_TUNABLE 0x01 /* is this a tunable? */
|
|
+ uint32_t flags;
|
|
} cond_bool_datum_t;
|
|
|
|
struct cond_node;
|
|
@@ -683,9 +685,10 @@ extern int policydb_set_target_platform(policydb_t *p, int platform);
|
|
#define MOD_POLICYDB_VERSION_FILENAME_TRANS 11
|
|
#define MOD_POLICYDB_VERSION_ROLETRANS 12
|
|
#define MOD_POLICYDB_VERSION_ROLEATTRIB 13
|
|
+#define MOD_POLICYDB_VERSION_TUNABLE_SEP 14
|
|
|
|
#define MOD_POLICYDB_VERSION_MIN MOD_POLICYDB_VERSION_BASE
|
|
-#define MOD_POLICYDB_VERSION_MAX MOD_POLICYDB_VERSION_ROLEATTRIB
|
|
+#define MOD_POLICYDB_VERSION_MAX MOD_POLICYDB_VERSION_TUNABLE_SEP
|
|
|
|
#define POLICYDB_CONFIG_MLS 1
|
|
|
|
diff --git a/libsepol/src/conditional.c b/libsepol/src/conditional.c
|
|
index 1482387..ea47cdd 100644
|
|
--- a/libsepol/src/conditional.c
|
|
+++ b/libsepol/src/conditional.c
|
|
@@ -160,6 +160,7 @@ cond_node_t *cond_node_create(policydb_t * p, cond_node_t * node)
|
|
for (i = 0; i < min(node->nbools, COND_MAX_BOOLS); i++)
|
|
new_node->bool_ids[i] = node->bool_ids[i];
|
|
new_node->expr_pre_comp = node->expr_pre_comp;
|
|
+ new_node->flags = node->flags;
|
|
}
|
|
|
|
return new_node;
|
|
@@ -563,8 +564,8 @@ static int bool_isvalid(cond_bool_datum_t * b)
|
|
return 1;
|
|
}
|
|
|
|
-int cond_read_bool(policydb_t * p
|
|
- __attribute__ ((unused)), hashtab_t h,
|
|
+int cond_read_bool(policydb_t * p,
|
|
+ hashtab_t h,
|
|
struct policy_file *fp)
|
|
{
|
|
char *key = 0;
|
|
@@ -596,6 +597,15 @@ int cond_read_bool(policydb_t * p
|
|
if (rc < 0)
|
|
goto err;
|
|
key[len] = 0;
|
|
+
|
|
+ if (p->policy_type != POLICY_KERN &&
|
|
+ p->policyvers >= MOD_POLICYDB_VERSION_TUNABLE_SEP) {
|
|
+ rc = next_entry(buf, fp, sizeof(uint32_t));
|
|
+ if (rc < 0)
|
|
+ goto err;
|
|
+ booldatum->flags = le32_to_cpu(buf[0]);
|
|
+ }
|
|
+
|
|
if (hashtab_insert(h, key, booldatum))
|
|
goto err;
|
|
|
|
@@ -810,6 +820,14 @@ static int cond_read_node(policydb_t * p, cond_node_t * node, void *fp)
|
|
goto err;
|
|
}
|
|
|
|
+ if (p->policy_type != POLICY_KERN &&
|
|
+ p->policyvers >= MOD_POLICYDB_VERSION_TUNABLE_SEP) {
|
|
+ rc = next_entry(buf, fp, sizeof(uint32_t));
|
|
+ if (rc < 0)
|
|
+ goto err;
|
|
+ node->flags = le32_to_cpu(buf[0]);
|
|
+ }
|
|
+
|
|
return 0;
|
|
err:
|
|
cond_node_destroy(node);
|
|
diff --git a/libsepol/src/expand.c b/libsepol/src/expand.c
|
|
index 06f11f4..2861776 100644
|
|
--- a/libsepol/src/expand.c
|
|
+++ b/libsepol/src/expand.c
|
|
@@ -1014,6 +1014,11 @@ static int bool_copy_callback(hashtab_key_t key, hashtab_datum_t datum,
|
|
return 0;
|
|
}
|
|
|
|
+ if (bool->flags & COND_BOOL_FLAGS_TUNABLE) {
|
|
+ /* Skip tunables */
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
if (state->verbose)
|
|
INFO(state->handle, "copying boolean %s", id);
|
|
|
|
@@ -1046,6 +1051,7 @@ static int bool_copy_callback(hashtab_key_t key, hashtab_datum_t datum,
|
|
state->boolmap[bool->s.value - 1] = new_bool->s.value;
|
|
|
|
new_bool->state = bool->state;
|
|
+ new_bool->flags = bool->flags;
|
|
|
|
return 0;
|
|
}
|
|
@@ -1940,6 +1946,13 @@ static int cond_node_copy(expand_state_t * state, cond_node_t * cn)
|
|
if (cond_node_copy(state, cn->next)) {
|
|
return -1;
|
|
}
|
|
+
|
|
+ /* If current cond_node_t is of tunable, its effective branch
|
|
+ * has been appended to its home decl->avrules list during link
|
|
+ * and now we should just skip it. */
|
|
+ if (cn->flags & COND_NODE_FLAGS_TUNABLE)
|
|
+ return 0;
|
|
+
|
|
if (cond_normalize_expr(state->base, cn)) {
|
|
ERR(state->handle, "Error while normalizing conditional");
|
|
return -1;
|
|
@@ -2665,6 +2678,106 @@ int expand_module_avrules(sepol_handle_t * handle, policydb_t * base,
|
|
return copy_and_expand_avrule_block(&state);
|
|
}
|
|
|
|
+static void discard_tunables(sepol_handle_t *sh, policydb_t *pol)
|
|
+{
|
|
+ avrule_block_t *block;
|
|
+ avrule_decl_t *decl;
|
|
+ cond_node_t *cur_node;
|
|
+ cond_expr_t *cur_expr;
|
|
+ int cur_state, preserve_tunables = 0;
|
|
+ avrule_t *tail, *to_be_appended;
|
|
+
|
|
+ if (sh && sh->preserve_tunables)
|
|
+ preserve_tunables = 1;
|
|
+
|
|
+ /* Iterate through all cond_node of all enabled decls, if a cond_node
|
|
+ * is about tunable, calculate its state value and concatenate one of
|
|
+ * its avrule list to the current decl->avrules list. On the other
|
|
+ * hand, the disabled unused branch of a tunable would be discarded.
|
|
+ *
|
|
+ * Note, such tunable cond_node would be skipped over in expansion,
|
|
+ * so we won't have to worry about removing it from decl->cond_list
|
|
+ * here :-)
|
|
+ *
|
|
+ * If tunables are requested to be preserved then they would be
|
|
+ * "transformed" as booleans by having their TUNABLE flag cleared.
|
|
+ */
|
|
+ for (block = pol->global; block != NULL; block = block->next) {
|
|
+ decl = block->enabled;
|
|
+ if (decl == NULL || decl->enabled == 0)
|
|
+ continue;
|
|
+
|
|
+ tail = decl->avrules;
|
|
+ while (tail && tail->next)
|
|
+ tail = tail->next;
|
|
+
|
|
+ for (cur_node = decl->cond_list; cur_node != NULL;
|
|
+ cur_node = cur_node->next) {
|
|
+ int booleans, tunables, i;
|
|
+ cond_bool_datum_t *booldatum;
|
|
+ cond_bool_datum_t *tmp[COND_EXPR_MAXDEPTH];
|
|
+
|
|
+ booleans = tunables = 0;
|
|
+ memset(tmp, 0, sizeof(cond_bool_datum_t *) * COND_EXPR_MAXDEPTH);
|
|
+
|
|
+ for (cur_expr = cur_node->expr; cur_expr != NULL;
|
|
+ cur_expr = cur_expr->next) {
|
|
+ if (cur_expr->expr_type != COND_BOOL)
|
|
+ continue;
|
|
+ booldatum = pol->bool_val_to_struct[cur_expr->bool - 1];
|
|
+ if (booldatum->flags & COND_BOOL_FLAGS_TUNABLE)
|
|
+ tmp[tunables++] = booldatum;
|
|
+ else
|
|
+ booleans++;
|
|
+ }
|
|
+
|
|
+ /* bool_copy_callback() at link phase has ensured
|
|
+ * that no mixture of tunables and booleans in one
|
|
+ * expression. However, this would be broken by the
|
|
+ * request to preserve tunables */
|
|
+ if (!preserve_tunables)
|
|
+ assert(!(booleans && tunables));
|
|
+
|
|
+ if (booleans || preserve_tunables) {
|
|
+ cur_node->flags &= ~COND_NODE_FLAGS_TUNABLE;
|
|
+ if (tunables) {
|
|
+ for (i = 0; i < tunables; i++)
|
|
+ tmp[i]->flags &= ~COND_BOOL_FLAGS_TUNABLE;
|
|
+ }
|
|
+ } else {
|
|
+ cur_node->flags |= COND_NODE_FLAGS_TUNABLE;
|
|
+ cur_state = cond_evaluate_expr(pol, cur_node->expr);
|
|
+ if (cur_state == -1) {
|
|
+ printf("Expression result was "
|
|
+ "undefined, skipping all"
|
|
+ "rules\n");
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ to_be_appended = (cur_state == 1) ?
|
|
+ cur_node->avtrue_list : cur_node->avfalse_list;
|
|
+
|
|
+ if (tail)
|
|
+ tail->next = to_be_appended;
|
|
+ else
|
|
+ tail = decl->avrules = to_be_appended;
|
|
+
|
|
+ /* Now that the effective branch has been
|
|
+ * appended, neutralize its original pointer */
|
|
+ if (cur_state == 1)
|
|
+ cur_node->avtrue_list = NULL;
|
|
+ else
|
|
+ cur_node->avfalse_list = NULL;
|
|
+
|
|
+ /* Update the tail of decl->avrules for
|
|
+ * further concatenation */
|
|
+ while (tail && tail->next)
|
|
+ tail = tail->next;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
/* Linking should always be done before calling expand, even if
|
|
* there is only a base since all optionals are dealt with at link time
|
|
* the base passed in should be indexed and avrule blocks should be
|
|
@@ -2678,6 +2791,16 @@ int expand_module(sepol_handle_t * handle,
|
|
expand_state_t state;
|
|
avrule_block_t *curblock;
|
|
|
|
+ /* Append tunable's avtrue_list or avfalse_list to the avrules list
|
|
+ * of its home decl depending on its state value, so that the effect
|
|
+ * rules of a tunable would be added to te_avtab permanently. Whereas
|
|
+ * the disabled unused branch would be discarded.
|
|
+ *
|
|
+ * Originally this function is called at the very end of link phase,
|
|
+ * however, we need to keep the linked policy intact for analysis
|
|
+ * purpose. */
|
|
+ discard_tunables(handle, base);
|
|
+
|
|
expand_state_init(&state);
|
|
|
|
state.verbose = verbose;
|
|
diff --git a/libsepol/src/handle.c b/libsepol/src/handle.c
|
|
index 191ac57..2e9a4ad 100644
|
|
--- a/libsepol/src/handle.c
|
|
+++ b/libsepol/src/handle.c
|
|
@@ -18,9 +18,24 @@ sepol_handle_t *sepol_handle_create(void)
|
|
sh->disable_dontaudit = 0;
|
|
sh->expand_consume_base = 0;
|
|
|
|
+ /* by default needless unused branch of tunables would be discarded */
|
|
+ sh->preserve_tunables = 0;
|
|
+
|
|
return sh;
|
|
}
|
|
|
|
+int sepol_get_preserve_tunables(sepol_handle_t *sh)
|
|
+{
|
|
+ assert(sh != NULL);
|
|
+ return sh->preserve_tunables;
|
|
+}
|
|
+
|
|
+void sepol_set_preserve_tunables(sepol_handle_t * sh, int preserve_tunables)
|
|
+{
|
|
+ assert(sh !=NULL);
|
|
+ sh->preserve_tunables = preserve_tunables;
|
|
+}
|
|
+
|
|
int sepol_get_disable_dontaudit(sepol_handle_t *sh)
|
|
{
|
|
assert(sh !=NULL);
|
|
diff --git a/libsepol/src/handle.h b/libsepol/src/handle.h
|
|
index 254fbd8..7728d04 100644
|
|
--- a/libsepol/src/handle.h
|
|
+++ b/libsepol/src/handle.h
|
|
@@ -17,7 +17,7 @@ struct sepol_handle {
|
|
|
|
int disable_dontaudit;
|
|
int expand_consume_base;
|
|
-
|
|
+ int preserve_tunables;
|
|
};
|
|
|
|
#endif
|
|
diff --git a/libsepol/src/libsepol.map b/libsepol/src/libsepol.map
|
|
index 719e5b7..c6bb788 100644
|
|
--- a/libsepol/src/libsepol.map
|
|
+++ b/libsepol/src/libsepol.map
|
|
@@ -1,5 +1,6 @@
|
|
{
|
|
global:
|
|
+ expand_module_avrules;
|
|
sepol_module_package_*; sepol_link_modules; sepol_expand_module; sepol_link_packages;
|
|
sepol_bool_*; sepol_genbools*;
|
|
sepol_context_*; sepol_mls_*; sepol_check_context;
|
|
@@ -15,5 +16,6 @@
|
|
sepol_get_disable_dontaudit;
|
|
sepol_set_disable_dontaudit;
|
|
sepol_set_expand_consume_base;
|
|
+ sepol_get_preserve_tunables; sepol_set_preserve_tunables;
|
|
local: *;
|
|
};
|
|
diff --git a/libsepol/src/link.c b/libsepol/src/link.c
|
|
index 421c47b..ee9675b 100644
|
|
--- a/libsepol/src/link.c
|
|
+++ b/libsepol/src/link.c
|
|
@@ -587,7 +587,18 @@ static int bool_copy_callback(hashtab_key_t key, hashtab_datum_t datum,
|
|
}
|
|
state->base->p_bools.nprim++;
|
|
base_bool = new_bool;
|
|
-
|
|
+ base_bool->flags = booldatum->flags;
|
|
+ } else if ((booldatum->flags & COND_BOOL_FLAGS_TUNABLE) !=
|
|
+ (base_bool->flags & COND_BOOL_FLAGS_TUNABLE)) {
|
|
+ /* A mismatch between boolean/tunable declaration
|
|
+ * and usage(for example a boolean used in the
|
|
+ * tunable_policy() or vice versa).
|
|
+ *
|
|
+ * This is not allowed and bail out with errors */
|
|
+ ERR(state->handle,
|
|
+ "%s: Mismatch between boolean/tunable definition "
|
|
+ "and usage for %s", state->cur_mod_name, id);
|
|
+ return -1;
|
|
}
|
|
|
|
/* Get the scope info for this boolean to see if this is the declaration,
|
|
@@ -595,9 +606,12 @@ static int bool_copy_callback(hashtab_key_t key, hashtab_datum_t datum,
|
|
scope = hashtab_search(state->cur->policy->p_bools_scope.table, id);
|
|
if (!scope)
|
|
return SEPOL_ERR;
|
|
- if (scope->scope == SCOPE_DECL)
|
|
+ if (scope->scope == SCOPE_DECL) {
|
|
base_bool->state = booldatum->state;
|
|
-
|
|
+ /* Only the declaration rather than requirement
|
|
+ * decides if it is a boolean or tunable. */
|
|
+ base_bool->flags = booldatum->flags;
|
|
+ }
|
|
state->cur->map[SYM_BOOLS][booldatum->s.value - 1] = base_bool->s.value;
|
|
return 0;
|
|
|
|
diff --git a/libsepol/src/policydb.c b/libsepol/src/policydb.c
|
|
index 017aeca..136b450 100644
|
|
--- a/libsepol/src/policydb.c
|
|
+++ b/libsepol/src/policydb.c
|
|
@@ -221,6 +221,13 @@ static struct policydb_compat_info policydb_compat[] = {
|
|
.target_platform = SEPOL_TARGET_SELINUX,
|
|
},
|
|
{
|
|
+ .type = POLICY_BASE,
|
|
+ .version = MOD_POLICYDB_VERSION_TUNABLE_SEP,
|
|
+ .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,
|
|
@@ -290,6 +297,13 @@ static struct policydb_compat_info policydb_compat[] = {
|
|
.ocon_num = 0,
|
|
.target_platform = SEPOL_TARGET_SELINUX,
|
|
},
|
|
+ {
|
|
+ .type = POLICY_MOD,
|
|
+ .version = MOD_POLICYDB_VERSION_TUNABLE_SEP,
|
|
+ .sym_num = SYM_NUM,
|
|
+ .ocon_num = 0,
|
|
+ .target_platform = SEPOL_TARGET_SELINUX,
|
|
+ },
|
|
};
|
|
|
|
#if 0
|
|
diff --git a/libsepol/src/write.c b/libsepol/src/write.c
|
|
index 290e036..e34ab52 100644
|
|
--- a/libsepol/src/write.c
|
|
+++ b/libsepol/src/write.c
|
|
@@ -607,6 +607,7 @@ static int cond_write_bool(hashtab_key_t key, hashtab_datum_t datum, void *ptr)
|
|
unsigned int items, items2;
|
|
struct policy_data *pd = ptr;
|
|
struct policy_file *fp = pd->fp;
|
|
+ struct policydb *p = pd->p;
|
|
|
|
booldatum = (cond_bool_datum_t *) datum;
|
|
|
|
@@ -621,6 +622,15 @@ static int cond_write_bool(hashtab_key_t key, hashtab_datum_t datum, void *ptr)
|
|
items = put_entry(key, 1, len, fp);
|
|
if (items != len)
|
|
return POLICYDB_ERROR;
|
|
+
|
|
+ if (p->policy_type != POLICY_KERN &&
|
|
+ p->policyvers >= MOD_POLICYDB_VERSION_TUNABLE_SEP) {
|
|
+ buf[0] = cpu_to_le32(booldatum->flags);
|
|
+ items = put_entry(buf, sizeof(uint32_t), 1, fp);
|
|
+ if (items != 1)
|
|
+ return POLICYDB_ERROR;
|
|
+ }
|
|
+
|
|
return POLICYDB_SUCCESS;
|
|
}
|
|
|
|
@@ -727,6 +737,14 @@ static int cond_write_node(policydb_t * p,
|
|
return POLICYDB_ERROR;
|
|
}
|
|
|
|
+ if (p->policy_type != POLICY_KERN &&
|
|
+ p->policyvers >= MOD_POLICYDB_VERSION_TUNABLE_SEP) {
|
|
+ buf[0] = cpu_to_le32(node->flags);
|
|
+ items = put_entry(buf, sizeof(uint32_t), 1, fp);
|
|
+ if (items != 1)
|
|
+ return POLICYDB_ERROR;
|
|
+ }
|
|
+
|
|
return POLICYDB_SUCCESS;
|
|
}
|
|
|
|
@@ -972,6 +990,19 @@ static int role_write(hashtab_key_t key, hashtab_datum_t datum, void *ptr)
|
|
|
|
role = (role_datum_t *) datum;
|
|
|
|
+ /*
|
|
+ * Role attributes are redundant for policy.X, skip them
|
|
+ * when writing the roles symbol table. They are also skipped
|
|
+ * when pp is downgraded.
|
|
+ *
|
|
+ * Their numbers would be deducted in policydb_write().
|
|
+ */
|
|
+ if ((role->flavor == ROLE_ATTRIB) &&
|
|
+ ((p->policy_type == POLICY_KERN) ||
|
|
+ (p->policy_type != POLICY_KERN &&
|
|
+ p->policyvers < MOD_POLICYDB_VERSION_ROLEATTRIB)))
|
|
+ return POLICYDB_SUCCESS;
|
|
+
|
|
len = strlen(key);
|
|
items = 0;
|
|
buf[items++] = cpu_to_le32(len);
|
|
@@ -1795,6 +1826,19 @@ static int type_attr_uncount(hashtab_key_t key __attribute__ ((unused)),
|
|
return 0;
|
|
}
|
|
|
|
+static int role_attr_uncount(hashtab_key_t key __attribute__ ((unused)),
|
|
+ hashtab_datum_t datum, void *args)
|
|
+{
|
|
+ role_datum_t *role = datum;
|
|
+ uint32_t *p_nel = args;
|
|
+
|
|
+ if (role->flavor == ROLE_ATTRIB) {
|
|
+ /* uncount attribute from total number of roles */
|
|
+ (*p_nel)--;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
/*
|
|
* Write the configuration data in a policy database
|
|
* structure to a policy database binary representation
|
|
@@ -1926,7 +1970,7 @@ int policydb_write(policydb_t * p, struct policy_file *fp)
|
|
num_syms = info->sym_num;
|
|
for (i = 0; i < num_syms; i++) {
|
|
buf[0] = cpu_to_le32(p->symtab[i].nprim);
|
|
- buf[1] = cpu_to_le32(p->symtab[i].table->nel);
|
|
+ buf[1] = p->symtab[i].table->nel;
|
|
|
|
/*
|
|
* A special case when writing type/attribute symbol table.
|
|
@@ -1939,6 +1983,20 @@ int policydb_write(policydb_t * p, struct policy_file *fp)
|
|
p->policy_type == POLICY_KERN) {
|
|
hashtab_map(p->symtab[i].table, type_attr_uncount, &buf[1]);
|
|
}
|
|
+
|
|
+ /*
|
|
+ * Another special case when writing role/attribute symbol
|
|
+ * table, role attributes are redundant for policy.X, or
|
|
+ * when the pp's version is not big enough. So deduct
|
|
+ * their numbers from p_roles.table->nel.
|
|
+ */
|
|
+ if ((i == SYM_ROLES) &&
|
|
+ ((p->policy_type == POLICY_KERN) ||
|
|
+ (p->policy_type != POLICY_KERN &&
|
|
+ p->policyvers < MOD_POLICYDB_VERSION_ROLEATTRIB)))
|
|
+ hashtab_map(p->symtab[i].table, role_attr_uncount, &buf[1]);
|
|
+
|
|
+ buf[1] = cpu_to_le32(buf[1]);
|
|
items = put_entry(buf, sizeof(uint32_t), 2, fp);
|
|
if (items != 2)
|
|
return POLICYDB_ERROR;
|