Add radiustimeout parameter for RADIUS HBA

Started by Samuel D. Leslieabout 9 years ago2 messages
#1Samuel D. Leslie
SDL@nexiom.net
1 attachment(s)

Hello everyone,

I’d like to submit the attached patch for feedback from the PostgreSQL community and potential future inclusion in the codebase. The patch adds a new parameter to the RADIUS authentication method named “radiustimeout”, allowing the database administrator to configure the timeout in seconds to wait for responses from a configured RADIUS server. Until now, this has been hardcoded to three seconds by the RADIUS_TIMEOUT define in auth.c. While this is usually sufficient for typical RADIUS server configurations, there are some more unusual configurations where a higher timeout is required. Examples include:
- Authenticating against a RADIUS server over a high latency link
- Authenticating against a RADIUS server that is performing additional out-of-band authentication

The latter case is applicable to a server I admin and spurred the development of this patch. We implemented multi-factor authentication for user access to a sensitive database via a RADIUS server implementation which performs the standard username & password verification, and if it succeeds, subsequently performs a second factor of authentication via a configured mobile app. The RADIUS response confirming successful authentication is only returned after both authentication factors have completed. In our deployment, a timeout of 60 seconds seems to work well, but certainly three seconds is not at all workable.

Thanks in advance for any and all feedback.

Kind regards,
-SDL

Attachments:

radiustimeout.patchapplication/octet-stream; name=radiustimeout.patchDownload
diff --git i/doc/src/sgml/client-auth.sgml w/doc/src/sgml/client-auth.sgml
index 960f5b5..7055d13 100644
--- i/doc/src/sgml/client-auth.sgml
+++ w/doc/src/sgml/client-auth.sgml
@@ -1612,6 +1612,17 @@ host ... ldap ldapurl="ldap://ldap.example.net/dc=example,dc=net?uid?sub"
        </listitem>
       </varlistentry>
 
+      <varlistentry>
+       <term><literal>radiustimeout</literal></term>
+       <listitem>
+        <para>
+         The timeout to wait for a response from the RADIUS server in seconds.
+         If no value is specified, a default timeout of <literal>3</> seconds
+         will be used.
+        </para>
+       </listitem>
+      </varlistentry>
+
       <varlistentry>
        <term><literal>radiusidentifier</literal></term>
        <listitem>
diff --git i/src/backend/libpq/auth.c w/src/backend/libpq/auth.c
index 0ba8530..8a4e9ab 100644
--- i/src/backend/libpq/auth.c
+++ w/src/backend/libpq/auth.c
@@ -2379,9 +2379,6 @@ typedef struct
 /* Maximum size of a RADIUS packet we will create or accept */
 #define RADIUS_BUFFER_SIZE 1024
 
-/* 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)
 {
@@ -2463,6 +2460,9 @@ CheckRADIUSAuth(Port *port)
 	if (port->hba->radiusport == 0)
 		port->hba->radiusport = 1812;
 
+	if (port->hba->radiustimeout == 0)
+		port->hba->radiustimeout = 3;
+
 	MemSet(&hint, 0, sizeof(hint));
 	hint.ai_socktype = SOCK_DGRAM;
 	hint.ai_family = AF_UNSPEC;
@@ -2626,7 +2626,7 @@ CheckRADIUSAuth(Port *port)
 	 * timeouts/cancellations.
 	 */
 	gettimeofday(&endtime, NULL);
-	endtime.tv_sec += RADIUS_TIMEOUT;
+	endtime.tv_sec += port->hba->radiustimeout;
 
 	while (true)
 	{
diff --git i/src/backend/libpq/hba.c w/src/backend/libpq/hba.c
index f1e9a38..7ef2888 100644
--- i/src/backend/libpq/hba.c
+++ w/src/backend/libpq/hba.c
@@ -1665,6 +1665,20 @@ parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num)
 		REQUIRE_AUTH_OPTION(uaRADIUS, "radiusidentifier", "radius");
 		hbaline->radiusidentifier = pstrdup(val);
 	}
+	else if (strcmp(name, "radiustimeout") == 0)
+	{
+		REQUIRE_AUTH_OPTION(uaRADIUS, "radiustimeout", "radius");
+		hbaline->radiustimeout = atoi(val);
+		if (hbaline->radiustimeout == 0)
+		{
+			ereport(LOG,
+					(errcode(ERRCODE_CONFIG_FILE_ERROR),
+					 errmsg("invalid RADIUS timeout value: \"%s\"", val),
+					 errcontext("line %d of configuration file \"%s\"",
+								line_num, HbaFileName)));
+			return false;
+		}
+	}
 	else
 	{
 		ereport(LOG,
diff --git i/src/include/libpq/hba.h w/src/include/libpq/hba.h
index dc7d257..e206fe9 100644
--- i/src/include/libpq/hba.h
+++ w/src/include/libpq/hba.h
@@ -85,6 +85,7 @@ typedef struct HbaLine
 	char	   *radiussecret;
 	char	   *radiusidentifier;
 	int			radiusport;
+	int			radiustimeout;
 } HbaLine;
 
 typedef struct IdentLine
#2Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Samuel D. Leslie (#1)
Re: Add radiustimeout parameter for RADIUS HBA

On Mon, Oct 24, 2016 at 2:03 PM, Samuel D. Leslie <SDL@nexiom.net> wrote:

Hello everyone,

I’d like to submit the attached patch for feedback from the PostgreSQL
community and potential future inclusion in the codebase. The patch adds a
new parameter to the RADIUS authentication method named “radiustimeout”,
allowing the database administrator to configure the timeout in seconds to
wait for responses from a configured RADIUS server. Until now, this has
been hardcoded to three seconds by the RADIUS_TIMEOUT define in auth.c.
While this is usually sufficient for typical RADIUS server configurations,
there are some more unusual configurations where a higher timeout is
required. Examples include:
- Authenticating against a RADIUS server over a high latency link
- Authenticating against a RADIUS server that is performing additional
out-of-band authentication

The latter case is applicable to a server I admin and spurred the
development of this patch. We implemented multi-factor authentication for
user access to a sensitive database via a RADIUS server implementation
which performs the standard username & password verification, and if it
succeeds, subsequently performs a second factor of authentication via a
configured mobile app. The RADIUS response confirming successful
authentication is only returned after both authentication factors have
completed. In our deployment, a timeout of 60 seconds seems to work well,
but certainly three seconds is not at all workable.

Thanks in advance for any and all feedback.

I reviewed and tested the patch. It works as expected.
Following are my observations during the test.

1. In case if the radiustimeout is more than authentication_timeout the
client connection is stopped only when the radiustimeout is occurred.

Do we need add the CHECK_FOR_INTERRUPTS() call or add this
behavior information in the docs?

2. When the Postgresql Backend is waiting for a response from
Radius server, in case if the client disconnects, still backend waits
for the response from RADIUS server and then it closes.

I feel the second case is rare, may not be a problem.

Regards,
Hari Babu
Fujitsu Australia