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