hackedteam/core-android-native

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

Summary

Maintainability
Test Coverage

/*
 * 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
 *
 * Updated: Red Hat, Inc.  James Morris <jmorris@redhat.com>
 *
 *      Fine-grained netlink support
 *      IPv6 support
 *      Code cleanup
 *
 * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
 * Copyright (C) 2003 - 2004 Tresys Technology, LLC
 * Copyright (C) 2003 - 2004 Red Hat, Inc.
 *
 *  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
 */

/* FLASK */

/*
 * Implementation of the security services.
 */

/* Initial sizes malloc'd for sepol_compute_av_reason_buffer() support */
#define REASON_BUF_SIZE 2048
#define EXPR_BUF_SIZE 1024
#define STACK_LEN 32

#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <sepol/policydb/policydb.h>
#include <sepol/policydb/sidtab.h>
#include <sepol/policydb/services.h>
#include <sepol/policydb/conditional.h>
#include <sepol/policydb/flask.h>
#include <sepol/policydb/util.h>

#include "debug.h"
#include "private.h"
#include "context.h"
#include "av_permissions.h"
#include "dso.h"
#include "mls.h"
#include "log.h"

#define BUG() do { LOGD( "Badness at %s:%d", __FILE__, __LINE__); } while (0)
#define BUG_ON(x) do { if (x) LOGD( "Badness at %s:%d", __FILE__, __LINE__); } while (0)

static int selinux_enforcing = 1;

static sidtab_t mysidtab, *sidtab = &mysidtab;
static policydb_t mypolicydb, *policydb = &mypolicydb;

/* Used by sepol_compute_av_reason_buffer() to keep track of entries */
static int reason_buf_used;
static int reason_buf_len;

/* Stack services for RPN to infix conversion. */
static char **stack;
static int stack_len;
static int next_stack_entry;

static void push(char *expr_ptr)
{
    if (next_stack_entry >= stack_len) {
        char **new_stack = stack;
        int new_stack_len;

        if (stack_len == 0)
            new_stack_len = STACK_LEN;
        else
            new_stack_len = stack_len * 2;

        new_stack = realloc(stack, new_stack_len * sizeof(*stack));
        if (!new_stack) {
            LOGD( "unable to allocate stack space");
            return;
        }
        stack_len = new_stack_len;
        stack = new_stack;
    }
    stack[next_stack_entry] = expr_ptr;
    next_stack_entry++;
}

static char *pop(void)
{
    next_stack_entry--;
    if (next_stack_entry < 0) {
        next_stack_entry = 0;
        LOGD( "pop called with no stack entries");
        return NULL;
    }
    return stack[next_stack_entry];
}
/* End Stack services */

int hidden sepol_set_sidtab(sidtab_t * s)
{
    sidtab = s;
    return 0;
}

int hidden sepol_set_policydb(policydb_t * p)
{
    policydb = p;
    return 0;
}

int sepol_set_policydb_from_file(FILE * fp)
{
    struct policy_file pf;

    policy_file_init(&pf);
    pf.fp = fp;
    pf.type = PF_USE_STDIO;
    if (mypolicydb.policy_type)
        policydb_destroy(&mypolicydb);
    if (policydb_init(&mypolicydb)) {
        LOGD( "Out of memory!");
        return -1;
    }
    if (policydb_read(&mypolicydb, &pf, 0)) {
        policydb_destroy(&mypolicydb);
        LOGD( "can't read binary policy: %s", strerror(errno));
        return -1;
    }
    policydb = &mypolicydb;
    return sepol_sidtab_init(sidtab);
}

/*
 * The largest sequence number that has been used when
 * providing an access decision to the access vector cache.
 * The sequence number only changes when a policy change
 * occurs.
 */
static uint32_t latest_granting = 0;

/*
 * cat_expr_buf adds a string to an expression buffer and handles
 * realloc's if buffer is too small. The array of expression text
 * buffer pointers and its counter are globally defined here as
 * constraint_expr_eval_reason() sets them up and cat_expr_buf
 * updates the e_buf pointer.
 */
static int expr_counter;
static char **expr_list;
static int expr_buf_used;
static int expr_buf_len;

static void cat_expr_buf(char *e_buf, char *string)
{
    int len, new_buf_len;
    char *p, *new_buf = e_buf;

    while (1) {
        p = e_buf + expr_buf_used;
        len = snprintf(p, expr_buf_len - expr_buf_used, "%s", string);
        if (len < 0 || len >= expr_buf_len - expr_buf_used) {
            new_buf_len = expr_buf_len + EXPR_BUF_SIZE;
            new_buf = realloc(e_buf, new_buf_len);
            if (!new_buf) {
                LOGD( "failed to realloc expr buffer");
                return;
            }
            /* Update new ptr in expr list and locally + new len */
            expr_list[expr_counter] = new_buf;
            e_buf = new_buf;
            expr_buf_len = new_buf_len;
        } else {
            expr_buf_used += len;
            return;
        }
    }
}

/*
 * If the POLICY_KERN version is >= POLICYDB_VERSION_CONSTRAINT_NAMES,
 * then for 'types' only, read the types_names->types list as it will
 * contain a list of types and attributes that were defined in the
 * policy source.
 * For user and role plus types (for policy vers <
 * POLICYDB_VERSION_CONSTRAINT_NAMES) just read the e->names list.
 */
static void get_name_list(constraint_expr_t *e, int type,
                            char *src, char *op, int failed)
{
    ebitmap_t *types;
    int rc = 0;
    unsigned int i;
    char tmp_buf[128];
    int counter = 0;

    if (policydb->policy_type == POLICY_KERN &&
            policydb->policyvers >= POLICYDB_VERSION_CONSTRAINT_NAMES &&
            type == CEXPR_TYPE)
        types = &e->type_names->types;
    else
        types = &e->names;

    /* Find out how many entries */
    for (i = ebitmap_startbit(types); i < ebitmap_length(types); i++) {
        rc = ebitmap_get_bit(types, i);
        if (rc == 0)
            continue;
        else
            counter++;
    }
    snprintf(tmp_buf, sizeof(tmp_buf), "(%s%s", src, op);
    cat_expr_buf(expr_list[expr_counter], tmp_buf);

    if (counter == 0)
        cat_expr_buf(expr_list[expr_counter], "<empty_set> ");
    if (counter > 1)
        cat_expr_buf(expr_list[expr_counter], " {");
    if (counter >= 1) {
        for (i = ebitmap_startbit(types); i < ebitmap_length(types); i++) {
            rc = ebitmap_get_bit(types, i);
            if (rc == 0)
                continue;

            /* Collect entries */
            switch (type) {
            case CEXPR_USER:
                snprintf(tmp_buf, sizeof(tmp_buf), " %s",
                            policydb->p_user_val_to_name[i]);
                break;
            case CEXPR_ROLE:
                snprintf(tmp_buf, sizeof(tmp_buf), " %s",
                            policydb->p_role_val_to_name[i]);
                break;
            case CEXPR_TYPE:
                snprintf(tmp_buf, sizeof(tmp_buf), " %s",
                            policydb->p_type_val_to_name[i]);
                break;
            }
            cat_expr_buf(expr_list[expr_counter], tmp_buf);
        }
    }
    if (counter > 1)
        cat_expr_buf(expr_list[expr_counter], " }");
    if (failed)
        cat_expr_buf(expr_list[expr_counter], " -Fail-) ");
    else
        cat_expr_buf(expr_list[expr_counter], ") ");

    return;
}

