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