ソースを参照

Cherry-pick "Support for server certificate validation on server links [S2S-TLS]"

Christoph Biedl 2 年 前
コミット
0599764fd6
19 ファイル変更1761 行追加0 行削除
  1. 682 0
      debian/patches/0002-Support-for-server-certificate-validation-on-server-.patch
  2. 28 0
      debian/patches/0003-S2S-TLS-Add-missing-CAFile-and-CRLFile-options-to-co.patch
  3. 47 0
      debian/patches/0004-S2S-TLS-Remove-leftover-debug-messages.patch
  4. 58 0
      debian/patches/0005-S2S-TLS-OpenSSL-Always-setup-host-name-verification.patch
  5. 45 0
      debian/patches/0006-S2S-TLS-OpenSSL-Set-the-verification-flags-only-once.patch
  6. 81 0
      debian/patches/0007-S2S-TLS-OpenSSL-Fix-handling-of-certificate-informat.patch
  7. 72 0
      debian/patches/0008-S2S-TLS-OpenSSL-Postpone-verification-of-TLS-session.patch
  8. 93 0
      debian/patches/0009-S2S-TLS-OpenSSL-Streamline-logging.patch
  9. 38 0
      debian/patches/0010-S2S-TLS-Fix-formatting-and-sort-new-SSL-options-in-n.patch
  10. 30 0
      debian/patches/0011-S2S-TLS-MAX_CERT_CHAIN_LENGTH-is-only-used-by-OpenSS.patch
  11. 27 0
      debian/patches/0012-S2S-TLS-GnuTLS-Update-SSL-code-for-GnuTLS-certificat.patch
  12. 118 0
      debian/patches/0013-S2S-TLS-GnuTLS-Fix-handling-of-certificate-informati.patch
  13. 49 0
      debian/patches/0014-S2S-TLS-GnuTLS-Streamline-logging.patch
  14. 27 0
      debian/patches/0015-S2S-TLS-Verify-the-TLS-certificates-by-default.patch
  15. 34 0
      debian/patches/0016-S2S-TLS-GnuTLS-Fix-handling-of-connections-without-p.patch
  16. 246 0
      debian/patches/0017-S2S-TLS-Convert-SSL.txt-to-Markdown-and-update-infor.patch
  17. 33 0
      debian/patches/0018-S2S-TLS-Add-notice-to-INSTALL.md.patch
  18. 35 0
      debian/patches/0019-S2S-TLS-Fix-make-check-in-separate-build-directory.patch
  19. 18 0
      debian/patches/series

+ 682 - 0
debian/patches/0002-Support-for-server-certificate-validation-on-server-.patch

