From 3fb3e2c7d002deed19f89d69f15b49c0d359b3e4 Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Fri, 23 Jan 2026 12:48:21 +1300
Subject: [PATCH 2/3] Drop RADIUS support.

Our RADIUS authentication supported only RADIUS/UDP with MD5 and weakly
protected credentials, and didn't use the recommended
Message-Authenticator attribute to mitigate against the Blast-RADIUS
vulnerability.  By now, popular RADIUS servers are expected to generate
loud warnings or reject our authentication attempts outright.

Since there have been no user reports about this, it seems unlikely that
there are users.

Reviewed-by:
Discussion:
---
 doc/src/sgml/client-auth.sgml        | 137 -------
 src/backend/libpq/auth.c             | 511 +--------------------------
 src/backend/libpq/hba.c              | 216 -----------
 src/backend/libpq/pg_hba.conf.sample |   4 +-
 src/backend/utils/adt/hbafuncs.c     |  19 -
 src/include/libpq/hba.h              |   9 -
 src/tools/pgindent/typedefs.list     |   2 -
 7 files changed, 3 insertions(+), 895 deletions(-)

diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml
index 00cf882a73f..16a385127d7 100644
--- a/doc/src/sgml/client-auth.sgml
+++ b/doc/src/sgml/client-auth.sgml
@@ -616,16 +616,6 @@ include_dir         <replaceable>directory</replaceable>
         </listitem>
        </varlistentry>
 
-       <varlistentry>
-        <term><literal>radius</literal></term>
-        <listitem>
-         <para>
-          Authenticate using a RADIUS server. See <xref
-          linkend="auth-radius"/> for details.
-         </para>
-        </listitem>
-       </varlistentry>
-
        <varlistentry>
         <term><literal>cert</literal></term>
         <listitem>
@@ -1127,12 +1117,6 @@ omicron         bryanh                  guest1
       relies on an LDAP authentication server.
      </para>
     </listitem>
-    <listitem>
-     <para>
-      <link linkend="auth-radius">RADIUS authentication</link>, which
-      relies on a RADIUS authentication server.
-     </para>
-    </listitem>
     <listitem>
      <para>
       <link linkend="auth-cert">Certificate authentication</link>, which
@@ -2096,127 +2080,6 @@ host ... ldap ldapbasedn="dc=example,dc=net"
 
   </sect1>
 
-  <sect1 id="auth-radius">
-   <title>RADIUS Authentication</title>
-
-   <indexterm zone="auth-radius">
-    <primary>RADIUS</primary>
-   </indexterm>
-
-   <para>
-    This authentication method operates similarly to
-    <literal>password</literal> except that it uses RADIUS
-    as the password verification method. RADIUS is used only to validate
-    the user name/password pairs. Therefore the user must already
-    exist in the database before RADIUS can be used for
-    authentication.
-    <note>
-     <para>
-      Only the obsolete RADIUS/UDP protocol is supported.  This
-      implementation does not provide the Message-Authenticator
-      attribute, which may be required by modern RADIUS servers.
-      RADIUS/UDP should be considered insecure and deprecated, and
-      will be removed in a future release.
-     </para>
-    </note>
-   </para>
-
-   <para>
-    When using RADIUS authentication, an Access Request message will be sent
-    to the configured RADIUS server. This request will be of type
-    <literal>Authenticate Only</literal>, and include parameters for
-    <literal>user name</literal>, <literal>password</literal> (encrypted) and
-    <literal>NAS Identifier</literal>. The request will be encrypted using
-    a secret shared with the server. The RADIUS server will respond to
-    this request with either <literal>Access Accept</literal> or
-    <literal>Access Reject</literal>. There is no support for RADIUS accounting.
-   </para>
-
-   <para>
-    Multiple RADIUS servers can be specified, in which case they will
-    be tried sequentially. If a negative response is received from
-    a server, the authentication will fail. If no response is received,
-    the next server in the list will be tried. To specify multiple
-    servers, separate the server names with commas and surround the list
-    with double quotes. If multiple servers are specified, the other
-    RADIUS options can also be given as comma-separated lists, to provide
-    individual values for each server. They can also be specified as
-    a single value, in which case that value will apply to all servers.
-   </para>
-
-   <para>
-    The following configuration options are supported for RADIUS:
-     <variablelist>
-      <varlistentry>
-       <term><literal>radiusservers</literal></term>
-       <listitem>
-        <para>
-         The DNS names or IP addresses of the RADIUS servers to connect to.
-         This parameter is required.
-        </para>
-       </listitem>
-      </varlistentry>
-
-      <varlistentry>
-       <term><literal>radiussecrets</literal></term>
-       <listitem>
-        <para>
-         The shared secrets used when talking securely to the RADIUS
-         servers. This must have exactly the same value on the PostgreSQL
-         and RADIUS servers. It is recommended that this be a string of
-         at least 16 characters. This parameter is required.
-         <note>
-         <para>
-          The encryption vector used will only be cryptographically
-          strong if <productname>PostgreSQL</productname> is built with support for
-          <productname>OpenSSL</productname>. In other cases, the transmission to the
-          RADIUS server should only be considered obfuscated, not secured, and
-          external security measures should be applied if necessary.
-         </para>
-         </note>
-        </para>
-       </listitem>
-      </varlistentry>
-
-      <varlistentry>
-       <term><literal>radiusports</literal></term>
-       <listitem>
-        <para>
-         The port numbers to connect to on the RADIUS servers. If no port
-         is specified, the default RADIUS port (<literal>1812</literal>)
-         will be used.
-        </para>
-       </listitem>
-      </varlistentry>
-
-      <varlistentry>
-       <term><literal>radiusidentifiers</literal></term>
-       <listitem>
-        <para>
-         The strings to be used as <literal>NAS Identifier</literal> in the
-         RADIUS requests. This parameter can be used, for example, to
-         identify which database cluster the user is attempting to connect
-         to, which can be useful for policy matching on
-         the RADIUS server. If no identifier is specified, the default
-         <literal>postgresql</literal> will be used.
-        </para>
-       </listitem>
-      </varlistentry>
-
-     </variablelist>
-   </para>
-
-   <para>
-    If it is necessary to have a comma or whitespace in a RADIUS parameter
-    value, that can be done by putting double quotes around the value, but
-    it is tedious because two layers of double-quoting are now required.
-    An example of putting whitespace into RADIUS secret strings is:
-<programlisting>
-host ... radius radiusservers="server1,server2" radiussecrets="""secret one"",""secret two"""
-</programlisting>
-   </para>
-  </sect1>
-
   <sect1 id="auth-cert">
    <title>Certificate Authentication</title>
 
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index 795bfed8d19..d5b913245f4 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -202,13 +202,6 @@ static int	pg_SSPI_make_upn(char *accountname,
 							 bool update_accountname);
 #endif
 
