From bb364badd3f6ae9863189565ae2508973607cb38 Mon Sep 17 00:00:00 2001 From: Lauri Kasanen Date: Mon, 6 Dec 2021 12:07:32 +0200 Subject: [PATCH 1/2] Apply blacklist options to httpd basicauth --- common/network/Blacklist.cxx | 61 +++++++++++++++++++++++++++++++++++ common/network/Blacklist.h | 33 +++++++++++++++++++ common/network/CMakeLists.txt | 1 + common/network/websocket.c | 17 ++++++++-- 4 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 common/network/Blacklist.cxx create mode 100644 common/network/Blacklist.h diff --git a/common/network/Blacklist.cxx b/common/network/Blacklist.cxx new file mode 100644 index 0000000..a5caeea --- /dev/null +++ b/common/network/Blacklist.cxx @@ -0,0 +1,61 @@ +/* Copyright (C) 2021 Kasm + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +static std::map hits; +static std::map blacklist; + +unsigned char bl_isBlacklisted(const char *addr) { + const unsigned char count = blacklist.count(addr); + if (!count) + return 0; + + const time_t now = time(NULL); + const unsigned timeout = rfb::Blacklist::initialTimeout; + + if (now - timeout > blacklist[addr]) { + blacklist.erase(addr); + hits.erase(addr); + return 0; + } else { + blacklist[addr] = now; + return 1; + } +} + +void bl_addFailure(const char *addr) { + const unsigned num = ++hits[addr]; + if (num >= (unsigned) rfb::Blacklist::threshold) { + blacklist[addr] = time(NULL); + } +} diff --git a/common/network/Blacklist.h b/common/network/Blacklist.h new file mode 100644 index 0000000..799c796 --- /dev/null +++ b/common/network/Blacklist.h @@ -0,0 +1,33 @@ +/* Copyright (C) 2021 Kasm + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef __NETWORK_BLACKLIST_H__ +#define __NETWORK_BLACKLIST_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +unsigned char bl_isBlacklisted(const char *); +void bl_addFailure(const char *); + +#ifdef __cplusplus +} // extern C +#endif + +#endif // __NETWORK_TCP_SOCKET_H__ diff --git a/common/network/CMakeLists.txt b/common/network/CMakeLists.txt index d63c696..a767e73 100644 --- a/common/network/CMakeLists.txt +++ b/common/network/CMakeLists.txt @@ -2,6 +2,7 @@ include_directories(${CMAKE_SOURCE_DIR}/common ${CMAKE_SOURCE_DIR}/unix/kasmvncp set(NETWORK_SOURCES GetAPIMessager.cxx + Blacklist.cxx Socket.cxx TcpSocket.cxx websocket.c diff --git a/common/network/websocket.c b/common/network/websocket.c index 6270b49..01d246e 100644 --- a/common/network/websocket.c +++ b/common/network/websocket.c @@ -32,6 +32,7 @@ #include /* sha1 hash */ #include "websocket.h" #include "kasmpasswd.h" +#include /* * Global state @@ -1203,7 +1204,7 @@ nope: return 1; } -ws_ctx_t *do_handshake(int sock) { +ws_ctx_t *do_handshake(int sock, const char *ip) { char handshake[4096], response[4096], sha1[29], trailer[17]; char *scheme, *pre; headers_t *headers; @@ -1271,10 +1272,20 @@ ws_ctx_t *do_handshake(int sock) { usleep(10); } + if (bl_isBlacklisted(ip)) { + wserr("IP %s is blacklisted, dropping\n", ip); + sprintf(response, "HTTP/1.1 401 Forbidden\r\n" + "\r\n"); + ws_send(ws_ctx, response, strlen(response)); + free_ws_ctx(ws_ctx); + return NULL; + } + unsigned char owner = 0; if (!settings.disablebasicauth) { const char *hdr = strstr(handshake, "Authorization: Basic "); if (!hdr) { + bl_addFailure(ip); handler_emsg("BasicAuth required, but client didn't send any. 401 Unauth\n"); sprintf(response, "HTTP/1.1 401 Unauthorized\r\n" "WWW-Authenticate: Basic realm=\"Websockify\"\r\n" @@ -1288,6 +1299,7 @@ ws_ctx_t *do_handshake(int sock) { const char *end = strchr(hdr, '\r'); if (!end || end - hdr > 256) { handler_emsg("Client sent invalid BasicAuth, dropping connection\n"); + bl_addFailure(ip); free_ws_ctx(ws_ctx); return NULL; } @@ -1357,6 +1369,7 @@ ws_ctx_t *do_handshake(int sock) { if (len <= 0 || strcmp(authbuf, response)) { handler_emsg("BasicAuth user/pw did not match\n"); + bl_addFailure(ip); sprintf(response, "HTTP/1.1 401 Forbidden\r\n" "\r\n"); ws_send(ws_ctx, response, strlen(response)); @@ -1445,7 +1458,7 @@ void *subthread(void *ptr) { ws_ctx_t *ws_ctx; - ws_ctx = do_handshake(csock); + ws_ctx = do_handshake(csock, pass->ip); if (ws_ctx == NULL) { handler_msg("No connection after handshake\n"); goto out; // Child process exits From 49174b15862c07bd654b318b9d4e57f5af1dc97b Mon Sep 17 00:00:00 2001 From: Lauri Kasanen Date: Mon, 6 Dec 2021 13:41:07 +0200 Subject: [PATCH 2/2] Threaded context --- common/network/Blacklist.cxx | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/common/network/Blacklist.cxx b/common/network/Blacklist.cxx index a5caeea..d1f7ff2 100644 --- a/common/network/Blacklist.cxx +++ b/common/network/Blacklist.cxx @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include #include @@ -35,6 +37,9 @@ static std::map hits; static std::map blacklist; +static pthread_mutex_t hitmutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t blmutex = PTHREAD_MUTEX_INITIALIZER; + unsigned char bl_isBlacklisted(const char *addr) { const unsigned char count = blacklist.count(addr); if (!count) @@ -43,19 +48,35 @@ unsigned char bl_isBlacklisted(const char *addr) { const time_t now = time(NULL); const unsigned timeout = rfb::Blacklist::initialTimeout; + if (pthread_mutex_lock(&blmutex)) + abort(); + if (now - timeout > blacklist[addr]) { blacklist.erase(addr); + pthread_mutex_unlock(&blmutex); + + if (pthread_mutex_lock(&hitmutex)) + abort(); hits.erase(addr); + pthread_mutex_unlock(&hitmutex); return 0; } else { blacklist[addr] = now; + pthread_mutex_unlock(&blmutex); return 1; } } void bl_addFailure(const char *addr) { + if (pthread_mutex_lock(&hitmutex)) + abort(); const unsigned num = ++hits[addr]; + pthread_mutex_unlock(&hitmutex); + if (num >= (unsigned) rfb::Blacklist::threshold) { + if (pthread_mutex_lock(&blmutex)) + abort(); blacklist[addr] = time(NULL); + pthread_mutex_unlock(&blmutex); } }