diff --git a/include/h.h b/include/h.h index 7965e8134..23218452c 100644 --- a/include/h.h +++ b/include/h.h @@ -750,3 +750,4 @@ extern void send_moddata_channel(aClient *srv, aChannel *chptr); extern void send_moddata_members(aClient *srv); extern int ssl_used_in_config_but_unavail(void); extern void config_report_ssl_error(void); +extern int dead_link(aClient *to, char *notice); diff --git a/modules.conf b/modules.conf index 235aeda2e..46a3a763c 100644 --- a/modules.conf +++ b/modules.conf @@ -107,6 +107,7 @@ loadmodule "modules/m_nopost"; loadmodule "modules/m_cap"; loadmodule "modules/m_sasl"; loadmodule "modules/m_md"; +loadmodule "modules/ssl_antidos"; loadmodule "modules/certfp"; loadmodule "modules/cap_invitenotify"; diff --git a/src/modules/Makefile.in b/src/modules/Makefile.in index b9b4024f1..00e4ea356 100644 --- a/src/modules/Makefile.in +++ b/src/modules/Makefile.in @@ -55,7 +55,7 @@ R_MODULES= \ m_mode.so m_watch.so m_part.so m_join.so m_motd.so m_opermotd.so \ m_botmotd.so m_lusers.so m_names.so m_svsnolag.so m_addmotd.so \ m_svslusers.so m_starttls.so m_nopost.so m_cap.so \ - m_sasl.so cap_invitenotify.so m_md.so certfp.so + m_sasl.so cap_invitenotify.so m_md.so certfp.so ssl_antidos.so MODULES=cloak.so $(R_MODULES) MODULEFLAGS=@MODULEFLAGS@ @@ -496,6 +496,10 @@ certfp.so: certfp.c $(INCLUDES) $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ -o certfp.so certfp.c +ssl_antidos.so: ssl_antidos.c $(INCLUDES) + $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ + -o ssl_antidos.so ssl_antidos.c + ############################################################################# # capabilities ############################################################################# diff --git a/src/modules/ssl_antidos.c b/src/modules/ssl_antidos.c new file mode 100644 index 000000000..45b41cd56 --- /dev/null +++ b/src/modules/ssl_antidos.c @@ -0,0 +1,107 @@ +/* + * SSL Anti DoS module + * This protects against SSL renegotiation attacks while still allowing us + * to leave renegotiation on with all it's security benefits. + * + * (C) Copyright 2015 The UnrealIRCd team (Syzop and others) + * + * License: GPLv2 + */ + +#include "unrealircd.h" + +ModuleHeader MOD_HEADER(ssl_antidos) + = { + "ssl_antidos", + "$Id$", + "SSL Renegotiation DoS protection", + "3.2-b8-1", + NULL + }; + +#define HANDSHAKE_LIMIT_COUNT 3 +#define HANDSHAKE_LIMIT_SECS 300 + +typedef struct _sad SAD; +struct _sad { + aClient *acptr; /**< client */ + time_t ts; /**< time */ + int n; /**< number of times */ +}; + +int ssl_antidos_index = 0; /* slot# we acquire from OpenSSL. Hmm.. looks awfully similar to our moddata system ;) */ + +/* Forward declaration */ +int ssl_antidos_handshake(aClient *acptr); + +void ssl_antidos_free(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx, long argl, void *argp); + +DLLFUNC int MOD_INIT(ssl_antidos)(ModuleInfo *modinfo) +{ + HookAddEx(modinfo->handle, HOOKTYPE_HANDSHAKE, ssl_antidos_handshake); + + ModuleSetOptions(modinfo->handle, MOD_OPT_PERM, 1); + + ssl_antidos_index = SSL_get_ex_new_index(0, "ssl_antidos", NULL, NULL, ssl_antidos_free); + + return MOD_SUCCESS; +} + +DLLFUNC int MOD_LOAD(ssl_antidos)(int module_load) +{ + return MOD_SUCCESS; +} + +DLLFUNC int MOD_UNLOAD(ssl_antidos)(int module_unload) +{ + return MOD_SUCCESS; +} + +/** Called upon handshake (and other events) */ +void ssl_info_callback(const SSL *ssl, int where, int ret) +{ + if (where & SSL_CB_HANDSHAKE_START) + { + SAD *e = SSL_get_ex_data(ssl, ssl_antidos_index); + aClient *acptr = e->acptr; + + if (IsServer(acptr) || IsDead(acptr)) + return; /* if it's a server, or already pending to be killed off then we don't care */ + + if (e->ts < TStime() - HANDSHAKE_LIMIT_SECS) + { + e->ts = TStime(); + e->n = 1; + } else { + e->n++; + if (e->n >= HANDSHAKE_LIMIT_COUNT) + { + ircd_log(LOG_ERROR, "SSL Handshake flood detected from %s -- killed", get_client_name(acptr, TRUE)); + sendto_realops("SSL Handshake flood detected from %s -- killed", get_client_name(acptr, TRUE)); + dead_link(acptr, "SSL Handshake flood detected"); + } + } + } +} + +/** Called when a client has just connected to us. + * This function is called quite quickly after accept(), + * in any case very likely before any data has been received. + */ +int ssl_antidos_handshake(aClient *acptr) +{ + if (acptr->ssl) + { + SAD *sad = MyMallocEx(sizeof(SAD)); + sad->acptr = acptr; + SSL_set_info_callback(acptr->ssl, ssl_info_callback); + SSL_set_ex_data(acptr->ssl, ssl_antidos_index, sad); + } + return 0; +} + +/** Called by OpenSSL when the SSL structure is freed (so we can free up our custom struct too) */ +void ssl_antidos_free(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx, long argl, void *argp) +{ + MyFree(ptr); +} diff --git a/src/send.c b/src/send.c index 65d6a27a6..4344fe527 100644 --- a/src/send.c +++ b/src/send.c @@ -69,7 +69,7 @@ MODVAR int sendanyways = 0; ** notice will be the quit message. notice will also be ** sent to failops in case 'to' is a server. */ -static int dead_link(aClient *to, char *notice) +int dead_link(aClient *to, char *notice) { to->flags |= FLAGS_DEADSOCKET;