]>
Commit | Line | Data |
---|---|---|
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 | */ | |
72 | struct saprop * | |
73 | newsaprop() | |
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 | ||
84 | struct saproto * | |
85 | newsaproto() | |
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 */ | |
97 | void | |
98 | inssaprop(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 */ | |
117 | void | |
118 | inssaproto(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 */ | |
135 | void | |
136 | inssaprotorev(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 | ||
146 | struct satrns * | |
147 | newsatrns() | |
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 */ | |
159 | void | |
160 | inssatrns(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 | */ | |
186 | struct saprop * | |
187 | cmpsaprop_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 | ||
447 | err: | |
448 | flushsaprop(newpp); | |
449 | return NULL; | |
450 | } | |
451 | ||
452 | /* take a single match between saprop. returns 0 if pp1 equals to pp2. */ | |
453 | int | |
454 | cmpsaprop(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 | */ | |
485 | int | |
486 | cmpsatrns(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 | ||
519 | int | |
520 | set_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 | ||
620 | err: | |
621 | flushsatrns(pr->head); | |
622 | return -1; | |
623 | } | |
624 | ||
625 | struct saprop * | |
626 | aproppair2saprop(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 | ||
723 | err: | |
724 | flushsaprop(newpp); | |
725 | return NULL; | |
726 | } | |
727 | ||
728 | void | |
729 | flushsaprop(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 | ||
743 | void | |
744 | flushsaproto(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 | ||
760 | void | |
761 | flushsatrns(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 | */ | |
777 | void | |
778 | printsaprop(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 | */ | |
799 | void | |
800 | printsaprop0(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 | ||
816 | void | |
817 | printsaproto(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 | ||
843 | void | |
844 | printsatrns(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 | ||
879 | void | |
880 | print_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 | ||
901 | void | |
902 | print_proppair(pri, p) | |
903 | int pri; | |
904 | struct prop_pair *p; | |
905 | { | |
906 | print_proppair0(pri, p, 1); | |
907 | } | |
908 | ||
909 | int | |
910 | set_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; | |
1030 | err: | |
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 | */ | |
1039 | int | |
1040 | set_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 | ||
1116 | end: | |
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 | } |