static void msgcat(char *src, char *tgt, char *op, int failed)
{
    char tmp_buf[128];
    if (failed)
        snprintf(tmp_buf, sizeof(tmp_buf), "(%s %s %s -Fail-) ",
                src, op, tgt);
    else
        snprintf(tmp_buf, sizeof(tmp_buf), "(%s %s %s) ",
                src, op, tgt);
    cat_expr_buf(expr_list[expr_counter], tmp_buf);
}

/* Returns a buffer with class, statement type and permissions */
static char *get_class_info(sepol_security_class_t tclass,
                            constraint_node_t *constraint,
                            context_struct_t *xcontext)
{
    constraint_expr_t *e;
    int mls, state_num;

    /* Find if MLS statement or not */
    mls = 0;
    for (e = constraint->expr; e; e = e->next) {
        if (e->attr >= CEXPR_L1L2) {
            mls = 1;
            break;
        }
    }

    /* Determine statement type */
    char *statements[] = {
        "constrain ",            /* 0 */
        "mlsconstrain ",        /* 1 */
        "validatetrans ",        /* 2 */
        "mlsvalidatetrans ",    /* 3 */
        0 };

    if (xcontext == NULL)
        state_num = mls + 0;
    else
        state_num = mls + 2;

    int class_buf_len = 0;
    int new_class_buf_len;
    int len, buf_used;
    char *class_buf = NULL, *p;
    char *new_class_buf = NULL;

    while (1) {
        new_class_buf_len = class_buf_len + EXPR_BUF_SIZE;
        new_class_buf = realloc(class_buf, new_class_buf_len);
            if (!new_class_buf)
                return NULL;
        class_buf_len = new_class_buf_len;
        class_buf = new_class_buf;
        buf_used = 0;
        p = class_buf;

        /* Add statement type */
        len = snprintf(p, class_buf_len - buf_used, "%s", statements[state_num]);
        if (len < 0 || len >= class_buf_len - buf_used)
            continue;

        /* Add class entry */
        p += len;
        buf_used += len;
        len = snprintf(p, class_buf_len - buf_used, "%s ",
                policydb->p_class_val_to_name[tclass - 1]);
        if (len < 0 || len >= class_buf_len - buf_used)
            continue;

        /* Add permission entries */
        p += len;
        buf_used += len;
        len = snprintf(p, class_buf_len - buf_used, "{%s } (",
                sepol_av_to_string(policydb, tclass, constraint->permissions));
        if (len < 0 || len >= class_buf_len - buf_used)
            continue;
        break;
    }
    return class_buf;
}

/*
 * Modified version of constraint_expr_eval that will process each
 * constraint as before but adds the information to text buffers that
 * will hold various components. The expression will be in RPN format,
 * therefore there is a stack based RPN to infix converter to produce
 * the final readable constraint.
 *
 * Return the boolean value of a constraint expression
 * when it is applied to the specified source and target
 * security contexts.
 *
 * xcontext is a special beast...  It is used by the validatetrans rules
 * only.  For these rules, scontext is the context before the transition,
 * tcontext is the context after the transition, and xcontext is the
 * context of the process performing the transition.  All other callers
 * of constraint_expr_eval_reason should pass in NULL for xcontext.
 *
 * This function will also build a buffer as the constraint is processed
 * for analysis. If this option is not required, then:
 *      'tclass' should be '0' and r_buf MUST be NULL.
 */
