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