libcollectdclient: Add encryption support for Windows.
authorFlorian Forster <octo@ssc-serv.com>
Tue, 28 Jan 2014 20:49:40 +0000 (21:49 +0100)
committerFlorian Forster <octo@collectd.org>
Tue, 28 Jan 2014 20:49:40 +0000 (21:49 +0100)
src/libcollectdclient/network_buffer.c

index 44a58c8..83a3ba8 100644 (file)
@@ -125,7 +125,11 @@ struct lcc_network_buffer_s
   char *username;
   char *password;
 
-#if HAVE_LIBGCRYPT
+#if WIN32
+  HCRYPTPROV hProv;
+  size_t encr_header_len;
+  BYTE encr_iv[16];
+#elif HAVE_LIBGCRYPT
   gcry_cipher_hd_t encr_cypher;
   size_t encr_header_len;
   char encr_iv[16];
@@ -521,7 +525,6 @@ static int nb_add_value_list (lcc_network_buffer_t *nb, /* {{{ */
   return (0);
 } /* }}} int nb_add_value_list */
 
-/* TODO: Add encryption for Windows */
 #if WIN32
 static int nb_add_signature (lcc_network_buffer_t *nb) /* {{{ */
 {
@@ -531,7 +534,6 @@ static int nb_add_signature (lcc_network_buffer_t *nb) /* {{{ */
   BYTE hash[32] = { 0 };
   DWORD hash_size = sizeof (hash) / sizeof (hash[0]);
 
-  HCRYPTPROV hProv;
   HCRYPTHASH hHash;
   HCRYPTKEY  hKey;
   BOOL status;
@@ -543,15 +545,18 @@ static int nb_add_signature (lcc_network_buffer_t *nb) /* {{{ */
   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);
+  if (!nb->hProv)
+  {
+    status = CryptAcquireContext (&nb->hProv,
+        /* szContainer   = */ NULL,
+        /* CSP name      = */ NULL,
+        /* provider type = */ PROV_RSA_AES,
+        /* flags         = */ CRYPT_VERIFYCONTEXT);
+    if (!status)
+      return (-1);
+  }
 
