diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c index 746d7cbb8a..82510fda63 100644 --- a/src/backend/libpq/auth.c +++ b/src/backend/libpq/auth.c @@ -206,6 +206,12 @@ static int pg_SSPI_make_upn(char *accountname, 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); +/*---------------------------------------------------------------- + * Connection Redirection + *---------------------------------------------------------------- + */ +static int SendAlternativeServerName(Port *port, char **logdetail); + /* * Maximum accepted size of GSS and SSPI authentication tokens. @@ -598,6 +604,9 @@ ClientAuthentication(Port *port) case uaTrust: status = STATUS_OK; break; + case uaRedirect: + status = SendAlternativeServerName(port, &logdetail); + break; } if (ClientAuthentication_hook) @@ -609,6 +618,28 @@ ClientAuthentication(Port *port) auth_failed(port, status, logdetail); } +/* + * Send alternative server information packet to the frontend. + */ +static int +SendAlternativeServerName(Port *port, char **logdetail) +{ + StringInfoData buf; + + CHECK_FOR_INTERRUPTS(); + + pq_beginmessage(&buf, 'M'); + pq_sendstring(&buf, "server"); + pq_sendstring(&buf, port->hba->alternativeservername); + pq_sendstring(&buf, "port"); + pq_sendstring(&buf, port->hba->alternativeserverport); + + pq_endmessage(&buf); + pq_flush(); + proc_exit(0); + + return STATUS_OK; +} /* * Send an authentication request packet to the frontend. diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c index aa20f266b8..d211068f44 100644 --- a/src/backend/libpq/hba.c +++ b/src/backend/libpq/hba.c @@ -134,7 +134,8 @@ static const char *const UserAuthName[] = "ldap", "cert", "radius", - "peer" + "peer", + "redirect" }; @@ -1358,6 +1359,8 @@ parse_hba_line(TokenizedLine *tok_line, int elevel) #endif else if (strcmp(token->string, "radius") == 0) parsedline->auth_method = uaRADIUS; + else if (strcmp(token->string, "redirect") == 0) + parsedline->auth_method = uaRedirect; else { ereport(elevel, @@ -1384,6 +1387,49 @@ parse_hba_line(TokenizedLine *tok_line, int elevel) return NULL; } + if (parsedline->auth_method == uaRedirect) + { + /* Get the alternative server name and port */ + field = lnext(field); + if (!field) + { + ereport(elevel, + (errcode(ERRCODE_CONFIG_FILE_ERROR), + errmsg("end-of-line before alternative server name"), + errcontext("line %d of configuration file \"%s\"", + line_num, HbaFileName))); + *err_msg = "end-of-line before alternative server name"; + return NULL; + } + tokens = lfirst(field); + if (tokens->length > 2) + { + ereport(elevel, + (errcode(ERRCODE_CONFIG_FILE_ERROR), + errmsg("multiple values specified for alternative server"), + errhint("Specify exactly one alternative server per line."), + errcontext("line %d of configuration file \"%s\"", + line_num, HbaFileName))); + *err_msg = "multiple values specified for alternative server"; + return NULL; + } + + tokencell = list_head(tokens); + token = lfirst(tokencell); + parsedline->alternativeservername = pstrdup(token->string); + + if (tokens->length == 2) + { + tokencell = lnext(tokencell); + token = lfirst(tokencell); + parsedline->alternativeserverport = pstrdup(token->string); + } + else + { + pg_itoa(DEF_PGPORT, parsedline->alternativeserverport); + } + } + /* * XXX: When using ident on local connections, change it to peer, for * backwards compatibility. @@ -2100,6 +2146,12 @@ check_hba(hbaPort *port) if (!check_role(port->user_name, roleid, hba->roles)) continue; + /* Check the protocol version to see if the client supports redirection */ + if (PG_PROTOCOL_MAJOR(port->proto) < PG_PROTOCOL_MAJOR(PG_PROTOCOL_LATEST) || + (PG_PROTOCOL_MAJOR(port->proto) == PG_PROTOCOL_MAJOR(PG_PROTOCOL_LATEST) && + PG_PROTOCOL_MINOR(port->proto) < PG_PROTOCOL_MINOR(PG_PROTOCOL_LATEST))) + continue; + /* Found a record that matched! */ port->hba = hba; return; diff --git a/src/backend/libpq/pg_hba.conf.sample b/src/backend/libpq/pg_hba.conf.sample index c853e36232..3af0972d48 100644 --- a/src/backend/libpq/pg_hba.conf.sample +++ b/src/backend/libpq/pg_hba.conf.sample @@ -43,7 +43,7 @@ # directly connected to. # # METHOD can be "trust", "reject", "md5", "password", "scram-sha-256", -# "gss", "sspi", "ident", "peer", "pam", "ldap", "radius" or "cert". +# "gss", "sspi", "ident", "peer", "pam", "ldap", "radius", "cert" or "redirect". # Note that "password" sends passwords in clear text; "md5" or # "scram-sha-256" are preferred since they send encrypted passwords. # diff --git a/src/include/libpq/hba.h b/src/include/libpq/hba.h index 5f68f4c666..1e4f2bd6db 100644 --- a/src/include/libpq/hba.h +++ b/src/include/libpq/hba.h @@ -38,8 +38,9 @@ typedef enum UserAuth uaLDAP, uaCert, uaRADIUS, - uaPeer -#define USER_AUTH_LAST uaPeer /* Must be last value of this enum */ + uaPeer, + uaRedirect +#define USER_AUTH_LAST uaRedirect /* Must be last value of this enum */ } UserAuth; typedef enum IPCompareMethod @@ -99,6 +100,8 @@ typedef struct HbaLine char *radiusidentifiers_s; List *radiusports; char *radiusports_s; + char *alternativeservername; + char *alternativeserverport; } HbaLine; typedef struct IdentLine