aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/libpq/be-secure-openssl.c67
-rw-r--r--src/include/common/openssl.h23
-rw-r--r--src/interfaces/libpq/fe-secure-openssl.c39
3 files changed, 127 insertions, 2 deletions
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 8adf64c78ee..d1cf455ab44 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -72,6 +72,7 @@ static bool dummy_ssl_passwd_cb_called = false;
static bool ssl_is_server_start;
static int ssl_protocol_version_to_openssl(int v);
+static const char *ssl_protocol_version_to_string(int v);
/* ------------------------------------------------------------ */
/* Public interface */
@@ -365,6 +366,7 @@ be_tls_open_server(Port *port)
int err;
int waitfor;
unsigned long ecode;
+ bool give_proto_hint;
Assert(!port->ssl);
Assert(!port->peer);
@@ -451,10 +453,50 @@ aloop:
errmsg("could not accept SSL connection: EOF detected")));
break;
case SSL_ERROR_SSL:
+ switch (ERR_GET_REASON(ecode))
+ {
+ /*
+ * UNSUPPORTED_PROTOCOL, WRONG_VERSION_NUMBER, and
+ * TLSV1_ALERT_PROTOCOL_VERSION have been observed
+ * when trying to communicate with an old OpenSSL
+ * library, or when the client and server specify
+ * disjoint protocol ranges. NO_PROTOCOLS_AVAILABLE
+ * occurs if there's a local misconfiguration (which
+ * can happen despite our checks, if openssl.cnf
+ * injects a limit we didn't account for). It's not
+ * very clear what would make OpenSSL return the other
+ * codes listed here, but a hint about protocol
+ * versions seems like it's appropriate for all.
+ */
+ case SSL_R_NO_PROTOCOLS_AVAILABLE:
+ case SSL_R_UNSUPPORTED_PROTOCOL:
+ case SSL_R_BAD_PROTOCOL_VERSION_NUMBER:
+ case SSL_R_UNKNOWN_PROTOCOL:
+ case SSL_R_UNKNOWN_SSL_VERSION:
+ case SSL_R_UNSUPPORTED_SSL_VERSION:
+ case SSL_R_VERSION_TOO_HIGH:
+ case SSL_R_VERSION_TOO_LOW:
+ case SSL_R_WRONG_SSL_VERSION:
+ case SSL_R_WRONG_VERSION_NUMBER:
+ case SSL_R_TLSV1_ALERT_PROTOCOL_VERSION:
+ give_proto_hint = true;
+ break;
+ default:
+ give_proto_hint = false;
+ break;
+ }
ereport(COMMERROR,
(errcode(ERRCODE_PROTOCOL_VIOLATION),
errmsg("could not accept SSL connection: %s",
- SSLerrmessage(ecode))));
+ SSLerrmessage(ecode)),
+ give_proto_hint ?
+ errhint("This may indicate that the client does not support any SSL protocol version between %s and %s.",
+ ssl_min_protocol_version ?
+ ssl_protocol_version_to_string(ssl_min_protocol_version) :
+ MIN_OPENSSL_TLS_VERSION,
+ ssl_max_protocol_version ?
+ ssl_protocol_version_to_string(ssl_max_protocol_version) :
+ MAX_OPENSSL_TLS_VERSION) : 0));
break;
case SSL_ERROR_ZERO_RETURN:
ereport(COMMERROR,
@@ -1328,6 +1370,29 @@ ssl_protocol_version_to_openssl(int v)
return -1;
}
+/*
+ * Likewise provide a mapping to strings.
+ */
+static const char *
+ssl_protocol_version_to_string(int v)
+{
+ switch (v)
+ {
+ case PG_TLS_ANY:
+ return "any";
+ case PG_TLS1_VERSION:
+ return "TLSv1";
+ case PG_TLS1_1_VERSION:
+ return "TLSv1.1";
+ case PG_TLS1_2_VERSION:
+ return "TLSv1.2";
+ case PG_TLS1_3_VERSION:
+ return "TLSv1.3";
+ }
+
+ return "(unrecognized)";
+}
+
static void
default_openssl_tls_init(SSL_CTX *context, bool isServerStart)
diff --git a/src/include/common/openssl.h b/src/include/common/openssl.h
index 47fa1299945..9d1c681d9f8 100644
--- a/src/include/common/openssl.h
+++ b/src/include/common/openssl.h
@@ -17,12 +17,33 @@
#ifdef USE_OPENSSL
#include <openssl/ssl.h>
+/*
+ * OpenSSL doesn't provide any very nice way to identify the min/max
+ * protocol versions the library supports, so we fake it as best we can.
+ * Note in particular that this doesn't account for restrictions that
+ * might be specified in the installation's openssl.cnf.
+ *
+ * We disable SSLv3 and older in library setup, so TLSv1 is the oldest
+ * protocol version of interest.
+ */
+#define MIN_OPENSSL_TLS_VERSION "TLSv1"
+
+#if defined(TLS1_3_VERSION)
+#define MAX_OPENSSL_TLS_VERSION "TLSv1.3"
+#elif defined(TLS1_2_VERSION)
+#define MAX_OPENSSL_TLS_VERSION "TLSv1.2"
+#elif defined(TLS1_1_VERSION)
+#define MAX_OPENSSL_TLS_VERSION "TLSv1.1"
+#else
+#define MAX_OPENSSL_TLS_VERSION "TLSv1"
+#endif
+
/* src/common/protocol_openssl.c */
#ifndef SSL_CTX_set_min_proto_version
extern int SSL_CTX_set_min_proto_version(SSL_CTX *ctx, int version);
extern int SSL_CTX_set_max_proto_version(SSL_CTX *ctx, int version);
#endif
-#endif
+#endif /* USE_OPENSSL */
#endif /* COMMON_OPENSSL_H */
diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c
index 2d813ef5f9b..b5b2006b75d 100644
--- a/src/interfaces/libpq/fe-secure-openssl.c
+++ b/src/interfaces/libpq/fe-secure-openssl.c
@@ -1304,6 +1304,45 @@ open_client_SSL(PGconn *conn)
libpq_gettext("SSL error: %s\n"),
err);
SSLerrfree(err);
+ switch (ERR_GET_REASON(ecode))
+ {
+ /*
+ * UNSUPPORTED_PROTOCOL, WRONG_VERSION_NUMBER, and
+ * TLSV1_ALERT_PROTOCOL_VERSION have been observed
+ * when trying to communicate with an old OpenSSL
+ * library, or when the client and server specify
+ * disjoint protocol ranges.
+ * NO_PROTOCOLS_AVAILABLE occurs if there's a
+ * local misconfiguration (which can happen
+ * despite our checks, if openssl.cnf injects a
+ * limit we didn't account for). It's not very
+ * clear what would make OpenSSL return the other
+ * codes listed here, but a hint about protocol
+ * versions seems like it's appropriate for all.
+ */
+ case SSL_R_NO_PROTOCOLS_AVAILABLE:
+ case SSL_R_UNSUPPORTED_PROTOCOL:
+ case SSL_R_BAD_PROTOCOL_VERSION_NUMBER:
+ case SSL_R_UNKNOWN_PROTOCOL:
+ case SSL_R_UNKNOWN_SSL_VERSION:
+ case SSL_R_UNSUPPORTED_SSL_VERSION:
+ case SSL_R_VERSION_TOO_HIGH:
+ case SSL_R_VERSION_TOO_LOW:
+ case SSL_R_WRONG_SSL_VERSION:
+ case SSL_R_WRONG_VERSION_NUMBER:
+ case SSL_R_TLSV1_ALERT_PROTOCOL_VERSION:
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("This may indicate that the server does not support any SSL protocol version between %s and %s.\n"),
+ conn->ssl_min_protocol_version ?
+ conn->ssl_min_protocol_version :
+ MIN_OPENSSL_TLS_VERSION,
+ conn->ssl_max_protocol_version ?
+ conn->ssl_max_protocol_version :
+ MAX_OPENSSL_TLS_VERSION);
+ break;
+ default:
+ break;
+ }
pgtls_close(conn);
return PGRES_POLLING_FAILED;
}