Rename "subsystems" directory to "base"
[oonf.git] / src / crypto / simple_security / simple_security.c
1
2 /*
3  * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2)
4  * Copyright (c) 2004-2015, the olsr.org team - see HISTORY file
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  *
11  * * Redistributions of source code must retain the above copyright
12  *   notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above copyright
14  *   notice, this list of conditions and the following disclaimer in
15  *   the documentation and/or other materials provided with the
16  *   distribution.
17  * * Neither the name of olsr.org, olsrd nor the names of its
18  *   contributors may be used to endorse or promote products derived
19  *   from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *
34  * Visit http://www.olsr.org for more information.
35  *
36  * If you find this software useful feel free to make a donation
37  * to the project. For more information see the website or contact
38  * the copyright holders.
39  *
40  */
41
42 /**
43  * @file
44  */
45
46 #include <string.h>
47
48 #include <oonf/libcommon/avl.h>
49 #include <oonf/libcommon/avl_comp.h>
50 #include <oonf/oonf.h>
51 #include <oonf/libcommon/netaddr.h>
52 #include <oonf/libconfig/cfg_schema.h>
53 #include <oonf/libcore/oonf_subsystem.h>
54 #include <oonf/crypto/rfc5444_signature/rfc5444_signature.h>
55 #include <oonf/base/oonf_class.h>
56 #include <oonf/base/oonf_rfc5444.h>
57 #include <oonf/base/oonf_timer.h>
58 #include <oonf/base/os_interface.h>
59
60 #include <oonf/crypto/simple_security/simple_security.h>
61
62 /* definitions */
63 #define LOG_SIMPLE_SECURITY _simple_security_subsystem.logging
64
65 /**
66  * simple security configuration
67  */
68 struct sise_config {
69   /*! binary cryptographic key for signature (not null-terminated) */
70   char key[256];
71
72   /*! length of cryptographic key */
73   size_t key_length;
74
75   /*! validity time for replay protection */
76   uint64_t vtime;
77
78   /*! delay until sequence number query is sent */
79   uint64_t trigger_delay;
80
81   /*! maximum amount of sequence number increase we accept */
82   uint32_t window_size;
83 };
84
85 /**
86  * Interface specific key for neighbor
87  */
88 struct neighbor_key {
89   /*! src IP of RFC5444 packet of neighbor on this interface */
90   struct netaddr src;
91
92   /*! interface index */
93   unsigned if_index;
94 };
95
96 /**
97  * Known neighbor with sequence number and protocol data
98  */
99 struct neighbor_node {
100   /*! key for AVL tree */
101   struct neighbor_key key;
102
103   /*! last counter we have received from this neighbor */
104   uint32_t last_counter;
105
106   /*! query id, NULL if no query should be generated */
107   uint32_t send_query;
108
109   /*! response id, NULL if no response was received */
110   uint32_t send_response;
111
112   /*! reference for RFC5444 unicast communication */
113   struct oonf_rfc5444_target *_target;
114
115   /*! timer to remove this node from memory after some time */
116   struct oonf_timer_instance _vtime;
117
118   /*! timer to delay generation of a challenge/response */
119   struct oonf_timer_instance _trigger;
120
121   /*! node for global AVL tree */
122   struct avl_node _node;
123 };
124 static int _init(void);
125 static void _cleanup(void);
126
127 static bool _cb_is_matching_signature(struct rfc5444_signature *sig, int msg_type);
128 static const void *_cb_getCryptoKey(struct rfc5444_signature *sig, size_t *length);
129 static const void *_cb_getKeyId(struct rfc5444_signature *sig, size_t *length);
130
131 static struct neighbor_node *_add_neighbor_node(struct neighbor_key *);
132 static void _cb_neighbor_node_timeout(void *);
133 static void _cb_query_trigger(void *);
134
135 static enum rfc5444_result _cb_timestamp_tlv(struct rfc5444_reader_tlvblock_context *context);
136 static enum rfc5444_result _cb_timestamp_failed(struct rfc5444_reader_tlvblock_context *context);
137 static void _cb_addPacketTLVs(struct rfc5444_writer *, struct rfc5444_writer_target *);
138 static void _cb_finishPacketTLVs(struct rfc5444_writer *, struct rfc5444_writer_target *);
139
140 static int _avl_comp_timestamp_keys(const void *, const void *);
141
142 static void _cb_config_changed(void);
143
144 /* configuration */
145 static struct cfg_schema_entry _sise_entries[] = {
146   CFG_MAP_STRING_ARRAY(sise_config, key, "key", NULL, "Key for HMAC signature", 256),
147   CFG_MAP_CLOCK_MIN(sise_config, vtime, "vtime", "60000", "Time until replay protection counters are dropped", 60000),
148   CFG_MAP_CLOCK_MIN(
149     sise_config, trigger_delay, "trigger_delay", "10000", "Time until a query/response will be generated ", 1000),
150   CFG_MAP_INT32_MINMAX(sise_config, window_size, "window", "100",
151     "What amount of counter increase we accept from a neighbor node", 0, false, 1, INT32_MAX),
152 };
153
154 static struct cfg_schema_section _sise_section = {
155   .type = OONF_SIMPLE_SECURITY_SUBSYSTEM,
156   .cb_delta_handler = _cb_config_changed,
157   .entries = _sise_entries,
158   .entry_count = ARRAYSIZE(_sise_entries),
159 };
160
161 /* subsystem */
162 static const char *_dependencies[] = {
163   OONF_CLASS_SUBSYSTEM,
164   OONF_TIMER_SUBSYSTEM,
165   OONF_RFC5444_SUBSYSTEM,
166   OONF_OS_INTERFACE_SUBSYSTEM,
167   OONF_RFC5444_SIG_SUBSYSTEM,
168 };
169 static struct oonf_subsystem _simple_security_subsystem = {
170   .name = OONF_SIMPLE_SECURITY_SUBSYSTEM,
171   .dependencies = _dependencies,
172   .dependencies_count = ARRAYSIZE(_dependencies),
173   .descr = "RFC5444 SHA256-HMAC shared-key security plugin",
174   .author = "Henning Rogge",
175
176   .init = _init,
177   .cleanup = _cleanup,
178
179   .cfg_section = &_sise_section,
180 };
181 DECLARE_OONF_PLUGIN(_simple_security_subsystem);
182
183 static struct sise_config _config;
184
185 /* packet signature */
186 static struct rfc5444_signature _signature = {
187   .key =
188     {
189       .crypt_function = RFC7182_ICV_CRYPT_HMAC,
190       .hash_function = RFC7182_ICV_HASH_SHA_256,
191     },
192
193   .is_matching_signature = _cb_is_matching_signature,
194   .getCryptoKey = _cb_getCryptoKey,
195   .getKeyId = _cb_getKeyId,
196   .drop_if_invalid = true,
197   .source_specific = true,
198 };
199
200 /* RFC5444 elements */
201 static struct oonf_rfc5444_protocol *_protocol;
202
203 static struct rfc5444_reader_tlvblock_consumer _pkt_consumer = {
204   .order = RFC5444_VALIDATOR_PRIORITY + 2,
205   .block_callback = _cb_timestamp_tlv,
206   .block_callback_failed_constraints = _cb_timestamp_failed,
207 };
208
209 enum
210 {
211   IDX_PKTTLV_SEND,
212   IDX_PKTTLV_QUERY,
213   IDX_PKTTLV_RESPONSE,
214 };
215 static struct rfc5444_reader_tlvblock_consumer_entry _pkt_tlvs[] = {
216   [IDX_PKTTLV_SEND] =
217     {
218       .type = RFC7182_PKTTLV_TIMESTAMP,
219       .mandatory = true,
220       .type_ext = RFC7182_TIMESTAMP_EXT_MONOTONIC,
221       .match_type_ext = true,
222       .min_length = 4,
223       .max_length = 4,
224       .match_length = true,
225     },
226   [IDX_PKTTLV_QUERY] =
227     {
228       .type = RFC5444_PKTTLV_CHALLENGE,
229       .type_ext = RFC5444_CHALLENGE_QUERY,
230       .match_type_ext = true,
231       .min_length = 4,
232       .max_length = 4,
233       .match_length = true,
234     },
235   [IDX_PKTTLV_RESPONSE] =
236     {
237       .type = RFC5444_PKTTLV_CHALLENGE,
238       .type_ext = RFC5444_CHALLENGE_RESPONSE,
239       .match_type_ext = true,
240       .min_length = 4,
241       .max_length = 4,
242       .match_length = true,
243     },
244 };
245
246 static struct rfc5444_writer_pkthandler _pkt_handler = {
247   .addPacketTLVs = _cb_addPacketTLVs,
248   .finishPacketTLVs = _cb_finishPacketTLVs,
249 };
250
251 /* storage for received timestamps */
252 static struct avl_tree _timestamp_tree;
253
254 static struct oonf_class _timestamp_class = {
255   .name = "signature timestamps",
256   .size = sizeof(struct neighbor_node),
257 };
258
259 static struct oonf_timer_class _timeout_class = {
260   .name = "signature timestamp timeout",
261   .callback = _cb_neighbor_node_timeout,
262 };
263
264 static struct oonf_timer_class _query_trigger_class = {
265   .name = "signature query trigger",
266   .callback = _cb_query_trigger,
267 };
268
269 /* global "timestamp" for replay protection */
270 static uint32_t _local_timestamp = 1;
271
272 /**
273  * Constructor of subsystem
274  * @return -1 if an error happened, 0 otherwise
275  */
276 static int
277 _init(void) {
278   _protocol = oonf_rfc5444_add_protocol(RFC5444_PROTOCOL, true);
279   if (_protocol == NULL) {
280     return -1;
281   }
282
283   rfc5444_reader_add_packet_consumer(&_protocol->reader, &_pkt_consumer, _pkt_tlvs, ARRAYSIZE(_pkt_tlvs));
284   rfc5444_writer_register_pkthandler(&_protocol->writer, &_pkt_handler);
285
286   rfc5444_sig_add(&_signature);
287
288   oonf_class_add(&_timestamp_class);
289   oonf_timer_add(&_timeout_class);
290   oonf_timer_add(&_query_trigger_class);
291   avl_init(&_timestamp_tree, _avl_comp_timestamp_keys, false);
292   return 0;
293 }
294
295 /**
296  * Destructor of subsystem
297  */
298 static void
299 _cleanup(void) {
300   struct neighbor_node *node, *node_it;
301
302   avl_for_each_element_safe(&_timestamp_tree, node, _node, node_it) {}
303   rfc5444_sig_remove(&_signature);
304   oonf_timer_remove(&_timeout_class);
305   oonf_timer_remove(&_query_trigger_class);
306   oonf_class_remove(&_timestamp_class);
307
308   rfc5444_reader_remove_packet_consumer(&_protocol->reader, &_pkt_consumer);
309   rfc5444_writer_unregister_pkthandler(&_protocol->writer, &_pkt_handler);
310   oonf_rfc5444_remove_protocol(_protocol);
311 }
312
313 /**
314  * Callback to check if signature type is handled by this plugin
315  * @param sig pointer to signature
316  * @param msg_type message type, -1 if packet signature
317  * @return true if packet signature, false otherwise
318  */
319 static bool
320 _cb_is_matching_signature(struct rfc5444_signature *sig __attribute__((unused)), int msg_type) {
321   return msg_type == RFC5444_WRITER_PKT_POSTPROCESSOR;
322 }
323
324 /**
325  * Returns length and value of cryptographic key
326  * @param sig pointer to signature
327  * @param length pointer to length of crypto key
328  * @return pointer to crypto key
329  */
330 static const void *
331 _cb_getCryptoKey(struct rfc5444_signature *sig __attribute__((unused)), size_t *length) {
332   *length = _config.key_length;
333   return _config.key;
334 }
335
336 /**
337  * Returns key id and length of signature
338  * @param sig pointer to signature
339  * @param length pointer to length of key id (0)
340  * @returns pointer to key id
341  */
342 static const void *
343 _cb_getKeyId(struct rfc5444_signature *sig __attribute__((unused)), size_t *length) {
344   static const char dummy[] = "";
345   *length = 0;
346   return dummy;
347 }
348
349 /**
350  * @param key neighbor key
351  * @return neighbor node for key, might be generated as a new node
352  */
353 static struct neighbor_node *
354 _add_neighbor_node(struct neighbor_key *key) {
355   struct neighbor_node *node;
356
357   node = oonf_class_malloc(&_timestamp_class);
358   if (!node) {
359     return NULL;
360   }
361
362   /* hook into tree */
363   memcpy(&node->key, key, sizeof(*key));
364   node->_node.key = &node->key;
365   avl_insert(&_timestamp_tree, &node->_node);
366
367   /* initialize timer */
368   node->_vtime.class = &_timeout_class;
369   node->_vtime.cb_context = node;
370   oonf_timer_set(&node->_vtime, _config.vtime);
371
372   node->_trigger.class = &_query_trigger_class;
373   node->_trigger.cb_context = node;
374
375   return node;
376 }
377
378 /**
379  * Callback to remove a neighbor node from memory
380  * @param ptr neighbor node
381  */
382 static void
383 _cb_neighbor_node_timeout(void *ptr) {
384   struct neighbor_node *node = ptr;
385
386   oonf_timer_stop(&node->_vtime);
387   oonf_rfc5444_remove_target(node->_target);
388   avl_remove(&_timestamp_tree, &node->_node);
389   oonf_class_free(&_timestamp_class, node);
390 }
391
392 /**
393  * Callback to generate new query/response
394  * @param ptr neighbor node
395  */
396 static void
397 _cb_query_trigger(void *ptr) {
398   struct neighbor_node *node = ptr;
399
400   rfc5444_writer_flush(&_protocol->writer, &node->_target->rfc5444_target, true);
401 }
402
403 /**
404  * Callback to parse incoming timestamp TLV. This code will assume that the packet
405  * signature was already checked
406  * @param context rfc5444 context
407  * @return RFC5444_OKAY or RFC5444_DROP
408  */
409 static enum rfc5444_result
410 _cb_timestamp_tlv(struct rfc5444_reader_tlvblock_context *context __attribute__((unused))) {
411   struct oonf_rfc5444_target *target;
412   struct os_interface_listener *core_if;
413   struct neighbor_node *node;
414   uint32_t timestamp, query, response;
415   enum rfc5444_result result;
416
417 #ifdef OONF_LOG_INFO
418   struct netaddr_str nbuf;
419 #endif
420
421   struct neighbor_key key;
422
423   core_if = oonf_rfc5444_get_core_if_listener(_protocol->input_interface);
424
425   /* get input-addr/interface combination */
426   memset(&key, 0, sizeof(key));
427   memcpy(&key.src, _protocol->input_address, sizeof(key.src));
428   key.if_index = core_if->data.index;
429
430   /* get timestamp packet TLV */
431   memcpy(&timestamp, _pkt_tlvs[IDX_PKTTLV_SEND].tlv->single_value, sizeof(timestamp));
432   timestamp = ntohl(timestamp);
433
434   /* get query packet TLV */
435   if (_pkt_tlvs[IDX_PKTTLV_QUERY].tlv) {
436     memcpy(&query, _pkt_tlvs[IDX_PKTTLV_QUERY].tlv->single_value, sizeof(query));
437     query = ntohl(query);
438   }
439   else {
440     query = 0;
441   }
442
443   /* get response packet TLV */
444   if (_pkt_tlvs[IDX_PKTTLV_RESPONSE].tlv) {
445     memcpy(&response, _pkt_tlvs[IDX_PKTTLV_RESPONSE].tlv->single_value, sizeof(response));
446     response = ntohl(response);
447   }
448   else {
449     response = 0;
450   }
451
452   /* get or create timestamp node */
453   node = avl_find_element(&_timestamp_tree, &key, node, _node);
454   if (!node) {
455     target = oonf_rfc5444_add_target(_protocol->input_interface, _protocol->input_address);
456     if (!target) {
457       return RFC5444_DROP_PACKET;
458     }
459
460     node = _add_neighbor_node(&key);
461     if (!node) {
462       oonf_rfc5444_remove_target(target);
463       return RFC5444_DROP_PACKET;
464     }
465
466     /* remember target */
467     node->_target = target;
468
469     /* reset timestamp to force query */
470     timestamp = 0;
471   }
472
473   OONF_DEBUG(LOG_SIMPLE_SECURITY, "Received new packet from %s/%s(%u): timestamp=%u (was %u), query=%u response=%u",
474     netaddr_to_string(&nbuf, &key.src), core_if->data.name, key.if_index, timestamp, node->last_counter, query,
475     response);
476
477   /* remember querry */
478   node->send_response = query;
479
480   /* handle incoming timestamp and query response */
481   if ((node->send_query > 0 && response == node->send_query) ||
482       (node->last_counter < timestamp && node->last_counter + _config.window_size > timestamp)) {
483     OONF_INFO(LOG_SIMPLE_SECURITY, "Received valid timestamp %u from %s/%s", timestamp,
484       netaddr_to_string(&nbuf, &key.src), core_if->data.name);
485
486     /* we got a valid query/response or a valid timestamp */
487     node->last_counter = timestamp;
488     node->send_query = 0;
489
490     result = RFC5444_OKAY;
491
492     /* stop trigger if we don't need to send a response, we just received a good packet */
493     if (node->send_response == 0) {
494       oonf_timer_stop(&node->_trigger);
495     }
496   }
497   else if (node->last_counter == timestamp) {
498     /* duplicate of the last packet */
499     result = RFC5444_DROP_PACKET;
500   }
501   else {
502     /* old counter, trigger challenge */
503     if (node->send_query == 0) {
504       /* generate new query */
505       node->send_query = ++_local_timestamp;
506     }
507
508     /* and drop packet */
509     result = RFC5444_DROP_PACKET;
510   }
511
512   if (node->send_query > 0 || node->send_response > 0) {
513     OONF_INFO(
514       LOG_SIMPLE_SECURITY, "Trigger challenge message: query=%u response=%u", node->send_query, node->send_response);
515
516     if (!oonf_timer_is_active(&node->_trigger)) {
517       /* trigger query response */
518       oonf_timer_set(&node->_trigger, node->send_response > 0 ? 1 : _config.trigger_delay);
519     }
520   }
521
522   /* reset validity time */
523   oonf_timer_set(&node->_vtime, _config.vtime);
524
525   return result;
526 }
527
528 /**
529  * Callback for incoming packet with missing/wrong timestamp TLV
530  * @param context RFC5444 context
531  * @return always RFC5444_DROP_PACKET
532  */
533 static enum rfc5444_result
534 _cb_timestamp_failed(struct rfc5444_reader_tlvblock_context *context __attribute((unused))) {
535   /* packet timestamp missing or wrong length */
536   return RFC5444_DROP_PACKET;
537 }
538
539 /**
540  * Callback to add timestamp, query and/or response TLVs to RFC5444 packet
541  * @param writer rfc5444 writer
542  * @param rfc5444_target rfc5444 target
543  */
544 static void
545 _cb_addPacketTLVs(struct rfc5444_writer *writer, struct rfc5444_writer_target *rfc5444_target) {
546   struct oonf_rfc5444_target *target;
547   struct os_interface_listener *core_if;
548   struct neighbor_node *node;
549   uint32_t query, response;
550 #ifdef OONF_LOG_DEBUG_INFO
551   struct netaddr_str nbuf;
552 #endif
553
554   struct neighbor_key key;
555
556   /* get OONF rfc5444 target */
557   target = oonf_rfc5444_get_target_from_rfc5444_target(rfc5444_target);
558
559   /* get core interface */
560   core_if = oonf_rfc5444_get_core_if_listener(target->interface);
561
562   /* get input-addr/interface combination */
563   memset(&key, 0, sizeof(key));
564   memcpy(&key.src, &target->dst, sizeof(key.src));
565   key.if_index = core_if->data.index;
566
567   /* Challenge query and response are only valid for unicasts */
568   node = avl_find_element(&_timestamp_tree, &key, node, _node);
569   if (node) {
570     if (node->send_query) {
571       /* send query */
572       query = htonl(node->send_query);
573
574       rfc5444_writer_add_packettlv(
575         writer, rfc5444_target, RFC5444_PKTTLV_CHALLENGE, RFC5444_CHALLENGE_QUERY, &query, sizeof(query));
576     }
577     if (node->send_response) {
578       /* send response */
579       response = htonl(node->send_response);
580
581       rfc5444_writer_add_packettlv(
582         writer, rfc5444_target, RFC5444_PKTTLV_CHALLENGE, RFC5444_CHALLENGE_RESPONSE, &response, sizeof(response));
583     }
584     OONF_DEBUG(LOG_SIMPLE_SECURITY, "Add packettvs to %s/%s(%u): query=%u response=%u",
585       netaddr_to_string(&nbuf, &key.src), core_if->data.name, key.if_index, node->send_query, node->send_response);
586
587     /* clear response */
588     node->send_response = 0;
589   }
590
591   /* allocate space for timestamp tlv */
592   rfc5444_writer_allocate_packettlv(writer, rfc5444_target, true, 4);
593 }
594
595 /**
596  * Callback to add the preallocated timestamp tlv to rfc5444 packet
597  * @param writer rfc5444 writer
598  * @param rfc5444_target rfc5444 target
599  */
600 static void
601 _cb_finishPacketTLVs(struct rfc5444_writer *writer, struct rfc5444_writer_target *rfc5444_target) {
602   uint32_t timestamp;
603
604   /* always send a timestamp */
605   timestamp = htonl(++_local_timestamp);
606
607   rfc5444_writer_set_packettlv(
608     writer, rfc5444_target, RFC7182_PKTTLV_TIMESTAMP, RFC7182_TIMESTAMP_EXT_MONOTONIC, &timestamp, sizeof(timestamp));
609 }
610
611 /**
612  * AVL comparator for neighbor nodes
613  * @param p1
614  * @param p2
615  * @return
616  */
617 static int
618 _avl_comp_timestamp_keys(const void *p1, const void *p2) {
619   return memcmp(p1, p2, sizeof(struct neighbor_key));
620 }
621
622 /**
623  * Callback for configuration changes
624  */
625 static void
626 _cb_config_changed(void) {
627   if (cfg_schema_tobin(&_config, _sise_section.post, _sise_entries, ARRAYSIZE(_sise_entries))) {
628     OONF_WARN(LOG_SIMPLE_SECURITY, "Cannot convert configuration for " OONF_SIMPLE_SECURITY_SUBSYSTEM);
629     return;
630   }
631
632   _config.key_length = strlen(_config.key);
633 }