Time to drop RADIUS support?
Hi,
A bit over a year ago, I wrote about a RADIUS vulnerability and a
recommended mitigation[1]/messages/by-id/CA+hUKGLRSPTOC_ygx4_sJjWeKOkOpWGCBCJiRq8cPNuMisuzgw@mail.gmail.com. I was grateful for the reviews, but I lost
steam on those patches because:
1. Our implementation seems to have accidental (?) resilience because
it has a short hard-coded timeout. The RADIUS/UDP Considered
Harmful[0]https://www.usenix.org/conference/usenixsecurity24/presentation/goldberg people used 47 servers to get "2% of the successful runs to
finish before 240s and 16% before 300s", but we time out after 3
seconds. Assuming perfect scaling, maybe they could use 4700 servers
to get a 16% chance of success in 3s... or maybe I have the maths
wrong but it's fairly extreme anyway...
2. It seems increasingly likely that there are no users, since
RADIUS/UDP without the mitigation spews warnings from FreeRADIUS, and
Microsoft RADIUS's 2024 update (KB5040456) recommended requiring it
(though you didn't have to accept IIUC). I'm pretty sure we'd have
heard about it from users if there were any.
3. That mitigation would help, but in the end it's still leaky
obfuscation of credentials + MD5-based technology that is being
formally deprecated with a mandated replacement[2]https://datatracker.ietf.org/doc/draft-ietf-radext-deprecating-radius/, and de facto has
been for a long time.
The real recommendation of the paper was "don't use RADIUS/UDP at
all", and I don't want to expend energy writing a RADIUS/TLS client
for a hypothetical user, so I think we should just delete it all, and
stick a deprecation notice in the release branch documentation, as
attached. That'd also mean our Windows select() and non-thread-safe
UDP kludges can be VACUUMed.
AFAICS you can already do RADIUS better with PAM using a module
maintained by the FreeRADIUS project (see below for quick and dirty
demo). That way it's not our problem, follows the standards etc. The
only issue I can think of with that is that Windows and OpenBSD
probably don't have PAM. But then, recall that we are talking about
approximately zero users so I think we can still hit 100% of them this
way?
=== Example of RADIUS via PAM ===
Tell PAM how to authenticate for service postgresql in /etc/pam.d/postgresql:
#%PAM-1.0
auth required pam_radius.so require_message_authenticator
account required pam_permit.so
Tell pam_radius.so how to talk to RADIUS server in /etc/radius.conf:
# Server[:port] SharedSecret Timeout Retries
127.0.0.1 shared_secret 3 3
Tell PostgreSQL to use PAM service postgresql in pg_hba.conf:
host all all 127.0.0.1/32 pam pamservice=postgresql
=== Setting up a test server to try it out ===
Tell FreeRADIUS how to be a RADIUS server in /tmp/radiusd/radiusd.conf:
/tmp/radiusd/radiusd.conf
client default {
ipaddr = "127.0.0.1"
secret = "shared_secret"
}
modules {
files {
filename = "/tmp/radiusd/users.txt"
}
pap {
}
}
server default {
listen {
type = "auth"
ipv4addr = "127.0.0.1"
port = "1812"
}
authenticate {
Auth-Type PAP {
pap
}
}
authorize {
files
pap
}
}
log {
destination = "files"
localstatedir = "/tmp/radiusd"
logdir = "/tmp/radiusd"
file = "/tmp/radiusd/radiusd.log"
}
pidfile = "/tmp/radiusd/radiusd.pid"
Tell FreeRADIUS the passwords in /tmp/radiusd/users.txt:
testuser Cleartext-Password := "xxx"
Then run it in the foreground with "radiusd -d /tmp/radiusd -f". If
you leave out "require_message_authenticator" from
/etc/pam.d/postgresql then you'll get log messages just like when
PostgreSQL speaks RADIUS natively:
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
BlastRADIUS check: Received packet without Message-Authenticator.
Setting "require_message_authenticator = false" for client default
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
UPGRADE THE CLIENT AS YOUR NETWORK IS VULNERABLE TO THE BLASTRADIUS ATTACK.
Once the client is upgraded, set "require_message_authenticator =
true" for client default
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Both client and server should ideally be set to require
Message-Authenticator. Presumably with more coffee and man pages you
could also configure it to use RADIUS/TLS instead of RADIUS/UDP, etc.
[0]: https://www.usenix.org/conference/usenixsecurity24/presentation/goldberg
[1]: /messages/by-id/CA+hUKGLRSPTOC_ygx4_sJjWeKOkOpWGCBCJiRq8cPNuMisuzgw@mail.gmail.com
[2]: https://datatracker.ietf.org/doc/draft-ietf-radext-deprecating-radius/
Attachments:
0001-Doc-Mark-RADIUS-deprecated.patchapplication/x-patch; name=0001-Doc-Mark-RADIUS-deprecated.patchDownload
From d341857e92f65eb1911721b0056428633233b275 Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Fri, 23 Jan 2026 14:23:53 +1300
Subject: [PATCH 1/3] Doc: Mark RADIUS deprecated.
RADIUS support will be dropped in master. It was never updated for
RADIUS/TLS or the MessageAuthenticator attribute that mitigates against
the Blast-RADIUS vulnerability, though PostgreSQL is thought to be
accidentally resistant because of its short timeout. The authors
recommend avoiding RADIUS/UDP completely, and a draft RFC[1] is in the
process of formalizing that.
[1] https://datatracker.ietf.org/doc/draft-ietf-radext-deprecating-radius/
Backpatch-through: 14
Reviewed-by:
Discussion:
---
doc/src/sgml/client-auth.sgml | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml
index a347ee18980..00cf882a73f 100644
--- a/doc/src/sgml/client-auth.sgml
+++ b/doc/src/sgml/client-auth.sgml
@@ -2110,6 +2110,15 @@ host ... ldap ldapbasedn="dc=example,dc=net"
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>
--
2.52.0
0002-Drop-RADIUS-support.patchapplication/x-patch; name=0002-Drop-RADIUS-support.patchDownload
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
0003-Remove-obsolete-Windows-select-and-UDP-support.patchapplication/x-patch; name=0003-Remove-obsolete-Windows-select-and-UDP-support.patchDownload
From f7f4d0af1446043fc781c9cf8ef7d645c20e2832 Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Fri, 23 Jan 2026 14:58:15 +1300
Subject: [PATCH 3/3] Remove obsolete Windows select() and UDP support.
Since the removal of RADIUS, there are no remaining users of select() in
the backend. All new code, be it in the tree or in extensions, should
be using the WaitEventSet API. Non-thread-safe code relating to UDP
sockets is also removed; it was relevant to the pgstat process (long
gone) and perhaps RADIUS.
With a bit of higher-level work to eradicate blocking sockets completely
from the backend, the non-thread-safe blocking code in the remaining
wrappers could also be deleted, but that isn't done here.
Reviewed-by:
Discussion:
---
src/backend/port/win32/socket.c | 270 +-------------------------------
src/include/port/win32_port.h | 2 -
2 files changed, 1 insertion(+), 271 deletions(-)
diff --git a/src/backend/port/win32/socket.c b/src/backend/port/win32/socket.c
index 3aaf971e973..3fbdf591b19 100644
--- a/src/backend/port/win32/socket.c
+++ b/src/backend/port/win32/socket.c
@@ -33,7 +33,6 @@ int pgwin32_noblock = 0;
#undef listen
#undef accept
#undef connect
-#undef select
#undef recv
#undef send
@@ -165,24 +164,10 @@ pgwin32_poll_signals(void)
return 0;
}
-static int
-isDataGram(SOCKET s)
-{
- int type;
- int typelen = sizeof(type);
-
- if (getsockopt(s, SOL_SOCKET, SO_TYPE, (char *) &type, &typelen))
- return 1;
-
- return (type == SOCK_DGRAM) ? 1 : 0;
-}
-
int
pgwin32_waitforsinglesocket(SOCKET s, int what, int timeout)
{
static HANDLE waitevent = INVALID_HANDLE_VALUE;
- static SOCKET current_socket = INVALID_SOCKET;
- static int isUDP = 0;
HANDLE events[2];
int r;
@@ -199,15 +184,6 @@ pgwin32_waitforsinglesocket(SOCKET s, int what, int timeout)
ereport(ERROR,
(errmsg_internal("could not reset socket waiting event: error code %lu", GetLastError())));
- /*
- * Track whether socket is UDP or not. (NB: most likely, this is both
- * useless and wrong; there is no reason to think that the behavior of
- * WSAEventSelect is different for TCP and UDP.)
- */
- if (current_socket != s)
- isUDP = isDataGram(s);
- current_socket = s;
-
/*
* Attach event to socket. NOTE: we must detach it again before
* returning, since other bits of code may try to attach other events to
@@ -222,47 +198,7 @@ pgwin32_waitforsinglesocket(SOCKET s, int what, int timeout)
events[0] = pgwin32_signal_event;
events[1] = waitevent;
- /*
- * Just a workaround of unknown locking problem with writing in UDP socket
- * under high load: Client's pgsql backend sleeps infinitely in
- * WaitForMultipleObjectsEx, pgstat process sleeps in pgwin32_select().
- * So, we will wait with small timeout(0.1 sec) and if socket is still
- * blocked, try WSASend (see comments in pgwin32_select) and wait again.
- */
- if ((what & FD_WRITE) && isUDP)
- {
- for (;;)
- {
- r = WaitForMultipleObjectsEx(2, events, FALSE, 100, TRUE);
-
- if (r == WAIT_TIMEOUT)
- {
- char c;
- WSABUF buf;
- DWORD sent;
-
- buf.buf = &c;
- buf.len = 0;
-
- r = WSASend(s, &buf, 1, &sent, 0, NULL, NULL);
- if (r == 0) /* Completed - means things are fine! */
- {
- WSAEventSelect(s, NULL, 0);
- return 1;
- }
- else if (WSAGetLastError() != WSAEWOULDBLOCK)
- {
- TranslateSocketError();
- WSAEventSelect(s, NULL, 0);
- return 0;
- }
- }
- else
- break;
- }
- }
- else
- r = WaitForMultipleObjectsEx(2, events, FALSE, timeout, TRUE);
+ r = WaitForMultipleObjectsEx(2, events, FALSE, timeout, TRUE);
WSAEventSelect(s, NULL, 0);
@@ -468,10 +404,6 @@ pgwin32_send(SOCKET s, const void *buf, int len, int flags)
wbuf.len = len;
wbuf.buf = (char *) buf;
- /*
- * Readiness of socket to send data to UDP socket may be not true: socket
- * can become busy again! So loop until send or error occurs.
- */
for (;;)
{
r = WSASend(s, &wbuf, 1, &b, flags, NULL, NULL);
@@ -504,203 +436,3 @@ pgwin32_send(SOCKET s, const void *buf, int len, int flags)
return -1;
}
-
-
-/*
- * Wait for activity on one or more sockets.
- * While waiting, allow signals to run
- *
- * NOTE! Currently does not implement exceptfds check,
- * since it is not used in postgresql!
- */
-int
-pgwin32_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timeval *timeout)
-{
- WSAEVENT events[FD_SETSIZE * 2]; /* worst case is readfds totally
- * different from writefds, so
- * 2*FD_SETSIZE sockets */
- SOCKET sockets[FD_SETSIZE * 2];
- int numevents = 0;
- int i;
- int r;
- DWORD timeoutval = WSA_INFINITE;
- FD_SET outreadfds;
- FD_SET outwritefds;
- int nummatches = 0;
-
- Assert(exceptfds == NULL);
-
- if (pgwin32_poll_signals())
- return -1;
-
- FD_ZERO(&outreadfds);
- FD_ZERO(&outwritefds);
-
- /*
- * Windows does not guarantee to log an FD_WRITE network event indicating
- * that more data can be sent unless the previous send() failed with
- * WSAEWOULDBLOCK. While our caller might well have made such a call, we
- * cannot assume that here. Therefore, if waiting for write-ready, force
- * the issue by doing a dummy send(). If the dummy send() succeeds,
- * assume that the socket is in fact write-ready, and return immediately.
- * Also, if it fails with something other than WSAEWOULDBLOCK, return a
- * write-ready indication to let our caller deal with the error condition.
- */
- if (writefds != NULL)
- {
- for (i = 0; i < writefds->fd_count; i++)
- {
- char c;
- WSABUF buf;
- DWORD sent;
-
- buf.buf = &c;
- buf.len = 0;
-
- r = WSASend(writefds->fd_array[i], &buf, 1, &sent, 0, NULL, NULL);
- if (r == 0 || WSAGetLastError() != WSAEWOULDBLOCK)
- FD_SET(writefds->fd_array[i], &outwritefds);
- }
-
- /* If we found any write-ready sockets, just return them immediately */
- if (outwritefds.fd_count > 0)
- {
- memcpy(writefds, &outwritefds, sizeof(fd_set));
- if (readfds)
- FD_ZERO(readfds);
- return outwritefds.fd_count;
- }
- }
-
-
- /* Now set up for an actual select */
-
- if (timeout != NULL)
- {
- /* timeoutval is in milliseconds */
- timeoutval = timeout->tv_sec * 1000 + timeout->tv_usec / 1000;
- }
-
- if (readfds != NULL)
- {
- for (i = 0; i < readfds->fd_count; i++)
- {
- events[numevents] = WSACreateEvent();
- sockets[numevents] = readfds->fd_array[i];
- numevents++;
- }
- }
- if (writefds != NULL)
- {
- for (i = 0; i < writefds->fd_count; i++)
- {
- if (!readfds ||
- !FD_ISSET(writefds->fd_array[i], readfds))
- {
- /* If the socket is not in the read list */
- events[numevents] = WSACreateEvent();
- sockets[numevents] = writefds->fd_array[i];
- numevents++;
- }
- }
- }
-
- for (i = 0; i < numevents; i++)
- {
- int flags = 0;
-
- if (readfds && FD_ISSET(sockets[i], readfds))
- flags |= FD_READ | FD_ACCEPT | FD_CLOSE;
-
- if (writefds && FD_ISSET(sockets[i], writefds))
- flags |= FD_WRITE | FD_CLOSE;
-
- if (WSAEventSelect(sockets[i], events[i], flags) != 0)
- {
- TranslateSocketError();
- /* release already-assigned event objects */
- while (--i >= 0)
- WSAEventSelect(sockets[i], NULL, 0);
- for (i = 0; i < numevents; i++)
- WSACloseEvent(events[i]);
- return -1;
- }
- }
-
- events[numevents] = pgwin32_signal_event;
- r = WaitForMultipleObjectsEx(numevents + 1, events, FALSE, timeoutval, TRUE);
- if (r != WAIT_TIMEOUT && r != WAIT_IO_COMPLETION && r != (WAIT_OBJECT_0 + numevents))
- {
- /*
- * We scan all events, even those not signaled, in case more than one
- * event has been tagged but Wait.. can only return one.
- */
- WSANETWORKEVENTS resEvents;
-
- for (i = 0; i < numevents; i++)
- {
- ZeroMemory(&resEvents, sizeof(resEvents));
- if (WSAEnumNetworkEvents(sockets[i], events[i], &resEvents) != 0)
- elog(ERROR, "failed to enumerate network events: error code %d",
- WSAGetLastError());
- /* Read activity? */
- if (readfds && FD_ISSET(sockets[i], readfds))
- {
- if ((resEvents.lNetworkEvents & FD_READ) ||
- (resEvents.lNetworkEvents & FD_ACCEPT) ||
- (resEvents.lNetworkEvents & FD_CLOSE))
- {
- FD_SET(sockets[i], &outreadfds);
-
- nummatches++;
- }
- }
- /* Write activity? */
- if (writefds && FD_ISSET(sockets[i], writefds))
- {
- if ((resEvents.lNetworkEvents & FD_WRITE) ||
- (resEvents.lNetworkEvents & FD_CLOSE))
- {
- FD_SET(sockets[i], &outwritefds);
-
- nummatches++;
- }
- }
- }
- }
-
- /* Clean up all the event objects */
- for (i = 0; i < numevents; i++)
- {
- WSAEventSelect(sockets[i], NULL, 0);
- WSACloseEvent(events[i]);
- }
-
- if (r == WSA_WAIT_TIMEOUT)
- {
- if (readfds)
- FD_ZERO(readfds);
- if (writefds)
- FD_ZERO(writefds);
- return 0;
- }
-
- /* Signal-like events. */
- if (r == WAIT_OBJECT_0 + numevents || r == WAIT_IO_COMPLETION)
- {
- pgwin32_dispatch_queued_signals();
- errno = EINTR;
- if (readfds)
- FD_ZERO(readfds);
- if (writefds)
- FD_ZERO(writefds);
- return -1;
- }
-
- /* Overwrite socket sets with our resulting values */
- if (readfds)
- memcpy(readfds, &outreadfds, sizeof(fd_set));
- if (writefds)
- memcpy(writefds, &outwritefds, sizeof(fd_set));
- return nummatches;
-}
diff --git a/src/include/port/win32_port.h b/src/include/port/win32_port.h
index 956c0b4b4c3..9feaf0249cb 100644
--- a/src/include/port/win32_port.h
+++ b/src/include/port/win32_port.h
@@ -497,7 +497,6 @@ extern int pgkill(int pid, int sig);
#define listen(s, backlog) pgwin32_listen(s, backlog)
#define accept(s, addr, addrlen) pgwin32_accept(s, addr, addrlen)
#define connect(s, name, namelen) pgwin32_connect(s, name, namelen)
-#define select(n, r, w, e, timeout) pgwin32_select(n, r, w, e, timeout)
#define recv(s, buf, len, flags) pgwin32_recv(s, buf, len, flags)
#define send(s, buf, len, flags) pgwin32_send(s, buf, len, flags)
@@ -506,7 +505,6 @@ extern int pgwin32_bind(SOCKET s, struct sockaddr *addr, int addrlen);
extern int pgwin32_listen(SOCKET s, int backlog);
extern SOCKET pgwin32_accept(SOCKET s, struct sockaddr *addr, int *addrlen);
extern int pgwin32_connect(SOCKET s, const struct sockaddr *addr, int addrlen);
-extern int pgwin32_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timeval *timeout);
extern int pgwin32_recv(SOCKET s, char *buf, int len, int flags);
extern int pgwin32_send(SOCKET s, const void *buf, int len, int flags);
extern int pgwin32_waitforsinglesocket(SOCKET s, int what, int timeout);
--
2.52.0
Hi,
On Fri, Jan 23, 2026 at 11:22:45PM +1300, Thomas Munro wrote:
The real recommendation of the paper was "don't use RADIUS/UDP at
all", and I don't want to expend energy writing a RADIUS/TLS client
for a hypothetical user, so I think we should just delete it all, and
stick a deprecation notice in the release branch documentation, as
attached.
So you are saying we add a deprecation notice in the back branches and
drop it in V19? If this is a severe security issue then maybe we can
just remove it everywhere (ugh), or if not, I think it probably warrants
at least one release cycle of deprecation. Do we have a formal
deprecation timeline policy nowadays?
Michael
=?utf-8?Q?=C3=81lvaro?= Herrera <alvherre@kurilemu.de> writes:
Would it work to add a WARNING (or something) to all back branches to
ask users to write here, so that we can confirm in the next few months
whether the protocol is completely unused or not? If we do find users,
then we could try to think of workarounds[*], but otherwise we'd just
remove it for pg19 (or pg20 at the latest) and not waste any more time
on it.
I don't think that'd prove a lot. Affected users (if any) wouldn't
necessarily be quick to adopt the latest minor releases. They're
probably not even up-to-date on their RADIUS server, or they'd have
noticed it spewing complaints.
I don't think removing it entirely from all back branches is a good
idea, without first making sure that there are no users.
Agreed, we can't pull it from the back branches. But I'm in favor of
pulling it from HEAD if we document how to use PAM-based RADIUS
instead. I agree with Thomas' argument that the cost-benefit ratio
of fixing our implementation would be poor.
regards, tom lane
Import Notes
Reply to msg id not found: 202601231423.4522ubhwkcwj@alvherre.pgsqlReference msg id not found: 202601231423.4522ubhwkcwj@alvherre.pgsql
On Fri, Jan 23, 2026 at 8:30 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
I don't think removing it entirely from all back branches is a good
idea, without first making sure that there are no users.Agreed, we can't pull it from the back branches. But I'm in favor of
pulling it from HEAD if we document how to use PAM-based RADIUS
instead. I agree with Thomas' argument that the cost-benefit ratio
of fixing our implementation would be poor.
+1.
I still think a WARNING in the back branches would be a kindness, to
let people know that they need to move.
--Jacob