Use distinct message types for HA message ID updates
[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 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 METHOD(ha_message_t, get_type, ha_message_type_t,
97 private_ha_message_t *this)
98 {
99 return this->buf.ptr[1];
100 }
101
102 /**
103 * check for space in buffer, increase if necessary
104 */
105 static void check_buf(private_ha_message_t *this, size_t len)
106 {
107 int increased = 0;
108
109 while (this->buf.len + len > this->allocated)
110 { /* double size */
111 this->allocated += ALLOCATION_BLOCK;
112 increased++;
113 }
114 if (increased)
115 {
116 this->buf.ptr = realloc(this->buf.ptr, this->allocated);
117 }
118 }
119
120 METHOD(ha_message_t, add_attribute, void,
121 private_ha_message_t *this, ha_message_attribute_t attribute, ...)
122 {
123 size_t len;
124 va_list args;
125
126 check_buf(this, sizeof(u_int8_t));
127 this->buf.ptr[this->buf.len] = attribute;
128 this->buf.len += sizeof(u_int8_t);
129
130 va_start(args, attribute);
131 switch (attribute)
132 {
133 /* ike_sa_id_t* */
134 case HA_IKE_ID:
135 case HA_IKE_REKEY_ID:
136 {
137 ike_sa_id_encoding_t *enc;
138 ike_sa_id_t *id;
139
140 id = va_arg(args, ike_sa_id_t*);
141 check_buf(this, sizeof(ike_sa_id_encoding_t));
142 enc = (ike_sa_id_encoding_t*)(this->buf.ptr + this->buf.len);
143 this->buf.len += sizeof(ike_sa_id_encoding_t);
144 enc->initiator = id->is_initiator(id);
145 enc->initiator_spi = id->get_initiator_spi(id);
146 enc->responder_spi = id->get_responder_spi(id);
147 break;
148 }
149 /* identification_t* */
150 case HA_LOCAL_ID:
151 case HA_REMOTE_ID:
152 {
153 identification_encoding_t *enc;
154 identification_t *id;
155 chunk_t data;
156
157 id = va_arg(args, identification_t*);
158 data = id->get_encoding(id);
159 check_buf(this, sizeof(identification_encoding_t) + data.len);
160 enc = (identification_encoding_t*)(this->buf.ptr + this->buf.len);
161 this->buf.len += sizeof(identification_encoding_t) + data.len;
162 enc->type = id->get_type(id);
163 enc->len = data.len;
164 memcpy(enc->encoding, data.ptr, data.len);
165 break;
166 }
167 /* host_t* */
168 case HA_LOCAL_ADDR:
169 case HA_REMOTE_ADDR:
170 case HA_LOCAL_VIP:
171 case HA_REMOTE_VIP:
172 case HA_ADDITIONAL_ADDR:
173 {
174 host_encoding_t *enc;
175 host_t *host;
176 chunk_t data;
177
178 host = va_arg(args, host_t*);
179 data = host->get_address(host);
180 check_buf(this, sizeof(host_encoding_t) + data.len);
181 enc = (host_encoding_t*)(this->buf.ptr + this->buf.len);
182 this->buf.len += sizeof(host_encoding_t) + data.len;
183 enc->family = host->get_family(host);
184 enc->port = htons(host->get_port(host));
185 memcpy(enc->encoding, data.ptr, data.len);
186 break;
187 }
188 /* char* */
189 case HA_CONFIG_NAME:
190 {
191 char *str;
192
193 str = va_arg(args, char*);
194 len = strlen(str) + 1;
195 check_buf(this, len);
196 memcpy(this->buf.ptr + this->buf.len, str, len);
197 this->buf.len += len;
198 break;
199 }
200 /* u_int8_t */
201 case HA_IPSEC_MODE:
202 case HA_IPCOMP:
203 {
204 u_int8_t val;
205
206 val = va_arg(args, u_int);
207 check_buf(this, sizeof(val));
208 this->buf.ptr[this->buf.len] = val;
209 this->buf.len += sizeof(val);
210 break;
211 }
212 /* u_int16_t */
213 case HA_ALG_PRF:
214 case HA_ALG_OLD_PRF:
215 case HA_ALG_ENCR:
216 case HA_ALG_ENCR_LEN:
217 case HA_ALG_INTEG:
218 case HA_INBOUND_CPI:
219 case HA_OUTBOUND_CPI:
220 case HA_SEGMENT:
221 {
222 u_int16_t val;
223
224 val = va_arg(args, u_int);
225 check_buf(this, sizeof(val));
226 *(u_int16_t*)(this->buf.ptr + this->buf.len) = htons(val);
227 this->buf.len += sizeof(val);
228 break;
229 }
230 /** u_int32_t */
231 case HA_CONDITIONS:
232 case HA_EXTENSIONS:
233 case HA_INBOUND_SPI:
234 case HA_OUTBOUND_SPI:
235 case HA_MID:
236 {
237 u_int32_t val;
238
239 val = va_arg(args, u_int);
240 check_buf(this, sizeof(val));
241 *(u_int32_t*)(this->buf.ptr + this->buf.len) = htonl(val);
242 this->buf.len += sizeof(val);
243 break;
244 }
245 /** chunk_t */
246 case HA_NONCE_I:
247 case HA_NONCE_R:
248 case HA_SECRET:
249 case HA_OLD_SKD:
250 {
251 chunk_t chunk;
252
253 chunk = va_arg(args, chunk_t);
254 check_buf(this, chunk.len + sizeof(u_int16_t));
255 *(u_int16_t*)(this->buf.ptr + this->buf.len) = htons(chunk.len);
256 memcpy(this->buf.ptr + this->buf.len + sizeof(u_int16_t),
257 chunk.ptr, chunk.len);
258 this->buf.len += chunk.len + sizeof(u_int16_t);;
259 break;
260 }
261 /** traffic_selector_t */
262 case HA_LOCAL_TS:
263 case HA_REMOTE_TS:
264 {
265 ts_encoding_t *enc;
266 traffic_selector_t *ts;
267 chunk_t data;
268
269 ts = va_arg(args, traffic_selector_t*);
270 data = chunk_cata("cc", ts->get_from_address(ts),
271 ts->get_to_address(ts));
272 check_buf(this, sizeof(ts_encoding_t) + data.len);
273 enc = (ts_encoding_t*)(this->buf.ptr + this->buf.len);
274 this->buf.len += sizeof(ts_encoding_t) + data.len;
275 enc->type = ts->get_type(ts);
276 enc->protocol = ts->get_protocol(ts);
277 enc->from_port = htons(ts->get_from_port(ts));
278 enc->to_port = htons(ts->get_to_port(ts));
279 enc->dynamic = ts->is_dynamic(ts);
280 memcpy(enc->encoding, data.ptr, data.len);
281 break;
282 }
283 default:
284 {
285 DBG1(DBG_CFG, "unable to encode, attribute %d unknown", attribute);
286 this->buf.len -= sizeof(u_int8_t);
287 break;
288 }
289 }
290 va_end(args);
291 }
292
293 /**
294 * Attribute enumerator implementation
295 */
296 typedef struct {
297 /** implementes enumerator_t */
298 enumerator_t public;
299 /** position in message */
300 chunk_t buf;
301 /** cleanup handler of current element, if any */
302 void (*cleanup)(void* data);
303 /** data to pass to cleanup handler */
304 void *cleanup_data;
305 } attribute_enumerator_t;
306
307 METHOD(enumerator_t, attribute_enumerate, bool,
308 attribute_enumerator_t *this, ha_message_attribute_t *attr_out,
309 ha_message_value_t *value)
310 {
311 ha_message_attribute_t attr;
312
313 if (this->cleanup)
314 {
315 this->cleanup(this->cleanup_data);
316 this->cleanup = NULL;
317 }
318 if (this->buf.len < 1)
319 {
320 return FALSE;
321 }
322 attr = this->buf.ptr[0];
323 this->buf = chunk_skip(this->buf, 1);
324 switch (attr)
325 {
326 /* ike_sa_id_t* */
327 case HA_IKE_ID:
328 case HA_IKE_REKEY_ID:
329 {
330 ike_sa_id_encoding_t *enc;
331
332 if (this->buf.len < sizeof(ike_sa_id_encoding_t))
333 {
334 return FALSE;
335 }
336 enc = (ike_sa_id_encoding_t*)(this->buf.ptr);
337 value->ike_sa_id = ike_sa_id_create(enc->initiator_spi,
338 enc->responder_spi, enc->initiator);
339 *attr_out = attr;
340 this->cleanup = (void*)value->ike_sa_id->destroy;
341 this->cleanup_data = value->ike_sa_id;
342 this->buf = chunk_skip(this->buf, sizeof(ike_sa_id_encoding_t));
343 return TRUE;
344 }
345 /* identification_t* */
346 case HA_LOCAL_ID:
347 case HA_REMOTE_ID:
348 {
349 identification_encoding_t *enc;
350
351 enc = (identification_encoding_t*)(this->buf.ptr);
352 if (this->buf.len < sizeof(identification_encoding_t) ||
353 this->buf.len < sizeof(identification_encoding_t) + enc->len)
354 {
355 return FALSE;
356 }
357 value->id = identification_create_from_encoding(enc->type,
358 chunk_create(enc->encoding, enc->len));
359 *attr_out = attr;
360 this->cleanup = (void*)value->id->destroy;
361 this->cleanup_data = value->id;
362 this->buf = chunk_skip(this->buf,
363 sizeof(identification_encoding_t) + enc->len);
364 return TRUE;
365 }
366 /* host_t* */
367 case HA_LOCAL_ADDR:
368 case HA_REMOTE_ADDR:
369 case HA_LOCAL_VIP:
370 case HA_REMOTE_VIP:
371 case HA_ADDITIONAL_ADDR:
372 {
373 host_encoding_t *enc;
374
375 enc = (host_encoding_t*)(this->buf.ptr);
376 if (this->buf.len < sizeof(host_encoding_t))
377 {
378 return FALSE;
379 }
380 value->host = host_create_from_chunk(enc->family,
381 chunk_create(enc->encoding,
382 this->buf.len - sizeof(host_encoding_t)),
383 ntohs(enc->port));
384 if (!value->host)
385 {
386 return FALSE;
387 }
388 *attr_out = attr;
389 this->cleanup = (void*)value->host->destroy;
390 this->cleanup_data = value->host;
391 this->buf = chunk_skip(this->buf, sizeof(host_encoding_t) +
392 value->host->get_address(value->host).len);
393 return TRUE;
394 }
395 /* char* */
396 case HA_CONFIG_NAME:
397 {
398 size_t len;
399
400 len = strnlen(this->buf.ptr, this->buf.len);
401 if (len >= this->buf.len)
402 {
403 return FALSE;
404 }
405 value->str = this->buf.ptr;
406 *attr_out = attr;
407 this->buf = chunk_skip(this->buf, len + 1);
408 return TRUE;
409 }
410 /* u_int8_t */
411 case HA_IPSEC_MODE:
412 case HA_IPCOMP:
413 {
414 if (this->buf.len < sizeof(u_int8_t))
415 {
416 return FALSE;
417 }
418 value->u8 = *(u_int8_t*)this->buf.ptr;
419 *attr_out = attr;
420 this->buf = chunk_skip(this->buf, sizeof(u_int8_t));
421 return TRUE;
422 }
423 /** u_int16_t */
424 case HA_ALG_PRF:
425 case HA_ALG_OLD_PRF:
426 case HA_ALG_ENCR:
427 case HA_ALG_ENCR_LEN:
428 case HA_ALG_INTEG:
429 case HA_INBOUND_CPI:
430 case HA_OUTBOUND_CPI:
431 case HA_SEGMENT:
432 {
433 if (this->buf.len < sizeof(u_int16_t))
434 {
435 return FALSE;
436 }
437 value->u16 = ntohs(*(u_int16_t*)this->buf.ptr);
438 *attr_out = attr;
439 this->buf = chunk_skip(this->buf, sizeof(u_int16_t));
440 return TRUE;
441 }
442 /** u_int32_t */
443 case HA_CONDITIONS:
444 case HA_EXTENSIONS:
445 case HA_INBOUND_SPI:
446 case HA_OUTBOUND_SPI:
447 case HA_MID:
448 {
449 if (this->buf.len < sizeof(u_int32_t))
450 {
451 return FALSE;
452 }
453 value->u32 = ntohl(*(u_int32_t*)this->buf.ptr);
454 *attr_out = attr;
455 this->buf = chunk_skip(this->buf, sizeof(u_int32_t));
456 return TRUE;
457 }
458 /** chunk_t */
459 case HA_NONCE_I:
460 case HA_NONCE_R:
461 case HA_SECRET:
462 case HA_OLD_SKD:
463 {
464 size_t len;
465
466 if (this->buf.len < sizeof(u_int16_t))
467 {
468 return FALSE;
469 }
470 len = ntohs(*(u_int16_t*)this->buf.ptr);
471 this->buf = chunk_skip(this->buf, sizeof(u_int16_t));
472 if (this->buf.len < len)
473 {
474 return FALSE;
475 }
476 value->chunk.len = len;
477 value->chunk.ptr = this->buf.ptr;
478 *attr_out = attr;
479 this->buf = chunk_skip(this->buf, len);
480 return TRUE;
481 }
482 case HA_LOCAL_TS:
483 case HA_REMOTE_TS:
484 {
485 ts_encoding_t *enc;
486 host_t *host;
487 int addr_len;
488
489 enc = (ts_encoding_t*)(this->buf.ptr);
490 if (this->buf.len < sizeof(ts_encoding_t))
491 {
492 return FALSE;
493 }
494 switch (enc->type)
495 {
496 case TS_IPV4_ADDR_RANGE:
497 addr_len = 4;
498 if (this->buf.len < sizeof(ts_encoding_t) + 2 * addr_len)
499 {
500 return FALSE;
501 }
502 break;
503 case TS_IPV6_ADDR_RANGE:
504 addr_len = 16;
505 if (this->buf.len < sizeof(ts_encoding_t) + 2 * addr_len)
506 {
507 return FALSE;
508 }
509 break;
510 default:
511 return FALSE;
512 }
513 if (enc->dynamic)
514 {
515 host = host_create_from_chunk(0,
516 chunk_create(enc->encoding, addr_len), 0);
517 if (!host)
518 {
519 return FALSE;
520 }
521 value->ts = traffic_selector_create_dynamic(enc->protocol,
522 ntohs(enc->from_port), ntohs(enc->to_port));
523 value->ts->set_address(value->ts, host);
524 host->destroy(host);
525 }
526 else
527 {
528 value->ts = traffic_selector_create_from_bytes(enc->protocol,
529 enc->type, chunk_create(enc->encoding, addr_len),
530 ntohs(enc->from_port),
531 chunk_create(enc->encoding + addr_len, addr_len),
532 ntohs(enc->to_port));
533 if (!value->ts)
534 {
535 return FALSE;
536 }
537 }
538 *attr_out = attr;
539 this->cleanup = (void*)value->ts->destroy;
540 this->cleanup_data = value->ts;
541 this->buf = chunk_skip(this->buf, sizeof(ts_encoding_t)
542 + addr_len * 2);
543 return TRUE;
544 }
545 default:
546 {
547 return FALSE;
548 }
549 }
550 }
551
552 METHOD(enumerator_t, enum_destroy, void,
553 attribute_enumerator_t *this)
554 {
555 if (this->cleanup)
556 {
557 this->cleanup(this->cleanup_data);
558 }
559 free(this);
560 }
561
562 METHOD(ha_message_t, create_attribute_enumerator, enumerator_t*,
563 private_ha_message_t *this)
564 {
565 attribute_enumerator_t *e;
566
567 INIT(e,
568 .public = {
569 .enumerate = (void*)_attribute_enumerate,
570 .destroy = _enum_destroy,
571 },
572 .buf = chunk_skip(this->buf, 2),
573 );
574
575 return &e->public;
576 }
577
578 METHOD(ha_message_t, get_encoding, chunk_t,
579 private_ha_message_t *this)
580 {
581 return this->buf;
582 }
583
584 METHOD(ha_message_t, destroy, void,
585 private_ha_message_t *this)
586 {
587 free(this->buf.ptr);
588 free(this);
589 }
590
591
592 static private_ha_message_t *ha_message_create_generic()
593 {
594 private_ha_message_t *this;
595
596 INIT(this,
597 .public = {
598 .get_type = _get_type,
599 .add_attribute = _add_attribute,
600 .create_attribute_enumerator = _create_attribute_enumerator,
601 .get_encoding = _get_encoding,
602 .destroy = _destroy,
603 },
604 );
605 return this;
606 }
607
608 /**
609 * See header
610 */
611 ha_message_t *ha_message_create(ha_message_type_t type)
612 {
613 private_ha_message_t *this = ha_message_create_generic();
614
615 this->allocated = ALLOCATION_BLOCK;
616 this->buf.ptr = malloc(this->allocated);
617 this->buf.len = 2;
618 this->buf.ptr[0] = HA_MESSAGE_VERSION;
619 this->buf.ptr[1] = type;
620
621 return &this->public;
622 }
623
624 /**
625 * See header
626 */
627 ha_message_t *ha_message_parse(chunk_t data)
628 {
629 private_ha_message_t *this;
630
631 if (data.len < 2)
632 {
633 DBG1(DBG_CFG, "HA message too short");
634 return NULL;
635 }
636 if (data.ptr[0] != HA_MESSAGE_VERSION)
637 {
638 DBG1(DBG_CFG, "HA message has version %d, expected %d",
639 data.ptr[0], HA_MESSAGE_VERSION);
640 return NULL;
641 }
642
643 this = ha_message_create_generic();
644 this->buf = chunk_clone(data);
645 this->allocated = this->buf.len;
646
647 return &this->public;
648 }
649