@@ -0,0 +1,682 @@
+From 315125a8bcf6d80c969a3310c43b486b0e422e5b Mon Sep 17 00:00:00 2001
+From: Christoph Biedl <ngircd.anoy@manchmal.in-ulm.de>
+Date: Sun, 2 Nov 2014 14:48:34 +0100
+Subject: [PATCH 02/20] Support for server certificate validation on server
+ links [S2S-TLS]
+
+This patch provides code to validate the server certificate in
+server links, defeating nasty man-in-the-middle attacks on server
+links.
+
+Features:
+
+- Check whether the certificate is signed by a trusted certificate
+  authority (CA).
+- Check the host name, including wildcard certificates and Subject
+  Alternative Names.
+- Optionally check against a certificate revocation list (CRL).
+- Implementation for both OpenSSL and GnuTLS linkage.
+
+Left for another day:
+
+- Parameterize the TLS parameter of an outbound connection. Currently,
+  it's hardcoded to disable all versions before TLSv1.1.
+- Using certificate as CA-certificate. They work for GnuTLS only but
+  perhaps this should rather raise an error there, too.
+- Optional OCSP checking.
+- Checking client certificates. Code is there but this first needs some
+  consideration about the use cases. This could replace all other
+  authentication methods, for both client-server and server-server
+  connections.
+
+This patch is based on a patch by Florian Westphal from 2009, which
+implemented this for OpenSSL only:
+
+  From: Florian Westphal <fw@strlen.de>
+  Date: Mon, 18 May 2009 00:29:02 +0200
+  Subject: SSL/TLS: Add initial certificate support to OpenSSL backend
+
+Commit message modified by Alex Barton.
+
+Closes #120, "Server links using TLS/SSL need certificate validation".
+Supersedes PR #8, "Options for verifying and requiring SSL client
+certificates", which had (incomplete?) code for OpenSSL, no GnuTLS.
+
+(cherry picked from commit 817937b218c4b57515f54216ebc936cd69df0aae)
+---
+ doc/sample-ngircd.conf.tmpl |  11 ++
+ man/ngircd.conf.5.tmpl      |  10 ++
+ src/ngircd/conf.c           |  27 ++-
+ src/ngircd/conf.h           |   3 +
+ src/ngircd/conn-ssl.c       | 323 ++++++++++++++++++++++++++++++++++--
+ src/ngircd/conn.c           |  21 +++
+ src/ngircd/conn.h           |   3 +-
+ 7 files changed, 385 insertions(+), 13 deletions(-)
+
+--- a/doc/sample-ngircd.conf.tmpl
++++ b/doc/sample-ngircd.conf.tmpl
+@@ -266,6 +266,13 @@
+ 	# is only available when ngIRCd is compiled with support for SSL!
+ 	# So don't forget to remove the ";" above if this is the case ...
+ 
++	# SSL Trusted CA Certificates File (for verifying peer certificates)
++	;CAFile = /etc/ssl/CA/cacert.pem
++
++	# Certificate Revocation File (for marking otherwise valid
++	# certficates as invalid)
++	;CRLFile = /etc/ssl/CA/crl.pem
++
+ 	# SSL Server Key Certificate
+ 	;CertFile = :ETCDIR:/ssl/server-cert.pem
+ 
+@@ -357,6 +364,10 @@
+ 	# Connect to the remote server using TLS/SSL (Default: false)
+ 	;SSLConnect = yes
+ 
++	# Verify the TLS certificate presented by the remote server
++	# (Default: yes)
++	;SSLVerify = yes
++
+ 	# Define a (case insensitive) list of masks matching nicknames that
+ 	# should be treated as IRC services when introduced via this remote
+ 	# server, separated by commas (",").
+--- a/man/ngircd.conf.5.tmpl
++++ b/man/ngircd.conf.5.tmpl
+@@ -385,6 +385,13 @@
+ section. Please note that this whole section is only recognized by ngIRCd
+ when it is compiled with support for SSL using OpenSSL or GnuTLS!
+ .TP
++\fBCAFile (string)\fR
++Filename pointing to the Trusted CA Certificates. This is required for
++verifying peer certificates.
++.TP
++\fBCRLFile (string)\fR
++Filename of Certificate Revocation List.
++.TP
+ \fBCertFile\fR (string)
+ SSL Certificate file of the private server key.
+ .TP
+@@ -479,6 +486,9 @@
+ \fBSSLConnect\fR (boolean)
+ Connect to the remote server using TLS/SSL. Default: false.
+ .TP
++\fBSSLVerify\fR (boolean)
++Verify the TLS certificate presented by the remote server. Default: yes.
++.TP
+ \fBServiceMask\fR (string)
+ Define a (case insensitive) list of masks matching nicknames that should be
+ treated as IRC services when introduced via this remote server, separated
+--- a/src/ngircd/conf.c
++++ b/src/ngircd/conf.c
+@@ -112,6 +112,12 @@
+ 	free(Conf_SSLOptions.CertFile);
+ 	Conf_SSLOptions.CertFile = NULL;
+ 
++	free(Conf_SSLOptions.CAFile);
++	Conf_SSLOptions.CAFile = NULL;
++
++	free(Conf_SSLOptions.CRLFile);
++	Conf_SSLOptions.CRLFile = NULL;
++
+ 	free(Conf_SSLOptions.DHFile);
+ 	Conf_SSLOptions.DHFile = NULL;
+ 	array_free_wipe(&Conf_SSLOptions.KeyFilePassword);
+@@ -464,7 +470,10 @@
+ 		printf( "  Host = %s\n", Conf_Server[i].host );
+ 		printf( "  Port = %u\n", (unsigned int)Conf_Server[i].port );
+ #ifdef SSL_SUPPORT
+-		printf( "  SSLConnect = %s\n", Conf_Server[i].SSLConnect?"yes":"no");
++		printf("  SSLConnect = %s\n",
++		       yesno_to_str(Conf_Server[i].SSLConnect));
++		printf("  SSLVerify = %s\n",
++		       yesno_to_str(Conf_Server[i].SSLVerify));
+ #endif
+ 		printf( "  MyPassword = %s\n", Conf_Server[i].pwd_in );
+ 		printf( "  PeerPassword = %s\n", Conf_Server[i].pwd_out );
+@@ -1774,6 +1783,16 @@
+ 		Conf_SSLOptions.CipherList = strdup_warn(Arg);
+ 		return;
+ 	}
++	if (strcasecmp(Var, "CAFile") == 0) {
++		assert(Conf_SSLOptions.CAFile == NULL);
++		Conf_SSLOptions.CAFile = strdup_warn(Arg);
++		return;
++	}
++	if (strcasecmp(Var, "CRLFile") == 0) {
++		assert(Conf_SSLOptions.CRLFile == NULL);
++		Conf_SSLOptions.CRLFile = strdup_warn(Arg);
++		return;
++	}
+ 
+ 	Config_Error_Section(File, Line, Var, "SSL");
+ }
+@@ -1904,7 +1923,11 @@
+ 	if( strcasecmp( Var, "SSLConnect" ) == 0 ) {
+ 		New_Server.SSLConnect = Check_ArgIsTrue(Arg);
+ 		return;
+-        }
++	}
++	if (strcasecmp(Var, "SSLVerify") == 0) {
++		New_Server.SSLVerify = Check_ArgIsTrue(Arg);
++		return;
++	}
+ #endif
+ 	if( strcasecmp( Var, "Group" ) == 0 ) {
+ 		/* Server group */
+--- a/src/ngircd/conf.h
++++ b/src/ngircd/conf.h
+@@ -61,6 +61,7 @@
+ 	ng_ipaddr_t dst_addr[2];	/**< List of addresses to connect to */
+ #ifdef SSL_SUPPORT
+ 	bool SSLConnect;		/**< Establish connection using SSL? */
++	bool SSLVerify;			/**< Verify server certificate using CA? */
+ #endif
+ 	char svs_mask[CLIENT_ID_LEN];	/**< Mask of nicknames that should be
+ 					     treated and counted as services */
+@@ -76,6 +77,8 @@
+ 	array ListenPorts;		/**< Array of listening SSL ports */
+ 	array KeyFilePassword;		/**< Key file password */
+ 	char *CipherList;		/**< Set SSL cipher list to use */
++	char *CAFile;			/**< Trusted CA certificates file */
++	char *CRLFile;			/**< Certificate revocation file */
+ };
+ #endif
+ 
+--- a/src/ngircd/conn-ssl.c
++++ b/src/ngircd/conn-ssl.c
+@@ -43,13 +43,17 @@
+ #include <openssl/err.h>
+ #include <openssl/rand.h>
+ #include <openssl/dh.h>
++#include <openssl/x509v3.h>
+ 
+ static SSL_CTX * ssl_ctx;
+ static DH *dh_params;
+ 
+ static bool ConnSSL_LoadServerKey_openssl PARAMS(( SSL_CTX *c ));
++static bool ConnSSL_SetVerifyProperties_openssl PARAMS((SSL_CTX * c));
+ #endif
+ 
++#define MAX_CERT_CHAIN_LENGTH	10	/* XXX: do not hardcode */
++
+ #ifdef HAVE_LIBGNUTLS
+ #include <sys/types.h>
+ #include <sys/stat.h>
+@@ -74,6 +78,7 @@
+ static gnutls_dh_params_t dh_params;
+ static gnutls_priority_t priorities_cache = NULL;
+ static bool ConnSSL_LoadServerKey_gnutls PARAMS(( void ));
++static bool ConnSSL_SetVerifyProperties_gnutls PARAMS((void));
+ #endif
+ 
+ #define SHA256_STRING_LEN	(32 * 2 + 1)
+@@ -131,10 +136,38 @@
+ /**
+  * Log OpenSSL error message.
+  *
++ * @param level The log level
+  * @param msg The error message.
+  * @param info Additional information text or NULL.
+  */
+ static void
++LogOpenSSL_CertInfo(int level, X509 * cert, const char *msg)
++{
++	BIO *mem;
++	char *memptr;
++	long len;
++
++	assert(cert);
++	assert(msg);
++
++	if (!cert)
++		return;
++	mem = BIO_new(BIO_s_mem());
++	if (!mem)
++		return;
++	X509_NAME_print_ex(mem, X509_get_subject_name(cert), 4,
++			   XN_FLAG_ONELINE);
++	X509_NAME_print_ex(mem, X509_get_issuer_name(cert), 4, XN_FLAG_ONELINE);
++	if (BIO_write(mem, "", 1) == 1) {
++		len = BIO_get_mem_data(mem, &memptr);
++		if (memptr && len > 0)
++			Log(level, "%s: \"%s\"", msg, memptr);
++	}
++	(void)BIO_set_close(mem, BIO_CLOSE);
++	BIO_free(mem);
++}
++
++static void
+ LogOpenSSLError(const char *error, const char *info)
+ {
+ 	unsigned long err = ERR_get_error();
+@@ -176,9 +209,16 @@
+ 
+ 
+ static int
+-Verify_openssl(UNUSED int preverify_ok, UNUSED X509_STORE_CTX *x509_ctx)
++Verify_openssl(int preverify_ok, X509_STORE_CTX * ctx)
+ {
+-	return 1;
++	int err;
++
++	if (!preverify_ok) {
++		err = X509_STORE_CTX_get_error(ctx);
++		Log(LOG_ERR, "Certificate validation failed: %s",
++		    X509_verify_cert_error_string(err));
++	}
++	return preverify_ok;
+ }
+ #endif
+ 
+@@ -354,7 +394,12 @@
+ 	}
+ 
+ 	SSL_CTX_set_session_id_context(newctx, (unsigned char *)"ngircd", 6);
+-	SSL_CTX_set_options(newctx, SSL_OP_SINGLE_DH_USE|SSL_OP_NO_SSLv2);
++	if (!ConnSSL_SetVerifyProperties_openssl(newctx))
++		goto out;
++	SSL_CTX_set_options(newctx,
++			    SSL_OP_SINGLE_DH_USE | SSL_OP_NO_SSLv2 |
++			    SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 |
++			    SSL_OP_NO_COMPRESSION);
+ 	SSL_CTX_set_mode(newctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
+ 	SSL_CTX_set_verify(newctx, SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE,
+ 			   Verify_openssl);
+@@ -394,6 +439,9 @@
+ 		goto out;
+ 	}
+ 
++	if (!ConnSSL_SetVerifyProperties_gnutls())
++		goto out;
++
+ 	Log(LOG_INFO, "GnuTLS %s initialized.", gnutls_check_version(NULL));
+ 	initialized = true;
+ 	return true;
+@@ -406,6 +454,37 @@
+ 
+ #ifdef HAVE_LIBGNUTLS
+ static bool
++ConnSSL_SetVerifyProperties_gnutls(void)
++{
++	int err;
++
++	if (!Conf_SSLOptions.CAFile)
++		return true;
++
++	err = gnutls_certificate_set_x509_trust_file(x509_cred,
++						     Conf_SSLOptions.CAFile,
++						     GNUTLS_X509_FMT_PEM);
++	if (err < 0) {
++		Log(LOG_ERR, "Failed to load x509 trust file %s: %s",
++		    Conf_SSLOptions.CAFile, gnutls_strerror(err));
++		return false;
++	}
++	if (Conf_SSLOptions.CRLFile) {
++		err =
++		    gnutls_certificate_set_x509_crl_file(x509_cred,
++							 Conf_SSLOptions.CRLFile,
++							 GNUTLS_X509_FMT_PEM);
++		if (err < 0) {
++			Log(LOG_ERR, "Failed to load x509 crl file %s: %s",
++			    Conf_SSLOptions.CRLFile, gnutls_strerror(err));
++			return false;
++		}
++	}
++	return true;
++}
++
++
++static bool
+ ConnSSL_LoadServerKey_gnutls(void)
+ {
+ 	int err;
+@@ -531,6 +610,56 @@
+ }
+ 
+ 
++static bool
++ConnSSL_SetVerifyProperties_openssl(SSL_CTX * ctx)
++{
++	X509_STORE *store = NULL;
++	X509_LOOKUP *lookup;
++	int verify_flags = SSL_VERIFY_PEER;
++	bool ret = false;
++
++	if (!Conf_SSLOptions.CAFile)
++		return true;
++
++	if (SSL_CTX_load_verify_locations(ctx, Conf_SSLOptions.CAFile, NULL) !=
++	    1) {
++		LogOpenSSLError("SSL_CTX_load_verify_locations", NULL);
++		goto out;
++	}
++
++	if (Conf_SSLOptions.CRLFile) {
++		X509_VERIFY_PARAM *param = X509_VERIFY_PARAM_new();
++		X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_CRL_CHECK);
++		SSL_CTX_set1_param(ctx, param);
++
++		store = SSL_CTX_get_cert_store(ctx);
++		assert(store);
++		lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
++		if (!lookup) {
++			LogOpenSSLError("X509_STORE_add_lookup",
++					Conf_SSLOptions.CRLFile);
++			goto out;
++		}
++
++		if (X509_load_crl_file
++		    (lookup, Conf_SSLOptions.CRLFile, X509_FILETYPE_PEM) != 1) {
++			LogOpenSSLError("X509_load_crl_file",
++					Conf_SSLOptions.CRLFile);
++			goto out;
++		}
++	}
++
++	SSL_CTX_set_verify(ctx, verify_flags, Verify_openssl);
++	SSL_CTX_set_verify_depth(ctx, MAX_CERT_CHAIN_LENGTH);
++	ret = true;
++out:
++	if (Conf_SSLOptions.CRLFile)
++		free(Conf_SSLOptions.CRLFile);
++	Conf_SSLOptions.CRLFile = NULL;
++	return ret;
++}
++
++
+ #endif
+ static bool
+ ConnSSL_Init_SSL(CONNECTION *c)
+@@ -602,7 +731,7 @@
+ 
+ 
+ bool
+-ConnSSL_PrepareConnect(CONNECTION *c, UNUSED CONF_SERVER *s)
++ConnSSL_PrepareConnect(CONNECTION * c, CONF_SERVER * s)
+ {
+ 	bool ret;
+ #ifdef HAVE_LIBGNUTLS
+@@ -613,7 +742,7 @@
+ 		Log(LOG_ERR, "Failed to initialize new SSL session: %s",
+ 		    gnutls_strerror(err));
+ 		return false;
+-        }
++	}
+ #endif
+ 	ret = ConnSSL_Init_SSL(c);
+ 	if (!ret)
+@@ -621,7 +750,23 @@
+ 	Conn_OPTION_ADD(c, CONN_SSL_CONNECT);
+ #ifdef HAVE_LIBSSL
+ 	assert(c->ssl_state.ssl);
+-	SSL_set_verify(c->ssl_state.ssl, SSL_VERIFY_NONE, NULL);
++	if (s->SSLVerify) {
++		X509_VERIFY_PARAM *param = NULL;
++		param = SSL_get0_param(c->ssl_state.ssl);
++		X509_VERIFY_PARAM_set_hostflags(param,
++						X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
++Log(LOG_ERR, "DEBUG: Setting up hostname verification for '%s'", s->host);
++		int err = X509_VERIFY_PARAM_set1_host(param, s->host, 0);
++		if (err != 1) {
++			Log(LOG_ERR,
++			    "Cannot set up hostname verification for '%s': %u",
++			    s->host, err);
++			return false;
++		}
++		SSL_set_verify(c->ssl_state.ssl, SSL_VERIFY_PEER,
++			       Verify_openssl);
++	} else
++		SSL_set_verify(c->ssl_state.ssl, SSL_VERIFY_NONE, NULL);
+ #endif
+ 	return true;
+ }
+@@ -720,18 +865,102 @@
+ }
+ 
+ 
++#ifdef HAVE_LIBGNUTLS
++static void *
++LogMalloc(size_t s)
++{
++	void *mem = malloc(s);
++	if (!mem)
++		Log(LOG_ERR, "Out of memory: Could not allocate %lu byte",
++		    (unsigned long)s);
++	return mem;
++}
++
++
+ static void
+-ConnSSL_LogCertInfo( CONNECTION *c )
++LogGnuTLS_CertInfo(int level, gnutls_x509_crt_t cert, const char *msg)
+ {
++	char *dn, *issuer_dn;
++	size_t size = 0;
++	int err = gnutls_x509_crt_get_dn(cert, NULL, &size);
++	if (size == 0) {
++		Log(LOG_ERR, "gnutls_x509_crt_get_dn: size == 0");
++		return;
++	}
++	if (err && err != GNUTLS_E_SHORT_MEMORY_BUFFER)
++		goto err_crt_get;
++	dn = LogMalloc(size);
++	if (!dn)
++		return;
++	err = gnutls_x509_crt_get_dn(cert, dn, &size);
++	if (err)
++		goto err_crt_get;
++	gnutls_x509_crt_get_issuer_dn(cert, NULL, &size);
++	assert(size);
++	issuer_dn = LogMalloc(size);
++	if (!issuer_dn) {
++		Log(level, "%s: Distinguished Name: %s", msg, dn);
++		free(dn);
++		return;
++	}
++	gnutls_x509_crt_get_issuer_dn(cert, issuer_dn, &size);
++	Log(level, "%s: Distinguished Name: \"%s\", Issuer \"%s\"", msg, dn,
++	    issuer_dn);
++	free(dn);
++	free(issuer_dn);
++	return;
++
++      err_crt_get:
++	Log(LOG_ERR, "gnutls_x509_crt_get_dn: %s", gnutls_strerror(err));
++	return;
++}
++#endif
++
++
++static void
++ConnSSL_LogCertInfo( CONNECTION * c, bool connect)
++{
++	bool cert_seen = false, cert_ok = false;
++	char msg[128];
+ #ifdef HAVE_LIBSSL
++	const char *comp_alg = "no compression";
++	const void *comp;
++	X509 *peer_cert = NULL;
+ 	SSL *ssl = c->ssl_state.ssl;
+ 
+ 	assert(ssl);
+ 
+-	Log(LOG_INFO, "Connection %d: initialized %s using cipher %s.",
+-		c->sock, SSL_get_version(ssl), SSL_get_cipher(ssl));
++	comp = SSL_get_current_compression(ssl);
++	if (comp)
++		comp_alg = SSL_COMP_get_name(comp);
++	Log(LOG_INFO, "Connection %d: initialized %s using cipher %s, %s.",
++	    c->sock, SSL_get_version(ssl), SSL_get_cipher(ssl), comp_alg);
++	peer_cert = SSL_get_peer_certificate(ssl);
++	if (peer_cert && connect) {
++		cert_seen = true;
++		/* Client: Check server certificate */
++		int err = SSL_get_verify_result(ssl);
++		if (err == X509_V_OK) {
++			const char *peername = SSL_get0_peername(ssl);
++			if (peername != NULL)
++				cert_ok = true;
++
++			Log(LOG_ERR, "X509_V_OK, peername = '%s'", peername);
++
++		} else
++			Log(LOG_ERR, "Certificate validation failed: %s",
++			    X509_verify_cert_error_string(err));
++		snprintf(msg, sizeof(msg), "%svalid peer certificate",
++			 cert_ok ? "" : "in");
++		LogOpenSSL_CertInfo(cert_ok ? LOG_DEBUG : LOG_ERR, peer_cert,
++				    msg);
++
++		X509_free(peer_cert);
++	}
+ #endif
+ #ifdef HAVE_LIBGNUTLS
++	unsigned int status;
++	gnutls_credentials_type_t cred;
+ 	gnutls_session_t sess = c->ssl_state.gnutls_session;
+ 	gnutls_cipher_algorithm_t cipher = gnutls_cipher_get(sess);
+ 
+@@ -740,7 +969,81 @@
+ 	    gnutls_protocol_get_name(gnutls_protocol_get_version(sess)),
+ 	    gnutls_cipher_get_name(cipher),
+ 	    gnutls_mac_get_name(gnutls_mac_get(sess)));
++	cred = gnutls_auth_get_type(c->ssl_state.gnutls_session);
++	if (cred == GNUTLS_CRD_CERTIFICATE && connect) {
++		cert_seen = true;
++		int verify =
++		    gnutls_certificate_verify_peers2(c->
++						     ssl_state.gnutls_session,
++						     &status);
++Log(LOG_ERR, "DEBUG: verify = %d", verify);
++		if (verify < 0) {
++			Log(LOG_ERR,
++			    "gnutls_certificate_verify_peers2 failed: %s",
++			    gnutls_strerror(verify));
++			goto done_cn_validation;
++		} else if (status) {
++			gnutls_datum_t out;
++
++			if (gnutls_certificate_verification_status_print
++			    (status, gnutls_certificate_type_get(sess), &out,
++			     0) == GNUTLS_E_SUCCESS) {
++				Log(LOG_ERR,
++				    "Certificate validation failed: %s",
++				    out.data);
++				gnutls_free(out.data);
++			}
++		}
++Log(LOG_ERR, "DEBUG: status = %d", status);
++
++		gnutls_x509_crt_t cert;
++		unsigned cert_list_size;
++		const gnutls_datum_t *cert_list =
++		    gnutls_certificate_get_peers(sess, &cert_list_size);
++		if (!cert_list || cert_list_size == 0) {
++			Log(LOG_ERR, "No certificates found");
++			goto done_cn_validation;
++		}
++		int err = gnutls_x509_crt_init(&cert);
++		if (err < 0) {
++			Log(LOG_ERR,
++			    "Failed to initialize x509 certificate: %s",
++			    gnutls_strerror(err));
++			goto done_cn_validation;
++		}
++		err = gnutls_x509_crt_import(cert, cert_list,
++					   GNUTLS_X509_FMT_DER);
++		if (err < 0) {
++			Log(LOG_ERR, "Failed to parse the certificate: %s",
++			    gnutls_strerror(err));
++			goto done_cn_validation;
++		}
++		err = gnutls_x509_crt_check_hostname(cert, c->host);
++		if (err == 0)
++			Log(LOG_ERR,
++			    "Failed to verify the hostname, expected \"%s\"",
++			    c->host);
++		else
++			cert_ok = verify == 0 && status == 0;
++
++		snprintf(msg, sizeof(msg), "%svalid peer certificate",
++			cert_ok ? "" : "in");
++		LogGnuTLS_CertInfo(cert_ok ? LOG_DEBUG : LOG_ERR, cert, msg);
++
++		gnutls_x509_crt_deinit(cert);
++done_cn_validation:
++		;
++	}
+ #endif
++	/*
++	 * can be used later to check if connection was authenticated, e.g.
++	 * if inbound connection tries to register itself as server.
++	 * Could also restrict /OPER to authenticated connections, etc.
++	 */
++	if (cert_ok)
++		Conn_OPTION_ADD(c, CONN_SSL_PEERCERT_OK);
++	if (!cert_seen)
++		Log(LOG_INFO, "Peer did not present a certificate");
+ }
+ 
+ 
+@@ -879,7 +1182,7 @@
+ 	(void)ConnSSL_InitCertFp(c);
+ 
+ 	Conn_OPTION_DEL(c, (CONN_SSL_WANT_WRITE|CONN_SSL_WANT_READ|CONN_SSL_CONNECT));
+-	ConnSSL_LogCertInfo(c);
++	ConnSSL_LogCertInfo(c, connect);
+ 
+ 	Conn_StartLogin(CONNECTION2ID(c));
+ 	return 1;
+--- a/src/ngircd/conn.c
++++ b/src/ngircd/conn.c
+@@ -2546,6 +2546,7 @@
+ cb_connserver_login_ssl(int sock, short unused)
+ {
+ 	CONN_ID idx = Socket2Index(sock);
++	int serveridx;
+ 
+ 	(void) unused;
+ 
+@@ -2564,10 +2565,30 @@
+ 			return;
+ 	}
+ 
++	serveridx = Conf_GetServer(idx);
++	assert(serveridx >= 0);
++	if (serveridx < 0)
++		goto err;
++
+ 	Log( LOG_INFO, "SSL connection %d with \"%s:%d\" established.", idx,
+ 	    My_Connections[idx].host, Conf_Server[Conf_GetServer( idx )].port );
+ 
++	if (!Conn_OPTION_ISSET(&My_Connections[idx], CONN_SSL_PEERCERT_OK)) {
++		if (Conf_Server[serveridx].SSLVerify) {
++			Log(LOG_ERR,
++				"SSLVerify enabled for %d, but peer certificate check failed",
++				idx);
++			goto err;
++		}
++		Log(LOG_WARNING,
++			"Peer certificate check failed for %d, but SSLVerify is disabled, continuing",
++			idx);
++	}
+ 	server_login(idx);
++	return;
++      err:
++	Log(LOG_ERR, "SSL connection on socket %d failed!", sock);
++	Conn_Close(idx, "Can't connect!", NULL, false);
+ }
+ 
+ 
+--- a/src/ngircd/conn.h
++++ b/src/ngircd/conn.h
+@@ -40,7 +40,8 @@
+ #define CONN_SSL		32	/* this connection is SSL encrypted */
+ #define CONN_SSL_WANT_WRITE	64	/* SSL/TLS library needs to write protocol data */
+ #define CONN_SSL_WANT_READ	128	/* SSL/TLS library needs to read protocol data */
+-#define CONN_SSL_FLAGS_ALL	(CONN_SSL_CONNECT|CONN_SSL|CONN_SSL_WANT_WRITE|CONN_SSL_WANT_READ)
++#define CONN_SSL_PEERCERT_OK	256	/* peer presented a valid certificate (used to check inbound server auth */
++#define CONN_SSL_FLAGS_ALL	(CONN_SSL_CONNECT|CONN_SSL|CONN_SSL_WANT_WRITE|CONN_SSL_WANT_READ|CONN_SSL_PEERCERT_OK)
+ #endif
+ typedef int CONN_ID;
+ 

