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 *
getsp(struct policyindex * spidx)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 *
getsp_r(struct policyindex * spidx)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 *
getsp_r(spidx,iph2)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 *
getspbyspid(uint32_t spid)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
cmpspidxstrict(struct policyindex * a,struct policyindex * b)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
cmpspidxwild(struct policyindex * a,struct policyindex * b)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 *
newsp(void)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
delsp(struct secpolicy * sp)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
delsp_bothdir(struct policyindex * spidx0)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
inssp(struct secpolicy * new)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
remsp(struct secpolicy * sp)403 remsp(struct secpolicy *sp)
404 {
405 TAILQ_REMOVE(&sptree, sp, chain);
406 }
407
408 void
flushsp(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
initsp(void)421 initsp(void)
422 {
423 TAILQ_INIT(&sptree);
424 }
425
426 struct ipsecrequest *
newipsecreq(void)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 *
spidx2str(const struct policyindex * spidx)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