Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Cope with a deficiency in OpenSSL 3.x's error reporting.
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 8 Mar 2024 00:37:51 +0000 (19:37 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 8 Mar 2024 00:37:51 +0000 (19:37 -0500)
In OpenSSL 3.0.0 and later, ERR_reason_error_string randomly refuses
to provide a string for error codes representing system errno values
(e.g., "No such file or directory").  There is a poorly-documented way
to extract the errno from the SSL error code in this case, so do that
and apply strerror, rather than falling back to reporting the error
code's numeric value as we were previously doing.

Problem reported by David Zhang, although this is not his proposed
patch; it's instead based on a suggestion from Heikki Linnakangas.
Back-patch to all supported branches, since any of them are likely
to be used with recent OpenSSL.

Discussion: https://postgr.es/m/b6fb018b-f05c-4afd-abd3-318c649faf18@highgo.ca

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

index 571ae436d577f20e9a7d7e5067edb98f51f7ed17..e19f4f0981c2db89e30de86e0833e693eced3e6d 100644 (file)
@@ -1096,9 +1096,9 @@ initialize_ecdh(SSL_CTX *context, bool isServerStart)
  *
  * ERR_get_error() is used by caller to get errcode to pass here.
  *
- * Some caution is needed here since ERR_reason_error_string will
- * return NULL if it doesn't recognize the error code.  We don't
- * want to return NULL ever.
+ * Some caution is needed here since ERR_reason_error_string will return NULL
+ * if it doesn't recognize the error code, or (in OpenSSL >= 3) if the code
+ * represents a system errno value.  We don't want to return NULL ever.
  */
 static const char *
 SSLerrmessage(unsigned long ecode)
@@ -1111,6 +1111,19 @@ SSLerrmessage(unsigned long ecode)
    errreason = ERR_reason_error_string(ecode);
    if (errreason != NULL)
        return errreason;
+
+   /*
+    * 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
+    * of code.  Older OpenSSL versions don't have the ERR_SYSTEM_ERROR macro,
+    * but that's okay because they don't have the shortcoming either.
+    */
+#ifdef ERR_SYSTEM_ERROR
+   if (ERR_SYSTEM_ERROR(ecode))
+       return strerror(ERR_GET_REASON(ecode));
+#endif
+
+   /* No choice but to report the numeric ecode */
    snprintf(errbuf, sizeof(errbuf), _("SSL error code %lu"), ecode);
    return errbuf;
 }
index eddefb02eeb1806b2cfe77a83261be4ccc683368..7c4db03607bf217a76ce3ec74e46e503b594dd55 100644 (file)
@@ -1355,10 +1355,11 @@ pgtls_close(PGconn *conn)
  * Obtain reason string for passed SSL errcode
  *
  * ERR_get_error() is used by caller to get errcode to pass here.
+ * The result must be freed after use, using SSLerrfree.
  *
- * Some caution is needed here since ERR_reason_error_string will
- * return NULL if it doesn't recognize the error code.  We don't
- * want to return NULL ever.
+ * Some caution is needed here since ERR_reason_error_string will return NULL
+ * if it doesn't recognize the error code, or (in OpenSSL >= 3) if the code
+ * represents a system errno value.  We don't want to return NULL ever.
  */
 static char ssl_nomem[] = "out of memory allocating error description";
 
@@ -1384,6 +1385,22 @@ SSLerrmessage(unsigned long ecode)
        strlcpy(errbuf, errreason, SSL_ERR_LEN);
        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
+    * of code.  Older OpenSSL versions don't have the ERR_SYSTEM_ERROR macro,
+    * but that's okay because they don't have the shortcoming either.
+    */
+#ifdef ERR_SYSTEM_ERROR
+   if (ERR_SYSTEM_ERROR(ecode))
+   {
+       strlcpy(errbuf, strerror(ERR_GET_REASON(ecode)), SSL_ERR_LEN);
+       return errbuf;
+   }
+#endif
+
+   /* No choice but to report the numeric ecode */
    snprintf(errbuf, SSL_ERR_LEN, libpq_gettext("SSL error code %lu"), ecode);
    return errbuf;
 }