+ 28 - 0
debian/patches/0003-S2S-TLS-Add-missing-CAFile-and-CRLFile-options-to-co.patch

@@ -0,0 +1,28 @@
+From 6a5d4ddeb6aaa63b958aed6ad9a84b1400530ecc Mon Sep 17 00:00:00 2001
+From: Alexander Barton <alex@barton.de>
+Date: Sat, 16 Dec 2023 16:29:05 +0100
+Subject: [PATCH 03/20] S2S-TLS: Add missing CAFile and CRLFile options to
+ "configtest" output
+
+(cherry picked from commit 5ca567a18caf699f93495ba2bc3749fb5f65383b)
+---
+ src/ngircd/conf.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/src/ngircd/conf.c
++++ b/src/ngircd/conf.c
+@@ -441,10 +441,14 @@
+ 
+ #ifdef SSL_SUPPORT
+ 	puts("[SSL]");
++	printf("  CAFile = %s\n", Conf_SSLOptions.CAFile
++					? Conf_SSLOptions.CAFile : "");
+ 	printf("  CertFile = %s\n", Conf_SSLOptions.CertFile
+ 					? Conf_SSLOptions.CertFile : "");
+ 	printf("  CipherList = %s\n", Conf_SSLOptions.CipherList ?
+ 	       Conf_SSLOptions.CipherList : DEFAULT_CIPHERS);
++	printf("  CRLFile = %s\n", Conf_SSLOptions.CRLFile
++					? Conf_SSLOptions.CRLFile : "");
+ 	printf("  DHFile = %s\n", Conf_SSLOptions.DHFile
+ 					? Conf_SSLOptions.DHFile : "");
+ 	printf("  KeyFile = %s\n", Conf_SSLOptions.KeyFile

+ 47 - 0
debian/patches/0004-S2S-TLS-Remove-leftover-debug-messages.patch

@@ -0,0 +1,47 @@
+From c6c9edfe6ccc99d18cad039f7c10525a79566081 Mon Sep 17 00:00:00 2001
+From: Alexander Barton <alex@barton.de>
+Date: Sat, 16 Dec 2023 16:30:06 +0100
+Subject: [PATCH 04/20] S2S-TLS: Remove leftover debug messages
+
+(cherry picked from commit 8f8bef9faee96a6033e8719fd38167017299847a)
+---
+ src/ngircd/conn-ssl.c | 7 +------
+ 1 file changed, 1 insertion(+), 6 deletions(-)
+
+--- a/src/ngircd/conn-ssl.c
++++ b/src/ngircd/conn-ssl.c
+@@ -755,7 +755,6 @@
+ 		param = SSL_get0_param(c->ssl_state.ssl);
+ 		X509_VERIFY_PARAM_set_hostflags(param,
+ 						X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
+-Log(LOG_ERR, "DEBUG: Setting up hostname verification for '%s'", s->host);
+ 		int err = X509_VERIFY_PARAM_set1_host(param, s->host, 0);
+ 		if (err != 1) {
+ 			Log(LOG_ERR,
+@@ -944,9 +943,7 @@
+ 			const char *peername = SSL_get0_peername(ssl);
+ 			if (peername != NULL)
+ 				cert_ok = true;
+-
+-			Log(LOG_ERR, "X509_V_OK, peername = '%s'", peername);
+-
++			LogDebug("X509_V_OK, peername = '%s'", peername);
+ 		} else
+ 			Log(LOG_ERR, "Certificate validation failed: %s",
+ 			    X509_verify_cert_error_string(err));
+@@ -976,7 +973,6 @@
+ 		    gnutls_certificate_verify_peers2(c->
+ 						     ssl_state.gnutls_session,
+ 						     &status);
+-Log(LOG_ERR, "DEBUG: verify = %d", verify);
+ 		if (verify < 0) {
+ 			Log(LOG_ERR,
+ 			    "gnutls_certificate_verify_peers2 failed: %s",
+@@ -994,7 +990,6 @@
+ 				gnutls_free(out.data);
+ 			}
+ 		}
+-Log(LOG_ERR, "DEBUG: status = %d", status);
+ 
+ 		gnutls_x509_crt_t cert;
+ 		unsigned cert_list_size;

+ 58 - 0
debian/patches/0005-S2S-TLS-OpenSSL-Always-setup-host-name-verification.patch

@@ -0,0 +1,58 @@
+From d7c7b67c0921f2f78350cd2983f38f386d560d1d Mon Sep 17 00:00:00 2001
+From: Alexander Barton <alex@barton.de>
+Date: Mon, 1 Jan 2024 19:58:35 +0100
+Subject: [PATCH 05/20] S2S-TLS/OpenSSL: Always setup host name verification
+
+Setup host name verification even when the "SSLVerify" option is
+disabled, because even then the peer can present a valid certificate and
+validation would always(!) fail because of the missing host name
+verification setup.
+
+(cherry picked from commit 84b019b11f761b71c8239d60e7f8db0b82a55df3)
+---
+ src/ngircd/conn-ssl.c | 28 +++++++++++++++-------------
+ 1 file changed, 15 insertions(+), 13 deletions(-)
+
+--- a/src/ngircd/conn-ssl.c
++++ b/src/ngircd/conn-ssl.c
+@@ -748,25 +748,27 @@
+ 	if (!ret)
+ 		return false;
+ 	Conn_OPTION_ADD(c, CONN_SSL_CONNECT);
++
+ #ifdef HAVE_LIBSSL
+ 	assert(c->ssl_state.ssl);
+-	if (s->SSLVerify) {
+-		X509_VERIFY_PARAM *param = NULL;
+-		param = SSL_get0_param(c->ssl_state.ssl);
+-		X509_VERIFY_PARAM_set_hostflags(param,
+-						X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
+-		int err = X509_VERIFY_PARAM_set1_host(param, s->host, 0);
+-		if (err != 1) {
+-			Log(LOG_ERR,
+-			    "Cannot set up hostname verification for '%s': %u",
+-			    s->host, err);
+-			return false;
+-		}
++
++	X509_VERIFY_PARAM *param = SSL_get0_param(c->ssl_state.ssl);
++	X509_VERIFY_PARAM_set_hostflags(param, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
++	int err = X509_VERIFY_PARAM_set1_host(param, s->host, 0);
++	if (err != 1) {
++		Log(LOG_ERR,
++		    "Cannot set up hostname verification for '%s': %u",
++		    s->host, err);
++		return false;
++	}
++
++	if (s->SSLVerify)
+ 		SSL_set_verify(c->ssl_state.ssl, SSL_VERIFY_PEER,
+ 			       Verify_openssl);
+-	} else
++	else
+ 		SSL_set_verify(c->ssl_state.ssl, SSL_VERIFY_NONE, NULL);
+ #endif
++
+ 	return true;
+ }
+ 

+ 45 - 0
debian/patches/0006-S2S-TLS-OpenSSL-Set-the-verification-flags-only-once.patch

@@ -0,0 +1,45 @@
+From 9fa5c1908e866d9480bfaf0516111c4741adbd59 Mon Sep 17 00:00:00 2001
+From: Alexander Barton <alex@barton.de>
+Date: Tue, 2 Jan 2024 20:55:15 +0100
+Subject: [PATCH 06/20] S2S-TLS/OpenSSL: Set the verification flags only once
+
+Set the verification flags in the ConnSSL_SetVerifyProperties_openssl
+function only, don't override them in ConnSSL_InitLibrary() afterwards.
+
+No functional changes, now ConnSSL_SetVerifyProperties_openssl() sets
+exactly the parameters which ConnSSL_InitLibrary() always overwrote ...
+
+(cherry picked from commit 08647ab1e7cf0d034f2d8987a3cac3201af84e02)
+---
+ src/ngircd/conn-ssl.c | 6 ++----
+ 1 file changed, 2 insertions(+), 4 deletions(-)
+
+--- a/src/ngircd/conn-ssl.c
++++ b/src/ngircd/conn-ssl.c
+@@ -401,8 +401,6 @@
+ 			    SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 |
+ 			    SSL_OP_NO_COMPRESSION);
+ 	SSL_CTX_set_mode(newctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
+-	SSL_CTX_set_verify(newctx, SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE,
+-			   Verify_openssl);
+ 	SSL_CTX_free(ssl_ctx);
+ 	ssl_ctx = newctx;
+ 	Log(LOG_INFO, "%s initialized.", OpenSSL_version(OPENSSL_VERSION));
+@@ -615,7 +613,6 @@
+ {
+ 	X509_STORE *store = NULL;
+ 	X509_LOOKUP *lookup;
+-	int verify_flags = SSL_VERIFY_PEER;
+ 	bool ret = false;
+ 
+ 	if (!Conf_SSLOptions.CAFile)
+@@ -649,7 +646,8 @@
+ 		}
+ 	}
+ 
+-	SSL_CTX_set_verify(ctx, verify_flags, Verify_openssl);
++	SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE,
++			   Verify_openssl);
+ 	SSL_CTX_set_verify_depth(ctx, MAX_CERT_CHAIN_LENGTH);
+ 	ret = true;
+ out:

+ 81 - 0
debian/patches/0007-S2S-TLS-OpenSSL-Fix-handling-of-certificate-informat.patch

@@ -0,0 +1,81 @@
+From 0bdc552ffe5c47be83c646b61e9a7d211d4568a6 Mon Sep 17 00:00:00 2001
+From: Alexander Barton <alex@barton.de>
+Date: Tue, 2 Jan 2024 21:10:17 +0100
+Subject: [PATCH 07/20] S2S-TLS/OpenSSL: Fix handling of certificate
+ information for incoming connections
+
+Show proper certificate information for incoming connections, too, and
+not "peer did not present a certificate", regardless if the client sent
+a certificate or not.
+
+And free the client certificate structure "peer_cert" on incoming
+connections as well!
+
+(cherry picked from commit 679505aab9fea21b27a3d4bbf99cf2a16cf3d3d5)
+---
+ src/ngircd/conn-ssl.c | 46 ++++++++++++++++++++++++++++---------------
+ 1 file changed, 30 insertions(+), 16 deletions(-)
+
+--- a/src/ngircd/conn-ssl.c
++++ b/src/ngircd/conn-ssl.c
+@@ -935,22 +935,36 @@
+ 	Log(LOG_INFO, "Connection %d: initialized %s using cipher %s, %s.",
+ 	    c->sock, SSL_get_version(ssl), SSL_get_cipher(ssl), comp_alg);
+ 	peer_cert = SSL_get_peer_certificate(ssl);
+-	if (peer_cert && connect) {
++	if (peer_cert) {
+ 		cert_seen = true;
+-		/* Client: Check server certificate */
+-		int err = SSL_get_verify_result(ssl);
+-		if (err == X509_V_OK) {
+-			const char *peername = SSL_get0_peername(ssl);
+-			if (peername != NULL)
+-				cert_ok = true;
+-			LogDebug("X509_V_OK, peername = '%s'", peername);
+-		} else
+-			Log(LOG_ERR, "Certificate validation failed: %s",
+-			    X509_verify_cert_error_string(err));
+-		snprintf(msg, sizeof(msg), "%svalid peer certificate",
+-			 cert_ok ? "" : "in");
+-		LogOpenSSL_CertInfo(cert_ok ? LOG_DEBUG : LOG_ERR, peer_cert,
+-				    msg);
++
++		if (connect) {
++			/* Outgoing connection. Verify the remote server! */
++			int err = SSL_get_verify_result(ssl);
++			if (err == X509_V_OK) {
++				const char *peername = SSL_get0_peername(ssl);
++				if (peername != NULL)
++					cert_ok = true;
++				LogDebug("X509_V_OK, peername = '%s'", peername);
++			} else
++				Log(LOG_WARNING, "Certificate validation failed: %s!",
++				    X509_verify_cert_error_string(err));
++
++			snprintf(msg, sizeof(msg), "Got %svalid server certificate",
++				 cert_ok ? "" : "in");
++			LogOpenSSL_CertInfo(LOG_INFO, peer_cert, msg);
++		} else {
++			/* Incoming connection.
++			 * Accept all certificates, don't depend on their
++			 * validity: for example, we don't know the hostname
++			 * to check, because we not yet even know if this is a
++			 * server connection at all and if so, which one, so we
++			 * don't know a host name to look for. On the other
++			 * hand we want client certificates, for example for
++			 * "CertFP" authentication with services ... */
++			LogOpenSSL_CertInfo(LOG_INFO, peer_cert,
++					    "Got unchecked client certificate");
++		}
+ 
+ 		X509_free(peer_cert);
+ 	}
+@@ -1038,7 +1052,7 @@
+ 	if (cert_ok)
+ 		Conn_OPTION_ADD(c, CONN_SSL_PEERCERT_OK);
+ 	if (!cert_seen)
+-		Log(LOG_INFO, "Peer did not present a certificate");
++		Log(LOG_INFO, "Peer did not present a certificate.");
+ }
+ 
+ 

+ 72 - 0
debian/patches/0008-S2S-TLS-OpenSSL-Postpone-verification-of-TLS-session.patch

@@ -0,0 +1,72 @@
+From 1a63a5e98b1dfe3a6e31dd6206585a1476f7d242 Mon Sep 17 00:00:00 2001
+From: Alexander Barton <alex@barton.de>
+Date: Tue, 2 Jan 2024 22:02:46 +0100
+Subject: [PATCH 08/20] S2S-TLS/OpenSSL: Postpone verification of TLS session
+ right before server handshake
+
+The verify callback in OpenSSL is called pretty early, and at that time
+it is not possible yet to check which connection it belongs to, and some
+connections may have relaxed requirements.
+
+So always return success in the Verify_openssl() callback, and postpone
+validation of the TLS session until starting the server handshake in
+cb_connserver_login_ssl(), when we know which server this connection
+belongs to and which options (like "SSLVerify") are in effect.
+
+The code doing this was already present in cb_connserver_login_ssl(),
+but this patch adds a more prominent comment to the function.
+
+(cherry picked from commit 3db3b47fc7172a69b7d99d66eddb07a323dc6e74)
+---
+ src/ngircd/conn-ssl.c | 21 +++++++++++++++------
+ src/ngircd/conn.c     |  7 +++++++
+ 2 files changed, 22 insertions(+), 6 deletions(-)
+
+--- a/src/ngircd/conn-ssl.c
++++ b/src/ngircd/conn-ssl.c
+@@ -211,14 +211,23 @@
+ static int
+ Verify_openssl(int preverify_ok, X509_STORE_CTX * ctx)
+ {
+-	int err;
+-
++#ifdef DEBUG
+ 	if (!preverify_ok) {
+-		err = X509_STORE_CTX_get_error(ctx);
+-		Log(LOG_ERR, "Certificate validation failed: %s",
+-		    X509_verify_cert_error_string(err));
++		int err = X509_STORE_CTX_get_error(ctx);
++		LogDebug("Certificate validation failed: %s",
++			 X509_verify_cert_error_string(err));
+ 	}
+-	return preverify_ok;
++#else
++	(void)preverify_ok;
++	(void)ctx;
++#endif
++
++	/* Always(!) return success as we have to deal with invalid
++	 * (self-signed, expired, ...) client certificates and with invalid
++	 * server certificates when "SSLVerify" is disabled, which we don't
++	 * know at this stage. Therefore we postpone this check, it will be
++	 * (and has to be!) handled in cb_connserver_login_ssl(). */
++	return 1;
+ }
+ #endif
+ 
+--- a/src/ngircd/conn.c
++++ b/src/ngircd/conn.c
+@@ -2539,6 +2539,13 @@
+ /**
+  * IO callback for new outgoing SSL-enabled server connections.
+  *
++ * IMPORTANT: The SSL session has been validated before, but all errors have
++ * been ignored so far! The reason for this is that the generic SSL code has no
++ * idea if the new session actually belongs to a server, as this only becomes
++ * clear when the remote peer sends its PASS command (and we have to handle
++ * invalid client certificates!). Therefore, it is important to check the
++ * status of the SSL session first before continuing the server handshake here!
++ *
+  * @param sock		Socket descriptor.
+  * @param unused	(ignored IO specification)
+  */

+ 93 - 0
debian/patches/0009-S2S-TLS-OpenSSL-Streamline-logging.patch

@@ -0,0 +1,93 @@
+From 51a6339a8e766f1dee0915e6aea022986eab7306 Mon Sep 17 00:00:00 2001
+From: Alexander Barton <alex@barton.de>
+Date: Tue, 2 Jan 2024 22:13:42 +0100
+Subject: [PATCH 09/20] S2S-TLS/OpenSSL: Streamline logging
+
+This includes simplifying cb_connserver_login_ssl() a bit, we do not
+have to code for invalid state which was ruled out by an assert() and
+therefore can get rid of the goto altogether (and don't log the same
+error twice with different messages).
+
+(cherry picked from commit 02bb99b0242ade8af78f957aa1657561374ef1d6)
+---
+ src/ngircd/conn-ssl.c | 15 +++++++++------
+ src/ngircd/conn.c     | 25 +++++++++++--------------
+ 2 files changed, 20 insertions(+), 20 deletions(-)
+
+--- a/src/ngircd/conn-ssl.c
++++ b/src/ngircd/conn-ssl.c
+@@ -155,13 +155,13 @@
+ 	mem = BIO_new(BIO_s_mem());
+ 	if (!mem)
+ 		return;
+-	X509_NAME_print_ex(mem, X509_get_subject_name(cert), 4,
++	X509_NAME_print_ex(mem, X509_get_subject_name(cert), 0,
+ 			   XN_FLAG_ONELINE);
+-	X509_NAME_print_ex(mem, X509_get_issuer_name(cert), 4, XN_FLAG_ONELINE);
++	X509_NAME_print_ex(mem, X509_get_issuer_name(cert), 2, XN_FLAG_ONELINE);
+ 	if (BIO_write(mem, "", 1) == 1) {
+ 		len = BIO_get_mem_data(mem, &memptr);
+ 		if (memptr && len > 0)
+-			Log(level, "%s: \"%s\"", msg, memptr);
++			Log(level, "%s: \"%s\".", msg, memptr);
+ 	}
+ 	(void)BIO_set_close(mem, BIO_CLOSE);
+ 	BIO_free(mem);
+@@ -832,9 +832,12 @@
+ 				    "SSL error, client disconnected [in %s()]!",
+ 				    fname);
+ 				break;
+-			case -1:	/* low level socket I/O error, check errno */
+-				Log(LOG_ERR, "SSL error: %s [in %s()]!",
+-				    strerror(real_errno), fname);
++			case -1:
++				/* Low level socket I/O error, check errno. But
++				 * we don't need to log this here, the generic
++				 * connection layer will take care of it. */
++				LogDebug("SSL error: %s [in %s()]!",
++					 strerror(real_errno), fname);
+ 			}
+ 		}
+ 		break;
+--- a/src/ngircd/conn.c
++++ b/src/ngircd/conn.c
+@@ -2574,28 +2574,25 @@
+ 
+ 	serveridx = Conf_GetServer(idx);
+ 	assert(serveridx >= 0);
+-	if (serveridx < 0)
+-		goto err;
+-
+-	Log( LOG_INFO, "SSL connection %d with \"%s:%d\" established.", idx,
+-	    My_Connections[idx].host, Conf_Server[Conf_GetServer( idx )].port );
+ 
++	/* The SSL handshake is done, but validation results were ignored so
++	 * far, so let's see where we are: */
++	LogDebug("SSL handshake on socket %d done.", idx);
+ 	if (!Conn_OPTION_ISSET(&My_Connections[idx], CONN_SSL_PEERCERT_OK)) {
+ 		if (Conf_Server[serveridx].SSLVerify) {
+ 			Log(LOG_ERR,
+-				"SSLVerify enabled for %d, but peer certificate check failed",
+-				idx);
+-			goto err;
++				"Peer certificate check failed for \"%s\" on connection %d!",
++				My_Connections[idx].host, idx);
++			Conn_Close(idx, "Valid certificate required",
++				   NULL, false);
++			return;
+ 		}
+ 		Log(LOG_WARNING,
+-			"Peer certificate check failed for %d, but SSLVerify is disabled, continuing",
+-			idx);
++			"Peer certificate check failed for \"%s\" on connection %d, but \"SSLVerify\" is disabled. Continuing ...",
++			My_Connections[idx].host, idx);
+ 	}
++	LogDebug("Server certificate accepted, continuing server login ...");
+ 	server_login(idx);
+-	return;
+-      err:
+-	Log(LOG_ERR, "SSL connection on socket %d failed!", sock);
+-	Conn_Close(idx, "Can't connect!", NULL, false);
+ }
+ 
+ 