-/*----------------------------------------------------------------
- * RADIUS Authentication
- *----------------------------------------------------------------
- */
-static int	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);
-
 
 /*----------------------------------------------------------------
  * Global authentication functions
@@ -295,9 +288,6 @@ auth_failed(Port *port, int status, const char *logdetail)
 		case uaCert:
 			errstr = gettext_noop("certificate authentication failed for user \"%s\"");
 			break;
-		case uaRADIUS:
-			errstr = gettext_noop("RADIUS authentication failed for user \"%s\"");
-			break;
 		case uaOAuth:
 			errstr = gettext_noop("OAuth bearer authentication failed for user \"%s\"");
 			break;
@@ -616,9 +606,6 @@ ClientAuthentication(Port *port)
 			Assert(false);
 #endif
 			break;
-		case uaRADIUS:
-			status = CheckRADIUSAuth(port);
-			break;
 		case uaCert:
 			/* uaCert will be treated as if clientcert=verify-full (uaTrust) */
 		case uaTrust:
@@ -757,7 +744,7 @@ recv_password_packet(Port *port)
 	 * We rely on that for MD5 and SCRAM authentication, but we still need
 	 * this check here, to prevent an empty password from being used with
 	 * authentication methods that check the password against an external
-	 * system, like PAM, LDAP and RADIUS.
+	 * system, like PAM and LDAP.
 	 */
 	if (buf.len == 1)
 		ereport(ERROR,
@@ -2772,499 +2759,3 @@ CheckCertAuth(Port *port)
 	return status_check_usermap;
 }
 #endif
