root/crypto/dist/ipsec-tools/src/racoon/security.c
/*
 * Copyright (C) 2005 International Business Machines Corporation
 * Copyright (c) 2005 by Trusted Computer Solutions, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the project nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */


#include "config.h"

#include <sys/types.h>

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <selinux/selinux.h>
#include <selinux/flask.h>
#include <selinux/av_permissions.h>
#include <selinux/avc.h>
#include <selinux/context.h>

#include "var.h"
#include "vmbuf.h"
#include "misc.h"
#include "plog.h"

#include "isakmp_var.h"
#include "isakmp.h"
#include "ipsec_doi.h"
#include "policy.h"
#include "proposal.h"
#include "strnames.h"
#include "handler.h"

/* 
 * Get the security context information from SA.
 */
int
get_security_context(sa, p)
        vchar_t *sa;
        struct policyindex *p;
{
        int len = 0;
        int flag, type = 0;
        uint16_t lorv;
        caddr_t bp;
        vchar_t *pbuf = NULL;
        vchar_t *tbuf = NULL;
        struct isakmp_parse_t *pa;
        struct isakmp_parse_t *ta;
        struct isakmp_pl_p *prop;
        struct isakmp_pl_t *trns;
        struct isakmp_data *d;
        struct ipsecdoi_sa_b *sab = (struct ipsecdoi_sa_b *)sa->v;
        
        /* check SA payload size */
        if (sa->l < sizeof(*sab)) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "Invalid SA length = %zu.\n", sa->l);
                return -1;
        }

        bp = (caddr_t)(sab + 1); /* here bp points to first proposal payload */
        len = sa->l - sizeof(*sab);

        pbuf = isakmp_parsewoh(ISAKMP_NPTYPE_P, (struct isakmp_gen *)bp, len);
        if (pbuf == NULL)
                return -1;

        pa = (struct isakmp_parse_t *)pbuf->v; 
        /* check the value of next payload */
        if (pa->type != ISAKMP_NPTYPE_P) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "Invalid payload type=%u\n", pa->type);
                vfree(pbuf);
                return -1;
        }

        if (pa->len == 0) {
                plog(LLV_ERROR, LOCATION, NULL,
                "invalid proposal with length %d\n", pa->len);
                vfree(pbuf);
                return -1;
        }

        /* our first proposal */
        prop = (struct isakmp_pl_p *)pa->ptr;

        /* now get transform */
        bp = (caddr_t)prop + sizeof(struct isakmp_pl_p) + prop->spi_size;
        len = ntohs(prop->h.len) - 
                (sizeof(struct isakmp_pl_p) + prop->spi_size);
        tbuf = isakmp_parsewoh(ISAKMP_NPTYPE_T, (struct isakmp_gen *)bp, len);
        if (tbuf == NULL)
                return -1;

        ta = (struct isakmp_parse_t *)tbuf->v;
        if (ta->type != ISAKMP_NPTYPE_T) {
                plog(LLV_ERROR, LOCATION, NULL,
                     "Invalid payload type=%u\n", ta->type);
                return -1;
        }
        
        trns = (struct isakmp_pl_t *)ta->ptr;

        len = ntohs(trns->h.len) - sizeof(struct isakmp_pl_t);
        d = (struct isakmp_data *)((caddr_t)trns + sizeof(struct isakmp_pl_t));

        while (len > 0) {
                type = ntohs(d->type) & ~ISAKMP_GEN_MASK;
                flag = ntohs(d->type) & ISAKMP_GEN_MASK;
                lorv = ntohs(d->lorv);

                if (type != IPSECDOI_ATTR_SECCTX) {
                        if (flag) {
                                len -= sizeof(*d);
                                d = (struct isakmp_data *)((char *)d 
                                     + sizeof(*d));
                        } else {
                                len -= (sizeof(*d) + lorv);
                                d = (struct isakmp_data *)((caddr_t)d
                                     + sizeof(*d) + lorv);
                        }
                } else {
                        flag = ntohs(d->type & ISAKMP_GEN_MASK);
                        if (flag) {
                                plog(LLV_ERROR, LOCATION, NULL,
                                     "SECCTX must be in TLV.\n");
                                return -1;
                        }
                        memcpy(&p->sec_ctx, d + 1, lorv);
                        p->sec_ctx.ctx_strlen = ntohs(p->sec_ctx.ctx_strlen);
                        return 0;
                }
        }
        return 0;
}

void
set_secctx_in_proposal(iph2, spidx)
        struct ph2handle *iph2;
        struct policyindex spidx;
{
        iph2->proposal->sctx.ctx_doi = spidx.sec_ctx.ctx_doi;
        iph2->proposal->sctx.ctx_alg = spidx.sec_ctx.ctx_alg;
        iph2->proposal->sctx.ctx_strlen = spidx.sec_ctx.ctx_strlen;
                memcpy(iph2->proposal->sctx.ctx_str, spidx.sec_ctx.ctx_str,
                        spidx.sec_ctx.ctx_strlen);
}


/*
 * function:    init_avc
 * description: function performs the steps necessary to initialize the
 *              userspace avc.
 * input:       void
 * return:      0       if avc was successfully initialized
 *              1       if the avc could not be initialized
 */

static int mls_ready = 0;

void
init_avc(void)
{
        if (!is_selinux_mls_enabled()) {
                plog(LLV_ERROR, LOCATION, NULL, "racoon: MLS support is not"
                                " enabled.\n");
                return;
        }

        if (avc_init("racoon", NULL, NULL, NULL, NULL) == 0)
                mls_ready = 1;
        else
                plog(LLV_ERROR, LOCATION, NULL, 
                     "racoon: could not initialize avc.\n");
}

/*
 * function:    within_range
 * description: function determines if the specified sl is within the
 *              configured range for a policy rule.
 * input:       security_context *sl            SL
 *              char *range             Range
 * return:      1       if the sl is within the range
 *              0       if the sl is not within the range or an error
 *                      occurred which prevented the determination
 */

int
within_range(security_context_t sl, security_context_t range)
{
        int rtn = 1;
        security_id_t slsid;
        security_id_t rangesid;
        struct av_decision avd;
        security_class_t tclass;
        access_vector_t av;

        if (!*range)    /* This policy doesn't have security context */
                return 1;

        if (!mls_ready)  /* mls may not be enabled */
                return 0;

        /*
         * Get the sids for the sl and range contexts
         */
        rtn = avc_context_to_sid(sl, &slsid);
        if (rtn != 0) {
                plog(LLV_ERROR, LOCATION, NULL, 
                                "within_range: Unable to retrieve "
                                "sid for sl context (%s).\n", sl);
                return 0;
        }
        rtn = avc_context_to_sid(range, &rangesid);
        if (rtn != 0) {
                plog(LLV_ERROR, LOCATION, NULL, 
                                "within_range: Unable to retrieve "
                                "sid for range context (%s).\n", range);
                sidput(slsid);
                return 0;
        }

        /* 
         * Straight up test between sl and range
         */
        tclass = SECCLASS_ASSOCIATION;
        av = ASSOCIATION__POLMATCH;
        rtn = avc_has_perm(slsid, rangesid, tclass, av, NULL, &avd);
        if (rtn != 0) {
                plog(LLV_INFO, LOCATION, NULL, 
                        "within_range: The sl is not within range\n");
                sidput(slsid);
                sidput(rangesid);
                return 0;
        }
        plog(LLV_DEBUG, LOCATION, NULL, 
                "within_range: The sl (%s) is within range (%s)\n", sl, range);
                return 1;
}