From 84776eeeb2a8d703e67859bf210e9919d15ba71c Mon Sep 17 00:00:00 2001 From: Bram Matthys Date: Wed, 9 Aug 2017 14:16:03 +0200 Subject: [PATCH] Add support for draft/sts http://ircv3.net/specs/core/sts-3.3.html Docs: https://www.unrealircd.org/docs/Set_block#set::ssl::sts-policy::port Example: set { ssl { certificate "ssl/server.cert.pem"; key "ssl/server.key.pem"; sts-policy { port 6697; duration 180d; }; }; }; IMPORTANT: Only use this if you know what STS is and what the implications are. The most important things being A) set a correct port and B) you need a 'real' SSL certificate and not a self-signed certificate. More documentation may follow at another place. --- include/struct.h | 3 ++ src/modules/cap/sts.c | 48 ++++++++++++++++++++++++++++-- src/s_conf.c | 69 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 118 insertions(+), 2 deletions(-) diff --git a/include/struct.h b/include/struct.h index d21fddc42..3bfc5defb 100644 --- a/include/struct.h +++ b/include/struct.h @@ -1116,6 +1116,9 @@ struct _ssloptions { long options; int renegotiate_bytes; int renegotiate_timeout; + int sts_port; + long sts_duration; + int sts_preload; }; struct _configitem_mask { diff --git a/src/modules/cap/sts.c b/src/modules/cap/sts.c index 7d6f25c01..269b0a9ea 100644 --- a/src/modules/cap/sts.c +++ b/src/modules/cap/sts.c @@ -52,14 +52,58 @@ MOD_UNLOAD(sts) return MOD_SUCCESS; } +SSLOptions *FindSSLOptionsForUser(aClient *acptr) +{ + if (!MyConnect(acptr) || !IsSecure(acptr)) + return NULL; + + /* TODO: different sts-policy depending on SNI */ + + if (!iConf.ssl_options) + return NULL; + + return iConf.ssl_options; +} + int sts_capability_visible(aClient *acptr) { - return 1; + SSLOptions *ssl; + + if (!MyConnect(acptr)) + return 0; /* not needed I guess */ + + if (!IsSecure(acptr)) + { + if (iConf.ssl_options && iConf.ssl_options->sts_port) + return 1; /* YES, non-SSL user and set::ssl::sts-policy configured */ + return 0; /* NO, there is no sts-policy */ + } + + ssl = FindSSLOptionsForUser(acptr); + + if (ssl && ssl->sts_port) + return 1; + + return 0; } char *sts_capability_parameter(aClient *acptr) { - return "port=6697"; + SSLOptions *ssl; + static char buf[256]; + + ssl = FindSSLOptionsForUser(acptr); + if (!ssl) + ssl = iConf.ssl_options; /* default, eg: for non-SSL users */ + + if (!ssl) + return ""; /* A possible, but rather silly configuration error. */ + + snprintf(buf, sizeof(buf), "port=%d,duration=%ld", ssl->sts_port, ssl->sts_duration); + if (ssl->sts_preload) + strlcat(buf, ",preload", sizeof(buf)); + + return buf; } void init_sts(ModuleInfo *modinfo) diff --git a/src/s_conf.c b/src/s_conf.c index 518c052a8..ea0f9435c 100644 --- a/src/s_conf.c +++ b/src/s_conf.c @@ -7137,6 +7137,56 @@ void test_sslblock(ConfigFile *conf, ConfigEntry *cep, int *totalerrors) errors ++; } } + else if (!strcmp(cepp->ce_varname, "sts-policy")) + { + int has_port = 0; + int has_duration = 0; + for (ceppp = cepp->ce_entries; ceppp; ceppp = ceppp->ce_next) + { + if (!strcmp(ceppp->ce_varname, "port")) + { + int port; + CheckNull(ceppp); + port = atoi(ceppp->ce_vardata); + if ((port < 1) || (port > 65535)) + { + config_error("%s:%i: invalid port number specified in sts-policy::port (%d)", + ceppp->ce_fileptr->cf_filename, ceppp->ce_varlinenum, port); + errors++; + } + has_port = 1; + } + else if (!strcmp(ceppp->ce_varname, "duration")) + { + long duration; + CheckNull(ceppp); + duration = config_checkval(ceppp->ce_vardata, CFG_TIME); + if (duration < 1) + { + config_error("%s:%i: invalid duration specified in sts-policy::duration (%ld seconds)", + ceppp->ce_fileptr->cf_filename, ceppp->ce_varlinenum, duration); + errors++; + } + has_duration = 1; + } + else if (!strcmp(ceppp->ce_varname, "preload")) + { + CheckNull(ceppp); + } + } + if (!has_port) + { + config_error("%s:%i: sts-policy block without port", + cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum); + errors++; + } + if (!has_duration) + { + config_error("%s:%i: sts-policy block without duration", + cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum); + errors++; + } + } else { config_error("%s:%i: unknown directive %s", @@ -7180,6 +7230,9 @@ void conf_sslblock(ConfigFile *conf, ConfigEntry *cep, SSLOptions *ssloptions) ssloptions->options = tempiConf.ssl_options->options; ssloptions->renegotiate_bytes = tempiConf.ssl_options->renegotiate_bytes; ssloptions->renegotiate_timeout = tempiConf.ssl_options->renegotiate_timeout; + ssloptions->sts_port = tempiConf.ssl_options->sts_port; + ssloptions->sts_duration = tempiConf.ssl_options->sts_duration; + ssloptions->sts_preload = tempiConf.ssl_options->sts_preload; } /* Now process the options */ @@ -7270,6 +7323,22 @@ void conf_sslblock(ConfigFile *conf, ConfigEntry *cep, SSLOptions *ssloptions) if (!(ssloptions->options & SSLFLAG_VERIFYCERT)) ssloptions->options |= SSLFLAG_VERIFYCERT; } + else if (!strcmp(cepp->ce_varname, "sts-policy")) + { + /* We do not inherit ::sts-policy if there is a specific block for this one... */ + ssloptions->sts_port = 0; + ssloptions->sts_duration = 0; + ssloptions->sts_preload = 0; + for (ceppp = cepp->ce_entries; ceppp; ceppp = ceppp->ce_next) + { + if (!strcmp(ceppp->ce_varname, "port")) + ssloptions->sts_port = atoi(ceppp->ce_vardata); + else if (!strcmp(ceppp->ce_varname, "duration")) + ssloptions->sts_duration = config_checkval(ceppp->ce_vardata, CFG_TIME); + else if (!strcmp(ceppp->ce_varname, "preload")) + ssloptions->sts_preload = config_checkval(ceppp->ce_vardata, CFG_YESNO); + } + } } }