+ 38 - 0
debian/patches/0010-S2S-TLS-Fix-formatting-and-sort-new-SSL-options-in-n.patch

@@ -0,0 +1,38 @@
+From c9247d7031c4f92d583035ea29a182b3dd8b478d Mon Sep 17 00:00:00 2001
+From: Alexander Barton <alex@barton.de>
+Date: Wed, 3 Jan 2024 15:40:58 +0100
+Subject: [PATCH 10/20] S2S-TLS: Fix formatting and sort new SSL options in
+ ngircd.conf manual page
+
+(cherry picked from commit 58ee4df2ae2e4e59ae8909b69670825229158da8)
+---
+ man/ngircd.conf.5.tmpl | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+--- a/man/ngircd.conf.5.tmpl
++++ b/man/ngircd.conf.5.tmpl
+@@ -385,13 +385,10 @@
+ section. Please note that this whole section is only recognized by ngIRCd
+ when it is compiled with support for SSL using OpenSSL or GnuTLS!
+ .TP
+-\fBCAFile (string)\fR
++\fBCAFile\fR (string)
+ Filename pointing to the Trusted CA Certificates. This is required for
+ verifying peer certificates.
+ .TP
+-\fBCRLFile (string)\fR
+-Filename of Certificate Revocation List.
+-.TP
+ \fBCertFile\fR (string)
+ SSL Certificate file of the private server key.
+ .TP
+@@ -401,6 +398,9 @@
+ Please see 'man 1ssl ciphers' (OpenSSL) and 'man 3 gnutls_priority_init'
+ (GnuTLS) for details.
+ .TP
++\fBCRLFile\fR (string)
++Filename of Certificate Revocation List.
++.TP
+ \fBDHFile\fR (string)
+ Name of the Diffie-Hellman Parameter file. Can be created with GnuTLS
+ "certtool \-\-generate-dh-params" or "openssl dhparam". If this file is not

