<literal>Access Reject</>. 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, put the names within quotes and separate the server names
+ with a comma. If multiple servers are specified, all other RADIUS
+ options can also be given as a comma separate list, to apply
+ individual values to each server. They can also be specified as
+ a single value, in which case this value will apply to all servers.
+ </para>
+
<para>
The following configuration options are supported for RADIUS:
<variablelist>
<varlistentry>
- <term><literal>radiusserver</literal></term>
+ <term><literal>radiusservers</literal></term>
<listitem>
<para>
- The name or IP address of the RADIUS server to connect to.
+ The name or IP addresses of the RADIUS servers to connect to.
This parameter is required.
</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><literal>radiussecret</literal></term>
+ <term><literal>radiussecrets</literal></term>
<listitem>
<para>
- The shared secret used when talking securely to the RADIUS
+ The shared secrets used when talking securely to the RADIUS
server. 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.
</varlistentry>
<varlistentry>
- <term><literal>radiusport</literal></term>
+ <term><literal>radiusports</literal></term>
<listitem>
<para>
- The port number on the RADIUS server to connect to. If no port
+ The port number on the RADIUS servers to connect to. If no port
is specified, the default port <literal>1812</> will be used.
</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><literal>radiusidentifier</literal></term>
+ <term><literal>radiusidentifiers</literal></term>
<listitem>
<para>
The string used as <literal>NAS Identifier</> in the RADIUS
*----------------------------------------------------------------
*/
static int CheckRADIUSAuth(Port *port);
+static int PerformRadiusTransaction(char *server, char *secret, char *portstr, char *identifier, char *user_name, char *passwd);
/*----------------------------------------------------------------
CheckRADIUSAuth(Port *port)
{
char *passwd;
- char *identifier = "postgresql";
+ ListCell *server,
+ *secrets,
+ *radiusports,
+ *identifiers;
+
+ /* Make sure struct alignment is correct */
+ Assert(offsetof(radius_packet, vector) == 4);
+
+ /* Verify parameters */
+ if (list_length(port->hba->radiusservers) < 1)
+ {
+ ereport(LOG,
+ (errmsg("RADIUS server not specified")));
+ return STATUS_ERROR;
+ }
+
+ if (list_length(port->hba->radiussecrets) < 1)
+ {
+ 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) == 0)
+ {
+ ereport(LOG,
+ (errmsg("empty password returned by client")));
+ return STATUS_ERROR;
+ }
+
+ if (strlen(passwd) > RADIUS_MAX_PASSWORD_LENGTH)
+ {
+ ereport(LOG,
+ (errmsg("RADIUS authentication does not support passwords longer than %d characters", RADIUS_MAX_PASSWORD_LENGTH)));
+ 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)
+ return STATUS_OK;
+ else if (ret == STATUS_EOF)
+ 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(secrets);
+ if (list_length(port->hba->radiusports) > 1)
+ radiusports = lnext(radiusports);
+ if (list_length(port->hba->radiusidentifiers) > 1)
+ identifiers = lnext(identifiers);
+ }
+
+ /* No servers left to try, so give up */
+ return STATUS_ERROR;
+}
+
+static int
+PerformRadiusTransaction(char *server, char *secret, char *portstr, char *identifier, char *user_name, char *passwd)
+{
char radius_buffer[RADIUS_BUFFER_SIZE];
char receive_buffer[RADIUS_BUFFER_SIZE];
radius_packet *packet = (radius_packet *) radius_buffer;
#endif
struct addrinfo hint;
struct addrinfo *serveraddrs;
- char portstr[128];
+ int port;
ACCEPT_TYPE_ARG3 addrsize;
fd_set fdset;
struct timeval endtime;
j,
r;
- /* Make sure struct alignment is correct */
- Assert(offsetof(radius_packet, vector) == 4);
-
- /* Verify parameters */
- if (!port->hba->radiusserver || port->hba->radiusserver[0] == '\0')
- {
- ereport(LOG,
- (errmsg("RADIUS server not specified")));
- return STATUS_ERROR;
- }
-
- if (!port->hba->radiussecret || port->hba->radiussecret[0] == '\0')
- {
- ereport(LOG,
- (errmsg("RADIUS secret not specified")));
- return STATUS_ERROR;
- }
-
- if (port->hba->radiusport == 0)
- port->hba->radiusport = 1812;
+ /* 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;
- snprintf(portstr, sizeof(portstr), "%d", port->hba->radiusport);
+ port = atoi(portstr);
- r = pg_getaddrinfo_all(port->hba->radiusserver, portstr, &hint, &serveraddrs);
+ r = pg_getaddrinfo_all(server, portstr, &hint, &serveraddrs);
if (r || !serveraddrs)
{
ereport(LOG,
(errmsg("could not translate RADIUS server name \"%s\" to address: %s",
- port->hba->radiusserver, gai_strerror(r))));
+ server, gai_strerror(r))));
if (serveraddrs)
pg_freeaddrinfo_all(hint.ai_family, serveraddrs);
return STATUS_ERROR;
}
/* XXX: add support for multiple returned addresses? */
- if (port->hba->radiusidentifier && port->hba->radiusidentifier[0])
- identifier = port->hba->radiusidentifier;
-
- /* 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) == 0)
- {
- ereport(LOG,
- (errmsg("empty password returned by client")));
- return STATUS_ERROR;
- }
-
- if (strlen(passwd) > RADIUS_MAX_PASSWORD_LENGTH)
- {
- ereport(LOG,
- (errmsg("RADIUS authentication does not support passwords longer than %d characters", RADIUS_MAX_PASSWORD_LENGTH)));
- return STATUS_ERROR;
- }
-
-
/* Construct RADIUS packet */
packet->code = RADIUS_ACCESS_REQUEST;
packet->length = RADIUS_HEADER_LENGTH;
}
packet->id = packet->vector[0];
radius_add_attribute(packet, RADIUS_SERVICE_TYPE, (unsigned char *) &service, sizeof(service));
- radius_add_attribute(packet, RADIUS_USER_NAME, (unsigned char *) port->user_name, strlen(port->user_name));
+ radius_add_attribute(packet, RADIUS_USER_NAME, (unsigned char *) user_name, strlen(user_name));
radius_add_attribute(packet, RADIUS_NAS_IDENTIFIER, (unsigned char *) identifier, strlen(identifier));
/*
* (if necessary)
*/
encryptedpasswordlen = ((strlen(passwd) + RADIUS_VECTOR_LENGTH - 1) / RADIUS_VECTOR_LENGTH) * RADIUS_VECTOR_LENGTH;
- cryptvector = palloc(strlen(port->hba->radiussecret) + RADIUS_VECTOR_LENGTH);
- memcpy(cryptvector, port->hba->radiussecret, strlen(port->hba->radiussecret));
+ 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)
{
- memcpy(cryptvector + strlen(port->hba->radiussecret), md5trailer, RADIUS_VECTOR_LENGTH);
+ memcpy(cryptvector + strlen(secret), md5trailer, RADIUS_VECTOR_LENGTH);
/*
* .. and for subsequent iterations the result of the previous XOR
*/
md5trailer = encryptedpassword + i;
- if (!pg_md5_binary(cryptvector, strlen(port->hba->radiussecret) + RADIUS_VECTOR_LENGTH, encryptedpassword + i))
+ if (!pg_md5_binary(cryptvector, strlen(secret) + RADIUS_VECTOR_LENGTH, encryptedpassword + i))
{
ereport(LOG,
(errmsg("could not perform MD5 encryption of password")));
if (timeoutval <= 0)
{
ereport(LOG,
- (errmsg("timeout waiting for RADIUS response")));
+ (errmsg("timeout waiting for RADIUS response from %s",
+ server)));
closesocket(sock);
return STATUS_ERROR;
}
if (r == 0)
{
ereport(LOG,
- (errmsg("timeout waiting for RADIUS response")));
+ (errmsg("timeout waiting for RADIUS response from %s",
+ server)));
closesocket(sock);
return STATUS_ERROR;
}
}
#ifdef HAVE_IPV6
- if (remoteaddr.sin6_port != htons(port->hba->radiusport))
+ if (remoteaddr.sin6_port != htons(port))
#else
- if (remoteaddr.sin_port != htons(port->hba->radiusport))
+ if (remoteaddr.sin_port != htons(port))
#endif
{
#ifdef HAVE_IPV6
ereport(LOG,
- (errmsg("RADIUS response was sent from incorrect port: %d",
- ntohs(remoteaddr.sin6_port))));
+ (errmsg("RADIUS response from %s was sent from incorrect port: %d",
+ server, ntohs(remoteaddr.sin6_port))));
#else
ereport(LOG,
- (errmsg("RADIUS response was sent from incorrect port: %d",
- ntohs(remoteaddr.sin_port))));
+ (errmsg("RADIUS response from %s was sent from incorrect port: %d",
+ server, ntohs(remoteaddr.sin_port))));
#endif
continue;
}
if (packetlength < RADIUS_HEADER_LENGTH)
{
ereport(LOG,
- (errmsg("RADIUS response too short: %d", packetlength)));
+ (errmsg("RADIUS response from %s too short: %d", server, packetlength)));
continue;
}
if (packetlength != ntohs(receivepacket->length))
{
ereport(LOG,
- (errmsg("RADIUS response has corrupt length: %d (actual length %d)",
- ntohs(receivepacket->length), packetlength)));
+ (errmsg("RADIUS response from %s has corrupt length: %d (actual length %d)",
+ server, ntohs(receivepacket->length), packetlength)));
continue;
}
if (packet->id != receivepacket->id)
{
ereport(LOG,
- (errmsg("RADIUS response is to a different request: %d (should be %d)",
- receivepacket->id, packet->id)));
+ (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(port->hba->radiussecret));
+ cryptvector = palloc(packetlength + strlen(secret));
memcpy(cryptvector, receivepacket, 4); /* code+id+length */
memcpy(cryptvector + 4, packet->vector, RADIUS_VECTOR_LENGTH); /* request
if (packetlength > RADIUS_HEADER_LENGTH) /* there may be no
* attributes at all */
memcpy(cryptvector + RADIUS_HEADER_LENGTH, receive_buffer + RADIUS_HEADER_LENGTH, packetlength - RADIUS_HEADER_LENGTH);
- memcpy(cryptvector + packetlength, port->hba->radiussecret, strlen(port->hba->radiussecret));
+ memcpy(cryptvector + packetlength, secret, strlen(secret));
if (!pg_md5_binary(cryptvector,
- packetlength + strlen(port->hba->radiussecret),
+ packetlength + strlen(secret),
encryptedpassword))
{
ereport(LOG,
if (memcmp(receivepacket->vector, encryptedpassword, RADIUS_VECTOR_LENGTH) != 0)
{
ereport(LOG,
- (errmsg("RADIUS response has incorrect MD5 signature")));
+ (errmsg("RADIUS response from %s has incorrect MD5 signature",
+ server)));
continue;
}
else if (receivepacket->code == RADIUS_ACCESS_REJECT)
{
closesocket(sock);
- return STATUS_ERROR;
+ return STATUS_EOF;
}
else
{
ereport(LOG,
- (errmsg("RADIUS response has invalid code (%d) for user \"%s\"",
- receivepacket->code, port->user_name)));
+ (errmsg("RADIUS response from %s has invalid code (%d) for user \"%s\"",
+ server, receivepacket->code, user_name)));
continue;
}
} /* while (true) */
#include "storage/fd.h"
#include "utils/acl.h"
#include "utils/builtins.h"
+#include "utils/varlena.h"
#include "utils/guc.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
const char *inc_filename, int elevel, char **err_msg);
static bool parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline,
int elevel, char **err_msg);
+static bool verify_option_list_length(List *options, char *optionname,
+ List *masters, char *mastername, int line_num);
static ArrayType *gethba_options(HbaLine *hba);
static void fill_hba_line(Tuplestorestate *tuple_store, TupleDesc tupdesc,
int lineno, HbaLine *hba, const char *err_msg);
if (parsedline->auth_method == uaRADIUS)
{
- MANDATORY_AUTH_ARG(parsedline->radiusserver, "radiusserver", "radius");
- MANDATORY_AUTH_ARG(parsedline->radiussecret, "radiussecret", "radius");
+ MANDATORY_AUTH_ARG(parsedline->radiusservers, "radiusservers", "radius");
+ MANDATORY_AUTH_ARG(parsedline->radiussecrets, "radiussecrets", "radius");
+
+ if (list_length(parsedline->radiusservers) < 1)
+ {
+ ereport(LOG,
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ errmsg("list of RADIUS servers cannot be empty"),
+ errcontext("line %d of configuration file \"%s\"",
+ line_num, HbaFileName)));
+ return NULL;
+ }
+
+ if (list_length(parsedline->radiussecrets) < 1)
+ {
+ ereport(LOG,
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ errmsg("list of RADIUS secrets cannot be empty"),
+ errcontext("line %d of configuration file \"%s\"",
+ line_num, HbaFileName)));
+ 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 (!verify_option_list_length(parsedline->radiussecrets,
+ "RADIUS secrets",
+ parsedline->radiusservers,
+ "RADIUS servers",
+ line_num))
+ return NULL;
+ if (!verify_option_list_length(parsedline->radiusports,
+ "RADIUS ports",
+ parsedline->radiusservers,
+ "RADIUS servers",
+ line_num))
+ return NULL;
+ if (!verify_option_list_length(parsedline->radiusidentifiers,
+ "RADIUS identifiers",
+ parsedline->radiusservers,
+ "RADIUS servers",
+ line_num))
+ return NULL;
}
/*
return parsedline;
}
+
+static bool
+verify_option_list_length(List *options, char *optionname, List *masters, char *mastername, int line_num)
+{
+ if (list_length(options) == 0 ||
+ list_length(options) == 1 ||
+ list_length(options) == list_length(masters))
+ return true;
+
+ ereport(LOG,
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ errmsg("the number of %s (%i) must be 1 or the same as the number of %s (%i)",
+ optionname,
+ list_length(options),
+ mastername,
+ list_length(masters)
+ ),
+ errcontext("line %d of configuration file \"%s\"",
+ line_num, HbaFileName)));
+ return false;
+}
+
/*
* Parse one name-value pair as an authentication option into the given
* HbaLine. Return true if we successfully parse the option, false if we
else
hbaline->upn_username = false;
}
- else if (strcmp(name, "radiusserver") == 0)
+ 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, "radiusserver", "radius");
-
- MemSet(&hints, 0, sizeof(hints));
- hints.ai_socktype = SOCK_DGRAM;
- hints.ai_family = AF_UNSPEC;
+ REQUIRE_AUTH_OPTION(uaRADIUS, "radiusservers", "radius");
- ret = pg_getaddrinfo_all(val, NULL, &hints, &gai_result);
- if (ret || !gai_result)
+ if (!SplitIdentifierString(dupval, ',', &parsed_servers))
{
+ /* syntax error in list */
ereport(elevel,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("could not translate RADIUS server name \"%s\" to address: %s",
- val, gai_strerror(ret)),
+ errmsg("could not parse RADIUS server list \"%s\"",
+ val),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
- *err_msg = psprintf("could not translate RADIUS server name \"%s\" to address: %s",
- val, gai_strerror(ret));
- if (gai_result)
- pg_freeaddrinfo_all(hints.ai_family, gai_result);
return false;
}
- pg_freeaddrinfo_all(hints.ai_family, gai_result);
- hbaline->radiusserver = pstrdup(val);
+
+ /* 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, HbaFileName)));
+ 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, "radiusport") == 0)
+ else if (strcmp(name, "radiusports") == 0)
{
- REQUIRE_AUTH_OPTION(uaRADIUS, "radiusport", "radius");
- hbaline->radiusport = atoi(val);
- if (hbaline->radiusport == 0)
+ List *parsed_ports;
+ ListCell *l;
+ char *dupval = pstrdup(val);
+
+ REQUIRE_AUTH_OPTION(uaRADIUS, "radiusports", "radius");
+
+ if (!SplitIdentifierString(dupval, ',', &parsed_ports))
{
ereport(elevel,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("invalid RADIUS port number: \"%s\"", val),
+ errmsg("could not parse RADIUS port list \"%s\"",
+ val),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
*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, HbaFileName)));
+
+ return false;
+ }
+ }
+ hbaline->radiusports = parsed_ports;
+ hbaline->radiusports_s = pstrdup(val);
}
- else if (strcmp(name, "radiussecret") == 0)
+ else if (strcmp(name, "radiussecrets") == 0)
{
- REQUIRE_AUTH_OPTION(uaRADIUS, "radiussecret", "radius");
- hbaline->radiussecret = pstrdup(val);
+ List *parsed_secrets;
+ char *dupval = pstrdup(val);
+
+ REQUIRE_AUTH_OPTION(uaRADIUS, "radiussecrets", "radius");
+
+ if (!SplitIdentifierString(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, HbaFileName)));
+ return false;
+ }
+
+ hbaline->radiussecrets = parsed_secrets;
+ hbaline->radiussecrets_s = pstrdup(val);
}
- else if (strcmp(name, "radiusidentifier") == 0)
+ else if (strcmp(name, "radiusidentifiers") == 0)
{
- REQUIRE_AUTH_OPTION(uaRADIUS, "radiusidentifier", "radius");
- hbaline->radiusidentifier = pstrdup(val);
+ List *parsed_identifiers;
+ char *dupval = pstrdup(val);
+
+ REQUIRE_AUTH_OPTION(uaRADIUS, "radiusidentifiers", "radius");
+
+ if (!SplitIdentifierString(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, HbaFileName)));
+ return false;
+ }
+
+ hbaline->radiusidentifiers = parsed_identifiers;
+ hbaline->radiusidentifiers_s = pstrdup(val);
}
else
{
if (hba->auth_method == uaRADIUS)
{
- if (hba->radiusserver)
+ if (hba->radiusservers_s)
options[noptions++] =
- CStringGetTextDatum(psprintf("radiusserver=%s", hba->radiusserver));
+ CStringGetTextDatum(psprintf("radiusservers=%s", hba->radiusservers_s));
- if (hba->radiussecret)
+ if (hba->radiussecrets_s)
options[noptions++] =
- CStringGetTextDatum(psprintf("radiussecret=%s", hba->radiussecret));
+ CStringGetTextDatum(psprintf("radiussecrets=%s", hba->radiussecrets_s));
- if (hba->radiusidentifier)
+ if (hba->radiusidentifiers_s)
options[noptions++] =
- CStringGetTextDatum(psprintf("radiusidentifier=%s", hba->radiusidentifier));
+ CStringGetTextDatum(psprintf("radiusidentifiers=%s", hba->radiusidentifiers_s));
- if (hba->radiusport)
+ if (hba->radiusports_s)
options[noptions++] =
- CStringGetTextDatum(psprintf("radiusport=%d", hba->radiusport));
+ CStringGetTextDatum(psprintf("radiusports=%s", hba->radiusports_s));
}
Assert(noptions <= MAX_HBA_OPTIONS);
bool include_realm;
bool compat_realm;
bool upn_username;
- char *radiusserver;
- char *radiussecret;
- char *radiusidentifier;
- int radiusport;
+ List *radiusservers;
+ char *radiusservers_s;
+ List *radiussecrets;
+ char *radiussecrets_s;
+ List *radiusidentifiers;
+ char *radiusidentifiers_s;
+ List *radiusports;
+ char *radiusports_s;
} HbaLine;
typedef struct IdentLine