| /* $NetBSD: policy.c,v 1.6.4.1 2007/08/01 11:52:21 vanhu Exp $ */ |
| |
| /* $KAME: policy.c,v 1.46 2001/11/16 04:08:10 sakane Exp $ */ |
| |
| /* |
| * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. |
| * 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/param.h> |
| #include <sys/types.h> |
| #include <sys/socket.h> |
| #include <sys/queue.h> |
| |
| #include <netinet/in.h> |
| #include PATH_IPSEC_H |
| |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <errno.h> |
| |
| #include "var.h" |
| #include "misc.h" |
| #include "vmbuf.h" |
| #include "plog.h" |
| #include "sockmisc.h" |
| #include "debug.h" |
| |
| #include "policy.h" |
| #include "localconf.h" |
| #include "isakmp_var.h" |
| #include "isakmp.h" |
| #include "oakley.h" |
| #include "handler.h" |
| #include "strnames.h" |
| #include "gcmalloc.h" |
| |
| static TAILQ_HEAD(_sptree, secpolicy) sptree; |
| |
| /* perform exact match against security policy table. */ |
| struct secpolicy * |
| getsp(spidx) |
| struct policyindex *spidx; |
| { |
| struct secpolicy *p; |
| |
| for (p = TAILQ_FIRST(&sptree); p; p = TAILQ_NEXT(p, chain)) { |
| if (!cmpspidxstrict(spidx, &p->spidx)) |
| return p; |
| } |
| |
| return NULL; |
| } |
| |
| /* |
| * perform non-exact match against security policy table, only if this is |
| * transport mode SA negotiation. for example, 0.0.0.0/0 -> 0.0.0.0/0 |
| * entry in policy.txt can be returned when we're negotiating transport |
| * mode SA. this is how the kernel works. |
| */ |
| #if 1 |
| struct secpolicy * |
| getsp_r(spidx) |
| struct policyindex *spidx; |
| { |
| struct secpolicy *p; |
| |
| for (p = TAILQ_FIRST(&sptree); p; p = TAILQ_NEXT(p, chain)) { |
| if (!cmpspidxwild(spidx, &p->spidx)) |
| return p; |
| } |
| |
| return NULL; |
| } |
| #else |
| struct secpolicy * |
| getsp_r(spidx, iph2) |
| struct policyindex *spidx; |
| struct ph2handle *iph2; |
| { |
| struct secpolicy *p; |
| u_int8_t prefixlen; |
| |
| plog(LLV_DEBUG, LOCATION, NULL, "checking for transport mode\n"); |
| |
| if (spidx->src.ss_family != spidx->dst.ss_family) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "address family mismatch, src:%d dst:%d\n", |
| spidx->src.ss_family, |
| spidx->dst.ss_family); |
| return NULL; |
| } |
| switch (spidx->src.ss_family) { |
| case AF_INET: |
| prefixlen = sizeof(struct in_addr) << 3; |
| break; |
| #ifdef INET6 |
| case AF_INET6: |
| prefixlen = sizeof(struct in6_addr) << 3; |
| break; |
| #endif |
| default: |
| plog(LLV_ERROR, LOCATION, NULL, |
| "invalid family: %d\n", spidx->src.ss_family); |
| return NULL; |
| } |
| |
| /* is it transport mode SA negotiation? */ |
| plog(LLV_DEBUG, LOCATION, NULL, "src1: %s\n", |
| saddr2str(iph2->src)); |
| plog(LLV_DEBUG, LOCATION, NULL, "src2: %s\n", |
| saddr2str((struct sockaddr *)&spidx->src)); |
| if (cmpsaddrwop(iph2->src, (struct sockaddr *)&spidx->src) |
| || spidx->prefs != prefixlen) |
| return NULL; |
| |
| plog(LLV_DEBUG, LOCATION, NULL, "dst1: %s\n", |
| saddr2str(iph2->dst)); |
| plog(LLV_DEBUG, LOCATION, NULL, "dst2: %s\n", |
| saddr2str((struct sockaddr *)&spidx->dst)); |
| if (cmpsaddrwop(iph2->dst, (struct sockaddr *)&spidx->dst) |
| || spidx->prefd != prefixlen) |
| return NULL; |
| |
| plog(LLV_DEBUG, LOCATION, NULL, "looks to be transport mode\n"); |
| |
| for (p = TAILQ_FIRST(&sptree); p; p = TAILQ_NEXT(p, chain)) { |
| if (!cmpspidx_wild(spidx, &p->spidx)) |
| return p; |
| } |
| |
| return NULL; |
| } |
| #endif |
| |
| struct secpolicy * |
| getspbyspid(spid) |
| u_int32_t spid; |
| { |
| struct secpolicy *p; |
| |
| for (p = TAILQ_FIRST(&sptree); p; p = TAILQ_NEXT(p, chain)) { |
| if (p->id == spid) |
| return p; |
| } |
| |
| return NULL; |
| } |
| |
| /* |
| * compare policyindex. |
| * a: subject b: db |
| * OUT: 0: equal |
| * 1: not equal |
| */ |
| int |
| cmpspidxstrict(a, b) |
| struct policyindex *a, *b; |
| { |
| plog(LLV_DEBUG, LOCATION, NULL, "sub:%p: %s\n", a, spidx2str(a)); |
| plog(LLV_DEBUG, LOCATION, NULL, "db :%p: %s\n", b, spidx2str(b)); |
| |
| /* XXX don't check direction now, but it's to be checked carefully. */ |
| if (a->dir != b->dir |
| || a->prefs != b->prefs |
| || a->prefd != b->prefd |
| || a->ul_proto != b->ul_proto) |
| return 1; |
| |
| if (cmpsaddrstrict((struct sockaddr *)&a->src, |
| (struct sockaddr *)&b->src)) |
| return 1; |
| if (cmpsaddrstrict((struct sockaddr *)&a->dst, |
| (struct sockaddr *)&b->dst)) |
| return 1; |
| |
| #ifdef HAVE_SECCTX |
| if (a->sec_ctx.ctx_alg != b->sec_ctx.ctx_alg |
| || a->sec_ctx.ctx_doi != b->sec_ctx.ctx_doi |
| || !within_range(a->sec_ctx.ctx_str, b->sec_ctx.ctx_str)) |
| return 1; |
| #endif |
| return 0; |
| } |
| |
| /* |
| * compare policyindex, with wildcard address/protocol match. |
| * a: subject b: db, can contain wildcard things. |
| * OUT: 0: equal |
| * 1: not equal |
| */ |
| int |
| cmpspidxwild(a, b) |
| struct policyindex *a, *b; |
| { |
| struct sockaddr_storage sa1, sa2; |
| |
| plog(LLV_DEBUG, LOCATION, NULL, "sub:%p: %s\n", a, spidx2str(a)); |
| plog(LLV_DEBUG, LOCATION, NULL, "db: %p: %s\n", b, spidx2str(b)); |
| |
| if (!(b->dir == IPSEC_DIR_ANY || a->dir == b->dir)) |
| return 1; |
| |
| if (!(a->ul_proto == IPSEC_ULPROTO_ANY || |
| b->ul_proto == IPSEC_ULPROTO_ANY || |
| a->ul_proto == b->ul_proto)) |
| return 1; |
| |
| if (a->src.ss_family != b->src.ss_family) |
| return 1; |
| if (a->dst.ss_family != b->dst.ss_family) |
| return 1; |
| |
| #ifndef __linux__ |
| /* compare src address */ |
| if (sizeof(sa1) < a->src.ss_len || sizeof(sa2) < b->src.ss_len) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "unexpected error: " |
| "src.ss_len:%d dst.ss_len:%d\n", |
| a->src.ss_len, b->src.ss_len); |
| return 1; |
| } |
| #endif |
| mask_sockaddr((struct sockaddr *)&sa1, (struct sockaddr *)&a->src, |
| b->prefs); |
| mask_sockaddr((struct sockaddr *)&sa2, (struct sockaddr *)&b->src, |
| b->prefs); |
| plog(LLV_DEBUG, LOCATION, NULL, "%p masked with /%d: %s\n", |
| a, b->prefs, saddr2str((struct sockaddr *)&sa1)); |
| plog(LLV_DEBUG, LOCATION, NULL, "%p masked with /%d: %s\n", |
| b, b->prefs, saddr2str((struct sockaddr *)&sa2)); |
| if (cmpsaddrwild((struct sockaddr *)&sa1, (struct sockaddr *)&sa2)) |
| return 1; |
| |
| #ifndef __linux__ |
| /* compare dst address */ |
| if (sizeof(sa1) < a->dst.ss_len || sizeof(sa2) < b->dst.ss_len) { |
| plog(LLV_ERROR, LOCATION, NULL, "unexpected error\n"); |
| exit(1); |
| } |
| #endif |
| mask_sockaddr((struct sockaddr *)&sa1, (struct sockaddr *)&a->dst, |
| b->prefd); |
| mask_sockaddr((struct sockaddr *)&sa2, (struct sockaddr *)&b->dst, |
| b->prefd); |
| plog(LLV_DEBUG, LOCATION, NULL, "%p masked with /%d: %s\n", |
| a, b->prefd, saddr2str((struct sockaddr *)&sa1)); |
| plog(LLV_DEBUG, LOCATION, NULL, "%p masked with /%d: %s\n", |
| b, b->prefd, saddr2str((struct sockaddr *)&sa2)); |
| if (cmpsaddrwild((struct sockaddr *)&sa1, (struct sockaddr *)&sa2)) |
| return 1; |
| |
| #ifdef HAVE_SECCTX |
| if (a->sec_ctx.ctx_alg != b->sec_ctx.ctx_alg |
| || a->sec_ctx.ctx_doi != b->sec_ctx.ctx_doi |
| || !within_range(a->sec_ctx.ctx_str, b->sec_ctx.ctx_str)) |
| return 1; |
| #endif |
| return 0; |
| } |
| |
| struct secpolicy * |
| newsp() |
| { |
| struct secpolicy *new; |
| |
| new = racoon_calloc(1, sizeof(*new)); |
| if (new == NULL) |
| return NULL; |
| |
| return new; |
| } |
| |
| void |
| delsp(sp) |
| struct secpolicy *sp; |
| { |
| struct ipsecrequest *req = NULL, *next; |
| |
| for (req = sp->req; req; req = next) { |
| next = req->next; |
| racoon_free(req); |
| } |
| |
| racoon_free(sp); |
| } |
| |
| void |
| delsp_bothdir(spidx0) |
| struct policyindex *spidx0; |
| { |
| struct policyindex spidx; |
| struct secpolicy *sp; |
| struct sockaddr_storage src, dst; |
| u_int8_t prefs, prefd; |
| |
| memcpy(&spidx, spidx0, sizeof(spidx)); |
| switch (spidx.dir) { |
| case IPSEC_DIR_INBOUND: |
| #ifdef HAVE_POLICY_FWD |
| case IPSEC_DIR_FWD: |
| #endif |
| src = spidx.src; |
| dst = spidx.dst; |
| prefs = spidx.prefs; |
| prefd = spidx.prefd; |
| break; |
| case IPSEC_DIR_OUTBOUND: |
| src = spidx.dst; |
| dst = spidx.src; |
| prefs = spidx.prefd; |
| prefd = spidx.prefs; |
| break; |
| default: |
| return; |
| } |
| |
| spidx.src = src; |
| spidx.dst = dst; |
| spidx.prefs = prefs; |
| spidx.prefd = prefd; |
| spidx.dir = IPSEC_DIR_INBOUND; |
| |
| sp = getsp(&spidx); |
| if (sp) { |
| remsp(sp); |
| delsp(sp); |
| } |
| |
| #ifdef HAVE_POLICY_FWD |
| spidx.dir = IPSEC_DIR_FWD; |
| |
| sp = getsp(&spidx); |
| if (sp) { |
| remsp(sp); |
| delsp(sp); |
| } |
| #endif |
| |
| spidx.src = dst; |
| spidx.dst = src; |
| spidx.prefs = prefd; |
| spidx.prefd = prefs; |
| spidx.dir = IPSEC_DIR_OUTBOUND; |
| |
| sp = getsp(&spidx); |
| if (sp) { |
| remsp(sp); |
| delsp(sp); |
| } |
| } |
| |
| void |
| inssp(new) |
| struct secpolicy *new; |
| { |
| #ifdef HAVE_PFKEY_POLICY_PRIORITY |
| struct secpolicy *p; |
| |
| TAILQ_FOREACH(p, &sptree, chain) { |
| if (new->spidx.priority < p->spidx.priority) { |
| TAILQ_INSERT_BEFORE(p, new, chain); |
| return; |
| } |
| } |
| if (p == NULL) |
| #endif |
| TAILQ_INSERT_TAIL(&sptree, new, chain); |
| |
| return; |
| } |
| |
| void |
| remsp(sp) |
| struct secpolicy *sp; |
| { |
| TAILQ_REMOVE(&sptree, sp, chain); |
| } |
| |
| void |
| flushsp() |
| { |
| struct secpolicy *p, *next; |
| |
| for (p = TAILQ_FIRST(&sptree); p; p = next) { |
| next = TAILQ_NEXT(p, chain); |
| remsp(p); |
| delsp(p); |
| } |
| } |
| |
| void |
| initsp() |
| { |
| TAILQ_INIT(&sptree); |
| } |
| |
| struct ipsecrequest * |
| newipsecreq() |
| { |
| struct ipsecrequest *new; |
| |
| new = racoon_calloc(1, sizeof(*new)); |
| if (new == NULL) |
| return NULL; |
| |
| return new; |
| } |
| |
| const char * |
| spidx2str(spidx) |
| const struct policyindex *spidx; |
| { |
| /* addr/pref[port] addr/pref[port] ul dir act */ |
| static char buf[256]; |
| char *p, *a, *b; |
| int blen, i; |
| |
| blen = sizeof(buf) - 1; |
| p = buf; |
| |
| a = saddr2str((const struct sockaddr *)&spidx->src); |
| for (b = a; *b != '\0'; b++) |
| if (*b == '[') { |
| *b = '\0'; |
| b++; |
| break; |
| } |
| i = snprintf(p, blen, "%s/%d[%s ", a, spidx->prefs, b); |
| if (i < 0 || i >= blen) |
| return NULL; |
| p += i; |
| blen -= i; |
| |
| a = saddr2str((const struct sockaddr *)&spidx->dst); |
| for (b = a; *b != '\0'; b++) |
| if (*b == '[') { |
| *b = '\0'; |
| b++; |
| break; |
| } |
| i = snprintf(p, blen, "%s/%d[%s ", a, spidx->prefd, b); |
| if (i < 0 || i >= blen) |
| return NULL; |
| p += i; |
| blen -= i; |
| |
| i = snprintf(p, blen, "proto=%s dir=%s", |
| s_proto(spidx->ul_proto), s_direction(spidx->dir)); |
| |
| #ifdef HAVE_SECCTX |
| if (spidx->sec_ctx.ctx_strlen) { |
| p += i; |
| blen -= i; |
| snprintf(p, blen, " sec_ctx:doi=%d,alg=%d,len=%d,str=%s", |
| spidx->sec_ctx.ctx_doi, spidx->sec_ctx.ctx_alg, |
| spidx->sec_ctx.ctx_strlen, spidx->sec_ctx.ctx_str); |
| } |
| #endif |
| return buf; |
| } |