diff options
Diffstat (limited to 'security/selinux/ss/policydb.c')
-rw-r--r-- | security/selinux/ss/policydb.c | 1843 |
1 files changed, 1843 insertions, 0 deletions
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c new file mode 100644 index 0000000..14190ef --- /dev/null +++ b/security/selinux/ss/policydb.c @@ -0,0 +1,1843 @@ +/* + * Implementation of the policy database. + * + * Author : Stephen Smalley, <sds@epoch.ncsc.mil> + */ + +/* + * Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com> + * + * Support for enhanced MLS infrastructure. + * + * Updated: Frank Mayer <mayerf@tresys.com> and Karl MacMillan <kmacmillan@tresys.com> + * + * Added conditional policy language extensions + * + * 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. + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/errno.h> +#include "security.h" + +#include "policydb.h" +#include "conditional.h" +#include "mls.h" + +#define _DEBUG_HASHES + +#ifdef DEBUG_HASHES +static char *symtab_name[SYM_NUM] = { + "common prefixes", + "classes", + "roles", + "types", + "users", + "bools", + "levels", + "categories", +}; +#endif + +int selinux_mls_enabled = 0; + +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; +}; + +/* These need to be updated if SYM_NUM or OCON_NUM changes */ +static struct policydb_compat_info policydb_compat[] = { + { + .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 - 1, + }, + { + .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, + }, + { + .version = POLICYDB_VERSION_MLS, + .sym_num = SYM_NUM, + .ocon_num = OCON_NUM, + }, +}; + +static struct policydb_compat_info *policydb_lookup_compat(int version) +{ + int i; + struct policydb_compat_info *info = NULL; + + for (i = 0; i < sizeof(policydb_compat)/sizeof(*info); i++) { + if (policydb_compat[i].version == version) { + info = &policydb_compat[i]; + break; + } + } + return info; +} + +/* + * Initialize the role table. + */ +static int roles_init(struct policydb *p) +{ + char *key = NULL; + int rc; + struct role_datum *role; + + role = kmalloc(sizeof(*role), GFP_KERNEL); + if (!role) { + rc = -ENOMEM; + goto out; + } + memset(role, 0, sizeof(*role)); + role->value = ++p->p_roles.nprim; + if (role->value != OBJECT_R_VAL) { + rc = -EINVAL; + goto out_free_role; + } + key = kmalloc(strlen(OBJECT_R)+1,GFP_KERNEL); + if (!key) { + rc = -ENOMEM; + goto out_free_role; + } + strcpy(key, OBJECT_R); + rc = hashtab_insert(p->p_roles.table, key, role); + if (rc) + goto out_free_key; +out: + return rc; + +out_free_key: + kfree(key); +out_free_role: + kfree(role); + goto out; +} + +/* + * Initialize a policy database structure. + */ +static int policydb_init(struct policydb *p) +{ + int i, rc; + + memset(p, 0, sizeof(*p)); + + for (i = 0; i < SYM_NUM; i++) { + rc = symtab_init(&p->symtab[i], symtab_sizes[i]); + if (rc) + goto out_free_symtab; + } + + rc = avtab_init(&p->te_avtab); + if (rc) + goto out_free_symtab; + + rc = roles_init(p); + if (rc) + goto out_free_avtab; + + rc = cond_policydb_init(p); + if (rc) + goto out_free_avtab; + +out: + return rc; + +out_free_avtab: + avtab_destroy(&p->te_avtab); + +out_free_symtab: + for (i = 0; i < SYM_NUM; i++) + hashtab_destroy(p->symtab[i].table); + goto out; +} + +/* + * The following *_index functions are used to + * define the val_to_name and val_to_struct arrays + * in a policy database structure. The val_to_name + * arrays are used when converting security context + * structures into string representations. The + * val_to_struct arrays are used when the attributes + * of a class, role, or user are needed. + */ + +static int common_index(void *key, void *datum, void *datap) +{ + struct policydb *p; + struct common_datum *comdatum; + + comdatum = datum; + p = datap; + if (!comdatum->value || comdatum->value > p->p_commons.nprim) + return -EINVAL; + p->p_common_val_to_name[comdatum->value - 1] = key; + return 0; +} + +static int class_index(void *key, void *datum, void *datap) +{ + struct policydb *p; + struct class_datum *cladatum; + + cladatum = datum; + p = datap; + if (!cladatum->value || cladatum->value > p->p_classes.nprim) + return -EINVAL; + p->p_class_val_to_name[cladatum->value - 1] = key; + p->class_val_to_struct[cladatum->value - 1] = cladatum; + return 0; +} + +static int role_index(void *key, void *datum, void *datap) +{ + struct policydb *p; + struct role_datum *role; + + role = datum; + p = datap; + if (!role->value || role->value > p->p_roles.nprim) + return -EINVAL; + p->p_role_val_to_name[role->value - 1] = key; + p->role_val_to_struct[role->value - 1] = role; + return 0; +} + +static int type_index(void *key, void *datum, void *datap) +{ + struct policydb *p; + struct type_datum *typdatum; + + typdatum = datum; + p = datap; + + if (typdatum->primary) { + if (!typdatum->value || typdatum->value > p->p_types.nprim) + return -EINVAL; + p->p_type_val_to_name[typdatum->value - 1] = key; + } + + return 0; +} + +static int user_index(void *key, void *datum, void *datap) +{ + struct policydb *p; + struct user_datum *usrdatum; + + usrdatum = datum; + p = datap; + if (!usrdatum->value || usrdatum->value > p->p_users.nprim) + return -EINVAL; + p->p_user_val_to_name[usrdatum->value - 1] = key; + p->user_val_to_struct[usrdatum->value - 1] = usrdatum; + return 0; +} + +static int sens_index(void *key, void *datum, void *datap) +{ + struct policydb *p; + struct level_datum *levdatum; + + levdatum = datum; + p = datap; + + if (!levdatum->isalias) { + if (!levdatum->level->sens || + levdatum->level->sens > p->p_levels.nprim) + return -EINVAL; + p->p_sens_val_to_name[levdatum->level->sens - 1] = key; + } + + return 0; +} + +static int cat_index(void *key, void *datum, void *datap) +{ + struct policydb *p; + struct cat_datum *catdatum; + + catdatum = datum; + p = datap; + + if (!catdatum->isalias) { + if (!catdatum->value || catdatum->value > p->p_cats.nprim) + return -EINVAL; + p->p_cat_val_to_name[catdatum->value - 1] = key; + } + + return 0; +} + +static int (*index_f[SYM_NUM]) (void *key, void *datum, void *datap) = +{ + common_index, + class_index, + role_index, + type_index, + user_index, + cond_index_bool, + sens_index, + cat_index, +}; + +/* + * Define the common val_to_name array and the class + * val_to_name and val_to_struct arrays in a policy + * database structure. + * + * Caller must clean up upon failure. + */ +static int policydb_index_classes(struct policydb *p) +{ + int rc; + + p->p_common_val_to_name = + kmalloc(p->p_commons.nprim * sizeof(char *), GFP_KERNEL); + if (!p->p_common_val_to_name) { + rc = -ENOMEM; + goto out; + } + + rc = hashtab_map(p->p_commons.table, common_index, p); + if (rc) + goto out; + + p->class_val_to_struct = + kmalloc(p->p_classes.nprim * sizeof(*(p->class_val_to_struct)), GFP_KERNEL); + if (!p->class_val_to_struct) { + rc = -ENOMEM; + goto out; + } + + p->p_class_val_to_name = + kmalloc(p->p_classes.nprim * sizeof(char *), GFP_KERNEL); + if (!p->p_class_val_to_name) { + rc = -ENOMEM; + goto out; + } + + rc = hashtab_map(p->p_classes.table, class_index, p); +out: + return rc; +} + +#ifdef DEBUG_HASHES +static void symtab_hash_eval(struct symtab *s) +{ + int i; + + for (i = 0; i < SYM_NUM; i++) { + struct hashtab *h = s[i].table; + struct hashtab_info info; + + hashtab_stat(h, &info); + printk(KERN_INFO "%s: %d entries and %d/%d buckets used, " + "longest chain length %d\n", symtab_name[i], h->nel, + info.slots_used, h->size, info.max_chain_len); + } +} +#endif + +/* + * Define the other val_to_name and val_to_struct arrays + * in a policy database structure. + * + * Caller must clean up on failure. + */ +static int policydb_index_others(struct policydb *p) +{ + int i, rc = 0; + + printk(KERN_INFO "security: %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 (selinux_mls_enabled) + printk(", %d sens, %d cats", p->p_levels.nprim, + p->p_cats.nprim); + printk("\n"); + + printk(KERN_INFO "security: %d classes, %d rules\n", + p->p_classes.nprim, p->te_avtab.nel); + +#ifdef DEBUG_HASHES + avtab_hash_eval(&p->te_avtab, "rules"); + symtab_hash_eval(p->symtab); +#endif + + p->role_val_to_struct = + kmalloc(p->p_roles.nprim * sizeof(*(p->role_val_to_struct)), + GFP_KERNEL); + if (!p->role_val_to_struct) { + rc = -ENOMEM; + goto out; + } + + p->user_val_to_struct = + kmalloc(p->p_users.nprim * sizeof(*(p->user_val_to_struct)), + GFP_KERNEL); + if (!p->user_val_to_struct) { + rc = -ENOMEM; + goto out; + } + + if (cond_init_bool_indexes(p)) { + rc = -ENOMEM; + goto out; + } + + for (i = SYM_ROLES; i < SYM_NUM; i++) { + p->sym_val_to_name[i] = + kmalloc(p->symtab[i].nprim * sizeof(char *), GFP_KERNEL); + if (!p->sym_val_to_name[i]) { + rc = -ENOMEM; + goto out; + } + rc = hashtab_map(p->symtab[i].table, index_f[i], p); + if (rc) + goto out; + } + +out: + return rc; +} + +/* + * 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); + comdatum = datum; + hashtab_map(comdatum->permissions.table, perm_destroy, NULL); + hashtab_destroy(comdatum->permissions.table); + kfree(datum); + return 0; +} + +static int class_destroy(void *key, void *datum, void *p) +{ + struct class_datum *cladatum; + struct constraint_node *constraint, *ctemp; + struct constraint_expr *e, *etmp; + + kfree(key); + 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); + 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); + 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); + 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, + class_destroy, + role_destroy, + type_destroy, + user_destroy, + cond_destroy_bool, + sens_destroy, + cat_destroy, +}; + +static void ocontext_destroy(struct ocontext *c, int i) +{ + 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; + + for (i = 0; i < SYM_NUM; i++) { + 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]) + kfree(p->sym_val_to_name[i]); + } + + if (p->class_val_to_struct) + kfree(p->class_val_to_struct); + if (p->role_val_to_struct) + kfree(p->role_val_to_struct); + if (p->user_val_to_struct) + kfree(p->user_val_to_struct); + + avtab_destroy(&p->te_avtab); + + for (i = 0; i < OCON_NUM; i++) { + c = p->ocontexts[i]; + while (c) { + ctmp = c; + c = c->next; + ocontext_destroy(ctmp,i); + } + } + + g = p->genfs; + while (g) { + kfree(g->fstype); + c = g->head; + while (c) { + ctmp = c; + c = c->next; + ocontext_destroy(ctmp,OCON_FSUSE); + } + gtmp = g; + g = g->next; + kfree(gtmp); + } + + cond_policydb_destroy(p); + + return; +} + +/* + * Load the initial SIDs specified in a policy database + * structure into a SID table. + */ +int policydb_load_isids(struct policydb *p, struct sidtab *s) +{ + struct ocontext *head, *c; + int rc; + + rc = sidtab_init(s); + if (rc) { + printk(KERN_ERR "security: out of memory on SID table init\n"); + goto out; + } + + head = p->ocontexts[OCON_ISID]; + for (c = head; c; c = c->next) { + if (!c->context[0].user) { + printk(KERN_ERR "security: SID %s was never " + "defined.\n", c->u.name); + rc = -EINVAL; + goto out; + } + if (sidtab_insert(s, c->sid[0], &c->context[0])) { + printk(KERN_ERR "security: unable to load initial " + "SID %s.\n", c->u.name); + rc = -EINVAL; + goto out; + } + } +out: + return rc; +} + +/* + * Return 1 if the fields in the security context + * structure `c' are valid. Return 0 otherwise. + */ +int policydb_context_isvalid(struct policydb *p, struct context *c) +{ + struct role_datum *role; + struct user_datum *usrdatum; + + if (!c->role || c->role > p->p_roles.nprim) + return 0; + + if (!c->user || c->user > p->p_users.nprim) + return 0; + + if (!c->type || c->type > p->p_types.nprim) + return 0; + + if (c->role != OBJECT_R_VAL) { + /* + * 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)) + /* role may not be associated with type */ + return 0; + + /* + * User must be authorized for the role. + */ + usrdatum = p->user_val_to_struct[c->user - 1]; + if (!usrdatum) + return 0; + + if (!ebitmap_get_bit(&usrdatum->roles, + c->role - 1)) + /* user may not be associated with role */ + return 0; + } + + if (!mls_context_isvalid(p, c)) + return 0; + + return 1; +} + +/* + * Read a MLS range structure from a policydb binary + * representation file. + */ +static int mls_read_range_helper(struct mls_range *r, void *fp) +{ + u32 buf[2], items; + int rc; + + rc = next_entry(buf, fp, sizeof(u32)); + if (rc < 0) + goto out; + + items = le32_to_cpu(buf[0]); + if (items > ARRAY_SIZE(buf)) { + printk(KERN_ERR "security: mls: range overflow\n"); + rc = -EINVAL; + goto out; + } + rc = next_entry(buf, fp, sizeof(u32) * items); + if (rc < 0) { + printk(KERN_ERR "security: mls: truncated range\n"); + goto out; + } + r->level[0].sens = le32_to_cpu(buf[0]); + if (items > 1) + r->level[1].sens = le32_to_cpu(buf[1]); + else + r->level[1].sens = r->level[0].sens; + + rc = ebitmap_read(&r->level[0].cat, fp); + if (rc) { + printk(KERN_ERR "security: mls: error reading low " + "categories\n"); + goto out; + } + if (items > 1) { + rc = ebitmap_read(&r->level[1].cat, fp); + if (rc) { + printk(KERN_ERR "security: 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 "security: mls: out of memory\n"); + goto bad_high; + } + } + + rc = 0; +out: + return rc; +bad_high: + ebitmap_destroy(&r->level[0].cat); + goto 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) +{ + u32 buf[3]; + int rc; + + rc = next_entry(buf, fp, sizeof buf); + if (rc < 0) { + printk(KERN_ERR "security: context truncated\n"); + goto out; + } + c->user = le32_to_cpu(buf[0]); + c->role = le32_to_cpu(buf[1]); + c->type = le32_to_cpu(buf[2]); + if (p->policyvers >= POLICYDB_VERSION_MLS) { + if (mls_read_range_helper(&c->range, fp)) { + printk(KERN_ERR "security: error reading MLS range of " + "context\n"); + rc = -EINVAL; + goto out; + } + } + + if (!policydb_context_isvalid(p, c)) { + printk(KERN_ERR "security: invalid security context\n"); + context_destroy(c); + rc = -EINVAL; + } +out: + return rc; +} + +/* + * The following *_read functions are used to + * read the symbol data from a policy database + * binary representation file. + */ + +static int perm_read(struct policydb *p, struct hashtab *h, void *fp) +{ + char *key = NULL; + struct perm_datum *perdatum; + int rc; + u32 buf[2], len; + + perdatum = kmalloc(sizeof(*perdatum), GFP_KERNEL); + if (!perdatum) { + rc = -ENOMEM; + goto out; + } + memset(perdatum, 0, sizeof(*perdatum)); + + rc = next_entry(buf, fp, sizeof buf); + if (rc < 0) + goto bad; + + len = le32_to_cpu(buf[0]); + perdatum->value = le32_to_cpu(buf[1]); + + key = kmalloc(len + 1,GFP_KERNEL); + if (!key) { + rc = -ENOMEM; + goto bad; + } + rc = next_entry(key, fp, len); + if (rc < 0) + goto bad; + key[len] = 0; + + rc = hashtab_insert(h, key, perdatum); + if (rc) + goto bad; +out: + return rc; +bad: + perm_destroy(key, perdatum, NULL); + goto out; +} + +static int common_read(struct policydb *p, struct hashtab *h, void *fp) +{ + char *key = NULL; + struct common_datum *comdatum; + u32 buf[4], len, nel; + int i, rc; + + comdatum = kmalloc(sizeof(*comdatum), GFP_KERNEL); + if (!comdatum) { + rc = -ENOMEM; + goto out; + } + memset(comdatum, 0, sizeof(*comdatum)); + + rc = next_entry(buf, fp, sizeof buf); + if (rc < 0) + goto bad; + + len = le32_to_cpu(buf[0]); + comdatum->value = le32_to_cpu(buf[1]); + + rc = symtab_init(&comdatum->permissions, PERM_SYMTAB_SIZE); + if (rc) + goto bad; + comdatum->permissions.nprim = le32_to_cpu(buf[2]); + nel = le32_to_cpu(buf[3]); + + key = kmalloc(len + 1,GFP_KERNEL); + if (!key) { + rc = -ENOMEM; + goto bad; + } + rc = next_entry(key, fp, len); + if (rc < 0) + goto bad; + key[len] = 0; + + for (i = 0; i < nel; i++) { + rc = perm_read(p, comdatum->permissions.table, fp); + if (rc) + goto bad; + } + + rc = hashtab_insert(h, key, comdatum); + if (rc) + goto bad; +out: + return rc; +bad: + common_destroy(key, comdatum, NULL); + goto out; +} + +static int read_cons_helper(struct constraint_node **nodep, int ncons, + int allowxtarget, void *fp) +{ + struct constraint_node *c, *lc; + struct constraint_expr *e, *le; + u32 buf[3], nexpr; + int rc, i, j, depth; + + lc = NULL; + for (i = 0; i < ncons; i++) { + c = kmalloc(sizeof(*c), GFP_KERNEL); + if (!c) + return -ENOMEM; + memset(c, 0, sizeof(*c)); + + if (lc) { + lc->next = c; + } else { + *nodep = c; + } + + rc = next_entry(buf, fp, (sizeof(u32) * 2)); + if (rc < 0) + return rc; + c->permissions = le32_to_cpu(buf[0]); + nexpr = le32_to_cpu(buf[1]); + le = NULL; + depth = -1; + for (j = 0; j < nexpr; j++) { + e = kmalloc(sizeof(*e), GFP_KERNEL); + if (!e) + return -ENOMEM; + memset(e, 0, sizeof(*e)); + + if (le) { + le->next = e; + } else { + c->expr = e; + } + + rc = next_entry(buf, fp, (sizeof(u32) * 3)); + if (rc < 0) + return rc; + e->expr_type = le32_to_cpu(buf[0]); + e->attr = le32_to_cpu(buf[1]); + e->op = le32_to_cpu(buf[2]); + + switch (e->expr_type) { + case CEXPR_NOT: + if (depth < 0) + return -EINVAL; + break; + case CEXPR_AND: + case CEXPR_OR: + if (depth < 1) + return -EINVAL; + depth--; + break; + case CEXPR_ATTR: + if (depth == (CEXPR_MAXDEPTH - 1)) + return -EINVAL; + depth++; + break; + case CEXPR_NAMES: + if (!allowxtarget && (e->attr & CEXPR_XTARGET)) + return -EINVAL; + if (depth == (CEXPR_MAXDEPTH - 1)) + return -EINVAL; + depth++; + if (ebitmap_read(&e->names, fp)) + return -EINVAL; + break; + default: + return -EINVAL; + } + le = e; + } + if (depth != 0) + return -EINVAL; + lc = c; + } + + return 0; +} + +static int class_read(struct policydb *p, struct hashtab *h, void *fp) +{ + char *key = NULL; + struct class_datum *cladatum; + u32 buf[6], len, len2, ncons, nel; + int i, rc; + + cladatum = kmalloc(sizeof(*cladatum), GFP_KERNEL); + if (!cladatum) { + rc = -ENOMEM; + goto out; + } + memset(cladatum, 0, sizeof(*cladatum)); + + rc = next_entry(buf, fp, sizeof(u32)*6); + if (rc < 0) + goto bad; + + len = le32_to_cpu(buf[0]); + len2 = le32_to_cpu(buf[1]); + cladatum->value = le32_to_cpu(buf[2]); + + rc = symtab_init(&cladatum->permissions, PERM_SYMTAB_SIZE); + if (rc) + goto bad; + cladatum->permissions.nprim = le32_to_cpu(buf[3]); + nel = le32_to_cpu(buf[4]); + + ncons = le32_to_cpu(buf[5]); + + key = kmalloc(len + 1,GFP_KERNEL); + if (!key) { + rc = -ENOMEM; + goto bad; + } + rc = next_entry(key, fp, len); + if (rc < 0) + goto bad; + key[len] = 0; + + if (len2) { + cladatum->comkey = kmalloc(len2 + 1,GFP_KERNEL); + if (!cladatum->comkey) { + rc = -ENOMEM; + goto bad; + } + rc = next_entry(cladatum->comkey, fp, len2); + if (rc < 0) + goto bad; + cladatum->comkey[len2] = 0; + + cladatum->comdatum = hashtab_search(p->p_commons.table, + cladatum->comkey); + if (!cladatum->comdatum) { + printk(KERN_ERR "security: unknown common %s\n", + cladatum->comkey); + rc = -EINVAL; + goto bad; + } + } + for (i = 0; i < nel; i++) { + rc = perm_read(p, cladatum->permissions.table, fp); + if (rc) + goto bad; + } + + rc = read_cons_helper(&cladatum->constraints, ncons, 0, fp); + if (rc) + goto bad; + + if (p->policyvers >= POLICYDB_VERSION_VALIDATETRANS) { + /* grab the validatetrans rules */ + rc = next_entry(buf, fp, sizeof(u32)); + if (rc < 0) + goto bad; + ncons = le32_to_cpu(buf[0]); + rc = read_cons_helper(&cladatum->validatetrans, ncons, 1, fp); + if (rc) + goto bad; + } + + rc = hashtab_insert(h, key, cladatum); + if (rc) + goto bad; + + rc = 0; +out: + return rc; +bad: + class_destroy(key, cladatum, NULL); + goto out; +} + +static int role_read(struct policydb *p, struct hashtab *h, void *fp) +{ + char *key = NULL; + struct role_datum *role; + int rc; + u32 buf[2], len; + + role = kmalloc(sizeof(*role), GFP_KERNEL); + if (!role) { + rc = -ENOMEM; + goto out; + } + memset(role, 0, sizeof(*role)); + + rc = next_entry(buf, fp, sizeof buf); + if (rc < 0) + goto bad; + + len = le32_to_cpu(buf[0]); + role->value = le32_to_cpu(buf[1]); + + key = kmalloc(len + 1,GFP_KERNEL); + if (!key) { + rc = -ENOMEM; + goto bad; + } + rc = next_entry(key, fp, len); + if (rc < 0) + goto bad; + key[len] = 0; + + rc = ebitmap_read(&role->dominates, fp); + if (rc) + goto bad; + + rc = ebitmap_read(&role->types, fp); + if (rc) + goto bad; + + if (strcmp(key, OBJECT_R) == 0) { + if (role->value != OBJECT_R_VAL) { + printk(KERN_ERR "Role %s has wrong value %d\n", + OBJECT_R, role->value); + rc = -EINVAL; + goto bad; + } + rc = 0; + goto bad; + } + + rc = hashtab_insert(h, key, role); + if (rc) + goto bad; +out: + return rc; +bad: + role_destroy(key, role, NULL); + goto out; +} + +static int type_read(struct policydb *p, struct hashtab *h, void *fp) +{ + char *key = NULL; + struct type_datum *typdatum; + int rc; + u32 buf[3], len; + + typdatum = kmalloc(sizeof(*typdatum),GFP_KERNEL); + if (!typdatum) { + rc = -ENOMEM; + return rc; + } + memset(typdatum, 0, sizeof(*typdatum)); + + rc = next_entry(buf, fp, sizeof buf); + if (rc < 0) + goto bad; + + len = le32_to_cpu(buf[0]); + typdatum->value = le32_to_cpu(buf[1]); + typdatum->primary = le32_to_cpu(buf[2]); + + key = kmalloc(len + 1,GFP_KERNEL); + if (!key) { + rc = -ENOMEM; + goto bad; + } + rc = next_entry(key, fp, len); + if (rc < 0) + goto bad; + key[len] = 0; + + rc = hashtab_insert(h, key, typdatum); + if (rc) + goto bad; +out: + return rc; +bad: + type_destroy(key, typdatum, NULL); + goto out; +} + + +/* + * Read a MLS level structure from a policydb binary + * representation file. + */ +static int mls_read_level(struct mls_level *lp, void *fp) +{ + u32 buf[1]; + int rc; + + memset(lp, 0, sizeof(*lp)); + + rc = next_entry(buf, fp, sizeof buf); + if (rc < 0) { + printk(KERN_ERR "security: mls: truncated level\n"); + goto bad; + } + lp->sens = le32_to_cpu(buf[0]); + + if (ebitmap_read(&lp->cat, fp)) { + printk(KERN_ERR "security: mls: error reading level " + "categories\n"); + goto bad; + } + return 0; + +bad: + return -EINVAL; +} + +static int user_read(struct policydb *p, struct hashtab *h, void *fp) +{ + char *key = NULL; + struct user_datum *usrdatum; + int rc; + u32 buf[2], len; + + usrdatum = kmalloc(sizeof(*usrdatum), GFP_KERNEL); + if (!usrdatum) { + rc = -ENOMEM; + goto out; + } + memset(usrdatum, 0, sizeof(*usrdatum)); + + rc = next_entry(buf, fp, sizeof buf); + if (rc < 0) + goto bad; + + len = le32_to_cpu(buf[0]); + usrdatum->value = le32_to_cpu(buf[1]); + + key = kmalloc(len + 1,GFP_KERNEL); + if (!key) { + rc = -ENOMEM; + goto bad; + } + rc = next_entry(key, fp, len); + if (rc < 0) + goto bad; + key[len] = 0; + + rc = ebitmap_read(&usrdatum->roles, fp); + if (rc) + goto bad; + + if (p->policyvers >= POLICYDB_VERSION_MLS) { + rc = mls_read_range_helper(&usrdatum->range, fp); + if (rc) + goto bad; + rc = mls_read_level(&usrdatum->dfltlevel, fp); + if (rc) + goto bad; + } + + rc = hashtab_insert(h, key, usrdatum); + if (rc) + goto bad; +out: + return rc; +bad: + user_destroy(key, usrdatum, NULL); + goto out; +} + +static int sens_read(struct policydb *p, struct hashtab *h, void *fp) +{ + char *key = NULL; + struct level_datum *levdatum; + int rc; + u32 buf[2], len; + + levdatum = kmalloc(sizeof(*levdatum), GFP_ATOMIC); + if (!levdatum) { + rc = -ENOMEM; + goto out; + } + memset(levdatum, 0, sizeof(*levdatum)); + + rc = next_entry(buf, fp, sizeof buf); + if (rc < 0) + goto bad; + + len = le32_to_cpu(buf[0]); + levdatum->isalias = le32_to_cpu(buf[1]); + + key = kmalloc(len + 1,GFP_ATOMIC); + if (!key) { + rc = -ENOMEM; + goto bad; + } + rc = next_entry(key, fp, len); + if (rc < 0) + goto bad; + key[len] = 0; + + levdatum->level = kmalloc(sizeof(struct mls_level), GFP_ATOMIC); + if (!levdatum->level) { + rc = -ENOMEM; + goto bad; + } + if (mls_read_level(levdatum->level, fp)) { + rc = -EINVAL; + goto bad; + } + + rc = hashtab_insert(h, key, levdatum); + if (rc) + goto bad; +out: + return rc; +bad: + sens_destroy(key, levdatum, NULL); + goto out; +} + +static int cat_read(struct policydb *p, struct hashtab *h, void *fp) +{ + char *key = NULL; + struct cat_datum *catdatum; + int rc; + u32 buf[3], len; + + catdatum = kmalloc(sizeof(*catdatum), GFP_ATOMIC); + if (!catdatum) { + rc = -ENOMEM; + goto out; + } + memset(catdatum, 0, sizeof(*catdatum)); + + rc = next_entry(buf, fp, sizeof buf); + if (rc < 0) + goto bad; + + len = le32_to_cpu(buf[0]); + catdatum->value = le32_to_cpu(buf[1]); + catdatum->isalias = le32_to_cpu(buf[2]); + + key = kmalloc(len + 1,GFP_ATOMIC); + if (!key) { + rc = -ENOMEM; + goto bad; + } + rc = next_entry(key, fp, len); + if (rc < 0) + goto bad; + key[len] = 0; + + rc = hashtab_insert(h, key, catdatum); + if (rc) + goto bad; +out: + return rc; + +bad: + cat_destroy(key, catdatum, NULL); + goto out; +} + +static int (*read_f[SYM_NUM]) (struct policydb *p, struct hashtab *h, void *fp) = +{ + common_read, + class_read, + role_read, + type_read, + user_read, + cond_read_bool, + sens_read, + cat_read, +}; + +extern int ss_initialized; + +/* + * Read the configuration data from a policy database binary + * representation file into a policy database structure. + */ +int policydb_read(struct policydb *p, void *fp) +{ + struct role_allow *ra, *lra; + struct role_trans *tr, *ltr; + struct ocontext *l, *c, *newc; + struct genfs *genfs_p, *genfs, *newgenfs; + int i, j, rc; + u32 buf[8], len, len2, config, nprim, nel, nel2; + char *policydb_str; + struct policydb_compat_info *info; + struct range_trans *rt, *lrt; + + config = 0; + + rc = policydb_init(p); + if (rc) + goto out; + + /* Read the magic number and string length. */ + rc = next_entry(buf, fp, sizeof(u32)* 2); + if (rc < 0) + goto bad; + + for (i = 0; i < 2; i++) + buf[i] = le32_to_cpu(buf[i]); + + if (buf[0] != POLICYDB_MAGIC) { + printk(KERN_ERR "security: policydb magic number 0x%x does " + "not match expected magic number 0x%x\n", + buf[0], POLICYDB_MAGIC); + goto bad; + } + + len = buf[1]; + if (len != strlen(POLICYDB_STRING)) { + printk(KERN_ERR "security: policydb string length %d does not " + "match expected length %Zu\n", + len, strlen(POLICYDB_STRING)); + goto bad; + } + policydb_str = kmalloc(len + 1,GFP_KERNEL); + if (!policydb_str) { + printk(KERN_ERR "security: unable to allocate memory for policydb " + "string of length %d\n", len); + rc = -ENOMEM; + goto bad; + } + rc = next_entry(policydb_str, fp, len); + if (rc < 0) { + printk(KERN_ERR "security: truncated policydb string identifier\n"); + kfree(policydb_str); + goto bad; + } + policydb_str[len] = 0; + if (strcmp(policydb_str, POLICYDB_STRING)) { + printk(KERN_ERR "security: policydb string %s does not match " + "my string %s\n", policydb_str, POLICYDB_STRING); + kfree(policydb_str); + goto bad; + } + /* Done with policydb_str. */ + kfree(policydb_str); + policydb_str = NULL; + + /* Read the version, config, and table sizes. */ + rc = next_entry(buf, fp, sizeof(u32)*4); + if (rc < 0) + goto bad; + for (i = 0; i < 4; i++) + buf[i] = le32_to_cpu(buf[i]); + + p->policyvers = buf[0]; + if (p->policyvers < POLICYDB_VERSION_MIN || + p->policyvers > POLICYDB_VERSION_MAX) { + printk(KERN_ERR "security: policydb version %d does not match " + "my version range %d-%d\n", + buf[0], POLICYDB_VERSION_MIN, POLICYDB_VERSION_MAX); + goto bad; + } + + if ((buf[1] & POLICYDB_CONFIG_MLS)) { + if (ss_initialized && !selinux_mls_enabled) { + printk(KERN_ERR "Cannot switch between non-MLS and MLS " + "policies\n"); + goto bad; + } + selinux_mls_enabled = 1; + config |= POLICYDB_CONFIG_MLS; + + if (p->policyvers < POLICYDB_VERSION_MLS) { + printk(KERN_ERR "security policydb version %d (MLS) " + "not backwards compatible\n", p->policyvers); + goto bad; + } + } else { + if (ss_initialized && selinux_mls_enabled) { + printk(KERN_ERR "Cannot switch between MLS and non-MLS " + "policies\n"); + goto bad; + } + } + + info = policydb_lookup_compat(p->policyvers); + if (!info) { + printk(KERN_ERR "security: unable to find policy compat info " + "for version %d\n", p->policyvers); + goto bad; + } + + if (buf[2] != info->sym_num || buf[3] != info->ocon_num) { + printk(KERN_ERR "security: policydb table sizes (%d,%d) do " + "not match mine (%d,%d)\n", buf[2], 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); + if (rc < 0) + goto bad; + nprim = le32_to_cpu(buf[0]); + nel = le32_to_cpu(buf[1]); + for (j = 0; j < nel; j++) { + rc = read_f[i](p, p->symtab[i].table, fp); + if (rc) + goto bad; + } + + p->symtab[i].nprim = nprim; + } + + rc = avtab_read(&p->te_avtab, fp, config); + if (rc) + goto bad; + + if (p->policyvers >= POLICYDB_VERSION_BOOL) { + rc = cond_read_list(p, fp); + if (rc) + goto bad; + } + + rc = next_entry(buf, fp, sizeof(u32)); + if (rc < 0) + goto bad; + nel = le32_to_cpu(buf[0]); + ltr = NULL; + for (i = 0; i < nel; i++) { + tr = kmalloc(sizeof(*tr), GFP_KERNEL); + if (!tr) { + rc = -ENOMEM; + goto bad; + } + memset(tr, 0, sizeof(*tr)); + if (ltr) { + ltr->next = tr; + } else { + p->role_tr = tr; + } + rc = next_entry(buf, fp, sizeof(u32)*3); + if (rc < 0) + goto bad; + tr->role = le32_to_cpu(buf[0]); + tr->type = le32_to_cpu(buf[1]); + tr->new_role = le32_to_cpu(buf[2]); + ltr = tr; + } + + rc = next_entry(buf, fp, sizeof(u32)); + if (rc < 0) + goto bad; + nel = le32_to_cpu(buf[0]); + lra = NULL; + for (i = 0; i < nel; i++) { + ra = kmalloc(sizeof(*ra), GFP_KERNEL); + if (!ra) { + rc = -ENOMEM; + goto bad; + } + memset(ra, 0, sizeof(*ra)); + if (lra) { + lra->next = ra; + } else { + p->role_allow = ra; + } + rc = next_entry(buf, fp, sizeof(u32)*2); + if (rc < 0) + goto bad; + ra->role = le32_to_cpu(buf[0]); + ra->new_role = le32_to_cpu(buf[1]); + lra = ra; + } + + rc = policydb_index_classes(p); + if (rc) + goto bad; + + rc = policydb_index_others(p); + if (rc) + goto bad; + + for (i = 0; i < info->ocon_num; i++) { + rc = next_entry(buf, fp, sizeof(u32)); + if (rc < 0) + goto bad; + nel = le32_to_cpu(buf[0]); + l = NULL; + for (j = 0; j < nel; j++) { + c = kmalloc(sizeof(*c), GFP_KERNEL); + if (!c) { + rc = -ENOMEM; + goto bad; + } + memset(c, 0, sizeof(*c)); + if (l) { + l->next = c; + } else { + p->ocontexts[i] = c; + } + l = c; + rc = -EINVAL; + switch (i) { + case OCON_ISID: + rc = next_entry(buf, fp, sizeof(u32)); + if (rc < 0) + goto bad; + c->sid[0] = le32_to_cpu(buf[0]); + rc = context_read_and_validate(&c->context[0], p, fp); + if (rc) + goto bad; + break; + case OCON_FS: + case OCON_NETIF: + rc = next_entry(buf, fp, sizeof(u32)); + if (rc < 0) + goto bad; + len = le32_to_cpu(buf[0]); + c->u.name = kmalloc(len + 1,GFP_KERNEL); + if (!c->u.name) { + rc = -ENOMEM; + goto bad; + } + rc = next_entry(c->u.name, fp, len); + if (rc < 0) + goto bad; + c->u.name[len] = 0; + rc = context_read_and_validate(&c->context[0], p, fp); + if (rc) + goto bad; + rc = context_read_and_validate(&c->context[1], p, fp); + if (rc) + goto bad; + break; + case OCON_PORT: + rc = next_entry(buf, fp, sizeof(u32)*3); + if (rc < 0) + goto bad; + 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); + if (rc) + goto bad; + break; + case OCON_NODE: + rc = next_entry(buf, fp, sizeof(u32)* 2); + if (rc < 0) + goto bad; + c->u.node.addr = le32_to_cpu(buf[0]); + c->u.node.mask = le32_to_cpu(buf[1]); + rc = context_read_and_validate(&c->context[0], p, fp); + if (rc) + goto bad; + break; + case OCON_FSUSE: + rc = next_entry(buf, fp, sizeof(u32)*2); + if (rc < 0) + goto bad; + c->v.behavior = le32_to_cpu(buf[0]); + if (c->v.behavior > SECURITY_FS_USE_NONE) + goto bad; + len = le32_to_cpu(buf[1]); + c->u.name = kmalloc(len + 1,GFP_KERNEL); + if (!c->u.name) { + rc = -ENOMEM; + goto bad; + } + rc = next_entry(c->u.name, fp, len); + if (rc < 0) + goto bad; + c->u.name[len] = 0; + rc = context_read_and_validate(&c->context[0], p, fp); + if (rc) + goto bad; + break; + case OCON_NODE6: { + int k; + + rc = next_entry(buf, fp, sizeof(u32) * 8); + if (rc < 0) + goto bad; + for (k = 0; k < 4; k++) + c->u.node6.addr[k] = le32_to_cpu(buf[k]); + for (k = 0; k < 4; k++) + c->u.node6.mask[k] = le32_to_cpu(buf[k+4]); + if (context_read_and_validate(&c->context[0], p, fp)) + goto bad; + break; + } + } + } + } + + rc = next_entry(buf, fp, sizeof(u32)); + if (rc < 0) + goto bad; + nel = le32_to_cpu(buf[0]); + genfs_p = NULL; + rc = -EINVAL; + for (i = 0; i < nel; i++) { + rc = next_entry(buf, fp, sizeof(u32)); + if (rc < 0) + goto bad; + len = le32_to_cpu(buf[0]); + newgenfs = kmalloc(sizeof(*newgenfs), GFP_KERNEL); + if (!newgenfs) { + rc = -ENOMEM; + goto bad; + } + memset(newgenfs, 0, sizeof(*newgenfs)); + + newgenfs->fstype = kmalloc(len + 1,GFP_KERNEL); + if (!newgenfs->fstype) { + rc = -ENOMEM; + kfree(newgenfs); + goto bad; + } + rc = next_entry(newgenfs->fstype, fp, len); + if (rc < 0) { + kfree(newgenfs->fstype); + kfree(newgenfs); + goto bad; + } + newgenfs->fstype[len] = 0; + for (genfs_p = NULL, genfs = p->genfs; genfs; + genfs_p = genfs, genfs = genfs->next) { + if (strcmp(newgenfs->fstype, genfs->fstype) == 0) { + printk(KERN_ERR "security: dup genfs " + "fstype %s\n", newgenfs->fstype); + kfree(newgenfs->fstype); + kfree(newgenfs); + goto bad; + } + if (strcmp(newgenfs->fstype, genfs->fstype) < 0) + break; + } + newgenfs->next = genfs; + if (genfs_p) + genfs_p->next = newgenfs; + else + p->genfs = newgenfs; + rc = next_entry(buf, fp, sizeof(u32)); + if (rc < 0) + goto bad; + nel2 = le32_to_cpu(buf[0]); + for (j = 0; j < nel2; j++) { + rc = next_entry(buf, fp, sizeof(u32)); + if (rc < 0) + goto bad; + len = le32_to_cpu(buf[0]); + + newc = kmalloc(sizeof(*newc), GFP_KERNEL); + if (!newc) { + rc = -ENOMEM; + goto bad; + } + memset(newc, 0, sizeof(*newc)); + + newc->u.name = kmalloc(len + 1,GFP_KERNEL); + if (!newc->u.name) { + rc = -ENOMEM; + goto bad_newc; + } + rc = next_entry(newc->u.name, fp, len); + if (rc < 0) + goto bad_newc; + newc->u.name[len] = 0; + rc = next_entry(buf, fp, sizeof(u32)); + if (rc < 0) + goto bad_newc; + newc->v.sclass = le32_to_cpu(buf[0]); + if (context_read_and_validate(&newc->context[0], p, fp)) + goto bad_newc; + for (l = NULL, c = newgenfs->head; c; + l = c, c = c->next) { + if (!strcmp(newc->u.name, c->u.name) && + (!c->v.sclass || !newc->v.sclass || + newc->v.sclass == c->v.sclass)) { + printk(KERN_ERR "security: dup genfs " + "entry (%s,%s)\n", + newgenfs->fstype, c->u.name); + goto bad_newc; + } + len = strlen(newc->u.name); + len2 = strlen(c->u.name); + if (len > len2) + break; + } + + newc->next = c; + if (l) + l->next = newc; + else + newgenfs->head = newc; + } + } + + if (p->policyvers >= POLICYDB_VERSION_MLS) { + rc = next_entry(buf, fp, sizeof(u32)); + if (rc < 0) + goto bad; + nel = le32_to_cpu(buf[0]); + lrt = NULL; + for (i = 0; i < nel; i++) { + rt = kmalloc(sizeof(*rt), GFP_KERNEL); + if (!rt) { + rc = -ENOMEM; + goto bad; + } + memset(rt, 0, sizeof(*rt)); + if (lrt) + lrt->next = rt; + else + p->range_tr = rt; + rc = next_entry(buf, fp, (sizeof(u32) * 2)); + if (rc < 0) + goto bad; + rt->dom = le32_to_cpu(buf[0]); + rt->type = le32_to_cpu(buf[1]); + rc = mls_read_range_helper(&rt->range, fp); + if (rc) + goto bad; + lrt = rt; + } + } + + rc = 0; +out: + return rc; +bad_newc: + ocontext_destroy(newc,OCON_FSUSE); +bad: + if (!rc) + rc = -EINVAL; + policydb_destroy(p); + goto out; +} |