hackedteam/core-android-native

View on GitHub
selinux_native/jni/libsepol/src/hierarchy.c

Summary

Maintainability
Test Coverage
/* Authors: Joshua Brindle <jbrindle@tresys.com>
 *         Jason Tang <jtang@tresys.com>
 *
 * Updates: KaiGai Kohei <kaigai@ak.jp.nec.com>
 *          adds checks based on newer boundary facility.
 *
 * A set of utility functions that aid policy decision when dealing
 * with hierarchal namespaces.
 *
 * Copyright (C) 2005 Tresys Technology, LLC
 *
 * Copyright (c) 2008 NEC Corporation
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <sepol/policydb/policydb.h>
#include <sepol/policydb/conditional.h>
#include <sepol/policydb/hierarchy.h>
#include <sepol/policydb/expand.h>
#include <sepol/policydb/util.h>

#include "debug.h"
#include "log.h"

typedef struct hierarchy_args {
    policydb_t *p;
    avtab_t *expa;        /* expanded avtab */
    /* This tells check_avtab_hierarchy to check this list in addition to the unconditional avtab */
    cond_av_list_t *opt_cond_list;
    sepol_handle_t *handle;
    int numerr;
} hierarchy_args_t;

/*
 * find_parent_(type|role|user)
 *
 * This function returns the parent datum of given XXX_datum_t
 * object or NULL, if it doesn't exist.
 *
 * If the given datum has a valid bounds, this function merely
 * returns the indicated object. Otherwise, it looks up the
 * parent based on the based hierarchy.
 */
