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