Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

configure anti brute force #1447

Merged
merged 12 commits into from Nov 14, 2020
2 changes: 2 additions & 0 deletions libtransmission/quark.c
Expand Up @@ -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"),
Expand Down
2 changes: 2 additions & 0 deletions libtransmission/quark.h
Expand Up @@ -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,
Expand Down
56 changes: 54 additions & 2 deletions libtransmission/rpc-server.c
Expand Up @@ -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;
Expand Down Expand Up @@ -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, "<p>Too many unsuccessful login attempts. Please restart transmission-daemon.</p>");
return;
Expand Down Expand Up @@ -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("<p>Unauthorized User. %d unsuccessful login attempts.</p>",
server->loginattempts);
send_simple_response(req, 401, unauthuser);
Expand Down Expand Up @@ -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
****/
Expand Down Expand Up @@ -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))
Expand Down
8 changes: 8 additions & 0 deletions libtransmission/rpc-server.h
Expand Up @@ -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);
18 changes: 18 additions & 0 deletions libtransmission/rpcimpl.c
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
46 changes: 46 additions & 0 deletions libtransmission/session.c
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
Expand Down
9 changes: 9 additions & 0 deletions libtransmission/transmission.h
Expand Up @@ -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
Expand Down
4 changes: 3 additions & 1 deletion tests/libtransmission/rpc-test.cc
Expand Up @@ -102,14 +102,16 @@ TEST_F(RpcTest, sessionGet)
EXPECT_TRUE(tr_variantDictFindDict(&response, TR_KEY_arguments, &args));

// what we expected
auto const expected_keys = std::array<tr_quark, 50>{
auto const expected_keys = std::array<tr_quark, 52>{
TR_KEY_alt_speed_down,
TR_KEY_alt_speed_enabled,
TR_KEY_alt_speed_time_begin,
TR_KEY_alt_speed_time_day,
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,
Expand Down