-
-
-/*----------------------------------------------------------------
- * RADIUS authentication
- *----------------------------------------------------------------
- */
-
-/*
- * RADIUS authentication is described in RFC2865 (and several others).
- */
-
-#define RADIUS_VECTOR_LENGTH 16
-#define RADIUS_HEADER_LENGTH 20
-#define RADIUS_MAX_PASSWORD_LENGTH 128
-
-/* Maximum size of a RADIUS packet we will create or accept */
-#define RADIUS_BUFFER_SIZE 1024
-
-typedef struct
-{
-	uint8		attribute;
-	uint8		length;
-	uint8		data[FLEXIBLE_ARRAY_MEMBER];
-} radius_attribute;
-
-typedef struct
-{
-	uint8		code;
-	uint8		id;
-	uint16		length;
-	uint8		vector[RADIUS_VECTOR_LENGTH];
-	/* this is a bit longer than strictly necessary: */
-	char		pad[RADIUS_BUFFER_SIZE - RADIUS_VECTOR_LENGTH];
-} radius_packet;
-
-/* RADIUS packet types */
-#define RADIUS_ACCESS_REQUEST	1
-#define RADIUS_ACCESS_ACCEPT	2
-#define RADIUS_ACCESS_REJECT	3
-
-/* RADIUS attributes */
-#define RADIUS_USER_NAME		1
-#define RADIUS_PASSWORD			2
-#define RADIUS_SERVICE_TYPE		6
-#define RADIUS_NAS_IDENTIFIER	32
-
-/* RADIUS service types */
-#define RADIUS_AUTHENTICATE_ONLY	8
-
-/* Seconds to wait - XXX: should be in a config variable! */
-#define RADIUS_TIMEOUT 3
-
-static void
-radius_add_attribute(radius_packet *packet, uint8 type, const unsigned char *data, int len)
-{
-	radius_attribute *attr;
-
-	if (packet->length + len > RADIUS_BUFFER_SIZE)
-	{
-		/*
-		 * With remotely realistic data, this can never happen. But catch it
-		 * just to make sure we don't overrun a buffer. We'll just skip adding
-		 * the broken attribute, which will in the end cause authentication to
-		 * fail.
-		 */
-		elog(WARNING,
-			 "adding attribute code %d with length %d to radius packet would create oversize packet, ignoring",
-			 type, len);
-		return;
-	}
-
-	attr = (radius_attribute *) ((unsigned char *) packet + packet->length);
-	attr->attribute = type;
-	attr->length = len + 2;		/* total size includes type and length */
-	memcpy(attr->data, data, len);
-	packet->length += attr->length;
-}
-
-static int
-CheckRADIUSAuth(Port *port)
-{
-	char	   *passwd;
-	ListCell   *server,
-			   *secrets,
-			   *radiusports,
-			   *identifiers;
-
-	/* Make sure struct alignment is correct */
-	Assert(offsetof(radius_packet, vector) == 4);
-
-	/* Verify parameters */
-	if (port->hba->radiusservers == NIL)
-	{
-		ereport(LOG,
-				(errmsg("RADIUS server not specified")));
-		return STATUS_ERROR;
-	}
-
-	if (port->hba->radiussecrets == NIL)
-	{
-		ereport(LOG,
-				(errmsg("RADIUS secret not specified")));
-		return STATUS_ERROR;
-	}
-
-	/* Send regular password request to client, and get the response */
-	sendAuthRequest(port, AUTH_REQ_PASSWORD, NULL, 0);
-
-	passwd = recv_password_packet(port);
-	if (passwd == NULL)
-		return STATUS_EOF;		/* client wouldn't send password */
-
-	if (strlen(passwd) > RADIUS_MAX_PASSWORD_LENGTH)
-	{
-		ereport(LOG,
-				(errmsg("RADIUS authentication does not support passwords longer than %d characters", RADIUS_MAX_PASSWORD_LENGTH)));
-		pfree(passwd);
-		return STATUS_ERROR;
-	}
-
-	/*
-	 * Loop over and try each server in order.
-	 */
-	secrets = list_head(port->hba->radiussecrets);
-	radiusports = list_head(port->hba->radiusports);
-	identifiers = list_head(port->hba->radiusidentifiers);
-	foreach(server, port->hba->radiusservers)
-	{
-		int			ret = PerformRadiusTransaction(lfirst(server),
-												   lfirst(secrets),
-												   radiusports ? lfirst(radiusports) : NULL,
-												   identifiers ? lfirst(identifiers) : NULL,
-												   port->user_name,
-												   passwd);
-
-		/*------
-		 * STATUS_OK = Login OK
-		 * STATUS_ERROR = Login not OK, but try next server
-		 * STATUS_EOF = Login not OK, and don't try next server
-		 *------
-		 */
-		if (ret == STATUS_OK)
-		{
-			set_authn_id(port, port->user_name);
-
-			pfree(passwd);
-			return STATUS_OK;
-		}
-		else if (ret == STATUS_EOF)
-		{
-			pfree(passwd);
-			return STATUS_ERROR;
-		}
-
-		/*
-		 * secret, port and identifiers either have length 0 (use default),
-		 * length 1 (use the same everywhere) or the same length as servers.
-		 * So if the length is >1, we advance one step. In other cases, we
-		 * don't and will then reuse the correct value.
-		 */
-		if (list_length(port->hba->radiussecrets) > 1)
-			secrets = lnext(port->hba->radiussecrets, secrets);
-		if (list_length(port->hba->radiusports) > 1)
-			radiusports = lnext(port->hba->radiusports, radiusports);
-		if (list_length(port->hba->radiusidentifiers) > 1)
-			identifiers = lnext(port->hba->radiusidentifiers, identifiers);
-	}
-
-	/* No servers left to try, so give up */
-	pfree(passwd);
-	return STATUS_ERROR;
-}
-
-static int
-PerformRadiusTransaction(const char *server, const char *secret, const char *portstr, const char *identifier, const char *user_name, const char *passwd)
-{
-	radius_packet radius_send_pack;
-	radius_packet radius_recv_pack;
-	radius_packet *packet = &radius_send_pack;
-	radius_packet *receivepacket = &radius_recv_pack;
-	void	   *radius_buffer = &radius_send_pack;
-	void	   *receive_buffer = &radius_recv_pack;
-	int32		service = pg_hton32(RADIUS_AUTHENTICATE_ONLY);
-	uint8	   *cryptvector;
-	int			encryptedpasswordlen;
-	uint8		encryptedpassword[RADIUS_MAX_PASSWORD_LENGTH];
-	uint8	   *md5trailer;
-	int			packetlength;
-	pgsocket	sock;
-
-	struct sockaddr_in6 localaddr;
-	struct sockaddr_in6 remoteaddr;
-	struct addrinfo hint;
-	struct addrinfo *serveraddrs;
-	int			port;
-	socklen_t	addrsize;
-	fd_set		fdset;
-	struct timeval endtime;
-	int			i,
-				j,
-				r;
-
-	/* Assign default values */
-	if (portstr == NULL)
-		portstr = "1812";
-	if (identifier == NULL)
-		identifier = "postgresql";
-
-	MemSet(&hint, 0, sizeof(hint));
-	hint.ai_socktype = SOCK_DGRAM;
-	hint.ai_family = AF_UNSPEC;
-	port = atoi(portstr);
-
-	r = pg_getaddrinfo_all(server, portstr, &hint, &serveraddrs);
-	if (r || !serveraddrs)
-	{
-		ereport(LOG,
-				(errmsg("could not translate RADIUS server name \"%s\" to address: %s",
-						server, gai_strerror(r))));
-		if (serveraddrs)
-			pg_freeaddrinfo_all(hint.ai_family, serveraddrs);
-		return STATUS_ERROR;
-	}
-	/* XXX: add support for multiple returned addresses? */
-
-	/* Construct RADIUS packet */
-	packet->code = RADIUS_ACCESS_REQUEST;
-	packet->length = RADIUS_HEADER_LENGTH;
-	if (!pg_strong_random(packet->vector, RADIUS_VECTOR_LENGTH))
-	{
-		ereport(LOG,
-				(errmsg("could not generate random encryption vector")));
-		pg_freeaddrinfo_all(hint.ai_family, serveraddrs);
-		return STATUS_ERROR;
-	}
-	packet->id = packet->vector[0];
-	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));
-	radius_add_attribute(packet, RADIUS_NAS_IDENTIFIER, (const unsigned char *) identifier, strlen(identifier));
-
-	/*
-	 * RADIUS password attributes are calculated as: e[0] = p[0] XOR
-	 * MD5(secret + Request Authenticator) for the first group of 16 octets,
-	 * and then: e[i] = p[i] XOR MD5(secret + e[i-1]) for the following ones
-	 * (if necessary)
-	 */
-	encryptedpasswordlen = ((strlen(passwd) + RADIUS_VECTOR_LENGTH - 1) / RADIUS_VECTOR_LENGTH) * RADIUS_VECTOR_LENGTH;
-	cryptvector = palloc(strlen(secret) + RADIUS_VECTOR_LENGTH);
-	memcpy(cryptvector, secret, strlen(secret));
-
-	/* for the first iteration, we use the Request Authenticator vector */
-	md5trailer = packet->vector;
-	for (i = 0; i < encryptedpasswordlen; i += RADIUS_VECTOR_LENGTH)
-	{
-		const char *errstr = NULL;
-
-		memcpy(cryptvector + strlen(secret), md5trailer, RADIUS_VECTOR_LENGTH);
-
-		/*
-		 * .. and for subsequent iterations the result of the previous XOR
-		 * (calculated below)
-		 */
-		md5trailer = encryptedpassword + i;
-
-		if (!pg_md5_binary(cryptvector, strlen(secret) + RADIUS_VECTOR_LENGTH,
-						   encryptedpassword + i, &errstr))
-		{
-			ereport(LOG,
-					(errmsg("could not perform MD5 encryption of password: %s",
-							errstr)));
-			pfree(cryptvector);
-			pg_freeaddrinfo_all(hint.ai_family, serveraddrs);
-			return STATUS_ERROR;
-		}
-
-		for (j = i; j < i + RADIUS_VECTOR_LENGTH; j++)
-		{
-			if (j < strlen(passwd))
-				encryptedpassword[j] = passwd[j] ^ encryptedpassword[j];
-			else
-				encryptedpassword[j] = '\0' ^ encryptedpassword[j];
-		}
-	}
-	pfree(cryptvector);
-
-	radius_add_attribute(packet, RADIUS_PASSWORD, encryptedpassword, encryptedpasswordlen);
-
-	/* Length needs to be in network order on the wire */
-	packetlength = packet->length;
-	packet->length = pg_hton16(packet->length);
-
-	sock = socket(serveraddrs[0].ai_family, SOCK_DGRAM, 0);
-	if (sock == PGINVALID_SOCKET)
-	{
-		ereport(LOG,
-				(errmsg("could not create RADIUS socket: %m")));
-		pg_freeaddrinfo_all(hint.ai_family, serveraddrs);
-		return STATUS_ERROR;
-	}
-
-	memset(&localaddr, 0, sizeof(localaddr));
-	localaddr.sin6_family = serveraddrs[0].ai_family;
-	localaddr.sin6_addr = in6addr_any;
-	if (localaddr.sin6_family == AF_INET6)
-		addrsize = sizeof(struct sockaddr_in6);
-	else
-		addrsize = sizeof(struct sockaddr_in);
-
-	if (bind(sock, (struct sockaddr *) &localaddr, addrsize))
-	{
-		ereport(LOG,
-				(errmsg("could not bind local RADIUS socket: %m")));
-		closesocket(sock);
-		pg_freeaddrinfo_all(hint.ai_family, serveraddrs);
-		return STATUS_ERROR;
-	}
-
-	if (sendto(sock, radius_buffer, packetlength, 0,
-			   serveraddrs[0].ai_addr, serveraddrs[0].ai_addrlen) < 0)
-	{
-		ereport(LOG,
-				(errmsg("could not send RADIUS packet: %m")));
-		closesocket(sock);
-		pg_freeaddrinfo_all(hint.ai_family, serveraddrs);
-		return STATUS_ERROR;
-	}
-
-	/* Don't need the server address anymore */
-	pg_freeaddrinfo_all(hint.ai_family, serveraddrs);
-
-	/*
-	 * Figure out at what time we should time out. We can't just use a single
-	 * call to select() with a timeout, since somebody can be sending invalid
-	 * packets to our port thus causing us to retry in a loop and never time
-	 * out.
-	 *
-	 * XXX: Using WaitLatchOrSocket() and doing a CHECK_FOR_INTERRUPTS() if
-	 * the latch was set would improve the responsiveness to
-	 * timeouts/cancellations.
-	 */
-	gettimeofday(&endtime, NULL);
-	endtime.tv_sec += RADIUS_TIMEOUT;
-
-	while (true)
-	{
-		struct timeval timeout;
-		struct timeval now;
-		int64		timeoutval;
-		const char *errstr = NULL;
-
-		gettimeofday(&now, NULL);
-		timeoutval = (endtime.tv_sec * 1000000 + endtime.tv_usec) - (now.tv_sec * 1000000 + now.tv_usec);
-		if (timeoutval <= 0)
-		{
-			ereport(LOG,
-					(errmsg("timeout waiting for RADIUS response from %s",
-							server)));
-			closesocket(sock);
-			return STATUS_ERROR;
-		}
-		timeout.tv_sec = timeoutval / 1000000;
-		timeout.tv_usec = timeoutval % 1000000;
-
-		FD_ZERO(&fdset);
-		FD_SET(sock, &fdset);
-
-		r = select(sock + 1, &fdset, NULL, NULL, &timeout);
-		if (r < 0)
-		{
-			if (errno == EINTR)
-				continue;
-
-			/* Anything else is an actual error */
-			ereport(LOG,
-					(errmsg("could not check status on RADIUS socket: %m")));
-			closesocket(sock);
-			return STATUS_ERROR;
-		}
-		if (r == 0)
-		{
-			ereport(LOG,
-					(errmsg("timeout waiting for RADIUS response from %s",
-							server)));
-			closesocket(sock);
-			return STATUS_ERROR;
-		}
-
-		/*
-		 * Attempt to read the response packet, and verify the contents.
-		 *
-		 * Any packet that's not actually a RADIUS packet, or otherwise does
-		 * not validate as an explicit reject, is just ignored and we retry
-		 * for another packet (until we reach the timeout). This is to avoid
-		 * the possibility to denial-of-service the login by flooding the
-		 * server with invalid packets on the port that we're expecting the
-		 * RADIUS response on.
-		 */
-
-		addrsize = sizeof(remoteaddr);
-		packetlength = recvfrom(sock, receive_buffer, RADIUS_BUFFER_SIZE, 0,
-								(struct sockaddr *) &remoteaddr, &addrsize);
-		if (packetlength < 0)
-		{
-			ereport(LOG,
-					(errmsg("could not read RADIUS response: %m")));
-			closesocket(sock);
-			return STATUS_ERROR;
-		}
-
-		if (remoteaddr.sin6_port != pg_hton16(port))
-		{
-			ereport(LOG,
-					(errmsg("RADIUS response from %s was sent from incorrect port: %d",
-							server, pg_ntoh16(remoteaddr.sin6_port))));
-			continue;
-		}
-
-		if (packetlength < RADIUS_HEADER_LENGTH)
-		{
-			ereport(LOG,
-					(errmsg("RADIUS response from %s too short: %d", server, packetlength)));
-			continue;
-		}
-
-		if (packetlength != pg_ntoh16(receivepacket->length))
-		{
-			ereport(LOG,
-					(errmsg("RADIUS response from %s has corrupt length: %d (actual length %d)",
-							server, pg_ntoh16(receivepacket->length), packetlength)));
-			continue;
-		}
-
-		if (packet->id != receivepacket->id)
-		{
-			ereport(LOG,
-					(errmsg("RADIUS response from %s is to a different request: %d (should be %d)",
-							server, receivepacket->id, packet->id)));
-			continue;
-		}
-
-		/*
-		 * Verify the response authenticator, which is calculated as
-		 * MD5(Code+ID+Length+RequestAuthenticator+Attributes+Secret)
-		 */
-		cryptvector = palloc(packetlength + strlen(secret));
-
-		memcpy(cryptvector, receivepacket, 4);	/* code+id+length */
-		memcpy(cryptvector + 4, packet->vector, RADIUS_VECTOR_LENGTH);	/* request
-																		 * authenticator, from
-																		 * original packet */
-		if (packetlength > RADIUS_HEADER_LENGTH)	/* there may be no
-													 * attributes at all */
-			memcpy(cryptvector + RADIUS_HEADER_LENGTH,
-				   (char *) receive_buffer + RADIUS_HEADER_LENGTH,
-				   packetlength - RADIUS_HEADER_LENGTH);
-		memcpy(cryptvector + packetlength, secret, strlen(secret));
-
-		if (!pg_md5_binary(cryptvector,
-						   packetlength + strlen(secret),
-						   encryptedpassword, &errstr))
-		{
-			ereport(LOG,
-					(errmsg("could not perform MD5 encryption of received packet: %s",
-							errstr)));
-			pfree(cryptvector);
-			continue;
-		}
-		pfree(cryptvector);
-
-		if (memcmp(receivepacket->vector, encryptedpassword, RADIUS_VECTOR_LENGTH) != 0)
-		{
-			ereport(LOG,
-					(errmsg("RADIUS response from %s has incorrect MD5 signature",
-							server)));
-			continue;
-		}
-
-		if (receivepacket->code == RADIUS_ACCESS_ACCEPT)
-		{
-			closesocket(sock);
-			return STATUS_OK;
-		}
-		else if (receivepacket->code == RADIUS_ACCESS_REJECT)
-		{
-			closesocket(sock);
-			return STATUS_EOF;
-		}
-		else
-		{
-			ereport(LOG,
-					(errmsg("RADIUS response from %s has invalid code (%d) for user \"%s\"",
-							server, receivepacket->code, user_name)));
-			continue;
-		}
-	}							/* while (true) */
-}
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index 87ee541e880..0afc9cddcea 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -114,7 +114,6 @@ static const char *const UserAuthName[] =
 	"bsd",
 	"ldap",
 	"cert",
