From 6778b3e26d9a976bd55afac78f9fd3977f0a1fb0 Mon Sep 17 00:00:00 2001 From: Bram Matthys Date: Sun, 11 Oct 2020 14:53:40 +0200 Subject: [PATCH] Warn when SSL/TLS certificate is expired or expires soon (<7d). Since an expired certificate usually means that users cannot connect we will actively warn all IRCOps about this situation twice a day. --- include/h.h | 23 +++++++++-- include/struct.h | 12 ------ src/api-event.c | 1 + src/conf.c | 1 + src/tls.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++- 5 files changed, 119 insertions(+), 18 deletions(-) diff --git a/include/h.h b/include/h.h index 23fbe7728..12f878f32 100644 --- a/include/h.h +++ b/include/h.h @@ -549,10 +549,6 @@ extern void *safe_alloc(size_t size); extern char *our_strdup(const char *str); extern char *our_strldup(const char *str, size_t max); -extern MODFUNC char *tls_get_cipher(SSL *ssl); -extern TLSOptions *get_tls_options_for_client(Client *acptr); -extern int outdated_tls_client(Client *acptr); -extern char *outdated_tls_client_build_string(char *pattern, Client *acptr); extern long config_checkval(char *value, unsigned short flags); extern void config_status(FORMAT_STRING(const char *format), ...) __attribute__((format(printf,1,2))); extern void init_random(); @@ -753,6 +749,25 @@ extern MODVAR void (*labeled_response_force_end)(void); extern MODVAR void (*kick_user)(MessageTag *mtags, Channel *channel, Client *client, Client *victim, char *comment); /* /Efuncs */ +/* SSL/TLS functions */ +extern int early_init_ssl(); +extern int init_ssl(); +extern int ssl_handshake(Client *); /* Handshake the accpeted con.*/ +extern int ssl_client_handshake(Client *, ConfigItem_link *); /* and the initiated con.*/ +extern int ircd_SSL_accept(Client *acptr, int fd); +extern int ircd_SSL_connect(Client *acptr, int fd); +extern int SSL_smart_shutdown(SSL *ssl); +extern void ircd_SSL_client_handshake(int, int, void *); +extern void SSL_set_nonblocking(SSL *s); +extern SSL_CTX *init_ctx(TLSOptions *tlsoptions, int server); +extern MODFUNC char *tls_get_cipher(SSL *ssl); +extern TLSOptions *get_tls_options_for_client(Client *acptr); +extern int outdated_tls_client(Client *acptr); +extern char *outdated_tls_client_build_string(char *pattern, Client *acptr); +extern int check_certificate_expiry_ctx(SSL_CTX *ctx, char **errstr); +extern EVENT(tls_check_expiry); +/* End of SSL/TLS functions */ + extern void parse_message_tags_default_handler(Client *client, char **str, MessageTag **mtag_list); extern char *mtags_to_string_default_handler(MessageTag *m, Client *client); extern void *labeled_response_save_context_default_handler(void); diff --git a/include/struct.h b/include/struct.h index c9a0f8374..4e99b0d3a 100644 --- a/include/struct.h +++ b/include/struct.h @@ -2011,18 +2011,6 @@ extern MODVAR SSL_CTX *ctx; extern MODVAR SSL_CTX *ctx_server; extern MODVAR SSL_CTX *ctx_client; -extern SSL_METHOD *meth; -extern int early_init_ssl(); -extern int init_ssl(); -extern int ssl_handshake(Client *); /* Handshake the accpeted con.*/ -extern int ssl_client_handshake(Client *, ConfigItem_link *); /* and the initiated con.*/ -extern int ircd_SSL_accept(Client *acptr, int fd); -extern int ircd_SSL_connect(Client *acptr, int fd); -extern int SSL_smart_shutdown(SSL *ssl); -extern void ircd_SSL_client_handshake(int, int, void *); -extern void SSL_set_nonblocking(SSL *s); -extern SSL_CTX *init_ctx(TLSOptions *tlsoptions, int server); - #define TLS_PROTOCOL_TLSV1 0x0001 #define TLS_PROTOCOL_TLSV1_1 0x0002 #define TLS_PROTOCOL_TLSV1_2 0x0004 diff --git a/src/api-event.c b/src/api-event.c index 2044e5be4..86ad8b6e4 100644 --- a/src/api-event.c +++ b/src/api-event.c @@ -240,4 +240,5 @@ void SetupEvents(void) EventAdd(NULL, "check_deadsockets", check_deadsockets, NULL, 1000, 0); EventAdd(NULL, "handshake_timeout", handshake_timeout, NULL, 1000, 0); EventAdd(NULL, "try_connections", try_connections, NULL, 2000, 0); + EventAdd(NULL, "tls_check_expiry", tls_check_expiry, NULL, 86400/2, 0); } diff --git a/src/conf.c b/src/conf.c index 4eb051f41..ebe9a2830 100644 --- a/src/conf.c +++ b/src/conf.c @@ -1902,6 +1902,7 @@ void postconf(void) postconf_fixes(); do_weird_shun_stuff(); isupport_init(); /* for all the 005 values that changed.. */ + tls_check_expiry(NULL); } int isanyserverlinked(void) diff --git a/src/tls.c b/src/tls.c index f30a24099..4ae1252f4 100644 --- a/src/tls.c +++ b/src/tls.c @@ -36,9 +36,10 @@ extern HWND hwIRCDWnd; #define SAFE_SSL_ACCEPT 3 #define SAFE_SSL_CONNECT 4 +/* Forward declarations */ static int fatal_ssl_error(int ssl_error, int where, int my_errno, Client *client); -extern int cipher_check(SSL_CTX *ctx, char **errstr); -extern int certificate_quality_check(SSL_CTX *ctx, char **errstr); +int cipher_check(SSL_CTX *ctx, char **errstr); +int certificate_quality_check(SSL_CTX *ctx, char **errstr); /* The SSL structures */ SSL_CTX *ctx_server; @@ -1040,6 +1041,8 @@ int verify_certificate(SSL *ssl, char *hostname, char **errstr) if (SSL_get_verify_result(ssl) != X509_V_OK) { + // FIXME: there are actually about 25+ different possible errors, + // this is only the most common one: strlcpy(buf, "Certificate is not issued by a trusted Certificate Authority", sizeof(buf)); if (errstr) *errstr = buf; @@ -1333,3 +1336,96 @@ char *outdated_tls_client_build_string(char *pattern, Client *client) buildvarstring(pattern, buf, sizeof(buf), name, value); return buf; } + +int check_certificate_expiry_ctx(SSL_CTX *ctx, char **errstr) +{ + static char errbuf[512]; + SSL *ssl; + X509 *cert; + const ASN1_TIME *cert_expiry_time; + int days_expiry = 0, seconds_expiry = 0; + long duration; + + *errstr = NULL; + + ssl = SSL_new(ctx); + if (!ssl) + return 0; + + cert = SSL_get_certificate(ssl); + if (!cert) + { + SSL_free(ssl); + return 0; + } + + /* get certificate time */ + cert_expiry_time = X509_get0_notAfter(cert); + + /* calculate difference */ + ASN1_TIME_diff(&days_expiry, &seconds_expiry, cert_expiry_time, NULL); + duration = (days_expiry * 86400) + seconds_expiry; + + /* certificate expiry? */ + if ((days_expiry > 0) || (seconds_expiry > 0)) + { + snprintf(errbuf, sizeof(errbuf), "certificate expired %s ago", pretty_time_val(duration)); + SSL_free(ssl); + *errstr = errbuf; + return 1; + } else + /* or near-expiry? */ + if (((days_expiry < 0) || (seconds_expiry < 0)) && (days_expiry > -7)) + { + snprintf(errbuf, sizeof(errbuf), "certificate will expire in %s", pretty_time_val(0 - duration)); + SSL_free(ssl); + *errstr = errbuf; + return 1; + } + + /* All good */ + SSL_free(ssl); + return 0; +} + +void check_certificate_expiry_tlsoptions_and_warn(TLSOptions *tlsoptions) +{ + SSL_CTX *ctx; + int ret; + char *errstr = NULL; + + ctx = init_ctx(tlsoptions, 1); + if (!ctx) + return; + + if (check_certificate_expiry_ctx(ctx, &errstr)) + { + sendto_umode_global(UMODE_OPER, "Warning: TLS certificate '%s': %s", tlsoptions->certificate_file, errstr); + ircd_log(LOG_ERROR, "[warning] TLS certificate '%s': %s", tlsoptions->certificate_file, errstr); + } + SSL_CTX_free(ctx); +} + +EVENT(tls_check_expiry) +{ + ConfigItem_listen *listen; + ConfigItem_sni *sni; + ConfigItem_link *link; + + /* set block */ + check_certificate_expiry_tlsoptions_and_warn(iConf.tls_options); + + for (listen = conf_listen; listen; listen = listen->next) + if (listen->tls_options) + check_certificate_expiry_tlsoptions_and_warn(listen->tls_options); + + /* sni::tls-options.... */ + for (sni = conf_sni; sni; sni = sni->next) + if (sni->tls_options) + check_certificate_expiry_tlsoptions_and_warn(sni->tls_options); + + /* link::outgoing::tls-options.... */ + for (link = conf_link; link; link = link->next) + if (link->tls_options) + check_certificate_expiry_tlsoptions_and_warn(link->tls_options); +}