Hi Willy > Le 5 juil. 2017 à 18:38, Willy Tarreau <w...@1wt.eu> a écrit : > > Hi guys, > > back to this old discussion. > > On Fri, May 12, 2017 at 04:10:20PM +0200, Willy Tarreau wrote: >> On Tue, May 09, 2017 at 12:12:42AM +0200, Lukas Tribus wrote: >>> Haproxy can verify the certificate of backend TLS servers since day 1. >>> >>> The only thing missing is client SNI based backend certificate >>> verification, which yes - since we can pass client SNI to the TLS server >>> - we need to consider for the certificate verification process as well. >> >> In fact the cert name is checked, it's just that it can only check against >> a constant in the configuration. I agree that it's a problem when using SNI. >> Furthermore it forces one to completely disable verifyhost in case SNI is >> used. >> >> I tend to think that the best approach would be to always enable it when >> SNI is involved in fact, because if SNI is used to the server, it really >> means we want to check what cert is provided. This could then possibly be >> explicitly turned off by the "verify none" directive. >> >> I have absolutely no idea how to do that however, I don't know if we can >> retrieve the previously configured SNI using openssl's API after the >> connection is established. > > So after digging I found a way to implement this. The configured SNI value > is indeed stored in the SSL session and can be extracted, more or less > easily depending on the version. I came up with the attached patch that > works for me on all cases below. I'm willing to merge it into 1.8-dev and > later backport to older versions once we're sure it does the right thing > and doesn't break compatibility with openssl forks. > > Please give it a try, and if some can test it on libressl/boringssl, it > would be nice. > > --------------- > tested with the following server configurations (cert on port 8443 is valid > for "localhost" only) : > > # passes checks and traffic (no hostname check) > server ssl 127.0.0.1:8443 ssl verify required check inter 100 ca-file > rsa2048.pem > > # passes checks and traffic (localhost is what the server presents) > server ssl 127.0.0.1:8443 ssl verify required check inter 100 ca-file > rsa2048.pem verifyhost localhost > > # fails checks and traffic (foo not matched on the server) > server ssl 127.0.0.1:8443 ssl verify required check inter 100 ca-file > rsa2048.pem verifyhost foo > > # passes checks and traffic (verify none ignores the host) > server ssl 127.0.0.1:8443 ssl verify none check inter 100 ca-file > rsa2048.pem verifyhost foo > > # passes checks and traffic (localhost is fine) > server ssl 127.0.0.1:8443 ssl verify required check inter 100 ca-file > rsa2048.pem sni str(localhost) verifyhost localhost > > # passes checks and traffic (verifyhost overrides sni) > server ssl 127.0.0.1:8443 ssl verify required check inter 100 ca-file > rsa2048.pem sni str(foo) verifyhost localhost > > # passes checks and traffic (localhost always valid) > server ssl 127.0.0.1:8443 ssl verify required check inter 100 ca-file > rsa2048.pem sni str(localhost) > > # passes checks, and traffic without host or with "host: localhost" > and fails other hosts. > server ssl 127.0.0.1:8443 ssl verify required check inter 100 ca-file > rsa2048.pem sni req.hdr(host) > --------------- > > Cheers, > Willy > <0001-MINOR-ssl-compare-server-certificate-names-to-the-SN.patch>
SSL_SESSION_get0_hostname take a ssl_session not a ssl structure. in openssl-compat.h a section is used for others functions like SSL_SESSION_get0_X introduce in openssl 1.1.0 With this diff, it build with boringssl and openssl 1.0.2k. It should also be ok with libressl diff --git a/include/proto/openssl-compat.h b/include/proto/openssl-compat.h index 7261a7b..88ea01e 100644 --- a/include/proto/openssl-compat.h +++ b/include/proto/openssl-compat.h @@ -94,6 +94,11 @@ static inline int SSL_SESSION_set1_id_context(SSL_SESSION *s, const unsigned cha * Functions introduced in OpenSSL 1.1.0 and not yet present in LibreSSL */ +static inline const char *SSL_SESSION_get0_hostname(const SSL_SESSION *sess) +{ + return sess->tlsext_hostname; +} + static inline const unsigned char *SSL_SESSION_get0_id_context(const SSL_SESSION *sess, unsigned int *sid_ctx_length) { *sid_ctx_length = sess->sid_ctx_length; diff --git a/src/ssl_sock.c b/src/ssl_sock.c index 6448a89..d90d64b 100644 --- a/src/ssl_sock.c +++ b/src/ssl_sock.c @@ -127,13 +127,6 @@ #define HASH_FUNCT EVP_sha256 #endif /* OPENSSL_NO_SHA256 */ -/* Needed to retrieve the configured SNI for an outgoing connection, but only - * appeared in openssl 1.1.0. I don't know how it's done with forked versions. - */ -#if OPENSSL_VERSION_NUMBER < 0x1010000fL -#define SSL_SESSION_get0_hostname(s) s->tlsext_hostname -#endif - /* ssl_methods flags for ssl options */ #define MC_SSL_O_ALL 0x0000 #define MC_SSL_O_NO_SSLV3 0x0001 /* disable SSLv3 */ @@ -3938,7 +3931,7 @@ static int ssl_sock_srv_verifycbk(int ok, X509_STORE_CTX *ctx) { SSL *ssl; struct connection *conn; - char *servername; + const char *servername; int depth; X509 *cert; @@ -3963,7 +3956,7 @@ static int ssl_sock_srv_verifycbk(int ok, X509_STORE_CTX *ctx) if (!ssl_sess) return ok; - servername = SSL_SESSION_get0_hostname(ssl); + servername = SSL_SESSION_get0_hostname(ssl_sess); if (!servername) return ok; } ++ Manu