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