-	"radius",
 	"peer",
 	"oauth",
 };
@@ -1744,8 +1743,6 @@ parse_hba_line(TokenizedAuthLine *tok_line, int elevel)
 #else
 		unsupauth = "cert";
 #endif
-	else if (strcmp(token->string, "radius") == 0)
-		parsedline->auth_method = uaRADIUS;
 	else if (strcmp(token->string, "oauth") == 0)
 		parsedline->auth_method = uaOAuth;
 	else
@@ -1947,87 +1944,6 @@ parse_hba_line(TokenizedAuthLine *tok_line, int elevel)
 		}
 	}
 
-	if (parsedline->auth_method == uaRADIUS)
-	{
-		MANDATORY_AUTH_ARG(parsedline->radiusservers, "radiusservers", "radius");
-		MANDATORY_AUTH_ARG(parsedline->radiussecrets, "radiussecrets", "radius");
-
-		if (parsedline->radiusservers == NIL)
-		{
-			ereport(elevel,
-					(errcode(ERRCODE_CONFIG_FILE_ERROR),
-					 errmsg("list of RADIUS servers cannot be empty"),
-					 errcontext("line %d of configuration file \"%s\"",
-								line_num, file_name)));
-			*err_msg = "list of RADIUS servers cannot be empty";
-			return NULL;
-		}
-
-		if (parsedline->radiussecrets == NIL)
-		{
-			ereport(elevel,
-					(errcode(ERRCODE_CONFIG_FILE_ERROR),
-					 errmsg("list of RADIUS secrets cannot be empty"),
-					 errcontext("line %d of configuration file \"%s\"",
-								line_num, file_name)));
-			*err_msg = "list of RADIUS secrets cannot be empty";
-			return NULL;
-		}
-
-		/*
-		 * Verify length of option lists - each can be 0 (except for secrets,
-		 * but that's already checked above), 1 (use the same value
-		 * everywhere) or the same as the number of servers.
-		 */
-		if (!(list_length(parsedline->radiussecrets) == 1 ||
-			  list_length(parsedline->radiussecrets) == list_length(parsedline->radiusservers)))
-		{
-			ereport(elevel,
-					(errcode(ERRCODE_CONFIG_FILE_ERROR),
-					 errmsg("the number of RADIUS secrets (%d) must be 1 or the same as the number of RADIUS servers (%d)",
-							list_length(parsedline->radiussecrets),
-							list_length(parsedline->radiusservers)),
-					 errcontext("line %d of configuration file \"%s\"",
-								line_num, file_name)));
-			*err_msg = psprintf("the number of RADIUS secrets (%d) must be 1 or the same as the number of RADIUS servers (%d)",
-								list_length(parsedline->radiussecrets),
-								list_length(parsedline->radiusservers));
-			return NULL;
-		}
-		if (!(list_length(parsedline->radiusports) == 0 ||
-			  list_length(parsedline->radiusports) == 1 ||
-			  list_length(parsedline->radiusports) == list_length(parsedline->radiusservers)))
-		{
-			ereport(elevel,
-					(errcode(ERRCODE_CONFIG_FILE_ERROR),
-					 errmsg("the number of RADIUS ports (%d) must be 1 or the same as the number of RADIUS servers (%d)",
-							list_length(parsedline->radiusports),
-							list_length(parsedline->radiusservers)),
-					 errcontext("line %d of configuration file \"%s\"",
-								line_num, file_name)));
-			*err_msg = psprintf("the number of RADIUS ports (%d) must be 1 or the same as the number of RADIUS servers (%d)",
-								list_length(parsedline->radiusports),
-								list_length(parsedline->radiusservers));
-			return NULL;
-		}
-		if (!(list_length(parsedline->radiusidentifiers) == 0 ||
-			  list_length(parsedline->radiusidentifiers) == 1 ||
-			  list_length(parsedline->radiusidentifiers) == list_length(parsedline->radiusservers)))
-		{
-			ereport(elevel,
-					(errcode(ERRCODE_CONFIG_FILE_ERROR),
-					 errmsg("the number of RADIUS identifiers (%d) must be 1 or the same as the number of RADIUS servers (%d)",
-							list_length(parsedline->radiusidentifiers),
-							list_length(parsedline->radiusservers)),
-					 errcontext("line %d of configuration file \"%s\"",
-								line_num, file_name)));
-			*err_msg = psprintf("the number of RADIUS identifiers (%d) must be 1 or the same as the number of RADIUS servers (%d)",
-								list_length(parsedline->radiusidentifiers),
-								list_length(parsedline->radiusservers));
-			return NULL;
-		}
-	}
-
 	/*
 	 * Enforce any parameters implied by other settings.
 	 */