static int constraint_expr_eval_reason(context_struct_t *scontext,
                context_struct_t *tcontext,
                context_struct_t *xcontext,
                sepol_security_class_t tclass,
                constraint_node_t *constraint,
                char **r_buf,
                unsigned int flags)
{
    uint32_t val1, val2;
    context_struct_t *c;
    role_datum_t *r1, *r2;
    mls_level_t *l1, *l2;
    constraint_expr_t *e;
    int s[CEXPR_MAXDEPTH];
    int sp = -1;
    char tmp_buf[128];

/*
 * Define the s_t_x_num values that make up r1, t2 etc. in text strings
 * Set 1 = source, 2 = target, 3 = xcontext for validatetrans
 */
#define SOURCE  1
#define TARGET  2
#define XTARGET 3

    int s_t_x_num = SOURCE;

    /* Set 0 = fail, u = CEXPR_USER, r = CEXPR_ROLE, t = CEXPR_TYPE */
    int u_r_t = 0;

    char *src = NULL;
    char *tgt = NULL;
    int rc = 0, x;
    char *class_buf = NULL;

    class_buf = get_class_info(tclass, constraint, xcontext);
    if (!class_buf) {
        LOGD( "failed to allocate class buffer");
        return -ENOMEM;
    }

    /* Original function but with buffer support */
    int expr_list_len = 0;
    expr_counter = 0;
    expr_list = NULL;
    for (e = constraint->expr; e; e = e->next) {
        /* Allocate a stack to hold expression buffer entries */
        if (expr_counter >= expr_list_len) {
            char **new_expr_list = expr_list;
            int new_expr_list_len;

            if (expr_list_len == 0)
                new_expr_list_len = STACK_LEN;
            else
                new_expr_list_len = expr_list_len * 2;

            new_expr_list = realloc(expr_list,
                    new_expr_list_len * sizeof(*expr_list));
            if (!new_expr_list) {
                LOGD( "failed to allocate expr buffer stack");
                rc = -ENOMEM;
                goto out;
            }
            expr_list_len = new_expr_list_len;
            expr_list = new_expr_list;
        }

        /*
         * malloc a buffer to store each expression text component. If
         * buffer is too small cat_expr_buf() will realloc extra space.
         */
        expr_buf_len = EXPR_BUF_SIZE;
        expr_list[expr_counter] = malloc(expr_buf_len);
        if (!expr_list[expr_counter]) {
            LOGD( "failed to allocate expr buffer");
            rc = -ENOMEM;
            goto out;
        }
        expr_buf_used = 0;

        /* Now process each expression of the constraint */
        switch (e->expr_type) {
        case CEXPR_NOT:
            BUG_ON(sp < 0);
            s[sp] = !s[sp];
            cat_expr_buf(expr_list[expr_counter], "not");
            break;
        case CEXPR_AND:
            BUG_ON(sp < 1);
            sp--;
            s[sp] &= s[sp + 1];
            cat_expr_buf(expr_list[expr_counter], "and");
            break;
        case CEXPR_OR:
            BUG_ON(sp < 1);
            sp--;
            s[sp] |= s[sp + 1];
            cat_expr_buf(expr_list[expr_counter], "or");
            break;
        case CEXPR_ATTR:
            if (sp == (CEXPR_MAXDEPTH - 1))
                goto out;

            switch (e->attr) {
            case CEXPR_USER:
                val1 = scontext->user;
                val2 = tcontext->user;
                free(src); src = strdup("u1");
                free(tgt); tgt = strdup("u2");
                break;
            case CEXPR_TYPE:
                val1 = scontext->type;
                val2 = tcontext->type;
                free(src); src = strdup("t1");
                free(tgt); tgt = strdup("t2");
                break;
            case CEXPR_ROLE:
                val1 = scontext->role;
                val2 = tcontext->role;
                r1 = policydb->role_val_to_struct[val1 - 1];
                r2 = policydb->role_val_to_struct[val2 - 1];
                free(src); src = strdup("r1");
                free(tgt); tgt = strdup("r2");

                switch (e->op) {
                case CEXPR_DOM:
                    s[++sp] = ebitmap_get_bit(&r1->dominates, val2 - 1);
                    msgcat(src, tgt, "dom", s[sp] == 0);
                    expr_counter++;
                    continue;
                case CEXPR_DOMBY:
                    s[++sp] = ebitmap_get_bit(&r2->dominates, val1 - 1);
                    msgcat(src, tgt, "domby", s[sp] == 0);
                    expr_counter++;
                    continue;
                case CEXPR_INCOMP:
                    s[++sp] = (!ebitmap_get_bit(&r1->dominates, val2 - 1)
                         && !ebitmap_get_bit(&r2->dominates, val1 - 1));
                    msgcat(src, tgt, "incomp", s[sp] == 0);
                    expr_counter++;
                    continue;
                default:
                    break;
                }
                break;
            case CEXPR_L1L2:
                l1 = &(scontext->range.level[0]);
                l2 = &(tcontext->range.level[0]);
                free(src); src = strdup("l1");
                free(tgt); tgt = strdup("l2");
                goto mls_ops;
            case CEXPR_L1H2:
                l1 = &(scontext->range.level[0]);
                l2 = &(tcontext->range.level[1]);
                free(src); src = strdup("l1");
                free(tgt); tgt = strdup("h2");
                goto mls_ops;
            case CEXPR_H1L2:
                l1 = &(scontext->range.level[1]);
                l2 = &(tcontext->range.level[0]);
                free(src); src = strdup("h1");
                free(tgt); tgt = strdup("l2");
                goto mls_ops;
            case CEXPR_H1H2:
                l1 = &(scontext->range.level[1]);
                l2 = &(tcontext->range.level[1]);
                free(src); src = strdup("h1");
                free(tgt); tgt = strdup("h2");
                goto mls_ops;
            case CEXPR_L1H1:
                l1 = &(scontext->range.level[0]);
                l2 = &(scontext->range.level[1]);
                free(src); src = strdup("l1");
                free(tgt); tgt = strdup("h1");
                goto mls_ops;
            case CEXPR_L2H2:
                l1 = &(tcontext->range.level[0]);
                l2 = &(tcontext->range.level[1]);
                free(src); src = strdup("l2");
                free(tgt); tgt = strdup("h2");
mls_ops:
                switch (e->op) {
                case CEXPR_EQ:
                    s[++sp] = mls_level_eq(l1, l2);
                    msgcat(src, tgt, "eq", s[sp] == 0);
                    expr_counter++;
                    continue;
                case CEXPR_NEQ:
                    s[++sp] = !mls_level_eq(l1, l2);
                    msgcat(src, tgt, "!=", s[sp] == 0);
                    expr_counter++;
                    continue;
                case CEXPR_DOM:
                    s[++sp] = mls_level_dom(l1, l2);
                    msgcat(src, tgt, "dom", s[sp] == 0);
                    expr_counter++;
                    continue;
                case CEXPR_DOMBY:
                    s[++sp] = mls_level_dom(l2, l1);
                    msgcat(src, tgt, "domby", s[sp] == 0);
                    expr_counter++;
                    continue;
                case CEXPR_INCOMP:
                    s[++sp] = mls_level_incomp(l2, l1);
                    msgcat(src, tgt, "incomp", s[sp] == 0);
                    expr_counter++;
                    continue;
                default:
                    BUG();
                    goto out;
                }
                break;
            default:
                BUG();
                goto out;
            }

            switch (e->op) {
            case CEXPR_EQ:
                s[++sp] = (val1 == val2);
                msgcat(src, tgt, "==", s[sp] == 0);
                break;
            case CEXPR_NEQ:
                s[++sp] = (val1 != val2);
                msgcat(src, tgt, "!=", s[sp] == 0);
                break;
            default:
                BUG();
                goto out;
            }
            break;
        case CEXPR_NAMES:
            if (sp == (CEXPR_MAXDEPTH - 1))
                goto out;
            s_t_x_num = SOURCE;
            c = scontext;
            if (e->attr & CEXPR_TARGET) {
                s_t_x_num = TARGET;
                c = tcontext;
            } else if (e->attr & CEXPR_XTARGET) {
                s_t_x_num = XTARGET;
                c = xcontext;
            }
            if (!c) {
                BUG();
                goto out;
            }
            if (e->attr & CEXPR_USER) {
                u_r_t = CEXPR_USER;
                val1 = c->user;
                snprintf(tmp_buf, sizeof(tmp_buf), "u%d ", s_t_x_num);
                free(src); src = strdup(tmp_buf);
            } else if (e->attr & CEXPR_ROLE) {
                u_r_t = CEXPR_ROLE;
                val1 = c->role;
                snprintf(tmp_buf, sizeof(tmp_buf), "r%d ", s_t_x_num);
                free(src); src = strdup(tmp_buf);
            } else if (e->attr & CEXPR_TYPE) {
                u_r_t = CEXPR_TYPE;
                val1 = c->type;
                snprintf(tmp_buf, sizeof(tmp_buf), "t%d ", s_t_x_num);
                free(src); src = strdup(tmp_buf);
            } else {
                BUG();
                goto out;
            }

            switch (e->op) {
            case CEXPR_EQ:
                s[++sp] = ebitmap_get_bit(&e->names, val1 - 1);
                get_name_list(e, u_r_t, src, "==", s[sp] == 0);
                break;

            case CEXPR_NEQ:
                s[++sp] = !ebitmap_get_bit(&e->names, val1 - 1);
                get_name_list(e, u_r_t, src, "!=", s[sp] == 0);
                break;
            default:
                BUG();
                goto out;
            }
            break;
        default:
            BUG();
            goto out;
        }
        expr_counter++;
    }

    /*
     * At this point each expression of the constraint is in
     * expr_list[n+1] and in RPN format. Now convert to 'infix'
     */

    /*
     * Save expr count but zero expr_counter to detect if
     * 'BUG(); goto out;' was called as we need to release any used
     * expr_list malloc's. Normally they are released by the RPN to
     * infix code.
     */
    int expr_count = expr_counter;
    expr_counter = 0;

    /*
     * The array of expression answer buffer pointers and counter.
     * Generate the same number of answer buffer entries as expression
     * buffers (as there will never be more).
     */
    char **answer_list;
    int answer_counter = 0;

    answer_list = malloc(expr_count * sizeof(*answer_list));
    if (!answer_list) {
        LOGD( "failed to allocate answer stack");
        rc = -ENOMEM;
        goto out;
    }

    /* The pop operands */
    char *a;
    char *b;
    int a_len, b_len;

    /* Convert constraint from RPN to infix notation. */
    for (x = 0; x != expr_count; x++) {
        if (strncmp(expr_list[x], "and", 3) == 0 || strncmp(expr_list[x],
                    "or", 2) == 0) {
            b = pop();
            b_len = strlen(b);
            a = pop();
            a_len = strlen(a);

            /* get a buffer to hold the answer */
            answer_list[answer_counter] = malloc(a_len + b_len + 8);
            if (!answer_list[answer_counter]) {
                LOGD( "failed to allocate answer buffer");
                rc = -ENOMEM;
                goto out;
            }
            memset(answer_list[answer_counter], '\0', a_len + b_len + 8);

            sprintf(answer_list[answer_counter], "%s %s %s", a,
                    expr_list[x], b);
            push(answer_list[answer_counter++]);
            free(a);
            free(b);
        } else if (strncmp(expr_list[x], "not", 3) == 0) {
            b = pop();
            b_len = strlen(b);

            answer_list[answer_counter] = malloc(b_len + 8);
            if (!answer_list[answer_counter]) {
                LOGD( "failed to allocate answer buffer");
                rc = -ENOMEM;
                goto out;
            }
            memset(answer_list[answer_counter], '\0', b_len + 8);

            if (strncmp(b, "not", 3) == 0)
                sprintf(answer_list[answer_counter], "%s (%s)",
                        expr_list[x], b);
            else
                sprintf(answer_list[answer_counter], "%s%s",
                        expr_list[x], b);
            push(answer_list[answer_counter++]);
            free(b);
        } else {
            push(expr_list[x]);
        }
    }
    /* Get the final answer from tos and build constraint text */
    a = pop();

    /* Constraint calculation: rc = 0 is denied, rc = 1 is granted */
    sprintf(tmp_buf, "Constraint %s\n", s[0] ? "GRANTED" : "DENIED");

    int len, new_buf_len;
    char *p, **new_buf = r_buf;
    /*
     * These contain the constraint components that are added to the
     * callers reason buffer.
     */
    char *buffers[] = { class_buf, a, "); ", tmp_buf, 0 };

    /*
     * This will add the constraints to the callers reason buffer (who is
     * responsible for freeing the memory). It will handle any realloc's
     * should the buffer be too short.
     * The reason_buf_used and reason_buf_len counters are defined
     * globally as multiple constraints can be in the buffer.
     */

    if (r_buf && ((s[0] == 0) || ((s[0] == 1 &&
                (flags & SHOW_GRANTED) == SHOW_GRANTED)))) {
        for (x = 0; buffers[x] != NULL; x++) {
            while (1) {
                p = *r_buf + reason_buf_used;
                len = snprintf(p, reason_buf_len - reason_buf_used,
                        "%s", buffers[x]);
                if (len < 0 || len >= reason_buf_len - reason_buf_used) {
                    new_buf_len = reason_buf_len + REASON_BUF_SIZE;
                    *new_buf = realloc(*r_buf, new_buf_len);
                    if (!new_buf) {
                        LOGD( "failed to realloc reason buffer");
                        goto out1;
                    }
                    **r_buf = **new_buf;
                    reason_buf_len = new_buf_len;
                    continue;
                } else {
                    reason_buf_used += len;
                    break;
                }
            }
        }
    }

out1:
    rc = s[0];
    free(a);

out:
    free(class_buf);
    free(src);
    free(tgt);

    if (expr_counter) {
        for (x = 0; expr_list[x] != NULL; x++)
            free(expr_list[x]);
    }
    return rc;
}

