ha: Add DH group to IKE_ADD message
[strongswan.git] / src / libcharon / plugins / ha / ha_message.c
1 /*
2 * Copyright (C) 2008 Martin Willi
3 * Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #define _GNU_SOURCE
17 #include <string.h>
18 #include <arpa/inet.h>
19
20 #include "ha_message.h"
21
22 #include <daemon.h>
23
24 #define ALLOCATION_BLOCK 64
25
26 typedef struct private_ha_message_t private_ha_message_t;
27
28 /**
29 * Private data of an ha_message_t object.
30 */
31 struct private_ha_message_t {
32
33 /**
34 * Public ha_message_t interface.
35 */
36 ha_message_t public;
37
38 /**
39 * Allocated size of buf
40 */
41 size_t allocated;
42
43 /**
44 * Buffer containing encoded data
45 */
46 chunk_t buf;
47 };
48
49 ENUM(ha_message_type_names, HA_IKE_ADD, HA_IKE_IV,
50 "IKE_ADD",
51 "IKE_UPDATE",
52 "IKE_MID_INITIATOR",
53 "IKE_MID_RESPONDER",
54 "IKE_DELETE",
55 "CHILD_ADD",
56 "CHILD_DELETE",
57 "SEGMENT_DROP",
58 "SEGMENT_TAKE",
59 "STATUS",
60 "RESYNC",
61 "IKE_IV",
62 );
63
64 typedef struct ike_sa_id_encoding_t ike_sa_id_encoding_t;
65
66 /**
67 * Encoding if an ike_sa_id_t
68 */
69 struct ike_sa_id_encoding_t {
70 u_int8_t ike_version;
71 u_int64_t initiator_spi;
72 u_int64_t responder_spi;
73 u_int8_t initiator;
74 } __attribute__((packed));
75
76 typedef struct identification_encoding_t identification_encoding_t;
77
78 /**
79 * Encoding of a identification_t
80 */
81 struct identification_encoding_t {
82 u_int8_t type;
83 u_int8_t len;
84 char encoding[];
85 } __attribute__((packed));
86
87 typedef struct host_encoding_t host_encoding_t;
88
89 /**
90 * encoding of a host_t
91 */
92 struct host_encoding_t {
93 u_int16_t port;
94 u_int8_t family;
95 char encoding[];
96 } __attribute__((packed));
97
98 typedef struct ts_encoding_t ts_encoding_t;
99
100 /**
101 * encoding of a traffic_selector_t
102 */
103 struct ts_encoding_t {
104 u_int8_t type;
105 u_int8_t protocol;
106 u_int16_t from_port;
107 u_int16_t to_port;
108 u_int8_t dynamic;
109 char encoding[];
110 } __attribute__((packed));
111
112 METHOD(ha_message_t, get_type, ha_message_type_t,
113 private_ha_message_t *this)
114 {
115 return this->buf.ptr[1];
116 }
117
118 /**
119 * check for space in buffer, increase if necessary
120 */
121 static void check_buf(private_ha_message_t *this, size_t len)
122 {
123 int increased = 0;
124
125 while (this->buf.len + len > this->allocated)
126 { /* double size */
127 this->allocated += ALLOCATION_BLOCK;
128 increased++;
129 }
130 if (increased)
131 {
132 this->buf.ptr = realloc(this->buf.ptr, this->allocated);
133 }
134 }
135
136 METHOD(ha_message_t, add_attribute, void,
137 private_ha_message_t *this, ha_message_attribute_t attribute, ...)
138 {
139 size_t len;
140 va_list args;
141
142 check_buf(this, sizeof(u_int8_t));
143 this->buf.ptr[this->buf.len] = attribute;
144 this->buf.len += sizeof(u_int8_t);
145
146 va_start(args, attribute);
147 switch (attribute)
148 {
149 /* ike_sa_id_t* */
150 case HA_IKE_ID:
151 case HA_IKE_REKEY_ID:
152 {
153 ike_sa_id_encoding_t *enc;
154 ike_sa_id_t *id;
155
156 id = va_arg(args, ike_sa_id_t*);
157 check_buf(this, sizeof(ike_sa_id_encoding_t));
158 enc = (ike_sa_id_encoding_t*)(this->buf.ptr + this->buf.len);
159 this->buf.len += sizeof(ike_sa_id_encoding_t);
160 enc->initiator = id->is_initiator(id);
161 enc->ike_version = id->get_ike_version(id);
162 enc->initiator_spi = id->get_initiator_spi(id);
163 enc->responder_spi = id->get_responder_spi(id);
164 break;
165 }
166 /* identification_t* */
167 case HA_LOCAL_ID:
168 case HA_REMOTE_ID:
169 case HA_REMOTE_EAP_ID:
170 {
171 identification_encoding_t *enc;
172 identification_t *id;
173 chunk_t data;
174
175 id = va_arg(args, identification_t*);
176 data = id->get_encoding(id);
177 check_buf(this, sizeof(identification_encoding_t) + data.len);
178 enc = (identification_encoding_t*)(this->buf.ptr + this->buf.len);
179 this->buf.len += sizeof(identification_encoding_t) + data.len;
180 enc->type = id->get_type(id);
181 enc->len = data.len;
182 memcpy(enc->encoding, data.ptr, data.len);
183 break;
184 }
185 /* host_t* */
186 case HA_LOCAL_ADDR:
187 case HA_REMOTE_ADDR:
188 case HA_LOCAL_VIP:
189 case HA_REMOTE_VIP:
190 case HA_PEER_ADDR:
191 {
192 host_encoding_t *enc;
193 host_t *host;
194 chunk_t data;
195
196 host = va_arg(args, host_t*);
197 data = host->get_address(host);
198 check_buf(this, sizeof(host_encoding_t) + data.len);
199 enc = (host_encoding_t*)(this->buf.ptr + this->buf.len);
200 this->buf.len += sizeof(host_encoding_t) + data.len;
201 enc->family = host->get_family(host);
202 enc->port = htons(host->get_port(host));
203 memcpy(enc->encoding, data.ptr, data.len);
204 break;
205 }
206 /* char* */
207 case HA_CONFIG_NAME:
208 {
209 char *str;
210
211 str = va_arg(args, char*);
212 len = strlen(str) + 1;
213 check_buf(this, len);
214 memcpy(this->buf.ptr + this->buf.len, str, len);
215 this->buf.len += len;
216 break;
217 }
218 /* u_int8_t */
219 case HA_IKE_VERSION:
220 case HA_INITIATOR:
221 case HA_IPSEC_MODE:
222 case HA_IPCOMP:
223 {
224 u_int8_t val;
225
226 val = va_arg(args, u_int);
227 check_buf(this, sizeof(val));
228 this->buf.ptr[this->buf.len] = val;
229 this->buf.len += sizeof(val);
230 break;
231 }
232 /* u_int16_t */
233 case HA_ALG_DH:
234 case HA_ALG_PRF:
235 case HA_ALG_OLD_PRF:
236 case HA_ALG_ENCR:
237 case HA_ALG_ENCR_LEN:
238 case HA_ALG_INTEG:
239 case HA_INBOUND_CPI:
240 case HA_OUTBOUND_CPI:
241 case HA_SEGMENT:
242 case HA_ESN:
243 {
244 u_int16_t val;
245
246 val = va_arg(args, u_int);
247 check_buf(this, sizeof(val));
248 *(u_int16_t*)(this->buf.ptr + this->buf.len) = htons(val);
249 this->buf.len += sizeof(val);
250 break;
251 }
252 /** u_int32_t */
253 case HA_CONDITIONS:
254 case HA_EXTENSIONS:
255 case HA_INBOUND_SPI:
256 case HA_OUTBOUND_SPI:
257 case HA_MID:
258 {
259 u_int32_t val;
260
261 val = va_arg(args, u_int);
262 check_buf(this, sizeof(val));
263 *(u_int32_t*)(this->buf.ptr + this->buf.len) = htonl(val);
264 this->buf.len += sizeof(val);
265 break;
266 }
267 /** chunk_t */
268 case HA_NONCE_I:
269 case HA_NONCE_R:
270 case HA_SECRET:
271 case HA_LOCAL_DH:
272 case HA_REMOTE_DH:
273 case HA_PSK:
274 case HA_IV:
275 case HA_OLD_SKD:
276 {
277 chunk_t chunk;
278
279 chunk = va_arg(args, chunk_t);
280 check_buf(this, chunk.len + sizeof(u_int16_t));
281 *(u_int16_t*)(this->buf.ptr + this->buf.len) = htons(chunk.len);
282 memcpy(this->buf.ptr + this->buf.len + sizeof(u_int16_t),
283 chunk.ptr, chunk.len);
284 this->buf.len += chunk.len + sizeof(u_int16_t);;
285 break;
286 }
287 /** traffic_selector_t */
288 case HA_LOCAL_TS:
289 case HA_REMOTE_TS:
290 {
291 ts_encoding_t *enc;
292 traffic_selector_t *ts;
293 chunk_t data;
294
295 ts = va_arg(args, traffic_selector_t*);
296 data = chunk_cata("cc", ts->get_from_address(ts),
297 ts->get_to_address(ts));
298 check_buf(this, sizeof(ts_encoding_t) + data.len);
299 enc = (ts_encoding_t*)(this->buf.ptr + this->buf.len);
300 this->buf.len += sizeof(ts_encoding_t) + data.len;
301 enc->type = ts->get_type(ts);
302 enc->protocol = ts->get_protocol(ts);
303 enc->from_port = htons(ts->get_from_port(ts));
304 enc->to_port = htons(ts->get_to_port(ts));
305 enc->dynamic = ts->is_dynamic(ts);
306 memcpy(enc->encoding, data.ptr, data.len);
307 break;
308 }
309 default:
310 {
311 DBG1(DBG_CFG, "unable to encode, attribute %d unknown", attribute);
312 this->buf.len -= sizeof(u_int8_t);
313 break;
314 }
315 }
316 va_end(args);
317 }
318
319 /**
320 * Attribute enumerator implementation
321 */
322 typedef struct {
323 /** implementes enumerator_t */
324 enumerator_t public;
325 /** position in message */
326 chunk_t buf;
327 /** cleanup handler of current element, if any */
328 void (*cleanup)(void* data);
329 /** data to pass to cleanup handler */
330 void *cleanup_data;
331 } attribute_enumerator_t;
332
333 METHOD(enumerator_t, attribute_enumerate, bool,
334 attribute_enumerator_t *this, ha_message_attribute_t *attr_out,
335 ha_message_value_t *value)
336 {
337 ha_message_attribute_t attr;
338
339 if (this->cleanup)
340 {
341 this->cleanup(this->cleanup_data);
342 this->cleanup = NULL;
343 }
344 if (this->buf.len < 1)
345 {
346 return FALSE;
347 }
348 attr = this->buf.ptr[0];
349 this->buf = chunk_skip(this->buf, 1);
350 switch (attr)
351 {
352 /* ike_sa_id_t* */
353 case HA_IKE_ID:
354 case HA_IKE_REKEY_ID:
355 {
356 ike_sa_id_encoding_t *enc;
357
358 if (this->buf.len < sizeof(ike_sa_id_encoding_t))
359 {
360 return FALSE;
361 }
362 enc = (ike_sa_id_encoding_t*)(this->buf.ptr);
363 value->ike_sa_id = ike_sa_id_create(enc->ike_version,
364 enc->initiator_spi, enc->responder_spi,
365 enc->initiator);
366 *attr_out = attr;
367 this->cleanup = (void*)value->ike_sa_id->destroy;
368 this->cleanup_data = value->ike_sa_id;
369 this->buf = chunk_skip(this->buf, sizeof(ike_sa_id_encoding_t));
370 return TRUE;
371 }
372 /* identification_t* */
373 case HA_LOCAL_ID:
374 case HA_REMOTE_ID:
375 case HA_REMOTE_EAP_ID:
376 {
377 identification_encoding_t *enc;
378
379 enc = (identification_encoding_t*)(this->buf.ptr);
380 if (this->buf.len < sizeof(identification_encoding_t) ||
381 this->buf.len < sizeof(identification_encoding_t) + enc->len)
382 {
383 return FALSE;
384 }
385 value->id = identification_create_from_encoding(enc->type,
386 chunk_create(enc->encoding, enc->len));
387 *attr_out = attr;
388 this->cleanup = (void*)value->id->destroy;
389 this->cleanup_data = value->id;
390 this->buf = chunk_skip(this->buf,
391 sizeof(identification_encoding_t) + enc->len);
392 return TRUE;
393 }
394 /* host_t* */
395 case HA_LOCAL_ADDR:
396 case HA_REMOTE_ADDR:
397 case HA_LOCAL_VIP:
398 case HA_REMOTE_VIP:
399 case HA_PEER_ADDR:
400 {
401 host_encoding_t *enc;
402
403 enc = (host_encoding_t*)(this->buf.ptr);
404 if (this->buf.len < sizeof(host_encoding_t))
405 {
406 return FALSE;
407 }
408 value->host = host_create_from_chunk(enc->family,
409 chunk_create(enc->encoding,
410 this->buf.len - sizeof(host_encoding_t)),
411 ntohs(enc->port));
412 if (!value->host)
413 {
414 return FALSE;
415 }
416 *attr_out = attr;
417 this->cleanup = (void*)value->host->destroy;
418 this->cleanup_data = value->host;
419 this->buf = chunk_skip(this->buf, sizeof(host_encoding_t) +
420 value->host->get_address(value->host).len);
421 return TRUE;
422 }
423 /* char* */
424 case HA_CONFIG_NAME:
425 {
426 size_t len;
427
428 len = strnlen(this->buf.ptr, this->buf.len);
429 if (len >= this->buf.len)
430 {
431 return FALSE;
432 }
433 value->str = this->buf.ptr;
434 *attr_out = attr;
435 this->buf = chunk_skip(this->buf, len + 1);
436 return TRUE;
437 }
438 /* u_int8_t */
439 case HA_IKE_VERSION:
440 case HA_INITIATOR:
441 case HA_IPSEC_MODE:
442 case HA_IPCOMP:
443 {
444 if (this->buf.len < sizeof(u_int8_t))
445 {
446 return FALSE;
447 }
448 value->u8 = *(u_int8_t*)this->buf.ptr;
449 *attr_out = attr;
450 this->buf = chunk_skip(this->buf, sizeof(u_int8_t));
451 return TRUE;
452 }
453 /** u_int16_t */
454 case HA_ALG_DH:
455 case HA_ALG_PRF:
456 case HA_ALG_OLD_PRF:
457 case HA_ALG_ENCR:
458 case HA_ALG_ENCR_LEN:
459 case HA_ALG_INTEG:
460 case HA_INBOUND_CPI:
461 case HA_OUTBOUND_CPI:
462 case HA_SEGMENT:
463 case HA_ESN:
464 {
465 if (this->buf.len < sizeof(u_int16_t))
466 {
467 return FALSE;
468 }
469 value->u16 = ntohs(*(u_int16_t*)this->buf.ptr);
470 *attr_out = attr;
471 this->buf = chunk_skip(this->buf, sizeof(u_int16_t));
472 return TRUE;
473 }
474 /** u_int32_t */
475 case HA_CONDITIONS:
476 case HA_EXTENSIONS:
477 case HA_INBOUND_SPI:
478 case HA_OUTBOUND_SPI:
479 case HA_MID:
480 {
481 if (this->buf.len < sizeof(u_int32_t))
482 {
483 return FALSE;
484 }
485 value->u32 = ntohl(*(u_int32_t*)this->buf.ptr);
486 *attr_out = attr;
487 this->buf = chunk_skip(this->buf, sizeof(u_int32_t));
488 return TRUE;
489 }
490 /** chunk_t */
491 case HA_NONCE_I:
492 case HA_NONCE_R:
493 case HA_SECRET:
494 case HA_LOCAL_DH:
495 case HA_REMOTE_DH:
496 case HA_PSK:
497 case HA_IV:
498 case HA_OLD_SKD:
499 {
500 size_t len;
501
502 if (this->buf.len < sizeof(u_int16_t))
503 {
504 return FALSE;
505 }
506 len = ntohs(*(u_int16_t*)this->buf.ptr);
507 this->buf = chunk_skip(this->buf, sizeof(u_int16_t));
508 if (this->buf.len < len)
509 {
510 return FALSE;
511 }
512 value->chunk.len = len;
513 value->chunk.ptr = this->buf.ptr;
514 *attr_out = attr;
515 this->buf = chunk_skip(this->buf, len);
516 return TRUE;
517 }
518 case HA_LOCAL_TS:
519 case HA_REMOTE_TS:
520 {
521 ts_encoding_t *enc;
522 host_t *host;
523 int addr_len;
524
525 enc = (ts_encoding_t*)(this->buf.ptr);
526 if (this->buf.len < sizeof(ts_encoding_t))
527 {
528 return FALSE;
529 }
530 switch (enc->type)
531 {
532 case TS_IPV4_ADDR_RANGE:
533 addr_len = 4;
534 if (this->buf.len < sizeof(ts_encoding_t) + 2 * addr_len)
535 {
536 return FALSE;
537 }
538 break;
539 case TS_IPV6_ADDR_RANGE:
540 addr_len = 16;
541 if (this->buf.len < sizeof(ts_encoding_t) + 2 * addr_len)
542 {
543 return FALSE;
544 }
545 break;
546 default:
547 return FALSE;
548 }
549 if (enc->dynamic)
550 {
551 host = host_create_from_chunk(0,
552 chunk_create(enc->encoding, addr_len), 0);
553 if (!host)
554 {
555 return FALSE;
556 }
557 value->ts = traffic_selector_create_dynamic(enc->protocol,
558 ntohs(enc->from_port), ntohs(enc->to_port));
559 value->ts->set_address(value->ts, host);
560 host->destroy(host);
561 }
562 else
563 {
564 value->ts = traffic_selector_create_from_bytes(enc->protocol,
565 enc->type, chunk_create(enc->encoding, addr_len),
566 ntohs(enc->from_port),
567 chunk_create(enc->encoding + addr_len, addr_len),
568 ntohs(enc->to_port));
569 if (!value->ts)
570 {
571 return FALSE;
572 }
573 }
574 *attr_out = attr;
575 this->cleanup = (void*)value->ts->destroy;
576 this->cleanup_data = value->ts;
577 this->buf = chunk_skip(this->buf, sizeof(ts_encoding_t)
578 + addr_len * 2);
579 return TRUE;
580 }
581 default:
582 {
583 return FALSE;
584 }
585 }
586 }
587
588 METHOD(enumerator_t, enum_destroy, void,
589 attribute_enumerator_t *this)
590 {
591 if (this->cleanup)
592 {
593 this->cleanup(this->cleanup_data);
594 }
595 free(this);
596 }
597
598 METHOD(ha_message_t, create_attribute_enumerator, enumerator_t*,
599 private_ha_message_t *this)
600 {
601 attribute_enumerator_t *e;
602
603 INIT(e,
604 .public = {
605 .enumerate = (void*)_attribute_enumerate,
606 .destroy = _enum_destroy,
607 },
608 .buf = chunk_skip(this->buf, 2),
609 );
610
611 return &e->public;
612 }
613
614 METHOD(ha_message_t, get_encoding, chunk_t,
615 private_ha_message_t *this)
616 {
617 return this->buf;
618 }
619
620 METHOD(ha_message_t, destroy, void,
621 private_ha_message_t *this)
622 {
623 free(this->buf.ptr);
624 free(this);
625 }
626
627
628 static private_ha_message_t *ha_message_create_generic()
629 {
630 private_ha_message_t *this;
631
632 INIT(this,
633 .public = {
634 .get_type = _get_type,
635 .add_attribute = _add_attribute,
636 .create_attribute_enumerator = _create_attribute_enumerator,
637 .get_encoding = _get_encoding,
638 .destroy = _destroy,
639 },
640 );
641 return this;
642 }
643
644 /**
645 * See header
646 */
647 ha_message_t *ha_message_create(ha_message_type_t type)
648 {
649 private_ha_message_t *this = ha_message_create_generic();
650
651 this->allocated = ALLOCATION_BLOCK;
652 this->buf.ptr = malloc(this->allocated);
653 this->buf.len = 2;
654 this->buf.ptr[0] = HA_MESSAGE_VERSION;
655 this->buf.ptr[1] = type;
656
657 return &this->public;
658 }
659
660 /**
661 * See header
662 */
663 ha_message_t *ha_message_parse(chunk_t data)
664 {
665 private_ha_message_t *this;
666
667 if (data.len < 2)
668 {
669 DBG1(DBG_CFG, "HA message too short");
670 return NULL;
671 }
672 if (data.ptr[0] != HA_MESSAGE_VERSION)
673 {
674 DBG1(DBG_CFG, "HA message has version %d, expected %d",
675 data.ptr[0], HA_MESSAGE_VERSION);
676 return NULL;
677 }
678
679 this = ha_message_create_generic();
680 this->buf = chunk_clone(data);
681 this->allocated = this->buf.len;
682
683 return &this->public;
684 }
685