@@ -2350,138 +2266,6 @@ parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline,
 		else
 			hbaline->upn_username = false;
 	}
-	else if (strcmp(name, "radiusservers") == 0)
-	{
-		struct addrinfo *gai_result;
-		struct addrinfo hints;
-		int			ret;
-		List	   *parsed_servers;
-		ListCell   *l;
-		char	   *dupval = pstrdup(val);
-
-		REQUIRE_AUTH_OPTION(uaRADIUS, "radiusservers", "radius");
-
-		if (!SplitGUCList(dupval, ',', &parsed_servers))
-		{
-			/* syntax error in list */
-			ereport(elevel,
-					(errcode(ERRCODE_CONFIG_FILE_ERROR),
-					 errmsg("could not parse RADIUS server list \"%s\"",
-							val),
-					 errcontext("line %d of configuration file \"%s\"",
-								line_num, file_name)));
-			return false;
-		}
-
-		/* For each entry in the list, translate it */
-		foreach(l, parsed_servers)
-		{
-			MemSet(&hints, 0, sizeof(hints));
-			hints.ai_socktype = SOCK_DGRAM;
-			hints.ai_family = AF_UNSPEC;
-
-			ret = pg_getaddrinfo_all((char *) lfirst(l), NULL, &hints, &gai_result);
-			if (ret || !gai_result)
-			{
-				ereport(elevel,
-						(errcode(ERRCODE_CONFIG_FILE_ERROR),
-						 errmsg("could not translate RADIUS server name \"%s\" to address: %s",
-								(char *) lfirst(l), gai_strerror(ret)),
-						 errcontext("line %d of configuration file \"%s\"",
-									line_num, file_name)));
-				if (gai_result)
-					pg_freeaddrinfo_all(hints.ai_family, gai_result);
-
-				list_free(parsed_servers);
-				return false;
-			}
-			pg_freeaddrinfo_all(hints.ai_family, gai_result);
-		}
-
-		/* All entries are OK, so store them */
-		hbaline->radiusservers = parsed_servers;
-		hbaline->radiusservers_s = pstrdup(val);
-	}
-	else if (strcmp(name, "radiusports") == 0)
-	{
-		List	   *parsed_ports;
-		ListCell   *l;
-		char	   *dupval = pstrdup(val);
-
-		REQUIRE_AUTH_OPTION(uaRADIUS, "radiusports", "radius");
-
-		if (!SplitGUCList(dupval, ',', &parsed_ports))
-		{
-			ereport(elevel,
-					(errcode(ERRCODE_CONFIG_FILE_ERROR),
-					 errmsg("could not parse RADIUS port list \"%s\"",
-							val),
-					 errcontext("line %d of configuration file \"%s\"",
-								line_num, file_name)));
-			*err_msg = psprintf("invalid RADIUS port number: \"%s\"", val);
-			return false;
-		}
-
-		foreach(l, parsed_ports)
-		{
-			if (atoi(lfirst(l)) == 0)
-			{
-				ereport(elevel,
-						(errcode(ERRCODE_CONFIG_FILE_ERROR),
-						 errmsg("invalid RADIUS port number: \"%s\"", val),
-						 errcontext("line %d of configuration file \"%s\"",
-									line_num, file_name)));
-
-				return false;
-			}
-		}
-		hbaline->radiusports = parsed_ports;
-		hbaline->radiusports_s = pstrdup(val);
-	}
-	else if (strcmp(name, "radiussecrets") == 0)
-	{
-		List	   *parsed_secrets;
-		char	   *dupval = pstrdup(val);
-
-		REQUIRE_AUTH_OPTION(uaRADIUS, "radiussecrets", "radius");
-
-		if (!SplitGUCList(dupval, ',', &parsed_secrets))
-		{
-			/* syntax error in list */
-			ereport(elevel,
-					(errcode(ERRCODE_CONFIG_FILE_ERROR),
-					 errmsg("could not parse RADIUS secret list \"%s\"",
-							val),
-					 errcontext("line %d of configuration file \"%s\"",
-								line_num, file_name)));
-			return false;
-		}
-
-		hbaline->radiussecrets = parsed_secrets;
-		hbaline->radiussecrets_s = pstrdup(val);
-	}
-	else if (strcmp(name, "radiusidentifiers") == 0)
-	{
-		List	   *parsed_identifiers;
-		char	   *dupval = pstrdup(val);
-
-		REQUIRE_AUTH_OPTION(uaRADIUS, "radiusidentifiers", "radius");
-
-		if (!SplitGUCList(dupval, ',', &parsed_identifiers))
-		{
-			/* syntax error in list */
-			ereport(elevel,
-					(errcode(ERRCODE_CONFIG_FILE_ERROR),
-					 errmsg("could not parse RADIUS identifiers list \"%s\"",
-							val),
-					 errcontext("line %d of configuration file \"%s\"",
-								line_num, file_name)));
-			return false;
-		}
-
-		hbaline->radiusidentifiers = parsed_identifiers;
-		hbaline->radiusidentifiers_s = pstrdup(val);
-	}
 	else if (strcmp(name, "issuer") == 0)
 	{
 		REQUIRE_AUTH_OPTION(uaOAuth, "issuer", "oauth");
diff --git a/src/backend/libpq/pg_hba.conf.sample b/src/backend/libpq/pg_hba.conf.sample
index b64c8dea97c..0dc360103e8 100644
--- a/src/backend/libpq/pg_hba.conf.sample
+++ b/src/backend/libpq/pg_hba.conf.sample
@@ -53,8 +53,8 @@
 # directly connected to.
 #
 # METHOD can be "trust", "reject", "md5", "password", "scram-sha-256",
-# "gss", "sspi", "ident", "peer", "pam", "oauth", "ldap", "radius" or
-# "cert".  Note that "password" sends passwords in clear text; "md5" or
+# "gss", "sspi", "ident", "peer", "pam", "oauth", "ldap" or "cert".
+# Note that "password" sends passwords in clear text; "md5" or
 # "scram-sha-256" are preferred since they send encrypted passwords.
 #
 # OPTIONS are a set of options for the authentication in the format
diff --git a/src/backend/utils/adt/hbafuncs.c b/src/backend/utils/adt/hbafuncs.c
index e7432c447e7..bf5cc601cb1 100644
--- a/src/backend/utils/adt/hbafuncs.c
+++ b/src/backend/utils/adt/hbafuncs.c
@@ -134,25 +134,6 @@ get_hba_options(HbaLine *hba)
 				CStringGetTextDatum(psprintf("ldapscope=%d", hba->ldapscope));
 	}
 