/*
 * Compute access vectors based on a context structure pair for
 * the permissions in a particular class.
 */
static int context_struct_compute_av(context_struct_t * scontext,
                     context_struct_t * tcontext,
                     sepol_security_class_t tclass,
                     sepol_access_vector_t requested,
                     struct sepol_av_decision *avd,
                     unsigned int *reason,
                     char **r_buf,
                     unsigned int flags)
{
    constraint_node_t *constraint;
    struct role_allow *ra;
    avtab_key_t avkey;
    class_datum_t *tclass_datum;
    avtab_ptr_t node;
    ebitmap_t *sattr, *tattr;
    ebitmap_node_t *snode, *tnode;
    unsigned int i, j;

    if (!tclass || tclass > policydb->p_classes.nprim) {
        LOGD( "unrecognized class %d", tclass);
        return -EINVAL;
    }
    tclass_datum = policydb->class_val_to_struct[tclass - 1];

    /* 
     * Initialize the access vectors to the default values.
     */
    avd->allowed = 0;
    avd->decided = 0xffffffff;
    avd->auditallow = 0;
    avd->auditdeny = 0xffffffff;
    avd->seqno = latest_granting;
    *reason = 0;

    /*
     * If a specific type enforcement rule was defined for
     * this permission check, then use it.
     */
    avkey.target_class = tclass;
    avkey.specified = AVTAB_AV;
    sattr = &policydb->type_attr_map[scontext->type - 1];
    tattr = &policydb->type_attr_map[tcontext->type - 1];
    ebitmap_for_each_bit(sattr, snode, i) {
        if (!ebitmap_node_get_bit(snode, i))
            continue;
        ebitmap_for_each_bit(tattr, tnode, j) {
            if (!ebitmap_node_get_bit(tnode, j))
                continue;
            avkey.source_type = i + 1;
            avkey.target_type = j + 1;
            for (node =
                 avtab_search_node(&policydb->te_avtab, &avkey);
                 node != NULL;
                 node =
                 avtab_search_node_next(node, avkey.specified)) {
                if (node->key.specified == AVTAB_ALLOWED)
                    avd->allowed |= node->datum.data;
                else if (node->key.specified ==
                     AVTAB_AUDITALLOW)
                    avd->auditallow |= node->datum.data;
                else if (node->key.specified == AVTAB_AUDITDENY)
                    avd->auditdeny &= node->datum.data;
            }

            /* Check conditional av table for additional permissions */
            cond_compute_av(&policydb->te_cond_avtab, &avkey, avd);

        }
    }

    if (requested & ~avd->allowed) {
        *reason |= SEPOL_COMPUTEAV_TE;
        requested &= avd->allowed;
    }

    /* 
     * Remove any permissions prohibited by a constraint (this includes
     * the MLS policy).
     */
    constraint = tclass_datum->constraints;
    while (constraint) {
        if ((constraint->permissions & (avd->allowed)) &&
            !constraint_expr_eval_reason(scontext, tcontext, NULL,
                      tclass, constraint, r_buf, flags)) {
            avd->allowed =
                (avd->allowed) & ~(constraint->permissions);
        }
        constraint = constraint->next;
    }

    if (requested & ~avd->allowed) {
        *reason |= SEPOL_COMPUTEAV_CONS;
        requested &= avd->allowed;
    }

    /* 
     * If checking process transition permission and the
     * role is changing, then check the (current_role, new_role) 
     * pair.
     */
    if (tclass == SECCLASS_PROCESS &&
        (avd->allowed & (PROCESS__TRANSITION | PROCESS__DYNTRANSITION)) &&
        scontext->role != tcontext->role) {
        for (ra = policydb->role_allow; ra; ra = ra->next) {
            if (scontext->role == ra->role &&
                tcontext->role == ra->new_role)
                break;
        }
        if (!ra)
            avd->allowed = (avd->allowed) & ~(PROCESS__TRANSITION |
                              PROCESS__DYNTRANSITION);
    }

    if (requested & ~avd->allowed) {
        *reason |= SEPOL_COMPUTEAV_RBAC;
        requested &= avd->allowed;
    }

    return 0;
}

int hidden sepol_validate_transition(sepol_security_id_t oldsid,
                     sepol_security_id_t newsid,
                     sepol_security_id_t tasksid,
                     sepol_security_class_t tclass)
{
    context_struct_t *ocontext;
    context_struct_t *ncontext;
    context_struct_t *tcontext;
    class_datum_t *tclass_datum;
    constraint_node_t *constraint;

    if (!tclass || tclass > policydb->p_classes.nprim) {
        LOGD( "unrecognized class %d", tclass);
        return -EINVAL;
    }
    tclass_datum = policydb->class_val_to_struct[tclass - 1];

    ocontext = sepol_sidtab_search(sidtab, oldsid);
    if (!ocontext) {
        LOGD( "unrecognized SID %d", oldsid);
        return -EINVAL;
    }

    ncontext = sepol_sidtab_search(sidtab, newsid);
    if (!ncontext) {
        LOGD( "unrecognized SID %d", newsid);
        return -EINVAL;
    }

    tcontext = sepol_sidtab_search(sidtab, tasksid);
    if (!tcontext) {
        LOGD( "unrecognized SID %d", tasksid);
        return -EINVAL;
    }

    constraint = tclass_datum->validatetrans;
    while (constraint) {
        if (!constraint_expr_eval_reason(ocontext, ncontext, tcontext,
                      0, constraint, NULL, 0)) {
            return -EPERM;
        }
        constraint = constraint->next;
    }

    return 0;
}

