From b2129205f978ff14d1ff03c36aa86d48fc25b13e Mon Sep 17 00:00:00 2001 From: Bram Matthys Date: Wed, 9 Aug 2017 12:00:04 +0200 Subject: [PATCH] Added support for the "Server Name Indication" (SNI) SSL/TLS extension. See https://www.unrealircd.org/docs/Sni_block Requested in #4380 by Eman. --- include/h.h | 1 + include/struct.h | 10 +++++ src/s_conf.c | 96 ++++++++++++++++++++++++++++++++++++++++++++++++ src/ssl.c | 18 +++++++++ 4 files changed, 125 insertions(+) diff --git a/include/h.h b/include/h.h index 9e7884816..e58754e78 100644 --- a/include/h.h +++ b/include/h.h @@ -107,6 +107,7 @@ extern ConfigItem_deny_dcc *Find_deny_dcc(char *name); extern ConfigItem_oper *Find_oper(char *name); extern ConfigItem_operclass *Find_operclass(char *name); extern ConfigItem_listen *Find_listen(char *ipmask, int port, int ipv6); +extern ConfigItem_sni *Find_sni(char *name); extern ConfigItem_ulines *Find_uline(char *host); extern ConfigItem_except *Find_except(aClient *, short type); extern ConfigItem_tld *Find_tld(aClient *cptr); diff --git a/include/struct.h b/include/struct.h index a1f4e1102..d21fddc42 100644 --- a/include/struct.h +++ b/include/struct.h @@ -97,6 +97,7 @@ typedef struct _configitem_drpass ConfigItem_drpass; typedef struct _configitem_ulines ConfigItem_ulines; typedef struct _configitem_tld ConfigItem_tld; typedef struct _configitem_listen ConfigItem_listen; +typedef struct _configitem_sni ConfigItem_sni; typedef struct _configitem_allow ConfigItem_allow; typedef struct _configflag_allow ConfigFlag_allow; typedef struct _configitem_allow_channel ConfigItem_allow_channel; @@ -1159,6 +1160,14 @@ struct _configitem_listen { SSLOptions *ssl_options; }; +struct _configitem_sni { + ConfigItem_sni *prev, *next; + ConfigFlag flag; + char *name; + SSL_CTX *ssl_ctx; + SSLOptions *ssl_options; +}; + struct _configitem_vhost { ConfigItem *prev, *next; ConfigFlag flag; @@ -1339,6 +1348,7 @@ struct _configitem_offchans { char *topic; }; + #define HM_HOST 1 #define HM_IPV4 2 #define HM_IPV6 3 diff --git a/src/s_conf.c b/src/s_conf.c index 8404ed9eb..d61953742 100644 --- a/src/s_conf.c +++ b/src/s_conf.c @@ -100,6 +100,7 @@ static int _conf_alias (ConfigFile *conf, ConfigEntry *ce); static int _conf_help (ConfigFile *conf, ConfigEntry *ce); static int _conf_offchans (ConfigFile *conf, ConfigEntry *ce); static int _conf_spamfilter (ConfigFile *conf, ConfigEntry *ce); +static int _conf_sni (ConfigFile *conf, ConfigEntry *ce); /* * Validation commands @@ -131,6 +132,7 @@ static int _test_alias (ConfigFile *conf, ConfigEntry *ce); static int _test_help (ConfigFile *conf, ConfigEntry *ce); static int _test_offchans (ConfigFile *conf, ConfigEntry *ce); static int _test_spamfilter (ConfigFile *conf, ConfigEntry *ce); +static int _test_sni (ConfigFile *conf, ConfigEntry *ce); /* This MUST be alphabetized */ static ConfigCommand _ConfigCommands[] = { @@ -154,6 +156,7 @@ static ConfigCommand _ConfigCommands[] = { { "oper", _conf_oper, _test_oper }, { "operclass", _conf_operclass, _test_operclass }, { "set", _conf_set, _test_set }, + { "sni", _conf_sni, _test_sni }, { "spamfilter", _conf_spamfilter, _test_spamfilter }, { "tld", _conf_tld, _test_tld }, { "ulines", _conf_ulines, _test_ulines }, @@ -288,6 +291,7 @@ ConfigItem_tld *conf_tld = NULL; ConfigItem_oper *conf_oper = NULL; ConfigItem_operclass *conf_operclass = NULL; ConfigItem_listen *conf_listen = NULL; +ConfigItem_sni *conf_sni = NULL; ConfigItem_allow *conf_allow = NULL; ConfigItem_except *conf_except = NULL; ConfigItem_vhost *conf_vhost = NULL; @@ -1920,6 +1924,7 @@ void config_rehash() ConfigItem_alias *alias_ptr; ConfigItem_help *help_ptr; ConfigItem_offchans *of_ptr; + ConfigItem_sni *sni; OperStat *os_ptr; ListStruct *next, *next2; aTKline *tk, *tk_next; @@ -2214,6 +2219,17 @@ void config_rehash() } conf_offchans = NULL; + /* Free sni { } blocks */ + for (sni = conf_sni; sni; sni = (ConfigItem_sni *)next) + { + next = (ListStruct *)sni->next; + SSL_CTX_free(sni->ssl_ctx); + free_ssl_options(sni->ssl_options); + safefree(sni->name); + MyFree(sni); + } + conf_sni = NULL; + for (i = 0; i < EXTCMODETABLESZ; i++) { if (iConf.modes_on_join.extparams[i]) @@ -2607,6 +2623,24 @@ ConfigItem_listen *Find_listen(char *ipmask, int port, int ipv6) return NULL; } +/** Find a SNI match. + * @param name The hostname to look for (eg: irc.xyz.com). + */ +ConfigItem_sni *Find_sni(char *name) +{ + ConfigItem_sni *e; + + if (!name) + return NULL; + + for (e = conf_sni; e; e = e->next) + { + if (!match(name, e->name)) + return e; + } + return NULL; +} + ConfigItem_ulines *Find_uline(char *host) { ConfigItem_ulines *ulines; @@ -6208,6 +6242,68 @@ int _test_spamfilter(ConfigFile *conf, ConfigEntry *ce) return errors; } + +int _test_sni(ConfigFile *conf, ConfigEntry *ce) +{ + int errors = 0; + ConfigEntry *cep, *sslconfig = NULL; + + if (!ce->ce_vardata) + { + config_error("%s:%i: sni block needs a name, eg: sni irc.xyz.com {", + ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + errors++; + } + + for (cep = ce->ce_entries; cep; cep = cep->ce_next) + { + if (!strcmp(cep->ce_varname, "ssl-options")) + { + test_sslblock(conf, cep, &errors); + } else + { + config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, + "sni", cep->ce_varname); + errors++; + continue; + } + } + + return errors; +} + +int _conf_sni(ConfigFile *conf, ConfigEntry *ce) +{ + ConfigEntry *cep; + ConfigEntry *sslconfig = NULL; + char *name; + ConfigItem_sni *sni = NULL; + + name = ce->ce_vardata; + if (!name) + return 0; + + for (cep = ce->ce_entries; cep; cep = cep->ce_next) + { + if (!strcmp(cep->ce_varname, "ssl-options")) + { + sslconfig = cep; + } + } + + if (!sslconfig) + return 0; + + sni = MyMallocEx(sizeof(ConfigItem_listen)); + sni->name = strdup(name); + sni->ssl_options = MyMallocEx(sizeof(SSLOptions)); + conf_sslblock(conf, sslconfig, sni->ssl_options); + sni->ssl_ctx = init_ctx(sni->ssl_options, 1); + AddListItem(sni, conf_sni); + + return 1; +} + int _conf_help(ConfigFile *conf, ConfigEntry *ce) { ConfigEntry *cep; diff --git a/src/ssl.c b/src/ssl.c index 38e3eb59f..322ec2c8b 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -188,6 +188,19 @@ static int ssl_verify_callback(int preverify_ok, X509_STORE_CTX *ctx) return 1; } +static int ssl_hostname_callback(SSL *ssl, int *unk, void *arg) +{ + char *name = (char *)SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); + ConfigItem_sni *sni; + + if (name && (sni = Find_sni(name))) + { + SSL_set_SSL_CTX(ssl, sni->ssl_ctx); + } + + return SSL_TLSEXT_ERR_OK; +} + static void mylog(char *fmt, ...) { va_list vl; @@ -353,6 +366,11 @@ SSL_CTX *init_ctx(SSLOptions *ssloptions, int server) SSL_CTX_set_options(ctx, SSL_OP_SINGLE_ECDH_USE|SSL_OP_SINGLE_DH_USE); } + if (server) + { + SSL_CTX_set_tlsext_servername_callback(ctx, ssl_hostname_callback); + } + return ctx; fail: SSL_CTX_free(ctx);