+ 30 - 0
debian/patches/0011-S2S-TLS-MAX_CERT_CHAIN_LENGTH-is-only-used-by-OpenSS.patch

@@ -0,0 +1,30 @@
+From 9653e891d5aa9b9182f10eb5b737242de0ccada9 Mon Sep 17 00:00:00 2001
+From: Alexander Barton <alex@barton.de>
+Date: Fri, 5 Jan 2024 22:17:12 +0100
+Subject: [PATCH 11/20] S2S-TLS: MAX_CERT_CHAIN_LENGTH is only used by OpenSSL
+
+(cherry picked from commit c8589e9890742c377c78595131ef1cdc8d784c66)
+---
+ src/ngircd/conn-ssl.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/src/ngircd/conn-ssl.c
++++ b/src/ngircd/conn-ssl.c
+@@ -45,6 +45,8 @@
+ #include <openssl/dh.h>
+ #include <openssl/x509v3.h>
+ 
++#define MAX_CERT_CHAIN_LENGTH	10	/* XXX: do not hardcode */
++
+ static SSL_CTX * ssl_ctx;
+ static DH *dh_params;
+ 
+@@ -52,8 +54,6 @@
+ static bool ConnSSL_SetVerifyProperties_openssl PARAMS((SSL_CTX * c));
+ #endif
+ 
+-#define MAX_CERT_CHAIN_LENGTH	10	/* XXX: do not hardcode */
+-
+ #ifdef HAVE_LIBGNUTLS
+ #include <sys/types.h>
+ #include <sys/stat.h>

