www.usr.com/support/gpl/USR9108_release1.5.tar.gz
[bcm963xx.git] / userapps / opensource / ipsec-tools / src / 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
67 static TAILQ_HEAD(_sptree, secpolicy) sptree;
68
69 /* perform exact match against security policy table. */
70 struct secpolicy *
71 getsp(spidx)
72         struct policyindex *spidx;
73 {
74         struct secpolicy *p;
75
76         for (p = TAILQ_FIRST(&sptree); p; p = TAILQ_NEXT(p, chain)) {
77                 if (!cmpspidxstrict(spidx, &p->spidx))
78                         return p;
79         }
80
81         return NULL;
82 }
83
84 /*
85  * perform non-exact match against security policy table, only if this is
86  * transport mode SA negotiation.  for example, 0.0.0.0/0 -> 0.0.0.0/0
87  * entry in policy.txt can be returned when we're negotiating transport
88  * mode SA.  this is how the kernel works.
89  */
90 #if 1
91 struct secpolicy *
92 getsp_r(spidx)
93         struct policyindex *spidx;
94 {
95         struct secpolicy *p;
96
97         for (p = TAILQ_FIRST(&sptree); p; p = TAILQ_NEXT(p, chain)) {
98                 if (!cmpspidxwild(spidx, &p->spidx))
99                         return p;
100         }
101
102         return NULL;
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         u_int8_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         if (cmpsaddrwop(iph2->src, (struct sockaddr *)&spidx->src)
143          || spidx->prefs != prefixlen)
144                 return NULL;
145
146         plog(LLV_DEBUG, LOCATION, NULL, "dst1: %s\n",
147                 saddr2str(iph2->dst));
148         plog(LLV_DEBUG, LOCATION, NULL, "dst2: %s\n",
149                 saddr2str((struct sockaddr *)&spidx->dst));
150         if (cmpsaddrwop(iph2->dst, (struct sockaddr *)&spidx->dst)
151          || spidx->prefd != prefixlen)
152                 return NULL;
153
154         plog(LLV_DEBUG, LOCATION, NULL, "looks to be transport mode\n");
155
156         for (p = TAILQ_FIRST(&sptree); p; p = TAILQ_NEXT(p, chain)) {
157                 if (!cmpspidx_wild(spidx, &p->spidx))
158                         return p;
159         }
160
161         return NULL;
162 }
163 #endif
164
165 struct secpolicy *
166 getspbyspid(spid)
167         u_int32_t spid;
168 {
169         struct secpolicy *p;
170
171         for (p = TAILQ_FIRST(&sptree); p; p = TAILQ_NEXT(p, chain)) {
172                 if (p->id == spid)
173                         return p;
174         }
175
176         return NULL;
177 }
178
179 /*
180  * compare policyindex.
181  * a: subject b: db
182  * OUT: 0:      equal
183  *      1:      not equal
184  */
185 int
186 cmpspidxstrict(a, b)
187         struct policyindex *a, *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 (cmpsaddrstrict((struct sockaddr *)&a->src,
200                            (struct sockaddr *)&b->src))
201                 return 1;
202         if (cmpsaddrstrict((struct sockaddr *)&a->dst,
203                            (struct sockaddr *)&b->dst))
204                 return 1;
205
206         return 0;
207 }
208
209 /*
210  * compare policyindex, with wildcard address/protocol match.
211  * a: subject b: db, can contain wildcard things.
212  * OUT: 0:      equal
213  *      1:      not equal
214  */
215 int
216 cmpspidxwild(a, b)
217         struct policyindex *a, *b;
218 {
219         struct sockaddr_storage sa1, sa2;
220
221         plog(LLV_DEBUG, LOCATION, NULL, "sub:%p: %s\n", a, spidx2str(a));
222         plog(LLV_DEBUG, LOCATION, NULL, "db: %p: %s\n", b, spidx2str(b));
223
224         if (!(b->dir == IPSEC_DIR_ANY || a->dir == b->dir))
225                 return 1;
226
227         if (!(a->ul_proto == IPSEC_ULPROTO_ANY ||
228               b->ul_proto == IPSEC_ULPROTO_ANY ||
229               a->ul_proto == b->ul_proto))
230                 return 1;
231
232         if (a->src.ss_family != b->src.ss_family)
233                 return 1;
234         if (a->dst.ss_family != b->dst.ss_family)
235                 return 1;
236
237 #ifndef __linux__
238         /* compare src address */
239         if (sizeof(sa1) < a->src.ss_len || sizeof(sa2) < b->src.ss_len) {
240                 plog(LLV_ERROR, LOCATION, NULL,
241                         "unexpected error: "
242                         "src.ss_len:%d dst.ss_len:%d\n",
243                         a->src.ss_len, b->src.ss_len);
244                 return 1;
245         }
246 #endif
247         mask_sockaddr((struct sockaddr *)&sa1, (struct sockaddr *)&a->src,
248                 b->prefs);
249         mask_sockaddr((struct sockaddr *)&sa2, (struct sockaddr *)&b->src,
250                 b->prefs);
251         plog(LLV_DEBUG, LOCATION, NULL, "%p masked with /%d: %s\n",
252                 a, b->prefs, saddr2str((struct sockaddr *)&sa1));
253         plog(LLV_DEBUG, LOCATION, NULL, "%p masked with /%d: %s\n",
254                 b, b->prefs, saddr2str((struct sockaddr *)&sa2));
255         if (cmpsaddrwild((struct sockaddr *)&sa1, (struct sockaddr *)&sa2))
256                 return 1;
257
258 #ifndef __linux__
259         /* compare dst address */
260         if (sizeof(sa1) < a->dst.ss_len || sizeof(sa2) < b->dst.ss_len) {
261                 plog(LLV_ERROR, LOCATION, NULL, "unexpected error\n");
262                 exit(1);
263         }
264 #endif
265         mask_sockaddr((struct sockaddr *)&sa1, (struct sockaddr *)&a->dst,
266                 b->prefd);
267         mask_sockaddr((struct sockaddr *)&sa2, (struct sockaddr *)&b->dst,
268                 b->prefd);
269         plog(LLV_DEBUG, LOCATION, NULL, "%p masked with /%d: %s\n",
270                 a, b->prefd, saddr2str((struct sockaddr *)&sa1));
271         plog(LLV_DEBUG, LOCATION, NULL, "%p masked with /%d: %s\n",
272                 b, b->prefd, saddr2str((struct sockaddr *)&sa2));
273         if (cmpsaddrwild((struct sockaddr *)&sa1, (struct sockaddr *)&sa2))
274                 return 1;
275
276         return 0;
277 }
278
279 struct secpolicy *
280 newsp()
281 {
282         struct secpolicy *new;
283
284         new = racoon_calloc(1, sizeof(*new));
285         if (new == NULL)
286                 return NULL;
287
288         return new;
289 }
290
291 void
292 delsp(sp)
293         struct secpolicy *sp;
294 {
295         struct ipsecrequest *req = NULL, *next;
296
297         for (req = sp->req; req; req = next) {
298                 next = req->next;
299                 racoon_free(req);
300         }
301         
302         racoon_free(sp);
303 }
304
305 void
306 delsp_bothdir(spidx0)
307         struct policyindex *spidx0;
308 {
309         struct policyindex spidx;
310         struct secpolicy *sp;
311         struct sockaddr_storage src, dst;
312         u_int8_t prefs, prefd;
313
314         memcpy(&spidx, spidx0, sizeof(spidx));
315         switch (spidx.dir) {
316         case IPSEC_DIR_INBOUND:
317 #ifdef HAVE_POLICY_FWD
318         case IPSEC_DIR_FWD:
319 #endif
320                 src   = spidx.src;
321                 dst   = spidx.dst;
322                 prefs = spidx.prefs;
323                 prefd = spidx.prefd;
324                 break;
325         case IPSEC_DIR_OUTBOUND:
326                 src   = spidx.dst;
327                 dst   = spidx.src;
328                 prefs = spidx.prefd;
329                 prefd = spidx.prefs;
330                 break;
331         default:
332                 return;
333         }
334
335         spidx.src   = src;
336         spidx.dst   = dst;
337         spidx.prefs = prefs;
338         spidx.prefd = prefd;
339         spidx.dir   = IPSEC_DIR_INBOUND;
340
341         sp = getsp(&spidx);
342         if (sp) {
343                 remsp(sp);
344                 delsp(sp);
345         }
346
347 #ifdef HAVE_POLICY_FWD
348         spidx.dir   = IPSEC_DIR_FWD;
349
350         sp = getsp(&spidx);
351         if (sp) {
352                 remsp(sp);
353                 delsp(sp);
354         }
355 #endif
356
357         spidx.src   = dst;
358         spidx.dst   = src;
359         spidx.prefs = prefd;
360         spidx.prefd = prefs;
361         spidx.dir   = IPSEC_DIR_OUTBOUND;
362
363         sp = getsp(&spidx);
364         if (sp) {
365                 remsp(sp);
366                 delsp(sp);
367         }
368 }
369
370 void
371 inssp(new)
372         struct secpolicy *new;
373 {
374         struct secpolicy *p;
375
376         TAILQ_FOREACH(p, &sptree, chain) {
377                 if (new->spidx.priority < p->spidx.priority) {
378                         TAILQ_INSERT_BEFORE(p, new, chain);
379                         return;
380                 }
381         }
382         if (p == NULL)
383                 TAILQ_INSERT_TAIL(&sptree, new, chain);
384
385         return;
386 }
387
388 void
389 remsp(sp)
390         struct secpolicy *sp;
391 {
392         TAILQ_REMOVE(&sptree, sp, chain);
393 }
394
395 void
396 flushsp()
397 {
398         struct secpolicy *p, *next;
399
400         for (p = TAILQ_FIRST(&sptree); p; p = next) {
401                 next = TAILQ_NEXT(p, chain);
402                 remsp(p);
403                 delsp(p);
404         }
405 }
406
407 void
408 initsp()
409 {
410         TAILQ_INIT(&sptree);
411 }
412
413 struct ipsecrequest *
414 newipsecreq()
415 {
416         struct ipsecrequest *new;
417
418         new = racoon_calloc(1, sizeof(*new));
419         if (new == NULL)
420                 return NULL;
421
422         return new;
423 }
424
425 const char *
426 spidx2str(spidx)
427         const struct policyindex *spidx;
428 {
429         /* addr/pref[port] addr/pref[port] ul dir act */
430         static char buf[256];
431         char *p, *a, *b;
432         int blen, i;
433
434         blen = sizeof(buf) - 1;
435         p = buf;
436
437         a = saddr2str((const struct sockaddr *)&spidx->src);
438         for (b = a; *b != '\0'; b++)
439                 if (*b == '[') {
440                         *b = '\0';
441                         b++;
442                         break;
443                 }
444         i = snprintf(p, blen, "%s/%d[%s ", a, spidx->prefs, b);
445         if (i < 0 || i >= blen)
446                 return NULL;
447         p += i;
448         blen -= i;
449
450         a = saddr2str((const struct sockaddr *)&spidx->dst);
451         for (b = a; *b != '\0'; b++)
452                 if (*b == '[') {
453                         *b = '\0';
454                         b++;
455                         break;
456                 }
457         i = snprintf(p, blen, "%s/%d[%s ", a, spidx->prefd, b);
458         if (i < 0 || i >= blen)
459                 return NULL;
460         p += i;
461         blen -= i;
462
463         snprintf(p, blen, "proto=%s dir=%s",
464                 s_proto(spidx->ul_proto), s_direction(spidx->dir));
465
466         return buf;
467 }