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