+ 27 - 0
debian/patches/0012-S2S-TLS-GnuTLS-Update-SSL-code-for-GnuTLS-certificat.patch

@@ -0,0 +1,27 @@
+From 8676c743246aaebe6d45f0978812c154ecfa25dd Mon Sep 17 00:00:00 2001
+From: Alexander Barton <alex@barton.de>
+Date: Fri, 5 Jan 2024 22:23:53 +0100
+Subject: [PATCH 12/20] S2S-TLS/GnuTLS: Update SSL code for GnuTLS certificate
+ reloading
+
+Without this, the S2S-TLS-Patch not even compiles with GnuTLS because
+of the "new" GnuTLS certificate reload support implemented in commit
+eead4a63 ("x509_cred_slot").
+
+(cherry picked from commit 0e176b557037b583f408229b518646804b33d745)
+---
+ src/ngircd/conn-ssl.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/src/ngircd/conn-ssl.c
++++ b/src/ngircd/conn-ssl.c
+@@ -468,6 +468,9 @@
+ 	if (!Conf_SSLOptions.CAFile)
+ 		return true;
+ 
++	x509_cred_slot *slot = array_get(&x509_creds, sizeof(x509_cred_slot), x509_cred_idx);
++	gnutls_certificate_credentials_t x509_cred = slot->x509_cred;
++
+ 	err = gnutls_certificate_set_x509_trust_file(x509_cred,
+ 						     Conf_SSLOptions.CAFile,
+ 						     GNUTLS_X509_FMT_PEM);

+ 118 - 0
debian/patches/0013-S2S-TLS-GnuTLS-Fix-handling-of-certificate-informati.patch

@@ -0,0 +1,118 @@
+From a1e391b1148b2e2cb406423473dc89ff28b7cdba Mon Sep 17 00:00:00 2001
+From: Alexander Barton <alex@barton.de>
+Date: Fri, 5 Jan 2024 22:29:40 +0100
+Subject: [PATCH 13/20] S2S-TLS/GnuTLS: Fix handling of certificate information
+ for incoming connections
+
+Show proper certificate information for incoming connections, too, and
+not "peer did not present a certificate", regardless if the client sent
+a certificate or not.
+
+This change is for GnuTLS and similar to what was implemented in commit
+for OpenSSL in "S2S-TLS/OpenSSL: Fix handling of certificate information
+for incoming connections".
+
+(cherry picked from commit 509ff6032686662328f4ecb0c5c287a34e929c53)
+---
+ src/ngircd/conn-ssl.c | 75 ++++++++++++++++++++++++-------------------
+ 1 file changed, 42 insertions(+), 33 deletions(-)
+
+--- a/src/ngircd/conn-ssl.c
++++ b/src/ngircd/conn-ssl.c
+@@ -747,6 +747,7 @@
+ #ifdef HAVE_LIBGNUTLS
+ 	int err;
+ 
++	(void)s;
+ 	err = gnutls_init(&c->ssl_state.gnutls_session, GNUTLS_CLIENT);
+ 	if (err) {
+ 		Log(LOG_ERR, "Failed to initialize new SSL session: %s",
+@@ -996,29 +997,8 @@
+ 	    gnutls_cipher_get_name(cipher),
+ 	    gnutls_mac_get_name(gnutls_mac_get(sess)));
+ 	cred = gnutls_auth_get_type(c->ssl_state.gnutls_session);
+-	if (cred == GNUTLS_CRD_CERTIFICATE && connect) {
++	if (cred == GNUTLS_CRD_CERTIFICATE) {
+ 		cert_seen = true;
+-		int verify =
+-		    gnutls_certificate_verify_peers2(c->
+-						     ssl_state.gnutls_session,
+-						     &status);
+-		if (verify < 0) {
+-			Log(LOG_ERR,
+-			    "gnutls_certificate_verify_peers2 failed: %s",
+-			    gnutls_strerror(verify));
+-			goto done_cn_validation;
+-		} else if (status) {
+-			gnutls_datum_t out;
+-
+-			if (gnutls_certificate_verification_status_print
+-			    (status, gnutls_certificate_type_get(sess), &out,
+-			     0) == GNUTLS_E_SUCCESS) {
+-				Log(LOG_ERR,
+-				    "Certificate validation failed: %s",
+-				    out.data);
+-				gnutls_free(out.data);
+-			}
+-		}
+ 
+ 		gnutls_x509_crt_t cert;
+ 		unsigned cert_list_size;
+@@ -1042,17 +1022,46 @@
+ 			    gnutls_strerror(err));
+ 			goto done_cn_validation;
+ 		}
+-		err = gnutls_x509_crt_check_hostname(cert, c->host);
+-		if (err == 0)
+-			Log(LOG_ERR,
+-			    "Failed to verify the hostname, expected \"%s\"",
+-			    c->host);
+-		else
+-			cert_ok = verify == 0 && status == 0;
+-
+-		snprintf(msg, sizeof(msg), "%svalid peer certificate",
+-			cert_ok ? "" : "in");
+-		LogGnuTLS_CertInfo(cert_ok ? LOG_DEBUG : LOG_ERR, cert, msg);
++
++		if (connect) {
++			int verify =
++			    gnutls_certificate_verify_peers2(c->
++							     ssl_state.gnutls_session,
++							     &status);
++			if (verify < 0) {
++				Log(LOG_ERR,
++				    "gnutls_certificate_verify_peers2 failed: %s",
++				    gnutls_strerror(verify));
++				goto done_cn_validation;
++			} else if (status) {
++				gnutls_datum_t out;
++
++				if (gnutls_certificate_verification_status_print
++				    (status, gnutls_certificate_type_get(sess), &out,
++				     0) == GNUTLS_E_SUCCESS) {
++					Log(LOG_ERR,
++					    "Certificate validation failed: %s",
++					    out.data);
++					gnutls_free(out.data);
++				}
++			}
++
++			err = gnutls_x509_crt_check_hostname(cert, c->host);
++			if (err == 0)
++				Log(LOG_ERR,
++				    "Failed to verify the hostname, expected \"%s\"",
++				    c->host);
++			else
++				cert_ok = verify == 0 && status == 0;
++
++			snprintf(msg, sizeof(msg), "Got %svalid server certificate",
++				cert_ok ? "" : "in");
++			LogGnuTLS_CertInfo(LOG_INFO, cert, msg);
++		} else {
++			/* Incoming connection. Please see comments for OpenSSL! */
++			LogGnuTLS_CertInfo(LOG_INFO, cert,
++					    "Got unchecked peer certificate");
++		}
+ 
+ 		gnutls_x509_crt_deinit(cert);
+ done_cn_validation:

+ 49 - 0
debian/patches/0014-S2S-TLS-GnuTLS-Streamline-logging.patch

@@ -0,0 +1,49 @@
+From e092877d9ad69f0cb4dea10bf546e561dd6a1abd Mon Sep 17 00:00:00 2001
+From: Alexander Barton <alex@barton.de>
+Date: Fri, 5 Jan 2024 22:31:32 +0100
+Subject: [PATCH 14/20] S2S-TLS/GnuTLS: Streamline logging
+
+(cherry picked from commit 663972c88d3ae3e3226fe6f95ca1113694ce0618)
+---
+ src/ngircd/conn-ssl.c | 12 +++++++-----
+ 1 file changed, 7 insertions(+), 5 deletions(-)
+
+--- a/src/ngircd/conn-ssl.c
++++ b/src/ngircd/conn-ssl.c
+@@ -869,8 +869,10 @@
+ 	default:
+ 		assert(code < 0);
+ 		if (gnutls_error_is_fatal(code)) {
+-			Log(LOG_ERR, "SSL error: %s [%s].",
+-			    gnutls_strerror(code), fname);
++			/* We don't need to log this here, the generic
++			 * connection layer will take care of it. */
++			LogDebug("SSL error: %s [%s].",
++				 gnutls_strerror(code), fname);
+ 			ConnSSL_Free(c);
+ 			return -1;
+ 		}
+@@ -914,12 +916,12 @@
+ 	assert(size);
+ 	issuer_dn = LogMalloc(size);
+ 	if (!issuer_dn) {
+-		Log(level, "%s: Distinguished Name: %s", msg, dn);
++		Log(level, "%s: Distinguished Name \"%s\".", msg, dn);
+ 		free(dn);
+ 		return;
+ 	}
+ 	gnutls_x509_crt_get_issuer_dn(cert, issuer_dn, &size);
+-	Log(level, "%s: Distinguished Name: \"%s\", Issuer \"%s\"", msg, dn,
++	Log(level, "%s: Distinguished Name \"%s\", Issuer \"%s\".", msg, dn,
+ 	    issuer_dn);
+ 	free(dn);
+ 	free(issuer_dn);
+@@ -979,7 +981,7 @@
+ 			 * hand we want client certificates, for example for
+ 			 * "CertFP" authentication with services ... */
+ 			LogOpenSSL_CertInfo(LOG_INFO, peer_cert,
+-					    "Got unchecked client certificate");
++					    "Got unchecked peer certificate");
+ 		}
+ 
+ 		X509_free(peer_cert);

