summaryrefslogtreecommitdiff
path: root/security/selinux/ss/policydb.c
diff options
context:
space:
mode:
Diffstat (limited to 'security/selinux/ss/policydb.c')
-rw-r--r--security/selinux/ss/policydb.c2262
1 files changed, 1320 insertions, 942 deletions
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index c8adde3aff8f..91df3db6a88c 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -1,28 +1,27 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Implementation of the policy database.
*
- * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
+ * Author : Stephen Smalley, <stephen.smalley.work@gmail.com>
*/
/*
* Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com>
+ * Support for enhanced MLS infrastructure.
+ * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
*
- * Support for enhanced MLS infrastructure.
- *
- * Updated: Frank Mayer <mayerf@tresys.com> and Karl MacMillan <kmacmillan@tresys.com>
- *
- * Added conditional policy language extensions
+ * Updated: Frank Mayer <mayerf@tresys.com> and
+ * Karl MacMillan <kmacmillan@tresys.com>
+ * Added conditional policy language extensions
+ * Copyright (C) 2003-2004 Tresys Technology, LLC
*
* Updated: Hewlett-Packard <paul@paul-moore.com>
+ * Added support for the policy capability bitmap
+ * Copyright (C) 2007 Hewlett-Packard Development Company, L.P.
*
- * Added support for the policy capability bitmap
- *
- * Copyright (C) 2007 Hewlett-Packard Development Company, L.P.
- * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
- * Copyright (C) 2003 - 2004 Tresys Technology, LLC
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, version 2.
+ * Update: Mellanox Techonologies
+ * Added Infiniband support
+ * Copyright (C) 2016 Mellanox Techonologies
*/
#include <linux/kernel.h>
@@ -31,7 +30,6 @@
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/audit.h>
-#include <linux/flex_array.h>
#include "security.h"
#include "policydb.h"
@@ -39,10 +37,9 @@
#include "mls.h"
#include "services.h"
-#define _DEBUG_HASHES
-
-#ifdef DEBUG_HASHES
-static const char *symtab_name[SYM_NUM] = {
+#ifdef CONFIG_SECURITY_SELINUX_DEBUG
+/* clang-format off */
+static const char *const symtab_name[SYM_NUM] = {
"common prefixes",
"classes",
"roles",
@@ -52,111 +49,336 @@ static const char *symtab_name[SYM_NUM] = {
"levels",
"categories",
};
+/* clang-format off */
#endif
-static unsigned int symtab_sizes[SYM_NUM] = {
- 2,
- 32,
- 16,
- 512,
- 128,
- 16,
- 16,
- 16,
-};
-
struct policydb_compat_info {
- int version;
- int sym_num;
- int ocon_num;
+ unsigned int version;
+ unsigned int sym_num;
+ unsigned int ocon_num;
};
/* These need to be updated if SYM_NUM or OCON_NUM changes */
-static struct policydb_compat_info policydb_compat[] = {
+static const struct policydb_compat_info policydb_compat[] = {
+ {
+ .version = POLICYDB_VERSION_BASE,
+ .sym_num = SYM_NUM - 3,
+ .ocon_num = OCON_NUM - 3,
+ },
{
- .version = POLICYDB_VERSION_BASE,
- .sym_num = SYM_NUM - 3,
- .ocon_num = OCON_NUM - 1,
+ .version = POLICYDB_VERSION_BOOL,
+ .sym_num = SYM_NUM - 2,
+ .ocon_num = OCON_NUM - 3,
},
{
- .version = POLICYDB_VERSION_BOOL,
- .sym_num = SYM_NUM - 2,
- .ocon_num = OCON_NUM - 1,
+ .version = POLICYDB_VERSION_IPV6,
+ .sym_num = SYM_NUM - 2,
+ .ocon_num = OCON_NUM - 2,
},
{
- .version = POLICYDB_VERSION_IPV6,
- .sym_num = SYM_NUM - 2,
- .ocon_num = OCON_NUM,
+ .version = POLICYDB_VERSION_NLCLASS,
+ .sym_num = SYM_NUM - 2,
+ .ocon_num = OCON_NUM - 2,
},
{
- .version = POLICYDB_VERSION_NLCLASS,
- .sym_num = SYM_NUM - 2,
- .ocon_num = OCON_NUM,
+ .version = POLICYDB_VERSION_MLS,
+ .sym_num = SYM_NUM,
+ .ocon_num = OCON_NUM - 2,
},
{
- .version = POLICYDB_VERSION_MLS,
- .sym_num = SYM_NUM,
- .ocon_num = OCON_NUM,
+ .version = POLICYDB_VERSION_AVTAB,
+ .sym_num = SYM_NUM,
+ .ocon_num = OCON_NUM - 2,
},
{
- .version = POLICYDB_VERSION_AVTAB,
- .sym_num = SYM_NUM,
- .ocon_num = OCON_NUM,
+ .version = POLICYDB_VERSION_RANGETRANS,
+ .sym_num = SYM_NUM,
+ .ocon_num = OCON_NUM - 2,
},
{
- .version = POLICYDB_VERSION_RANGETRANS,
- .sym_num = SYM_NUM,
- .ocon_num = OCON_NUM,
+ .version = POLICYDB_VERSION_POLCAP,
+ .sym_num = SYM_NUM,
+ .ocon_num = OCON_NUM - 2,
},
{
- .version = POLICYDB_VERSION_POLCAP,
- .sym_num = SYM_NUM,
- .ocon_num = OCON_NUM,
+ .version = POLICYDB_VERSION_PERMISSIVE,
+ .sym_num = SYM_NUM,
+ .ocon_num = OCON_NUM - 2,
},
{
- .version = POLICYDB_VERSION_PERMISSIVE,
- .sym_num = SYM_NUM,
- .ocon_num = OCON_NUM,
+ .version = POLICYDB_VERSION_BOUNDARY,
+ .sym_num = SYM_NUM,
+ .ocon_num = OCON_NUM - 2,
},
{
- .version = POLICYDB_VERSION_BOUNDARY,
- .sym_num = SYM_NUM,
- .ocon_num = OCON_NUM,
+ .version = POLICYDB_VERSION_FILENAME_TRANS,
+ .sym_num = SYM_NUM,
+ .ocon_num = OCON_NUM - 2,
},
{
- .version = POLICYDB_VERSION_FILENAME_TRANS,
- .sym_num = SYM_NUM,
- .ocon_num = OCON_NUM,
+ .version = POLICYDB_VERSION_ROLETRANS,
+ .sym_num = SYM_NUM,
+ .ocon_num = OCON_NUM - 2,
},
{
- .version = POLICYDB_VERSION_ROLETRANS,
- .sym_num = SYM_NUM,
- .ocon_num = OCON_NUM,
+ .version = POLICYDB_VERSION_NEW_OBJECT_DEFAULTS,
+ .sym_num = SYM_NUM,
+ .ocon_num = OCON_NUM - 2,
},
{
- .version = POLICYDB_VERSION_NEW_OBJECT_DEFAULTS,
- .sym_num = SYM_NUM,
- .ocon_num = OCON_NUM,
+ .version = POLICYDB_VERSION_DEFAULT_TYPE,
+ .sym_num = SYM_NUM,
+ .ocon_num = OCON_NUM - 2,
},
{
- .version = POLICYDB_VERSION_DEFAULT_TYPE,
- .sym_num = SYM_NUM,
- .ocon_num = OCON_NUM,
+ .version = POLICYDB_VERSION_CONSTRAINT_NAMES,
+ .sym_num = SYM_NUM,
+ .ocon_num = OCON_NUM - 2,
+ },
+ {
+ .version = POLICYDB_VERSION_XPERMS_IOCTL,
+ .sym_num = SYM_NUM,
+ .ocon_num = OCON_NUM - 2,
+ },
+ {
+ .version = POLICYDB_VERSION_INFINIBAND,
+ .sym_num = SYM_NUM,
+ .ocon_num = OCON_NUM,
+ },
+ {
+ .version = POLICYDB_VERSION_GLBLUB,
+ .sym_num = SYM_NUM,
+ .ocon_num = OCON_NUM,
+ },
+ {
+ .version = POLICYDB_VERSION_COMP_FTRANS,
+ .sym_num = SYM_NUM,
+ .ocon_num = OCON_NUM,
+ },
+ {
+ .version = POLICYDB_VERSION_COND_XPERMS,
+ .sym_num = SYM_NUM,
+ .ocon_num = OCON_NUM,
+ },
+ {
+ .version = POLICYDB_VERSION_NEVERAUDIT,
+ .sym_num = SYM_NUM,
+ .ocon_num = OCON_NUM,
},
};
-static struct policydb_compat_info *policydb_lookup_compat(int version)
+static const struct policydb_compat_info *
+policydb_lookup_compat(unsigned int version)
{
- int i;
- struct policydb_compat_info *info = NULL;
+ unsigned int i;
for (i = 0; i < ARRAY_SIZE(policydb_compat); i++) {
- if (policydb_compat[i].version == version) {
- info = &policydb_compat[i];
- break;
+ if (policydb_compat[i].version == version)
+ return &policydb_compat[i];
+ }
+
+ return NULL;
+}
+
+/*
+ * The following *_destroy functions are used to
+ * free any memory allocated for each kind of
+ * symbol data in the policy database.
+ */
+
+static int perm_destroy(void *key, void *datum, void *p)
+{
+ kfree(key);
+ kfree(datum);
+ return 0;
+}
+
+static int common_destroy(void *key, void *datum, void *p)
+{
+ struct common_datum *comdatum;
+
+ kfree(key);
+ if (datum) {
+ comdatum = datum;
+ hashtab_map(&comdatum->permissions.table, perm_destroy, NULL);
+ hashtab_destroy(&comdatum->permissions.table);
+ }
+ kfree(datum);
+ return 0;
+}
+
+static void constraint_expr_destroy(struct constraint_expr *expr)
+{
+ if (expr) {
+ ebitmap_destroy(&expr->names);
+ if (expr->type_names) {
+ ebitmap_destroy(&expr->type_names->types);
+ ebitmap_destroy(&expr->type_names->negset);
+ kfree(expr->type_names);
+ }
+ kfree(expr);
+ }
+}
+
+static int cls_destroy(void *key, void *datum, void *p)
+{
+ struct class_datum *cladatum;
+ struct constraint_node *constraint, *ctemp;
+ struct constraint_expr *e, *etmp;
+
+ kfree(key);
+ if (datum) {
+ cladatum = datum;
+ hashtab_map(&cladatum->permissions.table, perm_destroy, NULL);
+ hashtab_destroy(&cladatum->permissions.table);
+ constraint = cladatum->constraints;
+ while (constraint) {
+ e = constraint->expr;
+ while (e) {
+ etmp = e;
+ e = e->next;
+ constraint_expr_destroy(etmp);
+ }
+ ctemp = constraint;
+ constraint = constraint->next;
+ kfree(ctemp);
}
+
+ constraint = cladatum->validatetrans;
+ while (constraint) {
+ e = constraint->expr;
+ while (e) {
+ etmp = e;
+ e = e->next;
+ constraint_expr_destroy(etmp);
+ }
+ ctemp = constraint;
+ constraint = constraint->next;
+ kfree(ctemp);
+ }
+ kfree(cladatum->comkey);
+ }
+ kfree(datum);
+ return 0;
+}
+
+static int role_destroy(void *key, void *datum, void *p)
+{
+ struct role_datum *role;
+
+ kfree(key);
+ if (datum) {
+ role = datum;
+ ebitmap_destroy(&role->dominates);
+ ebitmap_destroy(&role->types);
+ }
+ kfree(datum);
+ return 0;
+}
+
+static int type_destroy(void *key, void *datum, void *p)
+{
+ kfree(key);
+ kfree(datum);
+ return 0;
+}
+
+static int user_destroy(void *key, void *datum, void *p)
+{
+ struct user_datum *usrdatum;
+
+ kfree(key);
+ if (datum) {
+ usrdatum = datum;
+ ebitmap_destroy(&usrdatum->roles);
+ ebitmap_destroy(&usrdatum->range.level[0].cat);
+ ebitmap_destroy(&usrdatum->range.level[1].cat);
+ ebitmap_destroy(&usrdatum->dfltlevel.cat);
}
- return info;
+ kfree(datum);
+ return 0;
+}
+
+static int sens_destroy(void *key, void *datum, void *p)
+{
+ struct level_datum *levdatum;
+
+ kfree(key);
+ if (datum) {
+ levdatum = datum;
+ ebitmap_destroy(&levdatum->level.cat);
+ }
+ kfree(datum);
+ return 0;
+}
+
+static int cat_destroy(void *key, void *datum, void *p)
+{
+ kfree(key);
+ kfree(datum);
+ return 0;
+}
+
+/* clang-format off */
+static int (*const destroy_f[SYM_NUM])(void *key, void *datum, void *datap) = {
+ common_destroy,
+ cls_destroy,
+ role_destroy,
+ type_destroy,
+ user_destroy,
+ cond_destroy_bool,
+ sens_destroy,
+ cat_destroy,
+};
+/* clang-format on */
+
+static int filenametr_destroy(void *key, void *datum, void *p)
+{
+ struct filename_trans_key *ft = key;
+ struct filename_trans_datum *next, *d = datum;
+
+ kfree(ft->name);
+ kfree(key);
+ do {
+ ebitmap_destroy(&d->stypes);
+ next = d->next;
+ kfree(d);
+ d = next;
+ } while (unlikely(d));
+ cond_resched();
+ return 0;
+}
+
+static int range_tr_destroy(void *key, void *datum, void *p)
+{
+ struct mls_range *rt = datum;
+
+ kfree(key);
+ ebitmap_destroy(&rt->level[0].cat);
+ ebitmap_destroy(&rt->level[1].cat);
+ kfree(datum);
+ cond_resched();
+ return 0;
+}
+
+static int role_tr_destroy(void *key, void *datum, void *p)
+{
+ kfree(key);
+ kfree(datum);
+ return 0;
+}
+
+static void ocontext_destroy(struct ocontext *c, unsigned int i)
+{
+ if (!c)
+ return;
+
+ context_destroy(&c->context[0]);
+ context_destroy(&c->context[1]);
+ if (i == OCON_ISID || i == OCON_FS || i == OCON_NETIF ||
+ i == OCON_FSUSE)
+ kfree(c->u.name);
+ kfree(c);
}
/*
@@ -168,10 +390,9 @@ static int roles_init(struct policydb *p)
int rc;
struct role_datum *role;
- rc = -ENOMEM;
role = kzalloc(sizeof(*role), GFP_KERNEL);
if (!role)
- goto out;
+ return -ENOMEM;
rc = -EINVAL;
role->value = ++p->p_roles.nprim;
@@ -183,7 +404,7 @@ static int roles_init(struct policydb *p)
if (!key)
goto out;
- rc = hashtab_insert(p->p_roles.table, key, role);
+ rc = symtab_insert(&p->p_roles, key, role);
if (rc)
goto out;
@@ -194,31 +415,20 @@ out:
return rc;
}
-static u32 filenametr_hash(struct hashtab *h, const void *k)
+static u32 filenametr_hash(const void *k)
{
- const struct filename_trans *ft = k;
- unsigned long hash;
- unsigned int byte_num;
- unsigned char focus;
+ const struct filename_trans_key *ft = k;
+ unsigned long salt = ft->ttype ^ ft->tclass;
- hash = ft->stype ^ ft->ttype ^ ft->tclass;
-
- byte_num = 0;
- while ((focus = ft->name[byte_num++]))
- hash = partial_name_hash(focus, hash);
- return hash & (h->size - 1);
+ return full_name_hash((void *)salt, ft->name, strlen(ft->name));
}
-static int filenametr_cmp(struct hashtab *h, const void *k1, const void *k2)
+static int filenametr_cmp(const void *k1, const void *k2)
{
- const struct filename_trans *ft1 = k1;
- const struct filename_trans *ft2 = k2;
+ const struct filename_trans_key *ft1 = k1;
+ const struct filename_trans_key *ft2 = k2;
int v;
- v = ft1->stype - ft2->stype;
- if (v)
- return v;
-
v = ft1->ttype - ft2->ttype;
if (v)
return v;
@@ -228,17 +438,28 @@ static int filenametr_cmp(struct hashtab *h, const void *k1, const void *k2)
return v;
return strcmp(ft1->name, ft2->name);
+}
+static const struct hashtab_key_params filenametr_key_params = {
+ .hash = filenametr_hash,
+ .cmp = filenametr_cmp,
+};
+
+struct filename_trans_datum *
+policydb_filenametr_search(struct policydb *p, struct filename_trans_key *key)
+{
+ return hashtab_search(&p->filename_trans, key, filenametr_key_params);
}
-static u32 rangetr_hash(struct hashtab *h, const void *k)
+static u32 rangetr_hash(const void *k)
{
const struct range_trans *key = k;
- return (key->source_type + (key->target_type << 3) +
- (key->target_class << 5)) & (h->size - 1);
+
+ return key->source_type + (key->target_type << 3) +
+ (key->target_class << 5);
}
-static int rangetr_cmp(struct hashtab *h, const void *k1, const void *k2)
+static int rangetr_cmp(const void *k1, const void *k2)
{
const struct range_trans *key1 = k1, *key2 = k2;
int v;
@@ -256,52 +477,66 @@ static int rangetr_cmp(struct hashtab *h, const void *k1, const void *k2)
return v;
}
-/*
- * Initialize a policy database structure.
- */
-static int policydb_init(struct policydb *p)
+static const struct hashtab_key_params rangetr_key_params = {
+ .hash = rangetr_hash,
+ .cmp = rangetr_cmp,
+};
+
+struct mls_range *policydb_rangetr_search(struct policydb *p,
+ struct range_trans *key)
{
- int i, rc;
+ return hashtab_search(&p->range_tr, key, rangetr_key_params);
+}
- memset(p, 0, sizeof(*p));
+static u32 role_trans_hash(const void *k)
+{
+ const struct role_trans_key *key = k;
- for (i = 0; i < SYM_NUM; i++) {
- rc = symtab_init(&p->symtab[i], symtab_sizes[i]);
- if (rc)
- goto out;
- }
+ return jhash_3words(key->role, key->type,
+ (u32)key->tclass << 16 | key->tclass, 0);
+}
- rc = avtab_init(&p->te_avtab);
- if (rc)
- goto out;
+static int role_trans_cmp(const void *k1, const void *k2)
+{
+ const struct role_trans_key *key1 = k1, *key2 = k2;
+ int v;
- rc = roles_init(p);
- if (rc)
- goto out;
+ v = key1->role - key2->role;
+ if (v)
+ return v;
- rc = cond_policydb_init(p);
- if (rc)
- goto out;
+ v = key1->type - key2->type;
+ if (v)
+ return v;
- p->filename_trans = hashtab_create(filenametr_hash, filenametr_cmp, (1 << 10));
- if (!p->filename_trans)
- goto out;
+ return key1->tclass - key2->tclass;
+}
- p->range_tr = hashtab_create(rangetr_hash, rangetr_cmp, 256);
- if (!p->range_tr)
- goto out;
+static const struct hashtab_key_params roletr_key_params = {
+ .hash = role_trans_hash,
+ .cmp = role_trans_cmp,
+};
+
+struct role_trans_datum *policydb_roletr_search(struct policydb *p,
+ struct role_trans_key *key)
+{
+ return hashtab_search(&p->role_tr, key, roletr_key_params);
+}
+
+/*
+ * Initialize a policy database structure.
+ */
+static void policydb_init(struct policydb *p)
+{
+ memset(p, 0, sizeof(*p));
+
+ avtab_init(&p->te_avtab);
+ cond_policydb_init(p);
ebitmap_init(&p->filename_trans_ttypes);
ebitmap_init(&p->policycaps);
ebitmap_init(&p->permissive_map);
-
- return 0;
-out:
- hashtab_destroy(p->filename_trans);
- hashtab_destroy(p->range_tr);
- for (i = 0; i < SYM_NUM; i++)
- hashtab_destroy(p->symtab[i].table);
- return rc;
+ ebitmap_init(&p->neveraudit_map);
}
/*
@@ -318,17 +553,14 @@ static int common_index(void *key, void *datum, void *datap)
{
struct policydb *p;
struct common_datum *comdatum;
- struct flex_array *fa;
comdatum = datum;
p = datap;
if (!comdatum->value || comdatum->value > p->p_commons.nprim)
return -EINVAL;
- fa = p->sym_val_to_name[SYM_COMMONS];
- if (flex_array_put_ptr(fa, comdatum->value - 1, key,
- GFP_KERNEL | __GFP_ZERO))
- BUG();
+ p->sym_val_to_name[SYM_COMMONS][comdatum->value - 1] = key;
+
return 0;
}
@@ -336,16 +568,13 @@ static int class_index(void *key, void *datum, void *datap)
{
struct policydb *p;
struct class_datum *cladatum;
- struct flex_array *fa;
cladatum = datum;
p = datap;
if (!cladatum->value || cladatum->value > p->p_classes.nprim)
return -EINVAL;
- fa = p->sym_val_to_name[SYM_CLASSES];
- if (flex_array_put_ptr(fa, cladatum->value - 1, key,
- GFP_KERNEL | __GFP_ZERO))
- BUG();
+
+ p->sym_val_to_name[SYM_CLASSES][cladatum->value - 1] = key;
p->class_val_to_struct[cladatum->value - 1] = cladatum;
return 0;
}
@@ -354,19 +583,14 @@ static int role_index(void *key, void *datum, void *datap)
{
struct policydb *p;
struct role_datum *role;
- struct flex_array *fa;
role = datum;
p = datap;
- if (!role->value
- || role->value > p->p_roles.nprim
- || role->bounds > p->p_roles.nprim)
+ if (!role->value || role->value > p->p_roles.nprim ||
+ role->bounds > p->p_roles.nprim)
return -EINVAL;
- fa = p->sym_val_to_name[SYM_ROLES];
- if (flex_array_put_ptr(fa, role->value - 1, key,
- GFP_KERNEL | __GFP_ZERO))
- BUG();
+ p->sym_val_to_name[SYM_ROLES][role->value - 1] = key;
p->role_val_to_struct[role->value - 1] = role;
return 0;
}
@@ -375,25 +599,16 @@ static int type_index(void *key, void *datum, void *datap)
{
struct policydb *p;
struct type_datum *typdatum;
- struct flex_array *fa;
typdatum = datum;
p = datap;
if (typdatum->primary) {
- if (!typdatum->value
- || typdatum->value > p->p_types.nprim
- || typdatum->bounds > p->p_types.nprim)
+ if (!typdatum->value || typdatum->value > p->p_types.nprim ||
+ typdatum->bounds > p->p_types.nprim)
return -EINVAL;
- fa = p->sym_val_to_name[SYM_TYPES];
- if (flex_array_put_ptr(fa, typdatum->value - 1, key,
- GFP_KERNEL | __GFP_ZERO))
- BUG();
-
- fa = p->type_val_to_struct_array;
- if (flex_array_put_ptr(fa, typdatum->value - 1, typdatum,
- GFP_KERNEL | __GFP_ZERO))
- BUG();
+ p->sym_val_to_name[SYM_TYPES][typdatum->value - 1] = key;
+ p->type_val_to_struct[typdatum->value - 1] = typdatum;
}
return 0;
@@ -403,19 +618,14 @@ static int user_index(void *key, void *datum, void *datap)
{
struct policydb *p;
struct user_datum *usrdatum;
- struct flex_array *fa;
usrdatum = datum;
p = datap;
- if (!usrdatum->value
- || usrdatum->value > p->p_users.nprim
- || usrdatum->bounds > p->p_users.nprim)
+ if (!usrdatum->value || usrdatum->value > p->p_users.nprim ||
+ usrdatum->bounds > p->p_users.nprim)
return -EINVAL;
- fa = p->sym_val_to_name[SYM_USERS];
- if (flex_array_put_ptr(fa, usrdatum->value - 1, key,
- GFP_KERNEL | __GFP_ZERO))
- BUG();
+ p->sym_val_to_name[SYM_USERS][usrdatum->value - 1] = key;
p->user_val_to_struct[usrdatum->value - 1] = usrdatum;
return 0;
}
@@ -424,19 +634,16 @@ static int sens_index(void *key, void *datum, void *datap)
{
struct policydb *p;
struct level_datum *levdatum;
- struct flex_array *fa;
levdatum = datum;
p = datap;
if (!levdatum->isalias) {
- if (!levdatum->level->sens ||
- levdatum->level->sens > p->p_levels.nprim)
+ if (!levdatum->level.sens ||
+ levdatum->level.sens > p->p_levels.nprim)
return -EINVAL;
- fa = p->sym_val_to_name[SYM_LEVELS];
- if (flex_array_put_ptr(fa, levdatum->level->sens - 1, key,
- GFP_KERNEL | __GFP_ZERO))
- BUG();
+
+ p->sym_val_to_name[SYM_LEVELS][levdatum->level.sens - 1] = key;
}
return 0;
@@ -446,7 +653,6 @@ static int cat_index(void *key, void *datum, void *datap)
{
struct policydb *p;
struct cat_datum *catdatum;
- struct flex_array *fa;
catdatum = datum;
p = datap;
@@ -454,17 +660,15 @@ static int cat_index(void *key, void *datum, void *datap)
if (!catdatum->isalias) {
if (!catdatum->value || catdatum->value > p->p_cats.nprim)
return -EINVAL;
- fa = p->sym_val_to_name[SYM_CATS];
- if (flex_array_put_ptr(fa, catdatum->value - 1, key,
- GFP_KERNEL | __GFP_ZERO))
- BUG();
+
+ p->sym_val_to_name[SYM_CATS][catdatum->value - 1] = key;
}
return 0;
}
-static int (*index_f[SYM_NUM]) (void *key, void *datum, void *datap) =
-{
+/* clang-format off */
+static int (*const index_f[SYM_NUM])(void *key, void *datum, void *datap) = {
common_index,
class_index,
role_index,
@@ -474,16 +678,20 @@ static int (*index_f[SYM_NUM]) (void *key, void *datum, void *datap) =
sens_index,
cat_index,
};
+/* clang-format on */
-#ifdef DEBUG_HASHES
-static void hash_eval(struct hashtab *h, const char *hash_name)
+#ifdef CONFIG_SECURITY_SELINUX_DEBUG
+static void hash_eval(struct hashtab *h, const char *hash_name,
+ const char *hash_details)
{
struct hashtab_info info;
hashtab_stat(h, &info);
- printk(KERN_DEBUG "SELinux: %s: %d entries and %d/%d buckets used, "
- "longest chain length %d\n", hash_name, h->nel,
- info.slots_used, h->size, info.max_chain_len);
+ pr_debug(
+ "SELinux: %s%s%s: %d entries and %d/%d buckets used, longest chain length %d, sum of chain length^2 %llu\n",
+ hash_name, hash_details ? "@" : "", hash_details ?: "", h->nel,
+ info.slots_used, h->size, info.max_chain_len,
+ info.chain2_len_sum);
}
static void symtab_hash_eval(struct symtab *s)
@@ -491,14 +699,18 @@ static void symtab_hash_eval(struct symtab *s)
int i;
for (i = 0; i < SYM_NUM; i++)
- hash_eval(s[i].table, symtab_name[i]);
+ hash_eval(&s[i].table, symtab_name[i], NULL);
}
#else
-static inline void hash_eval(struct hashtab *h, char *hash_name)
+static inline void hash_eval(struct hashtab *h, const char *hash_name,
+ const char *hash_details)
{
}
-#endif
+static inline void symtab_hash_eval(struct symtab *s)
+{
+}
+#endif /* CONFIG_SECURITY_SELINUX_DEBUG */
/*
* Define the other val_to_name and val_to_struct arrays
@@ -510,74 +722,54 @@ static int policydb_index(struct policydb *p)
{
int i, rc;
- printk(KERN_DEBUG "SELinux: %d users, %d roles, %d types, %d bools",
- p->p_users.nprim, p->p_roles.nprim, p->p_types.nprim, p->p_bools.nprim);
if (p->mls_enabled)
- printk(", %d sens, %d cats", p->p_levels.nprim,
- p->p_cats.nprim);
- printk("\n");
+ pr_debug(
+ "SELinux: %d users, %d roles, %d types, %d bools, %d sens, %d cats\n",
+ p->p_users.nprim, p->p_roles.nprim, p->p_types.nprim,
+ p->p_bools.nprim, p->p_levels.nprim, p->p_cats.nprim);
+ else
+ pr_debug("SELinux: %d users, %d roles, %d types, %d bools\n",
+ p->p_users.nprim, p->p_roles.nprim, p->p_types.nprim,
+ p->p_bools.nprim);
- printk(KERN_DEBUG "SELinux: %d classes, %d rules\n",
- p->p_classes.nprim, p->te_avtab.nel);
+ pr_debug("SELinux: %d classes, %d rules\n", p->p_classes.nprim,
+ p->te_avtab.nel);
-#ifdef DEBUG_HASHES
avtab_hash_eval(&p->te_avtab, "rules");
symtab_hash_eval(p->symtab);
-#endif
- rc = -ENOMEM;
- p->class_val_to_struct =
- kmalloc(p->p_classes.nprim * sizeof(*(p->class_val_to_struct)),
- GFP_KERNEL);
+ p->class_val_to_struct = kcalloc(p->p_classes.nprim,
+ sizeof(*p->class_val_to_struct),
+ GFP_KERNEL);
if (!p->class_val_to_struct)
- goto out;
+ return -ENOMEM;
- rc = -ENOMEM;
- p->role_val_to_struct =
- kmalloc(p->p_roles.nprim * sizeof(*(p->role_val_to_struct)),
- GFP_KERNEL);
+ p->role_val_to_struct = kcalloc(
+ p->p_roles.nprim, sizeof(*p->role_val_to_struct), GFP_KERNEL);
if (!p->role_val_to_struct)
- goto out;
+ return -ENOMEM;
- rc = -ENOMEM;
- p->user_val_to_struct =
- kmalloc(p->p_users.nprim * sizeof(*(p->user_val_to_struct)),
- GFP_KERNEL);
+ p->user_val_to_struct = kcalloc(
+ p->p_users.nprim, sizeof(*p->user_val_to_struct), GFP_KERNEL);
if (!p->user_val_to_struct)
- goto out;
+ return -ENOMEM;
- /* Yes, I want the sizeof the pointer, not the structure */
- rc = -ENOMEM;
- p->type_val_to_struct_array = flex_array_alloc(sizeof(struct type_datum *),
- p->p_types.nprim,
- GFP_KERNEL | __GFP_ZERO);
- if (!p->type_val_to_struct_array)
- goto out;
-
- rc = flex_array_prealloc(p->type_val_to_struct_array, 0,
- p->p_types.nprim, GFP_KERNEL | __GFP_ZERO);
- if (rc)
- goto out;
+ p->type_val_to_struct = kvcalloc(
+ p->p_types.nprim, sizeof(*p->type_val_to_struct), GFP_KERNEL);
+ if (!p->type_val_to_struct)
+ return -ENOMEM;
rc = cond_init_bool_indexes(p);
if (rc)
goto out;
for (i = 0; i < SYM_NUM; i++) {
- rc = -ENOMEM;
- p->sym_val_to_name[i] = flex_array_alloc(sizeof(char *),
- p->symtab[i].nprim,
- GFP_KERNEL | __GFP_ZERO);
+ p->sym_val_to_name[i] = kvcalloc(p->symtab[i].nprim,
+ sizeof(char *), GFP_KERNEL);
if (!p->sym_val_to_name[i])
- goto out;
-
- rc = flex_array_prealloc(p->sym_val_to_name[i],
- 0, p->symtab[i].nprim,
- GFP_KERNEL | __GFP_ZERO);
- if (rc)
- goto out;
+ return -ENOMEM;
- rc = hashtab_map(p->symtab[i].table, index_f[i], p);
+ rc = hashtab_map(&p->symtab[i].table, index_f[i], p);
if (rc)
goto out;
}
@@ -587,208 +779,28 @@ out:
}
/*
- * The following *_destroy functions are used to
- * free any memory allocated for each kind of
- * symbol data in the policy database.
- */
-
-static int perm_destroy(void *key, void *datum, void *p)
-{
- kfree(key);
- kfree(datum);
- return 0;
-}
-
-static int common_destroy(void *key, void *datum, void *p)
-{
- struct common_datum *comdatum;
-
- kfree(key);
- if (datum) {
- comdatum = datum;
- hashtab_map(comdatum->permissions.table, perm_destroy, NULL);
- hashtab_destroy(comdatum->permissions.table);
- }
- kfree(datum);
- return 0;
-}
-
-static int cls_destroy(void *key, void *datum, void *p)
-{
- struct class_datum *cladatum;
- struct constraint_node *constraint, *ctemp;
- struct constraint_expr *e, *etmp;
-
- kfree(key);
- if (datum) {
- cladatum = datum;
- hashtab_map(cladatum->permissions.table, perm_destroy, NULL);
- hashtab_destroy(cladatum->permissions.table);
- constraint = cladatum->constraints;
- while (constraint) {
- e = constraint->expr;
- while (e) {
- ebitmap_destroy(&e->names);
- etmp = e;
- e = e->next;
- kfree(etmp);
- }
- ctemp = constraint;
- constraint = constraint->next;
- kfree(ctemp);
- }
-
- constraint = cladatum->validatetrans;
- while (constraint) {
- e = constraint->expr;
- while (e) {
- ebitmap_destroy(&e->names);
- etmp = e;
- e = e->next;
- kfree(etmp);
- }
- ctemp = constraint;
- constraint = constraint->next;
- kfree(ctemp);
- }
-
- kfree(cladatum->comkey);
- }
- kfree(datum);
- return 0;
-}
-
-static int role_destroy(void *key, void *datum, void *p)
-{
- struct role_datum *role;
-
- kfree(key);
- if (datum) {
- role = datum;
- ebitmap_destroy(&role->dominates);
- ebitmap_destroy(&role->types);
- }
- kfree(datum);
- return 0;
-}
-
-static int type_destroy(void *key, void *datum, void *p)
-{
- kfree(key);
- kfree(datum);
- return 0;
-}
-
-static int user_destroy(void *key, void *datum, void *p)
-{
- struct user_datum *usrdatum;
-
- kfree(key);
- if (datum) {
- usrdatum = datum;
- ebitmap_destroy(&usrdatum->roles);
- ebitmap_destroy(&usrdatum->range.level[0].cat);
- ebitmap_destroy(&usrdatum->range.level[1].cat);
- ebitmap_destroy(&usrdatum->dfltlevel.cat);
- }
- kfree(datum);
- return 0;
-}
-
-static int sens_destroy(void *key, void *datum, void *p)
-{
- struct level_datum *levdatum;
-
- kfree(key);
- if (datum) {
- levdatum = datum;
- ebitmap_destroy(&levdatum->level->cat);
- kfree(levdatum->level);
- }
- kfree(datum);
- return 0;
-}
-
-static int cat_destroy(void *key, void *datum, void *p)
-{
- kfree(key);
- kfree(datum);
- return 0;
-}
-
-static int (*destroy_f[SYM_NUM]) (void *key, void *datum, void *datap) =
-{
- common_destroy,
- cls_destroy,
- role_destroy,
- type_destroy,
- user_destroy,
- cond_destroy_bool,
- sens_destroy,
- cat_destroy,
-};
-
-static int filenametr_destroy(void *key, void *datum, void *p)
-{
- struct filename_trans *ft = key;
- kfree(ft->name);
- kfree(key);
- kfree(datum);
- cond_resched();
- return 0;
-}
-
-static int range_tr_destroy(void *key, void *datum, void *p)
-{
- struct mls_range *rt = datum;
- kfree(key);
- ebitmap_destroy(&rt->level[0].cat);
- ebitmap_destroy(&rt->level[1].cat);
- kfree(datum);
- cond_resched();
- return 0;
-}
-
-static void ocontext_destroy(struct ocontext *c, int i)
-{
- if (!c)
- return;
-
- context_destroy(&c->context[0]);
- context_destroy(&c->context[1]);
- if (i == OCON_ISID || i == OCON_FS ||
- i == OCON_NETIF || i == OCON_FSUSE)
- kfree(c->u.name);
- kfree(c);
-}
-
-/*
* Free any memory allocated by a policy database structure.
*/
void policydb_destroy(struct policydb *p)
{
struct ocontext *c, *ctmp;
struct genfs *g, *gtmp;
- int i;
+ u32 i;
struct role_allow *ra, *lra = NULL;
- struct role_trans *tr, *ltr = NULL;
for (i = 0; i < SYM_NUM; i++) {
cond_resched();
- hashtab_map(p->symtab[i].table, destroy_f[i], NULL);
- hashtab_destroy(p->symtab[i].table);
+ hashtab_map(&p->symtab[i].table, destroy_f[i], NULL);
+ hashtab_destroy(&p->symtab[i].table);
}
- for (i = 0; i < SYM_NUM; i++) {
- if (p->sym_val_to_name[i])
- flex_array_free(p->sym_val_to_name[i]);
- }
+ for (i = 0; i < SYM_NUM; i++)
+ kvfree(p->sym_val_to_name[i]);
kfree(p->class_val_to_struct);
kfree(p->role_val_to_struct);
kfree(p->user_val_to_struct);
- if (p->type_val_to_struct_array)
- flex_array_free(p->type_val_to_struct_array);
+ kvfree(p->type_val_to_struct);
avtab_destroy(&p->te_avtab);
@@ -821,12 +833,8 @@ void policydb_destroy(struct policydb *p)
cond_policydb_destroy(p);
- for (tr = p->role_tr; tr; tr = tr->next) {
- cond_resched();
- kfree(ltr);
- ltr = tr;
- }
- kfree(ltr);
+ hashtab_map(&p->role_tr, role_tr_destroy, NULL);
+ hashtab_destroy(&p->role_tr);
for (ra = p->role_allow; ra; ra = ra->next) {
cond_resched();
@@ -835,29 +843,22 @@ void policydb_destroy(struct policydb *p)
}
kfree(lra);
- hashtab_map(p->filename_trans, filenametr_destroy, NULL);
- hashtab_destroy(p->filename_trans);
+ hashtab_map(&p->filename_trans, filenametr_destroy, NULL);
+ hashtab_destroy(&p->filename_trans);
- hashtab_map(p->range_tr, range_tr_destroy, NULL);
- hashtab_destroy(p->range_tr);
+ hashtab_map(&p->range_tr, range_tr_destroy, NULL);
+ hashtab_destroy(&p->range_tr);
if (p->type_attr_map_array) {
- for (i = 0; i < p->p_types.nprim; i++) {
- struct ebitmap *e;
-
- e = flex_array_get(p->type_attr_map_array, i);
- if (!e)
- continue;
- ebitmap_destroy(e);
- }
- flex_array_free(p->type_attr_map_array);
+ for (i = 0; i < p->p_types.nprim; i++)
+ ebitmap_destroy(&p->type_attr_map_array[i]);
+ kvfree(p->type_attr_map_array);
}
ebitmap_destroy(&p->filename_trans_ttypes);
ebitmap_destroy(&p->policycaps);
ebitmap_destroy(&p->permissive_map);
-
- return;
+ ebitmap_destroy(&p->neveraudit_map);
}
/*
@@ -867,33 +868,68 @@ void policydb_destroy(struct policydb *p)
int policydb_load_isids(struct policydb *p, struct sidtab *s)
{
struct ocontext *head, *c;
+ bool isid_init;
int rc;
rc = sidtab_init(s);
if (rc) {
- printk(KERN_ERR "SELinux: out of memory on SID table init\n");
- goto out;
+ pr_err("SELinux: out of memory on SID table init\n");
+ return rc;
}
+ isid_init = ebitmap_get_bit(&p->policycaps,
+ POLICYDB_CAP_USERSPACE_INITIAL_CONTEXT);
+
head = p->ocontexts[OCON_ISID];
for (c = head; c; c = c->next) {
- rc = -EINVAL;
- if (!c->context[0].user) {
- printk(KERN_ERR "SELinux: SID %s was never defined.\n",
- c->u.name);
- goto out;
+ u32 sid = c->sid[0];
+ const char *name = security_get_initial_sid_context(sid);
+
+ if (sid == SECSID_NULL) {
+ pr_err("SELinux: SID 0 was assigned a context.\n");
+ sidtab_destroy(s);
+ return -EINVAL;
}
- rc = sidtab_insert(s, c->sid[0], &c->context[0]);
+ /* Ignore initial SIDs unused by this kernel. */
+ if (!name)
+ continue;
+
+ /*
+ * Also ignore SECINITSID_INIT if the policy doesn't declare
+ * support for it
+ */
+ if (sid == SECINITSID_INIT && !isid_init)
+ continue;
+
+ rc = sidtab_set_initial(s, sid, &c->context[0]);
if (rc) {
- printk(KERN_ERR "SELinux: unable to load initial SID %s.\n",
- c->u.name);
- goto out;
+ pr_err("SELinux: unable to load initial SID %s.\n",
+ name);
+ sidtab_destroy(s);
+ return rc;
+ }
+
+ /*
+ * If the policy doesn't support the "userspace_initial_context"
+ * capability, set SECINITSID_INIT to the same context as
+ * SECINITSID_KERNEL. This ensures the same behavior as before
+ * the reintroduction of SECINITSID_INIT, where all tasks
+ * started before policy load would initially get the context
+ * corresponding to SECINITSID_KERNEL.
+ */
+ if (sid == SECINITSID_KERNEL && !isid_init) {
+ rc = sidtab_set_initial(s, SECINITSID_INIT,
+ &c->context[0]);
+ if (rc) {
+ pr_err("SELinux: unable to load initial SID %s.\n",
+ name);
+ sidtab_destroy(s);
+ return rc;
+ }
}
}
- rc = 0;
-out:
- return rc;
+ return 0;
}
int policydb_class_isvalid(struct policydb *p, unsigned int class)
@@ -940,7 +976,7 @@ int policydb_context_isvalid(struct policydb *p, struct context *c)
* Role must be authorized for the type.
*/
role = p->role_val_to_struct[c->role - 1];
- if (!ebitmap_get_bit(&role->types, c->type - 1))
+ if (!role || !ebitmap_get_bit(&role->types, c->type - 1))
/* role may not be associated with type */
return 0;
@@ -966,7 +1002,7 @@ int policydb_context_isvalid(struct policydb *p, struct context *c)
* Read a MLS range structure from a policydb binary
* representation file.
*/
-static int mls_read_range_helper(struct mls_range *r, void *fp)
+static int mls_read_range_helper(struct mls_range *r, struct policy_file *fp)
{
__le32 buf[2];
u32 items;
@@ -979,13 +1015,13 @@ static int mls_read_range_helper(struct mls_range *r, void *fp)
rc = -EINVAL;
items = le32_to_cpu(buf[0]);
if (items > ARRAY_SIZE(buf)) {
- printk(KERN_ERR "SELinux: mls: range overflow\n");
+ pr_err("SELinux: mls: range overflow\n");
goto out;
}
rc = next_entry(buf, fp, sizeof(u32) * items);
if (rc) {
- printk(KERN_ERR "SELinux: mls: truncated range\n");
+ pr_err("SELinux: mls: truncated range\n");
goto out;
}
@@ -997,19 +1033,19 @@ static int mls_read_range_helper(struct mls_range *r, void *fp)
rc = ebitmap_read(&r->level[0].cat, fp);
if (rc) {
- printk(KERN_ERR "SELinux: mls: error reading low categories\n");
+ pr_err("SELinux: mls: error reading low categories\n");
goto out;
}
if (items > 1) {
rc = ebitmap_read(&r->level[1].cat, fp);
if (rc) {
- printk(KERN_ERR "SELinux: mls: error reading high categories\n");
+ pr_err("SELinux: mls: error reading high categories\n");
goto bad_high;
}
} else {
rc = ebitmap_cpy(&r->level[1].cat, &r->level[0].cat);
if (rc) {
- printk(KERN_ERR "SELinux: mls: out of memory\n");
+ pr_err("SELinux: mls: out of memory\n");
goto bad_high;
}
}
@@ -1025,16 +1061,15 @@ out:
* Read and validate a security context structure
* from a policydb binary representation file.
*/
-static int context_read_and_validate(struct context *c,
- struct policydb *p,
- void *fp)
+static int context_read_and_validate(struct context *c, struct policydb *p,
+ struct policy_file *fp)
{
__le32 buf[3];
int rc;
rc = next_entry(buf, fp, sizeof buf);
if (rc) {
- printk(KERN_ERR "SELinux: context truncated\n");
+ pr_err("SELinux: context truncated\n");
goto out;
}
c->user = le32_to_cpu(buf[0]);
@@ -1043,14 +1078,14 @@ static int context_read_and_validate(struct context *c,
if (p->policyvers >= POLICYDB_VERSION_MLS) {
rc = mls_read_range_helper(&c->range, fp);
if (rc) {
- printk(KERN_ERR "SELinux: error reading MLS range of context\n");
+ pr_err("SELinux: error reading MLS range of context\n");
goto out;
}
}
rc = -EINVAL;
if (!policydb_context_isvalid(p, c)) {
- printk(KERN_ERR "SELinux: invalid security context\n");
+ pr_err("SELinux: invalid security context\n");
context_destroy(c);
goto out;
}
@@ -1065,7 +1100,30 @@ out:
* binary representation file.
*/
-static int perm_read(struct policydb *p, struct hashtab *h, void *fp)
+int str_read(char **strp, gfp_t flags, struct policy_file *fp, u32 len)
+{
+ int rc;
+ char *str;
+
+ if ((len == 0) || (len == (u32)-1))
+ return -EINVAL;
+
+ str = kmalloc(len + 1, flags | __GFP_NOWARN);
+ if (!str)
+ return -ENOMEM;
+
+ rc = next_entry(str, fp, len);
+ if (rc) {
+ kfree(str);
+ return rc;
+ }
+
+ str[len] = '\0';
+ *strp = str;
+ return 0;
+}
+
+static int perm_read(struct policydb *p, struct symtab *s, struct policy_file *fp)
{
char *key = NULL;
struct perm_datum *perdatum;
@@ -1073,10 +1131,9 @@ static int perm_read(struct policydb *p, struct hashtab *h, void *fp)
__le32 buf[2];
u32 len;
- rc = -ENOMEM;
perdatum = kzalloc(sizeof(*perdatum), GFP_KERNEL);
if (!perdatum)
- goto bad;
+ return -ENOMEM;
rc = next_entry(buf, fp, sizeof buf);
if (rc)
@@ -1085,17 +1142,11 @@ static int perm_read(struct policydb *p, struct hashtab *h, void *fp)
len = le32_to_cpu(buf[0]);
perdatum->value = le32_to_cpu(buf[1]);
- rc = -ENOMEM;
- key = kmalloc(len + 1, GFP_KERNEL);
- if (!key)
- goto bad;
-
- rc = next_entry(key, fp, len);
+ rc = str_read(&key, GFP_KERNEL, fp, len);
if (rc)
goto bad;
- key[len] = '\0';
- rc = hashtab_insert(h, key, perdatum);
+ rc = symtab_insert(s, key, perdatum);
if (rc)
goto bad;
@@ -1105,18 +1156,17 @@ bad:
return rc;
}
-static int common_read(struct policydb *p, struct hashtab *h, void *fp)
+static int common_read(struct policydb *p, struct symtab *s, struct policy_file *fp)
{
char *key = NULL;
struct common_datum *comdatum;
__le32 buf[4];
- u32 len, nel;
- int i, rc;
+ u32 i, len, nel;
+ int rc;
- rc = -ENOMEM;
comdatum = kzalloc(sizeof(*comdatum), GFP_KERNEL);
if (!comdatum)
- goto bad;
+ return -ENOMEM;
rc = next_entry(buf, fp, sizeof buf);
if (rc)
@@ -1124,30 +1174,26 @@ static int common_read(struct policydb *p, struct hashtab *h, void *fp)
len = le32_to_cpu(buf[0]);
comdatum->value = le32_to_cpu(buf[1]);
+ nel = le32_to_cpu(buf[3]);
- rc = symtab_init(&comdatum->permissions, PERM_SYMTAB_SIZE);
+ rc = symtab_init(&comdatum->permissions, nel);
if (rc)
goto bad;
comdatum->permissions.nprim = le32_to_cpu(buf[2]);
- nel = le32_to_cpu(buf[3]);
-
- rc = -ENOMEM;
- key = kmalloc(len + 1, GFP_KERNEL);
- if (!key)
- goto bad;
- rc = next_entry(key, fp, len);
+ rc = str_read(&key, GFP_KERNEL, fp, len);
if (rc)
goto bad;
- key[len] = '\0';
for (i = 0; i < nel; i++) {
- rc = perm_read(p, comdatum->permissions.table, fp);
+ rc = perm_read(p, &comdatum->permissions, fp);
if (rc)
goto bad;
}
- rc = hashtab_insert(h, key, comdatum);
+ hash_eval(&comdatum->permissions.table, "common_permissions", key);
+
+ rc = symtab_insert(s, key, comdatum);
if (rc)
goto bad;
return 0;
@@ -1156,14 +1202,38 @@ bad:
return rc;
}
-static int read_cons_helper(struct constraint_node **nodep, int ncons,
- int allowxtarget, void *fp)
+static void type_set_init(struct type_set *t)
+{
+ ebitmap_init(&t->types);
+ ebitmap_init(&t->negset);
+}
+
+static int type_set_read(struct type_set *t, struct policy_file *fp)
+{
+ __le32 buf[1];
+ int rc;
+
+ if (ebitmap_read(&t->types, fp))
+ return -EINVAL;
+ if (ebitmap_read(&t->negset, fp))
+ return -EINVAL;
+
+ rc = next_entry(buf, fp, sizeof(u32));
+ if (rc < 0)
+ return -EINVAL;
+ t->flags = le32_to_cpu(buf[0]);
+
+ return 0;
+}
+
+static int read_cons_helper(struct policydb *p, struct constraint_node **nodep,
+ u32 ncons, int allowxtarget, struct policy_file *fp)
{
struct constraint_node *c, *lc;
struct constraint_expr *e, *le;
__le32 buf[3];
- u32 nexpr;
- int rc, i, j, depth;
+ u32 i, j, nexpr;
+ int rc, depth;
lc = NULL;
for (i = 0; i < ncons; i++) {
@@ -1225,6 +1295,18 @@ static int read_cons_helper(struct constraint_node **nodep, int ncons,
rc = ebitmap_read(&e->names, fp);
if (rc)
return rc;
+ if (p->policyvers >=
+ POLICYDB_VERSION_CONSTRAINT_NAMES) {
+ e->type_names =
+ kzalloc(sizeof(*e->type_names),
+ GFP_KERNEL);
+ if (!e->type_names)
+ return -ENOMEM;
+ type_set_init(e->type_names);
+ rc = type_set_read(e->type_names, fp);
+ if (rc)
+ return rc;
+ }
break;
default:
return -EINVAL;
@@ -1239,69 +1321,61 @@ static int read_cons_helper(struct constraint_node **nodep, int ncons,
return 0;
}
-static int class_read(struct policydb *p, struct hashtab *h, void *fp)
+static int class_read(struct policydb *p, struct symtab *s, struct policy_file *fp)
{
char *key = NULL;
struct class_datum *cladatum;
__le32 buf[6];
- u32 len, len2, ncons, nel;
- int i, rc;
+ u32 i, len, len2, ncons, nel;
+ int rc;
- rc = -ENOMEM;
cladatum = kzalloc(sizeof(*cladatum), GFP_KERNEL);
if (!cladatum)
- goto bad;
+ return -ENOMEM;
- rc = next_entry(buf, fp, sizeof(u32)*6);
+ rc = next_entry(buf, fp, sizeof(u32) * 6);
if (rc)
goto bad;
len = le32_to_cpu(buf[0]);
len2 = le32_to_cpu(buf[1]);
cladatum->value = le32_to_cpu(buf[2]);
+ nel = le32_to_cpu(buf[4]);
- rc = symtab_init(&cladatum->permissions, PERM_SYMTAB_SIZE);
+ rc = symtab_init(&cladatum->permissions, nel);
if (rc)
goto bad;
cladatum->permissions.nprim = le32_to_cpu(buf[3]);
- nel = le32_to_cpu(buf[4]);
ncons = le32_to_cpu(buf[5]);
- rc = -ENOMEM;
- key = kmalloc(len + 1, GFP_KERNEL);
- if (!key)
- goto bad;
-
- rc = next_entry(key, fp, len);
+ rc = str_read(&key, GFP_KERNEL, fp, len);
if (rc)
goto bad;
- key[len] = '\0';
if (len2) {
- rc = -ENOMEM;
- cladatum->comkey = kmalloc(len2 + 1, GFP_KERNEL);
- if (!cladatum->comkey)
- goto bad;
- rc = next_entry(cladatum->comkey, fp, len2);
+ rc = str_read(&cladatum->comkey, GFP_KERNEL, fp, len2);
if (rc)
goto bad;
- cladatum->comkey[len2] = '\0';
rc = -EINVAL;
- cladatum->comdatum = hashtab_search(p->p_commons.table, cladatum->comkey);
+ cladatum->comdatum =
+ symtab_search(&p->p_commons, cladatum->comkey);
if (!cladatum->comdatum) {
- printk(KERN_ERR "SELinux: unknown common %s\n", cladatum->comkey);
+ pr_err("SELinux: unknown common %s\n",
+ cladatum->comkey);
goto bad;
}
}
for (i = 0; i < nel; i++) {
- rc = perm_read(p, cladatum->permissions.table, fp);
+ rc = perm_read(p, &cladatum->permissions, fp);
if (rc)
goto bad;
}
- rc = read_cons_helper(&cladatum->constraints, ncons, 0, fp);
+ hash_eval(&cladatum->permissions.table, "class_permissions", key);
+
+ rc = read_cons_helper(p, &cladatum->constraints, ncons, 0, fp);
if (rc)
goto bad;
@@ -1311,7 +1385,8 @@ static int class_read(struct policydb *p, struct hashtab *h, void *fp)
if (rc)
goto bad;
ncons = le32_to_cpu(buf[0]);
- rc = read_cons_helper(&cladatum->validatetrans, ncons, 1, fp);
+ rc = read_cons_helper(p, &cladatum->validatetrans, ncons, 1,
+ fp);
if (rc)
goto bad;
}
@@ -1333,7 +1408,7 @@ static int class_read(struct policydb *p, struct hashtab *h, void *fp)
cladatum->default_type = le32_to_cpu(buf[0]);
}
- rc = hashtab_insert(h, key, cladatum);
+ rc = symtab_insert(s, key, cladatum);
if (rc)
goto bad;
@@ -1343,18 +1418,18 @@ bad:
return rc;
}
-static int role_read(struct policydb *p, struct hashtab *h, void *fp)
+static int role_read(struct policydb *p, struct symtab *s, struct policy_file *fp)
{
char *key = NULL;
struct role_datum *role;
- int rc, to_read = 2;
+ int rc;
+ unsigned int to_read = 2;
__le32 buf[3];
u32 len;
- rc = -ENOMEM;
role = kzalloc(sizeof(*role), GFP_KERNEL);
if (!role)
- goto bad;
+ return -ENOMEM;
if (p->policyvers >= POLICYDB_VERSION_BOUNDARY)
to_read = 3;
@@ -1368,15 +1443,9 @@ static int role_read(struct policydb *p, struct hashtab *h, void *fp)
if (p->policyvers >= POLICYDB_VERSION_BOUNDARY)
role->bounds = le32_to_cpu(buf[2]);
- rc = -ENOMEM;
- key = kmalloc(len + 1, GFP_KERNEL);
- if (!key)
- goto bad;
-
- rc = next_entry(key, fp, len);
+ rc = str_read(&key, GFP_KERNEL, fp, len);
if (rc)
goto bad;
- key[len] = '\0';
rc = ebitmap_read(&role->dominates, fp);
if (rc)
@@ -1389,7 +1458,7 @@ static int role_read(struct policydb *p, struct hashtab *h, void *fp)
if (strcmp(key, OBJECT_R) == 0) {
rc = -EINVAL;
if (role->value != OBJECT_R_VAL) {
- printk(KERN_ERR "SELinux: Role %s has wrong value %d\n",
+ pr_err("SELinux: Role %s has wrong value %d\n",
OBJECT_R, role->value);
goto bad;
}
@@ -1397,7 +1466,7 @@ static int role_read(struct policydb *p, struct hashtab *h, void *fp)
goto bad;
}
- rc = hashtab_insert(h, key, role);
+ rc = symtab_insert(s, key, role);
if (rc)
goto bad;
return 0;
@@ -1406,18 +1475,18 @@ bad:
return rc;
}
-static int type_read(struct policydb *p, struct hashtab *h, void *fp)
+static int type_read(struct policydb *p, struct symtab *s, struct policy_file *fp)
{
char *key = NULL;
struct type_datum *typdatum;
- int rc, to_read = 3;
+ int rc;
+ unsigned int to_read = 3;
__le32 buf[4];
u32 len;
- rc = -ENOMEM;
typdatum = kzalloc(sizeof(*typdatum), GFP_KERNEL);
if (!typdatum)
- goto bad;
+ return -ENOMEM;
if (p->policyvers >= POLICYDB_VERSION_BOUNDARY)
to_read = 4;
@@ -1441,16 +1510,11 @@ static int type_read(struct policydb *p, struct hashtab *h, void *fp)
typdatum->primary = le32_to_cpu(buf[2]);
}
- rc = -ENOMEM;
- key = kmalloc(len + 1, GFP_KERNEL);
- if (!key)
- goto bad;
- rc = next_entry(key, fp, len);
+ rc = str_read(&key, GFP_KERNEL, fp, len);
if (rc)
goto bad;
- key[len] = '\0';
- rc = hashtab_insert(h, key, typdatum);
+ rc = symtab_insert(s, key, typdatum);
if (rc)
goto bad;
return 0;
@@ -1459,12 +1523,11 @@ bad:
return rc;
}
-
/*
* Read a MLS level structure from a policydb binary
* representation file.
*/
-static int mls_read_level(struct mls_level *lp, void *fp)
+static int mls_read_level(struct mls_level *lp, struct policy_file *fp)
{
__le32 buf[1];
int rc;
@@ -1473,31 +1536,31 @@ static int mls_read_level(struct mls_level *lp, void *fp)
rc = next_entry(buf, fp, sizeof buf);
if (rc) {
- printk(KERN_ERR "SELinux: mls: truncated level\n");
+ pr_err("SELinux: mls: truncated level\n");
return rc;
}
lp->sens = le32_to_cpu(buf[0]);
rc = ebitmap_read(&lp->cat, fp);
if (rc) {
- printk(KERN_ERR "SELinux: mls: error reading level categories\n");
+ pr_err("SELinux: mls: error reading level categories\n");
return rc;
}
return 0;
}
-static int user_read(struct policydb *p, struct hashtab *h, void *fp)
+static int user_read(struct policydb *p, struct symtab *s, struct policy_file *fp)
{
char *key = NULL;
struct user_datum *usrdatum;
- int rc, to_read = 2;
+ int rc;
+ unsigned int to_read = 2;
__le32 buf[3];
u32 len;
- rc = -ENOMEM;
usrdatum = kzalloc(sizeof(*usrdatum), GFP_KERNEL);
if (!usrdatum)
- goto bad;
+ return -ENOMEM;
if (p->policyvers >= POLICYDB_VERSION_BOUNDARY)
to_read = 3;
@@ -1511,14 +1574,9 @@ static int user_read(struct policydb *p, struct hashtab *h, void *fp)
if (p->policyvers >= POLICYDB_VERSION_BOUNDARY)
usrdatum->bounds = le32_to_cpu(buf[2]);
- rc = -ENOMEM;
- key = kmalloc(len + 1, GFP_KERNEL);
- if (!key)
- goto bad;
- rc = next_entry(key, fp, len);
+ rc = str_read(&key, GFP_KERNEL, fp, len);
if (rc)
goto bad;
- key[len] = '\0';
rc = ebitmap_read(&usrdatum->roles, fp);
if (rc)
@@ -1533,7 +1591,7 @@ static int user_read(struct policydb *p, struct hashtab *h, void *fp)
goto bad;
}
- rc = hashtab_insert(h, key, usrdatum);
+ rc = symtab_insert(s, key, usrdatum);
if (rc)
goto bad;
return 0;
@@ -1542,7 +1600,7 @@ bad:
return rc;
}
-static int sens_read(struct policydb *p, struct hashtab *h, void *fp)
+static int sens_read(struct policydb *p, struct symtab *s, struct policy_file *fp)
{
char *key = NULL;
struct level_datum *levdatum;
@@ -1550,10 +1608,9 @@ static int sens_read(struct policydb *p, struct hashtab *h, void *fp)
__le32 buf[2];
u32 len;
- rc = -ENOMEM;
- levdatum = kzalloc(sizeof(*levdatum), GFP_ATOMIC);
+ levdatum = kzalloc(sizeof(*levdatum), GFP_KERNEL);
if (!levdatum)
- goto bad;
+ return -ENOMEM;
rc = next_entry(buf, fp, sizeof buf);
if (rc)
@@ -1562,25 +1619,15 @@ static int sens_read(struct policydb *p, struct hashtab *h, void *fp)
len = le32_to_cpu(buf[0]);
levdatum->isalias = le32_to_cpu(buf[1]);
- rc = -ENOMEM;
- key = kmalloc(len + 1, GFP_ATOMIC);
- if (!key)
- goto bad;
- rc = next_entry(key, fp, len);
+ rc = str_read(&key, GFP_KERNEL, fp, len);
if (rc)
goto bad;
- key[len] = '\0';
- rc = -ENOMEM;
- levdatum->level = kmalloc(sizeof(struct mls_level), GFP_ATOMIC);
- if (!levdatum->level)
- goto bad;
-
- rc = mls_read_level(levdatum->level, fp);
+ rc = mls_read_level(&levdatum->level, fp);
if (rc)
goto bad;
- rc = hashtab_insert(h, key, levdatum);
+ rc = symtab_insert(s, key, levdatum);
if (rc)
goto bad;
return 0;
@@ -1589,7 +1636,7 @@ bad:
return rc;
}
-static int cat_read(struct policydb *p, struct hashtab *h, void *fp)
+static int cat_read(struct policydb *p, struct symtab *s, struct policy_file *fp)
{
char *key = NULL;
struct cat_datum *catdatum;
@@ -1597,10 +1644,9 @@ static int cat_read(struct policydb *p, struct hashtab *h, void *fp)
__le32 buf[3];
u32 len;
- rc = -ENOMEM;
- catdatum = kzalloc(sizeof(*catdatum), GFP_ATOMIC);
+ catdatum = kzalloc(sizeof(*catdatum), GFP_KERNEL);
if (!catdatum)
- goto bad;
+ return -ENOMEM;
rc = next_entry(buf, fp, sizeof buf);
if (rc)
@@ -1610,16 +1656,11 @@ static int cat_read(struct policydb *p, struct hashtab *h, void *fp)
catdatum->value = le32_to_cpu(buf[1]);
catdatum->isalias = le32_to_cpu(buf[2]);
- rc = -ENOMEM;
- key = kmalloc(len + 1, GFP_ATOMIC);
- if (!key)
- goto bad;
- rc = next_entry(key, fp, len);
+ rc = str_read(&key, GFP_KERNEL, fp, len);
if (rc)
goto bad;
- key[len] = '\0';
- rc = hashtab_insert(h, key, catdatum);
+ rc = symtab_insert(s, key, catdatum);
if (rc)
goto bad;
return 0;
@@ -1628,8 +1669,9 @@ bad:
return rc;
}
-static int (*read_f[SYM_NUM]) (struct policydb *p, struct hashtab *h, void *fp) =
-{
+/* clang-format off */
+static int (*const read_f[SYM_NUM])(struct policydb *p, struct symtab *s,
+ struct policy_file *fp) = {
common_read,
class_read,
role_read,
@@ -1639,6 +1681,7 @@ static int (*read_f[SYM_NUM]) (struct policydb *p, struct hashtab *h, void *fp)
sens_read,
cat_read,
};
+/* clang-format on */
static int user_bounds_sanity_check(void *key, void *datum, void *datap)
{
@@ -1649,22 +1692,22 @@ static int user_bounds_sanity_check(void *key, void *datum, void *datap)
upper = user = datum;
while (upper->bounds) {
struct ebitmap_node *node;
- unsigned long bit;
+ u32 bit;
if (++depth == POLICYDB_BOUNDS_MAXDEPTH) {
- printk(KERN_ERR "SELinux: user %s: "
- "too deep or looped boundary",
- (char *) key);
+ pr_err("SELinux: user %s: "
+ "too deep or looped boundary\n",
+ (char *)key);
return -EINVAL;
}
upper = p->user_val_to_struct[upper->bounds - 1];
- ebitmap_for_each_positive_bit(&user->roles, node, bit) {
+ ebitmap_for_each_positive_bit(&user->roles, node, bit)
+ {
if (ebitmap_get_bit(&upper->roles, bit))
continue;
- printk(KERN_ERR
- "SELinux: boundary violated policy: "
+ pr_err("SELinux: boundary violated policy: "
"user=%s role=%s bounds=%s\n",
sym_name(p, SYM_USERS, user->value - 1),
sym_name(p, SYM_ROLES, bit),
@@ -1686,22 +1729,22 @@ static int role_bounds_sanity_check(void *key, void *datum, void *datap)
upper = role = datum;
while (upper->bounds) {
struct ebitmap_node *node;
- unsigned long bit;
+ u32 bit;
if (++depth == POLICYDB_BOUNDS_MAXDEPTH) {
- printk(KERN_ERR "SELinux: role %s: "
+ pr_err("SELinux: role %s: "
"too deep or looped bounds\n",
- (char *) key);
+ (char *)key);
return -EINVAL;
}
upper = p->role_val_to_struct[upper->bounds - 1];
- ebitmap_for_each_positive_bit(&role->types, node, bit) {
+ ebitmap_for_each_positive_bit(&role->types, node, bit)
+ {
if (ebitmap_get_bit(&upper->types, bit))
continue;
- printk(KERN_ERR
- "SELinux: boundary violated policy: "
+ pr_err("SELinux: boundary violated policy: "
"role=%s type=%s bounds=%s\n",
sym_name(p, SYM_ROLES, role->value - 1),
sym_name(p, SYM_TYPES, bit),
@@ -1723,20 +1766,19 @@ static int type_bounds_sanity_check(void *key, void *datum, void *datap)
upper = datum;
while (upper->bounds) {
if (++depth == POLICYDB_BOUNDS_MAXDEPTH) {
- printk(KERN_ERR "SELinux: type %s: "
+ pr_err("SELinux: type %s: "
"too deep or looped boundary\n",
- (char *) key);
+ (char *)key);
return -EINVAL;
}
- upper = flex_array_get_ptr(p->type_val_to_struct_array,
- upper->bounds - 1);
+ upper = p->type_val_to_struct[upper->bounds - 1];
BUG_ON(!upper);
if (upper->attribute) {
- printk(KERN_ERR "SELinux: type %s: "
- "bounded by attribute %s",
- (char *) key,
+ pr_err("SELinux: type %s: "
+ "bounded by attribute %s\n",
+ (char *)key,
sym_name(p, SYM_TYPES, upper->value - 1));
return -EINVAL;
}
@@ -1752,18 +1794,15 @@ static int policydb_bounds_sanity_check(struct policydb *p)
if (p->policyvers < POLICYDB_VERSION_BOUNDARY)
return 0;
- rc = hashtab_map(p->p_users.table,
- user_bounds_sanity_check, p);
+ rc = hashtab_map(&p->p_users.table, user_bounds_sanity_check, p);
if (rc)
return rc;
- rc = hashtab_map(p->p_roles.table,
- role_bounds_sanity_check, p);
+ rc = hashtab_map(&p->p_roles.table, role_bounds_sanity_check, p);
if (rc)
return rc;
- rc = hashtab_map(p->p_types.table,
- type_bounds_sanity_check, p);
+ rc = hashtab_map(&p->p_types.table, type_bounds_sanity_check, p);
if (rc)
return rc;
@@ -1774,7 +1813,7 @@ u16 string_to_security_class(struct policydb *p, const char *name)
{
struct class_datum *cladatum;
- cladatum = hashtab_search(p->p_classes.table, name);
+ cladatum = symtab_search(&p->p_classes, name);
if (!cladatum)
return 0;
@@ -1790,36 +1829,39 @@ u32 string_to_av_perm(struct policydb *p, u16 tclass, const char *name)
if (!tclass || tclass > p->p_classes.nprim)
return 0;
- cladatum = p->class_val_to_struct[tclass-1];
+ cladatum = p->class_val_to_struct[tclass - 1];
comdatum = cladatum->comdatum;
if (comdatum)
- perdatum = hashtab_search(comdatum->permissions.table,
- name);
+ perdatum = symtab_search(&comdatum->permissions, name);
if (!perdatum)
- perdatum = hashtab_search(cladatum->permissions.table,
- name);
+ perdatum = symtab_search(&cladatum->permissions, name);
if (!perdatum)
return 0;
- return 1U << (perdatum->value-1);
+ return 1U << (perdatum->value - 1);
}
-static int range_read(struct policydb *p, void *fp)
+static int range_read(struct policydb *p, struct policy_file *fp)
{
struct range_trans *rt = NULL;
struct mls_range *r = NULL;
- int i, rc;
+ int rc;
__le32 buf[2];
- u32 nel;
+ u32 i, nel;
if (p->policyvers < POLICYDB_VERSION_MLS)
return 0;
rc = next_entry(buf, fp, sizeof(u32));
if (rc)
- goto out;
+ return rc;
nel = le32_to_cpu(buf[0]);
+
+ rc = hashtab_init(&p->range_tr, nel);
+ if (rc)
+ return rc;
+
for (i = 0; i < nel; i++) {
rc = -ENOMEM;
rt = kzalloc(sizeof(*rt), GFP_KERNEL);
@@ -1857,18 +1899,18 @@ static int range_read(struct policydb *p, void *fp)
rc = -EINVAL;
if (!mls_range_isvalid(p, r)) {
- printk(KERN_WARNING "SELinux: rangetrans: invalid range\n");
+ pr_warn("SELinux: rangetrans: invalid range\n");
goto out;
}
- rc = hashtab_insert(p->range_tr, rt, r);
+ rc = hashtab_insert(&p->range_tr, rt, r, rangetr_key_params);
if (rc)
goto out;
rt = NULL;
r = NULL;
}
- hash_eval(p->range_tr, "rangetr");
+ hash_eval(&p->range_tr, "rangetr", NULL);
rc = 0;
out:
kfree(rt);
@@ -1876,87 +1918,225 @@ out:
return rc;
}
-static int filename_trans_read(struct policydb *p, void *fp)
+static int filename_trans_read_helper_compat(struct policydb *p, struct policy_file *fp)
{
- struct filename_trans *ft;
- struct filename_trans_datum *otype;
- char *name;
- u32 nel, len;
+ struct filename_trans_key key, *ft = NULL;
+ struct filename_trans_datum *last, *datum = NULL;
+ char *name = NULL;
+ u32 len, stype, otype;
__le32 buf[4];
- int rc, i;
-
- if (p->policyvers < POLICYDB_VERSION_FILENAME_TRANS)
- return 0;
+ int rc;
+ /* length of the path component string */
rc = next_entry(buf, fp, sizeof(u32));
if (rc)
return rc;
- nel = le32_to_cpu(buf[0]);
+ len = le32_to_cpu(buf[0]);
- for (i = 0; i < nel; i++) {
- ft = NULL;
- otype = NULL;
- name = NULL;
+ /* path component string */
+ rc = str_read(&name, GFP_KERNEL, fp, len);
+ if (rc)
+ return rc;
- rc = -ENOMEM;
- ft = kzalloc(sizeof(*ft), GFP_KERNEL);
- if (!ft)
- goto out;
+ rc = next_entry(buf, fp, sizeof(u32) * 4);
+ if (rc)
+ goto out;
+
+ stype = le32_to_cpu(buf[0]);
+ key.ttype = le32_to_cpu(buf[1]);
+ key.tclass = le32_to_cpu(buf[2]);
+ key.name = name;
+
+ otype = le32_to_cpu(buf[3]);
+ last = NULL;
+ datum = policydb_filenametr_search(p, &key);
+ while (datum) {
+ if (unlikely(ebitmap_get_bit(&datum->stypes, stype - 1))) {
+ /* conflicting/duplicate rules are ignored */
+ datum = NULL;
+ rc = 0;
+ goto out;
+ }
+ if (likely(datum->otype == otype))
+ break;
+ last = datum;
+ datum = datum->next;
+ }
+ if (!datum) {
rc = -ENOMEM;
- otype = kmalloc(sizeof(*otype), GFP_KERNEL);
- if (!otype)
+ datum = kmalloc(sizeof(*datum), GFP_KERNEL);
+ if (!datum)
goto out;
- /* length of the path component string */
- rc = next_entry(buf, fp, sizeof(u32));
- if (rc)
- goto out;
- len = le32_to_cpu(buf[0]);
+ ebitmap_init(&datum->stypes);
+ datum->otype = otype;
+ datum->next = NULL;
+
+ if (unlikely(last)) {
+ last->next = datum;
+ } else {
+ rc = -ENOMEM;
+ ft = kmemdup(&key, sizeof(key), GFP_KERNEL);
+ if (!ft)
+ goto out;
+
+ rc = hashtab_insert(&p->filename_trans, ft, datum,
+ filenametr_key_params);
+ if (rc)
+ goto out;
+ name = NULL;
+
+ rc = ebitmap_set_bit(&p->filename_trans_ttypes,
+ key.ttype, 1);
+ if (rc)
+ return rc;
+ }
+ }
+ kfree(name);
+ return ebitmap_set_bit(&datum->stypes, stype - 1, 1);
+
+out:
+ kfree(ft);
+ kfree(name);
+ kfree(datum);
+ return rc;
+}
+
+static int filename_trans_read_helper(struct policydb *p, struct policy_file *fp)
+{
+ struct filename_trans_key *ft = NULL;
+ struct filename_trans_datum **dst, *datum, *first = NULL;
+ char *name = NULL;
+ u32 len, ttype, tclass, ndatum, i;
+ __le32 buf[3];
+ int rc;
+
+ /* length of the path component string */
+ rc = next_entry(buf, fp, sizeof(u32));
+ if (rc)
+ return rc;
+ len = le32_to_cpu(buf[0]);
+
+ /* path component string */
+ rc = str_read(&name, GFP_KERNEL, fp, len);
+ if (rc)
+ return rc;
+
+ rc = next_entry(buf, fp, sizeof(u32) * 3);
+ if (rc)
+ goto out;
+
+ ttype = le32_to_cpu(buf[0]);
+ tclass = le32_to_cpu(buf[1]);
+ ndatum = le32_to_cpu(buf[2]);
+ if (ndatum == 0) {
+ pr_err("SELinux: Filename transition key with no datum\n");
+ rc = -ENOENT;
+ goto out;
+ }
+
+ dst = &first;
+ for (i = 0; i < ndatum; i++) {
rc = -ENOMEM;
- name = kmalloc(len + 1, GFP_KERNEL);
- if (!name)
+ datum = kmalloc(sizeof(*datum), GFP_KERNEL);
+ if (!datum)
goto out;
- ft->name = name;
+ datum->next = NULL;
+ *dst = datum;
- /* path component string */
- rc = next_entry(name, fp, len);
+ /* ebitmap_read() will at least init the bitmap */
+ rc = ebitmap_read(&datum->stypes, fp);
if (rc)
goto out;
- name[len] = 0;
- rc = next_entry(buf, fp, sizeof(u32) * 4);
+ rc = next_entry(buf, fp, sizeof(u32));
if (rc)
goto out;
- ft->stype = le32_to_cpu(buf[0]);
- ft->ttype = le32_to_cpu(buf[1]);
- ft->tclass = le32_to_cpu(buf[2]);
+ datum->otype = le32_to_cpu(buf[0]);
- otype->otype = le32_to_cpu(buf[3]);
+ dst = &datum->next;
+ }
- rc = ebitmap_set_bit(&p->filename_trans_ttypes, ft->ttype, 1);
- if (rc)
- goto out;
+ rc = -ENOMEM;
+ ft = kmalloc(sizeof(*ft), GFP_KERNEL);
+ if (!ft)
+ goto out;
+
+ ft->ttype = ttype;
+ ft->tclass = tclass;
+ ft->name = name;
+
+ rc = hashtab_insert(&p->filename_trans, ft, first,
+ filenametr_key_params);
+ if (rc == -EEXIST)
+ pr_err("SELinux: Duplicate filename transition key\n");
+ if (rc)
+ goto out;
+
+ return ebitmap_set_bit(&p->filename_trans_ttypes, ttype, 1);
- hashtab_insert(p->filename_trans, ft, otype);
- }
- hash_eval(p->filename_trans, "filenametr");
- return 0;
out:
kfree(ft);
kfree(name);
- kfree(otype);
+ while (first) {
+ datum = first;
+ first = first->next;
+ ebitmap_destroy(&datum->stypes);
+ kfree(datum);
+ }
return rc;
}
-static int genfs_read(struct policydb *p, void *fp)
+static int filename_trans_read(struct policydb *p, struct policy_file *fp)
{
- int i, j, rc;
- u32 nel, nel2, len, len2;
+ u32 nel, i;
+ __le32 buf[1];
+ int rc;
+
+ if (p->policyvers < POLICYDB_VERSION_FILENAME_TRANS)
+ return 0;
+
+ rc = next_entry(buf, fp, sizeof(u32));
+ if (rc)
+ return rc;
+ nel = le32_to_cpu(buf[0]);
+
+ if (p->policyvers < POLICYDB_VERSION_COMP_FTRANS) {
+ p->compat_filename_trans_count = nel;
+
+ rc = hashtab_init(&p->filename_trans, (1 << 11));
+ if (rc)
+ return rc;
+
+ for (i = 0; i < nel; i++) {
+ rc = filename_trans_read_helper_compat(p, fp);
+ if (rc)
+ return rc;
+ }
+ } else {
+ rc = hashtab_init(&p->filename_trans, nel);
+ if (rc)
+ return rc;
+
+ for (i = 0; i < nel; i++) {
+ rc = filename_trans_read_helper(p, fp);
+ if (rc)
+ return rc;
+ }
+ }
+ hash_eval(&p->filename_trans, "filenametr", NULL);
+ return 0;
+}
+
+static int genfs_read(struct policydb *p, struct policy_file *fp)
+{
+ int rc;
+ u32 i, j, nel, nel2, len, len2;
__le32 buf[1];
struct ocontext *l, *c;
struct ocontext *newc = NULL;
@@ -1965,7 +2145,7 @@ static int genfs_read(struct policydb *p, void *fp)
rc = next_entry(buf, fp, sizeof(u32));
if (rc)
- goto out;
+ return rc;
nel = le32_to_cpu(buf[0]);
for (i = 0; i < nel; i++) {
@@ -1979,22 +2159,15 @@ static int genfs_read(struct policydb *p, void *fp)
if (!newgenfs)
goto out;
- rc = -ENOMEM;
- newgenfs->fstype = kmalloc(len + 1, GFP_KERNEL);
- if (!newgenfs->fstype)
- goto out;
-
- rc = next_entry(newgenfs->fstype, fp, len);
+ rc = str_read(&newgenfs->fstype, GFP_KERNEL, fp, len);
if (rc)
goto out;
- newgenfs->fstype[len] = 0;
-
for (genfs_p = NULL, genfs = p->genfs; genfs;
genfs_p = genfs, genfs = genfs->next) {
rc = -EINVAL;
if (strcmp(newgenfs->fstype, genfs->fstype) == 0) {
- printk(KERN_ERR "SELinux: dup genfs fstype %s\n",
+ pr_err("SELinux: dup genfs fstype %s\n",
newgenfs->fstype);
goto out;
}
@@ -2025,32 +2198,26 @@ static int genfs_read(struct policydb *p, void *fp)
if (!newc)
goto out;
- rc = -ENOMEM;
- newc->u.name = kmalloc(len + 1, GFP_KERNEL);
- if (!newc->u.name)
- goto out;
-
- rc = next_entry(newc->u.name, fp, len);
+ rc = str_read(&newc->u.name, GFP_KERNEL, fp, len);
if (rc)
goto out;
- newc->u.name[len] = 0;
rc = next_entry(buf, fp, sizeof(u32));
if (rc)
goto out;
newc->v.sclass = le32_to_cpu(buf[0]);
- rc = context_read_and_validate(&newc->context[0], p, fp);
+ rc = context_read_and_validate(&newc->context[0], p,
+ fp);
if (rc)
goto out;
- for (l = NULL, c = genfs->head; c;
- l = c, c = c->next) {
+ for (l = NULL, c = genfs->head; c; l = c, c = c->next) {
rc = -EINVAL;
if (!strcmp(newc->u.name, c->u.name) &&
(!c->v.sclass || !newc->v.sclass ||
newc->v.sclass == c->v.sclass)) {
- printk(KERN_ERR "SELinux: dup genfs entry (%s,%s)\n",
+ pr_err("SELinux: dup genfs entry (%s,%s)\n",
genfs->fstype, c->u.name);
goto out;
}
@@ -2070,19 +2237,22 @@ static int genfs_read(struct policydb *p, void *fp)
}
rc = 0;
out:
- if (newgenfs)
+ if (newgenfs) {
kfree(newgenfs->fstype);
- kfree(newgenfs);
+ kfree(newgenfs);
+ }
ocontext_destroy(newc, OCON_FSUSE);
return rc;
}
-static int ocontext_read(struct policydb *p, struct policydb_compat_info *info,
- void *fp)
+static int ocontext_read(struct policydb *p,
+ const struct policydb_compat_info *info, struct policy_file *fp)
{
- int i, j, rc;
- u32 nel, len;
+ int rc;
+ unsigned int i;
+ u32 j, nel, len;
+ __be64 prefixbuf[1];
__le32 buf[3];
struct ocontext *l, *c;
u32 nodebuf[8];
@@ -2112,7 +2282,8 @@ static int ocontext_read(struct policydb *p, struct policydb_compat_info *info,
goto out;
c->sid[0] = le32_to_cpu(buf[0]);
- rc = context_read_and_validate(&c->context[0], p, fp);
+ rc = context_read_and_validate(&c->context[0],
+ p, fp);
if (rc)
goto out;
break;
@@ -2123,31 +2294,32 @@ static int ocontext_read(struct policydb *p, struct policydb_compat_info *info,
goto out;
len = le32_to_cpu(buf[0]);
- rc = -ENOMEM;
- c->u.name = kmalloc(len + 1, GFP_KERNEL);
- if (!c->u.name)
- goto out;
-
- rc = next_entry(c->u.name, fp, len);
+ rc = str_read(&c->u.name, GFP_KERNEL, fp, len);
if (rc)
goto out;
- c->u.name[len] = 0;
- rc = context_read_and_validate(&c->context[0], p, fp);
+ if (i == OCON_FS)
+ pr_warn("SELinux: void and deprecated fs ocon %s\n",
+ c->u.name);
+
+ rc = context_read_and_validate(&c->context[0],
+ p, fp);
if (rc)
goto out;
- rc = context_read_and_validate(&c->context[1], p, fp);
+ rc = context_read_and_validate(&c->context[1],
+ p, fp);
if (rc)
goto out;
break;
case OCON_PORT:
- rc = next_entry(buf, fp, sizeof(u32)*3);
+ rc = next_entry(buf, fp, sizeof(u32) * 3);
if (rc)
goto out;
c->u.port.protocol = le32_to_cpu(buf[0]);
c->u.port.low_port = le32_to_cpu(buf[1]);
c->u.port.high_port = le32_to_cpu(buf[2]);
- rc = context_read_and_validate(&c->context[0], p, fp);
+ rc = context_read_and_validate(&c->context[0],
+ p, fp);
if (rc)
goto out;
break;
@@ -2157,12 +2329,13 @@ static int ocontext_read(struct policydb *p, struct policydb_compat_info *info,
goto out;
c->u.node.addr = nodebuf[0]; /* network order */
c->u.node.mask = nodebuf[1]; /* network order */
- rc = context_read_and_validate(&c->context[0], p, fp);
+ rc = context_read_and_validate(&c->context[0],
+ p, fp);
if (rc)
goto out;
break;
case OCON_FSUSE:
- rc = next_entry(buf, fp, sizeof(u32)*2);
+ rc = next_entry(buf, fp, sizeof(u32) * 2);
if (rc)
goto out;
@@ -2174,17 +2347,13 @@ static int ocontext_read(struct policydb *p, struct policydb_compat_info *info,
if (c->v.behavior > SECURITY_FS_USE_MAX)
goto out;
- rc = -ENOMEM;
len = le32_to_cpu(buf[1]);
- c->u.name = kmalloc(len + 1, GFP_KERNEL);
- if (!c->u.name)
- goto out;
-
- rc = next_entry(c->u.name, fp, len);
+ rc = str_read(&c->u.name, GFP_KERNEL, fp, len);
if (rc)
goto out;
- c->u.name[len] = 0;
- rc = context_read_and_validate(&c->context[0], p, fp);
+
+ rc = context_read_and_validate(&c->context[0],
+ p, fp);
if (rc)
goto out;
break;
@@ -2197,13 +2366,73 @@ static int ocontext_read(struct policydb *p, struct policydb_compat_info *info,
for (k = 0; k < 4; k++)
c->u.node6.addr[k] = nodebuf[k];
for (k = 0; k < 4; k++)
- c->u.node6.mask[k] = nodebuf[k+4];
- rc = context_read_and_validate(&c->context[0], p, fp);
+ c->u.node6.mask[k] = nodebuf[k + 4];
+ rc = context_read_and_validate(&c->context[0],
+ p, fp);
if (rc)
goto out;
break;
}
+ case OCON_IBPKEY: {
+ u32 pkey_lo, pkey_hi;
+
+ rc = next_entry(prefixbuf, fp, sizeof(u64));
+ if (rc)
+ goto out;
+
+ /* we need to have subnet_prefix in CPU order */
+ c->u.ibpkey.subnet_prefix =
+ be64_to_cpu(prefixbuf[0]);
+
+ rc = next_entry(buf, fp, sizeof(u32) * 2);
+ if (rc)
+ goto out;
+
+ pkey_lo = le32_to_cpu(buf[0]);
+ pkey_hi = le32_to_cpu(buf[1]);
+
+ if (pkey_lo > U16_MAX || pkey_hi > U16_MAX) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ c->u.ibpkey.low_pkey = pkey_lo;
+ c->u.ibpkey.high_pkey = pkey_hi;
+
+ rc = context_read_and_validate(&c->context[0],
+ p, fp);
+ if (rc)
+ goto out;
+ break;
}
+ case OCON_IBENDPORT: {
+ u32 port;
+
+ rc = next_entry(buf, fp, sizeof(u32) * 2);
+ if (rc)
+ goto out;
+ len = le32_to_cpu(buf[0]);
+
+ rc = str_read(&c->u.ibendport.dev_name,
+ GFP_KERNEL, fp, len);
+ if (rc)
+ goto out;
+
+ port = le32_to_cpu(buf[1]);
+ if (port > U8_MAX || port == 0) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ c->u.ibendport.port = port;
+
+ rc = context_read_and_validate(&c->context[0],
+ p, fp);
+ if (rc)
+ goto out;
+ break;
+ } /* end case */
+ } /* end switch */
}
}
rc = 0;
@@ -2215,20 +2444,19 @@ out:
* Read the configuration data from a policy database binary
* representation file into a policy database structure.
*/
-int policydb_read(struct policydb *p, void *fp)
+int policydb_read(struct policydb *p, struct policy_file *fp)
{
struct role_allow *ra, *lra;
- struct role_trans *tr, *ltr;
- int i, j, rc;
+ struct role_trans_key *rtk = NULL;
+ struct role_trans_datum *rtd = NULL;
+ int rc;
__le32 buf[4];
- u32 len, nprim, nel;
+ u32 i, j, len, nprim, nel, perm;
char *policydb_str;
- struct policydb_compat_info *info;
+ const struct policydb_compat_info *info;
- rc = policydb_init(p);
- if (rc)
- return rc;
+ policydb_init(p);
/* Read the magic number and string length. */
rc = next_entry(buf, fp, sizeof(u32) * 2);
@@ -2237,7 +2465,7 @@ int policydb_read(struct policydb *p, void *fp)
rc = -EINVAL;
if (le32_to_cpu(buf[0]) != POLICYDB_MAGIC) {
- printk(KERN_ERR "SELinux: policydb magic number 0x%x does "
+ pr_err("SELinux: policydb magic number 0x%x does "
"not match expected magic number 0x%x\n",
le32_to_cpu(buf[0]), POLICYDB_MAGIC);
goto bad;
@@ -2246,32 +2474,28 @@ int policydb_read(struct policydb *p, void *fp)
rc = -EINVAL;
len = le32_to_cpu(buf[1]);
if (len != strlen(POLICYDB_STRING)) {
- printk(KERN_ERR "SELinux: policydb string length %d does not "
- "match expected length %Zu\n",
+ pr_err("SELinux: policydb string length %d does not "
+ "match expected length %zu\n",
len, strlen(POLICYDB_STRING));
goto bad;
}
- rc = -ENOMEM;
- policydb_str = kmalloc(len + 1, GFP_KERNEL);
- if (!policydb_str) {
- printk(KERN_ERR "SELinux: unable to allocate memory for policydb "
- "string of length %d\n", len);
- goto bad;
- }
-
- rc = next_entry(policydb_str, fp, len);
+ rc = str_read(&policydb_str, GFP_KERNEL, fp, len);
if (rc) {
- printk(KERN_ERR "SELinux: truncated policydb string identifier\n");
- kfree(policydb_str);
+ if (rc == -ENOMEM) {
+ pr_err("SELinux: unable to allocate memory for policydb string of length %d\n",
+ len);
+ } else {
+ pr_err("SELinux: truncated policydb string identifier\n");
+ }
goto bad;
}
rc = -EINVAL;
- policydb_str[len] = '\0';
if (strcmp(policydb_str, POLICYDB_STRING)) {
- printk(KERN_ERR "SELinux: policydb string %s does not match "
- "my string %s\n", policydb_str, POLICYDB_STRING);
+ pr_err("SELinux: policydb string %s does not match "
+ "my string %s\n",
+ policydb_str, POLICYDB_STRING);
kfree(policydb_str);
goto bad;
}
@@ -2280,7 +2504,7 @@ int policydb_read(struct policydb *p, void *fp)
policydb_str = NULL;
/* Read the version and table sizes. */
- rc = next_entry(buf, fp, sizeof(u32)*4);
+ rc = next_entry(buf, fp, sizeof(u32) * 4);
if (rc)
goto bad;
@@ -2288,9 +2512,10 @@ int policydb_read(struct policydb *p, void *fp)
p->policyvers = le32_to_cpu(buf[0]);
if (p->policyvers < POLICYDB_VERSION_MIN ||
p->policyvers > POLICYDB_VERSION_MAX) {
- printk(KERN_ERR "SELinux: policydb version %d does not match "
+ pr_err("SELinux: policydb version %d does not match "
"my version range %d-%d\n",
- le32_to_cpu(buf[0]), POLICYDB_VERSION_MIN, POLICYDB_VERSION_MAX);
+ le32_to_cpu(buf[0]), POLICYDB_VERSION_MIN,
+ POLICYDB_VERSION_MAX);
goto bad;
}
@@ -2299,9 +2524,9 @@ int policydb_read(struct policydb *p, void *fp)
rc = -EINVAL;
if (p->policyvers < POLICYDB_VERSION_MLS) {
- printk(KERN_ERR "SELinux: security policydb version %d "
- "(MLS) not backwards compatible\n",
- p->policyvers);
+ pr_err("SELinux: security policydb version %d "
+ "(MLS) not backwards compatible\n",
+ p->policyvers);
goto bad;
}
}
@@ -2320,32 +2545,50 @@ int policydb_read(struct policydb *p, void *fp)
goto bad;
}
+ if (p->policyvers >= POLICYDB_VERSION_NEVERAUDIT) {
+ rc = ebitmap_read(&p->neveraudit_map, fp);
+ if (rc)
+ goto bad;
+ }
+
rc = -EINVAL;
info = policydb_lookup_compat(p->policyvers);
if (!info) {
- printk(KERN_ERR "SELinux: unable to find policy compat info "
- "for version %d\n", p->policyvers);
+ pr_err("SELinux: unable to find policy compat info "
+ "for version %d\n",
+ p->policyvers);
goto bad;
}
rc = -EINVAL;
if (le32_to_cpu(buf[2]) != info->sym_num ||
- le32_to_cpu(buf[3]) != info->ocon_num) {
- printk(KERN_ERR "SELinux: policydb table sizes (%d,%d) do "
- "not match mine (%d,%d)\n", le32_to_cpu(buf[2]),
- le32_to_cpu(buf[3]),
- info->sym_num, info->ocon_num);
+ le32_to_cpu(buf[3]) != info->ocon_num) {
+ pr_err("SELinux: policydb table sizes (%d,%d) do "
+ "not match mine (%d,%d)\n",
+ le32_to_cpu(buf[2]), le32_to_cpu(buf[3]), info->sym_num,
+ info->ocon_num);
goto bad;
}
for (i = 0; i < info->sym_num; i++) {
- rc = next_entry(buf, fp, sizeof(u32)*2);
+ rc = next_entry(buf, fp, sizeof(u32) * 2);
if (rc)
goto bad;
nprim = le32_to_cpu(buf[0]);
nel = le32_to_cpu(buf[1]);
+
+ rc = symtab_init(&p->symtab[i], nel);
+ if (rc)
+ goto out;
+
+ if (i == SYM_ROLES) {
+ rc = roles_init(p);
+ if (rc)
+ goto out;
+ }
+
for (j = 0; j < nel; j++) {
- rc = read_f[i](p, p->symtab[i].table, fp);
+ rc = read_f[i](p, &p->symtab[i], fp);
if (rc)
goto bad;
}
@@ -2355,8 +2598,10 @@ int policydb_read(struct policydb *p, void *fp)
rc = -EINVAL;
p->process_class = string_to_security_class(p, "process");
- if (!p->process_class)
+ if (!p->process_class) {
+ pr_err("SELinux: process class is required, not defined in policy\n");
goto bad;
+ }
rc = avtab_read(&p->te_avtab, fp, p);
if (rc)
@@ -2372,40 +2617,53 @@ int policydb_read(struct policydb *p, void *fp)
if (rc)
goto bad;
nel = le32_to_cpu(buf[0]);
- ltr = NULL;
+
+ rc = hashtab_init(&p->role_tr, nel);
+ if (rc)
+ goto bad;
for (i = 0; i < nel; i++) {
rc = -ENOMEM;
- tr = kzalloc(sizeof(*tr), GFP_KERNEL);
- if (!tr)
+ rtk = kmalloc(sizeof(*rtk), GFP_KERNEL);
+ if (!rtk)
goto bad;
- if (ltr)
- ltr->next = tr;
- else
- p->role_tr = tr;
- rc = next_entry(buf, fp, sizeof(u32)*3);
+
+ rc = -ENOMEM;
+ rtd = kmalloc(sizeof(*rtd), GFP_KERNEL);
+ if (!rtd)
+ goto bad;
+
+ rc = next_entry(buf, fp, sizeof(u32) * 3);
if (rc)
goto bad;
- rc = -EINVAL;
- tr->role = le32_to_cpu(buf[0]);
- tr->type = le32_to_cpu(buf[1]);
- tr->new_role = le32_to_cpu(buf[2]);
+ rtk->role = le32_to_cpu(buf[0]);
+ rtk->type = le32_to_cpu(buf[1]);
+ rtd->new_role = le32_to_cpu(buf[2]);
if (p->policyvers >= POLICYDB_VERSION_ROLETRANS) {
rc = next_entry(buf, fp, sizeof(u32));
if (rc)
goto bad;
- tr->tclass = le32_to_cpu(buf[0]);
+ rtk->tclass = le32_to_cpu(buf[0]);
} else
- tr->tclass = p->process_class;
+ rtk->tclass = p->process_class;
- if (!policydb_role_isvalid(p, tr->role) ||
- !policydb_type_isvalid(p, tr->type) ||
- !policydb_class_isvalid(p, tr->tclass) ||
- !policydb_role_isvalid(p, tr->new_role))
+ rc = -EINVAL;
+ if (!policydb_role_isvalid(p, rtk->role) ||
+ !policydb_type_isvalid(p, rtk->type) ||
+ !policydb_class_isvalid(p, rtk->tclass) ||
+ !policydb_role_isvalid(p, rtd->new_role))
goto bad;
- ltr = tr;
+
+ rc = hashtab_insert(&p->role_tr, rtk, rtd, roletr_key_params);
+ if (rc)
+ goto bad;
+
+ rtk = NULL;
+ rtd = NULL;
}
+ hash_eval(&p->role_tr, "roletr", NULL);
+
rc = next_entry(buf, fp, sizeof(u32));
if (rc)
goto bad;
@@ -2420,7 +2678,7 @@ int policydb_read(struct policydb *p, void *fp)
lra->next = ra;
else
p->role_allow = ra;
- rc = next_entry(buf, fp, sizeof(u32)*2);
+ rc = next_entry(buf, fp, sizeof(u32) * 2);
if (rc)
goto bad;
@@ -2442,10 +2700,18 @@ int policydb_read(struct policydb *p, void *fp)
goto bad;
rc = -EINVAL;
- p->process_trans_perms = string_to_av_perm(p, p->process_class, "transition");
- p->process_trans_perms |= string_to_av_perm(p, p->process_class, "dyntransition");
- if (!p->process_trans_perms)
+ perm = string_to_av_perm(p, p->process_class, "transition");
+ if (!perm) {
+ pr_err("SELinux: process transition permission is required, not defined in policy\n");
goto bad;
+ }
+ p->process_trans_perms = perm;
+ perm = string_to_av_perm(p, p->process_class, "dyntransition");
+ if (!perm) {
+ pr_err("SELinux: process dyntransition permission is required, not defined in policy\n");
+ goto bad;
+ }
+ p->process_trans_perms |= perm;
rc = ocontext_read(p, info, fp);
if (rc)
@@ -2460,23 +2726,18 @@ int policydb_read(struct policydb *p, void *fp)
goto bad;
rc = -ENOMEM;
- p->type_attr_map_array = flex_array_alloc(sizeof(struct ebitmap),
- p->p_types.nprim,
- GFP_KERNEL | __GFP_ZERO);
+ p->type_attr_map_array = kvcalloc(
+ p->p_types.nprim, sizeof(*p->type_attr_map_array), GFP_KERNEL);
if (!p->type_attr_map_array)
goto bad;
- /* preallocate so we don't have to worry about the put ever failing */
- rc = flex_array_prealloc(p->type_attr_map_array, 0, p->p_types.nprim,
- GFP_KERNEL | __GFP_ZERO);
- if (rc)
- goto bad;
+ /* just in case ebitmap_init() becomes more than just a memset(0): */
+ for (i = 0; i < p->p_types.nprim; i++)
+ ebitmap_init(&p->type_attr_map_array[i]);
for (i = 0; i < p->p_types.nprim; i++) {
- struct ebitmap *e = flex_array_get(p->type_attr_map_array, i);
+ struct ebitmap *e = &p->type_attr_map_array[i];
- BUG_ON(!e);
- ebitmap_init(e);
if (p->policyvers >= POLICYDB_VERSION_AVTAB) {
rc = ebitmap_read(e, fp);
if (rc)
@@ -2496,6 +2757,8 @@ int policydb_read(struct policydb *p, void *fp)
out:
return rc;
bad:
+ kfree(rtk);
+ kfree(rtd);
policydb_destroy(p);
goto out;
}
@@ -2504,7 +2767,7 @@ bad:
* Write a MLS level structure to a policydb binary
* representation file.
*/
-static int mls_write_level(struct mls_level *l, void *fp)
+static int mls_write_level(struct mls_level *l, struct policy_file *fp)
{
__le32 buf[1];
int rc;
@@ -2525,7 +2788,7 @@ static int mls_write_level(struct mls_level *l, void *fp)
* Write a MLS range structure to a policydb binary
* representation file.
*/
-static int mls_write_range_helper(struct mls_range *r, void *fp)
+static int mls_write_range_helper(struct mls_range *r, struct policy_file *fp)
{
__le32 buf[3];
size_t items;
@@ -2537,12 +2800,12 @@ static int mls_write_range_helper(struct mls_range *r, void *fp)
items = 2;
else
items = 3;
- buf[0] = cpu_to_le32(items-1);
+ buf[0] = cpu_to_le32(items - 1);
buf[1] = cpu_to_le32(r->level[0].sens);
if (!eq)
buf[2] = cpu_to_le32(r->level[1].sens);
- BUG_ON(items > (sizeof(buf)/sizeof(buf[0])));
+ BUG_ON(items > ARRAY_SIZE(buf));
rc = put_entry(buf, sizeof(u32), items, fp);
if (rc)
@@ -2565,7 +2828,7 @@ static int sens_write(void *vkey, void *datum, void *ptr)
char *key = vkey;
struct level_datum *levdatum = datum;
struct policy_data *pd = ptr;
- void *fp = pd->fp;
+ struct policy_file *fp = pd->fp;
__le32 buf[2];
size_t len;
int rc;
@@ -2581,7 +2844,7 @@ static int sens_write(void *vkey, void *datum, void *ptr)
if (rc)
return rc;
- rc = mls_write_level(levdatum->level, fp);
+ rc = mls_write_level(&levdatum->level, fp);
if (rc)
return rc;
@@ -2593,7 +2856,7 @@ static int cat_write(void *vkey, void *datum, void *ptr)
char *key = vkey;
struct cat_datum *catdatum = datum;
struct policy_data *pd = ptr;
- void *fp = pd->fp;
+ struct policy_file *fp = pd->fp;
__le32 buf[3];
size_t len;
int rc;
@@ -2613,43 +2876,49 @@ static int cat_write(void *vkey, void *datum, void *ptr)
return 0;
}
-static int role_trans_write(struct policydb *p, void *fp)
+static int role_trans_write_one(void *key, void *datum, void *ptr)
{
- struct role_trans *r = p->role_tr;
- struct role_trans *tr;
- u32 buf[3];
- size_t nel;
+ struct role_trans_key *rtk = key;
+ struct role_trans_datum *rtd = datum;
+ struct policy_data *pd = ptr;
+ struct policy_file *fp = pd->fp;
+ struct policydb *p = pd->p;
+ __le32 buf[3];
int rc;
- nel = 0;
- for (tr = r; tr; tr = tr->next)
- nel++;
- buf[0] = cpu_to_le32(nel);
- rc = put_entry(buf, sizeof(u32), 1, fp);
+ buf[0] = cpu_to_le32(rtk->role);
+ buf[1] = cpu_to_le32(rtk->type);
+ buf[2] = cpu_to_le32(rtd->new_role);
+ rc = put_entry(buf, sizeof(u32), 3, fp);
if (rc)
return rc;
- for (tr = r; tr; tr = tr->next) {
- buf[0] = cpu_to_le32(tr->role);
- buf[1] = cpu_to_le32(tr->type);
- buf[2] = cpu_to_le32(tr->new_role);
- rc = put_entry(buf, sizeof(u32), 3, fp);
+ if (p->policyvers >= POLICYDB_VERSION_ROLETRANS) {
+ buf[0] = cpu_to_le32(rtk->tclass);
+ rc = put_entry(buf, sizeof(u32), 1, fp);
if (rc)
return rc;
- if (p->policyvers >= POLICYDB_VERSION_ROLETRANS) {
- buf[0] = cpu_to_le32(tr->tclass);
- rc = put_entry(buf, sizeof(u32), 1, fp);
- if (rc)
- return rc;
- }
}
-
return 0;
}
-static int role_allow_write(struct role_allow *r, void *fp)
+static int role_trans_write(struct policydb *p, struct policy_file *fp)
+{
+ struct policy_data pd = { .p = p, .fp = fp };
+ __le32 buf[1];
+ int rc;
+
+ buf[0] = cpu_to_le32(p->role_tr.nel);
+ rc = put_entry(buf, sizeof(u32), 1, fp);
+ if (rc)
+ return rc;
+
+ return hashtab_map(&p->role_tr, role_trans_write_one, &pd);
+}
+
+static int role_allow_write(struct role_allow *r, struct policy_file *fp)
{
struct role_allow *ra;
- u32 buf[2];
+ __le32 buf[2];
size_t nel;
int rc;
@@ -2674,8 +2943,7 @@ static int role_allow_write(struct role_allow *r, void *fp)
* Write a security context structure
* to a policydb binary representation file.
*/
-static int context_write(struct policydb *p, struct context *c,
- void *fp)
+static int context_write(struct policydb *p, struct context *c, struct policy_file *fp)
{
int rc;
__le32 buf[3];
@@ -2728,7 +2996,7 @@ static int common_write(void *vkey, void *datum, void *ptr)
char *key = vkey;
struct common_datum *comdatum = datum;
struct policy_data *pd = ptr;
- void *fp = pd->fp;
+ struct policy_file *fp = pd->fp;
__le32 buf[4];
size_t len;
int rc;
@@ -2737,7 +3005,7 @@ static int common_write(void *vkey, void *datum, void *ptr)
buf[0] = cpu_to_le32(len);
buf[1] = cpu_to_le32(comdatum->value);
buf[2] = cpu_to_le32(comdatum->permissions.nprim);
- buf[3] = cpu_to_le32(comdatum->permissions.table->nel);
+ buf[3] = cpu_to_le32(comdatum->permissions.table.nel);
rc = put_entry(buf, sizeof(u32), 4, fp);
if (rc)
return rc;
@@ -2746,15 +3014,33 @@ static int common_write(void *vkey, void *datum, void *ptr)
if (rc)
return rc;
- rc = hashtab_map(comdatum->permissions.table, perm_write, fp);
+ rc = hashtab_map(&comdatum->permissions.table, perm_write, fp);
if (rc)
return rc;
return 0;
}
+static int type_set_write(struct type_set *t, struct policy_file *fp)
+{
+ int rc;
+ __le32 buf[1];
+
+ if (ebitmap_write(&t->types, fp))
+ return -EINVAL;
+ if (ebitmap_write(&t->negset, fp))
+ return -EINVAL;
+
+ buf[0] = cpu_to_le32(t->flags);
+ rc = put_entry(buf, sizeof(u32), 1, fp);
+ if (rc)
+ return -EINVAL;
+
+ return 0;
+}
+
static int write_cons_helper(struct policydb *p, struct constraint_node *node,
- void *fp)
+ struct policy_file *fp)
{
struct constraint_node *c;
struct constraint_expr *e;
@@ -2784,6 +3070,12 @@ static int write_cons_helper(struct policydb *p, struct constraint_node *node,
rc = ebitmap_write(&e->names, fp);
if (rc)
return rc;
+ if (p->policyvers >=
+ POLICYDB_VERSION_CONSTRAINT_NAMES) {
+ rc = type_set_write(e->type_names, fp);
+ if (rc)
+ return rc;
+ }
break;
default:
break;
@@ -2799,7 +3091,7 @@ static int class_write(void *vkey, void *datum, void *ptr)
char *key = vkey;
struct class_datum *cladatum = datum;
struct policy_data *pd = ptr;
- void *fp = pd->fp;
+ struct policy_file *fp = pd->fp;
struct policydb *p = pd->p;
struct constraint_node *c;
__le32 buf[6];
@@ -2821,10 +3113,7 @@ static int class_write(void *vkey, void *datum, void *ptr)
buf[1] = cpu_to_le32(len2);
buf[2] = cpu_to_le32(cladatum->value);
buf[3] = cpu_to_le32(cladatum->permissions.nprim);
- if (cladatum->permissions.table)
- buf[4] = cpu_to_le32(cladatum->permissions.table->nel);
- else
- buf[4] = 0;
+ buf[4] = cpu_to_le32(cladatum->permissions.table.nel);
buf[5] = cpu_to_le32(ncons);
rc = put_entry(buf, sizeof(u32), 6, fp);
if (rc)
@@ -2840,7 +3129,7 @@ static int class_write(void *vkey, void *datum, void *ptr)
return rc;
}
- rc = hashtab_map(cladatum->permissions.table, perm_write, fp);
+ rc = hashtab_map(&cladatum->permissions.table, perm_write, fp);
if (rc)
return rc;
@@ -2887,7 +3176,7 @@ static int role_write(void *vkey, void *datum, void *ptr)
char *key = vkey;
struct role_datum *role = datum;
struct policy_data *pd = ptr;
- void *fp = pd->fp;
+ struct policy_file *fp = pd->fp;
struct policydb *p = pd->p;
__le32 buf[3];
size_t items, len;
@@ -2900,7 +3189,7 @@ static int role_write(void *vkey, void *datum, void *ptr)
if (p->policyvers >= POLICYDB_VERSION_BOUNDARY)
buf[items++] = cpu_to_le32(role->bounds);
- BUG_ON(items > (sizeof(buf)/sizeof(buf[0])));
+ BUG_ON(items > ARRAY_SIZE(buf));
rc = put_entry(buf, sizeof(u32), items, fp);
if (rc)
@@ -2927,7 +3216,7 @@ static int type_write(void *vkey, void *datum, void *ptr)
struct type_datum *typdatum = datum;
struct policy_data *pd = ptr;
struct policydb *p = pd->p;
- void *fp = pd->fp;
+ struct policy_file *fp = pd->fp;
__le32 buf[4];
int rc;
size_t items, len;
@@ -2950,7 +3239,7 @@ static int type_write(void *vkey, void *datum, void *ptr)
} else {
buf[items++] = cpu_to_le32(typdatum->primary);
}
- BUG_ON(items > (sizeof(buf) / sizeof(buf[0])));
+ BUG_ON(items > ARRAY_SIZE(buf));
rc = put_entry(buf, sizeof(u32), items, fp);
if (rc)
return rc;
@@ -2968,7 +3257,7 @@ static int user_write(void *vkey, void *datum, void *ptr)
struct user_datum *usrdatum = datum;
struct policy_data *pd = ptr;
struct policydb *p = pd->p;
- void *fp = pd->fp;
+ struct policy_file *fp = pd->fp;
__le32 buf[3];
size_t items, len;
int rc;
@@ -2979,7 +3268,7 @@ static int user_write(void *vkey, void *datum, void *ptr)
buf[items++] = cpu_to_le32(usrdatum->value);
if (p->policyvers >= POLICYDB_VERSION_BOUNDARY)
buf[items++] = cpu_to_le32(usrdatum->bounds);
- BUG_ON(items > (sizeof(buf) / sizeof(buf[0])));
+ BUG_ON(items > ARRAY_SIZE(buf));
rc = put_entry(buf, sizeof(u32), items, fp);
if (rc)
return rc;
@@ -3003,9 +3292,8 @@ static int user_write(void *vkey, void *datum, void *ptr)
return 0;
}
-static int (*write_f[SYM_NUM]) (void *key, void *datum,
- void *datap) =
-{
+/* clang-format off */
+static int (*const write_f[SYM_NUM])(void *key, void *datum, void *datap) = {
common_write,
class_write,
role_write,
@@ -3015,12 +3303,16 @@ static int (*write_f[SYM_NUM]) (void *key, void *datum,
sens_write,
cat_write,
};
+/* clang-format on */
-static int ocontext_write(struct policydb *p, struct policydb_compat_info *info,
- void *fp)
+static int ocontext_write(struct policydb *p,
+ const struct policydb_compat_info *info,
+ struct policy_file *fp)
{
- unsigned int i, j, rc;
+ unsigned int i, j;
+ int rc;
size_t nel, len;
+ __be64 prefixbuf[1];
__le32 buf[3];
u32 nodebuf[8];
struct ocontext *c;
@@ -3097,9 +3389,13 @@ static int ocontext_write(struct policydb *p, struct policydb_compat_info *info,
break;
case OCON_NODE6:
for (j = 0; j < 4; j++)
- nodebuf[j] = c->u.node6.addr[j]; /* network order */
+ nodebuf[j] =
+ c->u.node6.addr
+ [j]; /* network order */
for (j = 0; j < 4; j++)
- nodebuf[j + 4] = c->u.node6.mask[j]; /* network order */
+ nodebuf[j + 4] =
+ c->u.node6.mask
+ [j]; /* network order */
rc = put_entry(nodebuf, sizeof(u32), 8, fp);
if (rc)
return rc;
@@ -3107,13 +3403,47 @@ static int ocontext_write(struct policydb *p, struct policydb_compat_info *info,
if (rc)
return rc;
break;
+ case OCON_IBPKEY:
+ /* subnet_prefix is in CPU order */
+ prefixbuf[0] =
+ cpu_to_be64(c->u.ibpkey.subnet_prefix);
+
+ rc = put_entry(prefixbuf, sizeof(u64), 1, fp);
+ if (rc)
+ return rc;
+
+ buf[0] = cpu_to_le32(c->u.ibpkey.low_pkey);
+ buf[1] = cpu_to_le32(c->u.ibpkey.high_pkey);
+
+ rc = put_entry(buf, sizeof(u32), 2, fp);
+ if (rc)
+ return rc;
+ rc = context_write(p, &c->context[0], fp);
+ if (rc)
+ return rc;
+ break;
+ case OCON_IBENDPORT:
+ len = strlen(c->u.ibendport.dev_name);
+ buf[0] = cpu_to_le32(len);
+ buf[1] = cpu_to_le32(c->u.ibendport.port);
+ rc = put_entry(buf, sizeof(u32), 2, fp);
+ if (rc)
+ return rc;
+ rc = put_entry(c->u.ibendport.dev_name, 1, len,
+ fp);
+ if (rc)
+ return rc;
+ rc = context_write(p, &c->context[0], fp);
+ if (rc)
+ return rc;
+ break;
}
}
}
return 0;
}
-static int genfs_write(struct policydb *p, void *fp)
+static int genfs_write(struct policydb *p, struct policy_file *fp)
{
struct genfs *genfs;
struct ocontext *c;
@@ -3165,21 +3495,13 @@ static int genfs_write(struct policydb *p, void *fp)
return 0;
}
-static int hashtab_cnt(void *key, void *data, void *ptr)
-{
- int *cnt = ptr;
- *cnt = *cnt + 1;
-
- return 0;
-}
-
static int range_write_helper(void *key, void *data, void *ptr)
{
__le32 buf[2];
struct range_trans *rt = key;
struct mls_range *r = data;
struct policy_data *pd = ptr;
- void *fp = pd->fp;
+ struct policy_file *fp = pd->fp;
struct policydb *p = pd->p;
int rc;
@@ -3201,9 +3523,8 @@ static int range_write_helper(void *key, void *data, void *ptr)
return 0;
}
-static int range_write(struct policydb *p, void *fp)
+static int range_write(struct policydb *p, struct policy_file *fp)
{
- size_t nel;
__le32 buf[1];
int rc;
struct policy_data pd;
@@ -3211,35 +3532,66 @@ static int range_write(struct policydb *p, void *fp)
pd.p = p;
pd.fp = fp;
- /* count the number of entries in the hashtab */
- nel = 0;
- rc = hashtab_map(p->range_tr, hashtab_cnt, &nel);
- if (rc)
- return rc;
-
- buf[0] = cpu_to_le32(nel);
+ buf[0] = cpu_to_le32(p->range_tr.nel);
rc = put_entry(buf, sizeof(u32), 1, fp);
if (rc)
return rc;
/* actually write all of the entries */
- rc = hashtab_map(p->range_tr, range_write_helper, &pd);
+ rc = hashtab_map(&p->range_tr, range_write_helper, &pd);
if (rc)
return rc;
return 0;
}
-static int filename_write_helper(void *key, void *data, void *ptr)
+static int filename_write_helper_compat(void *key, void *data, void *ptr)
{
+ struct filename_trans_key *ft = key;
+ struct filename_trans_datum *datum = data;
+ struct ebitmap_node *node;
+ struct policy_file *fp = ptr;
__le32 buf[4];
- struct filename_trans *ft = key;
- struct filename_trans_datum *otype = data;
- void *fp = ptr;
int rc;
- u32 len;
+ u32 bit, len = strlen(ft->name);
+
+ do {
+ ebitmap_for_each_positive_bit(&datum->stypes, node, bit)
+ {
+ buf[0] = cpu_to_le32(len);
+ rc = put_entry(buf, sizeof(u32), 1, fp);
+ if (rc)
+ return rc;
+
+ rc = put_entry(ft->name, sizeof(char), len, fp);
+ if (rc)
+ return rc;
+
+ buf[0] = cpu_to_le32(bit + 1);
+ buf[1] = cpu_to_le32(ft->ttype);
+ buf[2] = cpu_to_le32(ft->tclass);
+ buf[3] = cpu_to_le32(datum->otype);
+
+ rc = put_entry(buf, sizeof(u32), 4, fp);
+ if (rc)
+ return rc;
+ }
+
+ datum = datum->next;
+ } while (unlikely(datum));
+
+ return 0;
+}
+
+static int filename_write_helper(void *key, void *data, void *ptr)
+{
+ struct filename_trans_key *ft = key;
+ struct filename_trans_datum *datum;
+ struct policy_file *fp = ptr;
+ __le32 buf[3];
+ int rc;
+ u32 ndatum, len = strlen(ft->name);
- len = strlen(ft->name);
buf[0] = cpu_to_le32(len);
rc = put_entry(buf, sizeof(u32), 1, fp);
if (rc)
@@ -3249,42 +3601,62 @@ static int filename_write_helper(void *key, void *data, void *ptr)
if (rc)
return rc;
- buf[0] = ft->stype;
- buf[1] = ft->ttype;
- buf[2] = ft->tclass;
- buf[3] = otype->otype;
+ ndatum = 0;
+ datum = data;
+ do {
+ ndatum++;
+ datum = datum->next;
+ } while (unlikely(datum));
- rc = put_entry(buf, sizeof(u32), 4, fp);
+ buf[0] = cpu_to_le32(ft->ttype);
+ buf[1] = cpu_to_le32(ft->tclass);
+ buf[2] = cpu_to_le32(ndatum);
+ rc = put_entry(buf, sizeof(u32), 3, fp);
if (rc)
return rc;
+ datum = data;
+ do {
+ rc = ebitmap_write(&datum->stypes, fp);
+ if (rc)
+ return rc;
+
+ buf[0] = cpu_to_le32(datum->otype);
+ rc = put_entry(buf, sizeof(u32), 1, fp);
+ if (rc)
+ return rc;
+
+ datum = datum->next;
+ } while (unlikely(datum));
+
return 0;
}
-static int filename_trans_write(struct policydb *p, void *fp)
+static int filename_trans_write(struct policydb *p, struct policy_file *fp)
{
- u32 nel;
__le32 buf[1];
int rc;
if (p->policyvers < POLICYDB_VERSION_FILENAME_TRANS)
return 0;
- nel = 0;
- rc = hashtab_map(p->filename_trans, hashtab_cnt, &nel);
- if (rc)
- return rc;
-
- buf[0] = cpu_to_le32(nel);
- rc = put_entry(buf, sizeof(u32), 1, fp);
- if (rc)
- return rc;
+ if (p->policyvers < POLICYDB_VERSION_COMP_FTRANS) {
+ buf[0] = cpu_to_le32(p->compat_filename_trans_count);
+ rc = put_entry(buf, sizeof(u32), 1, fp);
+ if (rc)
+ return rc;
- rc = hashtab_map(p->filename_trans, filename_write_helper, fp);
- if (rc)
- return rc;
+ rc = hashtab_map(&p->filename_trans,
+ filename_write_helper_compat, fp);
+ } else {
+ buf[0] = cpu_to_le32(p->filename_trans.nel);
+ rc = put_entry(buf, sizeof(u32), 1, fp);
+ if (rc)
+ return rc;
- return 0;
+ rc = hashtab_map(&p->filename_trans, filename_write_helper, fp);
+ }
+ return rc;
}
/*
@@ -3292,14 +3664,14 @@ static int filename_trans_write(struct policydb *p, void *fp)
* structure to a policy database binary representation
* file.
*/
-int policydb_write(struct policydb *p, void *fp)
+int policydb_write(struct policydb *p, struct policy_file *fp)
{
- unsigned int i, num_syms;
+ unsigned int num_syms;
int rc;
__le32 buf[4];
- u32 config;
+ u32 config, i;
size_t len;
- struct policydb_compat_info *info;
+ const struct policydb_compat_info *info;
/*
* refuse to write policy older than compressed avtab
@@ -3308,9 +3680,9 @@ int policydb_write(struct policydb *p, void *fp)
* careful if you ever try to remove this restriction
*/
if (p->policyvers < POLICYDB_VERSION_AVTAB) {
- printk(KERN_ERR "SELinux: refusing to write policy version %d."
- " Because it is less than version %d\n", p->policyvers,
- POLICYDB_VERSION_AVTAB);
+ pr_err("SELinux: refusing to write policy version %d."
+ " Because it is less than version %d\n",
+ p->policyvers, POLICYDB_VERSION_AVTAB);
return -EINVAL;
}
@@ -3337,8 +3709,9 @@ int policydb_write(struct policydb *p, void *fp)
/* Write the version, config, and table sizes. */
info = policydb_lookup_compat(p->policyvers);
if (!info) {
- printk(KERN_ERR "SELinux: compatibility lookup failed for policy "
- "version %d", p->policyvers);
+ pr_err("SELinux: compatibility lookup failed for policy "
+ "version %d\n",
+ p->policyvers);
return -EINVAL;
}
@@ -3363,6 +3736,12 @@ int policydb_write(struct policydb *p, void *fp)
return rc;
}
+ if (p->policyvers >= POLICYDB_VERSION_NEVERAUDIT) {
+ rc = ebitmap_write(&p->neveraudit_map, fp);
+ if (rc)
+ return rc;
+ }
+
num_syms = info->sym_num;
for (i = 0; i < num_syms; i++) {
struct policy_data pd;
@@ -3371,12 +3750,12 @@ int policydb_write(struct policydb *p, void *fp)
pd.p = p;
buf[0] = cpu_to_le32(p->symtab[i].nprim);
- buf[1] = cpu_to_le32(p->symtab[i].table->nel);
+ buf[1] = cpu_to_le32(p->symtab[i].table.nel);
rc = put_entry(buf, sizeof(u32), 2, fp);
if (rc)
return rc;
- rc = hashtab_map(p->symtab[i].table, write_f[i], &pd);
+ rc = hashtab_map(&p->symtab[i].table, write_f[i], &pd);
if (rc)
return rc;
}
@@ -3385,7 +3764,7 @@ int policydb_write(struct policydb *p, void *fp)
if (rc)
return rc;
- rc = cond_write_list(p, p->cond_list, fp);
+ rc = cond_write_list(p, fp);
if (rc)
return rc;
@@ -3414,9 +3793,8 @@ int policydb_write(struct policydb *p, void *fp)
return rc;
for (i = 0; i < p->p_types.nprim; i++) {
- struct ebitmap *e = flex_array_get(p->type_attr_map_array, i);
+ struct ebitmap *e = &p->type_attr_map_array[i];
- BUG_ON(!e);
rc = ebitmap_write(e, fp);
if (rc)
return rc;