-	if (hba->auth_method == uaRADIUS)
-	{
-		if (hba->radiusservers_s)
-			options[noptions++] =
-				CStringGetTextDatum(psprintf("radiusservers=%s", hba->radiusservers_s));
-
-		if (hba->radiussecrets_s)
-			options[noptions++] =
-				CStringGetTextDatum(psprintf("radiussecrets=%s", hba->radiussecrets_s));
-
-		if (hba->radiusidentifiers_s)
-			options[noptions++] =
-				CStringGetTextDatum(psprintf("radiusidentifiers=%s", hba->radiusidentifiers_s));
-
-		if (hba->radiusports_s)
-			options[noptions++] =
-				CStringGetTextDatum(psprintf("radiusports=%s", hba->radiusports_s));
-	}
-
 	if (hba->auth_method == uaOAuth)
 	{
 		if (hba->oauth_issuer)
diff --git a/src/include/libpq/hba.h b/src/include/libpq/hba.h
index 7b93ba4a709..dffffe2033c 100644
--- a/src/include/libpq/hba.h
+++ b/src/include/libpq/hba.h
@@ -37,7 +37,6 @@ typedef enum UserAuth
 	uaBSD,
 	uaLDAP,
 	uaCert,
-	uaRADIUS,
 	uaPeer,
 	uaOAuth,
 #define USER_AUTH_LAST uaOAuth	/* Must be last value of this enum */
@@ -128,14 +127,6 @@ typedef struct HbaLine
 	bool		include_realm;
 	bool		compat_realm;
 	bool		upn_username;
-	List	   *radiusservers;
-	char	   *radiusservers_s;
-	List	   *radiussecrets;
-	char	   *radiussecrets_s;
-	List	   *radiusidentifiers;
-	char	   *radiusidentifiers_s;
-	List	   *radiusports;
-	char	   *radiusports_s;
 	char	   *oauth_issuer;
 	char	   *oauth_scope;
 	char	   *oauth_validator;
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 1c8610fd46c..4bc8a8d6fad 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -4053,8 +4053,6 @@ qc_hash_func
 qsort_arg_comparator
 qsort_comparator
 query_pathkeys_callback
-radius_attribute
-radius_packet
 rangeTableEntry_used_context
 rank_context
 rbt_allocfunc
-- 
2.52.0