+ 27 - 0
debian/patches/0015-S2S-TLS-Verify-the-TLS-certificates-by-default.patch

@@ -0,0 +1,27 @@
+From 7086f4d15e240f5394b0b3e24d100f9c4fe4c792 Mon Sep 17 00:00:00 2001
+From: Alexander Barton <alex@barton.de>
+Date: Sat, 6 Jan 2024 15:55:54 +0100
+Subject: [PATCH 15/20] S2S-TLS: Verify the TLS certificates by default
+
+This is already mentioned as the default in the manual page and the
+sample configuration file, but was actually not enabled in the code!
+
+(cherry picked from commit 180e2ec1359378172135472148c99a2d14e873cc)
+---
+ src/ngircd/conf.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/src/ngircd/conf.c
++++ b/src/ngircd/conf.c
+@@ -2298,6 +2298,11 @@
+ 	Proc_InitStruct(&Server->res_stat);
+ 	Server->conn_id = NONE;
+ 	memset(&Server->bind_addr, 0, sizeof(Server->bind_addr));
++
++#ifdef SSL_SUPPORT
++	/* Verify SSL connections by default! */
++	Server->SSLVerify = true;
++#endif
+ }
+ 
+ /* -eof- */

+ 34 - 0
debian/patches/0016-S2S-TLS-GnuTLS-Fix-handling-of-connections-without-p.patch

