]> git.saurik.com Git - apple/network_cmds.git/blob - racoon.tproj/proposal.c
network_cmds-115.2.tar.gz
[apple/network_cmds.git] / racoon.tproj / proposal.c
1 /* $KAME: proposal.c,v 1.45 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 <sys/param.h>
33 #include <sys/types.h>
34 #include <sys/socket.h>
35 #include <sys/queue.h>
36
37 #include <netkey/key_var.h>
38 #include <netinet/in.h>
39 #include <netinet6/ipsec.h>
40
41 #include <stdlib.h>
42 #include <stdio.h>
43 #include <string.h>
44 #include <errno.h>
45
46 #include "var.h"
47 #include "misc.h"
48 #include "vmbuf.h"
49 #include "plog.h"
50 #include "sockmisc.h"
51 #include "debug.h"
52
53 #include "policy.h"
54 #include "pfkey.h"
55 #include "isakmp_var.h"
56 #include "isakmp.h"
57 #include "ipsec_doi.h"
58 #include "algorithm.h"
59 #include "proposal.h"
60 #include "sainfo.h"
61 #include "localconf.h"
62 #include "remoteconf.h"
63 #include "oakley.h"
64 #include "handler.h"
65 #include "strnames.h"
66 #include "gcmalloc.h"
67
68 /* %%%
69 * modules for ipsec sa spec
70 */
71 struct saprop *
72 newsaprop()
73 {
74 struct saprop *new;
75
76 new = racoon_calloc(1, sizeof(*new));
77 if (new == NULL)
78 return NULL;
79
80 return new;
81 }
82
83 struct saproto *
84 newsaproto()
85 {
86 struct saproto *new;
87
88 new = racoon_calloc(1, sizeof(*new));
89 if (new == NULL)
90 return NULL;
91
92 return new;
93 }
94
95 /* set saprop to last part of the prop tree */
96 void
97 inssaprop(head, new)
98 struct saprop **head;
99 struct saprop *new;
100 {
101 struct saprop *p;
102
103 if (*head == NULL) {
104 *head = new;
105 return;
106 }
107
108 for (p = *head; p->next; p = p->next)
109 ;
110 p->next = new;
111
112 return;
113 }
114
115 /* set saproto to the end of the proto tree in saprop */
116 void
117 inssaproto(pp, new)
118 struct saprop *pp;
119 struct saproto *new;
120 {
121 struct saproto *p;
122
123 for (p = pp->head; p && p->next; p = p->next)
124 ;
125 if (p == NULL)
126 pp->head = new;
127 else
128 p->next = new;
129
130 return;
131 }
132
133 /* set saproto to the top of the proto tree in saprop */
134 void
135 inssaprotorev(pp, new)
136 struct saprop *pp;
137 struct saproto *new;
138 {
139 new->next = pp->head;
140 pp->head = new;
141
142 return;
143 }
144
145 struct satrns *
146 newsatrns()
147 {
148 struct satrns *new;
149
150 new = racoon_calloc(1, sizeof(*new));
151 if (new == NULL)
152 return NULL;
153
154 return new;
155 }
156
157 /* set saproto to last part of the proto tree in saprop */
158 void
159 inssatrns(pr, new)
160 struct saproto *pr;
161 struct satrns *new;
162 {
163 struct satrns *tr;
164
165 for (tr = pr->head; tr && tr->next; tr = tr->next)
166 ;
167 if (tr == NULL)
168 pr->head = new;
169 else
170 tr->next = new;
171
172 return;
173 }
174
175 /*
176 * take a single match between saprop. allocate a new proposal and return it
177 * for future use (like picking single proposal from a bundle).
178 * pp1: peer's proposal.
179 * pp2: my proposal.
180 * NOTE: In the case of initiator, must be ensured that there is no
181 * modification of the proposal by calling cmp_aproppair_i() before
182 * this function.
183 * XXX cannot understand the comment!
184 */
185 struct saprop *
186 cmpsaprop_alloc(ph1, pp1, pp2, side)
187 struct ph1handle *ph1;
188 const struct saprop *pp1, *pp2;
189 int side;
190 {
191 struct saprop *newpp = NULL;
192 struct saproto *pr1, *pr2, *newpr = NULL;
193 struct satrns *tr1, *tr2, *newtr;
194 const int ordermatters = 0;
195 int npr1, npr2;
196 int spisizematch;
197
198 newpp = newsaprop();
199 if (newpp == NULL) {
200 plog(LLV_ERROR, LOCATION, NULL,
201 "failed to allocate saprop.\n");
202 return NULL;
203 }
204 newpp->prop_no = pp1->prop_no;
205
206 /* see proposal.h about lifetime/key length and PFS selection. */
207
208 /* check time/bytes lifetime and PFS */
209 switch (ph1->rmconf->pcheck_level) {
210 case PROP_CHECK_OBEY:
211 newpp->lifetime = pp1->lifetime;
212 newpp->lifebyte = pp1->lifebyte;
213 newpp->pfs_group = pp1->pfs_group;
214 break;
215 case PROP_CHECK_STRICT:
216 if (pp1->lifetime > pp2->lifetime) {
217 plog(LLV_ERROR, LOCATION, NULL,
218 "long lifetime proposed: "
219 "my:%d peer:%d\n",
220 pp2->lifetime, pp1->lifetime);
221 goto err;
222 }
223 if (pp1->lifebyte > pp2->lifebyte) {
224 plog(LLV_ERROR, LOCATION, NULL,
225 "long lifebyte proposed: "
226 "my:%d peer:%d\n",
227 pp2->lifebyte, pp1->lifebyte);
228 goto err;
229 }
230 newpp->lifetime = pp1->lifetime;
231 newpp->lifebyte = pp1->lifebyte;
232
233 prop_pfs_check:
234 if (pp2->pfs_group != 0 && pp1->pfs_group != pp2->pfs_group) {
235 plog(LLV_ERROR, LOCATION, NULL,
236 "pfs group mismatched: "
237 "my:%d peer:%d\n",
238 pp2->pfs_group, pp1->pfs_group);
239 goto err;
240 }
241 newpp->pfs_group = pp1->pfs_group;
242 break;
243 case PROP_CHECK_CLAIM:
244 /* lifetime */
245 if (pp1->lifetime <= pp2->lifetime) {
246 newpp->lifetime = pp1->lifetime;
247 } else {
248 newpp->lifetime = pp2->lifetime;
249 newpp->claim |= IPSECDOI_ATTR_SA_LD_TYPE_SEC;
250 plog(LLV_NOTIFY, LOCATION, NULL,
251 "use own lifetime: "
252 "my:%d peer:%d\n",
253 pp2->lifetime, pp1->lifetime);
254 }
255
256 /* lifebyte */
257 if (pp1->lifebyte > pp2->lifebyte) {
258 newpp->lifebyte = pp2->lifebyte;
259 newpp->claim |= IPSECDOI_ATTR_SA_LD_TYPE_SEC;
260 plog(LLV_NOTIFY, LOCATION, NULL,
261 "use own lifebyte: "
262 "my:%d peer:%d\n",
263 pp2->lifebyte, pp1->lifebyte);
264 }
265 newpp->lifebyte = pp1->lifebyte;
266
267 goto prop_pfs_check;
268 break;
269 case PROP_CHECK_EXACT:
270 if (pp1->lifetime != pp2->lifetime) {
271 plog(LLV_ERROR, LOCATION, NULL,
272 "lifetime mismatched: "
273 "my:%d peer:%d\n",
274 pp2->lifetime, pp1->lifetime);
275 goto err;
276 }
277 if (pp1->lifebyte != pp2->lifebyte) {
278 plog(LLV_ERROR, LOCATION, NULL,
279 "lifebyte mismatched: "
280 "my:%d peer:%d\n",
281 pp2->lifebyte, pp1->lifebyte);
282 goto err;
283 }
284 if (pp1->pfs_group != pp2->pfs_group) {
285 plog(LLV_ERROR, LOCATION, NULL,
286 "pfs group mismatched: "
287 "my:%d peer:%d\n",
288 pp2->pfs_group, pp1->pfs_group);
289 goto err;
290 }
291 newpp->lifebyte = pp1->lifebyte;
292 newpp->lifebyte = pp1->lifebyte;
293 newpp->pfs_group = pp1->pfs_group;
294 break;
295 default:
296 plog(LLV_ERROR, LOCATION, NULL,
297 "invalid pcheck_level why?.\n");
298 goto err;
299 }
300
301 npr1 = npr2 = 0;
302 for (pr1 = pp1->head; pr1; pr1 = pr1->next)
303 npr1++;
304 for (pr2 = pp2->head; pr2; pr2 = pr2->next)
305 npr2++;
306 if (npr1 != npr2)
307 goto err;
308
309 /* check protocol order */
310 pr1 = pp1->head;
311 pr2 = pp2->head;
312
313 while (1) {
314 if (!ordermatters) {
315 /*
316 * XXX does not work if we have multiple proposals
317 * with the same proto_id
318 */
319 switch (side) {
320 case RESPONDER:
321 if (!pr2)
322 break;
323 for (pr1 = pp1->head; pr1; pr1 = pr1->next) {
324 if (pr1->proto_id == pr2->proto_id)
325 break;
326 }
327 break;
328 case INITIATOR:
329 if (!pr1)
330 break;
331 for (pr2 = pp2->head; pr2; pr2 = pr2->next) {
332 if (pr2->proto_id == pr1->proto_id)
333 break;
334 }
335 break;
336 }
337 }
338 if (!pr1 || !pr2)
339 break;
340
341 if (pr1->proto_id != pr2->proto_id) {
342 plog(LLV_ERROR, LOCATION, NULL,
343 "proto_id mismatched: "
344 "my:%d peer:%d\n",
345 pr2->proto_id, pr1->proto_id);
346 goto err;
347 }
348 spisizematch = 0;
349 if (pr1->spisize == pr2->spisize)
350 spisizematch = 1;
351 else if (pr1->proto_id == IPSECDOI_PROTO_IPCOMP) {
352 /*
353 * draft-shacham-ippcp-rfc2393bis-05.txt:
354 * need to accept 16bit and 32bit SPI (CPI) for IPComp.
355 */
356 if (pr1->spisize == sizeof(u_int16_t) &&
357 pr2->spisize == sizeof(u_int32_t)) {
358 spisizematch = 1;
359 } else if (pr1->spisize == sizeof(u_int16_t) &&
360 pr2->spisize == sizeof(u_int32_t)) {
361 spisizematch = 1;
362 }
363 if (spisizematch) {
364 plog(LLV_ERROR, LOCATION, NULL,
365 "IPComp SPI size promoted "
366 "from 16bit to 32bit\n");
367 }
368 }
369 if (!spisizematch) {
370 plog(LLV_ERROR, LOCATION, NULL,
371 "spisize mismatched: "
372 "my:%d peer:%d\n",
373 pr2->spisize, pr1->spisize);
374 goto err;
375 }
376 if (pr1->encmode != pr2->encmode) {
377 plog(LLV_ERROR, LOCATION, NULL,
378 "encmode mismatched: "
379 "my:%d peer:%d\n",
380 pr2->encmode, pr1->encmode);
381 goto err;
382 }
383
384 for (tr1 = pr1->head; tr1; tr1 = tr1->next) {
385 for (tr2 = pr2->head; tr2; tr2 = tr2->next) {
386 if (cmpsatrns(tr1, tr2) == 0)
387 goto found;
388 }
389 }
390
391 goto err;
392
393 found:
394 newpr = newsaproto();
395 if (newpr == NULL) {
396 plog(LLV_ERROR, LOCATION, NULL,
397 "failed to allocate saproto.\n");
398 goto err;
399 }
400 newpr->proto_id = pr1->proto_id;
401 newpr->spisize = pr1->spisize;
402 newpr->encmode = pr1->encmode;
403 newpr->spi = pr2->spi; /* copy my SPI */
404 newpr->spi_p = pr1->spi; /* copy peer's SPI */
405 newpr->reqid_in = pr2->reqid_in;
406 newpr->reqid_out = pr2->reqid_out;
407
408 newtr = newsatrns();
409 if (newtr == NULL) {
410 plog(LLV_ERROR, LOCATION, NULL,
411 "failed to allocate satrns.\n");
412 goto err;
413 }
414 newtr->trns_no = tr1->trns_no;
415 newtr->trns_id = tr1->trns_id;
416 newtr->encklen = tr1->encklen;
417 newtr->authtype = tr1->authtype;
418
419 inssatrns(newpr, newtr);
420 inssaproto(newpp, newpr);
421
422 pr1 = pr1->next;
423 pr2 = pr2->next;
424 }
425
426 /* XXX should check if we have visited all items or not */
427 if (!ordermatters) {
428 switch (side) {
429 case RESPONDER:
430 if (!pr2)
431 pr1 = NULL;
432 break;
433 case INITIATOR:
434 if (!pr1)
435 pr2 = NULL;
436 break;
437 }
438 }
439
440 /* should be matched all protocols in a proposal */
441 if (pr1 != NULL || pr2 != NULL)
442 goto err;
443
444 return newpp;
445
446 err:
447 flushsaprop(newpp);
448 return NULL;
449 }
450
451 /* take a single match between saprop. returns 0 if pp1 equals to pp2. */
452 int
453 cmpsaprop(pp1, pp2)
454 const struct saprop *pp1, *pp2;
455 {
456 if (pp1->pfs_group != pp2->pfs_group) {
457 plog(LLV_WARNING, LOCATION, NULL,
458 "pfs_group mismatch. mine:%d peer:%d\n",
459 pp1->pfs_group, pp2->pfs_group);
460 /* FALLTHRU */
461 }
462
463 if (pp1->lifetime > pp2->lifetime) {
464 plog(LLV_WARNING, LOCATION, NULL,
465 "less lifetime proposed. mine:%d peer:%d\n",
466 pp1->lifetime, pp2->lifetime);
467 /* FALLTHRU */
468 }
469 if (pp1->lifebyte > pp2->lifebyte) {
470 plog(LLV_WARNING, LOCATION, NULL,
471 "less lifebyte proposed. mine:%d peer:%d\n",
472 pp1->lifebyte, pp2->lifebyte);
473 /* FALLTHRU */
474 }
475
476 return 0;
477 }
478
479 /*
480 * take a single match between satrns. returns 0 if tr1 equals to tr2.
481 * tr1: peer's satrns
482 * tr2: my satrns
483 */
484 int
485 cmpsatrns(tr1, tr2)
486 const struct satrns *tr1, *tr2;
487 {
488 if (tr1->trns_id != tr2->trns_id) {
489 plog(LLV_ERROR, LOCATION, NULL,
490 "trns_id mismatched: "
491 "my:%d peer:%d\n",
492 tr2->trns_id, tr1->trns_id);
493 return 1;
494 }
495 if (tr1->authtype != tr2->authtype) {
496 plog(LLV_ERROR, LOCATION, NULL,
497 "authtype mismatched: "
498 "my:%d peer:%d\n",
499 tr2->authtype, tr1->authtype);
500 return 1;
501 }
502
503 /* XXX
504 * At this moment for interoperability, the responder obey
505 * the initiator. It should be defined a notify message.
506 */
507 if (tr1->encklen > tr2->encklen) {
508 plog(LLV_WARNING, LOCATION, NULL,
509 "less key length proposed, "
510 "mine:%d peer:%d. Use initiaotr's one.\n",
511 tr2->encklen, tr1->encklen);
512 /* FALLTHRU */
513 }
514
515 return 0;
516 }
517
518 int
519 set_satrnsbysainfo(pr, sainfo)
520 struct saproto *pr;
521 struct sainfo *sainfo;
522 {
523 struct sainfoalg *a, *b;
524 struct satrns *newtr;
525 int t;
526
527 switch (pr->proto_id) {
528 case IPSECDOI_PROTO_IPSEC_AH:
529 if (sainfo->algs[algclass_ipsec_auth] == NULL) {
530 plog(LLV_ERROR, LOCATION, NULL,
531 "no auth algorithm found\n");
532 goto err;
533 }
534 t = 1;
535 for (a = sainfo->algs[algclass_ipsec_auth]; a; a = a->next) {
536
537 if (a->alg == IPSECDOI_ATTR_AUTH_NONE)
538 continue;
539
540 /* allocate satrns */
541 newtr = newsatrns();
542 if (newtr == NULL) {
543 plog(LLV_ERROR, LOCATION, NULL,
544 "failed to allocate satrns.\n");
545 goto err;
546 }
547
548 newtr->trns_no = t++;
549 newtr->trns_id = ipsecdoi_authalg2trnsid(a->alg);
550 newtr->authtype = a->alg;
551
552 inssatrns(pr, newtr);
553 }
554 break;
555 case IPSECDOI_PROTO_IPSEC_ESP:
556 if (sainfo->algs[algclass_ipsec_enc] == NULL) {
557 plog(LLV_ERROR, LOCATION, NULL,
558 "no encryption algorithm found\n");
559 goto err;
560 }
561 t = 1;
562 for (a = sainfo->algs[algclass_ipsec_enc]; a; a = a->next) {
563 for (b = sainfo->algs[algclass_ipsec_auth]; b; b = b->next) {
564 /* allocate satrns */
565 newtr = newsatrns();
566 if (newtr == NULL) {
567 plog(LLV_ERROR, LOCATION, NULL,
568 "failed to allocate satrns.\n");
569 goto err;
570 }
571
572 newtr->trns_no = t++;
573 newtr->trns_id = a->alg;
574 newtr->encklen = a->encklen;
575 newtr->authtype = b->alg;
576
577 inssatrns(pr, newtr);
578 }
579 }
580 break;
581 case IPSECDOI_PROTO_IPCOMP:
582 if (sainfo->algs[algclass_ipsec_comp] == NULL) {
583 plog(LLV_ERROR, LOCATION, NULL,
584 "no ipcomp algorithm found\n");
585 goto err;
586 }
587 t = 1;
588 for (a = sainfo->algs[algclass_ipsec_comp]; a; a = a->next) {
589
590 /* allocate satrns */
591 newtr = newsatrns();
592 if (newtr == NULL) {
593 plog(LLV_ERROR, LOCATION, NULL,
594 "failed to allocate satrns.\n");
595 goto err;
596 }
597
598 newtr->trns_no = t++;
599 newtr->trns_id = a->alg;
600 newtr->authtype = IPSECDOI_ATTR_AUTH_NONE; /*no auth*/
601
602 inssatrns(pr, newtr);
603 }
604 break;
605 default:
606 plog(LLV_ERROR, LOCATION, NULL,
607 "unknown proto_id (%d).\n", pr->proto_id);
608 goto err;
609 }
610
611 /* no proposal found */
612 if (pr->head == NULL) {
613 plog(LLV_ERROR, LOCATION, NULL, "no algorithms found.\n");
614 return -1;
615 }
616
617 return 0;
618
619 err:
620 flushsatrns(pr->head);
621 return -1;
622 }
623
624 struct saprop *
625 aproppair2saprop(p0)
626 struct prop_pair *p0;
627 {
628 struct prop_pair *p, *t;
629 struct saprop *newpp;
630 struct saproto *newpr;
631 struct satrns *newtr;
632 u_int8_t *spi;
633
634 if (p0 == NULL)
635 return NULL;
636
637 /* allocate ipsec a sa proposal */
638 newpp = newsaprop();
639 if (newpp == NULL) {
640 plog(LLV_ERROR, LOCATION, NULL,
641 "failed to allocate saprop.\n");
642 return NULL;
643 }
644 newpp->prop_no = p0->prop->p_no;
645 /* lifetime & lifebyte must be updated later */
646
647 for (p = p0; p; p = p->next) {
648
649 /* allocate ipsec sa protocol */
650 newpr = newsaproto();
651 if (newpr == NULL) {
652 plog(LLV_ERROR, LOCATION, NULL,
653 "failed to allocate saproto.\n");
654 goto err;
655 }
656
657 /* check spi size */
658 /* XXX should be handled isakmp cookie */
659 if (sizeof(newpr->spi) < p->prop->spi_size) {
660 plog(LLV_ERROR, LOCATION, NULL,
661 "invalid spi size %d.\n", p->prop->spi_size);
662 goto err;
663 }
664
665 /*
666 * XXX SPI bits are left-filled, for use with IPComp.
667 * we should be switching to variable-length spi field...
668 */
669 newpr->proto_id = p->prop->proto_id;
670 newpr->spisize = p->prop->spi_size;
671 memset(&newpr->spi, 0, sizeof(newpr->spi));
672 spi = (u_int8_t *)&newpr->spi;
673 spi += sizeof(newpr->spi);
674 spi -= p->prop->spi_size;
675 memcpy(spi, p->prop + 1, p->prop->spi_size);
676 newpr->reqid_in = 0;
677 newpr->reqid_out = 0;
678
679 for (t = p; t; t = t->tnext) {
680
681 plog(LLV_DEBUG, LOCATION, NULL,
682 "prop#=%d prot-id=%s spi-size=%d "
683 "#trns=%d trns#=%d trns-id=%s\n",
684 t->prop->p_no,
685 s_ipsecdoi_proto(t->prop->proto_id),
686 t->prop->spi_size, t->prop->num_t,
687 t->trns->t_no,
688 s_ipsecdoi_trns(t->prop->proto_id,
689 t->trns->t_id));
690
691 /* allocate ipsec sa transform */
692 newtr = newsatrns();
693 if (newtr == NULL) {
694 plog(LLV_ERROR, LOCATION, NULL,
695 "failed to allocate satrns.\n");
696 goto err;
697 }
698
699 if (ipsecdoi_t2satrns(t->trns, newpp, newpr, newtr) < 0) {
700 flushsaprop(newpp);
701 return NULL;
702 }
703
704 inssatrns(newpr, newtr);
705 }
706
707 /*
708 * If the peer does not specify encryption mode, use
709 * transport mode by default. This is to conform to
710 * draft-shacham-ippcp-rfc2393bis-08.txt (explicitly specifies
711 * that unspecified == transport), as well as RFC2407
712 * (unspecified == implementation dependent default).
713 */
714 if (newpr->encmode == 0)
715 newpr->encmode = IPSECDOI_ATTR_ENC_MODE_TRNS;
716
717 inssaproto(newpp, newpr);
718 }
719
720 return newpp;
721
722 err:
723 flushsaprop(newpp);
724 return NULL;
725 }
726
727 void
728 flushsaprop(head)
729 struct saprop *head;
730 {
731 struct saprop *p, *save;
732
733 for (p = head; p != NULL; p = save) {
734 save = p->next;
735 flushsaproto(p->head);
736 racoon_free(p);
737 }
738
739 return;
740 }
741
742 void
743 flushsaproto(head)
744 struct saproto *head;
745 {
746 struct saproto *p, *save;
747
748 for (p = head; p != NULL; p = save) {
749 save = p->next;
750 flushsatrns(p->head);
751 vfree(p->keymat);
752 vfree(p->keymat_p);
753 racoon_free(p);
754 }
755
756 return;
757 }
758
759 void
760 flushsatrns(head)
761 struct satrns *head;
762 {
763 struct satrns *p, *save;
764
765 for (p = head; p != NULL; p = save) {
766 save = p->next;
767 racoon_free(p);
768 }
769
770 return;
771 }
772
773 /*
774 * print multiple proposals
775 */
776 void
777 printsaprop(pri, pp)
778 const int pri;
779 const struct saprop *pp;
780 {
781 const struct saprop *p;
782
783 if (pp == NULL) {
784 plog(pri, LOCATION, NULL, "(null)");
785 return;
786 }
787
788 for (p = pp; p; p = p->next) {
789 printsaprop0(pri, p);
790 }
791
792 return;
793 }
794
795 /*
796 * print one proposal.
797 */
798 void
799 printsaprop0(pri, pp)
800 int pri;
801 const struct saprop *pp;
802 {
803 const struct saproto *p;
804
805 if (pp == NULL)
806 return;
807
808 for (p = pp->head; p; p = p->next) {
809 printsaproto(pri, p);
810 }
811
812 return;
813 }
814
815 void
816 printsaproto(pri, pr)
817 const int pri;
818 const struct saproto *pr;
819 {
820 struct satrns *tr;
821
822 if (pr == NULL)
823 return;
824
825 plog(pri, LOCATION, NULL,
826 " (proto_id=%s spisize=%d spi=%08lx spi_p=%08lx "
827 "encmode=%s reqid=%d:%d)\n",
828 s_ipsecdoi_proto(pr->proto_id),
829 pr->spisize,
830 (unsigned long)ntohl(pr->spi),
831 (unsigned long)ntohl(pr->spi_p),
832 s_ipsecdoi_attr_v(IPSECDOI_ATTR_ENC_MODE, pr->encmode),
833 pr->reqid_in, pr->reqid_out);
834
835 for (tr = pr->head; tr; tr = tr->next) {
836 printsatrns(pri, pr->proto_id, tr);
837 }
838
839 return;
840 }
841
842 void
843 printsatrns(pri, proto_id, tr)
844 const int pri;
845 const int proto_id;
846 const struct satrns *tr;
847 {
848 if (tr == NULL)
849 return;
850
851 switch (proto_id) {
852 case IPSECDOI_PROTO_IPSEC_AH:
853 plog(pri, LOCATION, NULL,
854 " (trns_id=%s authtype=%s)\n",
855 s_ipsecdoi_trns(proto_id, tr->trns_id),
856 s_ipsecdoi_attr_v(IPSECDOI_ATTR_AUTH, tr->authtype));
857 break;
858 case IPSECDOI_PROTO_IPSEC_ESP:
859 plog(pri, LOCATION, NULL,
860 " (trns_id=%s encklen=%d authtype=%s)\n",
861 s_ipsecdoi_trns(proto_id, tr->trns_id),
862 tr->encklen,
863 s_ipsecdoi_attr_v(IPSECDOI_ATTR_AUTH, tr->authtype));
864 break;
865 case IPSECDOI_PROTO_IPCOMP:
866 plog(pri, LOCATION, NULL,
867 " (trns_id=%s)\n",
868 s_ipsecdoi_trns(proto_id, tr->trns_id));
869 break;
870 default:
871 plog(pri, LOCATION, NULL,
872 "(unknown proto_id %d)\n", proto_id);
873 }
874
875 return;
876 }
877
878 void
879 print_proppair0(pri, p, level)
880 int pri;
881 struct prop_pair *p;
882 int level;
883 {
884 char spc[21];
885
886 memset(spc, ' ', sizeof(spc));
887 spc[sizeof(spc) - 1] = '\0';
888 if (level < 20) {
889 spc[level] = '\0';
890 }
891
892 plog(pri, LOCATION, NULL,
893 "%s%p: next=%p tnext=%p\n", spc, p, p->next, p->tnext);
894 if (p->next)
895 print_proppair0(pri, p->next, level + 1);
896 if (p->tnext)
897 print_proppair0(pri, p->tnext, level + 1);
898 }
899
900 void
901 print_proppair(pri, p)
902 int pri;
903 struct prop_pair *p;
904 {
905 print_proppair0(pri, p, 1);
906 }
907
908 int
909 set_proposal_from_policy(iph2, sp_main, sp_sub)
910 struct ph2handle *iph2;
911 struct secpolicy *sp_main, *sp_sub;
912 {
913 struct saprop *newpp;
914 struct ipsecrequest *req;
915 int encmodesv = IPSEC_MODE_TRANSPORT; /* use only when complex_bundle */
916
917 newpp = newsaprop();
918 if (newpp == NULL) {
919 plog(LLV_ERROR, LOCATION, NULL,
920 "failed to allocate saprop.\n");
921 goto err;
922 }
923 newpp->prop_no = 1;
924 newpp->lifetime = iph2->sainfo->lifetime;
925 newpp->lifebyte = iph2->sainfo->lifebyte;
926 newpp->pfs_group = iph2->sainfo->pfs_group;
927
928 if (lcconf->complex_bundle)
929 goto skip1;
930
931 /*
932 * decide the encryption mode of this SA bundle.
933 * the mode becomes tunnel mode when there is even one policy
934 * of tunnel mode in the SPD. otherwise the mode becomes
935 * transport mode.
936 */
937 encmodesv = IPSEC_MODE_TRANSPORT;
938 for (req = sp_main->req; req; req = req->next) {
939 if (req->saidx.mode == IPSEC_MODE_TUNNEL) {
940 encmodesv = pfkey2ipsecdoi_mode(req->saidx.mode);
941 break;
942 }
943 }
944
945 skip1:
946 for (req = sp_main->req; req; req = req->next) {
947 struct saproto *newpr;
948 caddr_t paddr = NULL;
949
950 /*
951 * check if SA bundle ?
952 * nested SAs negotiation is NOT supported.
953 * me +--- SA1 ---+ peer1
954 * me +--- SA2 --------------+ peer2
955 */
956 if (req->saidx.src.ss_len && req->saidx.dst.ss_len) {
957
958 /* check the end of ip addresses of SA */
959 if (iph2->side == INITIATOR)
960 paddr = (caddr_t)&req->saidx.dst;
961 else
962 paddr = (caddr_t)&req->saidx.src;
963
964 if (memcmp(iph2->dst, paddr, iph2->dst->sa_len)){
965 plog(LLV_ERROR, LOCATION, NULL,
966 "not supported nested SA.");
967 goto err;
968 }
969 }
970
971 /* allocate ipsec sa protocol */
972 newpr = newsaproto();
973 if (newpr == NULL) {
974 plog(LLV_ERROR, LOCATION, NULL,
975 "failed to allocate saproto.\n");
976 goto err;
977 }
978
979 newpr->proto_id = ipproto2doi(req->saidx.proto);
980 newpr->spisize = 4;
981 if (lcconf->complex_bundle)
982 newpr->encmode = pfkey2ipsecdoi_mode(req->saidx.mode);
983 else
984 newpr->encmode = encmodesv;
985
986 newpr->reqid_out = req->saidx.reqid;
987
988 if (set_satrnsbysainfo(newpr, iph2->sainfo) < 0) {
989 plog(LLV_ERROR, LOCATION, NULL,
990 "failed to get algorithms.\n");
991 goto err;
992 }
993
994 /* set new saproto */
995 inssaprotorev(newpp, newpr);
996 }
997
998 /* get reqid_in from inbound policy */
999 if (sp_sub) {
1000 struct saproto *pr;
1001
1002 req = sp_sub->req;
1003 pr = newpp->head;
1004 while (req && pr) {
1005 pr->reqid_in = req->saidx.reqid;
1006 pr = pr->next;
1007 req = req->next;
1008 }
1009 if (pr || req) {
1010 plog(LLV_NOTIFY, LOCATION, NULL,
1011 "There is a difference "
1012 "between the in/out bound policies in SPD.\n");
1013 }
1014 }
1015
1016 iph2->proposal = newpp;
1017
1018 printsaprop0(LLV_DEBUG, newpp);
1019
1020 return 0;
1021 err:
1022 return -1;
1023 }
1024
1025 /*
1026 * generate a policy from peer's proposal.
1027 * this function unconditionally choices first proposal in SA payload
1028 * passed by peer.
1029 */
1030 int
1031 set_proposal_from_proposal(iph2)
1032 struct ph2handle *iph2;
1033 {
1034 struct saprop *newpp = NULL, *pp0, *pp_peer;
1035 struct saproto *newpr = NULL, *pr;
1036 struct prop_pair **pair;
1037 int error = -1;
1038 int i;
1039
1040 /* get proposal pair */
1041 pair = get_proppair(iph2->sa, IPSECDOI_TYPE_PH2);
1042 if (pair == NULL)
1043 goto end;
1044
1045 /*
1046 * make my proposal according as the client proposal.
1047 * XXX assumed there is only one proposal even if it's the SA bundle.
1048 */
1049 for (i = 0; i < MAXPROPPAIRLEN; i++) {
1050 if (pair[i] == NULL)
1051 continue;
1052 pp_peer = aproppair2saprop(pair[i]);
1053 if (pp_peer == NULL)
1054 goto end;
1055
1056 pp0 = newsaprop();
1057 if (pp0 == NULL) {
1058 plog(LLV_ERROR, LOCATION, NULL,
1059 "failed to allocate saprop.\n");
1060 goto end;
1061 }
1062 pp0->prop_no = 1;
1063 pp0->lifetime = iph2->sainfo->lifetime;
1064 pp0->lifebyte = iph2->sainfo->lifebyte;
1065 pp0->pfs_group = iph2->sainfo->pfs_group;
1066
1067 if (pp_peer->next != NULL) {
1068 plog(LLV_ERROR, LOCATION, NULL,
1069 "pp_peer is inconsistency, ignore it.\n");
1070 /*FALLTHROUGH*/
1071 }
1072
1073 for (pr = pp_peer->head; pr; pr = pr->next) {
1074
1075 newpr = newsaproto();
1076 if (newpr == NULL) {
1077 plog(LLV_ERROR, LOCATION, NULL,
1078 "failed to allocate saproto.\n");
1079 goto end;
1080 }
1081 newpr->proto_id = pr->proto_id;
1082 newpr->spisize = pr->spisize;
1083 newpr->encmode = pr->encmode;
1084 newpr->spi = 0;
1085 newpr->spi_p = pr->spi; /* copy peer's SPI */
1086 newpr->reqid_in = 0;
1087 newpr->reqid_out = 0;
1088 }
1089
1090 if (set_satrnsbysainfo(newpr, iph2->sainfo) < 0) {
1091 plog(LLV_ERROR, LOCATION, NULL,
1092 "failed to get algorithms.\n");
1093 goto end;
1094 }
1095
1096 inssaproto(pp0, newpr);
1097 inssaprop(&newpp, pp0);
1098 }
1099
1100 plog(LLV_DEBUG, LOCATION, NULL, "make a proposal from peer's:\n");
1101 printsaprop0(LLV_DEBUG, newpp);
1102
1103 iph2->proposal = newpp;
1104
1105 error = 0;
1106
1107 end:
1108 if (error && newpp)
1109 flushsaprop(newpp);
1110
1111 free_proppair(pair);
1112 return error;
1113 }