diff options
author | Heikki Linnakangas <heikki.linnakangas@iki.fi> | 2024-04-08 04:24:51 +0300 |
---|---|---|
committer | Heikki Linnakangas <heikki.linnakangas@iki.fi> | 2024-04-08 04:24:51 +0300 |
commit | 91044ae4baeac2e501e34164a69bd5d9c4976d21 (patch) | |
tree | 030ae118927d18f14ecb8840ec6719932b79773c /src/backend/libpq/be-secure-openssl.c | |
parent | d39a49c1e459804831302807c724fa6512e90cf0 (diff) | |
download | postgresql-91044ae4baeac2e501e34164a69bd5d9c4976d21.tar.gz postgresql-91044ae4baeac2e501e34164a69bd5d9c4976d21.zip |
Send ALPN in TLS handshake, require it in direct SSL connections
libpq now always tries to send ALPN. With the traditional negotiated
SSL connections, the server accepts the ALPN, and refuses the
connection if it's not what we expect, but connecting without ALPN is
still OK. With the new direct SSL connections, ALPN is mandatory.
NOTE: This uses "TBD-pgsql" as the protocol ID. We must register a
proper one with IANA before the release!
Author: Greg Stark, Heikki Linnakangas
Reviewed-by: Matthias van de Meent, Jacob Champion
Diffstat (limited to 'src/backend/libpq/be-secure-openssl.c')
-rw-r--r-- | src/backend/libpq/be-secure-openssl.c | 77 |
1 files changed, 77 insertions, 0 deletions
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c index 72e43af3537..6e877c01731 100644 --- a/src/backend/libpq/be-secure-openssl.c +++ b/src/backend/libpq/be-secure-openssl.c @@ -67,6 +67,12 @@ static int ssl_external_passwd_cb(char *buf, int size, int rwflag, void *userdat static int dummy_ssl_passwd_cb(char *buf, int size, int rwflag, void *userdata); static int verify_cb(int ok, X509_STORE_CTX *ctx); static void info_cb(const SSL *ssl, int type, int args); +static int alpn_cb(SSL *ssl, + const unsigned char **out, + unsigned char *outlen, + const unsigned char *in, + unsigned int inlen, + void *userdata); static bool initialize_dh(SSL_CTX *context, bool isServerStart); static bool initialize_ecdh(SSL_CTX *context, bool isServerStart); static const char *SSLerrmessage(unsigned long ecode); @@ -432,6 +438,9 @@ be_tls_open_server(Port *port) /* set up debugging/info callback */ SSL_CTX_set_info_callback(SSL_context, info_cb); + /* enable ALPN */ + SSL_CTX_set_alpn_select_cb(SSL_context, alpn_cb, port); + if (!(port->ssl = SSL_new(SSL_context))) { ereport(COMMERROR, @@ -571,6 +580,32 @@ aloop: return -1; } + /* Get the protocol selected by ALPN */ + port->alpn_used = false; + { + const unsigned char *selected; + unsigned int len; + + SSL_get0_alpn_selected(port->ssl, &selected, &len); + + /* If ALPN is used, check that we negotiated the expected protocol */ + if (selected != NULL) + { + if (len == strlen(PG_ALPN_PROTOCOL) && + memcmp(selected, PG_ALPN_PROTOCOL, strlen(PG_ALPN_PROTOCOL)) == 0) + { + port->alpn_used = true; + } + else + { + /* shouldn't happen */ + ereport(COMMERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("received SSL connection request with unexpected ALPN protocol"))); + } + } + } + /* Get client certificate, if available. */ port->peer = SSL_get_peer_certificate(port->ssl); @@ -1259,6 +1294,48 @@ info_cb(const SSL *ssl, int type, int args) } } +/* See pqcomm.h comments on OpenSSL implementation of ALPN (RFC 7301) */ +static const unsigned char alpn_protos[] = PG_ALPN_PROTOCOL_VECTOR; + +/* + * Server callback for ALPN negotiation. We use the standard "helper" function + * even though currently we only accept one value. + */ +static int +alpn_cb(SSL *ssl, + const unsigned char **out, + unsigned char *outlen, + const unsigned char *in, + unsigned int inlen, + void *userdata) +{ + /* + * Why does OpenSSL provide a helper function that requires a nonconst + * vector when the callback is declared to take a const vector? What are + * we to do with that? + */ + int retval; + + Assert(userdata != NULL); + Assert(out != NULL); + Assert(outlen != NULL); + Assert(in != NULL); + + retval = SSL_select_next_proto((unsigned char **) out, outlen, + alpn_protos, sizeof(alpn_protos), + in, inlen); + if (*out == NULL || *outlen > sizeof(alpn_protos) || outlen <= 0) + return SSL_TLSEXT_ERR_NOACK; /* can't happen */ + + if (retval == OPENSSL_NPN_NEGOTIATED) + return SSL_TLSEXT_ERR_OK; + else if (retval == OPENSSL_NPN_NO_OVERLAP) + return SSL_TLSEXT_ERR_NOACK; + else + return SSL_TLSEXT_ERR_NOACK; +} + + /* * Set DH parameters for generating ephemeral DH keys. The * DH parameters can take a long time to compute, so they must be |