summaryrefslogtreecommitdiffstats
path: root/libnetdata
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--libnetdata/json/json.c4
-rw-r--r--libnetdata/libnetdata.h2
-rw-r--r--libnetdata/locks/locks.c33
-rw-r--r--libnetdata/popen/popen.c161
-rw-r--r--libnetdata/socket/security.c77
-rw-r--r--libnetdata/socket/security.h5
-rw-r--r--libnetdata/socket/socket.c66
-rw-r--r--libnetdata/socket/socket.h8
-rw-r--r--libnetdata/string/utf8.h9
-rw-r--r--libnetdata/url/url.c334
-rw-r--r--libnetdata/url/url.h7
11 files changed, 563 insertions, 143 deletions
diff --git a/libnetdata/json/json.c b/libnetdata/json/json.c
index c9ff39b05..7c5adca3d 100644
--- a/libnetdata/json/json.c
+++ b/libnetdata/json/json.c
@@ -319,8 +319,8 @@ size_t json_walk_array(char *js, jsmntok_t *t, size_t nest, size_t start, JSON_E
info("JSON: JSON walk_array ignoring element with name:%s fullname:%s",e->name, e->fullname);
continue;
}
- sprintf(ne.name, "%s[%lu]", e->name, i);
- sprintf(ne.fullname, "%s[%lu]", e->fullname, i);
+ snprintfz(ne.name, JSON_NAME_LEN, "%s[%lu]", e->name, i);
+ snprintfz(ne.fullname, JSON_FULLNAME_LEN, "%s[%lu]", e->fullname, i);
switch(t[start].type) {
case JSMN_PRIMITIVE:
diff --git a/libnetdata/libnetdata.h b/libnetdata/libnetdata.h
index 43dc1e04d..ef883300b 100644
--- a/libnetdata/libnetdata.h
+++ b/libnetdata/libnetdata.h
@@ -81,6 +81,7 @@
#include <time.h>
#include <unistd.h>
#include <uuid/uuid.h>
+#include <spawn.h>
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
@@ -312,5 +313,6 @@ extern char *netdata_configured_host_prefix;
#include "url/url.h"
#include "json/json.h"
#include "health/health.h"
+#include "string/utf8.h"
#endif // NETDATA_LIB_H
diff --git a/libnetdata/locks/locks.c b/libnetdata/locks/locks.c
index 91e226902..ca9a5aee9 100644
--- a/libnetdata/locks/locks.c
+++ b/libnetdata/locks/locks.c
@@ -82,7 +82,8 @@ int __netdata_mutex_unlock(netdata_mutex_t *mutex) {
return ret;
}
-int netdata_mutex_init_debug( const char *file, const char *function, const unsigned long line, netdata_mutex_t *mutex) {
+int netdata_mutex_init_debug(const char *file __maybe_unused, const char *function __maybe_unused,
+ const unsigned long line __maybe_unused, netdata_mutex_t *mutex) {
usec_t start = 0;
(void)start;
@@ -98,7 +99,8 @@ int netdata_mutex_init_debug( const char *file, const char *function, const unsi
return ret;
}
-int netdata_mutex_lock_debug( const char *file, const char *function, const unsigned long line, netdata_mutex_t *mutex) {
+int netdata_mutex_lock_debug(const char *file __maybe_unused, const char *function __maybe_unused,
+ const unsigned long line __maybe_unused, netdata_mutex_t *mutex) {
usec_t start = 0;
(void)start;
@@ -114,7 +116,8 @@ int netdata_mutex_lock_debug( const char *file, const char *function, const unsi
return ret;
}
-int netdata_mutex_trylock_debug( const char *file, const char *function, const unsigned long line, netdata_mutex_t *mutex) {
+int netdata_mutex_trylock_debug(const char *file __maybe_unused, const char *function __maybe_unused,
+ const unsigned long line __maybe_unused, netdata_mutex_t *mutex) {
usec_t start = 0;
(void)start;
@@ -130,7 +133,8 @@ int netdata_mutex_trylock_debug( const char *file, const char *function, const u
return ret;
}
-int netdata_mutex_unlock_debug( const char *file, const char *function, const unsigned long line, netdata_mutex_t *mutex) {
+int netdata_mutex_unlock_debug(const char *file __maybe_unused, const char *function __maybe_unused,
+ const unsigned long line __maybe_unused, netdata_mutex_t *mutex) {
usec_t start = 0;
(void)start;
@@ -219,7 +223,8 @@ int __netdata_rwlock_trywrlock(netdata_rwlock_t *rwlock) {
}
-int netdata_rwlock_destroy_debug( const char *file, const char *function, const unsigned long line, netdata_rwlock_t *rwlock) {
+int netdata_rwlock_destroy_debug(const char *file __maybe_unused, const char *function __maybe_unused,
+ const unsigned long line __maybe_unused, netdata_rwlock_t *rwlock) {
usec_t start = 0;
(void)start;
@@ -235,7 +240,8 @@ int netdata_rwlock_destroy_debug( const char *file, const char *function, const
return ret;
}
-int netdata_rwlock_init_debug( const char *file, const char *function, const unsigned long line, netdata_rwlock_t *rwlock) {
+int netdata_rwlock_init_debug(const char *file __maybe_unused, const char *function __maybe_unused,
+ const unsigned long line __maybe_unused, netdata_rwlock_t *rwlock) {
usec_t start = 0;
(void)start;
@@ -251,7 +257,8 @@ int netdata_rwlock_init_debug( const char *file, const char *function, const uns
return ret;
}
-int netdata_rwlock_rdlock_debug( const char *file, const char *function, const unsigned long line, netdata_rwlock_t *rwlock) {
+int netdata_rwlock_rdlock_debug(const char *file __maybe_unused, const char *function __maybe_unused,
+ const unsigned long line __maybe_unused, netdata_rwlock_t *rwlock) {
usec_t start = 0;
(void)start;
@@ -267,7 +274,8 @@ int netdata_rwlock_rdlock_debug( const char *file, const char *function, const u
return ret;
}
-int netdata_rwlock_wrlock_debug( const char *file, const char *function, const unsigned long line, netdata_rwlock_t *rwlock) {
+int netdata_rwlock_wrlock_debug(const char *file __maybe_unused, const char *function __maybe_unused,
+ const unsigned long line __maybe_unused, netdata_rwlock_t *rwlock) {
usec_t start = 0;
(void)start;
@@ -283,7 +291,8 @@ int netdata_rwlock_wrlock_debug( const char *file, const char *function, const u
return ret;
}
-int netdata_rwlock_unlock_debug( const char *file, const char *function, const unsigned long line, netdata_rwlock_t *rwlock) {
+int netdata_rwlock_unlock_debug(const char *file __maybe_unused, const char *function __maybe_unused,
+ const unsigned long line __maybe_unused, netdata_rwlock_t *rwlock) {
usec_t start = 0;
(void)start;
@@ -299,7 +308,8 @@ int netdata_rwlock_unlock_debug( const char *file, const char *function, const u
return ret;
}
-int netdata_rwlock_tryrdlock_debug( const char *file, const char *function, const unsigned long line, netdata_rwlock_t *rwlock) {
+int netdata_rwlock_tryrdlock_debug(const char *file __maybe_unused, const char *function __maybe_unused,
+ const unsigned long line __maybe_unused, netdata_rwlock_t *rwlock) {
usec_t start = 0;
(void)start;
@@ -315,7 +325,8 @@ int netdata_rwlock_tryrdlock_debug( const char *file, const char *function, cons
return ret;
}
-int netdata_rwlock_trywrlock_debug( const char *file, const char *function, const unsigned long line, netdata_rwlock_t *rwlock) {
+int netdata_rwlock_trywrlock_debug(const char *file __maybe_unused, const char *function __maybe_unused,
+ const unsigned long line __maybe_unused, netdata_rwlock_t *rwlock) {
usec_t start = 0;
(void)start;
diff --git a/libnetdata/popen/popen.c b/libnetdata/popen/popen.c
index 845363fd2..177aebfc0 100644
--- a/libnetdata/popen/popen.c
+++ b/libnetdata/popen/popen.c
@@ -45,110 +45,91 @@ static void mypopen_del(FILE *fp) {
#define PIPE_READ 0
#define PIPE_WRITE 1
-FILE *mypopen(const char *command, volatile pid_t *pidptr)
-{
- int pipefd[2];
-
- if(pipe(pipefd) == -1) return NULL;
+static inline FILE *custom_popene(const char *command, volatile pid_t *pidptr, char **env) {
+ FILE *fp;
+ int pipefd[2], error;
+ pid_t pid;
+ char *const spawn_argv[] = {
+ "sh",
+ "-c",
+ (char *)command,
+ NULL
+ };
+ posix_spawnattr_t attr;
+ posix_spawn_file_actions_t fa;
- int pid = fork();
- if(pid == -1) {
- close(pipefd[PIPE_READ]);
- close(pipefd[PIPE_WRITE]);
+ if(pipe(pipefd) == -1)
return NULL;
+ if ((fp = fdopen(pipefd[PIPE_READ], "r")) == NULL) {
+ goto error_after_pipe;
}
- if(pid != 0) {
- // the parent
- *pidptr = pid;
- close(pipefd[PIPE_WRITE]);
- FILE *fp = fdopen(pipefd[PIPE_READ], "r");
- /*mypopen_add(fp, pid);*/
- return(fp);
- }
- // the child
- // close all files
+ // Mark all files to be closed by the exec() stage of posix_spawn()
int i;
for(i = (int) (sysconf(_SC_OPEN_MAX) - 1); i >= 0; i--)
- if(i != STDIN_FILENO && i != STDERR_FILENO && i != pipefd[PIPE_WRITE]) close(i);
-
- // move the pipe to stdout
- if(pipefd[PIPE_WRITE] != STDOUT_FILENO) {
- dup2(pipefd[PIPE_WRITE], STDOUT_FILENO);
- close(pipefd[PIPE_WRITE]);
+ if(i != STDIN_FILENO && i != STDERR_FILENO)
+ fcntl(i, F_SETFD, FD_CLOEXEC);
+
+ if (!posix_spawn_file_actions_init(&fa)) {
+ // move the pipe to stdout in the child
+ if (posix_spawn_file_actions_adddup2(&fa, pipefd[PIPE_WRITE], STDOUT_FILENO)) {
+ error("posix_spawn_file_actions_adddup2() failed");
+ goto error_after_posix_spawn_file_actions_init;
+ }
+ } else {
+ error("posix_spawn_file_actions_init() failed.");
+ goto error_after_pipe;
}
-
-#ifdef DETACH_PLUGINS_FROM_NETDATA
- // this was an attempt to detach the child and use the suspend mode charts.d
- // unfortunatelly it does not work as expected.
-
- // fork again to become session leader
- pid = fork();
- if(pid == -1)
- error("pre-execution of command '%s' on pid %d: Cannot fork 2nd time.", command, getpid());
-
- if(pid != 0) {
- // the parent
- exit(0);
+ if (!(error = posix_spawnattr_init(&attr))) {
+ // reset all signals in the child
+ sigset_t mask;
+
+ if (posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF))
+ error("posix_spawnattr_setflags() failed.");
+ sigemptyset(&mask);
+ if (posix_spawnattr_setsigmask(&attr, &mask))
+ error("posix_spawnattr_setsigmask() failed.");
+ } else {
+ error("posix_spawnattr_init() failed.");
}
-
- // set a new process group id for just this child
- if( setpgid(0, 0) != 0 )
- error("pre-execution of command '%s' on pid %d: Cannot set a new process group.", command, getpid());
-
- if( getpgid(0) != getpid() )
- error("pre-execution of command '%s' on pid %d: Cannot set a new process group. Process group set is incorrect. Expected %d, found %d", command, getpid(), getpid(), getpgid(0));
-
- if( setsid() != 0 )
- error("pre-execution of command '%s' on pid %d: Cannot set session id.", command, getpid());
-
- fprintf(stdout, "MYPID %d\n", getpid());
- fflush(NULL);
-#endif
-
- // reset all signals
- signals_unblock();
- signals_reset();
-
- debug(D_CHILDS, "executing command: '%s' on pid %d.", command, getpid());
- execl("/bin/sh", "sh", "-c", command, NULL);
- exit(1);
-}
-
-FILE *mypopene(const char *command, volatile pid_t *pidptr, char **env) {
- int pipefd[2];
-
- if(pipe(pipefd) == -1)
- return NULL;
-
- int pid = fork();
- if(pid == -1) {
+ if (!posix_spawn(&pid, "/bin/sh", &fa, &attr, spawn_argv, env)) {
+ *pidptr = pid;
+ debug(D_CHILDS, "Spawned command: '%s' on pid %d from parent pid %d.", command, pid, getpid());
+ } else {
+ error("Failed to spawn command: '%s' from parent pid %d.", command, getpid());
close(pipefd[PIPE_READ]);
- close(pipefd[PIPE_WRITE]);
- return NULL;
+ fp = NULL;
}
- if(pid != 0) {
- // the parent
- *pidptr = pid;
- close(pipefd[PIPE_WRITE]);
- FILE *fp = fdopen(pipefd[PIPE_READ], "r");
- return(fp);
+ close(pipefd[PIPE_WRITE]);
+
+ if (!error) {
+ // posix_spawnattr_init() succeeded
+ if (posix_spawnattr_destroy(&attr))
+ error("posix_spawnattr_destroy");
}
- // the child
+ if (posix_spawn_file_actions_destroy(&fa))
+ error("posix_spawn_file_actions_destroy");
+
+ return fp;
+
+error_after_posix_spawn_file_actions_init:
+ if (posix_spawn_file_actions_destroy(&fa))
+ error("posix_spawn_file_actions_destroy");
+error_after_pipe:
+ close(pipefd[PIPE_READ]);
+ close(pipefd[PIPE_WRITE]);
+ return NULL;
+}
- // close all files
- int i;
- for(i = (int) (sysconf(_SC_OPEN_MAX) - 1); i >= 0; i--)
- if(i != STDIN_FILENO && i != STDERR_FILENO && i != pipefd[PIPE_WRITE]) close(i);
+// See man environ
+extern char **environ;
- // move the pipe to stdout
- if(pipefd[PIPE_WRITE] != STDOUT_FILENO) {
- dup2(pipefd[PIPE_WRITE], STDOUT_FILENO);
- close(pipefd[PIPE_WRITE]);
- }
+FILE *mypopen(const char *command, volatile pid_t *pidptr) {
+ return custom_popene(command, pidptr, environ);
+}
- execle("/bin/sh", "sh", "-c", command, NULL, env);
- exit(1);
+FILE *mypopene(const char *command, volatile pid_t *pidptr, char **env) {
+ return custom_popene(command, pidptr, env);
}
int mypclose(FILE *fp, pid_t pid) {
diff --git a/libnetdata/socket/security.c b/libnetdata/socket/security.c
index dcbd3f650..ab324a169 100644
--- a/libnetdata/socket/security.c
+++ b/libnetdata/socket/security.c
@@ -7,8 +7,6 @@ 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;
/**
@@ -20,7 +18,7 @@ int netdata_validate_server = NETDATA_SSL_VALID_CERTIFICATE;
* @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) {
+static void security_info_callback(const SSL *ssl, int where, int ret __maybe_unused) {
(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));
@@ -166,7 +164,7 @@ 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)) {
+ 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;
}
@@ -176,6 +174,9 @@ void security_start_ssl(int selector) {
}
case NETDATA_SSL_CONTEXT_STREAMING: {
netdata_client_ctx = security_initialize_openssl_client();
+ //This is necessary for the stream, because it is working sometimes with nonblock socket.
+ //It returns the bitmask afte to change, there is not any description of errors in the documentation
+ SSL_CTX_set_mode(netdata_client_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE |SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER |SSL_MODE_AUTO_RETRY);
break;
}
case NETDATA_SSL_CONTEXT_OPENTSDB: {
@@ -185,6 +186,11 @@ void security_start_ssl(int selector) {
}
}
+/**
+ * Clean Open SSL
+ *
+ * Clean all the allocated contexts from netdata.
+ */
void security_clean_openssl() {
if (netdata_srv_ctx)
{
@@ -206,6 +212,17 @@ void security_clean_openssl() {
#endif
}
+/**
+ * Process accept
+ *
+ * Process the SSL handshake with the client case it is necessary.
+ *
+ * @param ssl is a pointer for the SSL structure
+ * @param msg is a copy of the first 8 bytes of the initial message received
+ *
+ * @return it returns 0 case it performs the handshake, 8 case it is clean connection
+ * and another integer power of 2 otherwise.
+ */
int security_process_accept(SSL *ssl,int msg) {
int sock = SSL_get_fd(ssl);
int test;
@@ -250,9 +267,18 @@ int security_process_accept(SSL *ssl,int msg) {
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;
+ return NETDATA_SSL_HANDSHAKE_COMPLETE;
}
+/**
+ * Test Certificate
+ *
+ * Check the certificate of Netdata master
+ *
+ * @param ssl is the connection structure
+ *
+ * @return It returns 0 on success and -1 otherwise
+ */
int security_test_certificate(SSL *ssl) {
X509* cert = SSL_get_peer_certificate(ssl);
int ret;
@@ -271,7 +297,48 @@ int security_test_certificate(SSL *ssl) {
} else {
ret = 0;
}
+
return ret;
}
+/**
+ * Location for context
+ *
+ * Case the user give us a directory with the certificates available and
+ * the Netdata master certificate, we use this function to validate the certificate.
+ *
+ * @param ctx the context where the path will be set.
+ * @param file the file with Netdata master certificate.
+ * @param path the directory where the certificates are stored.
+ *
+ * @return It returns 0 on success and -1 otherwise.
+ */
+int security_location_for_context(SSL_CTX *ctx, char *file, char *path) {
+ struct stat statbuf;
+ if (stat(file, &statbuf)) {
+ info("Netdata does not have a SSL master certificate, so it will use the default OpenSSL configuration to validate certificates!");
+ return 0;
+ }
+
+ ERR_clear_error();
+ u_long err;
+ char buf[256];
+ if(!SSL_CTX_load_verify_locations(ctx, file, path)) {
+ goto slfc;
+ }
+
+ if(!SSL_CTX_set_default_verify_paths(ctx)) {
+ goto slfc;
+ }
+
+ return 0;
+
+slfc:
+ while ((err = ERR_get_error()) != 0) {
+ ERR_error_string_n(err, buf, sizeof(buf));
+ error("Cannot set the directory for the certificates and the master SSL certificate: %s",buf);
+ }
+ return -1;
+}
+
#endif
diff --git a/libnetdata/socket/security.h b/libnetdata/socket/security.h
index 8beb9672f..697e0fda1 100644
--- a/libnetdata/socket/security.h
+++ b/libnetdata/socket/security.h
@@ -25,7 +25,7 @@
struct netdata_ssl{
SSL *conn; //SSL connection
- int flags;
+ int flags; //The flags for SSL connection
};
extern SSL_CTX *netdata_opentsdb_ctx;
@@ -33,9 +33,8 @@ 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;
+extern int security_location_for_context(SSL_CTX *ctx,char *file,char *path);
void security_openssl_library();
void security_clean_openssl();
diff --git a/libnetdata/socket/socket.c b/libnetdata/socket/socket.c
index 282710081..22abb47f4 100644
--- a/libnetdata/socket/socket.c
+++ b/libnetdata/socket/socket.c
@@ -301,39 +301,47 @@ void listen_sockets_close(LISTEN_SOCKETS *sockets) {
sockets->failed = 0;
}
-WEB_CLIENT_ACL socket_ssl_acl(char *ssl) {
+/*
+ * SSL ACL
+ *
+ * Search the SSL acl and apply it case it is set.
+ *
+ * @param acl is the acl given by the user.
+ */
+WEB_CLIENT_ACL socket_ssl_acl(char *acl) {
+ char *ssl = strchr(acl,'^');
+ if(ssl) {
+ //Due the format of the SSL command it is always the last command,
+ //we finish it here to avoid problems with the ACLs
+ *ssl = '\0';
#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;
- }
+ ssl++;
+ if (!strncmp("SSL=",ssl,4)) {
+ ssl += 4;
+ if (!strcmp(ssl,"optional")) {
+ return WEB_CLIENT_ACL_SSL_OPTIONAL;
+ }
+ else if (!strcmp(ssl,"force")) {
+ return WEB_CLIENT_ACL_SSL_FORCE;
+ }
+ }
#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);
- }
+ WEB_CLIENT_ACL ret = socket_ssl_acl(st);
- 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;
+ if (!strcmp(st,"dashboard")) ret |= WEB_CLIENT_ACL_DASHBOARD;
+ if (!strcmp(st,"registry")) ret |= WEB_CLIENT_ACL_REGISTRY;
+ if (!strcmp(st,"badges")) ret |= WEB_CLIENT_ACL_BADGE;
+ if (!strcmp(st,"management")) ret |= WEB_CLIENT_ACL_MGMT;
+ if (!strcmp(st,"streaming")) ret |= WEB_CLIENT_ACL_STREAMING;
+ if (!strcmp(st,"netdata.conf")) ret |= WEB_CLIENT_ACL_NETDATACONF;
- return socket_ssl_acl(st);
+ return ret;
}
static inline int bind_to_this(LISTEN_SOCKETS *sockets, const char *definition, uint16_t default_port, int listen_backlog) {
@@ -375,7 +383,7 @@ static inline int bind_to_this(LISTEN_SOCKETS *sockets, const char *definition,
error("LISTENER: Cannot create unix socket '%s'", path);
sockets->failed++;
} else {
- acl_flags = WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_REGISTRY | WEB_CLIENT_ACL_BADGE | WEB_CLIENT_ACL_MGMT | WEB_CLIENT_ACL_NETDATACONF | WEB_CLIENT_ACL_STREAMING;
+ acl_flags = WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_REGISTRY | WEB_CLIENT_ACL_BADGE | WEB_CLIENT_ACL_MGMT | WEB_CLIENT_ACL_NETDATACONF | WEB_CLIENT_ACL_STREAMING | WEB_CLIENT_ACL_SSL_DEFAULT;
listen_sockets_add(sockets, fd, AF_UNIX, socktype, protocol_str, path, 0, acl_flags);
added++;
}
@@ -425,7 +433,13 @@ static inline int bind_to_this(LISTEN_SOCKETS *sockets, const char *definition,
}
acl_flags |= read_acl(portconfig);
} else {
- acl_flags = WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_REGISTRY | WEB_CLIENT_ACL_BADGE | WEB_CLIENT_ACL_MGMT | WEB_CLIENT_ACL_NETDATACONF | WEB_CLIENT_ACL_STREAMING;
+ acl_flags = WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_REGISTRY | WEB_CLIENT_ACL_BADGE | WEB_CLIENT_ACL_MGMT | WEB_CLIENT_ACL_NETDATACONF | WEB_CLIENT_ACL_STREAMING | WEB_CLIENT_ACL_SSL_DEFAULT;
+ }
+
+ //Case the user does not set the option SSL in the "bind to", but he has
+ //the certificates, I must redirect, so I am assuming here the default option
+ if(!(acl_flags & WEB_CLIENT_ACL_SSL_OPTIONAL) && !(acl_flags & WEB_CLIENT_ACL_SSL_FORCE)) {
+ acl_flags |= WEB_CLIENT_ACL_SSL_DEFAULT;
}
uint32_t scope_id = 0;
diff --git a/libnetdata/socket/socket.h b/libnetdata/socket/socket.h
index 9ea83bcc0..76b15def5 100644
--- a/libnetdata/socket/socket.h
+++ b/libnetdata/socket/socket.h
@@ -17,7 +17,10 @@ typedef enum web_client_acl {
WEB_CLIENT_ACL_BADGE = 1 << 2,
WEB_CLIENT_ACL_MGMT = 1 << 3,
WEB_CLIENT_ACL_STREAMING = 1 << 4,
- WEB_CLIENT_ACL_NETDATACONF = 1 << 5
+ WEB_CLIENT_ACL_NETDATACONF = 1 << 5,
+ WEB_CLIENT_ACL_SSL_OPTIONAL = 1 << 6,
+ WEB_CLIENT_ACL_SSL_FORCE = 1 << 7,
+ WEB_CLIENT_ACL_SSL_DEFAULT = 1 << 8
} WEB_CLIENT_ACL;
#define web_client_can_access_dashboard(w) ((w)->acl & WEB_CLIENT_ACL_DASHBOARD)
@@ -26,6 +29,9 @@ typedef enum web_client_acl {
#define web_client_can_access_mgmt(w) ((w)->acl & WEB_CLIENT_ACL_MGMT)
#define web_client_can_access_stream(w) ((w)->acl & WEB_CLIENT_ACL_STREAMING)
#define web_client_can_access_netdataconf(w) ((w)->acl & WEB_CLIENT_ACL_NETDATACONF)
+#define web_client_is_using_ssl_optional(w) ((w)->port_acl & WEB_CLIENT_ACL_SSL_OPTIONAL)
+#define web_client_is_using_ssl_force(w) ((w)->port_acl & WEB_CLIENT_ACL_SSL_FORCE)
+#define web_client_is_using_ssl_default(w) ((w)->port_acl & WEB_CLIENT_ACL_SSL_DEFAULT)
typedef struct listen_sockets {
struct config *config; // the config file to use
diff --git a/libnetdata/string/utf8.h b/libnetdata/string/utf8.h
new file mode 100644
index 000000000..133ec710b
--- /dev/null
+++ b/libnetdata/string/utf8.h
@@ -0,0 +1,9 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#ifndef NETDATA_STRING_UTF8_H
+#define NETDATA_STRING_UTF8_H 1
+
+#define IS_UTF8_BYTE(x) (x & 0x80)
+#define IS_UTF8_STARTBYTE(x) (IS_UTF8_BYTE(x)&&(x & 0x40))
+
+#endif /* NETDATA_STRING_UTF8_H */
diff --git a/libnetdata/url/url.c b/libnetdata/url/url.c
index 07a9f8069..7df9faaf0 100644
--- a/libnetdata/url/url.c
+++ b/libnetdata/url/url.c
@@ -43,8 +43,16 @@ char *url_encode(char *str) {
return pbuf;
}
-/* Returns a url-decoded version of str */
-/* IMPORTANT: be sure to free() the returned string after use */
+/**
+ * URL Decode
+ *
+ * Returns a url-decoded version of str
+ * IMPORTANT: be sure to free() the returned string after use
+ *
+ * @param str the string that will be decode
+ *
+ * @return a pointer for the url decoded.
+ */
char *url_decode(char *str) {
size_t size = strlen(str) + 1;
@@ -52,6 +60,149 @@ char *url_decode(char *str) {
return url_decode_r(buf, str, size);
}
+/**
+ * Percentage escape decode
+ *
+ * Decode %XX character or return 0 if cannot
+ *
+ * @param s the string to decode
+ *
+ * @return The character decoded on success and 0 otherwise
+ */
+char url_percent_escape_decode(char *s) {
+ if(likely(s[1] && s[2]))
+ return from_hex(s[1]) << 4 | from_hex(s[2]);
+ return 0;
+}
+
+/**
+ * Get byte length
+ *
+ * This (utf8 string related) should be moved in separate file in future
+ *
+ * @param c is the utf8 character
+ * *
+ * @return It reurns the length of the specific character.
+ */
+char url_utf8_get_byte_length(char c) {
+ if(!IS_UTF8_BYTE(c))
+ return 1;
+
+ char length = 0;
+ while(likely(c & 0x80)) {
+ length++;
+ c <<= 1;
+ }
+ //4 byte is max size for UTF-8 char
+ //10XX XXXX is not valid character -> check length == 1
+ if(length > 4 || length == 1)
+ return -1;
+
+ return length;
+}
+
+/**
+ * Decode Multibyte UTF8
+ *
+ * Decode % encoded UTF-8 characters and copy them to *d
+ *
+ * @param s first address
+ * @param d
+ * @param d_end last address
+ *
+ * @return count of bytes written to *d
+ */
+char url_decode_multibyte_utf8(char *s, char *d, char *d_end) {
+ char first_byte = url_percent_escape_decode(s);
+
+ if(unlikely(!first_byte || !IS_UTF8_STARTBYTE(first_byte)))
+ return 0;
+
+ char byte_length = url_utf8_get_byte_length(first_byte);
+
+ if(unlikely(byte_length <= 0 || d+byte_length >= d_end))
+ return 0;
+
+ char to_read = byte_length;
+ while(to_read > 0) {
+ char c = url_percent_escape_decode(s);
+
+ if(unlikely( !IS_UTF8_BYTE(c) ))
+ return 0;
+ if((to_read != byte_length) && IS_UTF8_STARTBYTE(c))
+ return 0;
+
+ *d++ = c;
+ s+=3;
+ to_read--;
+ }
+
+ return byte_length;
+}
+
+/*
+ * The utf8_check() function scans the '\0'-terminated string starting
+ * at s. It returns a pointer to the first byte of the first malformed
+ * or overlong UTF-8 sequence found, or NULL if the string contains
+ * only correct UTF-8. It also spots UTF-8 sequences that could cause
+ * trouble if converted to UTF-16, namely surrogate characters
+ * (U+D800..U+DFFF) and non-Unicode positions (U+FFFE..U+FFFF). This
+ * routine is very likely to find a malformed sequence if the input
+ * uses any other encoding than UTF-8. It therefore can be used as a
+ * very effective heuristic for distinguishing between UTF-8 and other
+ * encodings.
+ *
+ * Markus Kuhn <http://www.cl.cam.ac.uk/~mgk25/> -- 2005-03-30
+ * License: http://www.cl.cam.ac.uk/~mgk25/short-license.html
+ */
+unsigned char *utf8_check(unsigned char *s)
+{
+ while (*s)
+ {
+ if (*s < 0x80)
+ /* 0xxxxxxx */
+ s++;
+ else if ((s[0] & 0xe0) == 0xc0)
+ {
+ /* 110XXXXx 10xxxxxx */
+ if ((s[1] & 0xc0) != 0x80 ||
+ (s[0] & 0xfe) == 0xc0) /* overlong? */
+ return s;
+ else
+ s += 2;
+ }
+ else if ((s[0] & 0xf0) == 0xe0)
+ {
+ /* 1110XXXX 10Xxxxxx 10xxxxxx */
+ if ((s[1] & 0xc0) != 0x80 ||
+ (s[2] & 0xc0) != 0x80 ||
+ (s[0] == 0xe0 && (s[1] & 0xe0) == 0x80) || /* overlong? */
+ (s[0] == 0xed && (s[1] & 0xe0) == 0xa0) || /* surrogate? */
+ (s[0] == 0xef && s[1] == 0xbf &&
+ (s[2] & 0xfe) == 0xbe)) /* U+FFFE or U+FFFF? */
+ return s;
+ else
+ s += 3;
+ }
+ else if ((s[0] & 0xf8) == 0xf0)
+ {
+ /* 11110XXX 10XXxxxx 10xxxxxx 10xxxxxx */
+ if ((s[1] & 0xc0) != 0x80 ||
+ (s[2] & 0xc0) != 0x80 ||
+ (s[3] & 0xc0) != 0x80 ||
+ (s[0] == 0xf0 && (s[1] & 0xf0) == 0x80) || /* overlong? */
+ (s[0] == 0xf4 && s[1] > 0x8f) || s[0] > 0xf4) /* > U+10FFFF? */
+ return s;
+ else
+ s += 4;
+ }
+ else
+ return s;
+ }
+
+ return NULL;
+}
+
char *url_decode_r(char *to, char *url, size_t size) {
char *s = url, // source
*d = to, // destination
@@ -59,12 +210,24 @@ char *url_decode_r(char *to, char *url, size_t size) {
while(*s && d < e) {
if(unlikely(*s == '%')) {
- if(likely(s[1] && s[2])) {
- char t = from_hex(s[1]) << 4 | from_hex(s[2]);
+ char t = url_percent_escape_decode(s);
+ if(IS_UTF8_BYTE(t)) {
+ char bytes_written = url_decode_multibyte_utf8(s, d, e);
+ if(likely(bytes_written)){
+ d += bytes_written;
+ s += (bytes_written * 3)-1;
+ }
+ else {
+ goto fail_cleanup;
+ }
+ }
+ else if(likely(t) && isprint(t)) {
// avoid HTTP header injection
- *d++ = (char)((isprint(t))? t : ' ');
+ *d++ = t;
s += 2;
}
+ else
+ goto fail_cleanup;
}
else if(unlikely(*s == '+'))
*d++ = ' ';
@@ -77,5 +240,166 @@ char *url_decode_r(char *to, char *url, size_t size) {
*d = '\0';
+ if(unlikely( utf8_check((unsigned char *)to) )) //NULL means sucess here
+ return NULL;
+
return to;
+
+fail_cleanup:
+ *d = '\0';
+ return NULL;
+}
+
+/**
+ * Is request complete?
+ *
+ * Check whether the request is complete.
+ * This function cannot check all the requests METHODS, for example, case you are working with POST, it will fail.
+ *
+ * @param begin is the first character of the sequence to analyse.
+ * @param end is the last character of the sequence
+ * @param length is the length of the total of bytes read, it is not the difference between end and begin.
+ *
+ * @return It returns 1 when the request is complete and 0 otherwise.
+ */
+inline int url_is_request_complete(char *begin, char *end, size_t length) {
+
+ if ( begin == end) {
+ //Message cannot be complete when first and last address are the same
+ return 0;
+ }
+
+ //This math to verify the last is valid, because we are discarding the POST
+ if (length > 4) {
+ begin = end - 4;
+ }
+
+ return (strstr(begin, "\r\n\r\n"))?1:0;
+}
+
+/**
+ * Find protocol
+ *
+ * Search for the string ' HTTP/' in the message given.
+ *
+ * @param s is the start of the user request.
+ * @return
+ */
+inline char *url_find_protocol(char *s) {
+ while(*s) {
+ // find the next space
+ while (*s && *s != ' ') s++;
+
+ // is it SPACE + "HTTP/" ?
+ if(*s && !strncmp(s, " HTTP/", 6)) break;
+ else s++;
+ }
+
+ return s;
+}
+
+/**
+ * Map query string
+ *
+ * Map the query string fields that will be decoded.
+ * This functions must be called after to check the presence of query strings,
+ * here we are assuming that you already tested this.
+ *
+ * @param out the pointer to pointers that will be used to map
+ * @param url the input url that we are decoding.
+ *
+ * @return It returns the number of total variables in the query string.
+ */
+int url_map_query_string(char **out, char *url) {
+ (void)out;
+ (void)url;
+ int count = 0;
+
+ //First we try to parse considering that there was not URL encode process
+ char *moveme = url;
+ char *ptr;
+
+ //We always we have at least one here, so I can set this.
+ out[count++] = moveme;
+ while(moveme) {
+ ptr = strchr((moveme+1), '&');
+ if(ptr) {
+ out[count++] = ptr;
+ }
+
+ moveme = ptr;
+ }
+
+ //I could not find any '&', so I am assuming now it is like '%26'
+ if (count == 1) {
+ moveme = url;
+ while(moveme) {
+ ptr = strchr((moveme+1), '%');
+ if(ptr) {
+ char *test = (ptr+1);
+ if (!strncmp(test, "3f", 2) || !strncmp(test, "3F", 2)) {
+ out[count++] = ptr;
+ }
+ }
+ moveme = ptr;
+ }
+ }
+
+ return count;
+}
+
+/**
+ * Parse query string
+ *
+ * Parse the query string mapped and store it inside output.
+ *
+ * @param output is a vector where I will store the string.
+ * @param max is the maximum length of the output
+ * @param map the map done by the function url_map_query_string.
+ * @param total the total number of variables inside map
+ *
+ * @return It returns 0 on success and -1 otherwise
+ */
+int url_parse_query_string(char *output, size_t max, char **map, int total) {
+ if(!total) {
+ return 0;
+ }
+
+ int counter, next;
+ size_t length;
+ char *end;
+ char *begin = map[0];
+ char save;
+ size_t copied = 0;
+ for(counter = 0, next=1 ; next <= total ; ++counter, ++next) {
+ if (next != total) {
+ end = map[next];
+ length = (size_t) (end - begin);
+ save = *end;
+ *end = 0x00;
+ } else {
+ length = strlen(begin);
+ end = NULL;
+ }
+ length++;
+
+ if (length > (max - copied)) {
+ error("Parsing query string: we cannot parse a query string so big");
+ break;
+ }
+
+ if(!url_decode_r(output, begin, length)) {
+ return -1;
+ }
+ length = strlen(output);
+ copied += length;
+ output += length;
+
+ begin = end;
+ if (begin) {
+ *begin = save;
+ }
+ }
+
+ return 0;
}
diff --git a/libnetdata/url/url.h b/libnetdata/url/url.h
index 6cef6d7a8..10f3fe176 100644
--- a/libnetdata/url/url.h
+++ b/libnetdata/url/url.h
@@ -25,4 +25,11 @@ extern char *url_decode(char *str);
extern char *url_decode_r(char *to, char *url, size_t size);
+#define WEB_FIELDS_MAX 400
+extern int url_map_query_string(char **out, char *url);
+extern int url_parse_query_string(char *output, size_t max, char **map, int total);
+
+extern int url_is_request_complete(char *begin,char *end,size_t length);
+extern char *url_find_protocol(char *s);
+
#endif /* NETDATA_URL_H */