diff options
Diffstat (limited to 'security/selinux/ss/policydb.c')
| -rw-r--r-- | security/selinux/ss/policydb.c | 1920 |
1 files changed, 1079 insertions, 841 deletions
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index c1c31e33657a..91df3db6a88c 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -1,33 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Implementation of the policy database. * - * Author : Stephen Smalley, <sds@tycho.nsa.gov> + * 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 + * Added support for the policy capability bitmap + * Copyright (C) 2007 Hewlett-Packard Development Company, L.P. * * Update: Mellanox Techonologies - * - * Added Infiniband support - * - * Copyright (C) 2016 Mellanox Techonologies - * 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. + * Added Infiniband support + * Copyright (C) 2016 Mellanox Techonologies */ #include <linux/kernel.h> @@ -36,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" @@ -44,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", @@ -57,126 +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 - 3, + .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 - 3, + .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 - 2, + .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 - 2, + .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 - 2, + .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 - 2, + .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 - 2, + .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 - 2, + .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 - 2, + .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 - 2, + .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 - 2, + .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 - 2, + .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 - 2, + .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 - 2, + .version = POLICYDB_VERSION_CONSTRAINT_NAMES, + .sym_num = SYM_NUM, + .ocon_num = OCON_NUM - 2, }, { - .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_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_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); } - return info; +} + +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); + } + 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); } /* @@ -202,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; @@ -213,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; @@ -247,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; @@ -275,56 +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) { - rc = -ENOMEM; - goto out; - } + return key1->tclass - key2->tclass; +} - p->range_tr = hashtab_create(rangetr_hash, rangetr_cmp, 256); - if (!p->range_tr) { - rc = -ENOMEM; - 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); } /* @@ -341,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; } @@ -359,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; } @@ -377,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; } @@ -398,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; @@ -426,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; } @@ -447,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; @@ -469,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; @@ -477,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, @@ -497,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); - pr_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) @@ -514,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 @@ -534,21 +723,20 @@ static int policydb_index(struct policydb *p) int i, rc; if (p->mls_enabled) - 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); + 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); - pr_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 p->class_val_to_struct = kcalloc(p->p_classes.nprim, sizeof(*p->class_val_to_struct), @@ -556,48 +744,32 @@ static int policydb_index(struct policydb *p) if (!p->class_val_to_struct) return -ENOMEM; - p->role_val_to_struct = kcalloc(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) return -ENOMEM; - p->user_val_to_struct = kcalloc(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) return -ENOMEM; - /* Yes, I want the sizeof the pointer, not the structure */ - 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) + 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 = flex_array_prealloc(p->type_val_to_struct_array, 0, - p->p_types.nprim, GFP_KERNEL | __GFP_ZERO); - if (rc) - goto out; - rc = cond_init_bool_indexes(p); if (rc) goto out; for (i = 0; i < SYM_NUM; i++) { - 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]) return -ENOMEM; - rc = flex_array_prealloc(p->sym_val_to_name[i], - 0, p->symtab[i].nprim, - GFP_KERNEL | __GFP_ZERO); - if (rc) - goto out; - - 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; } @@ -607,219 +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 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); - } - kfree(datum); - return 0; -} - -static int sens_destroy(void *key, void *datum, void *p) -{ - struct level_datum *levdatum; - - kfree(key); - if (datum) { - levdatum = datum; - if (levdatum->level) - 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); @@ -852,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(); @@ -866,27 +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); + ebitmap_destroy(&p->neveraudit_map); } /* @@ -896,41 +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) { pr_err("SELinux: out of memory on SID table init\n"); - goto out; + 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) { - pr_err("SELinux: SID %s was never defined.\n", - c->u.name); - sidtab_destroy(s); - goto out; - } - if (c->sid[0] == SECSID_NULL || c->sid[0] > SECINITSID_NUM) { - pr_err("SELinux: Initial SID %s out of range.\n", - c->u.name); + 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); - goto out; + return -EINVAL; } - rc = sidtab_set_initial(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) { pr_err("SELinux: unable to load initial SID %s.\n", - c->u.name); + name); sidtab_destroy(s); - goto out; + 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) @@ -1003,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; @@ -1062,9 +1061,8 @@ 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; @@ -1102,7 +1100,7 @@ out: * binary representation file. */ -static int str_read(char **strp, gfp_t flags, void *fp, u32 len) +int str_read(char **strp, gfp_t flags, struct policy_file *fp, u32 len) { int rc; char *str; @@ -1114,18 +1112,18 @@ static int str_read(char **strp, gfp_t flags, void *fp, u32 len) if (!str) return -ENOMEM; - /* it's expected the caller should free the str */ - *strp = str; - rc = next_entry(str, fp, len); - if (rc) + if (rc) { + kfree(str); return rc; + } str[len] = '\0'; + *strp = str; return 0; } -static int perm_read(struct policydb *p, struct hashtab *h, void *fp) +static int perm_read(struct policydb *p, struct symtab *s, struct policy_file *fp) { char *key = NULL; struct perm_datum *perdatum; @@ -1148,7 +1146,7 @@ static int perm_read(struct policydb *p, struct hashtab *h, void *fp) if (rc) goto bad; - rc = hashtab_insert(h, key, perdatum); + rc = symtab_insert(s, key, perdatum); if (rc) goto bad; @@ -1158,13 +1156,13 @@ 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; comdatum = kzalloc(sizeof(*comdatum), GFP_KERNEL); if (!comdatum) @@ -1176,24 +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 = str_read(&key, GFP_KERNEL, fp, len); if (rc) goto bad; 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; @@ -1208,7 +1208,7 @@ static void type_set_init(struct type_set *t) ebitmap_init(&t->negset); } -static int type_set_read(struct type_set *t, void *fp) +static int type_set_read(struct type_set *t, struct policy_file *fp) { __le32 buf[1]; int rc; @@ -1226,16 +1226,14 @@ static int type_set_read(struct type_set *t, void *fp) return 0; } - -static int read_cons_helper(struct policydb *p, - struct constraint_node **nodep, - int ncons, int allowxtarget, void *fp) +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++) { @@ -1298,10 +1296,10 @@ static int read_cons_helper(struct policydb *p, if (rc) return rc; if (p->policyvers >= - POLICYDB_VERSION_CONSTRAINT_NAMES) { - e->type_names = kzalloc(sizeof - (*e->type_names), - GFP_KERNEL); + 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); @@ -1323,31 +1321,31 @@ static int read_cons_helper(struct policydb *p, 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; cladatum = kzalloc(sizeof(*cladatum), GFP_KERNEL); if (!cladatum) 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]); @@ -1361,7 +1359,8 @@ static int class_read(struct policydb *p, struct hashtab *h, void *fp) goto bad; rc = -EINVAL; - cladatum->comdatum = hashtab_search(p->p_commons.table, cladatum->comkey); + cladatum->comdatum = + symtab_search(&p->p_commons, cladatum->comkey); if (!cladatum->comdatum) { pr_err("SELinux: unknown common %s\n", cladatum->comkey); @@ -1369,11 +1368,13 @@ static int class_read(struct policydb *p, struct hashtab *h, void *fp) } } 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; } + hash_eval(&cladatum->permissions.table, "class_permissions", key); + rc = read_cons_helper(p, &cladatum->constraints, ncons, 0, fp); if (rc) goto bad; @@ -1384,8 +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(p, &cladatum->validatetrans, - ncons, 1, fp); + rc = read_cons_helper(p, &cladatum->validatetrans, ncons, 1, + fp); if (rc) goto bad; } @@ -1407,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; @@ -1417,11 +1418,12 @@ 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; @@ -1464,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; @@ -1473,11 +1475,12 @@ 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; @@ -1511,7 +1514,7 @@ static int type_read(struct policydb *p, struct hashtab *h, void *fp) if (rc) goto bad; - rc = hashtab_insert(h, key, typdatum); + rc = symtab_insert(s, key, typdatum); if (rc) goto bad; return 0; @@ -1520,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; @@ -1547,11 +1549,12 @@ static int mls_read_level(struct mls_level *lp, void *fp) 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; @@ -1588,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; @@ -1597,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; @@ -1605,7 +1608,7 @@ static int sens_read(struct policydb *p, struct hashtab *h, void *fp) __le32 buf[2]; u32 len; - levdatum = kzalloc(sizeof(*levdatum), GFP_ATOMIC); + levdatum = kzalloc(sizeof(*levdatum), GFP_KERNEL); if (!levdatum) return -ENOMEM; @@ -1616,20 +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 = str_read(&key, GFP_ATOMIC, fp, len); + rc = str_read(&key, GFP_KERNEL, fp, len); if (rc) goto bad; - rc = -ENOMEM; - levdatum->level = kmalloc(sizeof(*levdatum->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; @@ -1638,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; @@ -1646,7 +1644,7 @@ static int cat_read(struct policydb *p, struct hashtab *h, void *fp) __le32 buf[3]; u32 len; - catdatum = kzalloc(sizeof(*catdatum), GFP_ATOMIC); + catdatum = kzalloc(sizeof(*catdatum), GFP_KERNEL); if (!catdatum) return -ENOMEM; @@ -1658,11 +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 = str_read(&key, GFP_ATOMIC, fp, len); + rc = str_read(&key, GFP_KERNEL, fp, len); if (rc) goto bad; - rc = hashtab_insert(h, key, catdatum); + rc = symtab_insert(s, key, catdatum); if (rc) goto bad; return 0; @@ -1671,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, @@ -1682,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) { @@ -1692,17 +1692,18 @@ 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) { pr_err("SELinux: user %s: " - "too deep or looped boundary", - (char *) key); + "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; @@ -1728,17 +1729,18 @@ 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) { 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; @@ -1766,18 +1768,17 @@ static int type_bounds_sanity_check(void *key, void *datum, void *datap) if (++depth == POLICYDB_BOUNDS_MAXDEPTH) { 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) { pr_err("SELinux: type %s: " - "bounded by attribute %s", - (char *) key, + "bounded by attribute %s\n", + (char *)key, sym_name(p, SYM_TYPES, upper->value - 1)); return -EINVAL; } @@ -1793,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; @@ -1815,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; @@ -1831,27 +1829,25 @@ 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; @@ -1861,6 +1857,11 @@ static int range_read(struct policydb *p, void *fp) 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); @@ -1902,14 +1903,14 @@ static int range_read(struct policydb *p, void *fp) 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); @@ -1917,92 +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++) { - otype = NULL; - name = NULL; + /* path component string */ + rc = str_read(&name, GFP_KERNEL, fp, len); + if (rc) + return rc; + + 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; - ft = kzalloc(sizeof(*ft), GFP_KERNEL); - if (!ft) + datum = kmalloc(sizeof(*datum), GFP_KERNEL); + if (!datum) goto out; + 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; - 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)); + datum->next = NULL; + *dst = datum; + + /* ebitmap_read() will at least init the bitmap */ + rc = ebitmap_read(&datum->stypes, fp); if (rc) goto out; - len = le32_to_cpu(buf[0]); - /* path component string */ - rc = str_read(&name, GFP_KERNEL, fp, len); + rc = next_entry(buf, fp, sizeof(u32)); if (rc) goto out; - ft->name = name; + datum->otype = le32_to_cpu(buf[0]); - rc = next_entry(buf, fp, sizeof(u32) * 4); - if (rc) - goto out; + dst = &datum->next; + } - ft->stype = le32_to_cpu(buf[0]); - ft->ttype = le32_to_cpu(buf[1]); - ft->tclass = le32_to_cpu(buf[2]); + rc = -ENOMEM; + ft = kmalloc(sizeof(*ft), GFP_KERNEL); + if (!ft) + goto out; - otype->otype = le32_to_cpu(buf[3]); + ft->ttype = ttype; + ft->tclass = tclass; + ft->name = name; - rc = ebitmap_set_bit(&p->filename_trans_ttypes, ft->ttype, 1); - if (rc) - goto out; + 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); - rc = hashtab_insert(p->filename_trans, ft, otype); - if (rc) { - /* - * Do not return -EEXIST to the caller, or the system - * will not boot. - */ - if (rc != -EEXIST) - goto out; - /* But free memory to avoid memory leak. */ - kfree(ft); - kfree(name); - kfree(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; @@ -2073,12 +2207,12 @@ static int genfs_read(struct policydb *p, void *fp) 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 || @@ -2112,11 +2246,12 @@ out: 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; @@ -2147,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; @@ -2162,21 +2298,28 @@ static int ocontext_read(struct policydb *p, struct policydb_compat_info *info, if (rc) goto out; - 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; @@ -2186,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; @@ -2208,7 +2352,8 @@ static int ocontext_read(struct policydb *p, struct policydb_compat_info *info, if (rc) goto out; - 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; @@ -2221,8 +2366,9 @@ 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; @@ -2235,7 +2381,8 @@ static int ocontext_read(struct policydb *p, struct policydb_compat_info *info, goto out; /* we need to have subnet_prefix in CPU order */ - c->u.ibpkey.subnet_prefix = be64_to_cpu(prefixbuf[0]); + c->u.ibpkey.subnet_prefix = + be64_to_cpu(prefixbuf[0]); rc = next_entry(buf, fp, sizeof(u32) * 2); if (rc) @@ -2249,12 +2396,11 @@ static int ocontext_read(struct policydb *p, struct policydb_compat_info *info, goto out; } - c->u.ibpkey.low_pkey = pkey_lo; + c->u.ibpkey.low_pkey = pkey_lo; c->u.ibpkey.high_pkey = pkey_hi; rc = context_read_and_validate(&c->context[0], - p, - fp); + p, fp); if (rc) goto out; break; @@ -2267,7 +2413,8 @@ static int ocontext_read(struct policydb *p, struct policydb_compat_info *info, goto out; len = le32_to_cpu(buf[0]); - rc = str_read(&c->u.ibendport.dev_name, GFP_KERNEL, fp, len); + rc = str_read(&c->u.ibendport.dev_name, + GFP_KERNEL, fp, len); if (rc) goto out; @@ -2280,8 +2427,7 @@ static int ocontext_read(struct policydb *p, struct policydb_compat_info *info, c->u.ibendport.port = port; rc = context_read_and_validate(&c->context[0], - p, - fp); + p, fp); if (rc) goto out; break; @@ -2298,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); @@ -2335,26 +2480,22 @@ int policydb_read(struct policydb *p, void *fp) goto bad; } - rc = -ENOMEM; - policydb_str = kmalloc(len + 1, GFP_KERNEL); - if (!policydb_str) { - pr_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) { - pr_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)) { pr_err("SELinux: policydb string %s does not match " - "my string %s\n", policydb_str, POLICYDB_STRING); + "my string %s\n", + policydb_str, POLICYDB_STRING); kfree(policydb_str); goto bad; } @@ -2363,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; @@ -2373,7 +2514,8 @@ int policydb_read(struct policydb *p, void *fp) p->policyvers > POLICYDB_VERSION_MAX) { 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; } @@ -2383,8 +2525,8 @@ int policydb_read(struct policydb *p, void *fp) rc = -EINVAL; if (p->policyvers < POLICYDB_VERSION_MLS) { pr_err("SELinux: security policydb version %d " - "(MLS) not backwards compatible\n", - p->policyvers); + "(MLS) not backwards compatible\n", + p->policyvers); goto bad; } } @@ -2403,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) { pr_err("SELinux: unable to find policy compat info " - "for version %d\n", p->policyvers); + "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) { + 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); + "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; } @@ -2438,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) @@ -2455,41 +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; rc = -EINVAL; - 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)) + 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; + + rc = hashtab_insert(&p->role_tr, rtk, rtd, roletr_key_params); + if (rc) goto bad; - ltr = tr; + + rtk = NULL; + rtd = NULL; } + hash_eval(&p->role_tr, "roletr", NULL); + rc = next_entry(buf, fp, sizeof(u32)); if (rc) goto bad; @@ -2504,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; @@ -2526,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) @@ -2544,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) @@ -2580,6 +2757,8 @@ int policydb_read(struct policydb *p, void *fp) out: return rc; bad: + kfree(rtk); + kfree(rtd); policydb_destroy(p); goto out; } @@ -2588,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; @@ -2609,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; @@ -2621,7 +2800,7 @@ 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); @@ -2649,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; @@ -2665,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; @@ -2677,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; @@ -2697,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; @@ -2758,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]; @@ -2812,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; @@ -2821,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; @@ -2830,14 +3014,14 @@ 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, void *fp) +static int type_set_write(struct type_set *t, struct policy_file *fp) { int rc; __le32 buf[1]; @@ -2856,7 +3040,7 @@ static int type_set_write(struct type_set *t, void *fp) } 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; @@ -2887,7 +3071,7 @@ static int write_cons_helper(struct policydb *p, struct constraint_node *node, if (rc) return rc; if (p->policyvers >= - POLICYDB_VERSION_CONSTRAINT_NAMES) { + POLICYDB_VERSION_CONSTRAINT_NAMES) { rc = type_set_write(e->type_names, fp); if (rc) return rc; @@ -2907,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]; @@ -2929,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) @@ -2948,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; @@ -2995,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; @@ -3035,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; @@ -3076,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; @@ -3111,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, @@ -3123,11 +3303,14 @@ 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]; @@ -3206,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; @@ -3218,7 +3405,8 @@ static int ocontext_write(struct policydb *p, struct policydb_compat_info *info, break; case OCON_IBPKEY: /* subnet_prefix is in CPU order */ - prefixbuf[0] = cpu_to_be64(c->u.ibpkey.subnet_prefix); + prefixbuf[0] = + cpu_to_be64(c->u.ibpkey.subnet_prefix); rc = put_entry(prefixbuf, sizeof(u64), 1, fp); if (rc) @@ -3241,7 +3429,8 @@ static int ocontext_write(struct policydb *p, struct policydb_compat_info *info, rc = put_entry(buf, sizeof(u32), 2, fp); if (rc) return rc; - rc = put_entry(c->u.ibendport.dev_name, 1, len, fp); + rc = put_entry(c->u.ibendport.dev_name, 1, len, + fp); if (rc) return rc; rc = context_write(p, &c->context[0], fp); @@ -3254,7 +3443,7 @@ static int ocontext_write(struct policydb *p, struct policydb_compat_info *info, 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; @@ -3306,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; @@ -3342,44 +3523,75 @@ 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) { __le32 buf[1]; - int rc, nel; + int rc; struct policy_data pd; 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) @@ -3389,42 +3601,62 @@ static int filename_write_helper(void *key, void *data, void *ptr) if (rc) return rc; - buf[0] = cpu_to_le32(ft->stype); - buf[1] = cpu_to_le32(ft->ttype); - buf[2] = cpu_to_le32(ft->tclass); - buf[3] = cpu_to_le32(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; } /* @@ -3432,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 @@ -3449,8 +3681,8 @@ int policydb_write(struct policydb *p, void *fp) */ if (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); + " Because it is less than version %d\n", + p->policyvers, POLICYDB_VERSION_AVTAB); return -EINVAL; } @@ -3478,7 +3710,8 @@ int policydb_write(struct policydb *p, void *fp) info = policydb_lookup_compat(p->policyvers); if (!info) { pr_err("SELinux: compatibility lookup failed for policy " - "version %d", p->policyvers); + "version %d\n", + p->policyvers); return -EINVAL; } @@ -3503,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; @@ -3511,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; } @@ -3525,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; @@ -3554,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; |
