From 849d9cb45286cb9f03111c6b503d7361068be21c Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Mon, 5 Aug 2024 17:09:57 +1200
Subject: [PATCH v1 5/5] XXX BlastRADIUS back-patch kludge for 12 and 13

---
 doc/src/sgml/client-auth.sgml | 11 +++++++++
 src/backend/libpq/auth.c      | 42 ++++++++++++++++++++++++++++++++++-
 2 files changed, 52 insertions(+), 1 deletion(-)

diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml
index 6c49ba18346..70a55db3ec1 100644
--- a/doc/src/sgml/client-auth.sgml
+++ b/doc/src/sgml/client-auth.sgml
@@ -2175,6 +2175,17 @@ host ... ldap ldapbasedn="dc=example,dc=net"
          always include a <literal>Message-Authenticator</literal> attribute
          (but didn't in earlier releases).
         </para>
+        <note>
+         <para>
+          <literal>Message-Authenticator</literal> attributes can only be
+          computed if <productname>PostgreSQL</productname> was built support for
+          <productname>OpenSSL</productname>.  Otherwise, they
+          will be silently omitted from outgoing messages, and ignored on
+          incoming messages regardless of this setting, which may cause some
+          <productname>RADIUS</productname> servers to reject or ignore
+          requests.
+         </para>
+        </note>
        </listitem>
       </varlistentry>
 
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index 40f0cabf3a9..8a5876723e0 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -24,7 +24,6 @@
 #include <unistd.h>
 
 #include "commands/user.h"
-#include "common/hmac.h"
 #include "common/ip.h"
 #include "common/md5.h"
 #include "libpq/auth.h"
@@ -40,6 +39,10 @@
 #include "storage/ipc.h"
 #include "utils/memutils.h"
 
+#ifdef USE_OPENSSL
+#include "openssl/hmac.h"
+#endif
+
 /*----------------------------------------------------------------
  * Global authentication functions
  *----------------------------------------------------------------
@@ -2811,6 +2814,31 @@ typedef struct
 /* Seconds to wait - XXX: should be in a config variable! */
 #define RADIUS_TIMEOUT 3
 
+#ifdef USE_OPENSSL
+/*
+ * Locally redirect PostgreSQL 14's pg_hmac_XXX API directly to OpenSSL
+ * functions, to simplify back-patching of BlastRADIUS mitigation to
+ * PostgreSQL 12 and 13.  If built without OpenSSL, skip HMAC support and
+ * disable Message-Authenticator attributes.
+ */
+#define RADIUS_USE_HMAC
+#define pg_hmac_ctx HMAC_CTX
+#define pg_hmac_create(...) HMAC_CTX_new()
+#define pg_hmac_init(ctx, key, key_len, ...) HMAC_Init_ex(ctx, key, key_len, EVP_md5(), NULL)
+#define pg_hmac_update HMAC_Update
+static inline int
+pg_hmac_final(pg_hmac_ctx *ctx, void *md, size_t len)
+{
+	unsigned int l = len;
+	int			result = HMAC_Final(ctx, md, &l);
+
+	Assert(l == len);
+	return result;
+}
+#define pg_hmac_free(x) if (x) HMAC_CTX_free(x)
+#define pg_hmac_error(x) "HMAC error"
+#endif
+
 static uint8 *
 radius_add_attribute(radius_packet *packet, uint8 type, const unsigned char *data, int len)
 {
@@ -2843,6 +2871,7 @@ radius_add_attribute(radius_packet *packet, uint8 type, const unsigned char *dat
 	return attr->data;
 }
 
+#ifdef RADIUS_USE_HMAC
 /*
  * Search for an attribute in a received message.  If the attribute is found,
  * sets *value and *length (not including the header), and returns true.  If
@@ -2889,6 +2918,7 @@ radius_find_attribute(uint8 *packet,
 	*length = 0;
 	return true;
 }
+#endif
 
 static int
 CheckRADIUSAuth(Port *port)
@@ -2989,11 +3019,13 @@ CheckRADIUSAuth(Port *port)
 static int
 PerformRadiusTransaction(const char *server, const char *secret, const char *portstr, const char *identifier, const char *user_name, const char *passwd, bool requirema)
 {
+#ifdef RADIUS_USE_HMAC
 	pg_hmac_ctx *hmac_context;
 	uint8		message_authenticator_key[RADIUS_VECTOR_LENGTH];
 	uint8		message_authenticator[RADIUS_VECTOR_LENGTH];
 	uint8	   *message_authenticator_location;
 	uint8		message_authenticator_size;
+#endif
 	radius_packet radius_send_pack;
 	radius_packet radius_recv_pack;
 	radius_packet *packet = &radius_send_pack;
@@ -3055,6 +3087,8 @@ PerformRadiusTransaction(const char *server, const char *secret, const char *por
 	}
 	packet->id = packet->vector[0];
 
+#ifdef RADIUS_USE_HMAC
+
 	/*
 	 * Add Message-Authenticator attribute first, which for now holds zeroes.
 	 * We remember where it is in the message so that we can fill it in later.
@@ -3065,6 +3099,7 @@ PerformRadiusTransaction(const char *server, const char *secret, const char *por
 							 RADIUS_MESSAGE_AUTHENTICATOR,
 							 message_authenticator,
 							 lengthof(message_authenticator));
+#endif
 
 	radius_add_attribute(packet, RADIUS_SERVICE_TYPE, (const unsigned char *) &service, sizeof(service));
 	radius_add_attribute(packet, RADIUS_USER_NAME, (const unsigned char *) user_name, strlen(user_name));
@@ -3121,6 +3156,8 @@ PerformRadiusTransaction(const char *server, const char *secret, const char *por
 	packetlength = packet->length;
 	packet->length = pg_hton16(packet->length);
 
+#ifdef RADIUS_USE_HMAC
+
 	/*
 	 * We use the first 16 bytes of the shared secret, zero-padded if too
 	 * short, as an HMAC-MD5 key for creating and validating
@@ -3158,6 +3195,7 @@ PerformRadiusTransaction(const char *server, const char *secret, const char *por
 	/* Overwrite the attribute with the computed signature. */
 	memcpy(message_authenticator_location, message_authenticator,
 		   lengthof(message_authenticator));
+#endif
 
 	sock = socket(serveraddrs[0].ai_family, SOCK_DGRAM, 0);
 	if (sock == PGINVALID_SOCKET)
@@ -3343,6 +3381,7 @@ PerformRadiusTransaction(const char *server, const char *secret, const char *por
 			continue;
 		}
 
+#ifdef RADIUS_USE_HMAC
 		/* Search for the Message-Authenticator attribute. */
 		if (!radius_find_attribute((uint8 *) receive_buffer,
 								   packetlength,
@@ -3429,6 +3468,7 @@ PerformRadiusTransaction(const char *server, const char *secret, const char *por
 				continue;
 			}
 		}
+#endif
 
 		if (receivepacket->code == RADIUS_ACCESS_ACCEPT)
 		{
-- 
2.39.3 (Apple Git-146)

