summaryrefslogtreecommitdiffstats
path: root/libnetdata/socket
diff options
context:
space:
mode:
Diffstat (limited to 'libnetdata/socket')
-rw-r--r--libnetdata/socket/security.c277
-rw-r--r--libnetdata/socket/security.h47
-rw-r--r--libnetdata/socket/socket.c59
-rw-r--r--libnetdata/socket/socket.h5
4 files changed, 386 insertions, 2 deletions
diff --git a/libnetdata/socket/security.c b/libnetdata/socket/security.c
new file mode 100644
index 000000000..dcbd3f650
--- /dev/null
+++ b/libnetdata/socket/security.c
@@ -0,0 +1,277 @@
+#include "../libnetdata.h"
+
+#ifdef ENABLE_HTTPS
+
+SSL_CTX *netdata_opentsdb_ctx=NULL;
+SSL_CTX *netdata_client_ctx=NULL;
+SSL_CTX *netdata_srv_ctx=NULL;
+const char *security_key=NULL;
+const char *security_cert=NULL;
+int netdata_use_ssl_on_stream = NETDATA_SSL_OPTIONAL;
+int netdata_use_ssl_on_http = NETDATA_SSL_FORCE; //We force SSL due safety reasons
+int netdata_validate_server = NETDATA_SSL_VALID_CERTIFICATE;
+
+/**
+ * Info Callback
+ *
+ * Function used as callback for the OpenSSL Library
+ *
+ * @param ssl a pointer to the SSL structure of the client
+ * @param where the variable with the flags set.
+ * @param ret the return of the caller
+ */
+static void security_info_callback(const SSL *ssl, int where, int ret) {
+ (void)ssl;
+ if (where & SSL_CB_ALERT) {
+ debug(D_WEB_CLIENT,"SSL INFO CALLBACK %s %s", SSL_alert_type_string(ret), SSL_alert_desc_string_long(ret));
+ }
+}
+
+/**
+ * OpenSSL Library
+ *
+ * Starts the openssl library for the Netdata.
+ */
+void security_openssl_library()
+{
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+# if (SSLEAY_VERSION_NUMBER >= 0x0907000L)
+ OPENSSL_config(NULL);
+# endif
+
+# if OPENSSL_API_COMPAT < 0x10100000L
+ SSL_load_error_strings();
+# endif
+
+ SSL_library_init();
+#else
+ if (OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, NULL) != 1) {
+ error("SSL library cannot be initialized.");
+ }
+#endif
+}
+
+/**
+ * OpenSSL common options
+ *
+ * Clients and SERVER have common options, this function is responsible to set them in the context.
+ *
+ * @param ctx
+ */
+void security_openssl_common_options(SSL_CTX *ctx) {
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+ static char *ciphers = {"ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA"};
+#endif
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ SSL_CTX_set_options (ctx,SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_COMPRESSION);
+#else
+ SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
+ //We are avoiding the TLS v1.3 for while, because Google Chrome
+ //is giving the message net::ERR_SSL_VERSION_INTERFERENCE with it.
+ SSL_CTX_set_max_proto_version(ctx, TLS1_2_VERSION);
+#endif
+ SSL_CTX_set_mode(ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
+
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+ if (!SSL_CTX_set_cipher_list(ctx, ciphers)) {
+ error("SSL error. cannot set the cipher list");
+ }
+#endif
+}
+
+/**
+ * Initialize Openssl Client
+ *
+ * Starts the client context with TLS 1.2.
+ *
+ * @return It returns the context on success or NULL otherwise
+ */
+static SSL_CTX * security_initialize_openssl_client() {
+ SSL_CTX *ctx;
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ ctx = SSL_CTX_new(SSLv23_client_method());
+#else
+ ctx = SSL_CTX_new(TLS_client_method());
+#endif
+ if(ctx) {
+ security_openssl_common_options(ctx);
+ }
+
+ return ctx;
+}
+
+/**
+ * Initialize OpenSSL server
+ *
+ * Starts the server context with TLS 1.2 and load the certificate.
+ *
+ * @return It returns the context on success or NULL otherwise
+ */
+static SSL_CTX * security_initialize_openssl_server() {
+ SSL_CTX *ctx;
+ char lerror[512];
+ static int netdata_id_context = 1;
+
+ //TO DO: Confirm the necessity to check return for other OPENSSL function
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ ctx = SSL_CTX_new(SSLv23_server_method());
+ if (!ctx) {
+ error("Cannot create a new SSL context, netdata won't encrypt communication");
+ return NULL;
+ }
+
+ SSL_CTX_use_certificate_file(ctx, security_cert, SSL_FILETYPE_PEM);
+#else
+ ctx = SSL_CTX_new(TLS_server_method());
+ if (!ctx) {
+ error("Cannot create a new SSL context, netdata won't encrypt communication");
+ return NULL;
+ }
+
+ SSL_CTX_use_certificate_chain_file(ctx, security_cert);
+#endif
+ security_openssl_common_options(ctx);
+
+ SSL_CTX_use_PrivateKey_file(ctx,security_key,SSL_FILETYPE_PEM);
+
+ if (!SSL_CTX_check_private_key(ctx)) {
+ ERR_error_string_n(ERR_get_error(),lerror,sizeof(lerror));
+ error("SSL cannot check the private key: %s",lerror);
+ SSL_CTX_free(ctx);
+ return NULL;
+ }
+
+ SSL_CTX_set_session_id_context(ctx,(void*)&netdata_id_context,(unsigned int)sizeof(netdata_id_context));
+ SSL_CTX_set_info_callback(ctx,security_info_callback);
+
+#if (OPENSSL_VERSION_NUMBER < 0x00905100L)
+ SSL_CTX_set_verify_depth(ctx,1);
+#endif
+ debug(D_WEB_CLIENT,"SSL GLOBAL CONTEXT STARTED\n");
+
+ return ctx;
+}
+
+/**
+ * Start SSL
+ *
+ * Call the correct function to start the SSL context.
+ *
+ * @param selector informs the context that must be initialized, the following list has the valid values:
+ * NETDATA_SSL_CONTEXT_SERVER - the server context
+ * NETDATA_SSL_CONTEXT_STREAMING - Starts the streaming context.
+ * NETDATA_SSL_CONTEXT_OPENTSDB - Starts the OpenTSDB contextv
+ */
+void security_start_ssl(int selector) {
+ switch (selector) {
+ case NETDATA_SSL_CONTEXT_SERVER: {
+ struct stat statbuf;
+ if (stat(security_key,&statbuf) || stat(security_cert,&statbuf)) {
+ info("To use encryption it is necessary to set \"ssl certificate\" and \"ssl key\" in [web] !\n");
+ return;
+ }
+
+ netdata_srv_ctx = security_initialize_openssl_server();
+ break;
+ }
+ case NETDATA_SSL_CONTEXT_STREAMING: {
+ netdata_client_ctx = security_initialize_openssl_client();
+ break;
+ }
+ case NETDATA_SSL_CONTEXT_OPENTSDB: {
+ netdata_opentsdb_ctx = security_initialize_openssl_client();
+ break;
+ }
+ }
+}
+
+void security_clean_openssl() {
+ if (netdata_srv_ctx)
+ {
+ SSL_CTX_free(netdata_srv_ctx);
+ }
+
+ if (netdata_client_ctx)
+ {
+ SSL_CTX_free(netdata_client_ctx);
+ }
+
+ if ( netdata_opentsdb_ctx )
+ {
+ SSL_CTX_free(netdata_opentsdb_ctx);
+ }
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ ERR_free_strings();
+#endif
+}
+
+int security_process_accept(SSL *ssl,int msg) {
+ int sock = SSL_get_fd(ssl);
+ int test;
+ if (msg > 0x17)
+ {
+ return NETDATA_SSL_NO_HANDSHAKE;
+ }
+
+ ERR_clear_error();
+ if ((test = SSL_accept(ssl)) <= 0) {
+ int sslerrno = SSL_get_error(ssl, test);
+ switch(sslerrno) {
+ case SSL_ERROR_WANT_READ:
+ {
+ error("SSL handshake did not finish and it wanna read on socket %d!", sock);
+ return NETDATA_SSL_WANT_READ;
+ }
+ case SSL_ERROR_WANT_WRITE:
+ {
+ error("SSL handshake did not finish and it wanna read on socket %d!", sock);
+ return NETDATA_SSL_WANT_WRITE;
+ }
+ case SSL_ERROR_NONE:
+ case SSL_ERROR_SSL:
+ case SSL_ERROR_SYSCALL:
+ default:
+ {
+ u_long err;
+ char buf[256];
+ int counter = 0;
+ while ((err = ERR_get_error()) != 0) {
+ ERR_error_string_n(err, buf, sizeof(buf));
+ info("%d SSL Handshake error (%s) on socket %d ", counter++, ERR_error_string((long)SSL_get_error(ssl, test), NULL), sock);
+ }
+ return NETDATA_SSL_NO_HANDSHAKE;
+ }
+ }
+ }
+
+ if (SSL_is_init_finished(ssl))
+ {
+ debug(D_WEB_CLIENT_ACCESS,"SSL Handshake finished %s errno %d on socket fd %d", ERR_error_string((long)SSL_get_error(ssl, test), NULL), errno, sock);
+ }
+
+ return 0;
+}
+
+int security_test_certificate(SSL *ssl) {
+ X509* cert = SSL_get_peer_certificate(ssl);
+ int ret;
+ long status;
+ if (!cert) {
+ return -1;
+ }
+
+ status = SSL_get_verify_result(ssl);
+ if((X509_V_OK != status))
+ {
+ char error[512];
+ ERR_error_string_n(ERR_get_error(), error, sizeof(error));
+ error("SSL RFC4158 check: We have a invalid certificate, the tests result with %ld and message %s", status, error);
+ ret = -1;
+ } else {
+ ret = 0;
+ }
+ return ret;
+}
+
+#endif
diff --git a/libnetdata/socket/security.h b/libnetdata/socket/security.h
new file mode 100644
index 000000000..8beb9672f
--- /dev/null
+++ b/libnetdata/socket/security.h
@@ -0,0 +1,47 @@
+#ifndef NETDATA_SECURITY_H
+# define NETDATA_SECURITY_H
+
+# define NETDATA_SSL_HANDSHAKE_COMPLETE 0 //All the steps were successful
+# define NETDATA_SSL_START 1 //Starting handshake, conn variable is NULL
+# define NETDATA_SSL_WANT_READ 2 //The connection wanna read from socket
+# define NETDATA_SSL_WANT_WRITE 4 //The connection wanna write on socket
+# define NETDATA_SSL_NO_HANDSHAKE 8 //Continue without encrypt connection.
+# define NETDATA_SSL_OPTIONAL 16 //Flag to define the HTTP request
+# define NETDATA_SSL_FORCE 32 //We only accepts HTTPS request
+# define NETDATA_SSL_INVALID_CERTIFICATE 64 //Accepts invalid certificate
+# define NETDATA_SSL_VALID_CERTIFICATE 128 //Accepts invalid certificate
+
+#define NETDATA_SSL_CONTEXT_SERVER 0
+#define NETDATA_SSL_CONTEXT_STREAMING 1
+#define NETDATA_SSL_CONTEXT_OPENTSDB 2
+
+# ifdef ENABLE_HTTPS
+
+# include <openssl/ssl.h>
+# include <openssl/err.h>
+# if (SSLEAY_VERSION_NUMBER >= 0x0907000L) && (OPENSSL_VERSION_NUMBER < 0x10100000L)
+# include <openssl/conf.h>
+# endif
+
+struct netdata_ssl{
+ SSL *conn; //SSL connection
+ int flags;
+};
+
+extern SSL_CTX *netdata_opentsdb_ctx;
+extern SSL_CTX *netdata_client_ctx;
+extern SSL_CTX *netdata_srv_ctx;
+extern const char *security_key;
+extern const char *security_cert;
+extern int netdata_use_ssl_on_stream;
+extern int netdata_use_ssl_on_http;
+extern int netdata_validate_server;
+
+void security_openssl_library();
+void security_clean_openssl();
+void security_start_ssl(int selector);
+int security_process_accept(SSL *ssl,int msg);
+int security_test_certificate(SSL *ssl);
+
+# endif //ENABLE_HTTPS
+#endif //NETDATA_SECURITY_H
diff --git a/libnetdata/socket/socket.c b/libnetdata/socket/socket.c
index bf9c60ea1..282710081 100644
--- a/libnetdata/socket/socket.c
+++ b/libnetdata/socket/socket.c
@@ -301,14 +301,39 @@ void listen_sockets_close(LISTEN_SOCKETS *sockets) {
sockets->failed = 0;
}
+WEB_CLIENT_ACL socket_ssl_acl(char *ssl) {
+#ifdef ENABLE_HTTPS
+ if (!strcmp(ssl,"optional")) {
+ netdata_use_ssl_on_http = NETDATA_SSL_OPTIONAL;
+ return WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_REGISTRY | WEB_CLIENT_ACL_BADGE | WEB_CLIENT_ACL_MGMT | WEB_CLIENT_ACL_NETDATACONF | WEB_CLIENT_ACL_STREAMING;
+ }
+ else if (!strcmp(ssl,"force")) {
+ netdata_use_ssl_on_stream = NETDATA_SSL_FORCE;
+ return WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_REGISTRY | WEB_CLIENT_ACL_BADGE | WEB_CLIENT_ACL_MGMT | WEB_CLIENT_ACL_NETDATACONF | WEB_CLIENT_ACL_STREAMING;
+ }
+#endif
+
+ return WEB_CLIENT_ACL_NONE;
+}
+
WEB_CLIENT_ACL read_acl(char *st) {
+ char *ssl = strchr(st,'^');
+ if (ssl) {
+ ssl++;
+ if (!strncmp("SSL=",ssl,4)) {
+ ssl += 4;
+ }
+ socket_ssl_acl(ssl);
+ }
+
if (!strcmp(st,"dashboard")) return WEB_CLIENT_ACL_DASHBOARD;
if (!strcmp(st,"registry")) return WEB_CLIENT_ACL_REGISTRY;
if (!strcmp(st,"badges")) return WEB_CLIENT_ACL_BADGE;
if (!strcmp(st,"management")) return WEB_CLIENT_ACL_MGMT;
if (!strcmp(st,"streaming")) return WEB_CLIENT_ACL_STREAMING;
if (!strcmp(st,"netdata.conf")) return WEB_CLIENT_ACL_NETDATACONF;
- return WEB_CLIENT_ACL_NONE;
+
+ return socket_ssl_acl(st);
}
static inline int bind_to_this(LISTEN_SOCKETS *sockets, const char *definition, uint16_t default_port, int listen_backlog) {
@@ -794,11 +819,15 @@ int connect_to_one_of(const char *destination, int default_port, struct timeval
while(*s) {
const char *e = s;
+ // skip path, moving both s(tart) and e(nd)
+ if(*e == '/')
+ while(!isspace(*e) && *e != ',') s = ++e;
+
// skip separators, moving both s(tart) and e(nd)
while(isspace(*e) || *e == ',') s = ++e;
// move e(nd) to the first separator
- while(*e && !isspace(*e) && *e != ',') e++;
+ while(*e && !isspace(*e) && *e != ',' && *e != '/') e++;
// is there anything?
if(!*s || s == e) break;
@@ -824,7 +853,12 @@ int connect_to_one_of(const char *destination, int default_port, struct timeval
// --------------------------------------------------------------------------------------------------------------------
// helpers to send/receive data in one call, in blocking mode, with a timeout
+#ifdef ENABLE_HTTPS
+ssize_t recv_timeout(struct netdata_ssl *ssl,int sockfd, void *buf, size_t len, int flags, int timeout) {
+#else
ssize_t recv_timeout(int sockfd, void *buf, size_t len, int flags, int timeout) {
+#endif
+
for(;;) {
struct pollfd fd = {
.fd = sockfd,
@@ -852,10 +886,22 @@ ssize_t recv_timeout(int sockfd, void *buf, size_t len, int flags, int timeout)
if(fd.events & POLLIN) break;
}
+#ifdef ENABLE_HTTPS
+ if (ssl->conn) {
+ if (!ssl->flags) {
+ return SSL_read(ssl->conn,buf,len);
+ }
+ }
+#endif
return recv(sockfd, buf, len, flags);
}
+#ifdef ENABLE_HTTPS
+ssize_t send_timeout(struct netdata_ssl *ssl,int sockfd, void *buf, size_t len, int flags, int timeout) {
+#else
ssize_t send_timeout(int sockfd, void *buf, size_t len, int flags, int timeout) {
+#endif
+
for(;;) {
struct pollfd fd = {
.fd = sockfd,
@@ -883,6 +929,13 @@ ssize_t send_timeout(int sockfd, void *buf, size_t len, int flags, int timeout)
if(fd.events & POLLOUT) break;
}
+#ifdef ENABLE_HTTPS
+ if(ssl->conn) {
+ if (!ssl->flags) {
+ return SSL_write(ssl->conn, buf, len);
+ }
+ }
+#endif
return send(sockfd, buf, len, flags);
}
@@ -1291,6 +1344,8 @@ static void poll_events_process(POLLJOB *p, POLLINFO *pi, struct pollfd *pf, sho
do {
char client_ip[NI_MAXHOST + 1];
char client_port[NI_MAXSERV + 1];
+ client_ip[0] = 0x00;
+ client_port[0] = 0x00;
debug(D_POLLFD, "POLLFD: LISTENER: calling accept4() slot %zu (fd %d)", i, fd);
nfd = accept_socket(fd, SOCK_NONBLOCK, client_ip, NI_MAXHOST + 1, client_port, NI_MAXSERV + 1, p->access_list);
diff --git a/libnetdata/socket/socket.h b/libnetdata/socket/socket.h
index c69d4897f..9ea83bcc0 100644
--- a/libnetdata/socket/socket.h
+++ b/libnetdata/socket/socket.h
@@ -51,8 +51,13 @@ extern void listen_sockets_close(LISTEN_SOCKETS *sockets);
extern int connect_to_this(const char *definition, int default_port, struct timeval *timeout);
extern int connect_to_one_of(const char *destination, int default_port, struct timeval *timeout, size_t *reconnects_counter, char *connected_to, size_t connected_to_size);
+#ifdef ENABLE_HTTPS
+extern ssize_t recv_timeout(struct netdata_ssl *ssl,int sockfd, void *buf, size_t len, int flags, int timeout);
+extern ssize_t send_timeout(struct netdata_ssl *ssl,int sockfd, void *buf, size_t len, int flags, int timeout);
+#else
extern ssize_t recv_timeout(int sockfd, void *buf, size_t len, int flags, int timeout);
extern ssize_t send_timeout(int sockfd, void *buf, size_t len, int flags, int timeout);
+#endif
extern int sock_setnonblock(int fd);
extern int sock_delnonblock(int fd);