/* * Implementation of the multi-level security (MLS) policy. * * Author : Stephen Smalley, */ #include #include #include #include #include "mls.h" #include "policydb.h" #include "services.h" /* * Remove any permissions from `allowed' that are * denied by the MLS policy. */ void mls_compute_av(struct context *scontext, struct context *tcontext, struct class_datum *tclass, u32 *allowed) { unsigned int rel[2]; int l; for (l = 0; l < 2; l++) rel[l] = mls_level_relation(scontext->range.level[l], tcontext->range.level[l]); if (rel[1] != MLS_RELATION_EQ) { if (rel[1] != MLS_RELATION_DOM && !ebitmap_get_bit(&policydb.trustedreaders, scontext->type - 1) && !ebitmap_get_bit(&policydb.trustedobjects, tcontext->type - 1)) { /* read(s,t) = (s.high >= t.high) = False */ *allowed = (*allowed) & ~(tclass->mlsperms.read); } if (rel[1] != MLS_RELATION_DOMBY && !ebitmap_get_bit(&policydb.trustedreaders, tcontext->type - 1) && !ebitmap_get_bit(&policydb.trustedobjects, scontext->type - 1)) { /* readby(s,t) = read(t,s) = False */ *allowed = (*allowed) & ~(tclass->mlsperms.readby); } } if (((rel[0] != MLS_RELATION_DOMBY && rel[0] != MLS_RELATION_EQ) || ((!mls_level_eq(tcontext->range.level[0], tcontext->range.level[1])) && (rel[1] != MLS_RELATION_DOM && rel[1] != MLS_RELATION_EQ))) && !ebitmap_get_bit(&policydb.trustedwriters, scontext->type - 1) && !ebitmap_get_bit(&policydb.trustedobjects, tcontext->type - 1)) { /* * write(s,t) = ((s.low <= t.low = t.high) or (s.low * <= t.low <= t.high <= s.high)) = False */ *allowed = (*allowed) & ~(tclass->mlsperms.write); } if (((rel[0] != MLS_RELATION_DOM && rel[0] != MLS_RELATION_EQ) || ((!mls_level_eq(scontext->range.level[0], scontext->range.level[1])) && (rel[1] != MLS_RELATION_DOMBY && rel[1] != MLS_RELATION_EQ))) && !ebitmap_get_bit(&policydb.trustedwriters, tcontext->type - 1) && !ebitmap_get_bit(&policydb.trustedobjects, scontext->type - 1)) { /* writeby(s,t) = write(t,s) = False */ *allowed = (*allowed) & ~(tclass->mlsperms.writeby); } } /* * Return the length in bytes for the MLS fields of the * security context string representation of `context'. */ int mls_compute_context_len(struct context * context) { int i, l, len; len = 0; for (l = 0; l < 2; l++) { len += strlen(policydb.p_sens_val_to_name[context->range.level[l].sens - 1]) + 1; for (i = 1; i <= ebitmap_length(&context->range.level[l].cat); i++) if (ebitmap_get_bit(&context->range.level[l].cat, i - 1)) len += strlen(policydb.p_cat_val_to_name[i - 1]) + 1; if (mls_level_relation(context->range.level[0], context->range.level[1]) == MLS_RELATION_EQ) break; } return len; } /* * Write the security context string representation of * the MLS fields of `context' into the string `*scontext'. * Update `*scontext' to point to the end of the MLS fields. */ int mls_sid_to_context(struct context *context, char **scontext) { char *scontextp; int i, l; scontextp = *scontext; for (l = 0; l < 2; l++) { strcpy(scontextp, policydb.p_sens_val_to_name[context->range.level[l].sens - 1]); scontextp += strlen(policydb.p_sens_val_to_name[context->range.level[l].sens - 1]); *scontextp = ':'; scontextp++; for (i = 1; i <= ebitmap_length(&context->range.level[l].cat); i++) if (ebitmap_get_bit(&context->range.level[l].cat, i - 1)) { strcpy(scontextp, policydb.p_cat_val_to_name[i - 1]); scontextp += strlen(policydb.p_cat_val_to_name[i - 1]); *scontextp = ','; scontextp++; } if (mls_level_relation(context->range.level[0], context->range.level[1]) != MLS_RELATION_EQ) { scontextp--; sprintf(scontextp, "-"); scontextp++; } else { break; } } *scontext = scontextp; return 0; } /* * Return 1 if the MLS fields in the security context * structure `c' are valid. Return 0 otherwise. */ int mls_context_isvalid(struct policydb *p, struct context *c) { unsigned int relation; struct level_datum *levdatum; struct user_datum *usrdatum; struct mls_range_list *rnode; int i, l; /* * MLS range validity checks: high must dominate low, low level must * be valid (category set <-> sensitivity check), and high level must * be valid (category set <-> sensitivity check) */ relation = mls_level_relation(c->range.level[1], c->range.level[0]); if (!(relation & (MLS_RELATION_DOM | MLS_RELATION_EQ))) /* High does not dominate low. */ return 0; for (l = 0; l < 2; l++) { if (!c->range.level[l].sens || c->range.level[l].sens > p->p_levels.nprim) return 0; levdatum = hashtab_search(p->p_levels.table, p->p_sens_val_to_name[c->range.level[l].sens - 1]); if (!levdatum) return 0; for (i = 1; i <= ebitmap_length(&c->range.level[l].cat); i++) { if (ebitmap_get_bit(&c->range.level[l].cat, i - 1)) { if (i > p->p_cats.nprim) return 0; if (!ebitmap_get_bit(&levdatum->level->cat, i - 1)) /* * Category may not be associated with * sensitivity in low level. */ return 0; } } } if (c->role == OBJECT_R_VAL) return 1; /* * User must be authorized for the MLS range. */ if (!c->user || c->user > p->p_users.nprim) return 0; usrdatum = p->user_val_to_struct[c->user - 1]; for (rnode = usrdatum->ranges; rnode; rnode = rnode->next) { if (mls_range_contains(rnode->range, c->range)) break; } if (!rnode) /* user may not be associated with range */ return 0; return 1; } /* * Set the MLS fields in the security context structure * `context' based on the string representation in * the string `*scontext'. Update `*scontext' to * point to the end of the string representation of * the MLS fields. * * This function modifies the string in place, inserting * NULL characters to terminate the MLS fields. */ int mls_context_to_sid(char oldc, char **scontext, struct context *context) { char delim; char *scontextp, *p; struct level_datum *levdatum; struct cat_datum *catdatum; int l, rc = -EINVAL; if (!oldc) { /* No MLS component to the security context. Try to use a default 'unclassified' value. */ levdatum = hashtab_search(policydb.p_levels.table, "unclassified"); if (!levdatum) goto out; context->range.level[0].sens = levdatum->level->sens; context->range.level[1].sens = context->range.level[0].sens; rc = 0; goto out; } /* Extract low sensitivity. */ scontextp = p = *scontext; while (*p && *p != ':' && *p != '-') p++; delim = *p; if (delim != 0) *p++ = 0; for (l = 0; l < 2; l++) { levdatum = hashtab_search(policydb.p_levels.table, scontextp); if (!levdatum) goto out; context->range.level[l].sens = levdatum->level->sens; if (delim == ':') { /* Extract low category set. */ while (1) { scontextp = p; while (*p && *p != ',' && *p != '-') p++; delim = *p; if (delim != 0) *p++ = 0; catdatum = hashtab_search(policydb.p_cats.table, scontextp); if (!catdatum) goto out; rc = ebitmap_set_bit(&context->range.level[l].cat, catdatum->value - 1, 1); if (rc) goto out; if (delim != ',') break; } } if (delim == '-') { /* Extract high sensitivity. */ scontextp = p; while (*p && *p != ':') p++; delim = *p; if (delim != 0) *p++ = 0; } else break; } if (l == 0) { context->range.level[1].sens = context->range.level[0].sens; rc = ebitmap_cpy(&context->range.level[1].cat, &context->range.level[0].cat); if (rc) goto out; } *scontext = p; rc = 0; out: return rc; } /* * Copies the MLS range from `src' into `dst'. */ static inline int mls_copy_context(struct context *dst, struct context *src) { int l, rc = 0; /* Copy the MLS range from the source context */ for (l = 0; l < 2; l++) { dst->range.level[l].sens = src->range.level[l].sens; rc = ebitmap_cpy(&dst->range.level[l].cat, &src->range.level[l].cat); if (rc) break; } return rc; } /* * Convert the MLS fields in the security context * structure `c' from the values specified in the * policy `oldp' to the values specified in the policy `newp'. */ int mls_convert_context(struct policydb *oldp, struct policydb *newp, struct context *c) { struct level_datum *levdatum; struct cat_datum *catdatum; struct ebitmap bitmap; int l, i; for (l = 0; l < 2; l++) { levdatum = hashtab_search(newp->p_levels.table, oldp->p_sens_val_to_name[c->range.level[l].sens - 1]); if (!levdatum) return -EINVAL; c->range.level[l].sens = levdatum->level->sens; ebitmap_init(&bitmap); for (i = 1; i <= ebitmap_length(&c->range.level[l].cat); i++) { if (ebitmap_get_bit(&c->range.level[l].cat, i - 1)) { int rc; catdatum = hashtab_search(newp->p_cats.table, oldp->p_cat_val_to_name[i - 1]); if (!catdatum) return -EINVAL; rc = ebitmap_set_bit(&bitmap, catdatum->value - 1, 1); if (rc) return rc; } } ebitmap_destroy(&c->range.level[l].cat); c->range.level[l].cat = bitmap; } return 0; } int mls_compute_sid(struct context *scontext, struct context *tcontext, u16 tclass, u32 specified, struct context *newcontext) { switch (specified) { case AVTAB_TRANSITION: case AVTAB_CHANGE: /* Use the process MLS attributes. */ return mls_copy_context(newcontext, scontext); case AVTAB_MEMBER: /* Only polyinstantiate the MLS attributes if the type is being polyinstantiated */ if (newcontext->type != tcontext->type) { /* Use the process MLS attributes. */ return mls_copy_context(newcontext, scontext); } else { /* Use the related object MLS attributes. */ return mls_copy_context(newcontext, tcontext); } default: return -EINVAL; } return -EINVAL; } void mls_user_destroy(struct user_datum *usrdatum) { struct mls_range_list *rnode, *rtmp; rnode = usrdatum->ranges; while (rnode) { rtmp = rnode; rnode = rnode->next; ebitmap_destroy(&rtmp->range.level[0].cat); ebitmap_destroy(&rtmp->range.level[1].cat); kfree(rtmp); } } int mls_read_perm(struct perm_datum *perdatum, void *fp) { u32 *buf; buf = next_entry(fp, sizeof(u32)); if (!buf) return -EINVAL; perdatum->base_perms = le32_to_cpu(buf[0]); return 0; } /* * Read a MLS level structure from a policydb binary * representation file. */ struct mls_level *mls_read_level(void *fp) { struct mls_level *l; u32 *buf; l = kmalloc(sizeof(*l), GFP_ATOMIC); if (!l) { printk(KERN_ERR "security: mls: out of memory\n"); return NULL; } memset(l, 0, sizeof(*l)); buf = next_entry(fp, sizeof(u32)); if (!buf) { printk(KERN_ERR "security: mls: truncated level\n"); goto bad; } l->sens = cpu_to_le32(buf[0]); if (ebitmap_read(&l->cat, fp)) { printk(KERN_ERR "security: mls: error reading level " "categories\n"); goto bad; } return l; bad: kfree(l); return NULL; } /* * 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; int items, rc = -EINVAL; buf = next_entry(fp, sizeof(u32)); if (!buf) goto out; items = le32_to_cpu(buf[0]); buf = next_entry(fp, sizeof(u32) * items); if (!buf) { 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; } int mls_read_range(struct context *c, void *fp) { return mls_read_range_helper(&c->range, fp); } /* * Read a MLS perms structure from a policydb binary * representation file. */ int mls_read_class(struct class_datum *cladatum, void *fp) { struct mls_perms *p = &cladatum->mlsperms; u32 *buf; buf = next_entry(fp, sizeof(u32)*4); if (!buf) { printk(KERN_ERR "security: mls: truncated mls permissions\n"); return -EINVAL; } p->read = le32_to_cpu(buf[0]); p->readby = le32_to_cpu(buf[1]); p->write = le32_to_cpu(buf[2]); p->writeby = le32_to_cpu(buf[3]); return 0; } int mls_read_user(struct user_datum *usrdatum, void *fp) { struct mls_range_list *r, *l; int rc = 0; u32 nel, i; u32 *buf; buf = next_entry(fp, sizeof(u32)); if (!buf) { rc = -EINVAL; goto out; } nel = le32_to_cpu(buf[0]); l = NULL; for (i = 0; i < nel; i++) { r = kmalloc(sizeof(*r), GFP_ATOMIC); if (!r) { rc = -ENOMEM; goto out; } memset(r, 0, sizeof(*r)); rc = mls_read_range_helper(&r->range, fp); if (rc) { kfree(r); goto out; } if (l) l->next = r; else usrdatum->ranges = r; l = r; } out: return rc; } int mls_read_nlevels(struct policydb *p, void *fp) { u32 *buf; buf = next_entry(fp, sizeof(u32)); if (!buf) return -EINVAL; p->nlevels = le32_to_cpu(buf[0]); return 0; } int mls_read_trusted(struct policydb *p, void *fp) { int rc = 0; rc = ebitmap_read(&p->trustedreaders, fp); if (rc) goto out; rc = ebitmap_read(&p->trustedwriters, fp); if (rc) goto bad; rc = ebitmap_read(&p->trustedobjects, fp); if (rc) goto bad2; out: return rc; bad2: ebitmap_destroy(&p->trustedwriters); bad: ebitmap_destroy(&p->trustedreaders); goto out; } int sens_index(void *key, void *datum, void *datap) { struct policydb *p; struct level_datum *levdatum; levdatum = datum; p = datap; if (!levdatum->isalias) p->p_sens_val_to_name[levdatum->level->sens - 1] = key; return 0; } int cat_index(void *key, void *datum, void *datap) { struct policydb *p; struct cat_datum *catdatum; catdatum = datum; p = datap; if (!catdatum->isalias) p->p_cat_val_to_name[catdatum->value - 1] = key; return 0; } int sens_destroy(void *key, void *datum, void *p) { struct level_datum *levdatum; kfree(key); levdatum = datum; if (!levdatum->isalias) { ebitmap_destroy(&levdatum->level->cat); kfree(levdatum->level); } kfree(datum); return 0; } int cat_destroy(void *key, void *datum, void *p) { kfree(key); kfree(datum); return 0; } int sens_read(struct policydb *p, struct hashtab *h, void *fp) { char *key = 0; struct level_datum *levdatum; int rc; u32 *buf, len; levdatum = kmalloc(sizeof(*levdatum), GFP_ATOMIC); if (!levdatum) { rc = -ENOMEM; goto out; } memset(levdatum, 0, sizeof(*levdatum)); buf = next_entry(fp, sizeof(u32)*2); if (!buf) { rc = -EINVAL; goto bad; } len = le32_to_cpu(buf[0]); levdatum->isalias = le32_to_cpu(buf[1]); buf = next_entry(fp, len); if (!buf) { rc = -EINVAL; goto bad; } key = kmalloc(len + 1,GFP_ATOMIC); if (!key) { rc = -ENOMEM; goto bad; } memcpy(key, buf, len); key[len] = 0; levdatum->level = mls_read_level(fp); if (!levdatum->level) { 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; } int cat_read(struct policydb *p, struct hashtab *h, void *fp) { char *key = 0; struct cat_datum *catdatum; int rc; u32 *buf, len; catdatum = kmalloc(sizeof(*catdatum), GFP_ATOMIC); if (!catdatum) { rc = -ENOMEM; goto out; } memset(catdatum, 0, sizeof(*catdatum)); buf = next_entry(fp, sizeof(u32)*3); if (!buf) { rc = -EINVAL; goto bad; } len = le32_to_cpu(buf[0]); catdatum->value = le32_to_cpu(buf[1]); catdatum->isalias = le32_to_cpu(buf[2]); buf = next_entry(fp, len); if (!buf) { rc = -EINVAL; goto bad; } key = kmalloc(len + 1,GFP_ATOMIC); if (!key) { rc = -ENOMEM; goto bad; } memcpy(key, buf, len); key[len] = 0; rc = hashtab_insert(h, key, catdatum); if (rc) goto bad; out: return rc; bad: cat_destroy(key, catdatum, NULL); goto out; }