int hidden sepol_compute_av_reason(sepol_security_id_t ssid,
                   sepol_security_id_t tsid,
                   sepol_security_class_t tclass,
                   sepol_access_vector_t requested,
                   struct sepol_av_decision *avd,
                   unsigned int *reason)
{
    context_struct_t *scontext = 0, *tcontext = 0;
    int rc = 0;

    scontext = sepol_sidtab_search(sidtab, ssid);
    if (!scontext) {
        LOGD( "unrecognized SID %d", ssid);
        rc = -EINVAL;
        goto out;
    }
    tcontext = sepol_sidtab_search(sidtab, tsid);
    if (!tcontext) {
        LOGD( "unrecognized SID %d", tsid);
        rc = -EINVAL;
        goto out;
    }

    rc = context_struct_compute_av(scontext, tcontext, tclass,
                    requested, avd, reason, NULL, 0);
      out:
    return rc;
}

/*
 * sepol_compute_av_reason_buffer - the reason buffer is malloc'd to
 * REASON_BUF_SIZE. If the buffer size is exceeded, then it is realloc'd
 * in the constraint_expr_eval_reason() function.
 */
int hidden sepol_compute_av_reason_buffer(sepol_security_id_t ssid,
                   sepol_security_id_t tsid,
                   sepol_security_class_t tclass,
                   sepol_access_vector_t requested,
                   struct sepol_av_decision *avd,
                   unsigned int *reason,
                   char **reason_buf,
                   unsigned int flags)
{
    context_struct_t *scontext = 0, *tcontext = 0;
    int rc = 0;

    scontext = sepol_sidtab_search(sidtab, ssid);
    if (!scontext) {
        LOGD( "unrecognized SID %d", ssid);
        rc = -EINVAL;
        goto out;
    }
    tcontext = sepol_sidtab_search(sidtab, tsid);
    if (!tcontext) {
        LOGD( "unrecognized SID %d", tsid);
        rc = -EINVAL;
        goto out;
    }

    /*
     * Set the buffer to NULL as constraints may not be processed.
     * If a buffer is required, then the routines in
     * constraint_expr_eval_reason will realloc in REASON_BUF_SIZE
     * chunks (as it gets called for each constraint processed).
     * We just make sure these start from zero.
     */
    *reason_buf = NULL;
    reason_buf_used = 0;
    reason_buf_len = 0;

    rc = context_struct_compute_av(scontext, tcontext, tclass,
                       requested, avd, reason, reason_buf, flags);
out:
    return rc;
}

int hidden sepol_compute_av(sepol_security_id_t ssid,
                sepol_security_id_t tsid,
                sepol_security_class_t tclass,
                sepol_access_vector_t requested,
                struct sepol_av_decision *avd)
{
    unsigned int reason = 0;
    return sepol_compute_av_reason(ssid, tsid, tclass, requested, avd,
                       &reason);
}

/*
 * Return a class ID associated with the class string specified by
 * class_name.
 */
int hidden sepol_string_to_security_class(const char *class_name,
            sepol_security_class_t *tclass)
{
    char *class = NULL;
    sepol_security_class_t id;

    for (id = 1;; id++) {
        class = policydb->p_class_val_to_name[id - 1];
        if (class == NULL) {
            LOGD( "could not convert %s to class id", class_name);
            return STATUS_ERR;
        }
        if ((strcmp(class, class_name)) == 0) {
            *tclass = id;
            return STATUS_SUCCESS;
        }
    }
}

/*
 * Return access vector bit associated with the class ID and permission
 * string.
 */
int hidden sepol_string_to_av_perm(sepol_security_class_t tclass,
                    const char *perm_name,
                    sepol_access_vector_t *av)
{
    class_datum_t *tclass_datum;
    perm_datum_t *perm_datum;

    if (!tclass || tclass > policydb->p_classes.nprim) {
        LOGD( "unrecognized class %d", tclass);
        return -EINVAL;
    }
    tclass_datum = policydb->class_val_to_struct[tclass - 1];

    /* Check for unique perms then the common ones (if any) */
    perm_datum = (perm_datum_t *)
            hashtab_search(tclass_datum->permissions.table,
            (hashtab_key_t)perm_name);
    if (perm_datum != NULL) {
        *av = 0x1 << (perm_datum->s.value - 1);
        return STATUS_SUCCESS;
    }

    if (tclass_datum->comdatum == NULL)
        goto out;

    perm_datum = (perm_datum_t *)
            hashtab_search(tclass_datum->comdatum->permissions.table,
            (hashtab_key_t)perm_name);

    if (perm_datum != NULL) {
        *av = 0x1 << (perm_datum->s.value - 1);
        return STATUS_SUCCESS;
    }
out:
    LOGD( "could not convert %s to av bit", perm_name);
    return STATUS_ERR;
}

/*
 * Write the security context string representation of 
 * the context associated with `sid' into a dynamically
 * allocated string of the correct size.  Set `*scontext'
 * to point to this string and set `*scontext_len' to
 * the length of the string.
 */
int hidden sepol_sid_to_context(sepol_security_id_t sid,
                sepol_security_context_t * scontext,
                size_t * scontext_len)
{
    context_struct_t *context;
    int rc = 0;

    context = sepol_sidtab_search(sidtab, sid);
    if (!context) {
        LOGD( "unrecognized SID %d", sid);
        rc = -EINVAL;
        goto out;
    }
    rc = context_to_string(NULL, policydb, context, scontext, scontext_len);
      out:
    return rc;

}

/*
 * Return a SID associated with the security context that
 * has the string representation specified by `scontext'.
 */
int hidden sepol_context_to_sid(const sepol_security_context_t scontext,
                size_t scontext_len, sepol_security_id_t * sid)
{

    context_struct_t *context = NULL;

    /* First, create the context */
    if (context_from_string(NULL, policydb, &context,
                scontext, scontext_len) < 0)
        goto err;

    /* Obtain the new sid */
    if (sid && (sepol_sidtab_context_to_sid(sidtab, context, sid) < 0))
        goto err;

    context_destroy(context);
    free(context);
    return STATUS_SUCCESS;

      err:
    if (context) {
        context_destroy(context);
        free(context);
    }
    LOGD( "could not convert %s to sid", scontext);
    return STATUS_ERR;
}

static inline int compute_sid_handle_invalid_context(context_struct_t *
                             scontext,
                             context_struct_t *
                             tcontext,
                             sepol_security_class_t
                             tclass,
                             context_struct_t *
                             newcontext)
{
    if (selinux_enforcing) {
        return -EACCES;
    } else {
        sepol_security_context_t s, t, n;
        size_t slen, tlen, nlen;

        context_to_string(NULL, policydb, scontext, &s, &slen);
        context_to_string(NULL, policydb, tcontext, &t, &tlen);
        context_to_string(NULL, policydb, newcontext, &n, &nlen);
        LOGD( "invalid context %s for "
            "scontext=%s tcontext=%s tclass=%s",
            n, s, t, policydb->p_class_val_to_name[tclass - 1]);
        free(s);
        free(t);
        free(n);
        return 0;
    }
}

