/*
* Private data types
*/
-typedef struct sockent
+#define SECURITY_LEVEL_NONE 0
- #if HAVE_GCRYPT_H
++#if HAVE_LIBGCRYPT
+# define SECURITY_LEVEL_SIGN 1
+# define SECURITY_LEVEL_ENCRYPT 2
+#endif
+struct sockent_client
{
- int fd;
+ int fd;
struct sockaddr_storage *addr;
socklen_t addrlen;
- #if HAVE_GCRYPT_H
++#if HAVE_LIBGCRYPT
+ int security_level;
+ char *username;
+ char *password;
+ gcry_cipher_hd_t cypher;
+ unsigned char password_hash[32];
+#endif
+};
-#define SECURITY_LEVEL_NONE 0
+struct sockent_server
+{
+ int *fd;
+ size_t fd_num;
- #if HAVE_GCRYPT_H
+ #if HAVE_LIBGCRYPT
-# define SECURITY_LEVEL_SIGN 1
-# define SECURITY_LEVEL_ENCRYPT 2
int security_level;
- char *shared_secret;
- unsigned char shared_secret_hash[32];
+ char *auth_file;
+ fbhash_t *userdb;
gcry_cipher_hd_t cypher;
-#endif /* HAVE_LIBGCRYPT */
+#endif
+};
+
+typedef struct sockent
+{
+#define SOCKENT_TYPE_CLIENT 1
+#define SOCKENT_TYPE_SERVER 2
+ int type;
+
+ char *node;
+ char *service;
+
+ union
+ {
+ struct sockent_client client;
+ struct sockent_server server;
+ } data;
- struct sockent *next;
+ struct sockent *next;
} sockent_t;
/* 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
return (retval);
} /* int cache_check */
- #if HAVE_GCRYPT_H
+ #if HAVE_LIBGCRYPT
static gcry_cipher_hd_t network_get_aes256_cypher (sockent_t *se, /* {{{ */
- const void *iv, size_t iv_size)
+ const void *iv, size_t iv_size, const char *username)
{
gcry_error_t err;
+ gcry_cipher_hd_t *cyper_ptr;
+ unsigned char password_hash[32];
- if (se->cypher == NULL)
+ if (se->type == SOCKENT_TYPE_CLIENT)
{
- err = gcry_cipher_open (&se->cypher,
- GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, /* flags = */ 0);
+ cyper_ptr = &se->data.client.cypher;
+ memcpy (password_hash, se->data.client.password_hash,
+ sizeof (password_hash));
+ }
+ else
+ {
+ char *secret;
+
+ cyper_ptr = &se->data.server.cypher;
+
+ if (username == NULL)
+ return (NULL);
+
+ secret = fbh_get (se->data.server.userdb, username);
+ if (secret == NULL)
+ return (NULL);
+
+ gcry_md_hash_buffer (GCRY_MD_SHA256,
+ password_hash,
+ secret, strlen (secret));
+
+ sfree (secret);
+ }
+
+ if (*cyper_ptr == NULL)
+ {
+ err = gcry_cipher_open (cyper_ptr,
+ GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_OFB, /* flags = */ 0);
if (err != 0)
{
ERROR ("network plugin: gcry_cipher_open returned: %s",
return (NULL);
}
- return (se->cypher);
+ return (*cyper_ptr);
} /* }}} int network_get_aes256_cypher */
- #endif /* HAVE_GCRYPT_H */
+ #endif /* HAVE_LIBGCRYPT */
static int write_part_values (char **ret_buffer, int *ret_buffer_len,
const data_set_t *ds, const value_list_t *vl)
break;
}
}
- #if HAVE_GCRYPT_H
+ #if HAVE_LIBGCRYPT
- else if ((se->security_level == SECURITY_LEVEL_ENCRYPT)
+ else if ((se->data.server.security_level == SECURITY_LEVEL_ENCRYPT)
&& (packet_was_encrypted == 0))
{
if (printed_ignore_warning == 0)
break;
}
}
- #if HAVE_GCRYPT_H
+ #if HAVE_LIBGCRYPT
- else if ((se->security_level == SECURITY_LEVEL_SIGN)
+ else if ((se->data.server.security_level == SECURITY_LEVEL_SIGN)
&& (packet_was_encrypted == 0)
&& (packet_was_signed == 0))
{
return (status);
} /* }}} int parse_packet */
-static void free_sockent (sockent_t *se) /* {{{ */
+static void free_sockent_client (struct sockent_client *sec) /* {{{ */
{
- sockent_t *next;
- while (se != NULL)
- {
- next = se->next;
+ if (sec->fd >= 0)
+ {
+ close (sec->fd);
+ sec->fd = -1;
+ }
+ sfree (sec->addr);
- #if HAVE_GCRYPT_H
++#if HAVE_LIBGCRYPT
+ sfree (sec->username);
+ sfree (sec->password);
+ if (sec->cypher != NULL)
+ gcry_cipher_close (sec->cypher);
+#endif
+} /* }}} void free_sockent_client */
+
+static void free_sockent_server (struct sockent_server *ses) /* {{{ */
+{
+ size_t i;
+
+ for (i = 0; i < ses->fd_num; i++)
+ {
+ if (ses->fd[i] >= 0)
+ {
+ close (ses->fd[i]);
+ ses->fd[i] = -1;
+ }
+ }
- #if HAVE_GCRYPT_H
+ sfree (ses->fd);
- if (se->cypher != NULL)
- {
- gcry_cipher_close (se->cypher);
- se->cypher = NULL;
- }
- free (se->shared_secret);
-#endif /* HAVE_LIBGCRYPT */
+ #if HAVE_LIBGCRYPT
+ sfree (ses->auth_file);
+ fbh_destroy (ses->userdb);
+ if (ses->cypher != NULL)
+ gcry_cipher_close (ses->cypher);
+#endif
+} /* }}} void free_sockent_server */
- free (se->addr);
- free (se);
+static void sockent_destroy (sockent_t *se) /* {{{ */
+{
+ sockent_t *next;
- se = next;
- }
-} /* }}} void free_sockent */
+ DEBUG ("network plugin: sockent_destroy (se = %p);", (void *) se);
+
+ while (se != NULL)
+ {
+ next = se->next;
+
+ sfree (se->node);
+ sfree (se->service);
+
+ if (se->type == SOCKENT_TYPE_CLIENT)
+ free_sockent_client (&se->data.client);
+ else
+ free_sockent_server (&se->data.server);
+
+ sfree (se);
+ se = next;
+ }
+} /* }}} void sockent_destroy */
/*
* int network_set_ttl
return (0);
} /* int network_bind_socket */
-#define CREATE_SOCKET_FLAGS_LISTEN 0x0001
-static sockent_t *network_create_socket (const char *node, /* {{{ */
- const char *service,
- const char *shared_secret,
- int security_level,
- int flags)
+/* Initialize a sockent structure. `type' must be either `SOCKENT_TYPE_CLIENT'
+ * or `SOCKENT_TYPE_SERVER' */
+static int sockent_init (sockent_t *se, int type) /* {{{ */
+{
+ if (se == NULL)
+ return (-1);
+
+ memset (se, 0, sizeof (*se));
+
+ se->type = SOCKENT_TYPE_CLIENT;
+ se->node = NULL;
+ se->service = NULL;
+ se->next = NULL;
+
+ if (type == SOCKENT_TYPE_SERVER)
+ {
+ se->type = SOCKENT_TYPE_SERVER;
+ se->data.server.fd = NULL;
- #if HAVE_GCRYPT_H
++#if HAVE_LIBGCRYPT
+ se->data.server.security_level = SECURITY_LEVEL_NONE;
+ se->data.server.auth_file = NULL;
+ se->data.server.userdb = NULL;
+ se->data.server.cypher = NULL;
+#endif
+ }
+ else
+ {
+ se->data.client.fd = -1;
+ se->data.client.addr = NULL;
- #if HAVE_GCRYPT_H
++#if HAVE_LIBGCRYPT
+ se->data.client.security_level = SECURITY_LEVEL_NONE;
+ se->data.client.username = NULL;
+ se->data.client.password = NULL;
+ se->data.client.cypher = NULL;
+#endif
+ }
+
+ return (0);
+} /* }}} int sockent_init */
+
+/* Open the file descriptors for a initialized sockent structure. */
+static int sockent_open (sockent_t *se) /* {{{ */
{
struct addrinfo ai_hints;
struct addrinfo *ai_list, *ai_ptr;
int ai_return;
- sockent_t *se_head = NULL;
- sockent_t *se_tail = NULL;
+ const char *node;
+ const char *service;
+
+ if (se == NULL)
+ return (-1);
+
+ /* Set up the security structures. */
- #if HAVE_GCRYPT_H /* {{{ */
++#if HAVE_LIBGCRYPT /* {{{ */
+ if (se->type == SOCKENT_TYPE_CLIENT)
+ {
+ if (se->data.client.security_level > SECURITY_LEVEL_NONE)
+ {
+ if ((se->data.client.username == NULL)
+ || (se->data.client.password == NULL))
+ {
+ ERROR ("network plugin: Client socket with "
+ "security requested, but no "
+ "credentials are configured.");
+ return (-1);
+ }
+ gcry_md_hash_buffer (GCRY_MD_SHA256,
+ se->data.client.password_hash,
+ se->data.client.password,
+ strlen (se->data.client.password));
+ }
+ }
+ else /* (se->type == SOCKENT_TYPE_SERVER) */
+ {
+ if (se->data.server.security_level > SECURITY_LEVEL_NONE)
+ {
+ if (se->data.server.auth_file == NULL)
+ {
+ ERROR ("network plugin: Server socket with "
+ "security requested, but no "
+ "password file is configured.");
+ return (-1);
+ }
+ }
+ if (se->data.server.auth_file != NULL)
+ {
+ se->data.server.userdb = fbh_create (se->data.server.auth_file);
+ if (se->data.server.userdb == NULL)
+ {
+ ERROR ("network plugin: Reading password file "
+ "`%s' failed.",
+ se->data.server.auth_file);
+ if (se->data.server.security_level > SECURITY_LEVEL_NONE)
+ return (-1);
+ }
+ }
+ }
- #endif /* }}} HAVE_GCRYPT_H */
++#endif /* }}} HAVE_LIBGCRYPT */
+
+ node = se->node;
+ service = se->service;
- DEBUG ("node = %s, service = %s", node, service);
+ if (service == NULL)
+ service = NET_DEFAULT_PORT;
- memset (&ai_hints, '\0', sizeof (ai_hints));
- ai_hints.ai_flags = 0;
+ DEBUG ("network plugin: sockent_open: node = %s; service = %s;",
+ node, service);
+
+ memset (&ai_hints, 0, sizeof (ai_hints));
+ ai_hints.ai_flags = 0;
#ifdef AI_PASSIVE
ai_hints.ai_flags |= AI_PASSIVE;
#endif
} /* while (42) */
} /* }}} void networt_send_buffer_plain */
- #if HAVE_GCRYPT_H
+ #if HAVE_LIBGCRYPT
+#define BUFFER_ADD(p,s) do { \
+ memcpy (buffer + buffer_offset, (p), (s)); \
+ buffer_offset += (s); \
+} while (0)
+
static void networt_send_buffer_signed (const sockent_t *se, /* {{{ */
const char *in_buffer, size_t in_buffer_size)
{
/* Send it out without further modifications */
networt_send_buffer_plain (se, buffer, buffer_size);
-#undef BUFFER_ADD
} /* }}} void networt_send_buffer_encrypted */
- #endif /* HAVE_GCRYPT_H */
+#undef BUFFER_ADD
+ #endif /* HAVE_LIBGCRYPT */
static void network_send_buffer (char *buffer, size_t buffer_len) /* {{{ */
{
for (se = sending_sockets; se != NULL; se = se->next)
{
- #if HAVE_GCRYPT_H
+ #if HAVE_LIBGCRYPT
- if (se->security_level == SECURITY_LEVEL_ENCRYPT)
+ if (se->data.client.security_level == SECURITY_LEVEL_ENCRYPT)
networt_send_buffer_encrypted (se, buffer, buffer_len);
- else if (se->security_level == SECURITY_LEVEL_SIGN)
+ else if (se->data.client.security_level == SECURITY_LEVEL_SIGN)
networt_send_buffer_signed (se, buffer, buffer_len);
- else /* if (se->security_level == SECURITY_LEVEL_NONE) */
+ else /* if (se->data.client.security_level == SECURITY_LEVEL_NONE) */
- #endif /* HAVE_GCRYPT_H */
+ #endif /* HAVE_LIBGCRYPT */
networt_send_buffer_plain (se, buffer, buffer_len);
} /* for (sending_sockets) */
} /* }}} void network_send_buffer */
return (0);
} /* }}} int network_config_set_ttl */
- #if HAVE_GCRYPT_H
+static int network_config_set_string (const oconfig_item_t *ci, /* {{{ */
+ char **ret_string)
+{
+ char *tmp;
+ if ((ci->values_num != 1)
+ || (ci->values[0].type != OCONFIG_TYPE_STRING))
+ {
+ WARNING ("network plugin: The `%s' config option needs exactly "
+ "one string argument.", ci->key);
+ return (-1);
+ }
+
+ tmp = strdup (ci->values[0].value.string);
+ if (tmp == NULL)
+ return (-1);
+
+ sfree (*ret_string);
+ *ret_string = tmp;
+
+ return (0);
+} /* }}} int network_config_set_string */
+
+ #if HAVE_LIBGCRYPT
static int network_config_set_security_level (oconfig_item_t *ci, /* {{{ */
int *retval)
{
return (0);
} /* }}} int network_config_set_security_level */
- #endif /* HAVE_GCRYPT_H */
+ #endif /* HAVE_LIBGCRYPT */
-static int network_config_listen_server (const oconfig_item_t *ci) /* {{{ */
+static int network_config_add_listen (const oconfig_item_t *ci) /* {{{ */
{
- char *node;
- char *service;
- char *shared_secret = NULL;
- int security_level = SECURITY_LEVEL_NONE;
+ sockent_t *se;
+ int status;
int i;
if ((ci->values_num < 1) || (ci->values_num > 2)
{
oconfig_item_t *child = ci->children + i;
- #if HAVE_GCRYPT_H
+ #if HAVE_LIBGCRYPT
- if (strcasecmp ("Secret", child->key) == 0)
+ if (strcasecmp ("AuthFile", child->key) == 0)
+ network_config_set_string (child, &se->data.server.auth_file);
+ else if (strcasecmp ("SecurityLevel", child->key) == 0)
+ network_config_set_security_level (child,
+ &se->data.server.security_level);
+ else
- #endif /* HAVE_GCRYPT_H */
++#endif /* HAVE_LIBGCRYPT */
{
- if ((child->values_num == 1)
- && (child->values[0].type == OCONFIG_TYPE_STRING))
- shared_secret = child->values[0].value.string;
- else
- ERROR ("network plugin: The `Secret' option needs exactly one string "
- "argument.");
+ WARNING ("network plugin: Option `%s' is not allowed here.",
+ child->key);
}
- #if HAVE_GCRYPT_H
+ }
+
+ if ((se->data.server.security_level > SECURITY_LEVEL_NONE)
+ && (se->data.server.auth_file == NULL))
+ {
+ ERROR ("network plugin: A security level higher than `none' was "
+ "requested, but no AuthFile option was given. Cowardly refusing to "
+ "open this socket!");
+ sockent_destroy (se);
+ return (-1);
+ }
+
+ status = sockent_open (se);
+ if (status != 0)
+ {
+ ERROR ("network plugin: network_config_add_listen: sockent_open failed.");
+ sockent_destroy (se);
+ return (-1);
+ }
+
+ status = sockent_add (se);
+ if (status != 0)
+ {
+ ERROR ("network plugin: network_config_add_listen: sockent_add failed.");
+ sockent_destroy (se);
+ return (-1);
+ }
+
+ return (0);
+} /* }}} int network_config_add_listen */
+
+static int network_config_add_server (const oconfig_item_t *ci) /* {{{ */
+{
+ sockent_t *se;
+ int status;
+ int i;
+
+ if ((ci->values_num < 1) || (ci->values_num > 2)
+ || (ci->values[0].type != OCONFIG_TYPE_STRING)
+ || ((ci->values_num > 1) && (ci->values[1].type != OCONFIG_TYPE_STRING)))
+ {
+ ERROR ("network plugin: The `%s' config option needs "
+ "one or two string arguments.", ci->key);
+ return (-1);
+ }
+
+ se = malloc (sizeof (*se));
+ if (se == NULL)
+ {
+ ERROR ("network plugin: malloc failed.");
+ return (-1);
+ }
+ sockent_init (se, SOCKENT_TYPE_CLIENT);
+
+ se->node = strdup (ci->values[0].value.string);
+ if (ci->values_num >= 2)
+ se->service = strdup (ci->values[1].value.string);
+
+ for (i = 0; i < ci->children_num; i++)
+ {
+ oconfig_item_t *child = ci->children + i;
+
++#if HAVE_LIBGCRYPT
+ if (strcasecmp ("Username", child->key) == 0)
+ network_config_set_string (child, &se->data.client.username);
+ else if (strcasecmp ("Password", child->key) == 0)
+ network_config_set_string (child, &se->data.client.password);
else if (strcasecmp ("SecurityLevel", child->key) == 0)
- network_config_set_security_level (child, &security_level);
+ network_config_set_security_level (child,
+ &se->data.client.security_level);
else
- #endif /* HAVE_GCRYPT_H */
+ #endif /* HAVE_LIBGCRYPT */
{
WARNING ("network plugin: Option `%s' is not allowed here.",
child->key);