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   if (gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread))
138     return (0);
139 # endif
140
141   if (!gcry_check_version (GCRYPT_VERSION))
142     return (0);
143
144   if (!gcry_control (GCRYCTL_INIT_SECMEM, 32768, 0))
145     return (0);
146
147   gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
148
149   result = 1;
150   return (1);
151 #else
152   return(0);
153 #endif
154 } /* }}} _Bool have_gcrypt */
155
156 #ifndef HAVE_HTONLL
157 static uint64_t htonll (uint64_t val) /* {{{ */
158 {
159   static int config = 0;
160
161   uint32_t hi;
162   uint32_t lo;
163
164   if (config == 0)
165   {
166     uint16_t h = 0x1234;
167     uint16_t n = htons (h);
168
169     if (h == n)
170       config = 1;
171     else
172       config = 2;
173   }
174
175   if (config == 1)
176     return (val);
177
178   hi = (uint32_t) (val >> 32);
179   lo = (uint32_t) (val & 0x00000000FFFFFFFF);
180
181   hi = htonl (hi);
182   lo = htonl (lo);
183
184   return ((((uint64_t) lo) << 32) | ((uint64_t) hi));
185 } /* }}} uint64_t htonll */
186 #endif
187
188 static double htond (double val) /* {{{ */
189 {
190   static int config = 0;
191
192   union { uint8_t byte[8]; double floating; } in;
193   union { uint8_t byte[8]; double floating; } out;
194
195   if (config == 0)
196   {
197     double d = 8.642135e130;
198     uint8_t c[8];
199
200     memcpy (c, &d, 8);
201
202     if ((c[0] == 0x2f) && (c[1] == 0x25)
203         && (c[2] == 0xc0) && (c[3] == 0xc7)
204         && (c[4] == 0x43) && (c[5] == 0x2b)
205         && (c[6] == 0x1f) && (c[7] == 0x5b))
206       config = 1; /* need nothing */
207     else if ((c[7] == 0x2f) && (c[6] == 0x25)
208         && (c[5] == 0xc0) && (c[4] == 0xc7)
209         && (c[3] == 0x43) && (c[2] == 0x2b)
210         && (c[1] == 0x1f) && (c[0] == 0x5b))
211       config = 2; /* endian flip */
212     else if ((c[4] == 0x2f) && (c[5] == 0x25)
213         && (c[6] == 0xc0) && (c[7] == 0xc7)
214         && (c[0] == 0x43) && (c[1] == 0x2b)
215         && (c[2] == 0x1f) && (c[3] == 0x5b))
216       config = 3; /* int swap */
217     else
218       config = 4;
219   }
220
221   if (isnan (val))
222   {
223     out.byte[0] = out.byte[1] = out.byte[2] = out.byte[3] = 0x00;
224     out.byte[4] = out.byte[5] = 0x00;
225     out.byte[6] = 0xf8;
226     out.byte[7] = 0x7f;
227     return (out.floating);
228   }
229   else if (config == 1)
230     return (val);
231   else if (config == 2)
232   {
233     in.floating = val;
234     out.byte[0] = in.byte[7];
235     out.byte[1] = in.byte[6];
236     out.byte[2] = in.byte[5];
237     out.byte[3] = in.byte[4];
238     out.byte[4] = in.byte[3];
239     out.byte[5] = in.byte[2];
240     out.byte[6] = in.byte[1];
241     out.byte[7] = in.byte[0];
242     return (out.floating);
243   }
244   else if (config == 3)
245   {
246     in.floating = val;
247     out.byte[0] = in.byte[4];
248     out.byte[1] = in.byte[5];
249     out.byte[2] = in.byte[6];
250     out.byte[3] = in.byte[7];
251     out.byte[4] = in.byte[0];
252     out.byte[5] = in.byte[1];
253     out.byte[6] = in.byte[2];
254     out.byte[7] = in.byte[3];
255     return (out.floating);
256   }
257   else
258   {
259     /* If in doubt, just copy the value back to the caller. */
260     return (val);
261   }
262 } /* }}} double htond */
263
264 static int nb_add_values (char **ret_buffer, /* {{{ */
265     size_t *ret_buffer_len,
266     const lcc_value_list_t *vl)
267 {
268   char *packet_ptr;
269   size_t packet_len;
270
271   uint16_t      pkg_type;
272   uint16_t      pkg_length;
273   uint16_t      pkg_num_values;
274   uint8_t       pkg_values_types[vl->values_len];
275   value_t       pkg_values[vl->values_len];
276
277   size_t offset;
278   size_t i;
279
280   packet_len = sizeof (pkg_type) + sizeof (pkg_length)
281     + sizeof (pkg_num_values)
282     + sizeof (pkg_values_types)
283     + sizeof (pkg_values);
284
285   if (*ret_buffer_len < packet_len)
286     return (ENOMEM);
287
288   pkg_type = htons (TYPE_VALUES);
289   pkg_length = htons ((uint16_t) packet_len);
290   pkg_num_values = htons ((uint16_t) vl->values_len);
291
292   for (i = 0; i < vl->values_len; i++)
293   {
294     pkg_values_types[i] = (uint8_t) vl->values_types[i];
295     switch (vl->values_types[i])
296     {
297       case LCC_TYPE_COUNTER:
298         pkg_values[i].counter = (counter_t) htonll (vl->values[i].counter);
299         break;
300
301       case LCC_TYPE_GAUGE:
302         pkg_values[i].gauge = (gauge_t) htond (vl->values[i].gauge);
303         break;
304
305       case LCC_TYPE_DERIVE:
306         pkg_values[i].derive = (derive_t) htonll (vl->values[i].derive);
307         break;
308
309       case LCC_TYPE_ABSOLUTE:
310         pkg_values[i].absolute = (absolute_t) htonll (vl->values[i].absolute);
311         break;
312
313       default:
314         return (EINVAL);
315     } /* switch (vl->values_types[i]) */
316   } /* for (vl->values_len) */
317
318   /*
319    * Use `memcpy' to write everything to the buffer, because the pointer
320    * may be unaligned and some architectures, such as SPARC, can't handle
321    * that.
322    */
323   packet_ptr = *ret_buffer;
324   offset = 0;
325   memcpy (packet_ptr + offset, &pkg_type, sizeof (pkg_type));
326   offset += sizeof (pkg_type);
327   memcpy (packet_ptr + offset, &pkg_length, sizeof (pkg_length));
328   offset += sizeof (pkg_length);
329   memcpy (packet_ptr + offset, &pkg_num_values, sizeof (pkg_num_values));
330   offset += sizeof (pkg_num_values);
331   memcpy (packet_ptr + offset, pkg_values_types, sizeof (pkg_values_types));
332   offset += sizeof (pkg_values_types);
333   memcpy (packet_ptr + offset, pkg_values, sizeof (pkg_values));
334   offset += sizeof (pkg_values);
335
336   assert (offset == packet_len);
337
338   *ret_buffer = packet_ptr + packet_len;
339   *ret_buffer_len -= packet_len;
340   return (0);
341 } /* }}} int nb_add_values */
342
343 static int nb_add_number (char **ret_buffer, /* {{{ */
344     size_t *ret_buffer_len,
345     uint16_t type, uint64_t value)
346 {
347   char *packet_ptr;
348   size_t packet_len;
349
350   uint16_t pkg_type;
351   uint16_t pkg_length;
352   uint64_t pkg_value;
353
354   size_t offset;
355
356   packet_len = sizeof (pkg_type)
357     + sizeof (pkg_length)
358     + sizeof (pkg_value);
359
360   if (*ret_buffer_len < packet_len)
361     return (ENOMEM);
362
363   pkg_type = htons (type);
364   pkg_length = htons ((uint16_t) packet_len);
365   pkg_value = htonll (value);
366
367   packet_ptr = *ret_buffer;
368   offset = 0;
369   memcpy (packet_ptr + offset, &pkg_type, sizeof (pkg_type));
370   offset += sizeof (pkg_type);
371   memcpy (packet_ptr + offset, &pkg_length, sizeof (pkg_length));
372   offset += sizeof (pkg_length);
373   memcpy (packet_ptr + offset, &pkg_value, sizeof (pkg_value));
374   offset += sizeof (pkg_value);
375
376   assert (offset == packet_len);
377
378   *ret_buffer = packet_ptr + packet_len;
379   *ret_buffer_len -= packet_len;
380   return (0);
381 } /* }}} int nb_add_number */
382
383 static int nb_add_time (char **ret_buffer, /* {{{ */
384     size_t *ret_buffer_len,
385     uint16_t type, double value)
386 {
387   /* Convert to collectd's "cdtime" representation. */
388   uint64_t cdtime_value = (uint64_t) (value * 1073741824.0);
389   return (nb_add_number (ret_buffer, ret_buffer_len, type, cdtime_value));
390 } /* }}} int nb_add_time */
391
392 static int nb_add_string (char **ret_buffer, /* {{{ */
393     size_t *ret_buffer_len,
394     uint16_t type, const char *str, size_t str_len)
395 {
396   char *packet_ptr;
397   size_t packet_len;
398
399   uint16_t pkg_type;
400   uint16_t pkg_length;
401
402   size_t offset;
403
404   packet_len = sizeof (pkg_type)
405     + sizeof (pkg_length)
406     + str_len + 1;
407   if (*ret_buffer_len < packet_len)
408     return (ENOMEM);
409
410   pkg_type = htons (type);
411   pkg_length = htons ((uint16_t) packet_len);
412
413   packet_ptr = *ret_buffer;
414   offset = 0;
415   memcpy (packet_ptr + offset, &pkg_type, sizeof (pkg_type));
416   offset += sizeof (pkg_type);
417   memcpy (packet_ptr + offset, &pkg_length, sizeof (pkg_length));
418   offset += sizeof (pkg_length);
419   memcpy (packet_ptr + offset, str, str_len);
420   offset += str_len;
421   memset (packet_ptr + offset, 0, 1);
422   offset += 1;
423
424   assert (offset == packet_len);
425
426   *ret_buffer = packet_ptr + packet_len;
427   *ret_buffer_len -= packet_len;
428   return (0);
429 } /* }}} int nb_add_string */
430
431 static int nb_add_value_list (lcc_network_buffer_t *nb, /* {{{ */
432     const lcc_value_list_t *vl)
433 {
434   char *buffer = nb->ptr;
435   size_t buffer_size = nb->free;
436
437   const lcc_identifier_t *ident_src;
438   lcc_identifier_t *ident_dst;
439
440   ident_src = &vl->identifier;
441   ident_dst = &nb->state.identifier;
442
443   if (strcmp (ident_dst->host, ident_src->host) != 0)
444   {
445     if (nb_add_string (&buffer, &buffer_size, TYPE_HOST,
446           ident_src->host, strlen (ident_src->host)) != 0)
447       return (-1);
448     SSTRNCPY (ident_dst->host, ident_src->host, sizeof (ident_dst->host));
449   }
450
451   if (strcmp (ident_dst->plugin, ident_src->plugin) != 0)
452   {
453     if (nb_add_string (&buffer, &buffer_size, TYPE_PLUGIN,
454           ident_src->plugin, strlen (ident_src->plugin)) != 0)
455       return (-1);
456     SSTRNCPY (ident_dst->plugin, ident_src->plugin,
457         sizeof (ident_dst->plugin));
458   }
459
460   if (strcmp (ident_dst->plugin_instance,
461         ident_src->plugin_instance) != 0)
462   {
463     if (nb_add_string (&buffer, &buffer_size, TYPE_PLUGIN_INSTANCE,
464           ident_src->plugin_instance,
465           strlen (ident_src->plugin_instance)) != 0)
466       return (-1);
467     SSTRNCPY (ident_dst->plugin_instance, ident_src->plugin_instance,
468         sizeof (ident_dst->plugin_instance));
469   }
470
471   if (strcmp (ident_dst->type, ident_src->type) != 0)
472   {
473     if (nb_add_string (&buffer, &buffer_size, TYPE_TYPE,
474           ident_src->type, strlen (ident_src->type)) != 0)
475       return (-1);
476     SSTRNCPY (ident_dst->type, ident_src->type, sizeof (ident_dst->type));
477   }
478
479   if (strcmp (ident_dst->type_instance,
480         ident_src->type_instance) != 0)
481   {
482     if (nb_add_string (&buffer, &buffer_size, TYPE_TYPE_INSTANCE,
483           ident_src->type_instance,
484           strlen (ident_src->type_instance)) != 0)
485       return (-1);
486     SSTRNCPY (ident_dst->type_instance, ident_src->type_instance,
487         sizeof (ident_dst->type_instance));
488   }
489
490   if (nb->state.time != vl->time)
491   {
492     if (nb_add_time (&buffer, &buffer_size, TYPE_TIME_HR, vl->time))
493       return (-1);
494     nb->state.time = vl->time;
495   }
496
497   if (nb->state.interval != vl->interval)
498   {
499     if (nb_add_time (&buffer, &buffer_size, TYPE_INTERVAL_HR, vl->interval))
500       return (-1);
501     nb->state.interval = vl->interval;
502   }
503
504   if (nb_add_values (&buffer, &buffer_size, vl) != 0)
505     return (-1);
506
507   nb->ptr = buffer;
508   nb->free = buffer_size;
509   return (0);
510 } /* }}} int nb_add_value_list */
511
512 #if HAVE_LIBGCRYPT
513 static int nb_add_signature (lcc_network_buffer_t *nb) /* {{{ */
514 {
515   char *buffer;
516   size_t buffer_size;
517
518   gcry_md_hd_t hd;
519   gcry_error_t err;
520   unsigned char *hash;
521   const size_t hash_length = 32;
522
523   /* The type, length and username have already been filled in by
524    * "lcc_network_buffer_initialize". All we do here is calculate the hash over
525    * the username and the data and add the hash value to the buffer. */
526
527   buffer = nb->buffer + PART_SIGNATURE_SHA256_SIZE;
528   assert (nb->size >= (nb->free + PART_SIGNATURE_SHA256_SIZE));
529   buffer_size = nb->size - (nb->free + PART_SIGNATURE_SHA256_SIZE);
530
531   hd = NULL;
532   err = gcry_md_open (&hd, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC);
533   if (err != 0)
534     return (-1);
535
536   assert (nb->password != NULL);
537   err = gcry_md_setkey (hd, nb->password, strlen (nb->password));
538   if (err != 0)
539   {
540     gcry_md_close (hd);
541     return (-1);
542   }
543
544   gcry_md_write (hd, buffer, buffer_size);
545   hash = gcry_md_read (hd, GCRY_MD_SHA256);
546   if (hash == NULL)
547   {
548     gcry_md_close (hd);
549     return (-1);
550   }
551
552   assert (((2 * sizeof (uint16_t)) + hash_length) == PART_SIGNATURE_SHA256_SIZE);
553   memcpy (nb->buffer + (2 * sizeof (uint16_t)), hash, hash_length);
554
555   gcry_md_close (hd);
556   return (0);
557 } /* }}} int nb_add_signature */
558
559 static int nb_add_encryption (lcc_network_buffer_t *nb) /* {{{ */
560 {
561   size_t package_length;
562   char *encr_ptr; /* pointer to data being encrypted */
563   size_t encr_size;
564
565   char *hash_ptr; /* pointer to data being hashed */
566   size_t hash_size;
567   char hash[20];
568
569   uint16_t pkg_length;
570   gcry_error_t err;
571
572   /* Fill in the package length */
573   package_length = nb->size - nb->free;
574   pkg_length = htons ((uint16_t) package_length);
575   memcpy (nb->buffer + 2, &pkg_length, sizeof (pkg_length));
576
577   /* Calculate what to hash */
578   hash_ptr = nb->buffer + PART_ENCRYPTION_AES256_SIZE;
579   hash_size = package_length - nb->encr_header_len;
580
581   /* Calculate what to encrypt */
582   encr_ptr = hash_ptr - sizeof (hash);
583   encr_size = hash_size + sizeof (hash);
584
585   /* Calculate the SHA-1 hash */
586   gcry_md_hash_buffer (GCRY_MD_SHA1, hash, hash_ptr, hash_size);
587   memcpy (encr_ptr, hash, sizeof (hash));
588
589   if (nb->encr_cypher == NULL)
590   {
591     unsigned char password_hash[32];
592
593     err = gcry_cipher_open (&nb->encr_cypher,
594         GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_OFB, /* flags = */ 0);
595     if (err != 0)
596       return (-1);
597
598     /* Calculate our 256bit key used for AES */
599     gcry_md_hash_buffer (GCRY_MD_SHA256, password_hash,
600         nb->password, strlen (nb->password));
601
602     err = gcry_cipher_setkey (nb->encr_cypher,
603         password_hash, sizeof (password_hash));
604     if (err != 0)
605     {
606       gcry_cipher_close (nb->encr_cypher);
607       nb->encr_cypher = NULL;
608       return (-1);
609     }
610   }
611   else /* if (nb->encr_cypher != NULL) */
612   {
613     gcry_cipher_reset (nb->encr_cypher);
614   }
615
616   /* Set the initialization vector */
617   err = gcry_cipher_setiv (nb->encr_cypher,
618       nb->encr_iv, sizeof (nb->encr_iv));
619   if (err != 0)
620   {
621     gcry_cipher_close (nb->encr_cypher);
622     nb->encr_cypher = NULL;
623     return (-1);
624   }
625
626   /* Encrypt the buffer in-place */
627   err = gcry_cipher_encrypt (nb->encr_cypher,
628       encr_ptr, encr_size,
629       /* in = */ NULL, /* in len = */ 0);
630   if (err != 0)
631   {
632     gcry_cipher_close (nb->encr_cypher);
633     nb->encr_cypher = NULL;
634     return (-1);
635   }
636
637   return (0);
638 } /* }}} int nb_add_encryption */
639 #endif
640
641 /*
642  * Public functions
643  */
644 lcc_network_buffer_t *lcc_network_buffer_create (size_t size) /* {{{ */
645 {
646   lcc_network_buffer_t *nb;
647
648   if (size == 0)
649     size = LCC_NETWORK_BUFFER_SIZE_DEFAULT;
650
651   if (size < 128)
652   {
653     errno = EINVAL;
654     return (NULL);
655   }
656
657   nb = malloc (sizeof (*nb));
658   if (nb == NULL)
659     return (NULL);
660   memset (nb, 0, sizeof (*nb));
661
662   nb->size = size;
663   nb->buffer = malloc (nb->size);
664   if (nb->buffer == NULL)
665   {
666     free (nb);
667     return (NULL);
668   }
669   memset (nb->buffer, 0, nb->size);
670
671   nb->ptr = nb->buffer;
672   nb->free = nb->size;
673
674   nb->seclevel = NONE;
675   nb->username = NULL;
676   nb->password = NULL;
677
678   return (nb);
679 } /* }}} lcc_network_buffer_t *lcc_network_buffer_create */
680
681 void lcc_network_buffer_destroy (lcc_network_buffer_t *nb) /* {{{ */
682 {
683   if (nb == NULL)
684     return;
685
686   free (nb->buffer);
687   free (nb);
688 } /* }}} void lcc_network_buffer_destroy */
689
690 int lcc_network_buffer_set_security_level (lcc_network_buffer_t *nb, /* {{{ */
691     lcc_security_level_t level,
692     const char *username, const char *password)
693 {
694   char *username_copy;
695   char *password_copy;
696
697   if (level == NONE)
698   {
699     free (nb->username);
700     free (nb->password);
701     nb->username = NULL;
702     nb->password = NULL;
703     nb->seclevel = NONE;
704     lcc_network_buffer_initialize (nb);
705     return (0);
706   }
707
708   if (!have_gcrypt ())
709     return (ENOTSUP);
710
711   username_copy = strdup (username);
712   password_copy = strdup (password);
713   if ((username_copy == NULL) || (password_copy == NULL))
714   {
715     free (username_copy);
716     free (password_copy);
717     return (ENOMEM);
718   }
719
720   free (nb->username);
721   free (nb->password);
722   nb->username = username_copy;
723   nb->password = password_copy;
724   nb->seclevel = level;
725
726   lcc_network_buffer_initialize (nb);
727   return (0);
728 } /* }}} int lcc_network_buffer_set_security_level */
729
730 int lcc_network_buffer_initialize (lcc_network_buffer_t *nb) /* {{{ */
731 {
732   if (nb == NULL)
733     return (EINVAL);
734
735   memset (nb->buffer, 0, nb->size);
736   memset (&nb->state, 0, sizeof (nb->state));
737   nb->ptr = nb->buffer;
738   nb->free = nb->size;
739
740 #if HAVE_LIBGCRYPT
741   if (nb->seclevel == SIGN)
742   {
743     size_t username_len;
744     uint16_t pkg_type = htons (TYPE_SIGN_SHA256);
745     uint16_t pkg_length = PART_SIGNATURE_SHA256_SIZE;
746
747     assert (nb->username != NULL);
748     username_len = strlen (nb->username);
749     pkg_length = htons (pkg_length + ((uint16_t) username_len));
750
751     /* Fill in everything but the hash value here. */
752     memcpy (nb->ptr, &pkg_type, sizeof (pkg_type));
753     memcpy (nb->ptr + sizeof (pkg_type), &pkg_length, sizeof (pkg_length));
754     nb->ptr += PART_SIGNATURE_SHA256_SIZE;
755     nb->free -= PART_SIGNATURE_SHA256_SIZE;
756
757     memcpy (nb->ptr, nb->username, username_len);
758     nb->ptr += username_len;
759     nb->free -= username_len;
760   }
761   else if (nb->seclevel == ENCRYPT)
762   {
763     size_t username_length = strlen (nb->username);
764     uint16_t pkg_type = htons (TYPE_ENCR_AES256);
765     uint16_t pkg_length = 0; /* Filled in in finalize. */
766     uint16_t pkg_user_len = htons ((uint16_t) username_length);
767     char hash[20];
768
769     nb->encr_header_len = username_length;
770     nb->encr_header_len += PART_ENCRYPTION_AES256_SIZE;
771
772     gcry_randomize ((void *) &nb->encr_iv, sizeof (nb->encr_iv),
773         GCRY_STRONG_RANDOM);
774
775     /* Filled in in finalize. */
776     memset (hash, 0, sizeof (hash));
777
778     ADD_STATIC (nb, pkg_type);
779     ADD_STATIC (nb, pkg_length);
780     ADD_STATIC (nb, pkg_user_len);
781     ADD_GENERIC (nb, nb->username, username_length);
782     ADD_GENERIC (nb, nb->encr_iv, sizeof (nb->encr_iv));
783     ADD_GENERIC (nb, hash, sizeof (hash));
784     assert ((nb->encr_header_len + nb->free) == nb->size);
785   }
786 #endif
787
788   return (0);
789 } /* }}} int lcc_network_buffer_initialize */
790
791 int lcc_network_buffer_finalize (lcc_network_buffer_t *nb) /* {{{ */
792 {
793   if (nb == NULL)
794     return (EINVAL);
795
796 #if HAVE_LIBGCRYPT
797   if (nb->seclevel == SIGN)
798     return nb_add_signature (nb);
799   else if (nb->seclevel == ENCRYPT)
800     return nb_add_encryption (nb);
801 #endif
802
803   return (0);
804 } /* }}} int lcc_network_buffer_finalize */
805
806 int lcc_network_buffer_add_value (lcc_network_buffer_t *nb, /* {{{ */
807     const lcc_value_list_t *vl)
808 {
809   int status;
810
811   if ((nb == NULL) || (vl == NULL))
812     return (EINVAL);
813
814   status = nb_add_value_list (nb, vl);
815   return (status);
816 } /* }}} int lcc_network_buffer_add_value */
817
818 int lcc_network_buffer_get (lcc_network_buffer_t *nb, /* {{{ */
819     void *buffer, size_t *buffer_size)
820 {
821   size_t sz_required;
822   size_t sz_available;
823
824   if ((nb == NULL) || (buffer_size == NULL))
825     return (EINVAL);
826
827   assert (nb->size >= nb->free);
828   sz_required = nb->size - nb->free;
829   sz_available = *buffer_size;
830
831   *buffer_size = sz_required;
832   if (buffer != NULL)
833     memcpy (buffer, nb->buffer,
834         (sz_available < sz_required) ? sz_available : sz_required);
835
836   return (0);
837 } /* }}} int lcc_network_buffer_get */
838
839 /* vim: set sw=2 sts=2 et fdm=marker : */