static int sepol_compute_sid(sepol_security_id_t ssid,
                 sepol_security_id_t tsid,
                 sepol_security_class_t tclass,
                 uint32_t specified, sepol_security_id_t * out_sid)
{
    context_struct_t *scontext = 0, *tcontext = 0, newcontext;
    struct role_trans *roletr = 0;
    avtab_key_t avkey;
    avtab_datum_t *avdatum;
    avtab_ptr_t node;
    int rc = 0;

    scontext = sepol_sidtab_search(sidtab, ssid);
    if (!scontext) {
        LOGD( "unrecognized SID %d", ssid);
        rc = -EINVAL;
        goto out;
    }
    tcontext = sepol_sidtab_search(sidtab, tsid);
    if (!tcontext) {
        LOGD( "unrecognized SID %d", tsid);
        rc = -EINVAL;
        goto out;
    }

    context_init(&newcontext);

    /* Set the user identity. */
    switch (specified) {
    case AVTAB_TRANSITION:
    case AVTAB_CHANGE:
        /* Use the process user identity. */
        newcontext.user = scontext->user;
        break;
    case AVTAB_MEMBER:
        /* Use the related object owner. */
        newcontext.user = tcontext->user;
        break;
    }

    /* Set the role and type to default values. */
    switch (tclass) {
    case SECCLASS_PROCESS:
        /* Use the current role and type of process. */
        newcontext.role = scontext->role;
        newcontext.type = scontext->type;
        break;
    default:
        /* Use the well-defined object role. */
        newcontext.role = OBJECT_R_VAL;
        /* Use the type of the related object. */
        newcontext.type = tcontext->type;
    }

    /* Look for a type transition/member/change rule. */
    avkey.source_type = scontext->type;
    avkey.target_type = tcontext->type;
    avkey.target_class = tclass;
    avkey.specified = specified;
    avdatum = avtab_search(&policydb->te_avtab, &avkey);

    /* If no permanent rule, also check for enabled conditional rules */
    if (!avdatum) {
        node = avtab_search_node(&policydb->te_cond_avtab, &avkey);
        for (; node != NULL;
             node = avtab_search_node_next(node, specified)) {
            if (node->key.specified & AVTAB_ENABLED) {
                avdatum = &node->datum;
                break;
            }
        }
    }

    if (avdatum) {
        /* Use the type from the type transition/member/change rule. */
        newcontext.type = avdatum->data;
    }

    /* Check for class-specific changes. */
    switch (tclass) {
    case SECCLASS_PROCESS:
        if (specified & AVTAB_TRANSITION) {
            /* Look for a role transition rule. */
            for (roletr = policydb->role_tr; roletr;
                 roletr = roletr->next) {
                if (roletr->role == scontext->role &&
                    roletr->type == tcontext->type) {
                    /* Use the role transition rule. */
                    newcontext.role = roletr->new_role;
                    break;
                }
            }
        }
        break;
    default:
        break;
    }

    /* Set the MLS attributes.
       This is done last because it may allocate memory. */
    rc = mls_compute_sid(policydb, scontext, tcontext, tclass, specified,
                 &newcontext);
    if (rc)
        goto out;

    /* Check the validity of the context. */
    if (!policydb_context_isvalid(policydb, &newcontext)) {
        rc = compute_sid_handle_invalid_context(scontext,
                            tcontext,
                            tclass, &newcontext);
        if (rc)
            goto out;
    }
    /* Obtain the sid for the context. */
    rc = sepol_sidtab_context_to_sid(sidtab, &newcontext, out_sid);
      out:
    context_destroy(&newcontext);
    return rc;
}

/*
 * Compute a SID to use for labeling a new object in the 
 * class `tclass' based on a SID pair.  
 */
int hidden sepol_transition_sid(sepol_security_id_t ssid,
                sepol_security_id_t tsid,
                sepol_security_class_t tclass,
                sepol_security_id_t * out_sid)
{
    return sepol_compute_sid(ssid, tsid, tclass, AVTAB_TRANSITION, out_sid);
}

/*
 * Compute a SID to use when selecting a member of a 
 * polyinstantiated object of class `tclass' based on 
 * a SID pair.
 */
int hidden sepol_member_sid(sepol_security_id_t ssid,
                sepol_security_id_t tsid,
                sepol_security_class_t tclass,
                sepol_security_id_t * out_sid)
{
    return sepol_compute_sid(ssid, tsid, tclass, AVTAB_MEMBER, out_sid);
}

/*
 * Compute a SID to use for relabeling an object in the 
 * class `tclass' based on a SID pair.  
 */
int hidden sepol_change_sid(sepol_security_id_t ssid,
                sepol_security_id_t tsid,
                sepol_security_class_t tclass,
                sepol_security_id_t * out_sid)
{
    return sepol_compute_sid(ssid, tsid, tclass, AVTAB_CHANGE, out_sid);
}

/*
 * Verify that each permission that is defined under the
 * existing policy is still defined with the same value
 * in the new policy.
 */
static int validate_perm(hashtab_key_t key, hashtab_datum_t datum, void *p)
{
    hashtab_t h;
    perm_datum_t *perdatum, *perdatum2;

    h = (hashtab_t) p;
    perdatum = (perm_datum_t *) datum;

    perdatum2 = (perm_datum_t *) hashtab_search(h, key);
    if (!perdatum2) {
        LOGD( "permission %s disappeared", key);
        return -1;
    }
    if (perdatum->s.value != perdatum2->s.value) {
        LOGD( "the value of permissions %s changed", key);
        return -1;
    }
    return 0;
}

/*
 * Verify that each class that is defined under the
 * existing policy is still defined with the same 
 * attributes in the new policy.
 */
static int validate_class(hashtab_key_t key, hashtab_datum_t datum, void *p)
{
    policydb_t *newp;
    class_datum_t *cladatum, *cladatum2;

    newp = (policydb_t *) p;
    cladatum = (class_datum_t *) datum;

    cladatum2 =
        (class_datum_t *) hashtab_search(newp->p_classes.table, key);
    if (!cladatum2) {
        LOGD( "class %s disappeared", key);
        return -1;
    }
    if (cladatum->s.value != cladatum2->s.value) {
        LOGD( "the value of class %s changed", key);
        return -1;
    }
    if ((cladatum->comdatum && !cladatum2->comdatum) ||
        (!cladatum->comdatum && cladatum2->comdatum)) {
        LOGD( "the inherits clause for the access "
            "vector definition for class %s changed", key);
        return -1;
    }
    if (cladatum->comdatum) {
        if (hashtab_map
            (cladatum->comdatum->permissions.table, validate_perm,
             cladatum2->comdatum->permissions.table)) {
            LOGD(
                " in the access vector definition "
                "for class %s\n", key);
            return -1;
        }
    }
    if (hashtab_map(cladatum->permissions.table, validate_perm,
            cladatum2->permissions.table)) {
        LOGD( " in access vector definition for class %s", key);
        return -1;
    }
    return 0;
}

/* Clone the SID into the new SID table. */
static int clone_sid(sepol_security_id_t sid,
             context_struct_t * context, void *arg)
{
    sidtab_t *s = arg;

    return sepol_sidtab_insert(s, sid, context);
}

static inline int convert_context_handle_invalid_context(context_struct_t *
                             context)
{
    if (selinux_enforcing) {
        return -EINVAL;
    } else {
        sepol_security_context_t s;
        size_t len;

        context_to_string(NULL, policydb, context, &s, &len);
        LOGD( "context %s is invalid", s);
        free(s);
        return 0;
    }
}

typedef struct {
    policydb_t *oldp;
    policydb_t *newp;
} convert_context_args_t;

/*
 * Convert the values in the security context
 * structure `c' from the values specified
 * in the policy `p->oldp' to the values specified
 * in the policy `p->newp'.  Verify that the
 * context is valid under the new policy.
 */
