From cfce6e2e3a9b9d31a9dafedd0bdc8bf2cdb6e876 Mon Sep 17 00:00:00 2001 From: Chrool <8381957+Chrool@users.noreply.github.com> Date: Sun, 15 Nov 2020 05:43:42 +1100 Subject: [PATCH] configure anti brute force (#1447) * add brute force configuration options --- libtransmission/quark.c | 2 ++ libtransmission/quark.h | 2 ++ libtransmission/rpc-server.c | 56 +++++++++++++++++++++++++++++-- libtransmission/rpc-server.h | 8 +++++ libtransmission/rpcimpl.c | 18 ++++++++++ libtransmission/session.c | 46 +++++++++++++++++++++++++ libtransmission/transmission.h | 9 +++++ tests/libtransmission/rpc-test.cc | 4 ++- 8 files changed, 142 insertions(+), 3 deletions(-) diff --git a/libtransmission/quark.c b/libtransmission/quark.c index b0b1bd8c2..a1cfd9b5c 100644 --- a/libtransmission/quark.c +++ b/libtransmission/quark.c @@ -46,6 +46,8 @@ static struct tr_key_struct const my_static[] = Q("announce"), Q("announce-list"), Q("announceState"), + Q("anti-brute-force-enabled"), + Q("anti-brute-force-threshold"), Q("arguments"), Q("bandwidth-priority"), Q("bandwidthPriority"), diff --git a/libtransmission/quark.h b/libtransmission/quark.h index 45466532c..823543f90 100644 --- a/libtransmission/quark.h +++ b/libtransmission/quark.h @@ -45,6 +45,8 @@ enum TR_KEY_announce, /* metainfo */ TR_KEY_announce_list, /* metainfo */ TR_KEY_announceState, /* rpc */ + TR_KEY_anti_brute_force_enabled, /* rpc */ + TR_KEY_anti_brute_force_threshold, /* rpc */ TR_KEY_arguments, /* rpc */ TR_KEY_bandwidth_priority, TR_KEY_bandwidthPriority, diff --git a/libtransmission/rpc-server.c b/libtransmission/rpc-server.c index d4f6db301..b8b3a6a41 100644 --- a/libtransmission/rpc-server.c +++ b/libtransmission/rpc-server.c @@ -65,6 +65,8 @@ struct tr_rpc_server tr_list* whitelist; tr_list* hostWhitelist; int loginattempts; + bool isAntiBruteForceEnabled; + int antiBruteForceThreshold; bool isStreamInitialized; z_stream stream; @@ -641,7 +643,7 @@ static void handle_request(struct evhttp_request* req, void* arg) evhttp_add_header(req->output_headers, "Server", MY_REALM); - if (server->loginattempts == 100) + if (server->isAntiBruteForceEnabled && server->loginattempts >= server->antiBruteForceThreshold) { send_simple_response(req, 403, "

Too many unsuccessful login attempts. Please restart transmission-daemon.

"); return; @@ -681,7 +683,11 @@ static void handle_request(struct evhttp_request* req, void* arg) !tr_ssha1_matches(server->password, pass))) { evhttp_add_header(req->output_headers, "WWW-Authenticate", "Basic realm=\"" MY_REALM "\""); - server->loginattempts++; + if (server->isAntiBruteForceEnabled) + { + server->loginattempts++; + } + char* unauthuser = tr_strdup_printf("

Unauthorized User. %d unsuccessful login attempts.

", server->loginattempts); send_simple_response(req, 401, unauthuser); @@ -1066,6 +1072,30 @@ char const* tr_rpcGetBindAddress(tr_rpc_server const* server) return tr_address_to_string(&server->bindAddress); } +bool tr_rpcGetAntiBruteForceEnabled(tr_rpc_server const* server) +{ + return server->isAntiBruteForceEnabled; +} + +void tr_rpcSetAntiBruteForceEnabled(tr_rpc_server* server, bool isEnabled) +{ + server->isAntiBruteForceEnabled = isEnabled; + if (!isEnabled) + { + server->loginattempts = 0; + } +} + +int tr_rpcGetAntiBruteForceThreshold(tr_rpc_server const* server) +{ + return server->antiBruteForceThreshold; +} + +void tr_rpcSetAntiBruteForceThreshold(tr_rpc_server* server, int badRequests) +{ + server->antiBruteForceThreshold = badRequests; +} + /**** ***** LIFE CYCLE ****/ @@ -1228,6 +1258,28 @@ tr_rpc_server* tr_rpcInit(tr_session* session, tr_variant* settings) tr_rpcSetPassword(s, str); } + key = TR_KEY_anti_brute_force_enabled; + + if (!tr_variantDictFindBool(settings, key, &boolVal)) + { + missing_settings_key(key); + } + else + { + tr_rpcSetAntiBruteForceEnabled(s, boolVal); + } + + key = TR_KEY_anti_brute_force_threshold; + + if (!tr_variantDictFindInt(settings, key, &i)) + { + missing_settings_key(key); + } + else + { + tr_rpcSetAntiBruteForceThreshold(s, i); + } + key = TR_KEY_rpc_bind_address; if (!tr_variantDictFindStr(settings, key, &str, NULL)) diff --git a/libtransmission/rpc-server.h b/libtransmission/rpc-server.h index ad1eb5204..496359b63 100644 --- a/libtransmission/rpc-server.h +++ b/libtransmission/rpc-server.h @@ -58,4 +58,12 @@ void tr_rpcSetPasswordEnabled(tr_rpc_server* server, bool isEnabled); bool tr_rpcIsPasswordEnabled(tr_rpc_server const* session); +bool tr_rpcGetAntiBruteForceEnabled(tr_rpc_server const* server); + +void tr_rpcSetAntiBruteForceEnabled(tr_rpc_server* server, bool is_enabled); + +int tr_rpcGetAntiBruteForceThreshold(tr_rpc_server const* server); + +void tr_rpcSetAntiBruteForceThreshold(tr_rpc_server* server, int badRequests); + char const* tr_rpcGetBindAddress(tr_rpc_server const* server); diff --git a/libtransmission/rpcimpl.c b/libtransmission/rpcimpl.c index 1d3a126f3..354fe93e8 100644 --- a/libtransmission/rpcimpl.c +++ b/libtransmission/rpcimpl.c @@ -2277,6 +2277,16 @@ static char const* sessionSet(tr_session* session, tr_variant* args_in, tr_varia } } + if (tr_variantDictFindInt(args_in, TR_KEY_anti_brute_force_threshold, &i)) + { + tr_sessionSetAntiBruteForceThreshold(session, i); + } + + if (tr_variantDictFindBool(args_in, TR_KEY_anti_brute_force_enabled, &boolVal)) + { + tr_sessionSetAntiBruteForceEnabled(session, boolVal); + } + notify(session, TR_RPC_SESSION_CHANGED, NULL); return NULL; @@ -2521,6 +2531,14 @@ static void addSessionField(tr_session* s, tr_variant* d, tr_quark key) tr_variantDictAddInt(d, key, tr_sessionGetQueueStalledMinutes(s)); break; + case TR_KEY_anti_brute_force_enabled: + tr_variantDictAddBool(d, key, tr_sessionGetAntiBruteForceEnabled(s)); + break; + + case TR_KEY_anti_brute_force_threshold: + tr_variantDictAddInt(d, key, tr_sessionGetAntiBruteForceThreshold(s)); + break; + case TR_KEY_units: tr_formatter_get_units(tr_variantDictAddDict(d, key, 0)); break; diff --git a/libtransmission/session.c b/libtransmission/session.c index 5ee1001c5..7c2d42041 100644 --- a/libtransmission/session.c +++ b/libtransmission/session.c @@ -404,6 +404,8 @@ void tr_sessionGetDefaultSettings(tr_variant* d) tr_variantDictAddStr(d, TR_KEY_bind_address_ipv6, TR_DEFAULT_BIND_ADDRESS_IPV6); tr_variantDictAddBool(d, TR_KEY_start_added_torrents, true); tr_variantDictAddBool(d, TR_KEY_trash_original_torrent_files, false); + tr_variantDictAddInt(d, TR_KEY_anti_brute_force_threshold, 100); + tr_variantDictAddBool(d, TR_KEY_anti_brute_force_enabled, true); } void tr_sessionGetSettings(tr_session* s, tr_variant* d) @@ -475,6 +477,8 @@ void tr_sessionGetSettings(tr_session* s, tr_variant* d) tr_variantDictAddStr(d, TR_KEY_bind_address_ipv6, tr_address_to_string(&s->public_ipv6->addr)); tr_variantDictAddBool(d, TR_KEY_start_added_torrents, !tr_sessionGetPaused(s)); tr_variantDictAddBool(d, TR_KEY_trash_original_torrent_files, tr_sessionGetDeleteSource(s)); + tr_variantDictAddInt(d, TR_KEY_anti_brute_force_threshold, tr_sessionGetAntiBruteForceThreshold(s)); + tr_variantDictAddBool(d, TR_KEY_anti_brute_force_enabled, tr_sessionGetAntiBruteForceEnabled(s)); } bool tr_sessionLoadSettings(tr_variant* dict, char const* configDir, char const* appName) @@ -1144,6 +1148,20 @@ static void sessionSetImpl(void* vdata) session->scrapePausedTorrents = boolVal; } + /** + *** BruteForce + **/ + + if (tr_variantDictFindInt(settings, TR_KEY_anti_brute_force_threshold, &i)) + { + tr_sessionSetAntiBruteForceThreshold(session, i); + } + + if (tr_variantDictFindBool(settings, TR_KEY_anti_brute_force_enabled, &boolVal)) + { + tr_sessionSetAntiBruteForceEnabled(session, boolVal); + } + data->done = true; } @@ -2947,6 +2965,34 @@ int tr_sessionGetQueueStalledMinutes(tr_session const* session) return session->queueStalledMinutes; } +void tr_sessionSetAntiBruteForceThreshold(tr_session* session, int bad_requests) +{ + TR_ASSERT(tr_isSession(session)); + TR_ASSERT(bad_requests > 0); + tr_rpcSetAntiBruteForceThreshold(session->rpcServer, bad_requests); +} + +void tr_sessionSetAntiBruteForceEnabled(tr_session* session, bool is_enabled) +{ + TR_ASSERT(tr_isSession(session)); + + tr_rpcSetAntiBruteForceEnabled(session->rpcServer, is_enabled); +} + +bool tr_sessionGetAntiBruteForceEnabled(tr_session const* session) +{ + TR_ASSERT(tr_isSession(session)); + + return tr_rpcGetAntiBruteForceEnabled(session->rpcServer); +} + +int tr_sessionGetAntiBruteForceThreshold(tr_session const* session) +{ + TR_ASSERT(tr_isSession(session)); + + return tr_rpcGetAntiBruteForceThreshold(session->rpcServer); +} + struct TorrentAndPosition { tr_torrent* tor; diff --git a/libtransmission/transmission.h b/libtransmission/transmission.h index 2dd7a8a7a..35a39a602 100644 --- a/libtransmission/transmission.h +++ b/libtransmission/transmission.h @@ -601,6 +601,15 @@ bool tr_sessionGetDeleteSource(tr_session const*); tr_priority_t tr_torrentGetPriority(tr_torrent const*); void tr_torrentSetPriority(tr_torrent*, tr_priority_t); +void tr_sessionSetAntiBruteForceThreshold(tr_session*, int bad_requests); +int tr_sessionGetAntiBruteForceThreshold(tr_session const*); + +void tr_sessionSetAntiBruteForceEnabled(tr_session*, bool enabled); +bool tr_sessionGetAntiBruteForceEnabled(tr_session const*); + +/** +**/ + /*** **** **** Torrent Queueing diff --git a/tests/libtransmission/rpc-test.cc b/tests/libtransmission/rpc-test.cc index 70e567ce6..613a8efb5 100644 --- a/tests/libtransmission/rpc-test.cc +++ b/tests/libtransmission/rpc-test.cc @@ -102,7 +102,7 @@ TEST_F(RpcTest, sessionGet) EXPECT_TRUE(tr_variantDictFindDict(&response, TR_KEY_arguments, &args)); // what we expected - auto const expected_keys = std::array{ + auto const expected_keys = std::array{ TR_KEY_alt_speed_down, TR_KEY_alt_speed_enabled, TR_KEY_alt_speed_time_begin, @@ -110,6 +110,8 @@ TEST_F(RpcTest, sessionGet) TR_KEY_alt_speed_time_enabled, TR_KEY_alt_speed_time_end, TR_KEY_alt_speed_up, + TR_KEY_anti_brute_force_enabled, + TR_KEY_anti_brute_force_threshold, TR_KEY_blocklist_enabled, TR_KEY_blocklist_size, TR_KEY_blocklist_url,