diff options
Diffstat (limited to '')
-rw-r--r-- | libnetdata/json/json.c | 4 | ||||
-rw-r--r-- | libnetdata/libnetdata.h | 2 | ||||
-rw-r--r-- | libnetdata/locks/locks.c | 33 | ||||
-rw-r--r-- | libnetdata/popen/popen.c | 161 | ||||
-rw-r--r-- | libnetdata/socket/security.c | 77 | ||||
-rw-r--r-- | libnetdata/socket/security.h | 5 | ||||
-rw-r--r-- | libnetdata/socket/socket.c | 66 | ||||
-rw-r--r-- | libnetdata/socket/socket.h | 8 | ||||
-rw-r--r-- | libnetdata/string/utf8.h | 9 | ||||
-rw-r--r-- | libnetdata/url/url.c | 334 | ||||
-rw-r--r-- | libnetdata/url/url.h | 7 |
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 */ |