static int convert_context(sepol_security_id_t key __attribute__ ((unused)),
               context_struct_t * c, void *p)
{
    convert_context_args_t *args;
    context_struct_t oldc;
    role_datum_t *role;
    type_datum_t *typdatum;
    user_datum_t *usrdatum;
    sepol_security_context_t s;
    size_t len;
    int rc = -EINVAL;

    args = (convert_context_args_t *) p;

    if (context_cpy(&oldc, c))
        return -ENOMEM;

    /* Convert the user. */
    usrdatum = (user_datum_t *) hashtab_search(args->newp->p_users.table,
                           args->oldp->
                           p_user_val_to_name[c->user -
                                      1]);

    if (!usrdatum) {
        goto bad;
    }
    c->user = usrdatum->s.value;

    /* Convert the role. */
    role = (role_datum_t *) hashtab_search(args->newp->p_roles.table,
                           args->oldp->
                           p_role_val_to_name[c->role - 1]);
    if (!role) {
        goto bad;
    }
    c->role = role->s.value;

    /* Convert the type. */
    typdatum = (type_datum_t *)
        hashtab_search(args->newp->p_types.table,
               args->oldp->p_type_val_to_name[c->type - 1]);
    if (!typdatum) {
        goto bad;
    }
    c->type = typdatum->s.value;

    rc = mls_convert_context(args->oldp, args->newp, c);
    if (rc)
        goto bad;

    /* Check the validity of the new context. */
    if (!policydb_context_isvalid(args->newp, c)) {
        rc = convert_context_handle_invalid_context(&oldc);
        if (rc)
            goto bad;
    }

    context_destroy(&oldc);
    return 0;

      bad:
    context_to_string(NULL, policydb, &oldc, &s, &len);
    context_destroy(&oldc);
    LOGD( "invalidating context %s", s);
    free(s);
    return rc;
}

/* Reading from a policy "file". */
int hidden next_entry(void *buf, struct policy_file *fp, size_t bytes)
{
    size_t nread;

    switch (fp->type) {
    case PF_USE_STDIO:
        nread = fread(buf, bytes, 1, fp->fp);

        if (nread != 1)
            return -1;
        break;
    case PF_USE_MEMORY:
        if (bytes > fp->len)
            return -1;
        memcpy(buf, fp->data, bytes);
        fp->data += bytes;
        fp->len -= bytes;
        break;
    default:
        return -1;
    }
    return 0;
}

size_t hidden put_entry(const void *ptr, size_t size, size_t n,
            struct policy_file *fp)
{
    size_t bytes = size * n;

    switch (fp->type) {
    case PF_USE_STDIO:
        return fwrite(ptr, size, n, fp->fp);
    case PF_USE_MEMORY:
        if (bytes > fp->len) {
            errno = ENOSPC;
            return 0;
        }

        memcpy(fp->data, ptr, bytes);
        fp->data += bytes;
        fp->len -= bytes;
        return n;
    case PF_LEN:
        fp->len += bytes;
        return n;
    default:
        return 0;
    }
    return 0;
}

/*
 * Read a new set of configuration data from 
 * a policy database binary representation file.
 *
 * Verify that each class that is defined under the
 * existing policy is still defined with the same 
 * attributes in the new policy.  
 *
 * Convert the context structures in the SID table to the
 * new representation and verify that all entries
 * in the SID table are valid under the new policy. 
 *
 * Change the active policy database to use the new 
 * configuration data.  
 *
 * Reset the access vector cache.
 */
int hidden sepol_load_policy(void *data, size_t len)
{
    policydb_t oldpolicydb, newpolicydb;
    sidtab_t oldsidtab, newsidtab;
    convert_context_args_t args;
    int rc = 0;
    struct policy_file file, *fp;

    policy_file_init(&file);
    file.type = PF_USE_MEMORY;
    file.data = data;
    file.len = len;
    fp = &file;

    if (policydb_init(&newpolicydb))
        return -ENOMEM;

    if (policydb_read(&newpolicydb, fp, 1)) {
        policydb_destroy(&mypolicydb);
        return -EINVAL;
    }

    sepol_sidtab_init(&newsidtab);

    /* Verify that the existing classes did not change. */
    if (hashtab_map
        (policydb->p_classes.table, validate_class, &newpolicydb)) {
        LOGD( "the definition of an existing class changed");
        rc = -EINVAL;
        goto err;
    }

    /* Clone the SID table. */
    sepol_sidtab_shutdown(sidtab);
    if (sepol_sidtab_map(sidtab, clone_sid, &newsidtab)) {
        rc = -ENOMEM;
        goto err;
    }

    /* Convert the internal representations of contexts 
       in the new SID table and remove invalid SIDs. */
    args.oldp = policydb;
    args.newp = &newpolicydb;
    sepol_sidtab_map_remove_on_error(&newsidtab, convert_context, &args);

    /* Save the old policydb and SID table to free later. */
    memcpy(&oldpolicydb, policydb, sizeof *policydb);
    sepol_sidtab_set(&oldsidtab, sidtab);

    /* Install the new policydb and SID table. */
    memcpy(policydb, &newpolicydb, sizeof *policydb);
    sepol_sidtab_set(sidtab, &newsidtab);

    /* Free the old policydb and SID table. */
    policydb_destroy(&oldpolicydb);
    sepol_sidtab_destroy(&oldsidtab);

    return 0;

      err:
    sepol_sidtab_destroy(&newsidtab);
    policydb_destroy(&newpolicydb);
    return rc;

}

/*
 * Return the SIDs to use for an unlabeled file system
 * that is being mounted from the device with the
 * the kdevname `name'.  The `fs_sid' SID is returned for 
 * the file system and the `file_sid' SID is returned
 * for all files within that file system.
 */
int hidden sepol_fs_sid(char *name,
            sepol_security_id_t * fs_sid,
            sepol_security_id_t * file_sid)
{
    int rc = 0;
    ocontext_t *c;

    c = policydb->ocontexts[OCON_FS];
    while (c) {
        if (strcmp(c->u.name, name) == 0)
            break;
        c = c->next;
    }

    if (c) {
        if (!c->sid[0] || !c->sid[1]) {
            rc = sepol_sidtab_context_to_sid(sidtab,
                             &c->context[0],
                             &c->sid[0]);
            if (rc)
                goto out;
            rc = sepol_sidtab_context_to_sid(sidtab,
                             &c->context[1],
                             &c->sid[1]);
            if (rc)
                goto out;
        }
        *fs_sid = c->sid[0];
        *file_sid = c->sid[1];
    } else {
        *fs_sid = SECINITSID_FS;
        *file_sid = SECINITSID_FILE;
    }

      out:
    return rc;
}

/*
 * Return the SID of the port specified by
 * `domain', `type', `protocol', and `port'.
 */
int hidden sepol_port_sid(uint16_t domain __attribute__ ((unused)),
              uint16_t type __attribute__ ((unused)),
              uint8_t protocol,
              uint16_t port, sepol_security_id_t * out_sid)
{
    ocontext_t *c;
    int rc = 0;

    c = policydb->ocontexts[OCON_PORT];
    while (c) {
        if (c->u.port.protocol == protocol &&
            c->u.port.low_port <= port && c->u.port.high_port >= port)
            break;
        c = c->next;
    }

    if (c) {
        if (!c->sid[0]) {
            rc = sepol_sidtab_context_to_sid(sidtab,
                             &c->context[0],
                             &c->sid[0]);
            if (rc)
                goto out;
        }
        *out_sid = c->sid[0];
    } else {
        *out_sid = SECINITSID_PORT;
    }

      out:
    return rc;
}

/*
 * Return the SIDs to use for a network interface
 * with the name `name'.  The `if_sid' SID is returned for 
 * the interface and the `msg_sid' SID is returned as 
 * the default SID for messages received on the
 * interface.
 */
int hidden sepol_netif_sid(char *name,
               sepol_security_id_t * if_sid,
               sepol_security_id_t * msg_sid)
{
    int rc = 0;
    ocontext_t *c;

    c = policydb->ocontexts[OCON_NETIF];
    while (c) {
        if (strcmp(name, c->u.name) == 0)
            break;
        c = c->next;
    }

    if (c) {
        if (!c->sid[0] || !c->sid[1]) {
            rc = sepol_sidtab_context_to_sid(sidtab,
                             &c->context[0],
                             &c->sid[0]);
            if (rc)
                goto out;
            rc = sepol_sidtab_context_to_sid(sidtab,
                             &c->context[1],
                             &c->sid[1]);
            if (rc)
                goto out;
        }
        *if_sid = c->sid[0];
        *msg_sid = c->sid[1];
    } else {
        *if_sid = SECINITSID_NETIF;
        *msg_sid = SECINITSID_NETMSG;
    }

      out:
    return rc;
}

