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:
@@ -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);
|
||||
|
Reference in New Issue
Block a user