1/* $KAME: policy.c,v 1.46 2001/11/16 04:08:10 sakane Exp $ */ 2 3/* 4 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the project nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32#include "config.h" 33 34#include <sys/param.h> 35#include <sys/types.h> 36#include <sys/socket.h> 37#include <sys/queue.h> 38 39#include <netinet/in.h> 40#ifdef HAVE_NETINET6_IPSEC 41# include <netinet6/ipsec.h> 42#else 43# include <netinet/ipsec.h> 44#endif 45 46#include <stdlib.h> 47#include <stdio.h> 48#include <string.h> 49#include <errno.h> 50 51#include "var.h" 52#include "misc.h" 53#include "vmbuf.h" 54#include "plog.h" 55#include "sockmisc.h" 56#include "debug.h" 57 58#include "policy.h" 59#include "localconf.h" 60#include "isakmp_var.h" 61#include "isakmp.h" 62#include "oakley.h" 63#include "handler.h" 64#include "strnames.h" 65#include "gcmalloc.h" 66#include "session.h" 67 68static TAILQ_HEAD(_sptree, secpolicy) sptree; 69 70/* perform exact match against security policy table. */ 71struct secpolicy * 72getsp(spidx) 73 struct policyindex *spidx; 74{ 75 struct secpolicy *p; 76 77 for (p = TAILQ_FIRST(&sptree); p; p = TAILQ_NEXT(p, chain)) { 78 if (!cmpspidxstrict(spidx, &p->spidx)) 79 return p; 80 } 81 82 return NULL; 83} 84 85/* 86 * perform non-exact match against security policy table, only if this is 87 * transport mode SA negotiation. for example, 0.0.0.0/0 -> 0.0.0.0/0 88 * entry in policy.txt can be returned when we're negotiating transport 89 * mode SA. this is how the kernel works. 90 */ 91#if 1 92struct secpolicy * 93getsp_r(spidx, iph2) 94 struct policyindex *spidx; 95 phase2_handle_t *iph2; 96{ 97 struct secpolicy *p; 98 int mismatched_outer_addr = 0; 99 100 for (p = TAILQ_FIRST(&sptree); p; p = TAILQ_NEXT(p, chain)) { 101 if (!cmpspidxwild(spidx, &p->spidx)) { 102 if (spidx->dir != IPSEC_DIR_ANY) { 103 struct ipsecrequest *isr; 104 for (isr = p->req; isr != NULL; isr = isr->next) { 105 if (isr->saidx.mode != IPSEC_MODE_TUNNEL) { 106 plog(ASL_LEVEL_DEBUG, "%s, skipping policy. dir %d, mode %d\n", 107 __FUNCTION__, spidx->dir, isr->saidx.mode); 108 continue; 109 } 110 111 // for tunnel mode: verify the outer ip addresses match the phase2's addresses 112 if (spidx->dir == IPSEC_DIR_INBOUND) { 113 // TODO: look out for wildcards 114 if (!cmpsaddrwop(iph2->dst, &isr->saidx.src) && 115 !cmpsaddrwop(iph2->src, &isr->saidx.dst)) { 116 plog(ASL_LEVEL_DEBUG, "%s, inbound policy outer addresses matched Phase 2 addresses\n", 117 __FUNCTION__); 118 return p; 119 } else { 120 mismatched_outer_addr = 1; 121 } 122 } else if (spidx->dir == IPSEC_DIR_OUTBOUND) { 123 // TODO: look out for wildcards 124 if (!cmpsaddrwop(iph2->src, &isr->saidx.src) && 125 !cmpsaddrwop(iph2->dst, &isr->saidx.dst)) { 126 plog(ASL_LEVEL_DEBUG, "%s, outbound policy outer addresses matched Phase 2 addresses\n", 127 __FUNCTION__); 128 return p; 129 } else { 130 mismatched_outer_addr = 1; 131 } 132 } else { 133 mismatched_outer_addr = 1; 134 } 135 if (mismatched_outer_addr) { 136 plog(ASL_LEVEL_DEBUG, "%s, policy outer addresses matched Phase 2 addresses: dir %d\n", 137 __FUNCTION__, spidx->dir); 138 plog(ASL_LEVEL_DEBUG, "src1: %s\n", 139 saddr2str((struct sockaddr *)iph2->src)); 140 plog(ASL_LEVEL_DEBUG, "src2: %s\n", 141 saddr2str((struct sockaddr *)&isr->saidx.src)); 142 plog(ASL_LEVEL_DEBUG, "dst1: %s\n", 143 saddr2str((struct sockaddr *)iph2->dst)); 144 plog(ASL_LEVEL_DEBUG, "dst2: %s\n", 145 saddr2str((struct sockaddr *)&isr->saidx.dst)); 146 } 147 } 148 } 149 if (!mismatched_outer_addr) { 150 return p; 151 } 152 } 153 } 154 155 return NULL; 156} 157#else 158struct secpolicy * 159getsp_r(spidx, iph2) 160 struct policyindex *spidx; 161 phase2_handle_t *iph2; 162{ 163 struct secpolicy *p; 164 u_int8_t prefixlen; 165 166 plog(ASL_LEVEL_DEBUG, "checking for transport mode\n"); 167 168 if (spidx->src.ss_family != spidx->dst.ss_family) { 169 plog(ASL_LEVEL_ERR, 170 "address family mismatch, src:%d dst:%d\n", 171 spidx->src.ss_family, 172 spidx->dst.ss_family); 173 return NULL; 174 } 175 switch (spidx->src.ss_family) { 176 case AF_INET: 177 prefixlen = sizeof(struct in_addr) << 3; 178 break; 179#ifdef INET6 180 case AF_INET6: 181 prefixlen = sizeof(struct in6_addr) << 3; 182 break; 183#endif 184 default: 185 plog(ASL_LEVEL_ERR, 186 "invalid family: %d\n", spidx->src.ss_family); 187 return NULL; 188 } 189 190 /* is it transport mode SA negotiation? */ 191 plog(ASL_LEVEL_DEBUG, "src1: %s\n", 192 saddr2str(iph2->src)); 193 plog(ASL_LEVEL_DEBUG, "src2: %s\n", 194 saddr2str(&spidx->src)); 195 if (cmpsaddrwop(iph2->src, &spidx->src) 196 || spidx->prefs != prefixlen) 197 return NULL; 198 199 plog(ASL_LEVEL_DEBUG, "dst1: %s\n", 200 saddr2str(iph2->dst)); 201 plog(ASL_LEVEL_DEBUG, "dst2: %s\n", 202 saddr2str(&spidx->dst)); 203 if (cmpsaddrwop(iph2->dst, &spidx->dst) 204 || spidx->prefd != prefixlen) 205 return NULL; 206 207 plog(ASL_LEVEL_DEBUG, "looks to be transport mode\n"); 208 209 for (p = TAILQ_FIRST(&sptree); p; p = TAILQ_NEXT(p, chain)) { 210 if (!cmpspidx_wild(spidx, &p->spidx)) 211 return p; 212 } 213 214 return NULL; 215} 216#endif 217 218struct secpolicy * 219getspbyspid(spid) 220 u_int32_t spid; 221{ 222 struct secpolicy *p; 223 224 for (p = TAILQ_FIRST(&sptree); p; p = TAILQ_NEXT(p, chain)) { 225 if (p->id == spid) 226 return p; 227 } 228 229 return NULL; 230} 231 232/* 233 * compare policyindex. 234 * a: subject b: db 235 * OUT: 0: equal 236 * 1: not equal 237 */ 238int 239cmpspidxstrict(a, b) 240 struct policyindex *a, *b; 241{ 242 243 /* XXX don't check direction now, but it's to be checked carefully. */ 244 if (a->dir != b->dir 245 || a->prefs != b->prefs 246 || a->prefd != b->prefd 247 || a->ul_proto != b->ul_proto) 248 return 1; 249 250 if (cmpsaddrstrict(&a->src, &b->src)) 251 return 1; 252 if (cmpsaddrstrict(&a->dst, &b->dst)) 253 return 1; 254 255 return 0; 256} 257 258/* 259 * compare policyindex, with wildcard address/protocol match. 260 * a: subject b: db, can contain wildcard things. 261 * OUT: 0: equal 262 * 1: not equal 263 */ 264int 265cmpspidxwild(a, b) 266 struct policyindex *a, *b; 267{ 268 struct sockaddr_storage sa1, sa2; 269 270 if (!(b->dir == IPSEC_DIR_ANY || a->dir == b->dir)) 271 return 1; 272 273 if (!(a->ul_proto == IPSEC_ULPROTO_ANY || 274 b->ul_proto == IPSEC_ULPROTO_ANY || 275 a->ul_proto == b->ul_proto)) 276 return 1; 277 278 if (a->src.ss_family != b->src.ss_family) 279 return 1; 280 if (a->dst.ss_family != b->dst.ss_family) 281 return 1; 282 283 /* compare src address */ 284 if (sizeof(sa1) < a->src.ss_len || sizeof(sa2) < b->src.ss_len) { 285 plog(ASL_LEVEL_ERR, 286 "unexpected error: " 287 "src.ss_len:%d dst.ss_len:%d\n", 288 a->src.ss_len, b->src.ss_len); 289 return 1; 290 } 291 mask_sockaddr(&sa1, &a->src, b->prefs); 292 mask_sockaddr(&sa2, &b->src, b->prefs); 293 plog(ASL_LEVEL_DEBUG, "%p masked with /%d: %s\n", 294 a, b->prefs, saddr2str((struct sockaddr *)&sa1)); 295 plog(ASL_LEVEL_DEBUG, "%p masked with /%d: %s\n", 296 b, b->prefs, saddr2str((struct sockaddr *)&sa2)); 297 if (cmpsaddrwild(&sa1, &sa2)) 298 return 1; 299 300 /* compare dst address */ 301 if (sizeof(sa1) < a->dst.ss_len || sizeof(sa2) < b->dst.ss_len) { 302 plog(ASL_LEVEL_ERR, "unexpected error\n"); 303 exit(1); 304 } 305 mask_sockaddr(&sa1, &a->dst, b->prefd); 306 mask_sockaddr(&sa2, &b->dst, b->prefd); 307 plog(ASL_LEVEL_DEBUG, "%p masked with /%d: %s\n", 308 a, b->prefd, saddr2str((struct sockaddr *)&sa1)); 309 plog(ASL_LEVEL_DEBUG, "%p masked with /%d: %s\n", 310 b, b->prefd, saddr2str((struct sockaddr *)&sa2)); 311 if (cmpsaddrwild(&sa1, &sa2)) 312 return 1; 313 314 return 0; 315} 316 317struct secpolicy * 318newsp() 319{ 320 struct secpolicy *new; 321 322 new = racoon_calloc(1, sizeof(*new)); 323 if (new == NULL) 324 return NULL; 325 326 return new; 327} 328 329void 330delsp(sp) 331 struct secpolicy *sp; 332{ 333 struct ipsecrequest *req = NULL, *next; 334 335 for (req = sp->req; req; req = next) { 336 next = req->next; 337 racoon_free(req); 338 } 339 340 racoon_free(sp); 341} 342 343void 344delsp_bothdir(spidx0) 345 struct policyindex *spidx0; 346{ 347 struct policyindex spidx; 348 struct secpolicy *sp; 349 struct sockaddr_storage src, dst; 350 u_int8_t prefs, prefd; 351 352 memcpy(&spidx, spidx0, sizeof(spidx)); 353 switch (spidx.dir) { 354 case IPSEC_DIR_INBOUND: 355#ifdef HAVE_POLICY_FWD 356 case IPSEC_DIR_FWD: 357#endif 358 src = spidx.src; 359 dst = spidx.dst; 360 prefs = spidx.prefs; 361 prefd = spidx.prefd; 362 break; 363 case IPSEC_DIR_OUTBOUND: 364 src = spidx.dst; 365 dst = spidx.src; 366 prefs = spidx.prefd; 367 prefd = spidx.prefs; 368 break; 369 default: 370 return; 371 } 372 373 spidx.src = src; 374 spidx.dst = dst; 375 spidx.prefs = prefs; 376 spidx.prefd = prefd; 377 spidx.dir = IPSEC_DIR_INBOUND; 378 379 sp = getsp(&spidx); 380 if (sp) { 381 remsp(sp); 382 delsp(sp); 383 } 384 385#ifdef HAVE_POLICY_FWD 386 spidx.dir = IPSEC_DIR_FWD; 387 388 sp = getsp(&spidx); 389 if (sp) { 390 remsp(sp); 391 delsp(sp); 392 } 393#endif 394 395 spidx.src = dst; 396 spidx.dst = src; 397 spidx.prefs = prefd; 398 spidx.prefd = prefs; 399 spidx.dir = IPSEC_DIR_OUTBOUND; 400 401 sp = getsp(&spidx); 402 if (sp) { 403 remsp(sp); 404 delsp(sp); 405 } 406} 407 408void 409inssp(new) 410 struct secpolicy *new; 411{ 412#ifdef HAVE_PFKEY_POLICY_PRIORITY 413 struct secpolicy *p; 414 415 TAILQ_FOREACH(p, &sptree, chain) { 416 if (new->spidx.priority < p->spidx.priority) { 417 TAILQ_INSERT_BEFORE(p, new, chain); 418 return; 419 } 420 } 421 if (p == NULL) 422#endif 423 TAILQ_INSERT_HEAD(&sptree, new, chain); 424 425 check_auto_exit(); 426 return; 427} 428 429void 430remsp(sp) 431 struct secpolicy *sp; 432{ 433 TAILQ_REMOVE(&sptree, sp, chain); 434 check_auto_exit(); 435} 436 437void 438flushsp() 439{ 440 struct secpolicy *p, *next; 441 442 for (p = TAILQ_FIRST(&sptree); p; p = next) { 443 next = TAILQ_NEXT(p, chain); 444 remsp(p); 445 delsp(p); 446 } 447} 448 449int 450policies_installed(void) 451{ 452 if (TAILQ_EMPTY(&sptree)) 453 return 0; 454 else 455 return 1; 456} 457 458void 459initsp() 460{ 461 TAILQ_INIT(&sptree); 462} 463 464struct ipsecrequest * 465newipsecreq() 466{ 467 struct ipsecrequest *new; 468 469 new = racoon_calloc(1, sizeof(*new)); 470 if (new == NULL) 471 return NULL; 472 473 return new; 474} 475 476const char * 477spidx2str(spidx) 478 const struct policyindex *spidx; 479{ 480 /* addr/pref[port] addr/pref[port] ul dir act */ 481 static char buf[256]; 482 char *p, *a, *b; 483 int blen, i; 484 485 blen = sizeof(buf) - 1; 486 p = buf; 487 488 a = saddr2str((const struct sockaddr *)&spidx->src); 489 for (b = a; *b != '\0'; b++) 490 if (*b == '[') { 491 *b = '\0'; 492 b++; 493 break; 494 } 495 i = snprintf(p, blen, "%s/%d[%s ", a, spidx->prefs, b); 496 if (i < 0 || i >= blen) 497 return NULL; 498 p += i; 499 blen -= i; 500 501 a = saddr2str((const struct sockaddr *)&spidx->dst); 502 for (b = a; *b != '\0'; b++) 503 if (*b == '[') { 504 *b = '\0'; 505 b++; 506 break; 507 } 508 i = snprintf(p, blen, "%s/%d[%s ", a, spidx->prefd, b); 509 if (i < 0 || i >= blen) 510 return NULL; 511 p += i; 512 blen -= i; 513 514 snprintf(p, blen, "proto=%s dir=%s", 515 s_proto(spidx->ul_proto), s_direction(spidx->dir)); 516 517 return buf; 518} 519