static int match_ipv6_addrmask(uint32_t * input, uint32_t * addr,
                   uint32_t * mask)
{
    int i, fail = 0;

    for (i = 0; i < 4; i++)
        if (addr[i] != (input[i] & mask[i])) {
            fail = 1;
            break;
        }

    return !fail;
}

/*
 * Return the SID of the node specified by the address
 * `addrp' where `addrlen' is the length of the address
 * in bytes and `domain' is the communications domain or
 * address family in which the address should be interpreted.
 */
int hidden sepol_node_sid(uint16_t domain,
              void *addrp,
              size_t addrlen, sepol_security_id_t * out_sid)
{
    int rc = 0;
    ocontext_t *c;

    switch (domain) {
    case AF_INET:{
            uint32_t addr;

            if (addrlen != sizeof(uint32_t)) {
                rc = -EINVAL;
                goto out;
            }

            addr = *((uint32_t *) addrp);

            c = policydb->ocontexts[OCON_NODE];
            while (c) {
                if (c->u.node.addr == (addr & c->u.node.mask))
                    break;
                c = c->next;
            }
            break;
        }

    case AF_INET6:
        if (addrlen != sizeof(uint64_t) * 2) {
            rc = -EINVAL;
            goto out;
        }

        c = policydb->ocontexts[OCON_NODE6];
        while (c) {
            if (match_ipv6_addrmask(addrp, c->u.node6.addr,
                        c->u.node6.mask))
                break;
            c = c->next;
        }
        break;

    default:
        *out_sid = SECINITSID_NODE;
        goto out;
    }

    if (c) {
        if (!c->sid[0]) {
            rc = sepol_sidtab_context_to_sid(sidtab,
                             &c->context[0],
                             &c->sid[0]);
            if (rc)
                goto out;
        }
        *out_sid = c->sid[0];
    } else {
        *out_sid = SECINITSID_NODE;
    }

      out:
    return rc;
}

/*
 * Generate the set of SIDs for legal security contexts
 * for a given user that can be reached by `fromsid'.
 * Set `*sids' to point to a dynamically allocated 
 * array containing the set of SIDs.  Set `*nel' to the
 * number of elements in the array.
 */
#define SIDS_NEL 25

int hidden sepol_get_user_sids(sepol_security_id_t fromsid,
                   char *username,
                   sepol_security_id_t ** sids, uint32_t * nel)
{
    context_struct_t *fromcon, usercon;
    sepol_security_id_t *mysids, *mysids2, sid;
    uint32_t mynel = 0, maxnel = SIDS_NEL;
    user_datum_t *user;
    role_datum_t *role;
    struct sepol_av_decision avd;
    int rc = 0;
    unsigned int i, j, reason;
    ebitmap_node_t *rnode, *tnode;

    fromcon = sepol_sidtab_search(sidtab, fromsid);
    if (!fromcon) {
        rc = -EINVAL;
        goto out;
    }

    user = (user_datum_t *) hashtab_search(policydb->p_users.table,
                           username);
    if (!user) {
        rc = -EINVAL;
        goto out;
    }
    usercon.user = user->s.value;

    mysids = malloc(maxnel * sizeof(sepol_security_id_t));
    if (!mysids) {
        rc = -ENOMEM;
        goto out;
    }
    memset(mysids, 0, maxnel * sizeof(sepol_security_id_t));

    ebitmap_for_each_bit(&user->roles.roles, rnode, i) {
        if (!ebitmap_node_get_bit(rnode, i))
            continue;
        role = policydb->role_val_to_struct[i];
        usercon.role = i + 1;
        ebitmap_for_each_bit(&role->types.types, tnode, j) {
            if (!ebitmap_node_get_bit(tnode, j))
                continue;
            usercon.type = j + 1;
            if (usercon.type == fromcon->type)
                continue;

            if (mls_setup_user_range
                (fromcon, user, &usercon, policydb->mls))
                continue;

            rc = context_struct_compute_av(fromcon, &usercon,
                               SECCLASS_PROCESS,
                               PROCESS__TRANSITION,
                               &avd, &reason, NULL, 0);
            if (rc || !(avd.allowed & PROCESS__TRANSITION))
                continue;
            rc = sepol_sidtab_context_to_sid(sidtab, &usercon,
                             &sid);
            if (rc) {
                free(mysids);
                goto out;
            }
            if (mynel < maxnel) {
                mysids[mynel++] = sid;
            } else {
                maxnel += SIDS_NEL;
                mysids2 =
                    malloc(maxnel *
                       sizeof(sepol_security_id_t));

                if (!mysids2) {
                    rc = -ENOMEM;
                    free(mysids);
                    goto out;
                }
                memset(mysids2, 0,
                       maxnel * sizeof(sepol_security_id_t));
                memcpy(mysids2, mysids,
                       mynel * sizeof(sepol_security_id_t));
                free(mysids);
                mysids = mysids2;
                mysids[mynel++] = sid;
            }
        }
    }

    *sids = mysids;
    *nel = mynel;

      out:
    return rc;
}

/*
 * Return the SID to use for a file in a filesystem
 * that cannot support a persistent label mapping or use another
 * fixed labeling behavior like transition SIDs or task SIDs.
 */
int hidden sepol_genfs_sid(const char *fstype,
               char *path,
               sepol_security_class_t sclass,
               sepol_security_id_t * sid)
{
    size_t len;
    genfs_t *genfs;
    ocontext_t *c;
    int rc = 0, cmp = 0;

    for (genfs = policydb->genfs; genfs; genfs = genfs->next) {
        cmp = strcmp(fstype, genfs->fstype);
        if (cmp <= 0)
            break;
    }

    if (!genfs || cmp) {
        *sid = SECINITSID_UNLABELED;
        rc = -ENOENT;
        goto out;
    }

    for (c = genfs->head; c; c = c->next) {
        len = strlen(c->u.name);
        if ((!c->v.sclass || sclass == c->v.sclass) &&
            (strncmp(c->u.name, path, len) == 0))
            break;
    }

    if (!c) {
        *sid = SECINITSID_UNLABELED;
        rc = -ENOENT;
        goto out;
    }

    if (!c->sid[0]) {
        rc = sepol_sidtab_context_to_sid(sidtab,
                         &c->context[0], &c->sid[0]);
        if (rc)
            goto out;
    }

    *sid = c->sid[0];
      out:
    return rc;
}

int hidden sepol_fs_use(const char *fstype,
            unsigned int *behavior, sepol_security_id_t * sid)
{
    int rc = 0;
    ocontext_t *c;

    c = policydb->ocontexts[OCON_FSUSE];
    while (c) {
        if (strcmp(fstype, c->u.name) == 0)
            break;
        c = c->next;
    }

    if (c) {
        *behavior = c->v.behavior;
        if (!c->sid[0]) {
            rc = sepol_sidtab_context_to_sid(sidtab,
                             &c->context[0],
                             &c->sid[0]);
            if (rc)
                goto out;
        }
        *sid = c->sid[0];
    } else {
        rc = sepol_genfs_sid(fstype, "/", SECCLASS_DIR, sid);
        if (rc) {
            *behavior = SECURITY_FS_USE_NONE;
            rc = 0;
        } else {
            *behavior = SECURITY_FS_USE_GENFS;
        }
    }

      out:
    return rc;
}

/* FLASK */