diff options
Diffstat (limited to 'security/selinux/ss/policydb.c')
| -rw-r--r-- | security/selinux/ss/policydb.c | 2262 |
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; |
