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