Merge branch 'collectd-5.4' into collectd-5.5
[collectd.git] / src / libcollectdclient / network_buffer.c
1 /**
2  * collectd - src/libcollectdclient/network_buffer.c
3  * Copyright (C) 2010-2015  Florian octo Forster
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  *
23  * Authors:
24  *   Florian octo Forster <octo at collectd.org>
25  **/
26
27 #include "config.h"
28
29 #include <stdlib.h>
30 #include <string.h>
31 #include <math.h>
32 #include <assert.h>
33 #include <errno.h>
34 #include <arpa/inet.h> /* htons */
35
36 #include <pthread.h>
37
38 #if HAVE_LIBGCRYPT
39 # include <pthread.h>
40 # if defined __APPLE__
41 /* default xcode compiler throws warnings even when deprecated functionality
42  * is not used. -Werror breaks the build because of erroneous warnings.
43  * http://stackoverflow.com/questions/10556299/compiler-warnings-with-libgcrypt-v1-5-0/12830209#12830209
44  */
45 #  pragma GCC diagnostic ignored "-Wdeprecated-declarations"
46 # endif
47 /* FreeBSD's copy of libgcrypt extends the existing GCRYPT_NO_DEPRECATED
48  * to properly hide all deprecated functionality.
49  * http://svnweb.freebsd.org/ports/head/security/libgcrypt/files/patch-src__gcrypt.h.in
50  */
51 # define GCRYPT_NO_DEPRECATED
52 # include <gcrypt.h>
53 # if defined __APPLE__
54 /* Re enable deprecation warnings */
55 #  pragma GCC diagnostic warning "-Wdeprecated-declarations"
56 # endif
57 # if GCRYPT_VERSION_NUMBER < 0x010600
58 GCRY_THREAD_OPTION_PTHREAD_IMPL;
59 # endif
60 #endif
61
62 #include "collectd/network_buffer.h"
63
64 #define TYPE_HOST            0x0000
65 #define TYPE_TIME            0x0001
66 #define TYPE_TIME_HR         0x0008
67 #define TYPE_PLUGIN          0x0002
68 #define TYPE_PLUGIN_INSTANCE 0x0003
69 #define TYPE_TYPE            0x0004
70 #define TYPE_TYPE_INSTANCE   0x0005
71 #define TYPE_VALUES          0x0006
72 #define TYPE_INTERVAL        0x0007
73 #define TYPE_INTERVAL_HR     0x0009
74
75 /* Types to transmit notifications */
76 #define TYPE_MESSAGE         0x0100
77 #define TYPE_SEVERITY        0x0101
78
79 #define TYPE_SIGN_SHA256     0x0200
80 #define TYPE_ENCR_AES256     0x0210
81
82 #define PART_SIGNATURE_SHA256_SIZE 36
83 #define PART_ENCRYPTION_AES256_SIZE 42
84
85 #define ADD_GENERIC(nb,srcptr,size) do {         \
86   assert ((size) <= (nb)->free);                 \
87   memcpy ((nb)->ptr, (srcptr), (size));          \
88   (nb)->ptr += (size);                           \
89   (nb)->free -= (size);                          \
90 } while (0)
91
92 #define ADD_STATIC(nb,var) \
93   ADD_GENERIC(nb,&(var),sizeof(var));
94
95 /*
96  * Data types
97  */
98 struct lcc_network_buffer_s
99 {
100   char *buffer;
101   size_t size;
102
103   lcc_value_list_t state;
104   char *ptr;
105   size_t free;
106
107   lcc_security_level_t seclevel;
108   char *username;
109   char *password;
110
111 #if HAVE_LIBGCRYPT
112   gcry_cipher_hd_t encr_cypher;
113   size_t encr_header_len;
114   char encr_iv[16];
115 #endif
116 };
117
118 #define SSTRNCPY(dst,src,sz) do { \
119   strncpy ((dst), (src), (sz));   \
120   (dst)[(sz) - 1] = 0;            \
121 } while (0)
122
123 /*
124  * Private functions
125  */
126 static _Bool have_gcrypt (void) /* {{{ */
127 {
128   static _Bool result = 0;
129   static _Bool need_init = 1;
130
131   if (!need_init)
132     return (result);
133   need_init = 0;
134
135 #if HAVE_LIBGCRYPT
136 # if GCRYPT_VERSION_NUMBER < 0x010600
137   gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
138 # endif
139
140   if (!gcry_check_version (GCRYPT_VERSION))
141     return (0);
142
143   gcry_control (GCRYCTL_INIT_SECMEM, 32768, 0);
144   gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
145
146   result = 1;
147   return (1);
148 #else
149   return(0);
150 #endif
151 } /* }}} _Bool have_gcrypt */
152
153 #ifndef HAVE_HTONLL
154 static uint64_t htonll (uint64_t val) /* {{{ */
155 {
156   static int config = 0;
157
158   uint32_t hi;
159   uint32_t lo;
160
161   if (config == 0)
162   {
163     uint16_t h = 0x1234;
164     uint16_t n = htons (h);
165
166     if (h == n)
167       config = 1;
168     else
169       config = 2;
170   }
171
172   if (config == 1)
173     return (val);
174
175   hi = (uint32_t) (val >> 32);
176   lo = (uint32_t) (val & 0x00000000FFFFFFFF);
177
178   hi = htonl (hi);
179   lo = htonl (lo);
180
181   return ((((uint64_t) lo) << 32) | ((uint64_t) hi));
182 } /* }}} uint64_t htonll */
183 #endif
184
185 static double htond (double val) /* {{{ */
186 {
187   static int config = 0;
188
189   union { uint8_t byte[8]; double floating; } in;
190   union { uint8_t byte[8]; double floating; } out;
191
192   if (config == 0)
193   {
194     double d = 8.642135e130;
195     uint8_t c[8];
196
197     memcpy (c, &d, 8);
198
199     if ((c[0] == 0x2f) && (c[1] == 0x25)
200         && (c[2] == 0xc0) && (c[3] == 0xc7)
201         && (c[4] == 0x43) && (c[5] == 0x2b)
202         && (c[6] == 0x1f) && (c[7] == 0x5b))
203       config = 1; /* need nothing */
204     else if ((c[7] == 0x2f) && (c[6] == 0x25)
205         && (c[5] == 0xc0) && (c[4] == 0xc7)
206         && (c[3] == 0x43) && (c[2] == 0x2b)
207         && (c[1] == 0x1f) && (c[0] == 0x5b))
208       config = 2; /* endian flip */
209     else if ((c[4] == 0x2f) && (c[5] == 0x25)
210         && (c[6] == 0xc0) && (c[7] == 0xc7)
211         && (c[0] == 0x43) && (c[1] == 0x2b)
212         && (c[2] == 0x1f) && (c[3] == 0x5b))
213       config = 3; /* int swap */
214     else
215       config = 4;
216   }
217
218   if (isnan (val))
219   {
220     out.byte[0] = out.byte[1] = out.byte[2] = out.byte[3] = 0x00;
221     out.byte[4] = out.byte[5] = 0x00;
222     out.byte[6] = 0xf8;
223     out.byte[7] = 0x7f;
224     return (out.floating);
225   }
226   else if (config == 1)
227     return (val);
228   else if (config == 2)
229   {
230     in.floating = val;
231     out.byte[0] = in.byte[7];
232     out.byte[1] = in.byte[6];
233     out.byte[2] = in.byte[5];
234     out.byte[3] = in.byte[4];
235     out.byte[4] = in.byte[3];
236     out.byte[5] = in.byte[2];
237     out.byte[6] = in.byte[1];
238     out.byte[7] = in.byte[0];
239     return (out.floating);
240   }
241   else if (config == 3)
242   {
243     in.floating = val;
244     out.byte[0] = in.byte[4];
245     out.byte[1] = in.byte[5];
246     out.byte[2] = in.byte[6];
247     out.byte[3] = in.byte[7];
248     out.byte[4] = in.byte[0];
249     out.byte[5] = in.byte[1];
250     out.byte[6] = in.byte[2];
251     out.byte[7] = in.byte[3];
252     return (out.floating);
253   }
254   else
255   {
256     /* If in doubt, just copy the value back to the caller. */
257     return (val);
258   }
259 } /* }}} double htond */
260
261 static int nb_add_values (char **ret_buffer, /* {{{ */
262     size_t *ret_buffer_len,
263     const lcc_value_list_t *vl)
264 {
265   char *packet_ptr;
266   size_t packet_len;
267
268   uint16_t      pkg_type;
269   uint16_t      pkg_length;
270   uint16_t      pkg_num_values;
271   uint8_t       pkg_values_types[vl->values_len];
272   value_t       pkg_values[vl->values_len];
273
274   size_t offset;
275   size_t i;
276
277   packet_len = sizeof (pkg_type) + sizeof (pkg_length)
278     + sizeof (pkg_num_values)
279     + sizeof (pkg_values_types)
280     + sizeof (pkg_values);
281
282   if (*ret_buffer_len < packet_len)
283     return (ENOMEM);
284
285   pkg_type = htons (TYPE_VALUES);
286   pkg_length = htons ((uint16_t) packet_len);
287   pkg_num_values = htons ((uint16_t) vl->values_len);
288
289   for (i = 0; i < vl->values_len; i++)
290   {
291     pkg_values_types[i] = (uint8_t) vl->values_types[i];
292     switch (vl->values_types[i])
293     {
294       case LCC_TYPE_COUNTER:
295         pkg_values[i].counter = (counter_t) htonll (vl->values[i].counter);
296         break;
297
298       case LCC_TYPE_GAUGE:
299         pkg_values[i].gauge = (gauge_t) htond (vl->values[i].gauge);
300         break;
301
302       case LCC_TYPE_DERIVE:
303         pkg_values[i].derive = (derive_t) htonll (vl->values[i].derive);
304         break;
305
306       case LCC_TYPE_ABSOLUTE:
307         pkg_values[i].absolute = (absolute_t) htonll (vl->values[i].absolute);
308         break;
309
310       default:
311         return (EINVAL);
312     } /* switch (vl->values_types[i]) */
313   } /* for (vl->values_len) */
314
315   /*
316    * Use `memcpy' to write everything to the buffer, because the pointer
317    * may be unaligned and some architectures, such as SPARC, can't handle
318    * that.
319    */
320   packet_ptr = *ret_buffer;
321   offset = 0;
322   memcpy (packet_ptr + offset, &pkg_type, sizeof (pkg_type));
323   offset += sizeof (pkg_type);
324   memcpy (packet_ptr + offset, &pkg_length, sizeof (pkg_length));
325   offset += sizeof (pkg_length);
326   memcpy (packet_ptr + offset, &pkg_num_values, sizeof (pkg_num_values));
327   offset += sizeof (pkg_num_values);
328   memcpy (packet_ptr + offset, pkg_values_types, sizeof (pkg_values_types));
329   offset += sizeof (pkg_values_types);
330   memcpy (packet_ptr + offset, pkg_values, sizeof (pkg_values));
331   offset += sizeof (pkg_values);
332
333   assert (offset == packet_len);
334
335   *ret_buffer = packet_ptr + packet_len;
336   *ret_buffer_len -= packet_len;
337   return (0);
338 } /* }}} int nb_add_values */
339
340 static int nb_add_number (char **ret_buffer, /* {{{ */
341     size_t *ret_buffer_len,
342     uint16_t type, uint64_t value)
343 {
344   char *packet_ptr;
345   size_t packet_len;
346
347   uint16_t pkg_type;
348   uint16_t pkg_length;
349   uint64_t pkg_value;
350
351   size_t offset;
352
353   packet_len = sizeof (pkg_type)
354     + sizeof (pkg_length)
355     + sizeof (pkg_value);
356
357   if (*ret_buffer_len < packet_len)
358     return (ENOMEM);
359
360   pkg_type = htons (type);
361   pkg_length = htons ((uint16_t) packet_len);
362   pkg_value = htonll (value);
363
364   packet_ptr = *ret_buffer;
365   offset = 0;
366   memcpy (packet_ptr + offset, &pkg_type, sizeof (pkg_type));
367   offset += sizeof (pkg_type);
368   memcpy (packet_ptr + offset, &pkg_length, sizeof (pkg_length));
369   offset += sizeof (pkg_length);
370   memcpy (packet_ptr + offset, &pkg_value, sizeof (pkg_value));
371   offset += sizeof (pkg_value);
372
373   assert (offset == packet_len);
374
375   *ret_buffer = packet_ptr + packet_len;
376   *ret_buffer_len -= packet_len;
377   return (0);
378 } /* }}} int nb_add_number */
379
380 static int nb_add_time (char **ret_buffer, /* {{{ */
381     size_t *ret_buffer_len,
382     uint16_t type, double value)
383 {
384   /* Convert to collectd's "cdtime" representation. */
385   uint64_t cdtime_value = (uint64_t) (value * 1073741824.0);
386   return (nb_add_number (ret_buffer, ret_buffer_len, type, cdtime_value));
387 } /* }}} int nb_add_time */
388
389 static int nb_add_string (char **ret_buffer, /* {{{ */
390     size_t *ret_buffer_len,
391     uint16_t type, const char *str, size_t str_len)
392 {
393   char *packet_ptr;
394   size_t packet_len;
395
396   uint16_t pkg_type;
397   uint16_t pkg_length;
398
399   size_t offset;
400
401   packet_len = sizeof (pkg_type)
402     + sizeof (pkg_length)
403     + str_len + 1;
404   if (*ret_buffer_len < packet_len)
405     return (ENOMEM);
406
407   pkg_type = htons (type);
408   pkg_length = htons ((uint16_t) packet_len);
409
410   packet_ptr = *ret_buffer;
411   offset = 0;
412   memcpy (packet_ptr + offset, &pkg_type, sizeof (pkg_type));
413   offset += sizeof (pkg_type);
414   memcpy (packet_ptr + offset, &pkg_length, sizeof (pkg_length));
415   offset += sizeof (pkg_length);
416   memcpy (packet_ptr + offset, str, str_len);
417   offset += str_len;
418   memset (packet_ptr + offset, 0, 1);
419   offset += 1;
420
421   assert (offset == packet_len);
422
423   *ret_buffer = packet_ptr + packet_len;
424   *ret_buffer_len -= packet_len;
425   return (0);
426 } /* }}} int nb_add_string */
427
428 static int nb_add_value_list (lcc_network_buffer_t *nb, /* {{{ */
429     const lcc_value_list_t *vl)
430 {
431   char *buffer = nb->ptr;
432   size_t buffer_size = nb->free;
433
434   const lcc_identifier_t *ident_src;
435   lcc_identifier_t *ident_dst;
436
437   ident_src = &vl->identifier;
438   ident_dst = &nb->state.identifier;
439
440   if (strcmp (ident_dst->host, ident_src->host) != 0)
441   {
442     if (nb_add_string (&buffer, &buffer_size, TYPE_HOST,
443           ident_src->host, strlen (ident_src->host)) != 0)
444       return (-1);
445     SSTRNCPY (ident_dst->host, ident_src->host, sizeof (ident_dst->host));
446   }
447
448   if (strcmp (ident_dst->plugin, ident_src->plugin) != 0)
449   {
450     if (nb_add_string (&buffer, &buffer_size, TYPE_PLUGIN,
451           ident_src->plugin, strlen (ident_src->plugin)) != 0)
452       return (-1);
453     SSTRNCPY (ident_dst->plugin, ident_src->plugin,
454         sizeof (ident_dst->plugin));
455   }
456
457   if (strcmp (ident_dst->plugin_instance,
458         ident_src->plugin_instance) != 0)
459   {
460     if (nb_add_string (&buffer, &buffer_size, TYPE_PLUGIN_INSTANCE,
461           ident_src->plugin_instance,
462           strlen (ident_src->plugin_instance)) != 0)
463       return (-1);
464     SSTRNCPY (ident_dst->plugin_instance, ident_src->plugin_instance,
465         sizeof (ident_dst->plugin_instance));
466   }
467
468   if (strcmp (ident_dst->type, ident_src->type) != 0)
469   {
470     if (nb_add_string (&buffer, &buffer_size, TYPE_TYPE,
471           ident_src->type, strlen (ident_src->type)) != 0)
472       return (-1);
473     SSTRNCPY (ident_dst->type, ident_src->type, sizeof (ident_dst->type));
474   }
475
476   if (strcmp (ident_dst->type_instance,
477         ident_src->type_instance) != 0)
478   {
479     if (nb_add_string (&buffer, &buffer_size, TYPE_TYPE_INSTANCE,
480           ident_src->type_instance,
481           strlen (ident_src->type_instance)) != 0)
482       return (-1);
483     SSTRNCPY (ident_dst->type_instance, ident_src->type_instance,
484         sizeof (ident_dst->type_instance));
485   }
486
487   if (nb->state.time != vl->time)
488   {
489     if (nb_add_time (&buffer, &buffer_size, TYPE_TIME_HR, vl->time))
490       return (-1);
491     nb->state.time = vl->time;
492   }
493
494   if (nb->state.interval != vl->interval)
495   {
496     if (nb_add_time (&buffer, &buffer_size, TYPE_INTERVAL_HR, vl->interval))
497       return (-1);
498     nb->state.interval = vl->interval;
499   }
500
501   if (nb_add_values (&buffer, &buffer_size, vl) != 0)
502     return (-1);
503
504   nb->ptr = buffer;
505   nb->free = buffer_size;
506   return (0);
507 } /* }}} int nb_add_value_list */
508
509 #if HAVE_LIBGCRYPT
510 static int nb_add_signature (lcc_network_buffer_t *nb) /* {{{ */
511 {
512   char *buffer;
513   size_t buffer_size;
514
515   gcry_md_hd_t hd;
516   gcry_error_t err;
517   unsigned char *hash;
518   const size_t hash_length = 32;
519
520   /* The type, length and username have already been filled in by
521    * "lcc_network_buffer_initialize". All we do here is calculate the hash over
522    * the username and the data and add the hash value to the buffer. */
523
524   buffer = nb->buffer + PART_SIGNATURE_SHA256_SIZE;
525   assert (nb->size >= (nb->free + PART_SIGNATURE_SHA256_SIZE));
526   buffer_size = nb->size - (nb->free + PART_SIGNATURE_SHA256_SIZE);
527
528   hd = NULL;
529   err = gcry_md_open (&hd, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC);
530   if (err != 0)
531     return (-1);
532
533   assert (nb->password != NULL);
534   err = gcry_md_setkey (hd, nb->password, strlen (nb->password));
535   if (err != 0)
536   {
537     gcry_md_close (hd);
538     return (-1);
539   }
540
541   gcry_md_write (hd, buffer, buffer_size);
542   hash = gcry_md_read (hd, GCRY_MD_SHA256);
543   if (hash == NULL)
544   {
545     gcry_md_close (hd);
546     return (-1);
547   }
548
549   assert (((2 * sizeof (uint16_t)) + hash_length) == PART_SIGNATURE_SHA256_SIZE);
550   memcpy (nb->buffer + (2 * sizeof (uint16_t)), hash, hash_length);
551
552   gcry_md_close (hd);
553   return (0);
554 } /* }}} int nb_add_signature */
555
556 static int nb_add_encryption (lcc_network_buffer_t *nb) /* {{{ */
557 {
558   size_t package_length;
559   char *encr_ptr; /* pointer to data being encrypted */
560   size_t encr_size;
561
562   char *hash_ptr; /* pointer to data being hashed */
563   size_t hash_size;
564   char hash[20];
565
566   uint16_t pkg_length;
567   gcry_error_t err;
568
569   /* Fill in the package length */
570   package_length = nb->size - nb->free;
571   pkg_length = htons ((uint16_t) package_length);
572   memcpy (nb->buffer + 2, &pkg_length, sizeof (pkg_length));
573
574   /* Calculate what to hash */
575   hash_ptr = nb->buffer + PART_ENCRYPTION_AES256_SIZE;
576   hash_size = package_length - nb->encr_header_len;
577
578   /* Calculate what to encrypt */
579   encr_ptr = hash_ptr - sizeof (hash);
580   encr_size = hash_size + sizeof (hash);
581
582   /* Calculate the SHA-1 hash */
583   gcry_md_hash_buffer (GCRY_MD_SHA1, hash, hash_ptr, hash_size);
584   memcpy (encr_ptr, hash, sizeof (hash));
585
586   if (nb->encr_cypher == NULL)
587   {
588     unsigned char password_hash[32];
589
590     err = gcry_cipher_open (&nb->encr_cypher,
591         GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_OFB, /* flags = */ 0);
592     if (err != 0)
593       return (-1);
594
595     /* Calculate our 256bit key used for AES */
596     gcry_md_hash_buffer (GCRY_MD_SHA256, password_hash,
597         nb->password, strlen (nb->password));
598
599     err = gcry_cipher_setkey (nb->encr_cypher,
600         password_hash, sizeof (password_hash));
601     if (err != 0)
602     {
603       gcry_cipher_close (nb->encr_cypher);
604       nb->encr_cypher = NULL;
605       return (-1);
606     }
607   }
608   else /* if (nb->encr_cypher != NULL) */
609   {
610     gcry_cipher_reset (nb->encr_cypher);
611   }
612
613   /* Set the initialization vector */
614   err = gcry_cipher_setiv (nb->encr_cypher,
615       nb->encr_iv, sizeof (nb->encr_iv));
616   if (err != 0)
617   {
618     gcry_cipher_close (nb->encr_cypher);
619     nb->encr_cypher = NULL;
620     return (-1);
621   }
622
623   /* Encrypt the buffer in-place */
624   err = gcry_cipher_encrypt (nb->encr_cypher,
625       encr_ptr, encr_size,
626       /* in = */ NULL, /* in len = */ 0);
627   if (err != 0)
628   {
629     gcry_cipher_close (nb->encr_cypher);
630     nb->encr_cypher = NULL;
631     return (-1);
632   }
633
634   return (0);
635 } /* }}} int nb_add_encryption */
636 #endif
637
638 /*
639  * Public functions
640  */
641 lcc_network_buffer_t *lcc_network_buffer_create (size_t size) /* {{{ */
642 {
643   lcc_network_buffer_t *nb;
644
645   if (size == 0)
646     size = LCC_NETWORK_BUFFER_SIZE_DEFAULT;
647
648   if (size < 128)
649   {
650     errno = EINVAL;
651     return (NULL);
652   }
653
654   nb = malloc (sizeof (*nb));
655   if (nb == NULL)
656     return (NULL);
657   memset (nb, 0, sizeof (*nb));
658
659   nb->size = size;
660   nb->buffer = malloc (nb->size);
661   if (nb->buffer == NULL)
662   {
663     free (nb);
664     return (NULL);
665   }
666   memset (nb->buffer, 0, nb->size);
667
668   nb->ptr = nb->buffer;
669   nb->free = nb->size;
670
671   nb->seclevel = NONE;
672   nb->username = NULL;
673   nb->password = NULL;
674
675   return (nb);
676 } /* }}} lcc_network_buffer_t *lcc_network_buffer_create */
677
678 void lcc_network_buffer_destroy (lcc_network_buffer_t *nb) /* {{{ */
679 {
680   if (nb == NULL)
681     return;
682
683   free (nb->buffer);
684   free (nb);
685 } /* }}} void lcc_network_buffer_destroy */
686
687 int lcc_network_buffer_set_security_level (lcc_network_buffer_t *nb, /* {{{ */
688     lcc_security_level_t level,
689     const char *username, const char *password)
690 {
691   char *username_copy;
692   char *password_copy;
693
694   if (level == NONE)
695   {
696     free (nb->username);
697     free (nb->password);
698     nb->username = NULL;
699     nb->password = NULL;
700     nb->seclevel = NONE;
701     lcc_network_buffer_initialize (nb);
702     return (0);
703   }
704
705   if (!have_gcrypt ())
706     return (ENOTSUP);
707
708   username_copy = strdup (username);
709   password_copy = strdup (password);
710   if ((username_copy == NULL) || (password_copy == NULL))
711   {
712     free (username_copy);
713     free (password_copy);
714     return (ENOMEM);
715   }
716
717   free (nb->username);
718   free (nb->password);
719   nb->username = username_copy;
720   nb->password = password_copy;
721   nb->seclevel = level;
722
723   lcc_network_buffer_initialize (nb);
724   return (0);
725 } /* }}} int lcc_network_buffer_set_security_level */
726
727 int lcc_network_buffer_initialize (lcc_network_buffer_t *nb) /* {{{ */
728 {
729   if (nb == NULL)
730     return (EINVAL);
731
732   memset (nb->buffer, 0, nb->size);
733   memset (&nb->state, 0, sizeof (nb->state));
734   nb->ptr = nb->buffer;
735   nb->free = nb->size;
736
737 #if HAVE_LIBGCRYPT
738   if (nb->seclevel == SIGN)
739   {
740     size_t username_len;
741     uint16_t pkg_type = htons (TYPE_SIGN_SHA256);
742     uint16_t pkg_length = PART_SIGNATURE_SHA256_SIZE;
743
744     assert (nb->username != NULL);
745     username_len = strlen (nb->username);
746     pkg_length = htons (pkg_length + ((uint16_t) username_len));
747
748     /* Fill in everything but the hash value here. */
749     memcpy (nb->ptr, &pkg_type, sizeof (pkg_type));
750     memcpy (nb->ptr + sizeof (pkg_type), &pkg_length, sizeof (pkg_length));
751     nb->ptr += PART_SIGNATURE_SHA256_SIZE;
752     nb->free -= PART_SIGNATURE_SHA256_SIZE;
753
754     memcpy (nb->ptr, nb->username, username_len);
755     nb->ptr += username_len;
756     nb->free -= username_len;
757   }
758   else if (nb->seclevel == ENCRYPT)
759   {
760     size_t username_length = strlen (nb->username);
761     uint16_t pkg_type = htons (TYPE_ENCR_AES256);
762     uint16_t pkg_length = 0; /* Filled in in finalize. */
763     uint16_t pkg_user_len = htons ((uint16_t) username_length);
764     char hash[20];
765
766     nb->encr_header_len = username_length;
767     nb->encr_header_len += PART_ENCRYPTION_AES256_SIZE;
768
769     gcry_randomize ((void *) &nb->encr_iv, sizeof (nb->encr_iv),
770         GCRY_STRONG_RANDOM);
771
772     /* Filled in in finalize. */
773     memset (hash, 0, sizeof (hash));
774
775     ADD_STATIC (nb, pkg_type);
776     ADD_STATIC (nb, pkg_length);
777     ADD_STATIC (nb, pkg_user_len);
778     ADD_GENERIC (nb, nb->username, username_length);
779     ADD_GENERIC (nb, nb->encr_iv, sizeof (nb->encr_iv));
780     ADD_GENERIC (nb, hash, sizeof (hash));
781     assert ((nb->encr_header_len + nb->free) == nb->size);
782   }
783 #endif
784
785   return (0);
786 } /* }}} int lcc_network_buffer_initialize */
787
788 int lcc_network_buffer_finalize (lcc_network_buffer_t *nb) /* {{{ */
789 {
790   if (nb == NULL)
791     return (EINVAL);
792
793 #if HAVE_LIBGCRYPT
794   if (nb->seclevel == SIGN)
795     return nb_add_signature (nb);
796   else if (nb->seclevel == ENCRYPT)
797     return nb_add_encryption (nb);
798 #endif
799
800   return (0);
801 } /* }}} int lcc_network_buffer_finalize */
802
803 int lcc_network_buffer_add_value (lcc_network_buffer_t *nb, /* {{{ */
804     const lcc_value_list_t *vl)
805 {
806   int status;
807
808   if ((nb == NULL) || (vl == NULL))
809     return (EINVAL);
810
811   status = nb_add_value_list (nb, vl);
812   return (status);
813 } /* }}} int lcc_network_buffer_add_value */
814
815 int lcc_network_buffer_get (lcc_network_buffer_t *nb, /* {{{ */
816     void *buffer, size_t *buffer_size)
817 {
818   size_t sz_required;
819   size_t sz_available;
820
821   if ((nb == NULL) || (buffer_size == NULL))
822     return (EINVAL);
823
824   assert (nb->size >= nb->free);
825   sz_required = nb->size - nb->free;
826   sz_available = *buffer_size;
827
828   *buffer_size = sz_required;
829   if (buffer != NULL)
830     memcpy (buffer, nb->buffer,
831         (sz_available < sz_required) ? sz_available : sz_required);
832
833   return (0);
834 } /* }}} int lcc_network_buffer_get */
835
836 /* vim: set sw=2 sts=2 et fdm=marker : */