-  status = CreateHMAC (hProv,
+  status = CreateHMAC (nb->hProv,
       /* algorithm = */ CALG_SHA_256,
       /* lpbKey    = */ (LPBYTE) nb->password,
       /* dwKeySize = */ (DWORD) strlen (nb->password),
@@ -559,10 +564,7 @@ static int nb_add_signature (lcc_network_buffer_t *nb) /* {{{ */
       /* lphHash   = */ &hHash,
       /* lphKey    = */ &hKey);
   if (!status)
-  {
-    CryptReleaseContext (hProv, /* dwFlags = */ 0);
     return (-1);
-  }
 
   status = CryptHashData (hHash,
       /* pbData     = */ buffer,
@@ -572,7 +574,6 @@ static int nb_add_signature (lcc_network_buffer_t *nb) /* {{{ */
   {
     CryptDestroyHash (hHash);
     CryptDestroyKey (hKey);
-    CryptReleaseContext (hProv, /* dwFlags = */ 0);
     return (-1);
   }
 
@@ -585,7 +586,6 @@ static int nb_add_signature (lcc_network_buffer_t *nb) /* {{{ */
   {
     CryptDestroyHash (hHash);
     CryptDestroyKey (hKey);
-    CryptReleaseContext (hProv, /* dwFlags = */ 0);
     return (-1);
   }
 
@@ -594,9 +594,185 @@ static int nb_add_signature (lcc_network_buffer_t *nb) /* {{{ */
 
   CryptDestroyHash (hHash);
   CryptDestroyKey (hKey);
-  CryptReleaseContext (hProv, /* dwFlags = */ 0);
   return (0);
 } /* }}} int nb_add_signature */
+
+static int nb_key_from_password (HCRYPTPROV hProv, /* {{{ */
+    LPCSTR pbPassword, LPBYTE pbIv, HCRYPTKEY *phKey)
+{
+  HCRYPTHASH hHash = 0;
+  HCRYPTKEY  hKey = 0;
+
+  struct
+  {
+    PUBLICKEYSTRUC pks;
+    DWORD dwKeySize;
+    BYTE pbKey[32];
+  } keyData;
+  DWORD cypherMode = CRYPT_MODE_OFB;
+
+  BOOL status;
+
+  memset (&keyData, 0, sizeof (keyData));
+  keyData.pks.bType = PLAINTEXTKEYBLOB;
+  keyData.pks.bVersion = CUR_BLOB_VERSION;
+  keyData.pks.reserved = 0;
+  keyData.dwKeySize = 32;
+
+  status = CryptCreateHash (hProv,
+      /* algorithm  = */ CALG_SHA_256,
+      /* hKey       = */ 0,
+      /* dwFlags    = */ 0,
+      /* out phHash = */ &hHash);
+  if (!status)
+    return (-1);
+
+  status = CryptHashData (hHash,
+      /* pbData     = */ (void *) pbPassword,
+      /* dwDataSize = */ strlen (pbPassword),
+      /* dwFlags    = */ 0);
+  if (!status)
+  {
+    CryptDestroyHash (hHash);
+    return (-1);
+  }
+
+  status = CryptGetHashParam (hHash,
+      /* dwParam    = */ HP_HASHVAL,
+      /* pbData     = */ keyData.pbKey,
+      /* pwdDataLen = */ &keyData.dwKeySize,
+      /* dwFlags    = */ 0);
+  if (!status)
+  {
+    CryptDestroyHash (hHash);
+    return (-1);
+  }
+  CryptDestroyHash (hHash);
+  assert (keyData.dwKeySize == 32);
+
+  status = CryptImportKey (hProv,
+      /* pbData    = */ (void *) &keyData,
+      /* dwDataLen = */ sizeof (keyData),
+      /* hPubKey   = */ 0,
+      /* dwFlags   = */ 0,
+      /* phKey     = */ &hKey);
+  if (!status)
+    return (-1);
+
+  status = CryptSetKeyParam (hKey,
+      /* dwParam = */ KP_MODE,
+      /* pbData  = */ (void *) &cypherMode,
+      /* dwFlags = */ 0);
+  if (!status)
+  {
+    CryptDestroyKey (hKey);
+    return (-1);
+  }
+
+  status = CryptSetKeyParam (hKey,
+      /* dwParam = */ KP_IV,
+      /* pbData  = */ (void *) pbIv,
+      /* dwFlags = */ 0);
+  if (!status)
+  {
+    CryptDestroyKey (hKey);
+    return (-1);
+  }
+
+  *phKey = hKey;
+  return (0);
+} /* }}} nb_key_from_password */
+
+static int nb_add_encryption (lcc_network_buffer_t *nb) /* {{{ */
+{
+  HCRYPTHASH hHash = 0;
+  HCRYPTKEY  hKey = 0;
+  DWORD package_length;
+  BYTE *encr_ptr; /* pointer to data being encrypted */
+  DWORD encr_size;
+
+  BYTE *hash_data_ptr; /* pointer to data being hashed */
+  DWORD hash_data_size;
+  DWORD hash_code_size = 20;
+
+  WORD pkg_length;
+  BOOL status;
+
+  /* Fill in the package length */
+  package_length = nb->size - nb->free;
+  pkg_length = htons ((uint16_t) package_length);
+  memcpy (nb->buffer + 2, &pkg_length, sizeof (pkg_length));
+
+  /* Calculate what to hash */
+  hash_data_ptr = (BYTE *) (nb->buffer + PART_ENCRYPTION_AES256_SIZE);
+  hash_data_size = package_length - nb->encr_header_len;
+
+  /* Calculate what to encrypt */
+  encr_ptr = hash_data_ptr - hash_code_size;
+  encr_size = hash_data_size + hash_code_size;
+
+  if (!nb->hProv)
+  {
+    status = CryptAcquireContext (&nb->hProv,
+        /* szContainer   = */ NULL,
+        /* CSP name      = */ NULL,
+        /* provider type = */ PROV_RSA_AES,
+        /* flags         = */ CRYPT_VERIFYCONTEXT);
+    if (!status)
+      return (-1);
+  }
+
+  status = CryptCreateHash (nb->hProv,
+      /* algorithm  = */ CALG_SHA1,
+      /* hKey       = */ 0,
+      /* dwFlags    = */ 0,
+      /* out phHash = */ &hHash);
+  if (!status)
+    return (-1);
+
+  status = CryptHashData (hHash,
+      /* pbData     = */ hash_data_ptr,
+      /* dwDataSize = */ hash_data_size,
+      /* dwFlags    = */ 0);
+  if (!status)
+  {
+    CryptDestroyHash (hHash);
+    return (-1);
+  }
+
+  status = CryptGetHashParam (hHash,
+      /* dwParam    = */ HP_HASHVAL,
+      /* pbData     = */ (void *) encr_ptr,
+      /* pwdDataLen = */ &hash_code_size,
+      /* dwFlags    = */ 0);
+  if (!status)
+  {
+    CryptDestroyHash (hHash);
+    return (-1);
+  }
+  CryptDestroyHash (hHash);
+
+  status = nb_key_from_password (nb->hProv,
+      nb->password, nb->encr_iv, &hKey);
+  if (!status)
+    return (-1);
+
+  status = CryptEncrypt (hKey,
+      /* hHash      = */ 0,
+      /* Final      = */ 1, /* last call to CryptEncrypt() */
+      /* dwFlags    = */ 0,
+      /* pbData     = */ encr_ptr,
+      /* pdwDataLen = */ &encr_size,
+      /* dwBufLen   = */ encr_size);
+  if (!status)
+  {
+    CryptDestroyKey (hKey);
+    return (-1);
+  }
+
+  CryptDestroyKey (hKey);
+  return (0);
+} /* }}} int nb_add_encryption */
 #elif HAVE_LIBGCRYPT
 static int nb_add_signature (lcc_network_buffer_t *nb) /* {{{ */
 {
@@ -881,10 +1057,7 @@ int lcc_network_buffer_finalize (lcc_network_buffer_t *nb) /* {{{ */
   if (nb == NULL)
     return (EINVAL);
 
-#if WIN32
-  if (nb->seclevel == SIGN)
-    nb_add_signature (nb);
-#elif HAVE_LIBGCRYPT
+#if WIN32 || HAVE_LIBGCRYPT
   if (nb->seclevel == SIGN)
     nb_add_signature (nb);
   else if (nb->seclevel == ENCRYPT)