X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Fnetcmd.c;h=0a77dfc3be201b4a0b119a031053a7c789b7cacf;hb=refs%2Fheads%2Fff%2Fnetcmd;hp=a4c443c54dd7c76f70abb773064c7da8029f5a8d;hpb=4a1ebc0aae8d2a8475518d197b7b7451e4d4f414;p=collectd.git diff --git a/src/netcmd.c b/src/netcmd.c index a4c443c5..0a77dfc3 100644 --- a/src/netcmd.c +++ b/src/netcmd.c @@ -1,6 +1,6 @@ /** * collectd - src/netcmd.c - * Copyright (C) 2007-2011 Florian octo Forster + * Copyright (C) 2007-2015 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"), @@ -47,14 +47,18 @@ #include #include +#include #define NC_DEFAULT_SERVICE "25826" -#define NC_TLS_DH_BITS 1024 + +#ifndef NC_DEFAULT_DH_BITS +# define NC_DEFAULT_DH_BITS 2048 +#endif /* * Private data structures */ -struct nc_peer_s +struct nc_peer_s /* {{{ */ { char *node; char *service; @@ -66,12 +70,12 @@ struct nc_peer_s char *tls_ca_file; char *tls_crl_file; _Bool tls_verify_peer; + unsigned int tls_dh_bits; gnutls_certificate_credentials_t tls_credentials; gnutls_dh_params_t tls_dh_params; gnutls_priority_t tls_priority; - -}; +}; /* }}} */ typedef struct nc_peer_s nc_peer_t; #if defined(PAGESIZE) @@ -82,7 +86,7 @@ typedef struct nc_peer_s nc_peer_t; # define NC_READ_BUFFER_SIZE 4096 #endif -struct nc_connection_s +struct nc_connection_s /* {{{ */ { /* TLS fields */ int fd; @@ -95,9 +99,19 @@ struct nc_connection_s gnutls_session_t tls_session; _Bool have_tls_session; -}; + _Bool tls_verify_peer; +}; /* }}} */ typedef struct nc_connection_s nc_connection_t; +struct nc_proxy_s +{ + int pipe_rx; + int pipe_tx; + + gnutls_session_t tls_session; +}; +typedef struct nc_proxy_s nc_proxy_t; + /* * Private variables */ @@ -109,13 +123,265 @@ static size_t peers_num; static struct pollfd *pollfd = NULL; static size_t pollfd_num; -static int listen_thread_loop = 0; -static int listen_thread_running = 0; +static _Bool listen_thread_loop = 0; +static _Bool listen_thread_running = 0; static pthread_t listen_thread; /* * Functions */ +static const char *nc_verify_status_to_string (gnutls_certificate_status_t status) /* {{{ */ +{ + if (status == 0) + return ("Valid"); + else if (status & GNUTLS_CERT_INVALID) + return ("Invalid"); + else if (status & GNUTLS_CERT_REVOKED) + return ("Revoked"); + else if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) + return ("Signer not found"); + else if (status & GNUTLS_CERT_SIGNER_NOT_CA) + return ("Signer not a CA"); + else if (status & GNUTLS_CERT_INSECURE_ALGORITHM) + return ("Insecure algorithm"); +#if GNUTLS_VERSION_NUMBER >= 0x020708 + else if (status & GNUTLS_CERT_NOT_ACTIVATED) + return ("Not activated"); + else if (status & GNUTLS_CERT_EXPIRED) + return ("Expired"); +#endif + else + return (NULL); +} /* }}} const char *nc_verify_status_to_string */ + +static void *nc_proxy_thread (void *args) /* {{{ */ +{ + nc_proxy_t *data = args; + struct pollfd fds[2]; + int gtls_fd; + long pagesize; + + gtls_fd = (int) gnutls_transport_get_ptr (data->tls_session); + DEBUG ("netcmd plugin: nc_proxy_thread: pipe_rx = %i; pipe_tx = %i; gtls_fd = %i;", + data->pipe_rx, data->pipe_tx, gtls_fd); + + memset (fds, 0, sizeof (fds)); + fds[0].fd = data->pipe_rx; + fds[0].events = POLLIN | POLLPRI; + fds[1].fd = gtls_fd; + fds[1].events = POLLIN | POLLPRI; + + pagesize = sysconf (_SC_PAGESIZE); + + while (42) + { + char errbuf[1024]; + char buffer[pagesize]; + int status; + + status = poll (fds, STATIC_ARRAY_SIZE(fds), /* timeout = */ -1); + if (status < 0) + { + if ((errno == EINTR) || (errno == EAGAIN)) + continue; + ERROR ("netcmd plugin: poll(2) failed: %s", + sstrerror (errno, errbuf, sizeof (errbuf))); + break; + } + + /* pipe -> TLS */ + if (fds[0].revents != 0) /* {{{ */ + { + ssize_t iostatus; + size_t buffer_size; + char *buffer_ptr; + + DEBUG ("netcmd plugin: nc_proxy_thread: Something's up on the pipe."); + + /* Check for hangup, error, ... */ + if ((fds[0].revents & (POLLIN | POLLPRI)) == 0) + break; + + iostatus = read (fds[0].fd, buffer, sizeof (buffer)); + DEBUG ("netcmd plugin: nc_proxy_thread: Received %zi bytes from pipe.", + iostatus); + if (iostatus < 0) + { + if ((errno == EINTR) || (errno == EAGAIN)) + continue; + ERROR ("netcmd plugin: read(2) failed: %s", + sstrerror (errno, errbuf, sizeof (errbuf))); + break; + } + else if (iostatus == 0) + { + break; + } + + buffer_ptr = buffer; + buffer_size = (size_t) iostatus; + while (buffer_size > 0) + { + iostatus = gnutls_record_send (data->tls_session, + buffer, buffer_size); + DEBUG ("netcmd plugin: nc_proxy_thread: Wrote %zi bytes to GNU-TLS.", + iostatus); + if (iostatus < 0) + { + ERROR ("netcmd plugin: gnutls_record_send failed: %s", + gnutls_strerror ((int) iostatus)); + break; + } + + assert (iostatus <= buffer_size); + buffer_ptr += iostatus; + buffer_size -= iostatus; + } /* while (buffer_size > 0) */ + + if (buffer_size != 0) + break; + + fds[0].revents = 0; + } /* }}} if (fds[0].revents != 0) */ + + /* TLS -> pipe */ + if (fds[1].revents != 0) /* {{{ */ + { + ssize_t iostatus; + size_t buffer_size; + + DEBUG ("netcmd plugin: nc_proxy_thread: Something's up on the TLS socket."); + + /* Check for hangup, error, ... */ + if ((fds[1].revents & (POLLIN | POLLPRI)) == 0) + break; + + iostatus = gnutls_record_recv (data->tls_session, buffer, sizeof (buffer)); + DEBUG ("netcmd plugin: nc_proxy_thread: Received %zi bytes from GNU-TLS.", + iostatus); + if (iostatus < 0) + { + if ((iostatus == GNUTLS_E_INTERRUPTED) + || (iostatus == GNUTLS_E_AGAIN)) + continue; + ERROR ("netcmd plugin: gnutls_record_recv failed: %s", + gnutls_strerror ((int) iostatus)); + break; + } + else if (iostatus == 0) + { + break; + } + + buffer_size = (size_t) iostatus; + iostatus = swrite (data->pipe_tx, buffer, buffer_size); + DEBUG ("netcmd plugin: nc_proxy_thread: Wrote %zi bytes to pipe.", + iostatus); + + fds[1].revents = 0; + } /* }}} if (fds[1].revents != 0) */ + } /* while (42) */ + + DEBUG ("netcmd plugin: nc_proxy_thread: Shutting down."); + return (NULL); +} /* }}} void *nc_proxy_thread */ + +/* Creates two pipes and a separate thread to pass data between two FILE* and + * the GNUTLS back and forth. This is required because the handle_ + * functions expect to be able to write to a FILE*. */ +static int nc_start_tls_file_handles (nc_connection_t *conn) /* {{{ */ +{ +#define BAIL_OUT(status) do { \ + DEBUG ("netcmd plugin: nc_start_tls_file_handles: Bailing out with status %i.", (status)); \ + if (proxy_config->pipe_rx >= 0) { close (proxy_config->pipe_rx); } \ + if (proxy_config->pipe_tx >= 0) { close (proxy_config->pipe_tx); } \ + if (conn->fh_in != NULL) { fclose (conn->fh_in); conn->fh_in = NULL; } \ + if (conn->fh_out != NULL) { fclose (conn->fh_out); conn->fh_out = NULL; } \ + free (proxy_config); \ + return (status); \ +} while (0) + + nc_proxy_t *proxy_config; + int pipe_fd[2]; + int status; + + pthread_attr_t attr; + pthread_t thread; + + if ((conn->fh_in != NULL) || (conn->fh_out != NULL)) + { + ERROR ("netcmd plugin: nc_start_tls_file_handles: Connection already connected."); + return (EEXIST); + } + + proxy_config = malloc (sizeof (*proxy_config)); + if (proxy_config == NULL) + { + ERROR ("netcmd plugin: malloc failed."); + return (ENOMEM); + } + memset (proxy_config, 0, sizeof (*proxy_config)); + proxy_config->pipe_rx = -1; + proxy_config->pipe_tx = -1; + proxy_config->tls_session = conn->tls_session; + + pipe_fd[0] = pipe_fd[1] = -1; + status = pipe (pipe_fd); + if (status != 0) + { + char errmsg[1024]; + ERROR ("netcmd plugin: pipe(2) failed: %s", + sstrerror (errno, errmsg, sizeof (errmsg))); + BAIL_OUT (-1); + } + proxy_config->pipe_rx = pipe_fd[0]; + conn->fh_out = fdopen (pipe_fd[1], "w"); + if (conn->fh_out == NULL) + { + char errmsg[1024]; + ERROR ("netcmd plugin: fdopen(2) failed: %s", + sstrerror (errno, errmsg, sizeof (errmsg))); + close (pipe_fd[1]); + BAIL_OUT (-1); + } + + pipe_fd[0] = pipe_fd[1] = -1; + status = pipe (pipe_fd); + if (status != 0) + { + char errmsg[1024]; + ERROR ("netcmd plugin: pipe(2) failed: %s", + sstrerror (errno, errmsg, sizeof (errmsg))); + BAIL_OUT (-1); + } + proxy_config->pipe_tx = pipe_fd[1]; + conn->fh_in = fdopen (pipe_fd[0], "r"); + if (conn->fh_in == NULL) + { + char errmsg[1024]; + ERROR ("netcmd plugin: fdopen(2) failed: %s", + sstrerror (errno, errmsg, sizeof (errmsg))); + close (pipe_fd[0]); + BAIL_OUT (-1); + } + + pthread_attr_init (&attr); + pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED); + + status = pthread_create (&thread, &attr, nc_proxy_thread, proxy_config); + pthread_attr_destroy (&attr); + if (status != 0) + { + char errmsg[1024]; + ERROR ("netcmd plugin: pthread_create(2) failed: %s", + sstrerror (errno, errmsg, sizeof (errmsg))); + BAIL_OUT (-1); + } + + DEBUG ("netcmd plugin: nc_start_tls_file_handles: Successfully started proxy thread."); + return (0); +} /* }}} int nc_start_tls_file_handles */ + static nc_peer_t *nc_fd_to_peer (int fd) /* {{{ */ { size_t i; @@ -132,6 +398,34 @@ static nc_peer_t *nc_fd_to_peer (int fd) /* {{{ */ return (NULL); } /* }}} nc_peer_t *nc_fd_to_peer */ +static void nc_free_peer (nc_peer_t *p) /* {{{ */ +{ + size_t i; + if (p == NULL) + return; + + sfree (p->node); + sfree (p->service); + + for (i = 0; i < p->fds_num; i++) + { + if (p->fds[i] >= 0) + close (p->fds[i]); + p->fds[i] = -1; + } + p->fds_num = 0; + sfree (p->fds); + + sfree (p->tls_cert_file); + sfree (p->tls_key_file); + sfree (p->tls_ca_file); + sfree (p->tls_crl_file); + + gnutls_certificate_free_credentials (p->tls_credentials); + gnutls_dh_params_deinit (p->tls_dh_params); + gnutls_priority_deinit (p->tls_priority); +} /* }}} void nc_free_peer */ + static int nc_register_fd (nc_peer_t *peer, int fd) /* {{{ */ { struct pollfd *poll_ptr; @@ -167,6 +461,82 @@ static int nc_register_fd (nc_peer_t *peer, int fd) /* {{{ */ return (0); } /* }}} int nc_register_fd */ +static gnutls_datum_t nc_read_file (char const *file) /* {{{ */ +{ + void *data = NULL; + size_t sz = 0; + gnutls_datum_t blob = { 0 }; + + if (read_file (file, &data, &sz) != 0) + return (blob); + + blob.data = data; + blob.size = (unsigned int) sz; + return (blob); +} /* }}} gnutls_datum_t nc_read_file */ + +static int nc_x509_crt_import_file (gnutls_x509_crt_t *cert, char const *file) /* {{{ */ +{ + gnutls_datum_t blob = nc_read_file (file); + if (blob.size == 0) + { + char errbuf[1024]; + ERROR ("netcmd plugin: reading \"%s\" failed: %s", file, + sstrerror (errno, errbuf, sizeof (errbuf))); + return (-1); + } + + int status = gnutls_x509_crt_init (cert); + if (status != GNUTLS_E_SUCCESS) + { + ERROR ("netcmd plugin: gnutls_x509_crt_init failed: %s", + gnutls_strerror (status)); + sfree (blob.data); + return (status); + } + + status = gnutls_x509_crt_import (*cert, &blob, GNUTLS_X509_FMT_PEM); + if (status != GNUTLS_E_SUCCESS) + { + ERROR ("netcmd plugin: gnutls_x509_crt_import failed: %s", + gnutls_strerror (status)); + } + + sfree (blob.data); + return status; +} /* }}} int nc_x509_crt_import_file */ + +static int nc_x509_privkey_import_file (gnutls_x509_privkey_t *key, char const *file) /* {{{ */ +{ + gnutls_datum_t blob = nc_read_file (file); + if (blob.size == 0) + { + char errbuf[1024]; + ERROR ("netcmd plugin: reading \"%s\" failed: %s", file, + sstrerror (errno, errbuf, sizeof (errbuf))); + return (-1); + } + + int status = gnutls_x509_privkey_init (key); + if (status != GNUTLS_E_SUCCESS) + { + ERROR ("netcmd plugin: gnutls_x509_privkey_init failed: %s", + gnutls_strerror (status)); + sfree (blob.data); + return (status); + } + + status = gnutls_x509_privkey_import (*key, &blob, GNUTLS_X509_FMT_PEM); + if (status != GNUTLS_E_SUCCESS) + { + ERROR ("netcmd plugin: gnutls_x509_privkey_import failed: %s", + gnutls_strerror (status)); + } + + sfree (blob.data); + return status; +} /* }}} int nc_x509_privkey_import_file */ + static int nc_tls_init (nc_peer_t *peer) /* {{{ */ { int status; @@ -174,8 +544,7 @@ static int nc_tls_init (nc_peer_t *peer) /* {{{ */ if (peer == NULL) return (EINVAL); - if ((peer->tls_cert_file == NULL) - || (peer->tls_key_file == NULL)) + if (peer->tls_key_file == NULL) { DEBUG ("netcmd plugin: Not setting up TLS environment for peer."); return (0); @@ -227,23 +596,57 @@ static int nc_tls_init (nc_peer_t *peer) /* {{{ */ } } - status = gnutls_certificate_set_x509_key_file (peer->tls_credentials, - peer->tls_cert_file, peer->tls_key_file, GNUTLS_X509_FMT_PEM); + gnutls_x509_crt_t cert; + status = nc_x509_crt_import_file (&cert, peer->tls_cert_file); if (status != GNUTLS_E_SUCCESS) { - ERROR ("netcmd plugin: gnutls_certificate_set_x509_key_file failed: %s", + ERROR ("netcmd plugin: failed to load certificate from \"%s\"", + peer->tls_cert_file); + return (status); + } + + gnutls_x509_privkey_t key; + status = nc_x509_privkey_import_file (&key, peer->tls_key_file); + if (status != GNUTLS_E_SUCCESS) + { + ERROR ("netcmd plugin: failed to load private key from \"%s\"", + peer->tls_key_file); + return (status); + } + + status = gnutls_certificate_set_x509_key (peer->tls_credentials, + /* cert_list = */ &cert, /* cert_list_size = */ 1, key); + if (status != GNUTLS_E_SUCCESS) + { + ERROR ("netcmd plugin: gnutls_certificate_set_x509_key failed: %s", gnutls_strerror (status)); return (status); } + if (peer->tls_dh_bits == 0) + { + status = gnutls_x509_crt_get_pk_algorithm (cert, &peer->tls_dh_bits); + if (status != GNUTLS_E_SUCCESS) + { + ERROR ("netcmd plugin: Failed to determine size of the public key: %s. " + "Falling back to using DH with %d bits.", + gnutls_strerror (status), NC_DEFAULT_DH_BITS); + peer->tls_dh_bits = NC_DEFAULT_DH_BITS; + } + else + { + DEBUG ("netcmd plugin: Public key has %u bits", peer->tls_dh_bits); + } + } + /* Initialize Diffie-Hellman parameters. */ gnutls_dh_params_init (&peer->tls_dh_params); - gnutls_dh_params_generate2 (peer->tls_dh_params, NC_TLS_DH_BITS); - gnutls_certificate_set_dh_params (peer->tls_credentials, - peer->tls_dh_params); + gnutls_dh_params_generate2 (peer->tls_dh_params, peer->tls_dh_bits); + gnutls_certificate_set_dh_params (peer->tls_credentials, peer->tls_dh_params); /* Initialize a "priority cache". This will tell GNUTLS which algorithms to * use and which to avoid. We use the "NORMAL" method for now. */ + /* TODO(octo): Add CipherList option. */ gnutls_priority_init (&peer->tls_priority, /* priority = */ "NORMAL", /* errpos = */ NULL); @@ -411,47 +814,84 @@ static void nc_connection_close (nc_connection_t *conn) /* {{{ */ sfree (conn); } /* }}} void nc_connection_close */ -static int nc_connection_init (nc_connection_t *conn) /* {{{ */ +static int nc_connection_init_tls (nc_connection_t *conn) /* {{{ */ { - int fd_copy; - char errbuf[1024]; + int status; + intptr_t fd; - DEBUG ("netcmd plugin: nc_connection_init();"); + conn->read_buffer = malloc (NC_READ_BUFFER_SIZE); + if (conn->read_buffer == NULL) + return (ENOMEM); + memset (conn->read_buffer, 0, NC_READ_BUFFER_SIZE); - if (conn->have_tls_session) + /* Make (relatively) sure that 'fd' and 'void*' have the same size to make + * GCC happy. */ + fd = (intptr_t) conn->fd; + gnutls_transport_set_ptr (conn->tls_session, + (gnutls_transport_ptr_t) fd); + + while (42) { - int status; - intptr_t fd; + status = gnutls_handshake (conn->tls_session); + if (status == GNUTLS_E_SUCCESS) + break; + else if ((status == GNUTLS_E_AGAIN) || (status == GNUTLS_E_INTERRUPTED)) + continue; + else + { + ERROR ("netcmd plugin: gnutls_handshake failed: %s", + gnutls_strerror (status)); + return (status); + } + } - conn->read_buffer = malloc (NC_READ_BUFFER_SIZE); - if (conn->read_buffer == NULL) - return (ENOMEM); - memset (conn->read_buffer, 0, NC_READ_BUFFER_SIZE); + if (conn->tls_verify_peer) + { + unsigned int verify_status = 0; - /* Make (relatively) sure that 'fd' and 'void*' have the same size to make - * GCC happy. */ - fd = (intptr_t) conn->fd; - gnutls_transport_set_ptr (conn->tls_session, - (gnutls_transport_ptr_t) fd); + status = gnutls_certificate_verify_peers2 (conn->tls_session, + &verify_status); + if (status != GNUTLS_E_SUCCESS) + { + ERROR ("netcmd plugin: gnutls_certificate_verify_peers2 failed: %s", + gnutls_strerror (status)); + return (status); + } - while (42) + if (verify_status != 0) { - status = gnutls_handshake (conn->tls_session); - if (status == GNUTLS_E_SUCCESS) - break; - else if ((status == GNUTLS_E_AGAIN) || (status == GNUTLS_E_INTERRUPTED)) - continue; + const char *reason; + + reason = nc_verify_status_to_string (verify_status); + if (reason == NULL) + ERROR ("netcmd plugin: Verification of peer failed with " + "status %i (%#x)", verify_status, verify_status); else - { - ERROR ("netcmd plugin: gnutls_handshake failed: %s", - gnutls_strerror (status)); - return (-1); - } + ERROR ("netcmd plugin: Verification of peer failed with " + "status %i (%s)", verify_status, reason); + + return (-1); } + } /* if (conn->tls_verify_peer) */ - return (0); + status = nc_start_tls_file_handles (conn); + if (status != 0) + { + nc_connection_close (conn); + return (-1); } + return (0); +} /* }}} int nc_connection_init_tls */ + +static int nc_connection_init (nc_connection_t *conn) /* {{{ */ +{ + int fd_copy; + char errbuf[1024]; + + if (conn->have_tls_session) + return (nc_connection_init_tls (conn)); + /* Duplicate the file descriptor. We need two file descriptors, because we * create two FILE* objects. If they pointed to the same FD and we called * fclose() on each, that would call close() twice on the same FD. If @@ -496,6 +936,9 @@ static int nc_connection_init (nc_connection_t *conn) /* {{{ */ return (0); } /* }}} int nc_connection_init */ +/* nc_connection_gets reads one more block from the connection, looks for a + * newline and copies everything up until and including the first newline into + * buffer end returns buffer itself. */ static char *nc_connection_gets (nc_connection_t *conn, /* {{{ */ char *buffer, size_t buffer_size) { @@ -723,7 +1166,7 @@ static void *nc_server_thread (void __attribute__((unused)) *arg) /* {{{ */ pthread_exit ((void *) -1); } - while (listen_thread_loop != 0) + while (listen_thread_loop) { status = poll (pollfd, (nfds_t) pollfd_num, /* timeout = */ -1); if (status < 0) @@ -790,23 +1233,39 @@ static void *nc_server_thread (void __attribute__((unused)) *arg) /* {{{ */ conn->fh_out = NULL; conn->fd = status; + + /* Start up the TLS session if the required configuration options have + * been given. */ if ((peer != NULL) - && (peer->tls_cert_file != NULL)) + && (peer->tls_key_file != NULL)) { - DEBUG ("netcmd plugin: Starting TLS session on [%s]:%s", + DEBUG ("netcmd plugin: Starting TLS session on a connection " + "via [%s]:%s", (peer->node != NULL) ? peer->node : "any", (peer->service != NULL) ? peer->service : NC_DEFAULT_SERVICE); conn->tls_session = nc_tls_get_session (peer); + if (conn->tls_session == NULL) + { + ERROR ("netcmd plugin: Creating TLS session on a connection via " + "[%s]:%s failed. For security reasons this connection will be " + "terminated.", + (peer->node != NULL) ? peer->node : "any", + (peer->service != NULL) ? peer->service : NC_DEFAULT_SERVICE); + nc_connection_close (conn); + continue; + } conn->have_tls_session = 1; + conn->tls_verify_peer = peer->tls_verify_peer; } - DEBUG ("Spawning child to handle connection on fd %i", conn->fd); + DEBUG ("netcmd plugin: Spawning child to handle connection on fd #%i", + conn->fd); pthread_attr_init (&th_attr); pthread_attr_setdetachstate (&th_attr, PTHREAD_CREATE_DETACHED); - status = pthread_create (&th, &th_attr, nc_handle_client, - conn); + status = pthread_create (&th, &th_attr, nc_handle_client, conn); + pthread_attr_destroy (&th_attr); if (status != 0) { WARNING ("netcmd plugin: pthread_create failed: %s", @@ -850,6 +1309,7 @@ static void *nc_server_thread (void __attribute__((unused)) *arg) /* {{{ */ static int nc_config_peer (const oconfig_item_t *ci) /* {{{ */ { nc_peer_t *p; + _Bool success; int i; p = realloc (peers, sizeof (*peers) * (peers_num + 1)); @@ -867,7 +1327,7 @@ static int nc_config_peer (const oconfig_item_t *ci) /* {{{ */ p->tls_key_file = NULL; p->tls_ca_file = NULL; p->tls_crl_file = NULL; - p->tls_verify_peer = 1; + p->tls_verify_peer = 0; for (i = 0; i < ci->children_num; i++) { @@ -876,7 +1336,7 @@ static int nc_config_peer (const oconfig_item_t *ci) /* {{{ */ if (strcasecmp ("Address", child->key) == 0) cf_util_get_string (child, &p->node); else if (strcasecmp ("Port", child->key) == 0) - cf_util_get_string (child, &p->service); + cf_util_get_service (child, &p->service); else if (strcasecmp ("TLSCertFile", child->key) == 0) cf_util_get_string (child, &p->tls_cert_file); else if (strcasecmp ("TLSKeyFile", child->key) == 0) @@ -887,11 +1347,64 @@ static int nc_config_peer (const oconfig_item_t *ci) /* {{{ */ cf_util_get_string (child, &p->tls_crl_file); else if (strcasecmp ("TLSVerifyPeer", child->key) == 0) cf_util_get_boolean (child, &p->tls_verify_peer); + else if (strcasecmp ("TLSDHBits", child->key) == 0) + { + int tmp = 0; + if (cf_util_get_int (child, &tmp) == 0) + { + if (tmp > 0) + p->tls_dh_bits = (unsigned int) tmp; + else + ERROR ("netcmd plugin: The \"TLSDHBits\" option was set to %d, but expects a positive integer.", tmp); + } + } else WARNING ("netcmd plugin: The option \"%s\" is not recognized within " "a \"%s\" block.", child->key, ci->key); } + /* TLS is confusing for many people. Be verbose on mis-configurations to + * help people set up encryption correctly. */ + success = 1; + if (p->tls_key_file == NULL) + { + if (p->tls_cert_file != NULL) + { + WARNING ("netcmd plugin: The \"TLSCertFile\" option is only valid in " + "combination with the \"TLSKeyFile\" option."); + success = 0; + } + if (p->tls_ca_file != NULL) + { + WARNING ("netcmd plugin: The \"TLSCAFile\" option is only valid when " + "the \"TLSKeyFile\" option has been specified."); + success = 0; + } + if (p->tls_crl_file != NULL) + { + WARNING ("netcmd plugin: The \"TLSCRLFile\" option is only valid when " + "the \"TLSKeyFile\" option has been specified."); + success = 0; + } + } + else if (p->tls_cert_file == NULL) + { + WARNING ("netcmd plugin: The \"TLSKeyFile\" option is only valid in " + "combination with the \"TLSCertFile\" option."); + success = 0; + } + + if (!success) + { + ERROR ("netcmd plugin: Problems in the security settings have been " + "detected in the block for [%s]:%s. The entire block " + "will be ignored to prevent unauthorized access.", + (p->node == NULL) ? "::0" : p->node, + (p->service == NULL) ? NC_DEFAULT_SERVICE : p->service); + nc_free_peer (p); + return (-1); + } + DEBUG ("netcmd plugin: node = \"%s\"; service = \"%s\";", p->node, p->service); peers_num++; @@ -899,7 +1412,7 @@ static int nc_config_peer (const oconfig_item_t *ci) /* {{{ */ return (0); } /* }}} int nc_config_peer */ -static int nc_config (oconfig_item_t *ci) +static int nc_config (oconfig_item_t *ci) /* {{{ */ { int i; @@ -915,9 +1428,9 @@ static int nc_config (oconfig_item_t *ci) } return (0); -} /* int nc_config */ +} /* }}} int nc_config */ -static int nc_init (void) +static int nc_init (void) /* {{{ */ { static int have_init = 0; @@ -945,16 +1458,18 @@ static int nc_init (void) listen_thread_running = 1; return (0); -} /* int nc_init */ +} /* }}} int nc_init */ -static int nc_shutdown (void) +static int nc_shutdown (void) /* {{{ */ { - void *ret; + size_t i; listen_thread_loop = 0; if (listen_thread != (pthread_t) 0) { + void *ret; + pthread_kill (listen_thread, SIGTERM); pthread_join (listen_thread, &ret); listen_thread = (pthread_t) 0; @@ -963,14 +1478,19 @@ static int nc_shutdown (void) plugin_unregister_init ("netcmd"); plugin_unregister_shutdown ("netcmd"); + for (i = 0; i < peers_num; i++) + nc_free_peer (peers + i); + peers_num = 0; + sfree (peers); + return (0); -} /* int nc_shutdown */ +} /* }}} int nc_shutdown */ -void module_register (void) +void module_register (void) /* {{{ */ { plugin_register_complex_config ("netcmd", nc_config); plugin_register_init ("netcmd", nc_init); plugin_register_shutdown ("netcmd", nc_shutdown); -} /* void module_register (void) */ +} /* }}} void module_register (void) */ /* vim: set sw=2 sts=2 tw=78 et fdm=marker : */