mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-12-27 08:39:28 +08:00
Add support for RADIUS authentication.
This commit is contained in:
parent
000416ac32
commit
b3daac5a9c
@ -1,4 +1,4 @@
|
|||||||
<!-- $PostgreSQL: pgsql/doc/src/sgml/client-auth.sgml,v 1.127 2010/01/26 06:45:31 petere Exp $ -->
|
<!-- $PostgreSQL: pgsql/doc/src/sgml/client-auth.sgml,v 1.128 2010/01/27 12:11:59 mha Exp $ -->
|
||||||
|
|
||||||
<chapter id="client-authentication">
|
<chapter id="client-authentication">
|
||||||
<title>Client Authentication</title>
|
<title>Client Authentication</title>
|
||||||
@ -394,6 +394,16 @@ hostnossl <replaceable>database</replaceable> <replaceable>user</replaceable>
|
|||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><literal>radius</></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Authenticate using a RADIUS server. See <xref
|
||||||
|
linkend="auth-radius"> for detauls.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><literal>cert</></term>
|
<term><literal>cert</></term>
|
||||||
<listitem>
|
<listitem>
|
||||||
@ -1331,6 +1341,95 @@ ldapserver=ldap.example.net ldapprefix="cn=" ldapsuffix=", dc=example, dc=net"
|
|||||||
|
|
||||||
</sect2>
|
</sect2>
|
||||||
|
|
||||||
|
<sect2 id="auth-radius">
|
||||||
|
<title>RADIUS authentication</title>
|
||||||
|
|
||||||
|
<indexterm zone="auth-radius">
|
||||||
|
<primary>RADIUS</primary>
|
||||||
|
</indexterm>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
This authentication method operates similarly to
|
||||||
|
<literal>password</literal> except that it uses RADIUS
|
||||||
|
as the password verification method. RADIUS is used only to validate
|
||||||
|
the user name/password pairs. Therefore the user must already
|
||||||
|
exist in the database before RADIUS can be used for
|
||||||
|
authentication.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
When using RADIUS authentication, an Access Request message will be sent
|
||||||
|
to the configured RADIUS server. This request will be of type
|
||||||
|
<literal>Authenticate Only</literal>, and include parameters for
|
||||||
|
<literal>user name</>, <literal>password</> (encrypted) and
|
||||||
|
<literal>NAS Identifier</>. The request will be encrypted using
|
||||||
|
a secret shared with the server. The RADIUS server will respond to
|
||||||
|
this server with either <literal>Access Accept</> or
|
||||||
|
<literal>Access Reject</>. There is no support for RADIUS accounting.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
The following configuration options are supported for RADIUS:
|
||||||
|
<variablelist>
|
||||||
|
<varlistentry>
|
||||||
|
<term><literal>radiusserver</literal></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
The IP address of the RADIUS server to connect to. This must
|
||||||
|
be an IPV4 address and not a hostname. This parameter is required.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><literal>radiussecret</literal></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
The shared secret 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 is a string of
|
||||||
|
at least 16 characters. This parameter is required.
|
||||||
|
<note>
|
||||||
|
<para>
|
||||||
|
The encryption vector used will only be cryptographically
|
||||||
|
strong if <productname>PostgreSQL</> is built with support for
|
||||||
|
<productname>OpenSSL</>. In other cases, the transmission to the
|
||||||
|
RADIUS server should only be considered obfuscated, not secured, and
|
||||||
|
external security measures should be applied if necessary.
|
||||||
|
</para>
|
||||||
|
</note>
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><literal>radiusport</literal></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
The port number on the RADIUS server 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>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
The string used as <literal>NAS Identifier</> in the RADIUS
|
||||||
|
requests. This parameter can be used as a second parameter
|
||||||
|
identifying for example which database the user is attempting
|
||||||
|
to authenticate as, which can be used for policy matching on
|
||||||
|
the RADIUS server. If no identifier is specified, the default
|
||||||
|
<literal>postgresql</> will be used.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
</variablelist>
|
||||||
|
</para>
|
||||||
|
</sect2>
|
||||||
|
|
||||||
<sect2 id="auth-cert">
|
<sect2 id="auth-cert">
|
||||||
<title>Certificate authentication</title>
|
<title>Certificate authentication</title>
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/libpq/auth.c,v 1.191 2010/01/10 14:16:07 mha Exp $
|
* $PostgreSQL: pgsql/src/backend/libpq/auth.c,v 1.192 2010/01/27 12:11:59 mha Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -27,12 +27,14 @@
|
|||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
#include "libpq/auth.h"
|
#include "libpq/auth.h"
|
||||||
#include "libpq/crypt.h"
|
#include "libpq/crypt.h"
|
||||||
#include "libpq/ip.h"
|
#include "libpq/ip.h"
|
||||||
#include "libpq/libpq.h"
|
#include "libpq/libpq.h"
|
||||||
#include "libpq/pqformat.h"
|
#include "libpq/pqformat.h"
|
||||||
|
#include "libpq/md5.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "storage/ipc.h"
|
#include "storage/ipc.h"
|
||||||
|
|
||||||
@ -182,6 +184,15 @@ typedef SECURITY_STATUS
|
|||||||
static int pg_SSPI_recvauth(Port *port);
|
static int pg_SSPI_recvauth(Port *port);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------
|
||||||
|
* RADIUS Authentication
|
||||||
|
*----------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
#ifdef USE_SSL
|
||||||
|
#include <openssl/rand.h>
|
||||||
|
#endif
|
||||||
|
static int CheckRADIUSAuth(Port *port);
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Maximum accepted size of GSS and SSPI authentication tokens.
|
* Maximum accepted size of GSS and SSPI authentication tokens.
|
||||||
@ -265,6 +276,9 @@ auth_failed(Port *port, int status)
|
|||||||
case uaLDAP:
|
case uaLDAP:
|
||||||
errstr = gettext_noop("LDAP authentication failed for user \"%s\"");
|
errstr = gettext_noop("LDAP authentication failed for user \"%s\"");
|
||||||
break;
|
break;
|
||||||
|
case uaRADIUS:
|
||||||
|
errstr = gettext_noop("RADIUS authentication failed for user \"%s\"");
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
errstr = gettext_noop("authentication failed for user \"%s\": invalid authentication method");
|
errstr = gettext_noop("authentication failed for user \"%s\": invalid authentication method");
|
||||||
break;
|
break;
|
||||||
@ -473,7 +487,9 @@ ClientAuthentication(Port *port)
|
|||||||
Assert(false);
|
Assert(false);
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
|
case uaRADIUS:
|
||||||
|
status = CheckRADIUSAuth(port);
|
||||||
|
break;
|
||||||
case uaTrust:
|
case uaTrust:
|
||||||
status = STATUS_OK;
|
status = STATUS_OK;
|
||||||
break;
|
break;
|
||||||
@ -2415,3 +2431,350 @@ CheckCertAuth(Port *port)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------
|
||||||
|
* RADIUS authentication
|
||||||
|
*----------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* RADIUS authentication is described in RFC2865 (and several
|
||||||
|
* others).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define RADIUS_VECTOR_LENGTH 16
|
||||||
|
#define RADIUS_HEADER_LENGTH 20
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint8 attribute;
|
||||||
|
uint8 length;
|
||||||
|
uint8 data[1];
|
||||||
|
} radius_attribute;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint8 code;
|
||||||
|
uint8 id;
|
||||||
|
uint16 length;
|
||||||
|
uint8 vector[RADIUS_VECTOR_LENGTH];
|
||||||
|
} radius_packet;
|
||||||
|
|
||||||
|
/* RADIUS packet types */
|
||||||
|
#define RADIUS_ACCESS_REQUEST 1
|
||||||
|
#define RADIUS_ACCESS_ACCEPT 2
|
||||||
|
#define RADIUS_ACCESS_REJECT 3
|
||||||
|
|
||||||
|
/* RAIDUS attributes */
|
||||||
|
#define RADIUS_USER_NAME 1
|
||||||
|
#define RADIUS_PASSWORD 2
|
||||||
|
#define RADIUS_SERVICE_TYPE 6
|
||||||
|
#define RADIUS_NAS_IDENTIFIER 32
|
||||||
|
|
||||||
|
/* RADIUS service types */
|
||||||
|
#define RADIUS_AUTHENTICATE_ONLY 8
|
||||||
|
|
||||||
|
/* Maximum size of a RADIUS packet we will create or accept */
|
||||||
|
#define RADIUS_BUFFER_SIZE 1024
|
||||||
|
|
||||||
|
/* Seconds to wait - XXX: should be in a config variable! */
|
||||||
|
#define RADIUS_TIMEOUT 3
|
||||||
|
|
||||||
|
static void
|
||||||
|
radius_add_attribute(radius_packet *packet, uint8 type, const unsigned char *data, int len)
|
||||||
|
{
|
||||||
|
radius_attribute *attr;
|
||||||
|
|
||||||
|
if (packet->length + len > RADIUS_BUFFER_SIZE)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* With remotely realistic data, this can never happen. But catch it just to make
|
||||||
|
* sure we don't overrun a buffer. We'll just skip adding the broken attribute,
|
||||||
|
* which will in the end cause authentication to fail.
|
||||||
|
*/
|
||||||
|
elog(WARNING,
|
||||||
|
"Adding attribute code %i with length %i to radius packet would create oversize packet, ignoring",
|
||||||
|
type, len);
|
||||||
|
return;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
attr = (radius_attribute *) ((unsigned char *)packet + packet->length);
|
||||||
|
attr->attribute = type;
|
||||||
|
attr->length = len + 2; /* total size includes type and length */
|
||||||
|
memcpy(attr->data, data, len);
|
||||||
|
packet->length += attr->length;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
CheckRADIUSAuth(Port *port)
|
||||||
|
{
|
||||||
|
char *passwd;
|
||||||
|
char *identifier = "postgresql";
|
||||||
|
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;
|
||||||
|
uint8 encryptedpassword[RADIUS_VECTOR_LENGTH];
|
||||||
|
int packetlength;
|
||||||
|
pgsocket sock;
|
||||||
|
struct sockaddr_in localaddr;
|
||||||
|
struct sockaddr_in remoteaddr;
|
||||||
|
socklen_t addrsize;
|
||||||
|
fd_set fdset;
|
||||||
|
struct timeval timeout;
|
||||||
|
int i,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;
|
||||||
|
|
||||||
|
memset(&remoteaddr, 0, sizeof(remoteaddr));
|
||||||
|
remoteaddr.sin_family = AF_INET;
|
||||||
|
remoteaddr.sin_addr.s_addr = inet_addr(port->hba->radiusserver);
|
||||||
|
if (remoteaddr.sin_addr.s_addr == INADDR_NONE)
|
||||||
|
{
|
||||||
|
ereport(LOG,
|
||||||
|
(errmsg("RADIUS server '%s' is not a valid IP address",
|
||||||
|
port->hba->radiusserver)));
|
||||||
|
return STATUS_ERROR;
|
||||||
|
}
|
||||||
|
remoteaddr.sin_port = htons(port->hba->radiusport);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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_VECTOR_LENGTH)
|
||||||
|
{
|
||||||
|
ereport(LOG,
|
||||||
|
(errmsg("RADIUS authentication does not support passwords longer than 16 characters")));
|
||||||
|
return STATUS_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Construct RADIUS packet */
|
||||||
|
packet->code = RADIUS_ACCESS_REQUEST;
|
||||||
|
packet->length = RADIUS_HEADER_LENGTH;
|
||||||
|
#ifdef USE_SSL
|
||||||
|
if (RAND_bytes(packet->vector, RADIUS_VECTOR_LENGTH) != 1)
|
||||||
|
{
|
||||||
|
ereport(LOG,
|
||||||
|
(errmsg("could not generate random encryption vector")));
|
||||||
|
return STATUS_ERROR;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
for (i = 0; i < RADIUS_VECTOR_LENGTH; i++)
|
||||||
|
/* Use a lower strengh random number of OpenSSL is not available */
|
||||||
|
packet->vector[i] = random() % 255;
|
||||||
|
#endif
|
||||||
|
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_NAS_IDENTIFIER, (unsigned char *) identifier, strlen(identifier));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* RADIUS password attributes are calculated as:
|
||||||
|
* e[0] = p[0] XOR MD5(secret + vector)
|
||||||
|
*/
|
||||||
|
cryptvector = palloc(RADIUS_VECTOR_LENGTH + strlen(port->hba->radiussecret));
|
||||||
|
memcpy(cryptvector, port->hba->radiussecret, strlen(port->hba->radiussecret));
|
||||||
|
memcpy(cryptvector + strlen(port->hba->radiussecret), packet->vector, RADIUS_VECTOR_LENGTH);
|
||||||
|
if (!pg_md5_binary(cryptvector, RADIUS_VECTOR_LENGTH + strlen(port->hba->radiussecret), encryptedpassword))
|
||||||
|
{
|
||||||
|
ereport(LOG,
|
||||||
|
(errmsg("could not perform md5 encryption of password")));
|
||||||
|
pfree(cryptvector);
|
||||||
|
return STATUS_ERROR;
|
||||||
|
}
|
||||||
|
pfree(cryptvector);
|
||||||
|
for (i = 0; i < RADIUS_VECTOR_LENGTH; i++)
|
||||||
|
{
|
||||||
|
if (i < strlen(passwd))
|
||||||
|
encryptedpassword[i] = passwd[i] ^ encryptedpassword[i];
|
||||||
|
else
|
||||||
|
encryptedpassword[i] = '\0' ^ encryptedpassword[i];
|
||||||
|
}
|
||||||
|
radius_add_attribute(packet, RADIUS_PASSWORD, encryptedpassword, RADIUS_VECTOR_LENGTH);
|
||||||
|
|
||||||
|
/* Length need to be in network order on the wire */
|
||||||
|
packetlength = packet->length;
|
||||||
|
packet->length = htons(packet->length);
|
||||||
|
|
||||||
|
sock = socket(AF_INET, SOCK_DGRAM, 0);
|
||||||
|
if (sock < 0)
|
||||||
|
{
|
||||||
|
ereport(LOG,
|
||||||
|
(errmsg("could not create RADIUS socket: %m")));
|
||||||
|
return STATUS_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&localaddr, 0, sizeof(localaddr));
|
||||||
|
localaddr.sin_family = AF_INET;
|
||||||
|
localaddr.sin_addr.s_addr = INADDR_ANY;
|
||||||
|
if (bind(sock, (struct sockaddr *) &localaddr, sizeof(localaddr)))
|
||||||
|
{
|
||||||
|
ereport(LOG,
|
||||||
|
(errmsg("could not bind local RADIUS socket: %m")));
|
||||||
|
closesocket(sock);
|
||||||
|
return STATUS_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sendto(sock, radius_buffer, packetlength, 0,
|
||||||
|
(struct sockaddr *) &remoteaddr, sizeof(remoteaddr)) < 0)
|
||||||
|
{
|
||||||
|
ereport(LOG,
|
||||||
|
(errmsg("could not send RADIUS packet: %m")));
|
||||||
|
closesocket(sock);
|
||||||
|
return STATUS_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
timeout.tv_sec = RADIUS_TIMEOUT;
|
||||||
|
timeout.tv_usec = 0;
|
||||||
|
FD_ZERO(&fdset);
|
||||||
|
FD_SET(sock, &fdset);
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
r = select(sock + 1, &fdset, NULL, NULL, &timeout);
|
||||||
|
if (r < 0)
|
||||||
|
{
|
||||||
|
if (errno == EINTR)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Anything else is an actual error */
|
||||||
|
ereport(LOG,
|
||||||
|
(errmsg("could not check status on RADIUS socket: %m")));
|
||||||
|
closesocket(sock);
|
||||||
|
return STATUS_ERROR;
|
||||||
|
}
|
||||||
|
if (r == 0)
|
||||||
|
{
|
||||||
|
ereport(LOG,
|
||||||
|
(errmsg("timeout waiting for RADIUS response")));
|
||||||
|
closesocket(sock);
|
||||||
|
return STATUS_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* else we actually have a packet ready to read */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read the response packet */
|
||||||
|
addrsize = sizeof(remoteaddr);
|
||||||
|
packetlength = recvfrom(sock, receive_buffer, RADIUS_BUFFER_SIZE, 0,
|
||||||
|
(struct sockaddr *) &remoteaddr, &addrsize);
|
||||||
|
if (packetlength < 0)
|
||||||
|
{
|
||||||
|
ereport(LOG,
|
||||||
|
(errmsg("could not read RADIUS response: %m")));
|
||||||
|
closesocket(sock);
|
||||||
|
return STATUS_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
closesocket(sock);
|
||||||
|
|
||||||
|
if (remoteaddr.sin_port != htons(port->hba->radiusport))
|
||||||
|
{
|
||||||
|
ereport(LOG,
|
||||||
|
(errmsg("RADIUS response was sent from incorrect port: %i",
|
||||||
|
ntohs(remoteaddr.sin_port))));
|
||||||
|
return STATUS_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packetlength < RADIUS_HEADER_LENGTH)
|
||||||
|
{
|
||||||
|
ereport(LOG,
|
||||||
|
(errmsg("RADIUS response too short: %i", packetlength)));
|
||||||
|
return STATUS_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packetlength != ntohs(receivepacket->length))
|
||||||
|
{
|
||||||
|
ereport(LOG,
|
||||||
|
(errmsg("RADIUS response has corrupt length: %i (actual length %i)",
|
||||||
|
ntohs(receivepacket->length), packetlength)));
|
||||||
|
return STATUS_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packet->id != receivepacket->id)
|
||||||
|
{
|
||||||
|
ereport(LOG,
|
||||||
|
(errmsg("RADIUS response is to a different request: %i (should be %i)",
|
||||||
|
receivepacket->id, packet->id)));
|
||||||
|
return STATUS_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Verify the response authenticator, which is calculated as
|
||||||
|
* MD5(Code+ID+Length+RequestAuthenticator+Attributes+Secret)
|
||||||
|
*/
|
||||||
|
cryptvector = palloc(packetlength + strlen(port->hba->radiussecret));
|
||||||
|
|
||||||
|
memcpy(cryptvector, receivepacket, 4); /* code+id+length */
|
||||||
|
memcpy(cryptvector+4, packet->vector, RADIUS_VECTOR_LENGTH); /* request authenticator, from original packet */
|
||||||
|
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));
|
||||||
|
|
||||||
|
if (!pg_md5_binary(cryptvector,
|
||||||
|
packetlength + strlen(port->hba->radiussecret),
|
||||||
|
encryptedpassword))
|
||||||
|
{
|
||||||
|
ereport(LOG,
|
||||||
|
(errmsg("could not perform md5 encryption of received packet")));
|
||||||
|
pfree(cryptvector);
|
||||||
|
return STATUS_ERROR;
|
||||||
|
}
|
||||||
|
pfree(cryptvector);
|
||||||
|
|
||||||
|
if (memcmp(receivepacket->vector, encryptedpassword, RADIUS_VECTOR_LENGTH) != 0)
|
||||||
|
{
|
||||||
|
ereport(LOG,
|
||||||
|
(errmsg("RADIUS response has incorrect MD5 signature")));
|
||||||
|
return STATUS_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (receivepacket->code == RADIUS_ACCESS_ACCEPT)
|
||||||
|
return STATUS_OK;
|
||||||
|
else if (receivepacket->code == RADIUS_ACCESS_REJECT)
|
||||||
|
return STATUS_ERROR;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ereport(LOG,
|
||||||
|
(errmsg("RADIUS response has invalid code (%i) for user '%s'",
|
||||||
|
receivepacket->code, port->user_name)));
|
||||||
|
return STATUS_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.195 2010/01/15 09:19:02 heikki Exp $
|
* $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.196 2010/01/27 12:11:59 mha Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -952,6 +952,8 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline)
|
|||||||
#else
|
#else
|
||||||
unsupauth = "cert";
|
unsupauth = "cert";
|
||||||
#endif
|
#endif
|
||||||
|
else if (strcmp(token, "radius")== 0)
|
||||||
|
parsedline->auth_method = uaRADIUS;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ereport(LOG,
|
ereport(LOG,
|
||||||
@ -1162,6 +1164,45 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline)
|
|||||||
else
|
else
|
||||||
parsedline->include_realm = false;
|
parsedline->include_realm = false;
|
||||||
}
|
}
|
||||||
|
else if (strcmp(token, "radiusserver") == 0)
|
||||||
|
{
|
||||||
|
REQUIRE_AUTH_OPTION(uaRADIUS, "radiusserver", "radius");
|
||||||
|
if (inet_addr(c) == INADDR_NONE)
|
||||||
|
{
|
||||||
|
ereport(LOG,
|
||||||
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
||||||
|
errmsg("invalid RADIUS server IP address: \"%s\"", c),
|
||||||
|
errcontext("line %d of configuration file \"%s\"",
|
||||||
|
line_num, HbaFileName)));
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
parsedline->radiusserver = pstrdup(c);
|
||||||
|
}
|
||||||
|
else if (strcmp(token, "radiusport") == 0)
|
||||||
|
{
|
||||||
|
REQUIRE_AUTH_OPTION(uaRADIUS, "radiusport", "radius");
|
||||||
|
parsedline->radiusport = atoi(c);
|
||||||
|
if (parsedline->radiusport == 0)
|
||||||
|
{
|
||||||
|
ereport(LOG,
|
||||||
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
||||||
|
errmsg("invalid RADIUS port number: \"%s\"", c),
|
||||||
|
errcontext("line %d of configuration file \"%s\"",
|
||||||
|
line_num, HbaFileName)));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (strcmp(token, "radiussecret") == 0)
|
||||||
|
{
|
||||||
|
REQUIRE_AUTH_OPTION(uaRADIUS, "radiussecret", "radius");
|
||||||
|
parsedline->radiussecret = pstrdup(c);
|
||||||
|
}
|
||||||
|
else if (strcmp(token, "radiusidentifier") == 0)
|
||||||
|
{
|
||||||
|
REQUIRE_AUTH_OPTION(uaRADIUS, "radiusidentifier", "radius");
|
||||||
|
parsedline->radiusidentifier = pstrdup(c);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ereport(LOG,
|
ereport(LOG,
|
||||||
@ -1214,6 +1255,12 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (parsedline->auth_method == uaRADIUS)
|
||||||
|
{
|
||||||
|
MANDATORY_AUTH_ARG(parsedline->radiusserver, "radiusserver", "radius");
|
||||||
|
MANDATORY_AUTH_ARG(parsedline->radiussecret, "radiussecret", "radius");
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Enforce any parameters implied by other settings.
|
* Enforce any parameters implied by other settings.
|
||||||
*/
|
*/
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/libpq/md5.c,v 1.38 2010/01/02 16:57:45 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/libpq/md5.c,v 1.39 2010/01/27 12:11:59 mha Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* This is intended to be used in both frontend and backend, so use c.h */
|
/* This is intended to be used in both frontend and backend, so use c.h */
|
||||||
@ -298,6 +298,12 @@ pg_md5_hash(const void *buff, size_t len, char *hexsum)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool pg_md5_binary(const void *buff, size_t len, void *outbuf)
|
||||||
|
{
|
||||||
|
if (!calculateDigestFromBuffer((uint8 *) buff, len, outbuf))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -39,9 +39,9 @@
|
|||||||
# any subnet that the server is directly connected to.
|
# any subnet that the server is directly connected to.
|
||||||
#
|
#
|
||||||
# METHOD can be "trust", "reject", "md5", "password", "gss", "sspi",
|
# METHOD can be "trust", "reject", "md5", "password", "gss", "sspi",
|
||||||
# "krb5", "ident", "pam", "ldap" or "cert". Note that "password"
|
# "krb5", "ident", "pam", "ldap", "radius" or "cert". Note that
|
||||||
# sends passwords in clear text; "md5" is preferred since it sends
|
# "password" sends passwords in clear text; "md5" is preferred since
|
||||||
# encrypted passwords.
|
# it sends encrypted passwords.
|
||||||
#
|
#
|
||||||
# OPTIONS are a set of options for the authentication in the format
|
# OPTIONS are a set of options for the authentication in the format
|
||||||
# NAME=VALUE. The available options depend on the different
|
# NAME=VALUE. The available options depend on the different
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
* Interface to hba.c
|
* Interface to hba.c
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/libpq/hba.h,v 1.60 2009/12/12 21:35:21 mha Exp $
|
* $PostgreSQL: pgsql/src/include/libpq/hba.h,v 1.61 2010/01/27 12:12:00 mha Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -27,7 +27,8 @@ typedef enum UserAuth
|
|||||||
uaSSPI,
|
uaSSPI,
|
||||||
uaPAM,
|
uaPAM,
|
||||||
uaLDAP,
|
uaLDAP,
|
||||||
uaCert
|
uaCert,
|
||||||
|
uaRADIUS
|
||||||
} UserAuth;
|
} UserAuth;
|
||||||
|
|
||||||
typedef enum IPCompareMethod
|
typedef enum IPCompareMethod
|
||||||
@ -71,6 +72,10 @@ typedef struct
|
|||||||
char *krb_server_hostname;
|
char *krb_server_hostname;
|
||||||
char *krb_realm;
|
char *krb_realm;
|
||||||
bool include_realm;
|
bool include_realm;
|
||||||
|
char *radiusserver;
|
||||||
|
char *radiussecret;
|
||||||
|
char *radiusidentifier;
|
||||||
|
int radiusport;
|
||||||
} HbaLine;
|
} HbaLine;
|
||||||
|
|
||||||
/* kluge to avoid including libpq/libpq-be.h here */
|
/* kluge to avoid including libpq/libpq-be.h here */
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/libpq/md5.h,v 1.7 2010/01/02 16:58:04 momjian Exp $
|
* $PostgreSQL: pgsql/src/include/libpq/md5.h,v 1.8 2010/01/27 12:12:00 mha Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -23,6 +23,7 @@
|
|||||||
|
|
||||||
|
|
||||||
extern bool pg_md5_hash(const void *buff, size_t len, char *hexsum);
|
extern bool pg_md5_hash(const void *buff, size_t len, char *hexsum);
|
||||||
|
extern bool pg_md5_binary(const void *buff, size_t len, void *outbuf);
|
||||||
extern bool pg_md5_encrypt(const char *passwd, const char *salt,
|
extern bool pg_md5_encrypt(const char *passwd, const char *salt,
|
||||||
size_t salt_len, char *buf);
|
size_t salt_len, char *buf);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user