* Florian octo Forster <octo at verplant.org>
**/
+#include "config.h"
+
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <errno.h>
#include <arpa/inet.h> /* htons */
-#include "network_buffer.h"
+#include <pthread.h>
+
+#if HAVE_LIBGCRYPT
+#include <gcrypt.h>
+GCRY_THREAD_OPTION_PTHREAD_IMPL;
+#endif
+
+#include "collectd/network_buffer.h"
#define TYPE_HOST 0x0000
#define TYPE_TIME 0x0001
#define TYPE_SIGN_SHA256 0x0200
#define TYPE_ENCR_AES256 0x0210
+#define PART_SIGNATURE_SHA256_SIZE 36
+#define PART_ENCRYPTION_AES256_SIZE 42
+
+#define ADD_GENERIC(nb,srcptr,size) do { \
+ assert ((size) <= (nb)->free); \
+ memcpy ((nb)->ptr, (srcptr), (size)); \
+ (nb)->ptr += (size); \
+ (nb)->free -= (size); \
+} while (0)
+
+#define ADD_STATIC(nb,var) \
+ ADD_GENERIC(nb,&(var),sizeof(var));
+
/*
* Data types
*/
lcc_value_list_t state;
char *ptr;
size_t free;
+
+ lcc_security_level_t seclevel;
+ char *username;
+ char *password;
+
+ gcry_cipher_hd_t encr_cypher;
+ size_t encr_header_len;
+ char encr_iv[16];
};
#define SSTRNCPY(dst,src,sz) do { \
/*
* Private functions
*/
+static _Bool have_gcrypt (void) /* {{{ */
+{
+ static _Bool result = 0;
+ static _Bool need_init = 1;
+
+ if (!need_init)
+ return (result);
+ need_init = 0;
+
+ gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
+
+ if (!gcry_check_version (GCRYPT_VERSION))
+ return (0);
+
+ gcry_control (GCRYCTL_INIT_SECMEM, 32768, 0);
+ gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
+
+ result = 1;
+ return (1);
+} /* }}} _Bool have_gcrypt */
+
static uint64_t htonll (uint64_t val) /* {{{ */
{
static int config = 0;
return (0);
} /* }}} int nb_add_value_list */
+static int nb_add_signature (lcc_network_buffer_t *nb) /* {{{ */
+{
+ char *buffer;
+ size_t buffer_size;
+
+ gcry_md_hd_t hd;
+ gcry_error_t err;
+ unsigned char *hash;
+ const size_t hash_length = 32;
+
+ /* 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 = nb->buffer + PART_SIGNATURE_SHA256_SIZE;
+ assert (nb->size >= (nb->free + PART_SIGNATURE_SHA256_SIZE));
+ buffer_size = nb->size - (nb->free + PART_SIGNATURE_SHA256_SIZE);
+
+ hd = NULL;
+ err = gcry_md_open (&hd, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC);
+ if (err != 0)
+ return (-1);
+
+ assert (nb->password != NULL);
+ err = gcry_md_setkey (hd, nb->password, strlen (nb->password));
+ if (err != 0)
+ {
+ gcry_md_close (hd);
+ return (-1);
+ }
+
+ gcry_md_write (hd, buffer, buffer_size);
+ hash = gcry_md_read (hd, GCRY_MD_SHA256);
+ if (hash == NULL)
+ {
+ gcry_md_close (hd);
+ return (-1);
+ }
+
+ assert (((2 * sizeof (uint16_t)) + hash_length) == PART_SIGNATURE_SHA256_SIZE);
+ memcpy (nb->buffer + (2 * sizeof (uint16_t)), hash, hash_length);
+
+ gcry_md_close (hd);
+ return (0);
+} /* }}} int nb_add_signature */
+
+static int nb_add_encryption (lcc_network_buffer_t *nb) /* {{{ */
+{
+ size_t package_length;
+ char *encr_ptr; /* pointer to data being encrypted */
+ size_t encr_size;
+
+ char *hash_ptr; /* pointer to data being hashed */
+ size_t hash_size;
+ char hash[20];
+
+ uint16_t pkg_length;
+ gcry_error_t err;
+
+ /* 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_ptr = nb->buffer + PART_ENCRYPTION_AES256_SIZE;
+ hash_size = package_length - nb->encr_header_len;
+
+ /* Calculate what to encrypt */
+ encr_ptr = hash_ptr - sizeof (hash);
+ encr_size = hash_size + sizeof (hash);
+
+ /* Calculate the SHA-1 hash */
+ gcry_md_hash_buffer (GCRY_MD_SHA1, hash, hash_ptr, hash_size);
+ memcpy (encr_ptr, hash, sizeof (hash));
+
+ if (nb->encr_cypher == NULL)
+ {
+ unsigned char password_hash[32];
+
+ err = gcry_cipher_open (&nb->encr_cypher,
+ GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_OFB, /* flags = */ 0);
+ if (err != 0)
+ return (-1);
+
+ /* Calculate our 256bit key used for AES */
+ gcry_md_hash_buffer (GCRY_MD_SHA256, password_hash,
+ nb->password, strlen (nb->password));
+
+ err = gcry_cipher_setkey (nb->encr_cypher,
+ password_hash, sizeof (password_hash));
+ if (err != 0)
+ {
+ gcry_cipher_close (nb->encr_cypher);
+ nb->encr_cypher = NULL;
+ return (-1);
+ }
+ }
+ else /* if (nb->encr_cypher != NULL) */
+ {
+ gcry_cipher_reset (nb->encr_cypher);
+ }
+
+ /* Set the initialization vector */
+ err = gcry_cipher_setiv (nb->encr_cypher,
+ nb->encr_iv, sizeof (nb->encr_iv));
+ if (err != 0)
+ {
+ gcry_cipher_close (nb->encr_cypher);
+ nb->encr_cypher = NULL;
+ return (-1);
+ }
+
+ /* Encrypt the buffer in-place */
+ err = gcry_cipher_encrypt (nb->encr_cypher,
+ encr_ptr, encr_size,
+ /* in = */ NULL, /* in len = */ 0);
+ if (err != 0)
+ {
+ gcry_cipher_close (nb->encr_cypher);
+ nb->encr_cypher = NULL;
+ return (-1);
+ }
+
+ return (0);
+} /* }}} int nb_add_encryption */
+
/*
* Public functions
*/
nb->ptr = nb->buffer;
nb->free = nb->size;
+ nb->seclevel = NONE;
+ nb->username = NULL;
+ nb->password = NULL;
+
return (nb);
} /* }}} lcc_network_buffer_t *lcc_network_buffer_create */
int lcc_network_buffer_set_security_level (lcc_network_buffer_t *nb, /* {{{ */
lcc_security_level_t level,
- const char *user, const char *password)
+ const char *username, const char *password)
{
- /* FIXME: Not yet implemented */
- return (-1);
+ char *username_copy;
+ char *password_copy;
+
+ if (level == NONE)
+ {
+ free (nb->username);
+ free (nb->password);
+ nb->username = NULL;
+ nb->password = NULL;
+ nb->seclevel = NONE;
+ lcc_network_buffer_initialize (nb);
+ return (0);
+ }
+
+ if (!have_gcrypt ())
+ return (ENOTSUP);
+
+ username_copy = strdup (username);
+ password_copy = strdup (password);
+ if ((username_copy == NULL) || (password_copy == NULL))
+ {
+ free (username_copy);
+ free (password_copy);
+ return (ENOMEM);
+ }
+
+ free (nb->username);
+ free (nb->password);
+ nb->username = username_copy;
+ nb->password = password_copy;
+ nb->seclevel = level;
+
+ lcc_network_buffer_initialize (nb);
+ return (0);
} /* }}} int lcc_network_buffer_set_security_level */
int lcc_network_buffer_initialize (lcc_network_buffer_t *nb) /* {{{ */
nb->ptr = nb->buffer;
nb->free = nb->size;
- /* FIXME: If security is enabled, reserve space for the signature /
- * encryption block here. */
+ if (nb->seclevel == SIGN)
+ {
+ size_t username_len;
+ uint16_t pkg_type = htons (TYPE_SIGN_SHA256);
+ uint16_t pkg_length = PART_SIGNATURE_SHA256_SIZE;
+
+ assert (nb->username != NULL);
+ username_len = strlen (nb->username);
+ pkg_length = htons (pkg_length + ((uint16_t) username_len));
+
+ /* Fill in everything but the hash value here. */
+ memcpy (nb->ptr, &pkg_type, sizeof (pkg_type));
+ memcpy (nb->ptr + sizeof (pkg_type), &pkg_length, sizeof (pkg_length));
+ nb->ptr += PART_SIGNATURE_SHA256_SIZE;
+ nb->free -= PART_SIGNATURE_SHA256_SIZE;
+
+ memcpy (nb->ptr, nb->username, username_len);
+ nb->ptr += username_len;
+ nb->free -= username_len;
+ }
+ else if (nb->seclevel == ENCRYPT)
+ {
+ size_t username_length = strlen (nb->username);
+ uint16_t pkg_type = htons (TYPE_ENCR_AES256);
+ uint16_t pkg_length = 0; /* Filled in in finalize. */
+ uint16_t pkg_user_len = htons ((uint16_t) username_length);
+ char hash[20];
+
+ nb->encr_header_len = username_length;
+ nb->encr_header_len += PART_ENCRYPTION_AES256_SIZE;
+
+ gcry_randomize ((void *) &nb->encr_iv, sizeof (nb->encr_iv),
+ GCRY_STRONG_RANDOM);
+
+ /* Filled in in finalize. */
+ memset (hash, 0, sizeof (hash));
+
+ ADD_STATIC (nb, pkg_type);
+ ADD_STATIC (nb, pkg_length);
+ ADD_STATIC (nb, pkg_user_len);
+ ADD_GENERIC (nb, nb->username, username_length);
+ ADD_GENERIC (nb, nb->encr_iv, sizeof (nb->encr_iv));
+ ADD_GENERIC (nb, hash, sizeof (hash));
+ assert ((nb->encr_header_len + nb->free) == nb->size);
+ }
return (0);
} /* }}} int lcc_network_buffer_initialize */
if (nb == NULL)
return (EINVAL);
- /* FIXME: If security is enabled, sign or encrypt the packet here. */
+ if (nb->seclevel == SIGN)
+ nb_add_signature (nb);
+ else if (nb->seclevel == ENCRYPT)
+ nb_add_encryption (nb);
return (0);
} /* }}} int lcc_network_buffer_finalize */