]> git.saurik.com Git - apple/ipsec.git/blob - ipsec-tools/racoon/policy.c
ipsec-93.15.tar.gz
[apple/ipsec.git] / ipsec-tools / racoon / policy.c
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
68 static TAILQ_HEAD(_sptree, secpolicy) sptree;
69
70 /* perform exact match against security policy table. */
71 struct secpolicy *
72 getsp(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
92 struct secpolicy *
93 getsp_r(spidx, iph2)
94 struct policyindex *spidx;
95 struct ph2handle *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(LLV_DEBUG2, LOCATION, NULL, "%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, (struct sockaddr *)&isr->saidx.src) &&
115 !cmpsaddrwop(iph2->src, (struct sockaddr *)&isr->saidx.dst)) {
116 plog(LLV_DEBUG2, LOCATION, NULL, "%s, inbound policy outer addresses matched phase2's 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, (struct sockaddr *)&isr->saidx.src) &&
125 !cmpsaddrwop(iph2->dst, (struct sockaddr *)&isr->saidx.dst)) {
126 plog(LLV_DEBUG2, LOCATION, NULL, "%s, outbound policy outer addresses matched phase2's 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(LLV_DEBUG2, LOCATION, NULL, "%s, policy outer addresses matched phase2's addresses: dir %d\n",
137 __FUNCTION__, spidx->dir);
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 *)&isr->saidx.src));
142 plog(LLV_DEBUG, LOCATION, NULL, "dst1: %s\n",
143 saddr2str(iph2->dst));
144 plog(LLV_DEBUG, LOCATION, NULL, "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
158 struct secpolicy *
159 getsp_r(spidx, iph2)
160 struct policyindex *spidx;
161 struct ph2handle *iph2;
162 {
163 struct secpolicy *p;
164 u_int8_t prefixlen;
165
166 plog(LLV_DEBUG, LOCATION, NULL, "checking for transport mode\n");
167
168 if (spidx->src.ss_family != spidx->dst.ss_family) {
169 plog(LLV_ERROR, LOCATION, NULL,
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(LLV_ERROR, LOCATION, NULL,
186 "invalid family: %d\n", spidx->src.ss_family);
187 return NULL;
188 }
189
190 /* is it transport mode SA negotiation? */
191 plog(LLV_DEBUG, LOCATION, NULL, "src1: %s\n",
192 saddr2str(iph2->src));
193 plog(LLV_DEBUG, LOCATION, NULL, "src2: %s\n",
194 saddr2str((struct sockaddr *)&spidx->src));
195 if (cmpsaddrwop(iph2->src, (struct sockaddr *)&spidx->src)
196 || spidx->prefs != prefixlen)
197 return NULL;
198
199 plog(LLV_DEBUG, LOCATION, NULL, "dst1: %s\n",
200 saddr2str(iph2->dst));
201 plog(LLV_DEBUG, LOCATION, NULL, "dst2: %s\n",
202 saddr2str((struct sockaddr *)&spidx->dst));
203 if (cmpsaddrwop(iph2->dst, (struct sockaddr *)&spidx->dst)
204 || spidx->prefd != prefixlen)
205 return NULL;
206
207 plog(LLV_DEBUG, LOCATION, NULL, "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
218 struct secpolicy *
219 getspbyspid(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 */
238 int
239 cmpspidxstrict(a, b)
240 struct policyindex *a, *b;
241 {
242 plog(LLV_DEBUG, LOCATION, NULL, "sub:%p: %s\n", a, spidx2str(a));
243 plog(LLV_DEBUG, LOCATION, NULL, "db :%p: %s\n", b, spidx2str(b));
244
245 /* XXX don't check direction now, but it's to be checked carefully. */
246 if (a->dir != b->dir
247 || a->prefs != b->prefs
248 || a->prefd != b->prefd
249 || a->ul_proto != b->ul_proto)
250 return 1;
251
252 if (cmpsaddrstrict((struct sockaddr *)&a->src,
253 (struct sockaddr *)&b->src))
254 return 1;
255 if (cmpsaddrstrict((struct sockaddr *)&a->dst,
256 (struct sockaddr *)&b->dst))
257 return 1;
258
259 return 0;
260 }
261
262 /*
263 * compare policyindex, with wildcard address/protocol match.
264 * a: subject b: db, can contain wildcard things.
265 * OUT: 0: equal
266 * 1: not equal
267 */
268 int
269 cmpspidxwild(a, b)
270 struct policyindex *a, *b;
271 {
272 struct sockaddr_storage sa1, sa2;
273
274 plog(LLV_DEBUG, LOCATION, NULL, "sub:%p: %s\n", a, spidx2str(a));
275 plog(LLV_DEBUG, LOCATION, NULL, "db: %p: %s\n", b, spidx2str(b));
276
277 if (!(b->dir == IPSEC_DIR_ANY || a->dir == b->dir))
278 return 1;
279
280 if (!(a->ul_proto == IPSEC_ULPROTO_ANY ||
281 b->ul_proto == IPSEC_ULPROTO_ANY ||
282 a->ul_proto == b->ul_proto))
283 return 1;
284
285 if (a->src.ss_family != b->src.ss_family)
286 return 1;
287 if (a->dst.ss_family != b->dst.ss_family)
288 return 1;
289
290 #ifndef __linux__
291 /* compare src address */
292 if (sizeof(sa1) < a->src.ss_len || sizeof(sa2) < b->src.ss_len) {
293 plog(LLV_ERROR, LOCATION, NULL,
294 "unexpected error: "
295 "src.ss_len:%d dst.ss_len:%d\n",
296 a->src.ss_len, b->src.ss_len);
297 return 1;
298 }
299 #endif
300 mask_sockaddr((struct sockaddr *)&sa1, (struct sockaddr *)&a->src,
301 b->prefs);
302 mask_sockaddr((struct sockaddr *)&sa2, (struct sockaddr *)&b->src,
303 b->prefs);
304 plog(LLV_DEBUG, LOCATION, NULL, "%p masked with /%d: %s\n",
305 a, b->prefs, saddr2str((struct sockaddr *)&sa1));
306 plog(LLV_DEBUG, LOCATION, NULL, "%p masked with /%d: %s\n",
307 b, b->prefs, saddr2str((struct sockaddr *)&sa2));
308 if (cmpsaddrwild((struct sockaddr *)&sa1, (struct sockaddr *)&sa2))
309 return 1;
310
311 #ifndef __linux__
312 /* compare dst address */
313 if (sizeof(sa1) < a->dst.ss_len || sizeof(sa2) < b->dst.ss_len) {
314 plog(LLV_ERROR, LOCATION, NULL, "unexpected error\n");
315 exit(1);
316 }
317 #endif
318 mask_sockaddr((struct sockaddr *)&sa1, (struct sockaddr *)&a->dst,
319 b->prefd);
320 mask_sockaddr((struct sockaddr *)&sa2, (struct sockaddr *)&b->dst,
321 b->prefd);
322 plog(LLV_DEBUG, LOCATION, NULL, "%p masked with /%d: %s\n",
323 a, b->prefd, saddr2str((struct sockaddr *)&sa1));
324 plog(LLV_DEBUG, LOCATION, NULL, "%p masked with /%d: %s\n",
325 b, b->prefd, saddr2str((struct sockaddr *)&sa2));
326 if (cmpsaddrwild((struct sockaddr *)&sa1, (struct sockaddr *)&sa2))
327 return 1;
328
329 return 0;
330 }
331
332 struct secpolicy *
333 newsp()
334 {
335 struct secpolicy *new;
336
337 new = racoon_calloc(1, sizeof(*new));
338 if (new == NULL)
339 return NULL;
340
341 return new;
342 }
343
344 void
345 delsp(sp)
346 struct secpolicy *sp;
347 {
348 struct ipsecrequest *req = NULL, *next;
349
350 for (req = sp->req; req; req = next) {
351 next = req->next;
352 racoon_free(req);
353 }
354
355 racoon_free(sp);
356 }
357
358 void
359 delsp_bothdir(spidx0)
360 struct policyindex *spidx0;
361 {
362 struct policyindex spidx;
363 struct secpolicy *sp;
364 struct sockaddr_storage src, dst;
365 u_int8_t prefs, prefd;
366
367 memcpy(&spidx, spidx0, sizeof(spidx));
368 switch (spidx.dir) {
369 case IPSEC_DIR_INBOUND:
370 #ifdef HAVE_POLICY_FWD
371 case IPSEC_DIR_FWD:
372 #endif
373 src = spidx.src;
374 dst = spidx.dst;
375 prefs = spidx.prefs;
376 prefd = spidx.prefd;
377 break;
378 case IPSEC_DIR_OUTBOUND:
379 src = spidx.dst;
380 dst = spidx.src;
381 prefs = spidx.prefd;
382 prefd = spidx.prefs;
383 break;
384 default:
385 return;
386 }
387
388 spidx.src = src;
389 spidx.dst = dst;
390 spidx.prefs = prefs;
391 spidx.prefd = prefd;
392 spidx.dir = IPSEC_DIR_INBOUND;
393
394 sp = getsp(&spidx);
395 if (sp) {
396 remsp(sp);
397 delsp(sp);
398 }
399
400 #ifdef HAVE_POLICY_FWD
401 spidx.dir = IPSEC_DIR_FWD;
402
403 sp = getsp(&spidx);
404 if (sp) {
405 remsp(sp);
406 delsp(sp);
407 }
408 #endif
409
410 spidx.src = dst;
411 spidx.dst = src;
412 spidx.prefs = prefd;
413 spidx.prefd = prefs;
414 spidx.dir = IPSEC_DIR_OUTBOUND;
415
416 sp = getsp(&spidx);
417 if (sp) {
418 remsp(sp);
419 delsp(sp);
420 }
421 }
422
423 void
424 inssp(new)
425 struct secpolicy *new;
426 {
427 #ifdef HAVE_PFKEY_POLICY_PRIORITY
428 struct secpolicy *p;
429
430 TAILQ_FOREACH(p, &sptree, chain) {
431 if (new->spidx.priority < p->spidx.priority) {
432 TAILQ_INSERT_BEFORE(p, new, chain);
433 return;
434 }
435 }
436 if (p == NULL)
437 #endif
438 TAILQ_INSERT_HEAD(&sptree, new, chain);
439
440 check_auto_exit();
441 return;
442 }
443
444 void
445 remsp(sp)
446 struct secpolicy *sp;
447 {
448 TAILQ_REMOVE(&sptree, sp, chain);
449 check_auto_exit();
450 }
451
452 void
453 flushsp()
454 {
455 struct secpolicy *p, *next;
456
457 for (p = TAILQ_FIRST(&sptree); p; p = next) {
458 next = TAILQ_NEXT(p, chain);
459 remsp(p);
460 delsp(p);
461 }
462 }
463
464 int
465 policies_installed(void)
466 {
467 if (TAILQ_EMPTY(&sptree))
468 return 0;
469 else
470 return 1;
471 }
472
473 void
474 initsp()
475 {
476 TAILQ_INIT(&sptree);
477 }
478
479 struct ipsecrequest *
480 newipsecreq()
481 {
482 struct ipsecrequest *new;
483
484 new = racoon_calloc(1, sizeof(*new));
485 if (new == NULL)
486 return NULL;
487
488 return new;
489 }
490
491 const char *
492 spidx2str(spidx)
493 const struct policyindex *spidx;
494 {
495 /* addr/pref[port] addr/pref[port] ul dir act */
496 static char buf[256];
497 char *p, *a, *b;
498 int blen, i;
499
500 blen = sizeof(buf) - 1;
501 p = buf;
502
503 a = saddr2str((const struct sockaddr *)&spidx->src);
504 for (b = a; *b != '\0'; b++)
505 if (*b == '[') {
506 *b = '\0';
507 b++;
508 break;
509 }
510 i = snprintf(p, blen, "%s/%d[%s ", a, spidx->prefs, b);
511 if (i < 0 || i >= blen)
512 return NULL;
513 p += i;
514 blen -= i;
515
516 a = saddr2str((const struct sockaddr *)&spidx->dst);
517 for (b = a; *b != '\0'; b++)
518 if (*b == '[') {
519 *b = '\0';
520 b++;
521 break;
522 }
523 i = snprintf(p, blen, "%s/%d[%s ", a, spidx->prefd, b);
524 if (i < 0 || i >= blen)
525 return NULL;
526 p += i;
527 blen -= i;
528
529 snprintf(p, blen, "proto=%s dir=%s",
530 s_proto(spidx->ul_proto), s_direction(spidx->dir));
531
532 return buf;
533 }