Reject SSL connection if ALPN is used but there's no common protocol
authorHeikki Linnakangas <[email protected]>
Mon, 29 Apr 2024 15:12:26 +0000 (18:12 +0300)
committerHeikki Linnakangas <[email protected]>
Mon, 29 Apr 2024 15:12:26 +0000 (18:12 +0300)
If the client supports ALPN but tries to use some other protocol, like
HTTPS, reject the connection in the server. That is surely a confusion
of some sort. Furthermore, the ALPN RFC 7301 says:

> In the event that the server supports no protocols that the client
> advertises, then the server SHALL respond with a fatal
> "no_application_protocol" alert.

This commit makes the server follow that advice.

In the client, specifically check for the OpenSSL error code for the
"no_application_protocol" alert. Otherwise you got a cryptic "SSL
error: SSL error code 167773280" error if you tried to connect to a
non-PostgreSQL server that rejects the connection with
"no_application_protocol". ERR_reason_error_string() returns NULL for
that code, which frankly seems like an OpenSSL bug to me, but we can
easily print a better message ourselves.

Reported-by: Jacob Champion
Discussion: https://www.postgresql.org/message-id/6aedcaa5-60f3-49af-a857-2c76ba55a1f3@iki.fi

src/backend/libpq/be-secure-openssl.c
src/interfaces/libpq/fe-secure-openssl.c

index fc46a33539497a533866f3fbc2f03916ad4c1273..60cf68aac4ab286b2a2e6e444896ebc1b58d7e6a 100644 (file)
@@ -1336,10 +1336,14 @@ alpn_cb(SSL *ssl,
 
    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;
+   {
+       /*
+        * The client doesn't support our protocol.  Reject the connection
+        * with TLS "no_application_protocol" alert, per RFC 7301.
+        */
+       return SSL_TLSEXT_ERR_ALERT_FATAL;
+   }
 }
 
 
index 0de21dc7e456c764b503c631cbf3131c9f87fec8..ee1a47f2b18616ed3f3ecb31efa5e32205d0d1c4 100644 (file)
@@ -1741,6 +1741,18 @@ SSLerrmessage(unsigned long ecode)
        return errbuf;
    }
 
+   if (ERR_GET_LIB(ecode) == ERR_LIB_SSL &&
+       ERR_GET_REASON(ecode) == SSL_AD_REASON_OFFSET + SSL_AD_NO_APPLICATION_PROTOCOL)
+   {
+       /*
+        * Server aborted the connection with TLS "no_application_protocol"
+        * alert.  The ERR_reason_error_string() function doesn't give any
+        * error string for that for some reason, so do it ourselves.
+        */
+       snprintf(errbuf, SSL_ERR_LEN, libpq_gettext("no application protocol"));
+       return errbuf;
+   }
+
    /*
     * In OpenSSL 3.0.0 and later, ERR_reason_error_string randomly refuses to
     * map system errno values.  We can cover that shortcoming with this bit