mirror of
https://github.com/postgres/postgres.git
synced 2025-04-25 21:42:33 +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:
parent
c137c68ea6
commit
6b76f1bb58
@ -1621,24 +1621,36 @@ host ... ldap ldapurl="ldap://ldap.example.net/dc=example,dc=net?uid?sub"
|
|||||||
<literal>Access Reject</>. There is no support for RADIUS accounting.
|
<literal>Access Reject</>. There is no support for RADIUS accounting.
|
||||||
</para>
|
</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>
|
<para>
|
||||||
The following configuration options are supported for RADIUS:
|
The following configuration options are supported for RADIUS:
|
||||||
<variablelist>
|
<variablelist>
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><literal>radiusserver</literal></term>
|
<term><literal>radiusservers</literal></term>
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<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.
|
This parameter is required.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><literal>radiussecret</literal></term>
|
<term><literal>radiussecrets</literal></term>
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<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
|
server. This must have exactly the same value on the PostgreSQL
|
||||||
and RADIUS servers. It is recommended that this be a string of
|
and RADIUS servers. It is recommended that this be a string of
|
||||||
at least 16 characters. This parameter is required.
|
at least 16 characters. This parameter is required.
|
||||||
@ -1656,17 +1668,17 @@ host ... ldap ldapurl="ldap://ldap.example.net/dc=example,dc=net?uid?sub"
|
|||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><literal>radiusport</literal></term>
|
<term><literal>radiusports</literal></term>
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<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.
|
is specified, the default port <literal>1812</> will be used.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><literal>radiusidentifier</literal></term>
|
<term><literal>radiusidentifiers</literal></term>
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
The string used as <literal>NAS Identifier</> in the RADIUS
|
The string used as <literal>NAS Identifier</> in the RADIUS
|
||||||
|
@ -197,6 +197,7 @@ static int pg_SSPI_make_upn(char *accountname,
|
|||||||
*----------------------------------------------------------------
|
*----------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
static int CheckRADIUSAuth(Port *port);
|
static int CheckRADIUSAuth(Port *port);
|
||||||
|
static int PerformRadiusTransaction(char *server, char *secret, char *portstr, char *identifier, char *user_name, char *passwd);
|
||||||
|
|
||||||
|
|
||||||
/*----------------------------------------------------------------
|
/*----------------------------------------------------------------
|
||||||
@ -2591,77 +2592,29 @@ static int
|
|||||||
CheckRADIUSAuth(Port *port)
|
CheckRADIUSAuth(Port *port)
|
||||||
{
|
{
|
||||||
char *passwd;
|
char *passwd;
|
||||||
char *identifier = "postgresql";
|
ListCell *server,
|
||||||
char radius_buffer[RADIUS_BUFFER_SIZE];
|
*secrets,
|
||||||
char receive_buffer[RADIUS_BUFFER_SIZE];
|
*radiusports,
|
||||||
radius_packet *packet = (radius_packet *) radius_buffer;
|
*identifiers;
|
||||||
radius_packet *receivepacket = (radius_packet *) receive_buffer;
|
|
||||||
int32 service = htonl(RADIUS_AUTHENTICATE_ONLY);
|
|
||||||
uint8 *cryptvector;
|
|
||||||
int encryptedpasswordlen;
|
|
||||||
uint8 encryptedpassword[RADIUS_MAX_PASSWORD_LENGTH];
|
|
||||||
uint8 *md5trailer;
|
|
||||||
int packetlength;
|
|
||||||
pgsocket sock;
|
|
||||||
|
|
||||||
#ifdef HAVE_IPV6
|
|
||||||
struct sockaddr_in6 localaddr;
|
|
||||||
struct sockaddr_in6 remoteaddr;
|
|
||||||
#else
|
|
||||||
struct sockaddr_in localaddr;
|
|
||||||
struct sockaddr_in remoteaddr;
|
|
||||||
#endif
|
|
||||||
struct addrinfo hint;
|
|
||||||
struct addrinfo *serveraddrs;
|
|
||||||
char portstr[128];
|
|
||||||
ACCEPT_TYPE_ARG3 addrsize;
|
|
||||||
fd_set fdset;
|
|
||||||
struct timeval endtime;
|
|
||||||
int i,
|
|
||||||
j,
|
|
||||||
r;
|
|
||||||
|
|
||||||
/* Make sure struct alignment is correct */
|
/* Make sure struct alignment is correct */
|
||||||
Assert(offsetof(radius_packet, vector) == 4);
|
Assert(offsetof(radius_packet, vector) == 4);
|
||||||
|
|
||||||
/* Verify parameters */
|
/* Verify parameters */
|
||||||
if (!port->hba->radiusserver || port->hba->radiusserver[0] == '\0')
|
if (list_length(port->hba->radiusservers) < 1)
|
||||||
{
|
{
|
||||||
ereport(LOG,
|
ereport(LOG,
|
||||||
(errmsg("RADIUS server not specified")));
|
(errmsg("RADIUS server not specified")));
|
||||||
return STATUS_ERROR;
|
return STATUS_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!port->hba->radiussecret || port->hba->radiussecret[0] == '\0')
|
if (list_length(port->hba->radiussecrets) < 1)
|
||||||
{
|
{
|
||||||
ereport(LOG,
|
ereport(LOG,
|
||||||
(errmsg("RADIUS secret not specified")));
|
(errmsg("RADIUS secret not specified")));
|
||||||
return STATUS_ERROR;
|
return STATUS_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (port->hba->radiusport == 0)
|
|
||||||
port->hba->radiusport = 1812;
|
|
||||||
|
|
||||||
MemSet(&hint, 0, sizeof(hint));
|
|
||||||
hint.ai_socktype = SOCK_DGRAM;
|
|
||||||
hint.ai_family = AF_UNSPEC;
|
|
||||||
snprintf(portstr, sizeof(portstr), "%d", port->hba->radiusport);
|
|
||||||
|
|
||||||
r = pg_getaddrinfo_all(port->hba->radiusserver, 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))));
|
|
||||||
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 */
|
/* Send regular password request to client, and get the response */
|
||||||
sendAuthRequest(port, AUTH_REQ_PASSWORD, NULL, 0);
|
sendAuthRequest(port, AUTH_REQ_PASSWORD, NULL, 0);
|
||||||
|
|
||||||
@ -2683,6 +2636,104 @@ CheckRADIUSAuth(Port *port)
|
|||||||
return STATUS_ERROR;
|
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;
|
||||||
|
radius_packet *receivepacket = (radius_packet *) receive_buffer;
|
||||||
|
int32 service = htonl(RADIUS_AUTHENTICATE_ONLY);
|
||||||
|
uint8 *cryptvector;
|
||||||
|
int encryptedpasswordlen;
|
||||||
|
uint8 encryptedpassword[RADIUS_MAX_PASSWORD_LENGTH];
|
||||||
|
uint8 *md5trailer;
|
||||||
|
int packetlength;
|
||||||
|
pgsocket sock;
|
||||||
|
|
||||||
|
#ifdef HAVE_IPV6
|
||||||
|
struct sockaddr_in6 localaddr;
|
||||||
|
struct sockaddr_in6 remoteaddr;
|
||||||
|
#else
|
||||||
|
struct sockaddr_in localaddr;
|
||||||
|
struct sockaddr_in remoteaddr;
|
||||||
|
#endif
|
||||||
|
struct addrinfo hint;
|
||||||
|
struct addrinfo *serveraddrs;
|
||||||
|
int port;
|
||||||
|
ACCEPT_TYPE_ARG3 addrsize;
|
||||||
|
fd_set fdset;
|
||||||
|
struct timeval endtime;
|
||||||
|
int i,
|
||||||
|
j,
|
||||||
|
r;
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
port = atoi(portstr);
|
||||||
|
|
||||||
|
r = pg_getaddrinfo_all(server, portstr, &hint, &serveraddrs);
|
||||||
|
if (r || !serveraddrs)
|
||||||
|
{
|
||||||
|
ereport(LOG,
|
||||||
|
(errmsg("could not translate RADIUS server name \"%s\" to address: %s",
|
||||||
|
server, gai_strerror(r))));
|
||||||
|
if (serveraddrs)
|
||||||
|
pg_freeaddrinfo_all(hint.ai_family, serveraddrs);
|
||||||
|
return STATUS_ERROR;
|
||||||
|
}
|
||||||
|
/* XXX: add support for multiple returned addresses? */
|
||||||
|
|
||||||
/* Construct RADIUS packet */
|
/* Construct RADIUS packet */
|
||||||
packet->code = RADIUS_ACCESS_REQUEST;
|
packet->code = RADIUS_ACCESS_REQUEST;
|
||||||
@ -2695,7 +2746,7 @@ CheckRADIUSAuth(Port *port)
|
|||||||
}
|
}
|
||||||
packet->id = packet->vector[0];
|
packet->id = packet->vector[0];
|
||||||
radius_add_attribute(packet, RADIUS_SERVICE_TYPE, (unsigned char *) &service, sizeof(service));
|
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));
|
radius_add_attribute(packet, RADIUS_NAS_IDENTIFIER, (unsigned char *) identifier, strlen(identifier));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -2705,14 +2756,14 @@ CheckRADIUSAuth(Port *port)
|
|||||||
* (if necessary)
|
* (if necessary)
|
||||||
*/
|
*/
|
||||||
encryptedpasswordlen = ((strlen(passwd) + RADIUS_VECTOR_LENGTH - 1) / RADIUS_VECTOR_LENGTH) * RADIUS_VECTOR_LENGTH;
|
encryptedpasswordlen = ((strlen(passwd) + RADIUS_VECTOR_LENGTH - 1) / RADIUS_VECTOR_LENGTH) * RADIUS_VECTOR_LENGTH;
|
||||||
cryptvector = palloc(strlen(port->hba->radiussecret) + RADIUS_VECTOR_LENGTH);
|
cryptvector = palloc(strlen(secret) + RADIUS_VECTOR_LENGTH);
|
||||||
memcpy(cryptvector, port->hba->radiussecret, strlen(port->hba->radiussecret));
|
memcpy(cryptvector, secret, strlen(secret));
|
||||||
|
|
||||||
/* for the first iteration, we use the Request Authenticator vector */
|
/* for the first iteration, we use the Request Authenticator vector */
|
||||||
md5trailer = packet->vector;
|
md5trailer = packet->vector;
|
||||||
for (i = 0; i < encryptedpasswordlen; i += RADIUS_VECTOR_LENGTH)
|
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
|
* .. and for subsequent iterations the result of the previous XOR
|
||||||
@ -2720,7 +2771,7 @@ CheckRADIUSAuth(Port *port)
|
|||||||
*/
|
*/
|
||||||
md5trailer = encryptedpassword + i;
|
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,
|
ereport(LOG,
|
||||||
(errmsg("could not perform MD5 encryption of password")));
|
(errmsg("could not perform MD5 encryption of password")));
|
||||||
@ -2812,7 +2863,8 @@ CheckRADIUSAuth(Port *port)
|
|||||||
if (timeoutval <= 0)
|
if (timeoutval <= 0)
|
||||||
{
|
{
|
||||||
ereport(LOG,
|
ereport(LOG,
|
||||||
(errmsg("timeout waiting for RADIUS response")));
|
(errmsg("timeout waiting for RADIUS response from %s",
|
||||||
|
server)));
|
||||||
closesocket(sock);
|
closesocket(sock);
|
||||||
return STATUS_ERROR;
|
return STATUS_ERROR;
|
||||||
}
|
}
|
||||||
@ -2837,7 +2889,8 @@ CheckRADIUSAuth(Port *port)
|
|||||||
if (r == 0)
|
if (r == 0)
|
||||||
{
|
{
|
||||||
ereport(LOG,
|
ereport(LOG,
|
||||||
(errmsg("timeout waiting for RADIUS response")));
|
(errmsg("timeout waiting for RADIUS response from %s",
|
||||||
|
server)));
|
||||||
closesocket(sock);
|
closesocket(sock);
|
||||||
return STATUS_ERROR;
|
return STATUS_ERROR;
|
||||||
}
|
}
|
||||||
@ -2864,19 +2917,19 @@ CheckRADIUSAuth(Port *port)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_IPV6
|
#ifdef HAVE_IPV6
|
||||||
if (remoteaddr.sin6_port != htons(port->hba->radiusport))
|
if (remoteaddr.sin6_port != htons(port))
|
||||||
#else
|
#else
|
||||||
if (remoteaddr.sin_port != htons(port->hba->radiusport))
|
if (remoteaddr.sin_port != htons(port))
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
#ifdef HAVE_IPV6
|
#ifdef HAVE_IPV6
|
||||||
ereport(LOG,
|
ereport(LOG,
|
||||||
(errmsg("RADIUS response was sent from incorrect port: %d",
|
(errmsg("RADIUS response from %s was sent from incorrect port: %d",
|
||||||
ntohs(remoteaddr.sin6_port))));
|
server, ntohs(remoteaddr.sin6_port))));
|
||||||
#else
|
#else
|
||||||
ereport(LOG,
|
ereport(LOG,
|
||||||
(errmsg("RADIUS response was sent from incorrect port: %d",
|
(errmsg("RADIUS response from %s was sent from incorrect port: %d",
|
||||||
ntohs(remoteaddr.sin_port))));
|
server, ntohs(remoteaddr.sin_port))));
|
||||||
#endif
|
#endif
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -2884,23 +2937,23 @@ CheckRADIUSAuth(Port *port)
|
|||||||
if (packetlength < RADIUS_HEADER_LENGTH)
|
if (packetlength < RADIUS_HEADER_LENGTH)
|
||||||
{
|
{
|
||||||
ereport(LOG,
|
ereport(LOG,
|
||||||
(errmsg("RADIUS response too short: %d", packetlength)));
|
(errmsg("RADIUS response from %s too short: %d", server, packetlength)));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (packetlength != ntohs(receivepacket->length))
|
if (packetlength != ntohs(receivepacket->length))
|
||||||
{
|
{
|
||||||
ereport(LOG,
|
ereport(LOG,
|
||||||
(errmsg("RADIUS response has corrupt length: %d (actual length %d)",
|
(errmsg("RADIUS response from %s has corrupt length: %d (actual length %d)",
|
||||||
ntohs(receivepacket->length), packetlength)));
|
server, ntohs(receivepacket->length), packetlength)));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (packet->id != receivepacket->id)
|
if (packet->id != receivepacket->id)
|
||||||
{
|
{
|
||||||
ereport(LOG,
|
ereport(LOG,
|
||||||
(errmsg("RADIUS response is to a different request: %d (should be %d)",
|
(errmsg("RADIUS response from %s is to a different request: %d (should be %d)",
|
||||||
receivepacket->id, packet->id)));
|
server, receivepacket->id, packet->id)));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2908,7 +2961,7 @@ CheckRADIUSAuth(Port *port)
|
|||||||
* Verify the response authenticator, which is calculated as
|
* Verify the response authenticator, which is calculated as
|
||||||
* MD5(Code+ID+Length+RequestAuthenticator+Attributes+Secret)
|
* 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, receivepacket, 4); /* code+id+length */
|
||||||
memcpy(cryptvector + 4, packet->vector, RADIUS_VECTOR_LENGTH); /* request
|
memcpy(cryptvector + 4, packet->vector, RADIUS_VECTOR_LENGTH); /* request
|
||||||
@ -2917,10 +2970,10 @@ CheckRADIUSAuth(Port *port)
|
|||||||
if (packetlength > RADIUS_HEADER_LENGTH) /* there may be no
|
if (packetlength > RADIUS_HEADER_LENGTH) /* there may be no
|
||||||
* attributes at all */
|
* attributes at all */
|
||||||
memcpy(cryptvector + RADIUS_HEADER_LENGTH, receive_buffer + RADIUS_HEADER_LENGTH, packetlength - RADIUS_HEADER_LENGTH);
|
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,
|
if (!pg_md5_binary(cryptvector,
|
||||||
packetlength + strlen(port->hba->radiussecret),
|
packetlength + strlen(secret),
|
||||||
encryptedpassword))
|
encryptedpassword))
|
||||||
{
|
{
|
||||||
ereport(LOG,
|
ereport(LOG,
|
||||||
@ -2933,7 +2986,8 @@ CheckRADIUSAuth(Port *port)
|
|||||||
if (memcmp(receivepacket->vector, encryptedpassword, RADIUS_VECTOR_LENGTH) != 0)
|
if (memcmp(receivepacket->vector, encryptedpassword, RADIUS_VECTOR_LENGTH) != 0)
|
||||||
{
|
{
|
||||||
ereport(LOG,
|
ereport(LOG,
|
||||||
(errmsg("RADIUS response has incorrect MD5 signature")));
|
(errmsg("RADIUS response from %s has incorrect MD5 signature",
|
||||||
|
server)));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2945,13 +2999,13 @@ CheckRADIUSAuth(Port *port)
|
|||||||
else if (receivepacket->code == RADIUS_ACCESS_REJECT)
|
else if (receivepacket->code == RADIUS_ACCESS_REJECT)
|
||||||
{
|
{
|
||||||
closesocket(sock);
|
closesocket(sock);
|
||||||
return STATUS_ERROR;
|
return STATUS_EOF;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ereport(LOG,
|
ereport(LOG,
|
||||||
(errmsg("RADIUS response has invalid code (%d) for user \"%s\"",
|
(errmsg("RADIUS response from %s has invalid code (%d) for user \"%s\"",
|
||||||
receivepacket->code, port->user_name)));
|
server, receivepacket->code, user_name)));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
} /* while (true) */
|
} /* while (true) */
|
||||||
|
@ -39,6 +39,7 @@
|
|||||||
#include "storage/fd.h"
|
#include "storage/fd.h"
|
||||||
#include "utils/acl.h"
|
#include "utils/acl.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
|
#include "utils/varlena.h"
|
||||||
#include "utils/guc.h"
|
#include "utils/guc.h"
|
||||||
#include "utils/lsyscache.h"
|
#include "utils/lsyscache.h"
|
||||||
#include "utils/memutils.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);
|
const char *inc_filename, int elevel, char **err_msg);
|
||||||
static bool parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline,
|
static bool parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline,
|
||||||
int elevel, char **err_msg);
|
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 ArrayType *gethba_options(HbaLine *hba);
|
||||||
static void fill_hba_line(Tuplestorestate *tuple_store, TupleDesc tupdesc,
|
static void fill_hba_line(Tuplestorestate *tuple_store, TupleDesc tupdesc,
|
||||||
int lineno, HbaLine *hba, const char *err_msg);
|
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)
|
if (parsedline->auth_method == uaRADIUS)
|
||||||
{
|
{
|
||||||
MANDATORY_AUTH_ARG(parsedline->radiusserver, "radiusserver", "radius");
|
MANDATORY_AUTH_ARG(parsedline->radiusservers, "radiusservers", "radius");
|
||||||
MANDATORY_AUTH_ARG(parsedline->radiussecret, "radiussecret", "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;
|
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
|
* Parse one name-value pair as an authentication option into the given
|
||||||
* HbaLine. Return true if we successfully parse the option, false if we
|
* 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
|
else
|
||||||
hbaline->upn_username = false;
|
hbaline->upn_username = false;
|
||||||
}
|
}
|
||||||
else if (strcmp(name, "radiusserver") == 0)
|
else if (strcmp(name, "radiusservers") == 0)
|
||||||
{
|
{
|
||||||
struct addrinfo *gai_result;
|
struct addrinfo *gai_result;
|
||||||
struct addrinfo hints;
|
struct addrinfo hints;
|
||||||
int ret;
|
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));
|
if (!SplitIdentifierString(dupval, ',', &parsed_servers))
|
||||||
hints.ai_socktype = SOCK_DGRAM;
|
|
||||||
hints.ai_family = AF_UNSPEC;
|
|
||||||
|
|
||||||
ret = pg_getaddrinfo_all(val, NULL, &hints, &gai_result);
|
|
||||||
if (ret || !gai_result)
|
|
||||||
{
|
{
|
||||||
|
/* syntax error in list */
|
||||||
ereport(elevel,
|
ereport(elevel,
|
||||||
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
||||||
errmsg("could not translate RADIUS server name \"%s\" to address: %s",
|
errmsg("could not parse RADIUS server list \"%s\"",
|
||||||
val, gai_strerror(ret)),
|
val),
|
||||||
errcontext("line %d of configuration file \"%s\"",
|
errcontext("line %d of configuration file \"%s\"",
|
||||||
line_num, HbaFileName)));
|
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;
|
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");
|
List *parsed_ports;
|
||||||
hbaline->radiusport = atoi(val);
|
ListCell *l;
|
||||||
if (hbaline->radiusport == 0)
|
char *dupval = pstrdup(val);
|
||||||
|
|
||||||
|
REQUIRE_AUTH_OPTION(uaRADIUS, "radiusports", "radius");
|
||||||
|
|
||||||
|
if (!SplitIdentifierString(dupval, ',', &parsed_ports))
|
||||||
{
|
{
|
||||||
ereport(elevel,
|
ereport(elevel,
|
||||||
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
(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\"",
|
errcontext("line %d of configuration file \"%s\"",
|
||||||
line_num, HbaFileName)));
|
line_num, HbaFileName)));
|
||||||
*err_msg = psprintf("invalid RADIUS port number: \"%s\"", val);
|
*err_msg = psprintf("invalid RADIUS port number: \"%s\"", val);
|
||||||
return false;
|
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");
|
List *parsed_secrets;
|
||||||
hbaline->radiussecret = pstrdup(val);
|
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");
|
List *parsed_identifiers;
|
||||||
hbaline->radiusidentifier = pstrdup(val);
|
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
|
else
|
||||||
{
|
{
|
||||||
@ -2124,21 +2270,21 @@ gethba_options(HbaLine *hba)
|
|||||||
|
|
||||||
if (hba->auth_method == uaRADIUS)
|
if (hba->auth_method == uaRADIUS)
|
||||||
{
|
{
|
||||||
if (hba->radiusserver)
|
if (hba->radiusservers_s)
|
||||||
options[noptions++] =
|
options[noptions++] =
|
||||||
CStringGetTextDatum(psprintf("radiusserver=%s", hba->radiusserver));
|
CStringGetTextDatum(psprintf("radiusservers=%s", hba->radiusservers_s));
|
||||||
|
|
||||||
if (hba->radiussecret)
|
if (hba->radiussecrets_s)
|
||||||
options[noptions++] =
|
options[noptions++] =
|
||||||
CStringGetTextDatum(psprintf("radiussecret=%s", hba->radiussecret));
|
CStringGetTextDatum(psprintf("radiussecrets=%s", hba->radiussecrets_s));
|
||||||
|
|
||||||
if (hba->radiusidentifier)
|
if (hba->radiusidentifiers_s)
|
||||||
options[noptions++] =
|
options[noptions++] =
|
||||||
CStringGetTextDatum(psprintf("radiusidentifier=%s", hba->radiusidentifier));
|
CStringGetTextDatum(psprintf("radiusidentifiers=%s", hba->radiusidentifiers_s));
|
||||||
|
|
||||||
if (hba->radiusport)
|
if (hba->radiusports_s)
|
||||||
options[noptions++] =
|
options[noptions++] =
|
||||||
CStringGetTextDatum(psprintf("radiusport=%d", hba->radiusport));
|
CStringGetTextDatum(psprintf("radiusports=%s", hba->radiusports_s));
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert(noptions <= MAX_HBA_OPTIONS);
|
Assert(noptions <= MAX_HBA_OPTIONS);
|
||||||
|
@ -89,10 +89,14 @@ typedef struct HbaLine
|
|||||||
bool include_realm;
|
bool include_realm;
|
||||||
bool compat_realm;
|
bool compat_realm;
|
||||||
bool upn_username;
|
bool upn_username;
|
||||||
char *radiusserver;
|
List *radiusservers;
|
||||||
char *radiussecret;
|
char *radiusservers_s;
|
||||||
char *radiusidentifier;
|
List *radiussecrets;
|
||||||
int radiusport;
|
char *radiussecrets_s;
|
||||||
|
List *radiusidentifiers;
|
||||||
|
char *radiusidentifiers_s;
|
||||||
|
List *radiusports;
|
||||||
|
char *radiusports_s;
|
||||||
} HbaLine;
|
} HbaLine;
|
||||||
|
|
||||||
typedef struct IdentLine
|
typedef struct IdentLine
|
||||||
|
Loading…
x
Reference in New Issue
Block a user