libcollectdclient: Sending signed network packages works now.
[collectd.git] / src / libcollectdclient / network_buffer.c
1 /**
2  * collectd - src/libcollectdclient/network_buffer.c
3  * Copyright (C) 2010  Florian octo Forster
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU Lesser General Public License as published by
7  * the Free Software Foundation; only version 2.1 of the License is
8  * applicable.
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
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with this program; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  *
19  * Authors:
20  *   Florian octo Forster <octo at verplant.org>
21  **/
22
23 #include "config.h"
24
25 #include <stdlib.h>
26 #include <string.h>
27 #include <math.h>
28 #include <assert.h>
29 #include <errno.h>
30 #include <arpa/inet.h> /* htons */
31
32 #include <pthread.h>
33
34 #if HAVE_LIBGCRYPT
35 #include <gcrypt.h>
36 GCRY_THREAD_OPTION_PTHREAD_IMPL;
37 #endif
38
39 #include "collectd/network_buffer.h"
40
41 #define TYPE_HOST            0x0000
42 #define TYPE_TIME            0x0001
43 #define TYPE_PLUGIN          0x0002
44 #define TYPE_PLUGIN_INSTANCE 0x0003
45 #define TYPE_TYPE            0x0004
46 #define TYPE_TYPE_INSTANCE   0x0005
47 #define TYPE_VALUES          0x0006
48 #define TYPE_INTERVAL        0x0007
49
50 /* Types to transmit notifications */
51 #define TYPE_MESSAGE         0x0100
52 #define TYPE_SEVERITY        0x0101
53
54 #define TYPE_SIGN_SHA256     0x0200
55 #define TYPE_ENCR_AES256     0x0210
56
57 #define PART_SIGNATURE_SHA256_SIZE 36
58
59 /*
60  * Data types
61  */
62 struct lcc_network_buffer_s
63 {
64   char *buffer;
65   size_t size;
66
67   lcc_value_list_t state;
68   char *ptr;
69   size_t free;
70
71   lcc_security_level_t seclevel;
72   char *username;
73   char *password;
74 };
75
76 #define SSTRNCPY(dst,src,sz) do { \
77   strncpy ((dst), (src), (sz));   \
78   (dst)[(sz) - 1] = 0;            \
79 } while (0)
80
81 /*
82  * Private functions
83  */
84 static _Bool have_gcrypt (void) /* {{{ */
85 {
86   static _Bool result = 0;
87   static _Bool need_init = 1;
88
89   if (!need_init)
90     return (result);
91   need_init = 0;
92
93   gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
94
95   if (!gcry_check_version (GCRYPT_VERSION))
96     return (0);
97
98   gcry_control (GCRYCTL_INIT_SECMEM, 32768, 0);
99   gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
100
101   result = 1;
102   return (1);
103 } /* }}} _Bool have_gcrypt */
104
105 static uint64_t htonll (uint64_t val) /* {{{ */
106 {
107   static int config = 0;
108
109   uint32_t hi;
110   uint32_t lo;
111
112   if (config == 0)
113   {
114     uint16_t h = 0x1234;
115     uint16_t n = htons (h);
116
117     if (h == n)
118       config = 1;
119     else
120       config = 2;
121   }
122
123   if (config == 1)
124     return (val);
125
126   hi = (uint32_t) (val >> 32);
127   lo = (uint32_t) (val & 0x00000000FFFFFFFF);
128
129   hi = htonl (hi);
130   lo = htonl (lo);
131
132   return ((((uint64_t) lo) << 32) | ((uint64_t) hi));
133 } /* }}} uint64_t htonll */
134
135 static double htond (double val) /* {{{ */
136 {
137   static int config = 0;
138
139   union { uint8_t byte[8]; double floating; } in;
140   union { uint8_t byte[8]; double floating; } out;
141
142   if (config == 0)
143   {
144     double d = 8.642135e130;
145     uint8_t c[8];
146
147     memcpy (c, &d, 8);
148
149     if ((c[0] == 0x2f) && (c[1] == 0x25)
150         && (c[2] == 0xc0) && (c[3] == 0xc7)
151         && (c[4] == 0x43) && (c[5] == 0x2b)
152         && (c[6] == 0x1f) && (c[7] == 0x5b))
153       config = 1; /* need nothing */
154     else if ((c[7] == 0x2f) && (c[6] == 0x25)
155         && (c[5] == 0xc0) && (c[4] == 0xc7)
156         && (c[3] == 0x43) && (c[2] == 0x2b)
157         && (c[1] == 0x1f) && (c[0] == 0x5b))
158       config = 2; /* endian flip */
159     else if ((c[4] == 0x2f) && (c[5] == 0x25)
160         && (c[6] == 0xc0) && (c[7] == 0xc7)
161         && (c[0] == 0x43) && (c[1] == 0x2b)
162         && (c[2] == 0x1f) && (c[3] == 0x5b))
163       config = 3; /* int swap */
164     else
165       config = 4;
166   }
167
168   if (isnan (val))
169   {
170     out.byte[0] = out.byte[1] = out.byte[2] = out.byte[3] = 0x00;
171     out.byte[4] = out.byte[5] = 0x00;
172     out.byte[6] = 0xf8;
173     out.byte[7] = 0x7f;
174     return (out.floating);
175   }
176   else if (config == 1)
177     return (val);
178   else if (config == 2)
179   {
180     in.floating = val;
181     out.byte[0] = in.byte[7];
182     out.byte[1] = in.byte[6];
183     out.byte[2] = in.byte[5];
184     out.byte[3] = in.byte[4];
185     out.byte[4] = in.byte[3];
186     out.byte[5] = in.byte[2];
187     out.byte[6] = in.byte[1];
188     out.byte[7] = in.byte[0];
189     return (out.floating);
190   }
191   else if (config == 3)
192   {
193     in.floating = val;
194     out.byte[0] = in.byte[4];
195     out.byte[1] = in.byte[5];
196     out.byte[2] = in.byte[6];
197     out.byte[3] = in.byte[7];
198     out.byte[4] = in.byte[0];
199     out.byte[5] = in.byte[1];
200     out.byte[6] = in.byte[2];
201     out.byte[7] = in.byte[3];
202     return (out.floating);
203   }
204   else
205   {
206     /* If in doubt, just copy the value back to the caller. */
207     return (val);
208   }
209 } /* }}} double htond */
210
211 static int nb_add_values (char **ret_buffer, /* {{{ */
212     size_t *ret_buffer_len,
213     const lcc_value_list_t *vl)
214 {
215   char *packet_ptr;
216   size_t packet_len;
217
218   uint16_t      pkg_type;
219   uint16_t      pkg_length;
220   uint16_t      pkg_num_values;
221   uint8_t       pkg_values_types[vl->values_len];
222   value_t       pkg_values[vl->values_len];
223
224   size_t offset;
225   size_t i;
226
227   packet_len = sizeof (pkg_type) + sizeof (pkg_length)
228     + sizeof (pkg_num_values)
229     + sizeof (pkg_values_types)
230     + sizeof (pkg_values);
231
232   if (*ret_buffer_len < packet_len)
233     return (ENOMEM);
234
235   pkg_type = htons (TYPE_VALUES);
236   pkg_length = htons ((uint16_t) packet_len);
237   pkg_num_values = htons ((uint16_t) vl->values_len);
238
239   for (i = 0; i < vl->values_len; i++)
240   {
241     pkg_values_types[i] = (uint8_t) vl->values_types[i];
242     switch (vl->values_types[i])
243     {
244       case LCC_TYPE_COUNTER:
245         pkg_values[i].counter = (counter_t) htonll (vl->values[i].counter);
246         break;
247
248       case LCC_TYPE_GAUGE:
249         pkg_values[i].gauge = (gauge_t) htond (vl->values[i].gauge);
250         break;
251
252       case LCC_TYPE_DERIVE:
253         pkg_values[i].derive = (derive_t) htonll (vl->values[i].derive);
254         break;
255
256       case LCC_TYPE_ABSOLUTE:
257         pkg_values[i].absolute = (absolute_t) htonll (vl->values[i].absolute);
258         break;
259
260       default:
261         return (EINVAL);
262     } /* switch (vl->values_types[i]) */
263   } /* for (vl->values_len) */
264
265   /*
266    * Use `memcpy' to write everything to the buffer, because the pointer
267    * may be unaligned and some architectures, such as SPARC, can't handle
268    * that.
269    */
270   packet_ptr = *ret_buffer;
271   offset = 0;
272   memcpy (packet_ptr + offset, &pkg_type, sizeof (pkg_type));
273   offset += sizeof (pkg_type);
274   memcpy (packet_ptr + offset, &pkg_length, sizeof (pkg_length));
275   offset += sizeof (pkg_length);
276   memcpy (packet_ptr + offset, &pkg_num_values, sizeof (pkg_num_values));
277   offset += sizeof (pkg_num_values);
278   memcpy (packet_ptr + offset, pkg_values_types, sizeof (pkg_values_types));
279   offset += sizeof (pkg_values_types);
280   memcpy (packet_ptr + offset, pkg_values, sizeof (pkg_values));
281   offset += sizeof (pkg_values);
282
283   assert (offset == packet_len);
284
285   *ret_buffer = packet_ptr + packet_len;
286   *ret_buffer_len -= packet_len;
287   return (0);
288 } /* }}} int nb_add_values */
289
290 static int nb_add_number (char **ret_buffer, /* {{{ */
291     size_t *ret_buffer_len,
292     uint16_t type, uint64_t value)
293 {
294   char *packet_ptr;
295   size_t packet_len;
296
297   uint16_t pkg_type;
298   uint16_t pkg_length;
299   uint64_t pkg_value;
300
301   size_t offset;
302
303   packet_len = sizeof (pkg_type)
304     + sizeof (pkg_length)
305     + sizeof (pkg_value);
306
307   if (*ret_buffer_len < packet_len)
308     return (ENOMEM);
309
310   pkg_type = htons (type);
311   pkg_length = htons ((uint16_t) packet_len);
312   pkg_value = htonll (value);
313
314   packet_ptr = *ret_buffer;
315   offset = 0;
316   memcpy (packet_ptr + offset, &pkg_type, sizeof (pkg_type));
317   offset += sizeof (pkg_type);
318   memcpy (packet_ptr + offset, &pkg_length, sizeof (pkg_length));
319   offset += sizeof (pkg_length);
320   memcpy (packet_ptr + offset, &pkg_value, sizeof (pkg_value));
321   offset += sizeof (pkg_value);
322
323   assert (offset == packet_len);
324
325   *ret_buffer = packet_ptr + packet_len;
326   *ret_buffer_len -= packet_len;
327   return (0);
328 } /* }}} int nb_add_number */
329
330 static int nb_add_string (char **ret_buffer, /* {{{ */
331     size_t *ret_buffer_len,
332     uint16_t type, const char *str, size_t str_len)
333 {
334   char *packet_ptr;
335   size_t packet_len;
336
337   uint16_t pkg_type;
338   uint16_t pkg_length;
339
340   size_t offset;
341
342   packet_len = sizeof (pkg_type)
343     + sizeof (pkg_length)
344     + str_len + 1;
345   if (*ret_buffer_len < packet_len)
346     return (ENOMEM);
347
348   pkg_type = htons (type);
349   pkg_length = htons ((uint16_t) packet_len);
350
351   packet_ptr = *ret_buffer;
352   offset = 0;
353   memcpy (packet_ptr + offset, &pkg_type, sizeof (pkg_type));
354   offset += sizeof (pkg_type);
355   memcpy (packet_ptr + offset, &pkg_length, sizeof (pkg_length));
356   offset += sizeof (pkg_length);
357   memcpy (packet_ptr + offset, str, str_len);
358   offset += str_len;
359   memset (packet_ptr + offset, 0, 1);
360   offset += 1;
361
362   assert (offset == packet_len);
363
364   *ret_buffer = packet_ptr + packet_len;
365   *ret_buffer_len -= packet_len;
366   return (0);
367 } /* }}} int nb_add_string */
368
369 static int nb_add_value_list (lcc_network_buffer_t *nb, /* {{{ */
370     const lcc_value_list_t *vl)
371 {
372   char *buffer = nb->ptr;
373   size_t buffer_size = nb->free;
374
375   const lcc_identifier_t *ident_src;
376   lcc_identifier_t *ident_dst;
377
378   ident_src = &vl->identifier;
379   ident_dst = &nb->state.identifier;
380
381   if (strcmp (ident_dst->host, ident_src->host) != 0)
382   {
383     if (nb_add_string (&buffer, &buffer_size, TYPE_HOST,
384           ident_src->host, strlen (ident_src->host)) != 0)
385       return (-1);
386     SSTRNCPY (ident_dst->host, ident_src->host, sizeof (ident_dst->host));
387   }
388
389   if (strcmp (ident_dst->plugin, ident_src->plugin) != 0)
390   {
391     if (nb_add_string (&buffer, &buffer_size, TYPE_PLUGIN,
392           ident_src->plugin, strlen (ident_src->plugin)) != 0)
393       return (-1);
394     SSTRNCPY (ident_dst->plugin, ident_src->plugin,
395         sizeof (ident_dst->plugin));
396   }
397
398   if (strcmp (ident_dst->plugin_instance,
399         ident_src->plugin_instance) != 0)
400   {
401     if (nb_add_string (&buffer, &buffer_size, TYPE_PLUGIN_INSTANCE,
402           ident_src->plugin_instance,
403           strlen (ident_src->plugin_instance)) != 0)
404       return (-1);
405     SSTRNCPY (ident_dst->plugin_instance, ident_src->plugin_instance,
406         sizeof (ident_dst->plugin_instance));
407   }
408
409   if (strcmp (ident_dst->type, ident_src->type) != 0)
410   {
411     if (nb_add_string (&buffer, &buffer_size, TYPE_TYPE,
412           ident_src->type, strlen (ident_src->type)) != 0)
413       return (-1);
414     SSTRNCPY (ident_dst->type, ident_src->type, sizeof (ident_dst->type));
415   }
416
417   if (strcmp (ident_dst->type_instance,
418         ident_src->type_instance) != 0)
419   {
420     if (nb_add_string (&buffer, &buffer_size, TYPE_TYPE_INSTANCE,
421           ident_src->type_instance,
422           strlen (ident_src->type_instance)) != 0)
423       return (-1);
424     SSTRNCPY (ident_dst->type_instance, ident_src->type_instance,
425         sizeof (ident_dst->type_instance));
426   }
427
428   if (nb->state.time != vl->time)
429   {
430     if (nb_add_number (&buffer, &buffer_size, TYPE_TIME,
431           (uint64_t) vl->time))
432       return (-1);
433     nb->state.time = vl->time;
434   }
435
436   if (nb->state.interval != vl->interval)
437   {
438     if (nb_add_number (&buffer, &buffer_size, TYPE_INTERVAL,
439           (uint64_t) vl->interval))
440       return (-1);
441     nb->state.interval = vl->interval;
442   }
443
444   if (nb_add_values (&buffer, &buffer_size, vl) != 0)
445     return (-1);
446
447   nb->ptr = buffer;
448   nb->free = buffer_size;
449   return (0);
450 } /* }}} int nb_add_value_list */
451
452 static int nb_add_signature (lcc_network_buffer_t *nb) /* {{{ */
453 {
454   char *buffer;
455   size_t buffer_size;
456
457   gcry_md_hd_t hd;
458   gcry_error_t err;
459   unsigned char *hash;
460   const size_t hash_length = 32;
461
462   /* The type, length and username have already been filled in by
463    * "lcc_network_buffer_initialize". All we do here is calculate the hash over
464    * the username and the data and add the hash value to the buffer. */
465
466   buffer = nb->buffer + PART_SIGNATURE_SHA256_SIZE;
467   assert (nb->size >= (nb->free + PART_SIGNATURE_SHA256_SIZE));
468   buffer_size = nb->size - (nb->free + PART_SIGNATURE_SHA256_SIZE);
469
470   hd = NULL;
471   err = gcry_md_open (&hd, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC);
472   if (err != 0)
473     return (-1);
474
475   assert (nb->password != NULL);
476   err = gcry_md_setkey (hd, nb->password, strlen (nb->password));
477   if (err != 0)
478   {
479     gcry_md_close (hd);
480     return (-1);
481   }
482
483   gcry_md_write (hd, buffer, buffer_size);
484   hash = gcry_md_read (hd, GCRY_MD_SHA256);
485   if (hash == NULL)
486   {
487     gcry_md_close (hd);
488     return (-1);
489   }
490
491   assert (((2 * sizeof (uint16_t)) + hash_length) == PART_SIGNATURE_SHA256_SIZE);
492   memcpy (nb->buffer + (2 * sizeof (uint16_t)), hash, hash_length);
493
494   gcry_md_close (hd);
495   return (0);
496 } /* }}} int nb_add_signature */
497
498 /*
499  * Public functions
500  */
501 lcc_network_buffer_t *lcc_network_buffer_create (size_t size) /* {{{ */
502 {
503   lcc_network_buffer_t *nb;
504
505   if (size == 0)
506     size = LCC_NETWORK_BUFFER_SIZE_DEFAULT;
507
508   if (size < 128)
509   {
510     errno = EINVAL;
511     return (NULL);
512   }
513
514   nb = malloc (sizeof (*nb));
515   if (nb == NULL)
516     return (NULL);
517   memset (nb, 0, sizeof (*nb));
518
519   nb->size = size;
520   nb->buffer = malloc (nb->size);
521   if (nb->buffer == NULL)
522   {
523     free (nb);
524     return (NULL);
525   }
526   memset (nb->buffer, 0, nb->size);
527
528   nb->ptr = nb->buffer;
529   nb->free = nb->size;
530
531   nb->seclevel = NONE;
532   nb->username = NULL;
533   nb->password = NULL;
534
535   return (nb);
536 } /* }}} lcc_network_buffer_t *lcc_network_buffer_create */
537
538 void lcc_network_buffer_destroy (lcc_network_buffer_t *nb) /* {{{ */
539 {
540   if (nb == NULL)
541     return;
542
543   free (nb->buffer);
544   free (nb);
545 } /* }}} void lcc_network_buffer_destroy */
546
547 int lcc_network_buffer_set_security_level (lcc_network_buffer_t *nb, /* {{{ */
548     lcc_security_level_t level,
549     const char *username, const char *password)
550 {
551   char *username_copy;
552   char *password_copy;
553
554   if (level == NONE)
555   {
556     free (nb->username);
557     free (nb->password);
558     nb->username = NULL;
559     nb->password = NULL;
560     nb->seclevel = NONE;
561     lcc_network_buffer_initialize (nb);
562     return (0);
563   }
564
565   if (!have_gcrypt ())
566     return (ENOTSUP);
567
568   username_copy = strdup (username);
569   password_copy = strdup (password);
570   if ((username_copy == NULL) || (password_copy == NULL))
571   {
572     free (username_copy);
573     free (password_copy);
574     return (ENOMEM);
575   }
576
577   free (nb->username);
578   free (nb->password);
579   nb->username = username_copy;
580   nb->password = password_copy;
581   nb->seclevel = level;
582
583   lcc_network_buffer_initialize (nb);
584   return (0);
585 } /* }}} int lcc_network_buffer_set_security_level */
586
587 int lcc_network_buffer_initialize (lcc_network_buffer_t *nb) /* {{{ */
588 {
589   if (nb == NULL)
590     return (EINVAL);
591
592   memset (nb->buffer, 0, nb->size);
593   memset (&nb->state, 0, sizeof (nb->state));
594   nb->ptr = nb->buffer;
595   nb->free = nb->size;
596
597   if (nb->seclevel == SIGN)
598   {
599     size_t username_len;
600     uint16_t pkg_type = htons (TYPE_SIGN_SHA256);
601     uint16_t pkg_length = PART_SIGNATURE_SHA256_SIZE;
602
603     assert (nb->username != NULL);
604     username_len = strlen (nb->username);
605     pkg_length = htons (pkg_length + ((uint16_t) username_len));
606
607     /* Fill in everything but the hash value here. */
608     memcpy (nb->ptr, &pkg_type, sizeof (pkg_type));
609     memcpy (nb->ptr + sizeof (pkg_type), &pkg_length, sizeof (pkg_length));
610     nb->ptr += PART_SIGNATURE_SHA256_SIZE;
611     nb->free -= PART_SIGNATURE_SHA256_SIZE;
612
613     memcpy (nb->ptr, nb->username, username_len);
614     nb->ptr += username_len;
615     nb->free -= username_len;
616   }
617
618   /* FIXME: If security is enabled, reserve space for the signature /
619    * encryption block here. */
620
621   return (0);
622 } /* }}} int lcc_network_buffer_initialize */
623
624 int lcc_network_buffer_finalize (lcc_network_buffer_t *nb) /* {{{ */
625 {
626   if (nb == NULL)
627     return (EINVAL);
628
629   if (nb->seclevel == SIGN)
630     nb_add_signature (nb);
631   /* FIXME: If security is enabled, sign or encrypt the packet here. */
632
633   return (0);
634 } /* }}} int lcc_network_buffer_finalize */
635
636 int lcc_network_buffer_add_value (lcc_network_buffer_t *nb, /* {{{ */
637     const lcc_value_list_t *vl)
638 {
639   int status;
640
641   if ((nb == NULL) || (vl == NULL))
642     return (EINVAL);
643
644   status = nb_add_value_list (nb, vl);
645   return (status);
646 } /* }}} int lcc_network_buffer_add_value */
647
648 int lcc_network_buffer_get (lcc_network_buffer_t *nb, /* {{{ */
649     void *buffer, size_t *buffer_size)
650 {
651   size_t sz_required;
652   size_t sz_available;
653
654   if ((nb == NULL) || (buffer_size == NULL))
655     return (EINVAL);
656
657   assert (nb->size >= nb->free);
658   sz_required = nb->size - nb->free;
659   sz_available = *buffer_size;
660
661   *buffer_size = sz_required;
662   if (buffer != NULL)
663     memcpy (buffer, nb->buffer,
664         (sz_available < sz_required) ? sz_available : sz_required);
665
666   return (0);
667 } /* }}} int lcc_network_buffer_get */
668
669 /* vim: set sw=2 sts=2 et fdm=marker : */