diff --git a/src/framework/eventloop.cpp b/src/framework/eventloop.cpp index 6bf20e5..4273048 100644 --- a/src/framework/eventloop.cpp +++ b/src/framework/eventloop.cpp @@ -31,6 +31,9 @@ void EventLoop::signalHandler(int signum) { #include "socketreceiver.h" #include "workerprocess.h" +#include +#include +#include #ifdef USE_THREADS thread_local @@ -79,9 +82,30 @@ int EventLoop::externalCommand(Task *owner, const char *const argv[]) { return -1; } if (!chld) { - // TODO: Close all file descriptors in child! + // Close all file descriptors >= 3 in the child before exec + #if defined(__linux__) && !defined(_WIN32) + // Best effort: use /proc/self/fd + { + DIR *d = opendir("/proc/self/fd"); + if (d) { + int dirfd_no = dirfd(d); + struct dirent *de; + while ((de = readdir(d))) { + int fd = atoi(de->d_name); + if (fd > 2 && fd != dirfd_no) close(fd); + } + closedir(d); + } + } + #else + // Fallback: close up to RLIMIT_NOFILE + struct rlimit rl; + if (getrlimit(RLIMIT_NOFILE, &rl) == 0) { + for (int fd = 3; fd < (int)rl.rlim_cur; ++fd) close(fd); + } + #endif int ret = execvp( argv[0], const_cast(argv) ); - exit(ret); + _exit(ret); } pidOwner[chld] = owner; return chld; diff --git a/src/framework/mk.inc b/src/framework/mk.inc index 2819fd7..7637323 100644 --- a/src/framework/mk.inc +++ b/src/framework/mk.inc @@ -56,8 +56,8 @@ CXXFLAGS += --pedantic -Wextra endif ifeq ($(GNUTLS),1) -CXXFLAGS += -DUSE_GNUTLS -I/usr/local/include -LIBS += -L/usr/local/lib -lgnutls +CXXFLAGS += -DUSE_GNUTLS $(shell pkg-config --cflags gnutls) +LIBS += $(shell pkg-config --libs gnutls) endif ifeq ($(THREADS),1) CXXFLAGS += -DUSE_THREADS diff --git a/src/framework/socket.cpp b/src/framework/socket.cpp index b2c90a7..9f11b12 100644 --- a/src/framework/socket.cpp +++ b/src/framework/socket.cpp @@ -23,6 +23,14 @@ #endif #include + +static inline void set_cloexec(int fd) { + if (fd >= 0) { + int flags = fcntl(fd, F_GETFD); + if (flags != -1) fcntl(fd, F_SETFD, flags | FD_CLOEXEC); + } +} + #include #include @@ -56,8 +64,10 @@ Socket::Socket(const std::string &label, Task *owner, } else { _socket = pair_sd[0]; unix_domain_peer = pair_sd[1]; + set_cloexec(_socket); + set_cloexec(unix_domain_peer); fcntl(pair_sd[0], F_SETFL, O_NONBLOCK|O_CLOEXEC); - fcntl(pair_sd[1], F_SETFL, O_NONBLOCK); + fcntl(pair_sd[1], F_SETFL, O_NONBLOCK|O_CLOEXEC); } return; } diff --git a/src/framework/socketconnection.cpp b/src/framework/socketconnection.cpp index e135244..34965b1 100644 --- a/src/framework/socketconnection.cpp +++ b/src/framework/socketconnection.cpp @@ -247,8 +247,12 @@ init_tls_client(gnutls_certificate_credentials_t &x509_cred, bool verify_cert) { gnutls_session_set_verify_cert(session, peer_ip.c_str(), 0); } +#ifndef ALLOW_BROKEN_TLS + // Do not weaken verification by default +#else gnutls_certificate_set_verify_flags (x509_cred, GNUTLS_VERIFY_ALLOW_BROKEN); +#endif gnutls_handshake_set_timeout(session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT); diff --git a/src/http/httpclientconnection.cpp b/src/http/httpclientconnection.cpp index 9303c6b..2a437ff 100644 --- a/src/http/httpclientconnection.cpp +++ b/src/http/httpclientconnection.cpp @@ -13,6 +13,12 @@ #include "httpclienttask.h" #include "http_common.h" #include "sha1.h" +#include +#include +#include +#ifdef USE_GNUTLS +#include +#endif HttpClientConnection::HttpClientConnection(const std::string &label, HttpClientTask *task, @@ -342,10 +348,21 @@ void HttpClientConnection::ws_get(const std::string &url) { static char dst[] { "01234567890123456789====258EAFA5-E914-47DA-95CA-C5AB0DC85B11" }; - // 16 byte random data, convert to 24 base64 chars: - int32_t rnd[4] = { rand(), rand(), rand(), rand() }; - const unsigned char *src = reinterpret_cast(rnd); - base64_encode(src, sizeof(rnd), dst); + // 16-byte random data, convert to 24 base64 chars (RFC 6455) + std::array rnd{}; +#ifdef USE_GNUTLS + gnutls_rnd(GNUTLS_RND_NONCE, rnd.data(), rnd.size()); +#else + int fd = open("/dev/urandom", O_RDONLY); + if (fd >= 0) { + ssize_t n = read(fd, rnd.data(), rnd.size()); + (void)n; + close(fd); + } else { + for (size_t i = 0; i < rnd.size(); ++i) rnd[i] = static_cast(rand()); + } +#endif + base64_encode(rnd.data(), rnd.size(), dst); current_uri = url; current_request = "GET " + proto_hostname + diff --git a/src/json11/json11.cpp b/src/json11/json11.cpp index 56ae4e8..ada6654 100644 --- a/src/json11/json11.cpp +++ b/src/json11/json11.cpp @@ -22,6 +22,7 @@ // Modified by Göran Andersson #include "json11.hpp" +#include #include #include #include @@ -98,13 +99,15 @@ static void dump(const string &value, string &out) { out += "\\t"; } else if (static_cast(ch) <= 0x1f) { char buf[8]; - snprintf(buf, sizeof buf, "\\u%04x", ch); + snprintf(buf, sizeof buf, "\\u%04x", static_cast(static_cast(ch))); out += buf; - } else if (static_cast(ch) == 0xe2 && static_cast(value[i+1]) == 0x80 + } else if (i + 2 < value.size() + && static_cast(ch) == 0xe2 && static_cast(value[i+1]) == 0x80 && static_cast(value[i+2]) == 0xa8) { out += "\\u2028"; i += 2; - } else if (static_cast(ch) == 0xe2 && static_cast(value[i+1]) == 0x80 + } else if (i + 2 < value.size() + && static_cast(ch) == 0xe2 && static_cast(value[i+1]) == 0x80 && static_cast(value[i+2]) == 0xa9) { out += "\\u2029"; i += 2; diff --git a/src/measurement/defs.cpp b/src/measurement/defs.cpp index fdbe36d..0db99da 100644 --- a/src/measurement/defs.cpp +++ b/src/measurement/defs.cpp @@ -81,7 +81,20 @@ namespace { #ifdef _WIN32 std::shared_ptr pipe(_popen(cmd, "r"), _pclose); #else + #ifdef NO_EXTERNAL_CMD + (void)cmd; + return std::string(); +#else + // Basic guard: reject characters that imply shell metacharacters + for (const char *p = cmd; *p; ++p) { + switch (*p) { + case ';': case '|': case '&': case '`': case '$': + case '>': case '<': case '\n': case '\r': + return std::string(); + } + } std::shared_ptr pipe(popen(cmd, "r"), pclose); +#endif #endif if (pipe) { while (!feof(pipe.get())) diff --git a/src/measurement/wsuploadtask.cpp b/src/measurement/wsuploadtask.cpp index b093e62..0b74e3f 100644 --- a/src/measurement/wsuploadtask.cpp +++ b/src/measurement/wsuploadtask.cpp @@ -3,6 +3,12 @@ #include "wsuploadtask.h" #include +#include +#include +#ifdef USE_GNUTLS +#include +#endif +#include WsUploadTask::WsUploadTask(const std::string &ticket, const HttpHost &server, unsigned int no_conn, unsigned int max_conn, @@ -27,7 +33,17 @@ namespace { std::vector buf; // Fill the buffer with dummy data that cannot cause data compression while (len) { - buf.push_back(static_cast(rand())); +#ifdef USE_GNUTLS + unsigned char x; + gnutls_rnd(GNUTLS_RND_NONCE, &x, 1); + buf.push_back(static_cast(x)); +#else + int fd = open("/dev/urandom", O_RDONLY); + unsigned char x; + if (fd >= 0) { read(fd, &x, 1); close(fd); } + else { x = static_cast(rand()); } + buf.push_back(static_cast(x)); +#endif --len; } return buf;