libcollectdclient: Add support for signing packets on Windows.
[collectd.git] / src / libcollectdclient / network_buffer.c
index acbe93f..44a58c8 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/libcollectdclient/network_buffer.c
- * Copyright (C) 2010-2012  Florian octo Forster
+ * Copyright (C) 2010-2014  Florian octo Forster
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
  *   Florian octo Forster <octo at collectd.org>
  **/
 
-#include "config.h"
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#if WIN32
+
+#include <windows.h>
+#include <math.h>
+#include <assert.h>
+
+#else
 
 #include <stdlib.h>
 #include <string.h>
 GCRY_THREAD_OPTION_PTHREAD_IMPL;
 #endif
 
+#endif /* !WIN32 */
+
 #include "collectd/network_buffer.h"
+#if WIN32
+# include "collectd/win_hmac.h"
+#endif
 
 #define TYPE_HOST            0x0000
 #define TYPE_TIME            0x0001
@@ -80,6 +95,10 @@ GCRY_THREAD_OPTION_PTHREAD_IMPL;
 #define PART_SIGNATURE_SHA256_SIZE 36
 #define PART_ENCRYPTION_AES256_SIZE 42
 
+#ifndef ENOTSUP
+# define ENOTSUP -1
+#endif
+
 #define ADD_GENERIC(nb,srcptr,size) do {         \
   assert ((size) <= (nb)->free);                 \
   memcpy ((nb)->ptr, (srcptr), (size));          \
@@ -146,6 +165,7 @@ static _Bool have_gcrypt (void) /* {{{ */
 #endif
 } /* }}} _Bool have_gcrypt */
 
+#ifndef HAVE_HTONLL
 static uint64_t htonll (uint64_t val) /* {{{ */
 {
   static int config = 0;
@@ -175,6 +195,7 @@ static uint64_t htonll (uint64_t val) /* {{{ */
 
   return ((((uint64_t) lo) << 32) | ((uint64_t) hi));
 } /* }}} uint64_t htonll */
+#endif
 
 static double htond (double val) /* {{{ */
 {
@@ -500,7 +521,83 @@ static int nb_add_value_list (lcc_network_buffer_t *nb, /* {{{ */
   return (0);
 } /* }}} int nb_add_value_list */
 
-#if HAVE_LIBGCRYPT
+/* TODO: Add encryption for Windows */
+#if WIN32
+static int nb_add_signature (lcc_network_buffer_t *nb) /* {{{ */
+{
+  BYTE *buffer;
+  DWORD buffer_size;
+
+  BYTE hash[32] = { 0 };
+  DWORD hash_size = sizeof (hash) / sizeof (hash[0]);
+
+  HCRYPTPROV hProv;
+  HCRYPTHASH hHash;
+  HCRYPTKEY  hKey;
+  BOOL status;
+
+  /* The type, length and username have already been filled in by
+   * "lcc_network_buffer_initialize". All we do here is calculate the hash over
+   * the username and the data and add the hash value to the buffer. */
+  buffer = (LPBYTE) (nb->buffer + PART_SIGNATURE_SHA256_SIZE);
+  assert (nb->size >= (nb->free + PART_SIGNATURE_SHA256_SIZE));
+  buffer_size = (DWORD) (nb->size - (nb->free + PART_SIGNATURE_SHA256_SIZE));
+
+  status = CryptAcquireContext (&hProv,
+      /* szContainer   = */ NULL,
+      /* CSP name      = */ NULL,
+      /* provider type = */ PROV_RSA_AES,
+      /* flags         = */ CRYPT_VERIFYCONTEXT);
+  if (!status)
+    return (-1);
+
+  status = CreateHMAC (hProv,
+      /* algorithm = */ CALG_SHA_256,
+      /* lpbKey    = */ (LPBYTE) nb->password,
+      /* dwKeySize = */ (DWORD) strlen (nb->password),
+      /* dwFlags   = */ 0,
+      /* lphHash   = */ &hHash,
+      /* lphKey    = */ &hKey);
+  if (!status)
+  {
+    CryptReleaseContext (hProv, /* dwFlags = */ 0);
+    return (-1);
+  }
+
+  status = CryptHashData (hHash,
+      /* pbData     = */ buffer,
+      /* dwDataSize = */ buffer_size,
+      /* dwFlags    = */ 0);
+  if (!status)
+  {
+    CryptDestroyHash (hHash);
+    CryptDestroyKey (hKey);
+    CryptReleaseContext (hProv, /* dwFlags = */ 0);
+    return (-1);
+  }
+
+  status = CryptGetHashParam (hHash,
+      /* dwParam    = */ HP_HASHVAL,
+      /* pbData     = */ hash,
+      /* pwdDataLen = */ &hash_size,
+      /* dwFlags    = */ 0);
+  if (!status)
+  {
+    CryptDestroyHash (hHash);
+    CryptDestroyKey (hKey);
+    CryptReleaseContext (hProv, /* dwFlags = */ 0);
+    return (-1);
+  }
+
+  assert (((2 * sizeof (uint16_t)) + hash_size) == PART_SIGNATURE_SHA256_SIZE);
+  memcpy (nb->buffer + (2 * sizeof (uint16_t)), hash, hash_size);
+
+  CryptDestroyHash (hHash);
+  CryptDestroyKey (hKey);
+  CryptReleaseContext (hProv, /* dwFlags = */ 0);
+  return (0);
+} /* }}} int nb_add_signature */
+#elif HAVE_LIBGCRYPT
 static int nb_add_signature (lcc_network_buffer_t *nb) /* {{{ */
 {
   char *buffer;
@@ -784,7 +881,10 @@ int lcc_network_buffer_finalize (lcc_network_buffer_t *nb) /* {{{ */
   if (nb == NULL)
     return (EINVAL);
 
-#if HAVE_LIBGCRYPT
+#if WIN32
+  if (nb->seclevel == SIGN)
+    nb_add_signature (nb);
+#elif HAVE_LIBGCRYPT
   if (nb->seclevel == SIGN)
     nb_add_signature (nb);
   else if (nb->seclevel == ENCRYPT)