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