3766168f41f94f1d417d7361d545f7e7ffc190f6
[strongswan.git] / src / charon / plugins / ha_sync / ha_sync_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_sync_message.h"
21
22 #include <daemon.h>
23
24 #define ALLOCATION_BLOCK 64
25
26 typedef struct private_ha_sync_message_t private_ha_sync_message_t;
27
28 /**
29 * Private data of an ha_sync_message_t object.
30 */
31 struct private_ha_sync_message_t {
32
33 /**
34 * Public ha_sync_message_t interface.
35 */
36 ha_sync_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 typedef struct ike_sa_id_encoding_t ike_sa_id_encoding_t;
50
51 /**
52 * Encoding if an ike_sa_id_t
53 */
54 struct ike_sa_id_encoding_t {
55 u_int64_t initiator_spi;
56 u_int64_t responder_spi;
57 u_int8_t initiator;
58 } __attribute__((packed));
59
60 typedef struct identification_encoding_t identification_encoding_t;
61
62 /**
63 * Encoding of a identification_t
64 */
65 struct identification_encoding_t {
66 u_int8_t type;
67 u_int8_t len;
68 char encoding[];
69 } __attribute__((packed));
70
71 typedef struct host_encoding_t host_encoding_t;
72
73 /**
74 * encoding of a host_t
75 */
76 struct host_encoding_t {
77 u_int16_t port;
78 u_int8_t family;
79 char encoding[];
80 } __attribute__((packed));
81
82 typedef struct ts_encoding_t ts_encoding_t;
83
84 /**
85 * encoding of a traffic_selector_t
86 */
87 struct ts_encoding_t {
88 u_int8_t type;
89 u_int8_t protocol;
90 u_int16_t from_port;
91 u_int16_t to_port;
92 u_int8_t dynamic;
93 char encoding[];
94 } __attribute__((packed));
95
96 /**
97 * Implementation of ha_sync_message_t.get_type
98 */
99 static ha_sync_message_type_t get_type(private_ha_sync_message_t *this)
100 {
101 return this->buf.ptr[1];
102 }
103
104 /**
105 * check for space in buffer, increase if necessary
106 */
107 static void check_buf(private_ha_sync_message_t *this, size_t len)
108 {
109 int increased = 0;
110
111 while (this->buf.len + len > this->allocated)
112 { /* double size */
113 this->allocated += ALLOCATION_BLOCK;
114 increased++;
115 }
116 if (increased)
117 {
118 this->buf.ptr = realloc(this->buf.ptr, this->allocated);
119 }
120 }
121
122 /**
123 * Implementation of ha_sync_message_t.add_attribute
124 */
125 static void add_attribute(private_ha_sync_message_t *this,
126 ha_sync_message_attribute_t attribute, ...)
127 {
128 size_t len;
129 va_list args;
130
131 check_buf(this, sizeof(u_int8_t));
132 this->buf.ptr[this->buf.len] = attribute;
133 this->buf.len += sizeof(u_int8_t);
134
135 va_start(args, attribute);
136 switch (attribute)
137 {
138 /* ike_sa_id_t* */
139 case HA_SYNC_IKE_ID:
140 case HA_SYNC_IKE_REKEY_ID:
141 {
142 ike_sa_id_encoding_t *enc;
143 ike_sa_id_t *id;
144
145 id = va_arg(args, ike_sa_id_t*);
146 check_buf(this, sizeof(ike_sa_id_encoding_t));
147 enc = (ike_sa_id_encoding_t*)(this->buf.ptr + this->buf.len);
148 this->buf.len += sizeof(ike_sa_id_encoding_t);
149 enc->initiator = id->is_initiator(id);
150 enc->initiator_spi = id->get_initiator_spi(id);
151 enc->responder_spi = id->get_responder_spi(id);
152 break;
153 }
154 /* identification_t* */
155 case HA_SYNC_LOCAL_ID:
156 case HA_SYNC_REMOTE_ID:
157 case HA_SYNC_EAP_ID:
158 {
159 identification_encoding_t *enc;
160 identification_t *id;
161 chunk_t data;
162
163 id = va_arg(args, identification_t*);
164 data = id->get_encoding(id);
165 check_buf(this, sizeof(identification_encoding_t) + data.len);
166 enc = (identification_encoding_t*)(this->buf.ptr + this->buf.len);
167 this->buf.len += sizeof(identification_encoding_t) + data.len;
168 enc->type = id->get_type(id);
169 enc->len = data.len;
170 memcpy(enc->encoding, data.ptr, data.len);
171 break;
172 }
173 /* host_t* */
174 case HA_SYNC_LOCAL_ADDR:
175 case HA_SYNC_REMOTE_ADDR:
176 case HA_SYNC_LOCAL_VIP:
177 case HA_SYNC_REMOTE_VIP:
178 case HA_SYNC_ADDITIONAL_ADDR:
179 {
180 host_encoding_t *enc;
181 host_t *host;
182 chunk_t data;
183
184 host = va_arg(args, host_t*);
185 data = host->get_address(host);
186 check_buf(this, sizeof(host_encoding_t) + data.len);
187 enc = (host_encoding_t*)(this->buf.ptr + this->buf.len);
188 this->buf.len += sizeof(host_encoding_t) + data.len;
189 enc->family = host->get_family(host);
190 enc->port = htons(host->get_port(host));
191 memcpy(enc->encoding, data.ptr, data.len);
192 break;
193 }
194 /* char* */
195 case HA_SYNC_CONFIG_NAME:
196 {
197 char *str;
198
199 str = va_arg(args, char*);
200 len = strlen(str) + 1;
201 check_buf(this, len);
202 memcpy(this->buf.ptr + this->buf.len, str, len);
203 this->buf.len += len;
204 break;
205 }
206 /* u_int8_t */
207 case HA_SYNC_IPSEC_MODE:
208 case HA_SYNC_IPCOMP:
209 {
210 u_int8_t val;
211
212 val = va_arg(args, u_int);
213 check_buf(this, sizeof(val));
214 this->buf.ptr[this->buf.len] = val;
215 this->buf.len += sizeof(val);
216 break;
217 }
218 /* u_int16_t */
219 case HA_SYNC_ALG_PRF:
220 case HA_SYNC_ALG_OLD_PRF:
221 case HA_SYNC_ALG_ENCR:
222 case HA_SYNC_ALG_ENCR_LEN:
223 case HA_SYNC_ALG_INTEG:
224 case HA_SYNC_INBOUND_CPI:
225 case HA_SYNC_OUTBOUND_CPI:
226 {
227 u_int16_t val;
228
229 val = va_arg(args, u_int);
230 check_buf(this, sizeof(val));
231 *(u_int16_t*)(this->buf.ptr + this->buf.len) = htons(val);
232 this->buf.len += sizeof(val);
233 break;
234 }
235 /** u_int32_t */
236 case HA_SYNC_CONDITIONS:
237 case HA_SYNC_EXTENSIONS:
238 case HA_SYNC_INBOUND_SPI:
239 case HA_SYNC_OUTBOUND_SPI:
240 case HA_SYNC_INITIATE_MID:
241 case HA_SYNC_RESPOND_MID:
242 {
243 u_int32_t val;
244
245 val = va_arg(args, u_int);
246 check_buf(this, sizeof(val));
247 *(u_int32_t*)(this->buf.ptr + this->buf.len) = htonl(val);
248 this->buf.len += sizeof(val);
249 break;
250 }
251 /** chunk_t */
252 case HA_SYNC_NONCE_I:
253 case HA_SYNC_NONCE_R:
254 case HA_SYNC_SECRET:
255 case HA_SYNC_OLD_SKD:
256 {
257 chunk_t chunk;
258
259 chunk = va_arg(args, chunk_t);
260 check_buf(this, chunk.len + sizeof(u_int16_t));
261 *(u_int16_t*)(this->buf.ptr + this->buf.len) = htons(chunk.len);
262 memcpy(this->buf.ptr + this->buf.len + sizeof(u_int16_t),
263 chunk.ptr, chunk.len);
264 this->buf.len += chunk.len + sizeof(u_int16_t);;
265 break;
266 }
267 /** traffic_selector_t */
268 case HA_SYNC_LOCAL_TS:
269 case HA_SYNC_REMOTE_TS:
270 {
271 ts_encoding_t *enc;
272 traffic_selector_t *ts;
273 chunk_t data;
274
275 ts = va_arg(args, traffic_selector_t*);
276 data = chunk_cata("cc", ts->get_from_address(ts),
277 ts->get_to_address(ts));
278 check_buf(this, sizeof(ts_encoding_t) + data.len);
279 enc = (ts_encoding_t*)(this->buf.ptr + this->buf.len);
280 this->buf.len += sizeof(ts_encoding_t) + data.len;
281 enc->type = ts->get_type(ts);
282 enc->protocol = ts->get_protocol(ts);
283 enc->from_port = htons(ts->get_from_port(ts));
284 enc->to_port = htons(ts->get_to_port(ts));
285 enc->dynamic = ts->is_dynamic(ts);
286 memcpy(enc->encoding, data.ptr, data.len);
287 break;
288 }
289 default:
290 {
291 DBG1(DBG_CFG, "unable to encode, attribute %d unknown", attribute);
292 this->buf.len -= sizeof(u_int8_t);
293 break;
294 }
295 }
296 va_end(args);
297 }
298
299 /**
300 * Attribute enumerator implementation
301 */
302 typedef struct {
303 /** implementes enumerator_t */
304 enumerator_t public;
305 /** position in message */
306 chunk_t buf;
307 /** cleanup handler of current element, if any */
308 void (*cleanup)(void* data);
309 /** data to pass to cleanup handler */
310 void *cleanup_data;
311 } attribute_enumerator_t;
312
313 /**
314 * Implementation of create_attribute_enumerator().enumerate
315 */
316 static bool attribute_enumerate(attribute_enumerator_t *this,
317 ha_sync_message_attribute_t *attr_out,
318 ha_sync_message_value_t *value)
319 {
320 ha_sync_message_attribute_t attr;
321
322 if (this->cleanup)
323 {
324 this->cleanup(this->cleanup_data);
325 this->cleanup = NULL;
326 }
327 if (this->buf.len < 1)
328 {
329 return FALSE;
330 }
331 attr = this->buf.ptr[0];
332 this->buf = chunk_skip(this->buf, 1);
333 switch (attr)
334 {
335 /* ike_sa_id_t* */
336 case HA_SYNC_IKE_ID:
337 case HA_SYNC_IKE_REKEY_ID:
338 {
339 ike_sa_id_encoding_t *enc;
340
341 if (this->buf.len < sizeof(ike_sa_id_encoding_t))
342 {
343 return FALSE;
344 }
345 enc = (ike_sa_id_encoding_t*)(this->buf.ptr);
346 value->ike_sa_id = ike_sa_id_create(enc->initiator_spi,
347 enc->responder_spi, enc->initiator);
348 *attr_out = attr;
349 this->cleanup = (void*)value->ike_sa_id->destroy;
350 this->cleanup_data = value->ike_sa_id;
351 this->buf = chunk_skip(this->buf, sizeof(ike_sa_id_encoding_t));
352 return TRUE;
353 }
354 /* identification_t* */
355 case HA_SYNC_LOCAL_ID:
356 case HA_SYNC_REMOTE_ID:
357 case HA_SYNC_EAP_ID:
358 {
359 identification_encoding_t *enc;
360
361 enc = (identification_encoding_t*)(this->buf.ptr);
362 if (this->buf.len < sizeof(identification_encoding_t) ||
363 this->buf.len < sizeof(identification_encoding_t) + enc->len)
364 {
365 return FALSE;
366 }
367 value->id = identification_create_from_encoding(enc->type,
368 chunk_create(enc->encoding, enc->len));
369 *attr_out = attr;
370 this->cleanup = (void*)value->id->destroy;
371 this->cleanup_data = value->id;
372 this->buf = chunk_skip(this->buf,
373 sizeof(identification_encoding_t) + enc->len);
374 return TRUE;
375 }
376 /* host_t* */
377 case HA_SYNC_LOCAL_ADDR:
378 case HA_SYNC_REMOTE_ADDR:
379 case HA_SYNC_LOCAL_VIP:
380 case HA_SYNC_REMOTE_VIP:
381 case HA_SYNC_ADDITIONAL_ADDR:
382 {
383 host_encoding_t *enc;
384
385 enc = (host_encoding_t*)(this->buf.ptr);
386 if (this->buf.len < sizeof(host_encoding_t))
387 {
388 return FALSE;
389 }
390 value->host = host_create_from_chunk(enc->family,
391 chunk_create(enc->encoding,
392 this->buf.len - sizeof(host_encoding_t)),
393 ntohs(enc->port));
394 if (!value->host)
395 {
396 return FALSE;
397 }
398 *attr_out = attr;
399 this->cleanup = (void*)value->host->destroy;
400 this->cleanup_data = value->host;
401 this->buf = chunk_skip(this->buf, sizeof(host_encoding_t) +
402 value->host->get_address(value->host).len);
403 return TRUE;
404 }
405 /* char* */
406 case HA_SYNC_CONFIG_NAME:
407 {
408 size_t len;
409
410 len = strnlen(this->buf.ptr, this->buf.len);
411 if (len >= this->buf.len)
412 {
413 return FALSE;
414 }
415 value->str = this->buf.ptr;
416 *attr_out = attr;
417 this->buf = chunk_skip(this->buf, len + 1);
418 return TRUE;
419 }
420 /* u_int8_t */
421 case HA_SYNC_IPSEC_MODE:
422 case HA_SYNC_IPCOMP:
423 {
424 if (this->buf.len < sizeof(u_int8_t))
425 {
426 return FALSE;
427 }
428 value->u8 = *(u_int8_t*)this->buf.ptr;
429 *attr_out = attr;
430 this->buf = chunk_skip(this->buf, sizeof(u_int8_t));
431 return TRUE;
432 }
433 /** u_int16_t */
434 case HA_SYNC_ALG_PRF:
435 case HA_SYNC_ALG_OLD_PRF:
436 case HA_SYNC_ALG_ENCR:
437 case HA_SYNC_ALG_ENCR_LEN:
438 case HA_SYNC_ALG_INTEG:
439 case HA_SYNC_INBOUND_CPI:
440 case HA_SYNC_OUTBOUND_CPI:
441 {
442 if (this->buf.len < sizeof(u_int16_t))
443 {
444 return FALSE;
445 }
446 value->u16 = ntohs(*(u_int16_t*)this->buf.ptr);
447 *attr_out = attr;
448 this->buf = chunk_skip(this->buf, sizeof(u_int16_t));
449 return TRUE;
450 }
451 /** u_int32_t */
452 case HA_SYNC_CONDITIONS:
453 case HA_SYNC_EXTENSIONS:
454 case HA_SYNC_INBOUND_SPI:
455 case HA_SYNC_OUTBOUND_SPI:
456 case HA_SYNC_INITIATE_MID:
457 case HA_SYNC_RESPOND_MID:
458 {
459 if (this->buf.len < sizeof(u_int32_t))
460 {
461 return FALSE;
462 }
463 value->u32 = ntohl(*(u_int32_t*)this->buf.ptr);
464 *attr_out = attr;
465 this->buf = chunk_skip(this->buf, sizeof(u_int32_t));
466 return TRUE;
467 }
468 /** chunk_t */
469 case HA_SYNC_NONCE_I:
470 case HA_SYNC_NONCE_R:
471 case HA_SYNC_SECRET:
472 case HA_SYNC_OLD_SKD:
473 {
474 size_t len;
475
476 if (this->buf.len < sizeof(u_int16_t))
477 {
478 return FALSE;
479 }
480 len = ntohs(*(u_int16_t*)this->buf.ptr);
481 this->buf = chunk_skip(this->buf, sizeof(u_int16_t));
482 if (this->buf.len < len)
483 {
484 return FALSE;
485 }
486 value->chunk.len = len;
487 value->chunk.ptr = this->buf.ptr;
488 *attr_out = attr;
489 this->buf = chunk_skip(this->buf, len);
490 return TRUE;
491 }
492 case HA_SYNC_LOCAL_TS:
493 case HA_SYNC_REMOTE_TS:
494 {
495 ts_encoding_t *enc;
496 host_t *host;
497 int addr_len;
498
499 enc = (ts_encoding_t*)(this->buf.ptr);
500 if (this->buf.len < sizeof(ts_encoding_t))
501 {
502 return FALSE;
503 }
504 switch (enc->type)
505 {
506 case TS_IPV4_ADDR_RANGE:
507 addr_len = 4;
508 if (this->buf.len < sizeof(ts_encoding_t) + 2 * addr_len)
509 {
510 return FALSE;
511 }
512 break;
513 case TS_IPV6_ADDR_RANGE:
514 addr_len = 16;
515 if (this->buf.len < sizeof(ts_encoding_t) + 2 * addr_len)
516 {
517 return FALSE;
518 }
519 break;
520 default:
521 return FALSE;
522 }
523 if (enc->dynamic)
524 {
525 host = host_create_from_chunk(0,
526 chunk_create(enc->encoding, addr_len), 0);
527 if (!host)
528 {
529 return FALSE;
530 }
531 value->ts = traffic_selector_create_dynamic(enc->protocol,
532 ntohs(enc->from_port), ntohs(enc->to_port));
533 value->ts->set_address(value->ts, host);
534 host->destroy(host);
535 }
536 else
537 {
538 value->ts = traffic_selector_create_from_bytes(enc->protocol,
539 enc->type, chunk_create(enc->encoding, addr_len),
540 ntohs(enc->from_port),
541 chunk_create(enc->encoding + addr_len, addr_len),
542 ntohs(enc->to_port));
543 if (!value->ts)
544 {
545 return FALSE;
546 }
547 }
548 *attr_out = attr;
549 this->cleanup = (void*)value->ts->destroy;
550 this->cleanup_data = value->ts;
551 this->buf = chunk_skip(this->buf, sizeof(ts_encoding_t)
552 + addr_len * 2);
553 return TRUE;
554 }
555 default:
556 {
557 return FALSE;
558 }
559 }
560 }
561
562 /**
563 * Implementation of create_attribute_enumerator().destroy
564 */
565 static void enum_destroy(attribute_enumerator_t *this)
566 {
567 if (this->cleanup)
568 {
569 this->cleanup(this->cleanup_data);
570 }
571 free(this);
572 }
573
574 /**
575 * Implementation of ha_sync_message_t.create_attribute_enumerator
576 */
577 static enumerator_t* create_attribute_enumerator(private_ha_sync_message_t *this)
578 {
579 attribute_enumerator_t *e = malloc_thing(attribute_enumerator_t);
580
581 e->public.enumerate = (void*)attribute_enumerate;
582 e->public.destroy = (void*)enum_destroy;
583
584 e->buf = chunk_skip(this->buf, 2);
585 e->cleanup = NULL;
586 e->cleanup_data = NULL;
587
588 return &e->public;
589 }
590
591 /**
592 * Implementation of ha_sync_message_t.get_encoding
593 */
594 static chunk_t get_encoding(private_ha_sync_message_t *this)
595 {
596 return this->buf;
597 }
598
599 /**
600 * Implementation of ha_sync_message_t.destroy.
601 */
602 static void destroy(private_ha_sync_message_t *this)
603 {
604 free(this->buf.ptr);
605 free(this);
606 }
607
608
609 static private_ha_sync_message_t *ha_sync_message_create_generic()
610 {
611 private_ha_sync_message_t *this = malloc_thing(private_ha_sync_message_t);
612
613 this->public.get_type = (ha_sync_message_type_t(*)(ha_sync_message_t*))get_type;
614 this->public.add_attribute = (void(*)(ha_sync_message_t*, ha_sync_message_attribute_t attribute, ...))add_attribute;
615 this->public.create_attribute_enumerator = (enumerator_t*(*)(ha_sync_message_t*))create_attribute_enumerator;
616 this->public.get_encoding = (chunk_t(*)(ha_sync_message_t*))get_encoding;
617 this->public.destroy = (void(*)(ha_sync_message_t*))destroy;
618
619 return this;
620 }
621
622 /**
623 * See header
624 */
625 ha_sync_message_t *ha_sync_message_create(ha_sync_message_type_t type)
626 {
627 private_ha_sync_message_t *this = ha_sync_message_create_generic();
628
629 this->allocated = ALLOCATION_BLOCK;
630 this->buf.ptr = malloc(this->allocated);
631 this->buf.len = 2;
632 this->buf.ptr[0] = HA_SYNC_MESSAGE_VERSION;
633 this->buf.ptr[1] = type;
634
635 return &this->public;
636 }
637
638 /**
639 * See header
640 */
641 ha_sync_message_t *ha_sync_message_parse(chunk_t data)
642 {
643 private_ha_sync_message_t *this;
644
645 if (data.len < 2)
646 {
647 DBG1(DBG_CFG, "HA sync message too short");
648 return NULL;
649 }
650 if (data.ptr[0] != HA_SYNC_MESSAGE_VERSION)
651 {
652 DBG1(DBG_CFG, "HA sync message has version %d, expected %d",
653 data.ptr[0], HA_SYNC_MESSAGE_VERSION);
654 return NULL;
655 }
656
657 this = ha_sync_message_create_generic();
658 this->buf = chunk_clone(data);
659 this->allocated = this->buf.len;
660
661 return &this->public;
662 }
663