Path: blob/master/security/selinux/ss/conditional.c
10817 views
/* Authors: Karl MacMillan <[email protected]>1* Frank Mayer <[email protected]>2*3* Copyright (C) 2003 - 2004 Tresys Technology, LLC4* This program is free software; you can redistribute it and/or modify5* it under the terms of the GNU General Public License as published by6* the Free Software Foundation, version 2.7*/89#include <linux/kernel.h>10#include <linux/errno.h>11#include <linux/string.h>12#include <linux/spinlock.h>13#include <linux/slab.h>1415#include "security.h"16#include "conditional.h"1718/*19* cond_evaluate_expr evaluates a conditional expr20* in reverse polish notation. It returns true (1), false (0),21* or undefined (-1). Undefined occurs when the expression22* exceeds the stack depth of COND_EXPR_MAXDEPTH.23*/24static int cond_evaluate_expr(struct policydb *p, struct cond_expr *expr)25{2627struct cond_expr *cur;28int s[COND_EXPR_MAXDEPTH];29int sp = -1;3031for (cur = expr; cur; cur = cur->next) {32switch (cur->expr_type) {33case COND_BOOL:34if (sp == (COND_EXPR_MAXDEPTH - 1))35return -1;36sp++;37s[sp] = p->bool_val_to_struct[cur->bool - 1]->state;38break;39case COND_NOT:40if (sp < 0)41return -1;42s[sp] = !s[sp];43break;44case COND_OR:45if (sp < 1)46return -1;47sp--;48s[sp] |= s[sp + 1];49break;50case COND_AND:51if (sp < 1)52return -1;53sp--;54s[sp] &= s[sp + 1];55break;56case COND_XOR:57if (sp < 1)58return -1;59sp--;60s[sp] ^= s[sp + 1];61break;62case COND_EQ:63if (sp < 1)64return -1;65sp--;66s[sp] = (s[sp] == s[sp + 1]);67break;68case COND_NEQ:69if (sp < 1)70return -1;71sp--;72s[sp] = (s[sp] != s[sp + 1]);73break;74default:75return -1;76}77}78return s[0];79}8081/*82* evaluate_cond_node evaluates the conditional stored in83* a struct cond_node and if the result is different than the84* current state of the node it sets the rules in the true/false85* list appropriately. If the result of the expression is undefined86* all of the rules are disabled for safety.87*/88int evaluate_cond_node(struct policydb *p, struct cond_node *node)89{90int new_state;91struct cond_av_list *cur;9293new_state = cond_evaluate_expr(p, node->expr);94if (new_state != node->cur_state) {95node->cur_state = new_state;96if (new_state == -1)97printk(KERN_ERR "SELinux: expression result was undefined - disabling all rules.\n");98/* turn the rules on or off */99for (cur = node->true_list; cur; cur = cur->next) {100if (new_state <= 0)101cur->node->key.specified &= ~AVTAB_ENABLED;102else103cur->node->key.specified |= AVTAB_ENABLED;104}105106for (cur = node->false_list; cur; cur = cur->next) {107/* -1 or 1 */108if (new_state)109cur->node->key.specified &= ~AVTAB_ENABLED;110else111cur->node->key.specified |= AVTAB_ENABLED;112}113}114return 0;115}116117int cond_policydb_init(struct policydb *p)118{119int rc;120121p->bool_val_to_struct = NULL;122p->cond_list = NULL;123124rc = avtab_init(&p->te_cond_avtab);125if (rc)126return rc;127128return 0;129}130131static void cond_av_list_destroy(struct cond_av_list *list)132{133struct cond_av_list *cur, *next;134for (cur = list; cur; cur = next) {135next = cur->next;136/* the avtab_ptr_t node is destroy by the avtab */137kfree(cur);138}139}140141static void cond_node_destroy(struct cond_node *node)142{143struct cond_expr *cur_expr, *next_expr;144145for (cur_expr = node->expr; cur_expr; cur_expr = next_expr) {146next_expr = cur_expr->next;147kfree(cur_expr);148}149cond_av_list_destroy(node->true_list);150cond_av_list_destroy(node->false_list);151kfree(node);152}153154static void cond_list_destroy(struct cond_node *list)155{156struct cond_node *next, *cur;157158if (list == NULL)159return;160161for (cur = list; cur; cur = next) {162next = cur->next;163cond_node_destroy(cur);164}165}166167void cond_policydb_destroy(struct policydb *p)168{169kfree(p->bool_val_to_struct);170avtab_destroy(&p->te_cond_avtab);171cond_list_destroy(p->cond_list);172}173174int cond_init_bool_indexes(struct policydb *p)175{176kfree(p->bool_val_to_struct);177p->bool_val_to_struct = (struct cond_bool_datum **)178kmalloc(p->p_bools.nprim * sizeof(struct cond_bool_datum *), GFP_KERNEL);179if (!p->bool_val_to_struct)180return -ENOMEM;181return 0;182}183184int cond_destroy_bool(void *key, void *datum, void *p)185{186kfree(key);187kfree(datum);188return 0;189}190191int cond_index_bool(void *key, void *datum, void *datap)192{193struct policydb *p;194struct cond_bool_datum *booldatum;195struct flex_array *fa;196197booldatum = datum;198p = datap;199200if (!booldatum->value || booldatum->value > p->p_bools.nprim)201return -EINVAL;202203fa = p->sym_val_to_name[SYM_BOOLS];204if (flex_array_put_ptr(fa, booldatum->value - 1, key,205GFP_KERNEL | __GFP_ZERO))206BUG();207p->bool_val_to_struct[booldatum->value - 1] = booldatum;208209return 0;210}211212static int bool_isvalid(struct cond_bool_datum *b)213{214if (!(b->state == 0 || b->state == 1))215return 0;216return 1;217}218219int cond_read_bool(struct policydb *p, struct hashtab *h, void *fp)220{221char *key = NULL;222struct cond_bool_datum *booldatum;223__le32 buf[3];224u32 len;225int rc;226227booldatum = kzalloc(sizeof(struct cond_bool_datum), GFP_KERNEL);228if (!booldatum)229return -ENOMEM;230231rc = next_entry(buf, fp, sizeof buf);232if (rc)233goto err;234235booldatum->value = le32_to_cpu(buf[0]);236booldatum->state = le32_to_cpu(buf[1]);237238rc = -EINVAL;239if (!bool_isvalid(booldatum))240goto err;241242len = le32_to_cpu(buf[2]);243244rc = -ENOMEM;245key = kmalloc(len + 1, GFP_KERNEL);246if (!key)247goto err;248rc = next_entry(key, fp, len);249if (rc)250goto err;251key[len] = '\0';252rc = hashtab_insert(h, key, booldatum);253if (rc)254goto err;255256return 0;257err:258cond_destroy_bool(key, booldatum, NULL);259return rc;260}261262struct cond_insertf_data {263struct policydb *p;264struct cond_av_list *other;265struct cond_av_list *head;266struct cond_av_list *tail;267};268269static int cond_insertf(struct avtab *a, struct avtab_key *k, struct avtab_datum *d, void *ptr)270{271struct cond_insertf_data *data = ptr;272struct policydb *p = data->p;273struct cond_av_list *other = data->other, *list, *cur;274struct avtab_node *node_ptr;275u8 found;276int rc = -EINVAL;277278/*279* For type rules we have to make certain there aren't any280* conflicting rules by searching the te_avtab and the281* cond_te_avtab.282*/283if (k->specified & AVTAB_TYPE) {284if (avtab_search(&p->te_avtab, k)) {285printk(KERN_ERR "SELinux: type rule already exists outside of a conditional.\n");286goto err;287}288/*289* If we are reading the false list other will be a pointer to290* the true list. We can have duplicate entries if there is only291* 1 other entry and it is in our true list.292*293* If we are reading the true list (other == NULL) there shouldn't294* be any other entries.295*/296if (other) {297node_ptr = avtab_search_node(&p->te_cond_avtab, k);298if (node_ptr) {299if (avtab_search_node_next(node_ptr, k->specified)) {300printk(KERN_ERR "SELinux: too many conflicting type rules.\n");301goto err;302}303found = 0;304for (cur = other; cur; cur = cur->next) {305if (cur->node == node_ptr) {306found = 1;307break;308}309}310if (!found) {311printk(KERN_ERR "SELinux: conflicting type rules.\n");312goto err;313}314}315} else {316if (avtab_search(&p->te_cond_avtab, k)) {317printk(KERN_ERR "SELinux: conflicting type rules when adding type rule for true.\n");318goto err;319}320}321}322323node_ptr = avtab_insert_nonunique(&p->te_cond_avtab, k, d);324if (!node_ptr) {325printk(KERN_ERR "SELinux: could not insert rule.\n");326rc = -ENOMEM;327goto err;328}329330list = kzalloc(sizeof(struct cond_av_list), GFP_KERNEL);331if (!list) {332rc = -ENOMEM;333goto err;334}335336list->node = node_ptr;337if (!data->head)338data->head = list;339else340data->tail->next = list;341data->tail = list;342return 0;343344err:345cond_av_list_destroy(data->head);346data->head = NULL;347return rc;348}349350static int cond_read_av_list(struct policydb *p, void *fp, struct cond_av_list **ret_list, struct cond_av_list *other)351{352int i, rc;353__le32 buf[1];354u32 len;355struct cond_insertf_data data;356357*ret_list = NULL;358359len = 0;360rc = next_entry(buf, fp, sizeof(u32));361if (rc)362return rc;363364len = le32_to_cpu(buf[0]);365if (len == 0)366return 0;367368data.p = p;369data.other = other;370data.head = NULL;371data.tail = NULL;372for (i = 0; i < len; i++) {373rc = avtab_read_item(&p->te_cond_avtab, fp, p, cond_insertf,374&data);375if (rc)376return rc;377}378379*ret_list = data.head;380return 0;381}382383static int expr_isvalid(struct policydb *p, struct cond_expr *expr)384{385if (expr->expr_type <= 0 || expr->expr_type > COND_LAST) {386printk(KERN_ERR "SELinux: conditional expressions uses unknown operator.\n");387return 0;388}389390if (expr->bool > p->p_bools.nprim) {391printk(KERN_ERR "SELinux: conditional expressions uses unknown bool.\n");392return 0;393}394return 1;395}396397static int cond_read_node(struct policydb *p, struct cond_node *node, void *fp)398{399__le32 buf[2];400u32 len, i;401int rc;402struct cond_expr *expr = NULL, *last = NULL;403404rc = next_entry(buf, fp, sizeof(u32));405if (rc)406return rc;407408node->cur_state = le32_to_cpu(buf[0]);409410len = 0;411rc = next_entry(buf, fp, sizeof(u32));412if (rc)413return rc;414415/* expr */416len = le32_to_cpu(buf[0]);417418for (i = 0; i < len; i++) {419rc = next_entry(buf, fp, sizeof(u32) * 2);420if (rc)421goto err;422423rc = -ENOMEM;424expr = kzalloc(sizeof(struct cond_expr), GFP_KERNEL);425if (!expr)426goto err;427428expr->expr_type = le32_to_cpu(buf[0]);429expr->bool = le32_to_cpu(buf[1]);430431if (!expr_isvalid(p, expr)) {432rc = -EINVAL;433kfree(expr);434goto err;435}436437if (i == 0)438node->expr = expr;439else440last->next = expr;441last = expr;442}443444rc = cond_read_av_list(p, fp, &node->true_list, NULL);445if (rc)446goto err;447rc = cond_read_av_list(p, fp, &node->false_list, node->true_list);448if (rc)449goto err;450return 0;451err:452cond_node_destroy(node);453return rc;454}455456int cond_read_list(struct policydb *p, void *fp)457{458struct cond_node *node, *last = NULL;459__le32 buf[1];460u32 i, len;461int rc;462463rc = next_entry(buf, fp, sizeof buf);464if (rc)465return rc;466467len = le32_to_cpu(buf[0]);468469rc = avtab_alloc(&(p->te_cond_avtab), p->te_avtab.nel);470if (rc)471goto err;472473for (i = 0; i < len; i++) {474rc = -ENOMEM;475node = kzalloc(sizeof(struct cond_node), GFP_KERNEL);476if (!node)477goto err;478479rc = cond_read_node(p, node, fp);480if (rc)481goto err;482483if (i == 0)484p->cond_list = node;485else486last->next = node;487last = node;488}489return 0;490err:491cond_list_destroy(p->cond_list);492p->cond_list = NULL;493return rc;494}495496int cond_write_bool(void *vkey, void *datum, void *ptr)497{498char *key = vkey;499struct cond_bool_datum *booldatum = datum;500struct policy_data *pd = ptr;501void *fp = pd->fp;502__le32 buf[3];503u32 len;504int rc;505506len = strlen(key);507buf[0] = cpu_to_le32(booldatum->value);508buf[1] = cpu_to_le32(booldatum->state);509buf[2] = cpu_to_le32(len);510rc = put_entry(buf, sizeof(u32), 3, fp);511if (rc)512return rc;513rc = put_entry(key, 1, len, fp);514if (rc)515return rc;516return 0;517}518519/*520* cond_write_cond_av_list doesn't write out the av_list nodes.521* Instead it writes out the key/value pairs from the avtab. This522* is necessary because there is no way to uniquely identifying rules523* in the avtab so it is not possible to associate individual rules524* in the avtab with a conditional without saving them as part of525* the conditional. This means that the avtab with the conditional526* rules will not be saved but will be rebuilt on policy load.527*/528static int cond_write_av_list(struct policydb *p,529struct cond_av_list *list, struct policy_file *fp)530{531__le32 buf[1];532struct cond_av_list *cur_list;533u32 len;534int rc;535536len = 0;537for (cur_list = list; cur_list != NULL; cur_list = cur_list->next)538len++;539540buf[0] = cpu_to_le32(len);541rc = put_entry(buf, sizeof(u32), 1, fp);542if (rc)543return rc;544545if (len == 0)546return 0;547548for (cur_list = list; cur_list != NULL; cur_list = cur_list->next) {549rc = avtab_write_item(p, cur_list->node, fp);550if (rc)551return rc;552}553554return 0;555}556557int cond_write_node(struct policydb *p, struct cond_node *node,558struct policy_file *fp)559{560struct cond_expr *cur_expr;561__le32 buf[2];562int rc;563u32 len = 0;564565buf[0] = cpu_to_le32(node->cur_state);566rc = put_entry(buf, sizeof(u32), 1, fp);567if (rc)568return rc;569570for (cur_expr = node->expr; cur_expr != NULL; cur_expr = cur_expr->next)571len++;572573buf[0] = cpu_to_le32(len);574rc = put_entry(buf, sizeof(u32), 1, fp);575if (rc)576return rc;577578for (cur_expr = node->expr; cur_expr != NULL; cur_expr = cur_expr->next) {579buf[0] = cpu_to_le32(cur_expr->expr_type);580buf[1] = cpu_to_le32(cur_expr->bool);581rc = put_entry(buf, sizeof(u32), 2, fp);582if (rc)583return rc;584}585586rc = cond_write_av_list(p, node->true_list, fp);587if (rc)588return rc;589rc = cond_write_av_list(p, node->false_list, fp);590if (rc)591return rc;592593return 0;594}595596int cond_write_list(struct policydb *p, struct cond_node *list, void *fp)597{598struct cond_node *cur;599u32 len;600__le32 buf[1];601int rc;602603len = 0;604for (cur = list; cur != NULL; cur = cur->next)605len++;606buf[0] = cpu_to_le32(len);607rc = put_entry(buf, sizeof(u32), 1, fp);608if (rc)609return rc;610611for (cur = list; cur != NULL; cur = cur->next) {612rc = cond_write_node(p, cur, fp);613if (rc)614return rc;615}616617return 0;618}619/* Determine whether additional permissions are granted by the conditional620* av table, and if so, add them to the result621*/622void cond_compute_av(struct avtab *ctab, struct avtab_key *key, struct av_decision *avd)623{624struct avtab_node *node;625626if (!ctab || !key || !avd)627return;628629for (node = avtab_search_node(ctab, key); node;630node = avtab_search_node_next(node, key->specified)) {631if ((u16)(AVTAB_ALLOWED|AVTAB_ENABLED) ==632(node->key.specified & (AVTAB_ALLOWED|AVTAB_ENABLED)))633avd->allowed |= node->datum.data;634if ((u16)(AVTAB_AUDITDENY|AVTAB_ENABLED) ==635(node->key.specified & (AVTAB_AUDITDENY|AVTAB_ENABLED)))636/* Since a '0' in an auditdeny mask represents a637* permission we do NOT want to audit (dontaudit), we use638* the '&' operand to ensure that all '0's in the mask639* are retained (much unlike the allow and auditallow cases).640*/641avd->auditdeny &= node->datum.data;642if ((u16)(AVTAB_AUDITALLOW|AVTAB_ENABLED) ==643(node->key.specified & (AVTAB_AUDITALLOW|AVTAB_ENABLED)))644avd->auditallow |= node->datum.data;645}646return;647}648649650