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