libcollectdclient: Add encryption support for 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 WIN32
129   HCRYPTPROV hProv;
130   size_t encr_header_len;
131   BYTE encr_iv[16];
132 #elif HAVE_LIBGCRYPT
133   gcry_cipher_hd_t encr_cypher;
134   size_t encr_header_len;
135   char encr_iv[16];
136 #endif
137 };
138
139 #define SSTRNCPY(dst,src,sz) do { \
140   strncpy ((dst), (src), (sz));   \
141   (dst)[(sz) - 1] = 0;            \
142 } while (0)
143
144 /*
145  * Private functions
146  */
147 static _Bool have_gcrypt (void) /* {{{ */
148 {
149   static _Bool result = 0;
150   static _Bool need_init = 1;
151
152   if (!need_init)
153     return (result);
154   need_init = 0;
155
156 #if HAVE_LIBGCRYPT
157   gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
158
159   if (!gcry_check_version (GCRYPT_VERSION))
160     return (0);
161
162   gcry_control (GCRYCTL_INIT_SECMEM, 32768, 0);
163   gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
164
165   result = 1;
166   return (1);
167 #else
168   return(0);
169 #endif
170 } /* }}} _Bool have_gcrypt */
171
172 #ifndef HAVE_HTONLL
173 static uint64_t htonll (uint64_t val) /* {{{ */
174 {
175   static int config = 0;
176
177   uint32_t hi;
178   uint32_t lo;
179
180   if (config == 0)
181   {
182     uint16_t h = 0x1234;
183     uint16_t n = htons (h);
184
185     if (h == n)
186       config = 1;
187     else
188       config = 2;
189   }
190
191   if (config == 1)
192     return (val);
193
194   hi = (uint32_t) (val >> 32);
195   lo = (uint32_t) (val & 0x00000000FFFFFFFF);
196
197   hi = htonl (hi);
198   lo = htonl (lo);
199
200   return ((((uint64_t) lo) << 32) | ((uint64_t) hi));
201 } /* }}} uint64_t htonll */
202 #endif
203
204 static double htond (double val) /* {{{ */
205 {
206   static int config = 0;
207
208   union { uint8_t byte[8]; double floating; } in;
209   union { uint8_t byte[8]; double floating; } out;
210
211   if (config == 0)
212   {
213     double d = 8.642135e130;
214     uint8_t c[8];
215
216     memcpy (c, &d, 8);
217
218     if ((c[0] == 0x2f) && (c[1] == 0x25)
219         && (c[2] == 0xc0) && (c[3] == 0xc7)
220         && (c[4] == 0x43) && (c[5] == 0x2b)
221         && (c[6] == 0x1f) && (c[7] == 0x5b))
222       config = 1; /* need nothing */
223     else if ((c[7] == 0x2f) && (c[6] == 0x25)
224         && (c[5] == 0xc0) && (c[4] == 0xc7)
225         && (c[3] == 0x43) && (c[2] == 0x2b)
226         && (c[1] == 0x1f) && (c[0] == 0x5b))
227       config = 2; /* endian flip */
228     else if ((c[4] == 0x2f) && (c[5] == 0x25)
229         && (c[6] == 0xc0) && (c[7] == 0xc7)
230         && (c[0] == 0x43) && (c[1] == 0x2b)
231         && (c[2] == 0x1f) && (c[3] == 0x5b))
232       config = 3; /* int swap */
233     else
234       config = 4;
235   }
236
237   if (isnan (val))
238   {
239     out.byte[0] = out.byte[1] = out.byte[2] = out.byte[3] = 0x00;
240     out.byte[4] = out.byte[5] = 0x00;
241     out.byte[6] = 0xf8;
242     out.byte[7] = 0x7f;
243     return (out.floating);
244   }
245   else if (config == 1)
246     return (val);
247   else if (config == 2)
248   {
249     in.floating = val;
250     out.byte[0] = in.byte[7];
251     out.byte[1] = in.byte[6];
252     out.byte[2] = in.byte[5];
253     out.byte[3] = in.byte[4];
254     out.byte[4] = in.byte[3];
255     out.byte[5] = in.byte[2];
256     out.byte[6] = in.byte[1];
257     out.byte[7] = in.byte[0];
258     return (out.floating);
259   }
260   else if (config == 3)
261   {
262     in.floating = val;
263     out.byte[0] = in.byte[4];
264     out.byte[1] = in.byte[5];
265     out.byte[2] = in.byte[6];
266     out.byte[3] = in.byte[7];
267     out.byte[4] = in.byte[0];
268     out.byte[5] = in.byte[1];
269     out.byte[6] = in.byte[2];
270     out.byte[7] = in.byte[3];
271     return (out.floating);
272   }
273   else
274   {
275     /* If in doubt, just copy the value back to the caller. */
276     return (val);
277   }
278 } /* }}} double htond */
279
280 static int nb_add_values (char **ret_buffer, /* {{{ */
281     size_t *ret_buffer_len,
282     const lcc_value_list_t *vl)
283 {
284   char *packet_ptr;
285   size_t packet_len;
286
287   uint16_t      pkg_type;
288   uint16_t      pkg_length;
289   uint16_t      pkg_num_values;
290   uint8_t       pkg_values_types[vl->values_len];
291   value_t       pkg_values[vl->values_len];
292
293   size_t offset;
294   size_t i;
295
296   packet_len = sizeof (pkg_type) + sizeof (pkg_length)
297     + sizeof (pkg_num_values)
298     + sizeof (pkg_values_types)
299     + sizeof (pkg_values);
300
301   if (*ret_buffer_len < packet_len)
302     return (ENOMEM);
303
304   pkg_type = htons (TYPE_VALUES);
305   pkg_length = htons ((uint16_t) packet_len);
306   pkg_num_values = htons ((uint16_t) vl->values_len);
307
308   for (i = 0; i < vl->values_len; i++)
309   {
310     pkg_values_types[i] = (uint8_t) vl->values_types[i];
311     switch (vl->values_types[i])
312     {
313       case LCC_TYPE_COUNTER:
314         pkg_values[i].counter = (counter_t) htonll (vl->values[i].counter);
315         break;
316
317       case LCC_TYPE_GAUGE:
318         pkg_values[i].gauge = (gauge_t) htond (vl->values[i].gauge);
319         break;
320
321       case LCC_TYPE_DERIVE:
322         pkg_values[i].derive = (derive_t) htonll (vl->values[i].derive);
323         break;
324
325       case LCC_TYPE_ABSOLUTE:
326         pkg_values[i].absolute = (absolute_t) htonll (vl->values[i].absolute);
327         break;
328
329       default:
330         return (EINVAL);
331     } /* switch (vl->values_types[i]) */
332   } /* for (vl->values_len) */
333
334   /*
335    * Use `memcpy' to write everything to the buffer, because the pointer
336    * may be unaligned and some architectures, such as SPARC, can't handle
337    * that.
338    */
339   packet_ptr = *ret_buffer;
340   offset = 0;
341   memcpy (packet_ptr + offset, &pkg_type, sizeof (pkg_type));
342   offset += sizeof (pkg_type);
343   memcpy (packet_ptr + offset, &pkg_length, sizeof (pkg_length));
344   offset += sizeof (pkg_length);
345   memcpy (packet_ptr + offset, &pkg_num_values, sizeof (pkg_num_values));
346   offset += sizeof (pkg_num_values);
347   memcpy (packet_ptr + offset, pkg_values_types, sizeof (pkg_values_types));
348   offset += sizeof (pkg_values_types);
349   memcpy (packet_ptr + offset, pkg_values, sizeof (pkg_values));
350   offset += sizeof (pkg_values);
351
352   assert (offset == packet_len);
353
354   *ret_buffer = packet_ptr + packet_len;
355   *ret_buffer_len -= packet_len;
356   return (0);
357 } /* }}} int nb_add_values */
358
359 static int nb_add_number (char **ret_buffer, /* {{{ */
360     size_t *ret_buffer_len,
361     uint16_t type, uint64_t value)
362 {
363   char *packet_ptr;
364   size_t packet_len;
365
366   uint16_t pkg_type;
367   uint16_t pkg_length;
368   uint64_t pkg_value;
369
370   size_t offset;
371
372   packet_len = sizeof (pkg_type)
373     + sizeof (pkg_length)
374     + sizeof (pkg_value);
375
376   if (*ret_buffer_len < packet_len)
377     return (ENOMEM);
378
379   pkg_type = htons (type);
380   pkg_length = htons ((uint16_t) packet_len);
381   pkg_value = htonll (value);
382
383   packet_ptr = *ret_buffer;
384   offset = 0;
385   memcpy (packet_ptr + offset, &pkg_type, sizeof (pkg_type));
386   offset += sizeof (pkg_type);
387   memcpy (packet_ptr + offset, &pkg_length, sizeof (pkg_length));
388   offset += sizeof (pkg_length);
389   memcpy (packet_ptr + offset, &pkg_value, sizeof (pkg_value));
390   offset += sizeof (pkg_value);
391
392   assert (offset == packet_len);
393
394   *ret_buffer = packet_ptr + packet_len;
395   *ret_buffer_len -= packet_len;
396   return (0);
397 } /* }}} int nb_add_number */
398
399 static int nb_add_time (char **ret_buffer, /* {{{ */
400     size_t *ret_buffer_len,
401     uint16_t type, double value)
402 {
403   /* Convert to collectd's "cdtime" representation. */
404   uint64_t cdtime_value = (uint64_t) (value * 1073741824.0);
405   return (nb_add_number (ret_buffer, ret_buffer_len, type, cdtime_value));
406 } /* }}} int nb_add_time */
407
408 static int nb_add_string (char **ret_buffer, /* {{{ */
409     size_t *ret_buffer_len,
410     uint16_t type, const char *str, size_t str_len)
411 {
412   char *packet_ptr;
413   size_t packet_len;
414
415   uint16_t pkg_type;
416   uint16_t pkg_length;
417
418   size_t offset;
419
420   packet_len = sizeof (pkg_type)
421     + sizeof (pkg_length)
422     + str_len + 1;
423   if (*ret_buffer_len < packet_len)
424     return (ENOMEM);
425
426   pkg_type = htons (type);
427   pkg_length = htons ((uint16_t) packet_len);
428
429   packet_ptr = *ret_buffer;
430   offset = 0;
431   memcpy (packet_ptr + offset, &pkg_type, sizeof (pkg_type));
432   offset += sizeof (pkg_type);
433   memcpy (packet_ptr + offset, &pkg_length, sizeof (pkg_length));
434   offset += sizeof (pkg_length);
435   memcpy (packet_ptr + offset, str, str_len);
436   offset += str_len;
437   memset (packet_ptr + offset, 0, 1);
438   offset += 1;
439
440   assert (offset == packet_len);
441
442   *ret_buffer = packet_ptr + packet_len;
443   *ret_buffer_len -= packet_len;
444   return (0);
445 } /* }}} int nb_add_string */
446
447 static int nb_add_value_list (lcc_network_buffer_t *nb, /* {{{ */
448     const lcc_value_list_t *vl)
449 {
450   char *buffer = nb->ptr;
451   size_t buffer_size = nb->free;
452
453   const lcc_identifier_t *ident_src;
454   lcc_identifier_t *ident_dst;
455
456   ident_src = &vl->identifier;
457   ident_dst = &nb->state.identifier;
458
459   if (strcmp (ident_dst->host, ident_src->host) != 0)
460   {
461     if (nb_add_string (&buffer, &buffer_size, TYPE_HOST,
462           ident_src->host, strlen (ident_src->host)) != 0)
463       return (-1);
464     SSTRNCPY (ident_dst->host, ident_src->host, sizeof (ident_dst->host));
465   }
466
467   if (strcmp (ident_dst->plugin, ident_src->plugin) != 0)
468   {
469     if (nb_add_string (&buffer, &buffer_size, TYPE_PLUGIN,
470           ident_src->plugin, strlen (ident_src->plugin)) != 0)
471       return (-1);
472     SSTRNCPY (ident_dst->plugin, ident_src->plugin,
473         sizeof (ident_dst->plugin));
474   }
475
476   if (strcmp (ident_dst->plugin_instance,
477         ident_src->plugin_instance) != 0)
478   {
479     if (nb_add_string (&buffer, &buffer_size, TYPE_PLUGIN_INSTANCE,
480           ident_src->plugin_instance,
481           strlen (ident_src->plugin_instance)) != 0)
482       return (-1);
483     SSTRNCPY (ident_dst->plugin_instance, ident_src->plugin_instance,
484         sizeof (ident_dst->plugin_instance));
485   }
486
487   if (strcmp (ident_dst->type, ident_src->type) != 0)
488   {
489     if (nb_add_string (&buffer, &buffer_size, TYPE_TYPE,
490           ident_src->type, strlen (ident_src->type)) != 0)
491       return (-1);
492     SSTRNCPY (ident_dst->type, ident_src->type, sizeof (ident_dst->type));
493   }
494
495   if (strcmp (ident_dst->type_instance,
496         ident_src->type_instance) != 0)
497   {
498     if (nb_add_string (&buffer, &buffer_size, TYPE_TYPE_INSTANCE,
499           ident_src->type_instance,
500           strlen (ident_src->type_instance)) != 0)
501       return (-1);
502     SSTRNCPY (ident_dst->type_instance, ident_src->type_instance,
503         sizeof (ident_dst->type_instance));
504   }
505
506   if (nb->state.time != vl->time)
507   {
508     if (nb_add_time (&buffer, &buffer_size, TYPE_TIME_HR, vl->time))
509       return (-1);
510     nb->state.time = vl->time;
511   }
512
513   if (nb->state.interval != vl->interval)
514   {
515     if (nb_add_time (&buffer, &buffer_size, TYPE_INTERVAL_HR, vl->interval))
516       return (-1);
517     nb->state.interval = vl->interval;
518   }
519
520   if (nb_add_values (&buffer, &buffer_size, vl) != 0)
521     return (-1);
522
523   nb->ptr = buffer;
524   nb->free = buffer_size;
525   return (0);
526 } /* }}} int nb_add_value_list */
527
528 #if WIN32
529 static int nb_add_signature (lcc_network_buffer_t *nb) /* {{{ */
530 {
531   BYTE *buffer;
532   DWORD buffer_size;
533
534   BYTE hash[32] = { 0 };
535   DWORD hash_size = sizeof (hash) / sizeof (hash[0]);
536
537   HCRYPTHASH hHash;
538   HCRYPTKEY  hKey;
539   BOOL status;
540
541   /* The type, length and username have already been filled in by
542    * "lcc_network_buffer_initialize". All we do here is calculate the hash over
543    * the username and the data and add the hash value to the buffer. */
544   buffer = (LPBYTE) (nb->buffer + PART_SIGNATURE_SHA256_SIZE);
545   assert (nb->size >= (nb->free + PART_SIGNATURE_SHA256_SIZE));
546   buffer_size = (DWORD) (nb->size - (nb->free + PART_SIGNATURE_SHA256_SIZE));
547
548   if (!nb->hProv)
549   {
550     status = CryptAcquireContext (&nb->hProv,
551         /* szContainer   = */ NULL,
552         /* CSP name      = */ NULL,
553         /* provider type = */ PROV_RSA_AES,
554         /* flags         = */ CRYPT_VERIFYCONTEXT);
555     if (!status)
556       return (-1);
557   }
558
559   status = CreateHMAC (nb->hProv,
560       /* algorithm = */ CALG_SHA_256,
561       /* lpbKey    = */ (LPBYTE) nb->password,
562       /* dwKeySize = */ (DWORD) strlen (nb->password),
563       /* dwFlags   = */ 0,
564       /* lphHash   = */ &hHash,
565       /* lphKey    = */ &hKey);
566   if (!status)
567     return (-1);
568
569   status = CryptHashData (hHash,
570       /* pbData     = */ buffer,
571       /* dwDataSize = */ buffer_size,
572       /* dwFlags    = */ 0);
573   if (!status)
574   {
575     CryptDestroyHash (hHash);
576     CryptDestroyKey (hKey);
577     return (-1);
578   }
579
580   status = CryptGetHashParam (hHash,
581       /* dwParam    = */ HP_HASHVAL,
582       /* pbData     = */ hash,
583       /* pwdDataLen = */ &hash_size,
584       /* dwFlags    = */ 0);
585   if (!status)
586   {
587     CryptDestroyHash (hHash);
588     CryptDestroyKey (hKey);
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   return (0);
598 } /* }}} int nb_add_signature */
599
600 static int nb_key_from_password (HCRYPTPROV hProv, /* {{{ */
601     LPCSTR pbPassword, LPBYTE pbIv, HCRYPTKEY *phKey)
602 {
603   HCRYPTHASH hHash = 0;
604   HCRYPTKEY  hKey = 0;
605
606   struct
607   {
608     PUBLICKEYSTRUC pks;
609     DWORD dwKeySize;
610     BYTE pbKey[32];
611   } keyData;
612   DWORD cypherMode = CRYPT_MODE_OFB;
613
614   BOOL status;
615
616   memset (&keyData, 0, sizeof (keyData));
617   keyData.pks.bType = PLAINTEXTKEYBLOB;
618   keyData.pks.bVersion = CUR_BLOB_VERSION;
619   keyData.pks.reserved = 0;
620   keyData.dwKeySize = 32;
621
622   status = CryptCreateHash (hProv,
623       /* algorithm  = */ CALG_SHA_256,
624       /* hKey       = */ 0,
625       /* dwFlags    = */ 0,
626       /* out phHash = */ &hHash);
627   if (!status)
628     return (-1);
629
630   status = CryptHashData (hHash,
631       /* pbData     = */ (void *) pbPassword,
632       /* dwDataSize = */ strlen (pbPassword),
633       /* dwFlags    = */ 0);
634   if (!status)
635   {
636     CryptDestroyHash (hHash);
637     return (-1);
638   }
639
640   status = CryptGetHashParam (hHash,
641       /* dwParam    = */ HP_HASHVAL,
642       /* pbData     = */ keyData.pbKey,
643       /* pwdDataLen = */ &keyData.dwKeySize,
644       /* dwFlags    = */ 0);
645   if (!status)
646   {
647     CryptDestroyHash (hHash);
648     return (-1);
649   }
650   CryptDestroyHash (hHash);
651   assert (keyData.dwKeySize == 32);
652
653   status = CryptImportKey (hProv,
654       /* pbData    = */ (void *) &keyData,
655       /* dwDataLen = */ sizeof (keyData),
656       /* hPubKey   = */ 0,
657       /* dwFlags   = */ 0,
658       /* phKey     = */ &hKey);
659   if (!status)
660     return (-1);
661
662   status = CryptSetKeyParam (hKey,
663       /* dwParam = */ KP_MODE,
664       /* pbData  = */ (void *) &cypherMode,
665       /* dwFlags = */ 0);
666   if (!status)
667   {
668     CryptDestroyKey (hKey);
669     return (-1);
670   }
671
672   status = CryptSetKeyParam (hKey,
673       /* dwParam = */ KP_IV,
674       /* pbData  = */ (void *) pbIv,
675       /* dwFlags = */ 0);
676   if (!status)
677   {
678     CryptDestroyKey (hKey);
679     return (-1);
680   }
681
682   *phKey = hKey;
683   return (0);
684 } /* }}} nb_key_from_password */
685
686 static int nb_add_encryption (lcc_network_buffer_t *nb) /* {{{ */
687 {
688   HCRYPTHASH hHash = 0;
689   HCRYPTKEY  hKey = 0;
690   DWORD package_length;
691   BYTE *encr_ptr; /* pointer to data being encrypted */
692   DWORD encr_size;
693
694   BYTE *hash_data_ptr; /* pointer to data being hashed */
695   DWORD hash_data_size;
696   DWORD hash_code_size = 20;
697
698   WORD pkg_length;
699   BOOL status;
700
701   /* Fill in the package length */
702   package_length = nb->size - nb->free;
703   pkg_length = htons ((uint16_t) package_length);
704   memcpy (nb->buffer + 2, &pkg_length, sizeof (pkg_length));
705
706   /* Calculate what to hash */
707   hash_data_ptr = (BYTE *) (nb->buffer + PART_ENCRYPTION_AES256_SIZE);
708   hash_data_size = package_length - nb->encr_header_len;
709
710   /* Calculate what to encrypt */
711   encr_ptr = hash_data_ptr - hash_code_size;
712   encr_size = hash_data_size + hash_code_size;
713
714   if (!nb->hProv)
715   {
716     status = CryptAcquireContext (&nb->hProv,
717         /* szContainer   = */ NULL,
718         /* CSP name      = */ NULL,
719         /* provider type = */ PROV_RSA_AES,
720         /* flags         = */ CRYPT_VERIFYCONTEXT);
721     if (!status)
722       return (-1);
723   }
724
725   status = CryptCreateHash (nb->hProv,
726       /* algorithm  = */ CALG_SHA1,
727       /* hKey       = */ 0,
728       /* dwFlags    = */ 0,
729       /* out phHash = */ &hHash);
730   if (!status)
731     return (-1);
732
733   status = CryptHashData (hHash,
734       /* pbData     = */ hash_data_ptr,
735       /* dwDataSize = */ hash_data_size,
736       /* dwFlags    = */ 0);
737   if (!status)
738   {
739     CryptDestroyHash (hHash);
740     return (-1);
741   }
742
743   status = CryptGetHashParam (hHash,
744       /* dwParam    = */ HP_HASHVAL,
745       /* pbData     = */ (void *) encr_ptr,
746       /* pwdDataLen = */ &hash_code_size,
747       /* dwFlags    = */ 0);
748   if (!status)
749   {
750     CryptDestroyHash (hHash);
751     return (-1);
752   }
753   CryptDestroyHash (hHash);
754
755   status = nb_key_from_password (nb->hProv,
756       nb->password, nb->encr_iv, &hKey);
757   if (!status)
758     return (-1);
759
760   status = CryptEncrypt (hKey,
761       /* hHash      = */ 0,
762       /* Final      = */ 1, /* last call to CryptEncrypt() */
763       /* dwFlags    = */ 0,
764       /* pbData     = */ encr_ptr,
765       /* pdwDataLen = */ &encr_size,
766       /* dwBufLen   = */ encr_size);
767   if (!status)
768   {
769     CryptDestroyKey (hKey);
770     return (-1);
771   }
772
773   CryptDestroyKey (hKey);
774   return (0);
775 } /* }}} int nb_add_encryption */
776 #elif HAVE_LIBGCRYPT
777 static int nb_add_signature (lcc_network_buffer_t *nb) /* {{{ */
778 {
779   char *buffer;
780   size_t buffer_size;
781
782   gcry_md_hd_t hd;
783   gcry_error_t err;
784   unsigned char *hash;
785   const size_t hash_length = 32;
786
787   /* The type, length and username have already been filled in by
788    * "lcc_network_buffer_initialize". All we do here is calculate the hash over
789    * the username and the data and add the hash value to the buffer. */
790
791   buffer = nb->buffer + PART_SIGNATURE_SHA256_SIZE;
792   assert (nb->size >= (nb->free + PART_SIGNATURE_SHA256_SIZE));
793   buffer_size = nb->size - (nb->free + PART_SIGNATURE_SHA256_SIZE);
794
795   hd = NULL;
796   err = gcry_md_open (&hd, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC);
797   if (err != 0)
798     return (-1);
799
800   assert (nb->password != NULL);
801   err = gcry_md_setkey (hd, nb->password, strlen (nb->password));
802   if (err != 0)
803   {
804     gcry_md_close (hd);
805     return (-1);
806   }
807
808   gcry_md_write (hd, buffer, buffer_size);
809   hash = gcry_md_read (hd, GCRY_MD_SHA256);
810   if (hash == NULL)
811   {
812     gcry_md_close (hd);
813     return (-1);
814   }
815
816   assert (((2 * sizeof (uint16_t)) + hash_length) == PART_SIGNATURE_SHA256_SIZE);
817   memcpy (nb->buffer + (2 * sizeof (uint16_t)), hash, hash_length);
818
819   gcry_md_close (hd);
820   return (0);
821 } /* }}} int nb_add_signature */
822
823 static int nb_add_encryption (lcc_network_buffer_t *nb) /* {{{ */
824 {
825   size_t package_length;
826   char *encr_ptr; /* pointer to data being encrypted */
827   size_t encr_size;
828
829   char *hash_ptr; /* pointer to data being hashed */
830   size_t hash_size;
831   char hash[20];
832
833   uint16_t pkg_length;
834   gcry_error_t err;
835
836   /* Fill in the package length */
837   package_length = nb->size - nb->free;
838   pkg_length = htons ((uint16_t) package_length);
839   memcpy (nb->buffer + 2, &pkg_length, sizeof (pkg_length));
840
841   /* Calculate what to hash */
842   hash_ptr = nb->buffer + PART_ENCRYPTION_AES256_SIZE;
843   hash_size = package_length - nb->encr_header_len;
844
845   /* Calculate what to encrypt */
846   encr_ptr = hash_ptr - sizeof (hash);
847   encr_size = hash_size + sizeof (hash);
848
849   /* Calculate the SHA-1 hash */
850   gcry_md_hash_buffer (GCRY_MD_SHA1, hash, hash_ptr, hash_size);
851   memcpy (encr_ptr, hash, sizeof (hash));
852
853   if (nb->encr_cypher == NULL)
854   {
855     unsigned char password_hash[32];
856
857     err = gcry_cipher_open (&nb->encr_cypher,
858         GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_OFB, /* flags = */ 0);
859     if (err != 0)
860       return (-1);
861
862     /* Calculate our 256bit key used for AES */
863     gcry_md_hash_buffer (GCRY_MD_SHA256, password_hash,
864         nb->password, strlen (nb->password));
865
866     err = gcry_cipher_setkey (nb->encr_cypher,
867         password_hash, sizeof (password_hash));
868     if (err != 0)
869     {
870       gcry_cipher_close (nb->encr_cypher);
871       nb->encr_cypher = NULL;
872       return (-1);
873     }
874   }
875   else /* if (nb->encr_cypher != NULL) */
876   {
877     gcry_cipher_reset (nb->encr_cypher);
878   }
879
880   /* Set the initialization vector */
881   err = gcry_cipher_setiv (nb->encr_cypher,
882       nb->encr_iv, sizeof (nb->encr_iv));
883   if (err != 0)
884   {
885     gcry_cipher_close (nb->encr_cypher);
886     nb->encr_cypher = NULL;
887     return (-1);
888   }
889
890   /* Encrypt the buffer in-place */
891   err = gcry_cipher_encrypt (nb->encr_cypher,
892       encr_ptr, encr_size,
893       /* in = */ NULL, /* in len = */ 0);
894   if (err != 0)
895   {
896     gcry_cipher_close (nb->encr_cypher);
897     nb->encr_cypher = NULL;
898     return (-1);
899   }
900
901   return (0);
902 } /* }}} int nb_add_encryption */
903 #endif
904
905 /*
906  * Public functions
907  */
908 lcc_network_buffer_t *lcc_network_buffer_create (size_t size) /* {{{ */
909 {
910   lcc_network_buffer_t *nb;
911
912   if (size == 0)
913     size = LCC_NETWORK_BUFFER_SIZE_DEFAULT;
914
915   if (size < 128)
916   {
917     errno = EINVAL;
918     return (NULL);
919   }
920
921   nb = malloc (sizeof (*nb));
922   if (nb == NULL)
923     return (NULL);
924   memset (nb, 0, sizeof (*nb));
925
926   nb->size = size;
927   nb->buffer = malloc (nb->size);
928   if (nb->buffer == NULL)
929   {
930     free (nb);
931     return (NULL);
932   }
933   memset (nb->buffer, 0, nb->size);
934
935   nb->ptr = nb->buffer;
936   nb->free = nb->size;
937
938   nb->seclevel = NONE;
939   nb->username = NULL;
940   nb->password = NULL;
941
942   return (nb);
943 } /* }}} lcc_network_buffer_t *lcc_network_buffer_create */
944
945 void lcc_network_buffer_destroy (lcc_network_buffer_t *nb) /* {{{ */
946 {
947   if (nb == NULL)
948     return;
949
950   free (nb->buffer);
951   free (nb);
952 } /* }}} void lcc_network_buffer_destroy */
953
954 int lcc_network_buffer_set_security_level (lcc_network_buffer_t *nb, /* {{{ */
955     lcc_security_level_t level,
956     const char *username, const char *password)
957 {
958   char *username_copy;
959   char *password_copy;
960
961   if (level == NONE)
962   {
963     free (nb->username);
964     free (nb->password);
965     nb->username = NULL;
966     nb->password = NULL;
967     nb->seclevel = NONE;
968     lcc_network_buffer_initialize (nb);
969     return (0);
970   }
971
972   if (!have_gcrypt ())
973     return (ENOTSUP);
974
975   username_copy = strdup (username);
976   password_copy = strdup (password);
977   if ((username_copy == NULL) || (password_copy == NULL))
978   {
979     free (username_copy);
980     free (password_copy);
981     return (ENOMEM);
982   }
983
984   free (nb->username);
985   free (nb->password);
986   nb->username = username_copy;
987   nb->password = password_copy;
988   nb->seclevel = level;
989
990   lcc_network_buffer_initialize (nb);
991   return (0);
992 } /* }}} int lcc_network_buffer_set_security_level */
993
994 int lcc_network_buffer_initialize (lcc_network_buffer_t *nb) /* {{{ */
995 {
996   if (nb == NULL)
997     return (EINVAL);
998
999   memset (nb->buffer, 0, nb->size);
1000   memset (&nb->state, 0, sizeof (nb->state));
1001   nb->ptr = nb->buffer;
1002   nb->free = nb->size;
1003
1004 #if HAVE_LIBGCRYPT
1005   if (nb->seclevel == SIGN)
1006   {
1007     size_t username_len;
1008     uint16_t pkg_type = htons (TYPE_SIGN_SHA256);
1009     uint16_t pkg_length = PART_SIGNATURE_SHA256_SIZE;
1010
1011     assert (nb->username != NULL);
1012     username_len = strlen (nb->username);
1013     pkg_length = htons (pkg_length + ((uint16_t) username_len));
1014
1015     /* Fill in everything but the hash value here. */
1016     memcpy (nb->ptr, &pkg_type, sizeof (pkg_type));
1017     memcpy (nb->ptr + sizeof (pkg_type), &pkg_length, sizeof (pkg_length));
1018     nb->ptr += PART_SIGNATURE_SHA256_SIZE;
1019     nb->free -= PART_SIGNATURE_SHA256_SIZE;
1020
1021     memcpy (nb->ptr, nb->username, username_len);
1022     nb->ptr += username_len;
1023     nb->free -= username_len;
1024   }
1025   else if (nb->seclevel == ENCRYPT)
1026   {
1027     size_t username_length = strlen (nb->username);
1028     uint16_t pkg_type = htons (TYPE_ENCR_AES256);
1029     uint16_t pkg_length = 0; /* Filled in in finalize. */
1030     uint16_t pkg_user_len = htons ((uint16_t) username_length);
1031     char hash[20];
1032
1033     nb->encr_header_len = username_length;
1034     nb->encr_header_len += PART_ENCRYPTION_AES256_SIZE;
1035
1036     gcry_randomize ((void *) &nb->encr_iv, sizeof (nb->encr_iv),
1037         GCRY_STRONG_RANDOM);
1038
1039     /* Filled in in finalize. */
1040     memset (hash, 0, sizeof (hash));
1041
1042     ADD_STATIC (nb, pkg_type);
1043     ADD_STATIC (nb, pkg_length);
1044     ADD_STATIC (nb, pkg_user_len);
1045     ADD_GENERIC (nb, nb->username, username_length);
1046     ADD_GENERIC (nb, nb->encr_iv, sizeof (nb->encr_iv));
1047     ADD_GENERIC (nb, hash, sizeof (hash));
1048     assert ((nb->encr_header_len + nb->free) == nb->size);
1049   }
1050 #endif
1051
1052   return (0);
1053 } /* }}} int lcc_network_buffer_initialize */
1054
1055 int lcc_network_buffer_finalize (lcc_network_buffer_t *nb) /* {{{ */
1056 {
1057   if (nb == NULL)
1058     return (EINVAL);
1059
1060 #if WIN32 || HAVE_LIBGCRYPT
1061   if (nb->seclevel == SIGN)
1062     nb_add_signature (nb);
1063   else if (nb->seclevel == ENCRYPT)
1064     nb_add_encryption (nb);
1065 #endif
1066
1067   return (0);
1068 } /* }}} int lcc_network_buffer_finalize */
1069
1070 int lcc_network_buffer_add_value (lcc_network_buffer_t *nb, /* {{{ */
1071     const lcc_value_list_t *vl)
1072 {
1073   int status;
1074
1075   if ((nb == NULL) || (vl == NULL))
1076     return (EINVAL);
1077
1078   status = nb_add_value_list (nb, vl);
1079   return (status);
1080 } /* }}} int lcc_network_buffer_add_value */
1081
1082 int lcc_network_buffer_get (lcc_network_buffer_t *nb, /* {{{ */
1083     void *buffer, size_t *buffer_size)
1084 {
1085   size_t sz_required;
1086   size_t sz_available;
1087
1088   if ((nb == NULL) || (buffer_size == NULL))
1089     return (EINVAL);
1090
1091   assert (nb->size >= nb->free);
1092   sz_required = nb->size - nb->free;
1093   sz_available = *buffer_size;
1094
1095   *buffer_size = sz_required;
1096   if (buffer != NULL)
1097     memcpy (buffer, nb->buffer,
1098         (sz_available < sz_required) ? sz_available : sz_required);
1099
1100   return (0);
1101 } /* }}} int lcc_network_buffer_get */
1102
1103 /* vim: set sw=2 sts=2 et fdm=marker : */