#define find_parent_template(prefix)                \
int find_parent_##prefix(hierarchy_args_t *a,            \
             prefix##_datum_t *datum,        \
             prefix##_datum_t **parent)        \
{                                \
    char *parent_name, *datum_name, *tmp;            \
                                \
    if (datum->bounds)                        \
        *parent = a->p->prefix##_val_to_struct[datum->bounds - 1]; \
    else {                                \
        datum_name = a->p->p_##prefix##_val_to_name[datum->s.value - 1]; \
                                    \
        tmp = strrchr(datum_name, '.');                \
        /* no '.' means it has no parent */            \
        if (!tmp) {                        \
            *parent = NULL;                    \
            return 0;                    \
        }                            \
                                    \
        parent_name = strdup(datum_name);            \
        if (!parent_name)                    \
            return -1;                    \
        parent_name[tmp - datum_name] = '\0';            \
                                    \
        *parent = hashtab_search(a->p->p_##prefix##s.table, parent_name); \
        if (!*parent) {                        \
            /* Orphan type/role/user */            \
            ERR(a->handle,                    \
                "%s doesn't exist, %s is an orphan",    \
                parent_name,                \
                a->p->p_##prefix##_val_to_name[datum->s.value - 1]); \
            free(parent_name);                \
            return -1;                    \
        }                            \
        free(parent_name);                    \
    }                                \
                                    \
    return 0;                            \
}

static find_parent_template(type)
static find_parent_template(role)
static find_parent_template(user)

static void compute_avtab_datum(hierarchy_args_t *args,
                avtab_key_t *key,
                avtab_datum_t *result)
{
    avtab_datum_t *avdatp;
    uint32_t av = 0;

    avdatp = avtab_search(args->expa, key);
    if (avdatp)
        av = avdatp->data;
    if (args->opt_cond_list) {
        avdatp = cond_av_list_search(key, args->opt_cond_list);
        if (avdatp)
            av |= avdatp->data;
    }

    result->data = av;
}

/* This function verifies that the type passed in either has a parent or is in the 
 * root of the namespace, 0 on success, 1 on orphan and -1 on error
 */
static int check_type_hierarchy_callback(hashtab_key_t k, hashtab_datum_t d,
                     void *args)
{
    hierarchy_args_t *a;
    type_datum_t *t, *tp;

    a = (hierarchy_args_t *) args;
    t = (type_datum_t *) d;

    if (t->flavor == TYPE_ATTRIB) {
        /* It's an attribute, we don't care */
        return 0;
    }
    if (find_parent_type(a, t, &tp) < 0)
        return -1;

    if (tp && tp->flavor == TYPE_ATTRIB) {
        /* The parent is an attribute but the child isn't, not legal */
        LOGD("type %s is a child of an attribute %s",
            (char *) k, a->p->p_type_val_to_name[tp->s.value - 1]);
        a->numerr++;
        return -1;
    }
    return 0;
}

/* This function only verifies that the avtab node passed in does not violate any
 * hiearchy constraint via any relationship with other types in the avtab.
 * it should be called using avtab_map, returns 0 on success, 1 on violation and
 * -1 on error. opt_cond_list is an optional argument that tells this to check
 * a conditional list for the relationship as well as the unconditional avtab
 */
static int check_avtab_hierarchy_callback(avtab_key_t * k, avtab_datum_t * d,
                      void *args)
{
    avtab_key_t key;
    hierarchy_args_t *a = (hierarchy_args_t *) args;
    type_datum_t *s, *t1 = NULL, *t2 = NULL;
    avtab_datum_t av;

    if (!(k->specified & AVTAB_ALLOWED)) {
        /* This is not an allow rule, no checking done */
        return 0;
    }

    /* search for parent first */
    s = a->p->type_val_to_struct[k->source_type - 1];
    if (find_parent_type(a, s, &t1) < 0)
        return -1;
    if (t1) {
        /*
         * search for access allowed between type 1's
         * parent and type 2.
         */
        key.source_type = t1->s.value;
        key.target_type = k->target_type;
        key.target_class = k->target_class;
        key.specified = AVTAB_ALLOWED;
        compute_avtab_datum(a, &key, &av);

        if ((av.data & d->data) == d->data)
            return 0;
    }

    /* next we try type 1 and type 2's parent */
    s = a->p->type_val_to_struct[k->target_type - 1];
    if (find_parent_type(a, s, &t2) < 0)
        return -1;
    if (t2) {
        /*
         * search for access allowed between type 1 and
         * type 2's parent.
         */
        key.source_type = k->source_type;
        key.target_type = t2->s.value;
        key.target_class = k->target_class;
        key.specified = AVTAB_ALLOWED;
        compute_avtab_datum(a, &key, &av);

        if ((av.data & d->data) == d->data)
            return 0;
    }

    if (t1 && t2) {
        /*
                 * search for access allowed between type 1's parent
                 * and type 2's parent.
                 */
        key.source_type = t1->s.value;
        key.target_type = t2->s.value;
        key.target_class = k->target_class;
        key.specified = AVTAB_ALLOWED;
        compute_avtab_datum(a, &key, &av);

        if ((av.data & d->data) == d->data)
            return 0;
    }

    /*
     * Neither one of these types have parents and 
     * therefore the hierarchical constraint does not apply
     */
    if (!t1 && !t2)
        return 0;

    /*
     * At this point there is a violation of the hierarchal
     * constraint, send error condition back
     */
    ERR(a->handle,
        "hierarchy violation between types %s and %s : %s { %s }",
        a->p->p_type_val_to_name[k->source_type - 1],
        a->p->p_type_val_to_name[k->target_type - 1],
        a->p->p_class_val_to_name[k->target_class - 1],
        sepol_av_to_string(a->p, k->target_class, d->data & ~av.data));
    a->numerr++;
    return 0;
}

/*
 * If same permissions are allowed for same combination of
 * source and target, we can evaluate them as unconditional
 * one.
 * See the following example. A_t type is bounds of B_t type,
 * so B_t can never have wider permissions then A_t.
 * A_t has conditional permission on X_t, however, a part of
 * them (getattr and read) are unconditionaly allowed to A_t.
 *
 * Example)
 * typebounds A_t B_t;
 *
 * allow B_t X_t : file { getattr };
 * if (foo_bool) {
 *     allow A_t X_t : file { getattr read };
 * } else {
 *     allow A_t X_t : file { getattr read write };
 * }
 *
 * We have to pull up them as unconditional ones in this case,
 * because it seems to us B_t is violated to bounds constraints
 * during unconditional policy checking.
 */
static int pullup_unconditional_perms(cond_list_t * cond_list,
                      hierarchy_args_t * args)
{
    cond_list_t *cur_node;
    cond_av_list_t *cur_av, *expl_true = NULL, *expl_false = NULL;
    avtab_t expa_true, expa_false;
    avtab_datum_t *avdatp;
    avtab_datum_t avdat;
    avtab_ptr_t avnode;

    for (cur_node = cond_list; cur_node; cur_node = cur_node->next) {
        if (avtab_init(&expa_true))
            goto oom0;
        if (avtab_init(&expa_false))
            goto oom1;
        if (expand_cond_av_list(args->p, cur_node->true_list,
                    &expl_true, &expa_true))
            goto oom2;
        if (expand_cond_av_list(args->p, cur_node->false_list,
                    &expl_false, &expa_false))
            goto oom3;
        for (cur_av = expl_true; cur_av; cur_av = cur_av->next) {
            avdatp = avtab_search(&expa_false,
                          &cur_av->node->key);
            if (!avdatp)
                continue;

            avdat.data = (cur_av->node->datum.data
                      & avdatp->data);
            if (!avdat.data)
                continue;

            avnode = avtab_search_node(args->expa,
                           &cur_av->node->key);
            if (avnode) {
                avnode->datum.data |= avdat.data;
            } else {
                if (avtab_insert(args->expa,
                         &cur_av->node->key,
                         &avdat))
                    goto oom4;
            }
        }
        cond_av_list_destroy(expl_false);
        cond_av_list_destroy(expl_true);
        avtab_destroy(&expa_false);
        avtab_destroy(&expa_true);
    }
    return 0;

oom4:
    cond_av_list_destroy(expl_false);
oom3:
    cond_av_list_destroy(expl_true);
oom2:
    avtab_destroy(&expa_false);
oom1:
    avtab_destroy(&expa_true);
oom0:
    ERR(args->handle, "out of memory on conditional av list expansion");
        return 1;
}

static int check_cond_avtab_hierarchy(cond_list_t * cond_list,
                      hierarchy_args_t * args)
{
    int rc;
    cond_list_t *cur_node;
    cond_av_list_t *cur_av, *expl = NULL;
    avtab_t expa;
    hierarchy_args_t *a = (hierarchy_args_t *) args;
    avtab_datum_t avdat, *uncond;

    for (cur_node = cond_list; cur_node; cur_node = cur_node->next) {
        /*
         * Check true condition
         */
        if (avtab_init(&expa))
            goto oom;
        if (expand_cond_av_list(args->p, cur_node->true_list,
                    &expl, &expa)) {
            avtab_destroy(&expa);
            goto oom;
        }
        args->opt_cond_list = expl;
        for (cur_av = expl; cur_av; cur_av = cur_av->next) {
            avdat.data = cur_av->node->datum.data;
            uncond = avtab_search(a->expa, &cur_av->node->key);
            if (uncond)
                avdat.data |= uncond->data;
            rc = check_avtab_hierarchy_callback(&cur_av->node->key,
                                &avdat, args);
            if (rc)
                args->numerr++;
        }
        cond_av_list_destroy(expl);
        avtab_destroy(&expa);

        /*
         * Check false condition
         */
        if (avtab_init(&expa))
            goto oom;
        if (expand_cond_av_list(args->p, cur_node->false_list,
                    &expl, &expa)) {
            avtab_destroy(&expa);
            goto oom;
        }
        args->opt_cond_list = expl;
        for (cur_av = expl; cur_av; cur_av = cur_av->next) {
            avdat.data = cur_av->node->datum.data;
            uncond = avtab_search(a->expa, &cur_av->node->key);
            if (uncond)
                avdat.data |= uncond->data;

            rc = check_avtab_hierarchy_callback(&cur_av->node->key,
                                &avdat, args);
            if (rc)
                a->numerr++;
        }
        cond_av_list_destroy(expl);
        avtab_destroy(&expa);
    }

    return 0;

      oom:
    ERR(args->handle, "out of memory on conditional av list expansion");
    return 1;
}

/* The role hierarchy is defined as: a child role cannot have more types than it's parent.
 * This function should be called with hashtab_map, it will return 0 on success, 1 on 
 * constraint violation and -1 on error
 */
static int check_role_hierarchy_callback(hashtab_key_t k
                     __attribute__ ((unused)),
                     hashtab_datum_t d, void *args)
{
    hierarchy_args_t *a;
    role_datum_t *r, *rp;

    a = (hierarchy_args_t *) args;
    r = (role_datum_t *) d;

    if (find_parent_role(a, r, &rp) < 0)
        return -1;

    if (rp && !ebitmap_contains(&rp->types.types, &r->types.types)) {
        /* hierarchical constraint violation, return error */
        LOGD("Role hierarchy violation, %s exceeds %s",
            (char *) k, a->p->p_role_val_to_name[rp->s.value - 1]);
        a->numerr++;
    }
    return 0;
}

/* The user hierarchy is defined as: a child user cannot have a role that
 * its parent doesn't have.  This function should be called with hashtab_map,
 * it will return 0 on success, 1 on constraint violation and -1 on error.
 */
static int check_user_hierarchy_callback(hashtab_key_t k
                     __attribute__ ((unused)),
                     hashtab_datum_t d, void *args)
{
    hierarchy_args_t *a;
    user_datum_t *u, *up;

    a = (hierarchy_args_t *) args;
    u = (user_datum_t *) d;

    if (find_parent_user(a, u, &up) < 0)
        return -1;

    if (up && !ebitmap_contains(&up->roles.roles, &u->roles.roles)) {
        /* hierarchical constraint violation, return error */
        LOGD("User hierarchy violation, %s exceeds %s",
            (char *) k, a->p->p_user_val_to_name[up->s.value - 1]);
        a->numerr++;
    }
    return 0;
}

int hierarchy_check_constraints(sepol_handle_t * handle, policydb_t * p)
{
    hierarchy_args_t args;
    avtab_t expa;

    if (avtab_init(&expa))
        goto oom;
    if (expand_avtab(p, &p->te_avtab, &expa)) {
        avtab_destroy(&expa);
        goto oom;
    }

    args.p = p;
    args.expa = &expa;
    args.opt_cond_list = NULL;
    args.handle = handle;
    args.numerr = 0;

    if (hashtab_map(p->p_types.table, check_type_hierarchy_callback, &args))
        goto bad;

    if (pullup_unconditional_perms(p->cond_list, &args))
        return -1;

    if (avtab_map(&expa, check_avtab_hierarchy_callback, &args))
        goto bad;

    if (check_cond_avtab_hierarchy(p->cond_list, &args))
        goto bad;

    if (hashtab_map(p->p_roles.table, check_role_hierarchy_callback, &args))
        goto bad;

    if (hashtab_map(p->p_users.table, check_user_hierarchy_callback, &args))
        goto bad;

    if (args.numerr) {
        ERR(handle, "%d total errors found during hierarchy check",
            args.numerr);
        goto bad;
    }

    avtab_destroy(&expa);
    return 0;

      bad:
    avtab_destroy(&expa);
    return -1;

      oom:
    ERR(handle, "Out of memory");
    return -1;
}