From 50d1b194dfd8f19ca20c572ff01ae1cfbd8cb9da Mon Sep 17 00:00:00 2001 From: Bram Matthys Date: Wed, 15 Jul 2015 12:42:47 +0200 Subject: [PATCH] Linking: for outgoing connects, only support SSL/TLS by default (either 'directly' on an ssl port or via STARTTLS 'upgrading'). Set link::outgoing::options::insecure to disable. --- include/h.h | 1 + include/struct.h | 2 +- src/s_bsd.c | 15 ++++++++- src/s_conf.c | 6 ++-- src/s_numeric.c | 80 +++++++++++++++++++++++++++++------------------- 5 files changed, 67 insertions(+), 37 deletions(-) diff --git a/include/h.h b/include/h.h index f2d57476a..7569f6d3e 100644 --- a/include/h.h +++ b/include/h.h @@ -761,3 +761,4 @@ extern int swhois_delete(aClient *acptr, char *tag, char *swhois, aClient *from, extern void remove_oper_privileges(aClient *sptr, int broadcast_mode_change); extern int client_starttls(aClient *acptr); extern void start_server_handshake(aClient *cptr); +extern void reject_insecure_server(aClient *cptr); diff --git a/include/struct.h b/include/struct.h index de94ffe77..5252b926d 100644 --- a/include/struct.h +++ b/include/struct.h @@ -829,7 +829,7 @@ extern void unload_all_unused_moddata(void); #define CONNECT_QUARANTINE 0x000008 #define CONNECT_NODNSCACHE 0x000010 #define CONNECT_NOHOSTCHECK 0x000020 -#define CONNECT_NO_STARTTLS 0x000040 +#define CONNECT_INSECURE 0x000040 #define SSLFLAG_FAILIFNOCERT 0x1 #define SSLFLAG_VERIFYCERT 0x2 diff --git a/src/s_bsd.c b/src/s_bsd.c index 55ca8224f..cf764e645 100644 --- a/src/s_bsd.c +++ b/src/s_bsd.c @@ -759,6 +759,19 @@ int check_client(aClient *cptr, char *username) return 0; } +/** Reject an insecure (outgoing) server link that isn't SSL/TLS. + * This function is void and not int because it can be called from other void functions + */ +void reject_insecure_server(aClient *cptr) +{ + sendto_umode(UMODE_OPER, "Could not link with server %s with SSL/TLS enabled. " + "Please check logs on the other side of the link and make sure the other IRCd " + "is compiled with SSL support enabled. " + "If you insist with insecure linking then you can set link::options::outgoing::insecure", + cptr->name); + dead_link(cptr, "Rejected link without SSL/TLS"); +} + void start_server_handshake(aClient *cptr) { ConfigItem_link *aconf = cptr->serv ? cptr->serv->conf : NULL; @@ -819,7 +832,7 @@ void completed_connection(int fd, int revents, void *data) return; } - if (!cptr->ssl && !(aconf->outgoing.options & CONNECT_NO_STARTTLS)) + if (!cptr->ssl && !(aconf->outgoing.options & CONNECT_INSECURE)) { sendto_one(cptr, "STARTTLS"); } else diff --git a/src/s_conf.c b/src/s_conf.c index 605098549..3339b0d0c 100644 --- a/src/s_conf.c +++ b/src/s_conf.c @@ -172,9 +172,9 @@ static NameValue _ListenerFlags[] = { /* This MUST be alphabetized */ static NameValue _LinkFlags[] = { { CONNECT_AUTO, "autoconnect" }, - { CONNECT_NO_STARTTLS, "no-starttls" }, + { CONNECT_INSECURE, "insecure" }, { CONNECT_QUARANTINE, "quarantine"}, - { CONNECT_SSL, "ssl" }, + { CONNECT_SSL, "ssl" }, }; /* This MUST be alphabetized */ @@ -6436,7 +6436,7 @@ int _test_link(ConfigFile *conf, ConfigEntry *ce) ; else if (!strcmp(ceppp->ce_varname, "ssl")) ; - else if (!strcmp(ceppp->ce_varname, "no-starttls")) + else if (!strcmp(ceppp->ce_varname, "insecure")) ; else { diff --git a/src/s_numeric.c b/src/s_numeric.c index 085a011ea..0906a005e 100644 --- a/src/s_numeric.c +++ b/src/s_numeric.c @@ -57,40 +57,56 @@ int do_numeric(int numeric, aClient *cptr, aClient *sptr, int parc, char *parv[ char *nick, *p; int i; - /* Is this an outgoing connect, and we get a numeric 451 (not registered) back for the - * magic command __PANGPANG__ and we did not send a SERVER message yet? - * Then this means we are dealing with an Unreal server <3.2.9 and we should send the - * SERVER command right now. - */ - if (!IsServer(sptr) && !IsPerson(sptr) && (numeric == 451) && (parc > 2) && strstr(parv[1], "__PANGPANG__") && - IsHandshake(cptr) && sptr->serv && !IsServerSent(sptr)) + if (!IsServer(sptr) && !IsPerson(sptr) && IsHandshake(cptr) && sptr->serv && !IsServerSent(sptr)) { - send_server_message(sptr); - return 0; - } - - if (!IsServer(sptr) && !IsPerson(sptr) && (numeric == 451) && (parc > 2) && strstr(parv[1], "STARTTLS") && - IsHandshake(cptr) && sptr->serv && !IsServerSent(sptr)) - { - start_server_handshake(cptr); - } - - // TODO: handle 691 as well (starttls failed) ? unusual. - - if (!IsServer(sptr) && !IsPerson(sptr) && (numeric == 670) && - IsHandshake(cptr) && sptr->serv && !IsServerSent(sptr)) - { - int ret = client_starttls(cptr); - if (ret < 0) - { - // When failed we could continue with start_server_handshake() here. - return ret; - } - /* We don't call start_server_handshake() here. First the TLS handshake will - * be completed, then completed_connection() will be called for a second time, - * which will call completed_connection() from there. + /* This is an outgoing server connect that is currently not yet IsServer() but in 'unknown' state. + * We need to handle a few responses here. */ - return 0; + + /* If we get a numeric 451 (not registered) back for the magic command __PANGPANG__ + * Then this means we are dealing with an Unreal server <3.2.9 and we should send the + * SERVER command right now. + */ + if ((numeric == 451) && (parc > 2) && strstr(parv[1], "__PANGPANG__")) + { + send_server_message(sptr); + return 0; + } + + /* STARTTLS: unknown command */ + if ((numeric == 451) && (parc > 2) && strstr(parv[1], "STARTTLS")) + { + if (cptr->serv->conf && (cptr->serv->conf->outgoing.options & CONNECT_INSECURE)) + start_server_handshake(cptr); + else + reject_insecure_server(cptr); + return 0; + } + + /* STARTTLS failed */ + if (numeric == 691) + { + sendto_umode(UMODE_OPER, "STARTTLS failed for link %s. Please check the other side of the link.", cptr->name); + reject_insecure_server(cptr); + return 0; + } + + /* STARTTLS OK */ + if (numeric == 670) + { + int ret = client_starttls(cptr); + if (ret < 0) + { + sendto_umode(UMODE_OPER, "STARTTLS handshake failed for link %s. Strange.", cptr->name); + reject_insecure_server(cptr); + return ret; + } + /* We don't call start_server_handshake() here. First the TLS handshake will + * be completed, then completed_connection() will be called for a second time, + * which will call completed_connection() from there. + */ + return 0; + } } if (parc < 1 || !IsServer(sptr))