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