1
0
mirror of https://github.com/postgres/postgres.git synced 2025-08-30 06:01:21 +03:00

Support multiple RADIUS servers

This changes all the RADIUS related parameters (radiusserver,
radiussecret, radiusport, radiusidentifier) to be plural and to accept a
comma separated list of servers, which will be tried in order.

Reviewed by Adam Brightwell
This commit is contained in:
Magnus Hagander
2017-03-22 17:55:16 +01:00
parent c137c68ea6
commit 6b76f1bb58
4 changed files with 343 additions and 127 deletions

View File

@@ -39,6 +39,7 @@
#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"
@@ -143,6 +144,8 @@ static List *tokenize_inc_file(List *tokens, const char *outer_filename,
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);
@@ -1532,8 +1535,52 @@ parse_hba_line(TokenizedLine *tok_line, int elevel)
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;
}
/*
@@ -1547,6 +1594,28 @@ parse_hba_line(TokenizedLine *tok_line, int elevel)
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
@@ -1766,60 +1835,137 @@ parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline,
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");
REQUIRE_AUTH_OPTION(uaRADIUS, "radiusservers", "radius");
MemSet(&hints, 0, sizeof(hints));
hints.ai_socktype = SOCK_DGRAM;
hints.ai_family = AF_UNSPEC;
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
{
@@ -2124,21 +2270,21 @@ gethba_options(HbaLine *hba)
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);