diff options
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 |