selinux: return -ENOMEM when memory allocation fails
[linux-flexiantxendom0-natty.git] / security / selinux / ss / conditional.c
index da0566c..a533732 100644 (file)
@@ -1,9 +1,9 @@
 /* Authors: Karl MacMillan <kmacmillan@tresys.com>
- *          Frank Mayer <mayerf@tresys.com>
+ *         Frank Mayer <mayerf@tresys.com>
  *
  * 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
+ *     it under the terms of the GNU General Public License as published by
  *     the Free Software Foundation, version 2.
  */
 
@@ -29,7 +29,7 @@ static int cond_evaluate_expr(struct policydb *p, struct cond_expr *expr)
        int s[COND_EXPR_MAXDEPTH];
        int sp = -1;
 
-       for (cur = expr; cur != NULL; cur = cur->next) {
+       for (cur = expr; cur; cur = cur->next) {
                switch (cur->expr_type) {
                case COND_BOOL:
                        if (sp == (COND_EXPR_MAXDEPTH - 1))
@@ -89,7 +89,7 @@ static int cond_evaluate_expr(struct policydb *p, struct cond_expr *expr)
 int evaluate_cond_node(struct policydb *p, struct cond_node *node)
 {
        int new_state;
-       struct cond_av_list* cur;
+       struct cond_av_list *cur;
 
        new_state = cond_evaluate_expr(p, node->expr);
        if (new_state != node->cur_state) {
@@ -97,21 +97,19 @@ int evaluate_cond_node(struct policydb *p, struct cond_node *node)
                if (new_state == -1)
                        printk(KERN_ERR "SELinux: expression result was undefined - disabling all rules.\n");
                /* turn the rules on or off */
-               for (cur = node->true_list; cur != NULL; cur = cur->next) {
-                       if (new_state <= 0) {
+               for (cur = node->true_list; cur; cur = cur->next) {
+                       if (new_state <= 0)
                                cur->node->key.specified &= ~AVTAB_ENABLED;
-                       } else {
+                       else
                                cur->node->key.specified |= AVTAB_ENABLED;
-                       }
                }
 
-               for (cur = node->false_list; cur != NULL; cur = cur->next) {
+               for (cur = node->false_list; cur; cur = cur->next) {
                        /* -1 or 1 */
-                       if (new_state) {
+                       if (new_state)
                                cur->node->key.specified &= ~AVTAB_ENABLED;
-                       } else {
+                       else
                                cur->node->key.specified |= AVTAB_ENABLED;
-                       }
                }
        }
        return 0;
@@ -119,10 +117,14 @@ int evaluate_cond_node(struct policydb *p, struct cond_node *node)
 
 int cond_policydb_init(struct policydb *p)
 {
+       int rc;
+
        p->bool_val_to_struct = NULL;
        p->cond_list = NULL;
-       if (avtab_init(&p->te_cond_avtab))
-               return -1;
+
+       rc = avtab_init(&p->te_cond_avtab);
+       if (rc)
+               return rc;
 
        return 0;
 }
@@ -130,7 +132,7 @@ int cond_policydb_init(struct policydb *p)
 static void cond_av_list_destroy(struct cond_av_list *list)
 {
        struct cond_av_list *cur, *next;
-       for (cur = list; cur != NULL; cur = next) {
+       for (cur = list; cur; cur = next) {
                next = cur->next;
                /* the avtab_ptr_t node is destroy by the avtab */
                kfree(cur);
@@ -141,7 +143,7 @@ static void cond_node_destroy(struct cond_node *node)
 {
        struct cond_expr *cur_expr, *next_expr;
 
-       for (cur_expr = node->expr; cur_expr != NULL; cur_expr = next_expr) {
+       for (cur_expr = node->expr; cur_expr; cur_expr = next_expr) {
                next_expr = cur_expr->next;
                kfree(cur_expr);
        }
@@ -157,7 +159,7 @@ static void cond_list_destroy(struct cond_node *list)
        if (list == NULL)
                return;
 
-       for (cur = list; cur != NULL; cur = next) {
+       for (cur = list; cur; cur = next) {
                next = cur->next;
                cond_node_destroy(cur);
        }
@@ -173,10 +175,10 @@ void cond_policydb_destroy(struct policydb *p)
 int cond_init_bool_indexes(struct policydb *p)
 {
        kfree(p->bool_val_to_struct);
-       p->bool_val_to_struct = (struct cond_bool_datum**)
-               kmalloc(p->p_bools.nprim * sizeof(struct cond_bool_datum*), GFP_KERNEL);
+       p->bool_val_to_struct = (struct cond_bool_datum **)
+               kmalloc(p->p_bools.nprim * sizeof(struct cond_bool_datum *), GFP_KERNEL);
        if (!p->bool_val_to_struct)
-               return -1;
+               return -ENOMEM;
        return 0;
 }
 
@@ -191,6 +193,7 @@ int cond_index_bool(void *key, void *datum, void *datap)
 {
        struct policydb *p;
        struct cond_bool_datum *booldatum;
+       struct flex_array *fa;
 
        booldatum = datum;
        p = datap;
@@ -198,8 +201,11 @@ int cond_index_bool(void *key, void *datum, void *datap)
        if (!booldatum->value || booldatum->value > p->p_bools.nprim)
                return -EINVAL;
 
-       p->p_bool_val_to_name[booldatum->value - 1] = key;
-       p->bool_val_to_struct[booldatum->value -1] = booldatum;
+       fa = p->sym_val_to_name[SYM_BOOLS];
+       if (flex_array_put_ptr(fa, booldatum->value - 1, key,
+                              GFP_KERNEL | __GFP_ZERO))
+               BUG();
+       p->bool_val_to_struct[booldatum->value - 1] = booldatum;
 
        return 0;
 }
@@ -221,38 +227,40 @@ int cond_read_bool(struct policydb *p, struct hashtab *h, void *fp)
 
        booldatum = kzalloc(sizeof(struct cond_bool_datum), GFP_KERNEL);
        if (!booldatum)
-               return -1;
+               return -ENOMEM;
 
        rc = next_entry(buf, fp, sizeof buf);
-       if (rc < 0)
+       if (rc)
                goto err;
 
        booldatum->value = le32_to_cpu(buf[0]);
        booldatum->state = le32_to_cpu(buf[1]);
 
+       rc = -EINVAL;
        if (!bool_isvalid(booldatum))
                goto err;
 
        len = le32_to_cpu(buf[2]);
 
+       rc = -ENOMEM;
        key = kmalloc(len + 1, GFP_KERNEL);
        if (!key)
                goto err;
        rc = next_entry(key, fp, len);
-       if (rc < 0)
+       if (rc)
                goto err;
-       key[len] = 0;
-       if (hashtab_insert(h, key, booldatum))
+       key[len] = '\0';
+       rc = hashtab_insert(h, key, booldatum);
+       if (rc)
                goto err;
 
        return 0;
 err:
        cond_destroy_bool(key, booldatum, NULL);
-       return -1;
+       return rc;
 }
 
-struct cond_insertf_data
-{
+struct cond_insertf_data {
        struct policydb *p;
        struct cond_av_list *other;
        struct cond_av_list *head;
@@ -266,7 +274,7 @@ static int cond_insertf(struct avtab *a, struct avtab_key *k, struct avtab_datum
        struct cond_av_list *other = data->other, *list, *cur;
        struct avtab_node *node_ptr;
        u8 found;
-
+       int rc = -EINVAL;
 
        /*
         * For type rules we have to make certain there aren't any
@@ -275,7 +283,7 @@ static int cond_insertf(struct avtab *a, struct avtab_key *k, struct avtab_datum
         */
        if (k->specified & AVTAB_TYPE) {
                if (avtab_search(&p->te_avtab, k)) {
-                       printk("SELinux: type rule already exists outside of a conditional.");
+                       printk(KERN_ERR "SELinux: type rule already exists outside of a conditional.\n");
                        goto err;
                }
                /*
@@ -290,24 +298,24 @@ static int cond_insertf(struct avtab *a, struct avtab_key *k, struct avtab_datum
                        node_ptr = avtab_search_node(&p->te_cond_avtab, k);
                        if (node_ptr) {
                                if (avtab_search_node_next(node_ptr, k->specified)) {
-                                       printk("SELinux: too many conflicting type rules.");
+                                       printk(KERN_ERR "SELinux: too many conflicting type rules.\n");
                                        goto err;
                                }
                                found = 0;
-                               for (cur = other; cur != NULL; cur = cur->next) {
+                               for (cur = other; cur; cur = cur->next) {
                                        if (cur->node == node_ptr) {
                                                found = 1;
                                                break;
                                        }
                                }
                                if (!found) {
-                                       printk("SELinux: conflicting type rules.\n");
+                                       printk(KERN_ERR "SELinux: conflicting type rules.\n");
                                        goto err;
                                }
                        }
                } else {
                        if (avtab_search(&p->te_cond_avtab, k)) {
-                               printk("SELinux: conflicting type rules when adding type rule for true.\n");
+                               printk(KERN_ERR "SELinux: conflicting type rules when adding type rule for true.\n");
                                goto err;
                        }
                }
@@ -315,13 +323,16 @@ static int cond_insertf(struct avtab *a, struct avtab_key *k, struct avtab_datum
 
        node_ptr = avtab_insert_nonunique(&p->te_cond_avtab, k, d);
        if (!node_ptr) {
-               printk("SELinux: could not insert rule.");
+               printk(KERN_ERR "SELinux: could not insert rule.\n");
+               rc = -ENOMEM;
                goto err;
        }
 
        list = kzalloc(sizeof(struct cond_av_list), GFP_KERNEL);
-       if (!list)
+       if (!list) {
+               rc = -ENOMEM;
                goto err;
+       }
 
        list->node = node_ptr;
        if (!data->head)
@@ -334,7 +345,7 @@ static int cond_insertf(struct avtab *a, struct avtab_key *k, struct avtab_datum
 err:
        cond_av_list_destroy(data->head);
        data->head = NULL;
-       return -1;
+       return rc;
 }
 
 static int cond_read_av_list(struct policydb *p, void *fp, struct cond_av_list **ret_list, struct cond_av_list *other)
@@ -348,13 +359,12 @@ static int cond_read_av_list(struct policydb *p, void *fp, struct cond_av_list *
 
        len = 0;
        rc = next_entry(buf, fp, sizeof(u32));
-       if (rc < 0)
-               return -1;
+       if (rc)
+               return rc;
 
        len = le32_to_cpu(buf[0]);
-       if (len == 0) {
+       if (len == 0)
                return 0;
-       }
 
        data.p = p;
        data.other = other;
@@ -365,7 +375,6 @@ static int cond_read_av_list(struct policydb *p, void *fp, struct cond_av_list *
                                     &data);
                if (rc)
                        return rc;
-
        }
 
        *ret_list = data.head;
@@ -375,12 +384,12 @@ static int cond_read_av_list(struct policydb *p, void *fp, struct cond_av_list *
 static int expr_isvalid(struct policydb *p, struct cond_expr *expr)
 {
        if (expr->expr_type <= 0 || expr->expr_type > COND_LAST) {
-               printk("SELinux: conditional expressions uses unknown operator.\n");
+               printk(KERN_ERR "SELinux: conditional expressions uses unknown operator.\n");
                return 0;
        }
 
        if (expr->bool > p->p_bools.nprim) {
-               printk("SELinux: conditional expressions uses unknown bool.\n");
+               printk(KERN_ERR "SELinux: conditional expressions uses unknown bool.\n");
                return 0;
        }
        return 1;
@@ -394,53 +403,55 @@ static int cond_read_node(struct policydb *p, struct cond_node *node, void *fp)
        struct cond_expr *expr = NULL, *last = NULL;
 
        rc = next_entry(buf, fp, sizeof(u32));
-       if (rc < 0)
-               return -1;
+       if (rc)
+               return rc;
 
        node->cur_state = le32_to_cpu(buf[0]);
 
        len = 0;
        rc = next_entry(buf, fp, sizeof(u32));
-       if (rc < 0)
-               return -1;
+       if (rc)
+               return rc;
 
        /* expr */
        len = le32_to_cpu(buf[0]);
 
-       for (i = 0; i < len; i++ ) {
+       for (i = 0; i < len; i++) {
                rc = next_entry(buf, fp, sizeof(u32) * 2);
-               if (rc < 0)
+               if (rc)
                        goto err;
 
+               rc = -ENOMEM;
                expr = kzalloc(sizeof(struct cond_expr), GFP_KERNEL);
-               if (!expr) {
+               if (!expr)
                        goto err;
-               }
 
                expr->expr_type = le32_to_cpu(buf[0]);
                expr->bool = le32_to_cpu(buf[1]);
 
                if (!expr_isvalid(p, expr)) {
+                       rc = -EINVAL;
                        kfree(expr);
                        goto err;
                }
 
-               if (i == 0) {
+               if (i == 0)
                        node->expr = expr;
-               } else {
+               else
                        last->next = expr;
-               }
                last = expr;
        }
 
-       if (cond_read_av_list(p, fp, &node->true_list, NULL) != 0)
+       rc = cond_read_av_list(p, fp, &node->true_list, NULL);
+       if (rc)
                goto err;
-       if (cond_read_av_list(p, fp, &node->false_list, node->true_list) != 0)
+       rc = cond_read_av_list(p, fp, &node->false_list, node->true_list);
+       if (rc)
                goto err;
        return 0;
 err:
        cond_node_destroy(node);
-       return -1;
+       return rc;
 }
 
 int cond_read_list(struct policydb *p, void *fp)
@@ -451,8 +462,8 @@ int cond_read_list(struct policydb *p, void *fp)
        int rc;
 
        rc = next_entry(buf, fp, sizeof buf);
-       if (rc < 0)
-               return -1;
+       if (rc)
+               return rc;
 
        len = le32_to_cpu(buf[0]);
 
@@ -461,27 +472,151 @@ int cond_read_list(struct policydb *p, void *fp)
                goto err;
 
        for (i = 0; i < len; i++) {
+               rc = -ENOMEM;
                node = kzalloc(sizeof(struct cond_node), GFP_KERNEL);
                if (!node)
                        goto err;
 
-               if (cond_read_node(p, node, fp) != 0)
+               rc = cond_read_node(p, node, fp);
+               if (rc)
                        goto err;
 
-               if (i == 0) {
+               if (i == 0)
                        p->cond_list = node;
-               } else {
+               else
                        last->next = node;
-               }
                last = node;
        }
        return 0;
 err:
        cond_list_destroy(p->cond_list);
        p->cond_list = NULL;
-       return -1;
+       return rc;
 }
 
+int cond_write_bool(void *vkey, void *datum, void *ptr)
+{
+       char *key = vkey;
+       struct cond_bool_datum *booldatum = datum;
+       struct policy_data *pd = ptr;
+       void *fp = pd->fp;
+       __le32 buf[3];
+       u32 len;
+       int rc;
+
+       len = strlen(key);
+       buf[0] = cpu_to_le32(booldatum->value);
+       buf[1] = cpu_to_le32(booldatum->state);
+       buf[2] = cpu_to_le32(len);
+       rc = put_entry(buf, sizeof(u32), 3, fp);
+       if (rc)
+               return rc;
+       rc = put_entry(key, 1, len, fp);
+       if (rc)
+               return rc;
+       return 0;
+}
+
+/*
+ * cond_write_cond_av_list doesn't write out the av_list nodes.
+ * Instead it writes out the key/value pairs from the avtab. This
+ * is necessary because there is no way to uniquely identifying rules
+ * in the avtab so it is not possible to associate individual rules
+ * in the avtab with a conditional without saving them as part of
+ * the conditional. This means that the avtab with the conditional
+ * rules will not be saved but will be rebuilt on policy load.
+ */
+static int cond_write_av_list(struct policydb *p,
+                             struct cond_av_list *list, struct policy_file *fp)
+{
+       __le32 buf[1];
+       struct cond_av_list *cur_list;
+       u32 len;
+       int rc;
+
+       len = 0;
+       for (cur_list = list; cur_list != NULL; cur_list = cur_list->next)
+               len++;
+
+       buf[0] = cpu_to_le32(len);
+       rc = put_entry(buf, sizeof(u32), 1, fp);
+       if (rc)
+               return rc;
+
+       if (len == 0)
+               return 0;
+
+       for (cur_list = list; cur_list != NULL; cur_list = cur_list->next) {
+               rc = avtab_write_item(p, cur_list->node, fp);
+               if (rc)
+                       return rc;
+       }
+
+       return 0;
+}
+
+int cond_write_node(struct policydb *p, struct cond_node *node,
+                   struct policy_file *fp)
+{
+       struct cond_expr *cur_expr;
+       __le32 buf[2];
+       int rc;
+       u32 len = 0;
+
+       buf[0] = cpu_to_le32(node->cur_state);
+       rc = put_entry(buf, sizeof(u32), 1, fp);
+       if (rc)
+               return rc;
+
+       for (cur_expr = node->expr; cur_expr != NULL; cur_expr = cur_expr->next)
+               len++;
+
+       buf[0] = cpu_to_le32(len);
+       rc = put_entry(buf, sizeof(u32), 1, fp);
+       if (rc)
+               return rc;
+
+       for (cur_expr = node->expr; cur_expr != NULL; cur_expr = cur_expr->next) {
+               buf[0] = cpu_to_le32(cur_expr->expr_type);
+               buf[1] = cpu_to_le32(cur_expr->bool);
+               rc = put_entry(buf, sizeof(u32), 2, fp);
+               if (rc)
+                       return rc;
+       }
+
+       rc = cond_write_av_list(p, node->true_list, fp);
+       if (rc)
+               return rc;
+       rc = cond_write_av_list(p, node->false_list, fp);
+       if (rc)
+               return rc;
+
+       return 0;
+}
+
+int cond_write_list(struct policydb *p, struct cond_node *list, void *fp)
+{
+       struct cond_node *cur;
+       u32 len;
+       __le32 buf[1];
+       int rc;
+
+       len = 0;
+       for (cur = list; cur != NULL; cur = cur->next)
+               len++;
+       buf[0] = cpu_to_le32(len);
+       rc = put_entry(buf, sizeof(u32), 1, fp);
+       if (rc)
+               return rc;
+
+       for (cur = list; cur != NULL; cur = cur->next) {
+               rc = cond_write_node(p, cur, fp);
+               if (rc)
+                       return rc;
+       }
+
+       return 0;
+}
 /* Determine whether additional permissions are granted by the conditional
  * av table, and if so, add them to the result
  */
@@ -489,24 +624,24 @@ void cond_compute_av(struct avtab *ctab, struct avtab_key *key, struct av_decisi
 {
        struct avtab_node *node;
 
-       if(!ctab || !key || !avd)
+       if (!ctab || !key || !avd)
                return;
 
-       for(node = avtab_search_node(ctab, key); node != NULL;
+       for (node = avtab_search_node(ctab, key); node;
                                node = avtab_search_node_next(node, key->specified)) {
-               if ( (u16) (AVTAB_ALLOWED|AVTAB_ENABLED) ==
-                    (node->key.specified & (AVTAB_ALLOWED|AVTAB_ENABLED)))
+               if ((u16)(AVTAB_ALLOWED|AVTAB_ENABLED) ==
+                   (node->key.specified & (AVTAB_ALLOWED|AVTAB_ENABLED)))
                        avd->allowed |= node->datum.data;
-               if ( (u16) (AVTAB_AUDITDENY|AVTAB_ENABLED) ==
-                    (node->key.specified & (AVTAB_AUDITDENY|AVTAB_ENABLED)))
+               if ((u16)(AVTAB_AUDITDENY|AVTAB_ENABLED) ==
+                   (node->key.specified & (AVTAB_AUDITDENY|AVTAB_ENABLED)))
                        /* Since a '0' in an auditdeny mask represents a
                         * permission we do NOT want to audit (dontaudit), we use
                         * the '&' operand to ensure that all '0's in the mask
                         * are retained (much unlike the allow and auditallow cases).
                         */
                        avd->auditdeny &= node->datum.data;
-               if ( (u16) (AVTAB_AUDITALLOW|AVTAB_ENABLED) ==
-                    (node->key.specified & (AVTAB_AUDITALLOW|AVTAB_ENABLED)))
+               if ((u16)(AVTAB_AUDITALLOW|AVTAB_ENABLED) ==
+                   (node->key.specified & (AVTAB_AUDITALLOW|AVTAB_ENABLED)))
                        avd->auditallow |= node->datum.data;
        }
        return;