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