@@ -0,0 +1,34 @@
+From 0f866fd27333c6d92fa514ba05b2a47d129357e2 Mon Sep 17 00:00:00 2001
+From: Alexander Barton <alex@barton.de>
+Date: Sat, 6 Jan 2024 19:57:50 +0100
+Subject: [PATCH 16/20] S2S-TLS/GnuTLS: Fix handling of connections without
+ peer certificates
+
+(cherry picked from commit 8cef3ce42cd645a3ffb0e1eded52b8b77bb8caff)
+---
+ src/ngircd/conn-ssl.c | 9 ++++-----
+ 1 file changed, 4 insertions(+), 5 deletions(-)
+
+--- a/src/ngircd/conn-ssl.c
++++ b/src/ngircd/conn-ssl.c
+@@ -1000,16 +1000,15 @@
+ 	    gnutls_mac_get_name(gnutls_mac_get(sess)));
+ 	cred = gnutls_auth_get_type(c->ssl_state.gnutls_session);
+ 	if (cred == GNUTLS_CRD_CERTIFICATE) {
+-		cert_seen = true;
+-
+ 		gnutls_x509_crt_t cert;
+ 		unsigned cert_list_size;
+ 		const gnutls_datum_t *cert_list =
+ 		    gnutls_certificate_get_peers(sess, &cert_list_size);
+-		if (!cert_list || cert_list_size == 0) {
+-			Log(LOG_ERR, "No certificates found");
++
++		if (!cert_list || cert_list_size == 0)
+ 			goto done_cn_validation;
+-		}
++
++		cert_seen = true;
+ 		int err = gnutls_x509_crt_init(&cert);
+ 		if (err < 0) {
+ 			Log(LOG_ERR,

+ 246 - 0
debian/patches/0017-S2S-TLS-Convert-SSL.txt-to-Markdown-and-update-infor.patch

@@ -0,0 +1,246 @@
+From b04ed224cf95c0f31849d3683412cb937cc00efc Mon Sep 17 00:00:00 2001
+From: Alexander Barton <alex@barton.de>
+Date: Mon, 8 Jan 2024 18:31:30 +0100
+Subject: [PATCH 17/20] S2S-TLS: Convert SSL.txt to Markdown and update
+ information given
+
+No longer describe creating self-signed certificates or using "stunnel",
+as both is not recommended.
+
+(cherry picked from commit b826fad15871f73435328b1d77fd364838389adb)
+---
+ INSTALL.md      |   2 +-
+ doc/Makefile.am |   2 +-
+ doc/SSL.md      |  80 +++++++++++++++++++++++++++++++++++
+ doc/SSL.txt     | 108 ------------------------------------------------
+ 4 files changed, 82 insertions(+), 110 deletions(-)
+ create mode 100644 doc/SSL.md
+ delete mode 100644 doc/SSL.txt
+
+--- a/INSTALL.md
++++ b/INSTALL.md
+@@ -347,7 +347,7 @@
+   - `--with-gnutls[=<path>]`
+ 
+   Enable support for SSL/TLS using OpenSSL or GnuTLS libraries.
+-  See `doc/SSL.txt` for details.
++  See `doc/SSL.md` for details.
+ 
+ - IPv6:
+ 
+--- a/doc/Makefile.am
++++ b/doc/Makefile.am
+@@ -33,7 +33,7 @@
+ 	README-Interix.txt \
+ 	RFC.txt \
+ 	Services.txt \
+-	SSL.txt
++	SSL.md
+ 
+ doc_templates = sample-ngircd.conf.tmpl
+ 
+--- /dev/null
++++ b/doc/SSL.md
+@@ -0,0 +1,80 @@
++# [ngIRCd](https://ngircd.barton.de) - SSL/TLS Encrypted Connections
++
++ngIRCd supports SSL/TLS encrypted connections using the *OpenSSL* or *GnuTLS*
++libraries. Both encrypted server-server links as well as client-server links
++are supported.
++
++SSL is a compile-time option which is disabled by default. Use one of these
++options of the ./configure script to enable it:
++
++- `--with-openssl`: enable SSL support using OpenSSL.
++- `--with-gnutls`: enable SSL support using GnuTLS.
++
++You can check the output of `ngircd --version` to validate if your executable
++includes support for SSL or not: "+SSL" must be listed in the feature flags.
++
++You also need a SSL key and certificate, for example using Let's Encrypt, which
++is out of the scope of this document.
++
++From a feature point of view, ngIRCds support for both libraries is
++comparable. The only major difference (at this time) is that ngIRCd with GnuTLS
++does not support password protected private keys.
++
++## Configuration
++
++SSL-encrypted connections and plain-text connects can't run on the same network
++port (which is a limitation of the IRC protocol); therefore you have to define
++separate port(s) in your `[SSL]` block in the configuration file.
++
++A minimal configuration for *accepting* SSL-encrypted client & server
++connections looks like this:
++
++``` ini
++[SSL]
++CertFile = /etc/ssl/certs/my-fullchain.pem
++KeyFile = /etc/ssl/certs/my-privkey.pem
++Ports = 6697, 6698
++```
++
++In this case, the server only deals with *incoming* connections and never has to
++validate SSL certificates itself, and therefore no "Certificate Authorities" are
++needed.
++
++If you want to use *outgoing* SSL-connections to other servers, you need to add:
++
++``` ini
++[SSL]
++...
++CAFile = /etc/ssl/certs/ca-certificates.crt
++DHFile = /etc/ngircd/dhparams.pem
++
++[SERVER]
++...
++SSLConnect = yes
++```
++
++The `CAFile` option configures a file listing all the certificates of the
++trusted Certificate Authorities.
++
++The Diffie-Hellman parameters file `dhparams.pem` can be created like this:
++
++- OpenSSL: `openssl dhparam -2 -out /etc/ngircd/dhparams.pem 4096`
++- GnuTLS: `certtool --generate-dh-params --bits 4096 --outfile /etc/ngircd/dhparams.pem`
++
++Note that enabling `SSLConnect` not only enforces SSL-encrypted links for
++*outgoing* connections to other servers, but for *incoming* connections as well:
++If a server configured with `SSLConnect = yes` tries to connect on a plain-text
++connection, it won't be accepted to prevent data leakage! Therefore you should
++set this for *all* servers you expect to use SSL-encrypted connections!
++
++## Accepting untrusted Remote Certificates
++
++If you are using self-signed certificates or otherwise invalid certificates,
++which ngIRCd would reject by default, you can force ngIRCd to skip certificate
++validation on a per-server basis and continue establishing outgoing connections
++to the respective peer by setting `SSLVerify = no` in the `[SERVER]` block of
++this remote server in your configuration.
++
++But please think twice before doing so: the established connection is still
++encrypted but the remote site is *not verified at all* and man-in-the-middle
++attacks are possible!
+--- a/doc/SSL.txt
++++ /dev/null
+@@ -1,108 +0,0 @@
+-
+-                     ngIRCd - Next Generation IRC Server
+-
+-                        (c)2001-2008 Alexander Barton,
+-                    alex@barton.de, http://www.barton.de/
+-
+-               ngIRCd is free software and published under the
+-                   terms of the GNU General Public License.
+-
+-                                 -- SSL.txt --
+-
+-
+-ngIRCd supports SSL/TLSv1 encrypted connections using the OpenSSL or GnuTLS
+-libraries. Both encrypted server-server links as well as client-server links
+-are supported.
+-
+-SSL is a compile-time option which is disabled by default. Use one of these
+-options of the ./configure script to enable it:
+-
+-  --with-openssl     enable SSL support using OpenSSL
+-  --with-gnutls      enable SSL support using GnuTLS
+-
+-You also need a key/certificate, see below for how to create a self-signed one.
+-
+-From a feature point of view, ngIRCds support for both libraries is
+-comparable. The only major difference (at this time) is that ngircd with gnutls
+-does not support password protected private keys.
+-
+-Configuration
+-~~~~~~~~~~~~~
+-
+-To enable SSL connections a separate port must be configured: it is NOT
+-possible to handle unencrypted and encrypted connections on the same port!
+-This is a limitation of the IRC protocol ...
+-
+-You have to set (at least) the following configuration variables in the
+-[SSL] section of ngircd.conf(5): Ports, KeyFile, and CertFile.
+-
+-Now IRC clients are able to connect using SSL on the configured port(s).
+-(Using port 6697 for encrypted connections is common.)
+-
+-To enable encrypted server-server links, you have to additionally set
+-SSLConnect to "yes" in the corresponding [SERVER] section.
+-
+-
+-Creating a self-signed certificate
+-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+-
+-OpenSSL:
+-
+-Creating a self-signed certificate and key:
+- $ openssl req -newkey rsa:2048 -x509 -keyout server-key.pem -out server-cert.pem -days 1461
+-Create DH parameters (optional):
+- $ openssl dhparam -2 -out dhparams.pem 4096
+-
+-GnuTLS:
+-
+-Creating a self-signed certificate and key:
+- $ certtool --generate-privkey --bits 2048 --outfile server-key.pem
+- $ certtool --generate-self-signed --load-privkey server-key.pem --outfile server-cert.pem
+-Create DH parameters (optional):
+- $ certtool  --generate-dh-params --bits 4096 --outfile dhparams.pem
+-
+-
+-Alternate approach using stunnel(1)
+-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+-
+-Alternatively (or if you are using ngIRCd compiled without support
+-for GnuTLS/OpenSSL), you can use external programs/tools like stunnel(1) to
+-get SSL encrypted connections:
+-
+-  <http://stunnel.mirt.net/>
+-  <http://www.stunnel.org/>
+-
+-Stefan Sperling (stefan at binarchy dot net) mailed the following text as a
+-short "how-to", thanks Stefan!
+-
+-=== snip ===
+-    ! This guide applies to stunnel 4.x !
+-
+-    Put this in your stunnel.conf:
+-
+-        [ircs]
+-        accept = 6667
+-        connect = 6668
+-
+-    This makes stunnel listen for incoming connections
+-    on port 6667 and forward decrypted data to port 6668.
+-    We call the connection 'ircs'. Stunnel will use this
+-    name when logging connection attempts via syslog.
+-    You can also use the name in /etc/hosts.{allow,deny}
+-    if you run tcp-wrappers.
+-
+-    To make sure ngircd is listening on the port where
+-    the decrypted data arrives, set
+-
+-        Ports = 6668
+-
+-    in your ngircd.conf.
+-
+-    Start stunnel and restart ngircd.
+-
+-    That's it.
+-    Don't forget to activate ssl support in your irc client ;)
+-    The main drawback of this approach compared to using builtin ssl
+-    is that from ngIRCds point of view, all ssl-enabled client connections will
+-    originate from the host running stunnel.
+-=== snip ===
+--- a/doc/Makefile.in
++++ b/doc/Makefile.in
+@@ -248,7 +248,7 @@
+ 	README-Interix.txt \
+ 	RFC.txt \
+ 	Services.txt \
+-	SSL.txt
++	SSL.md
+ 
+ doc_templates = sample-ngircd.conf.tmpl
+ generated_docs = sample-ngircd.conf

+ 33 - 0
debian/patches/0018-S2S-TLS-Add-notice-to-INSTALL.md.patch

@@ -0,0 +1,33 @@
+From e8ffea49c26f298b3317fb765f7e6fb378db3923 Mon Sep 17 00:00:00 2001
+From: Alexander Barton <alex@barton.de>
+Date: Thu, 11 Jan 2024 14:24:22 +0100
+Subject: [PATCH 18/20] S2S-TLS: Add notice to INSTALL.md
+
+(cherry picked from commit 6b27eabf5bdbc6bf6f71d7b1e7d059dfeab6849b)
+---
+ INSTALL.md | 14 ++++++++++++++
+ 1 file changed, 14 insertions(+)
+
+--- a/INSTALL.md
++++ b/INSTALL.md
+@@ -12,6 +12,20 @@
+ 
+ ## Upgrade Information
+ 
++This section lists important updates and breaking changes that you should be
++aware of *before* starting the upgrade:
++
++- **Attention**:
++  Starting with release 27, ngIRCd validates SSL/TLS certificates on outgoing
++  server-server links by default and drops(!) connections when the remote
++  certificate is invalid (for example self-signed, expired, not matching the
++  host name, ...). Therefore you have to make sure that all relevant
++  *certificates are valid* (or to disable certificate validation on this
++  connection using the new `SSLVerify = false` setting in the affected
++  `[Server]` block, where the remote certificate is not valid and you can not
++  fix this issue).
++  And this change was backported to this ngIRCd release!
++
+ Differences to version 25
+ 
+ - **Attention**:

+ 35 - 0
debian/patches/0019-S2S-TLS-Fix-make-check-in-separate-build-directory.patch

@@ -0,0 +1,35 @@
+From e4937e6ed3a13408f8e0f57124aa784de4299147 Mon Sep 17 00:00:00 2001
+From: Alexander Barton <alex@barton.de>
+Date: Sat, 10 Feb 2024 00:14:33 +0100
+Subject: [PATCH 19/20] S2S-TLS: Fix "make check" in separate build directory
+
+(cherry picked from commit b9d6a2f49c4b3607c69b298cc770c0c945f627f6)
+---
+ src/testsuite/prep-server3   | 9 +++++----
+ src/testsuite/switch-server3 | 9 ++++-----
+ 2 files changed, 9 insertions(+), 9 deletions(-)
+
+--- a/src/testsuite/prep-server3
++++ b/src/testsuite/prep-server3
+@@ -1,4 +1,5 @@
+-#!/bin/sh
+-cp ssl/cert-my-first-domain-tld.pem ssl/cert.pem
+-cp ssl/key-my-first-domain-tld.pem ssl/key.pem
+-cp ssl/dhparams-my-first-domain-tld.pem ssl/dhparams.pem
++#!/bin/sh -e
++mkdir -p ssl
++cp "${srcdir}"/ssl/cert-my-first-domain-tld.pem ssl/cert.pem
++cp "${srcdir}"/ssl/key-my-first-domain-tld.pem ssl/key.pem
++cp "${srcdir}"/ssl/dhparams-my-first-domain-tld.pem ssl/dhparams.pem
+--- a/src/testsuite/switch-server3
++++ b/src/testsuite/switch-server3
+@@ -1,5 +1,4 @@
+-#!/bin/sh
+-cp ssl/cert-my-second-domain-tld.pem ssl/cert.pem
+-cp ssl/key-my-second-domain-tld.pem ssl/key.pem
+-cp ssl/dhparams-my-second-domain-tld.pem ssl/dhparams.pem
+-# -eof-
++#!/bin/sh -e
++cp "${srcdir}"/ssl/cert-my-second-domain-tld.pem ssl/cert.pem
++cp "${srcdir}"/ssl/key-my-second-domain-tld.pem ssl/key.pem
++cp "${srcdir}"/ssl/dhparams-my-second-domain-tld.pem ssl/dhparams.pem

+ 18 - 0
debian/patches/series

@@ -1,5 +1,23 @@
 # cherry-picked from upstream
 0001-Respect-SSLConnect-option-for-incoming-connections.patch
+0002-Support-for-server-certificate-validation-on-server-.patch
+0003-S2S-TLS-Add-missing-CAFile-and-CRLFile-options-to-co.patch
+0004-S2S-TLS-Remove-leftover-debug-messages.patch
+0005-S2S-TLS-OpenSSL-Always-setup-host-name-verification.patch
+0006-S2S-TLS-OpenSSL-Set-the-verification-flags-only-once.patch
+0007-S2S-TLS-OpenSSL-Fix-handling-of-certificate-informat.patch
+0008-S2S-TLS-OpenSSL-Postpone-verification-of-TLS-session.patch
+0009-S2S-TLS-OpenSSL-Streamline-logging.patch
+0010-S2S-TLS-Fix-formatting-and-sort-new-SSL-options-in-n.patch
+0011-S2S-TLS-MAX_CERT_CHAIN_LENGTH-is-only-used-by-OpenSS.patch
+0012-S2S-TLS-GnuTLS-Update-SSL-code-for-GnuTLS-certificat.patch
+0013-S2S-TLS-GnuTLS-Fix-handling-of-certificate-informati.patch
+0014-S2S-TLS-GnuTLS-Streamline-logging.patch
+0015-S2S-TLS-Verify-the-TLS-certificates-by-default.patch
+0016-S2S-TLS-GnuTLS-Fix-handling-of-connections-without-p.patch
+0017-S2S-TLS-Convert-SSL.txt-to-Markdown-and-update-infor.patch
+0018-S2S-TLS-Add-notice-to-INSTALL.md.patch
+0019-S2S-TLS-Fix-make-check-in-separate-build-directory.patch
 
 # patches that should go upstream
 fix-race-in-testsuite.patch