Header And Logo

PostgreSQL
| The world's most advanced open source database.

Data Structures | Defines | Functions | Variables

auth.c File Reference

#include "postgres.h"
#include <sys/param.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include "libpq/auth.h"
#include "libpq/crypt.h"
#include "libpq/ip.h"
#include "libpq/libpq.h"
#include "libpq/pqformat.h"
#include "libpq/md5.h"
#include "miscadmin.h"
#include "replication/walsender.h"
#include "storage/ipc.h"
Include dependency graph for auth.c:

Go to the source code of this file.

Data Structures

struct  radius_attribute
struct  radius_packet

Defines

#define IDENT_USERNAME_MAX   512
#define IDENT_PORT   113
#define PG_MAX_AUTH_TOKEN_LENGTH   65535
#define HOSTNAME_LOOKUP_DETAIL(port)
#define RADIUS_VECTOR_LENGTH   16
#define RADIUS_HEADER_LENGTH   20
#define RADIUS_ACCESS_REQUEST   1
#define RADIUS_ACCESS_ACCEPT   2
#define RADIUS_ACCESS_REJECT   3
#define RADIUS_USER_NAME   1
#define RADIUS_PASSWORD   2
#define RADIUS_SERVICE_TYPE   6
#define RADIUS_NAS_IDENTIFIER   32
#define RADIUS_AUTHENTICATE_ONLY   8
#define RADIUS_BUFFER_SIZE   1024
#define RADIUS_TIMEOUT   3

Functions

static void sendAuthRequest (Port *port, AuthRequest areq)
static void auth_failed (Port *port, int status)
static char * recv_password_packet (Port *port)
static int recv_and_check_password_packet (Port *port)
static int ident_inet (hbaPort *port)
static int CheckRADIUSAuth (Port *port)
void ClientAuthentication (Port *port)
static bool interpret_ident_response (const char *ident_response, char *ident_user)
static void radius_add_attribute (radius_packet *packet, uint8 type, const unsigned char *data, int len)

Variables

char * pg_krb_server_keyfile
char * pg_krb_srvnam
bool pg_krb_caseins_users
ClientAuthentication_hook_type ClientAuthentication_hook = NULL

Define Documentation

#define HOSTNAME_LOOKUP_DETAIL (   port  ) 
Value:
(port->remote_hostname                \
                 ? (port->remote_hostname_resolv == +1                  \
                    ? errdetail_log("Client IP address resolved to \"%s\", forward lookup matches.", port->remote_hostname) \
                    : (port->remote_hostname_resolv == 0                \
                       ? errdetail_log("Client IP address resolved to \"%s\", forward lookup not checked.", port->remote_hostname) \
                       : (port->remote_hostname_resolv == -1            \
                          ? errdetail_log("Client IP address resolved to \"%s\", forward lookup does not match.", port->remote_hostname) \
                          : 0)))                                        \
                 : 0)

Referenced by ClientAuthentication().

#define IDENT_PORT   113

Definition at line 53 of file auth.c.

Referenced by ident_inet().

#define IDENT_USERNAME_MAX   512

Definition at line 50 of file auth.c.

Referenced by ident_inet(), and interpret_ident_response().

#define PG_MAX_AUTH_TOKEN_LENGTH   65535

Definition at line 207 of file auth.c.

#define RADIUS_ACCESS_ACCEPT   2

Definition at line 2389 of file auth.c.

Referenced by CheckRADIUSAuth().

#define RADIUS_ACCESS_REJECT   3

Definition at line 2390 of file auth.c.

Referenced by CheckRADIUSAuth().

#define RADIUS_ACCESS_REQUEST   1

Definition at line 2388 of file auth.c.

#define RADIUS_AUTHENTICATE_ONLY   8

Definition at line 2399 of file auth.c.

Referenced by CheckRADIUSAuth().

#define RADIUS_BUFFER_SIZE   1024

Definition at line 2402 of file auth.c.

Referenced by CheckRADIUSAuth(), and radius_add_attribute().

#define RADIUS_HEADER_LENGTH   20

Definition at line 2370 of file auth.c.

Referenced by CheckRADIUSAuth().

#define RADIUS_NAS_IDENTIFIER   32

Definition at line 2396 of file auth.c.

Referenced by CheckRADIUSAuth().

#define RADIUS_PASSWORD   2

Definition at line 2394 of file auth.c.

Referenced by CheckRADIUSAuth().

#define RADIUS_SERVICE_TYPE   6

Definition at line 2395 of file auth.c.

Referenced by CheckRADIUSAuth().

#define RADIUS_TIMEOUT   3

Definition at line 2405 of file auth.c.

#define RADIUS_USER_NAME   1

Definition at line 2393 of file auth.c.

Referenced by CheckRADIUSAuth().

#define RADIUS_VECTOR_LENGTH   16

Definition at line 2369 of file auth.c.

Referenced by CheckRADIUSAuth().


Function Documentation

static void auth_failed ( Port port,
int  status 
) [static]

Definition at line 235 of file auth.c.

References HbaLine::auth_method, ereport, errcode(), errdetail_log(), errmsg(), FATAL, gettext_noop, Port::hba, HbaLine::linenumber, proc_exit(), HbaLine::rawline, STATUS_EOF, uaCert, uaGSS, uaIdent, uaImplicitReject, uaKrb5, uaLDAP, uaMD5, uaPAM, uaPassword, uaPeer, uaRADIUS, uaReject, uaSSPI, uaTrust, and Port::user_name.

Referenced by ClientAuthentication().

{
    const char *errstr;
    int         errcode_return = ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION;

    /*
     * If we failed due to EOF from client, just quit; there's no point in
     * trying to send a message to the client, and not much point in logging
     * the failure in the postmaster log.  (Logging the failure might be
     * desirable, were it not for the fact that libpq closes the connection
     * unceremoniously if challenged for a password when it hasn't got one to
     * send.  We'll get a useless log entry for every psql connection under
     * password auth, even if it's perfectly successful, if we log STATUS_EOF
     * events.)
     */
    if (status == STATUS_EOF)
        proc_exit(0);

    switch (port->hba->auth_method)
    {
        case uaReject:
        case uaImplicitReject:
            errstr = gettext_noop("authentication failed for user \"%s\": host rejected");
            break;
        case uaKrb5:
            errstr = gettext_noop("Kerberos 5 authentication failed for user \"%s\"");
            break;
        case uaTrust:
            errstr = gettext_noop("\"trust\" authentication failed for user \"%s\"");
            break;
        case uaIdent:
            errstr = gettext_noop("Ident authentication failed for user \"%s\"");
            break;
        case uaPeer:
            errstr = gettext_noop("Peer authentication failed for user \"%s\"");
            break;
        case uaPassword:
        case uaMD5:
            errstr = gettext_noop("password authentication failed for user \"%s\"");
            /* We use it to indicate if a .pgpass password failed. */
            errcode_return = ERRCODE_INVALID_PASSWORD;
            break;
        case uaGSS:
            errstr = gettext_noop("GSSAPI authentication failed for user \"%s\"");
            break;
        case uaSSPI:
            errstr = gettext_noop("SSPI authentication failed for user \"%s\"");
            break;
        case uaPAM:
            errstr = gettext_noop("PAM authentication failed for user \"%s\"");
            break;
        case uaLDAP:
            errstr = gettext_noop("LDAP authentication failed for user \"%s\"");
            break;
        case uaCert:
            errstr = gettext_noop("certificate authentication failed for user \"%s\"");
            break;
        case uaRADIUS:
            errstr = gettext_noop("RADIUS authentication failed for user \"%s\"");
            break;
        default:
            errstr = gettext_noop("authentication failed for user \"%s\": invalid authentication method");
            break;
    }

    if (port->hba)
        ereport(FATAL,
                (errcode(errcode_return),
                 errmsg(errstr, port->user_name),
                 errdetail_log("Connection matched pg_hba.conf line %d: \"%s\"", port->hba->linenumber, port->hba->rawline)));
    else
        ereport(FATAL,
                (errcode(errcode_return),
                 errmsg(errstr, port->user_name)));

    /* doesn't return */
}

static int CheckRADIUSAuth ( Port port  )  [static]

Definition at line 2435 of file auth.c.

References addrinfo::ai_family, addrinfo::ai_socktype, Assert, AUTH_REQ_PASSWORD, closesocket, radius_packet::code, EINTR, ereport, errmsg(), gai_strerror, gettimeofday(), Port::hba, radius_packet::id, in6addr_any, radius_packet::length, LOG, memcmp(), MemSet, NULL, offsetof, palloc(), pfree(), pg_freeaddrinfo_all(), pg_getaddrinfo_all(), pg_md5_binary(), RADIUS_ACCESS_ACCEPT, RADIUS_ACCESS_REJECT, radius_add_attribute(), RADIUS_AUTHENTICATE_ONLY, RADIUS_BUFFER_SIZE, RADIUS_HEADER_LENGTH, RADIUS_NAS_IDENTIFIER, RADIUS_PASSWORD, RADIUS_SERVICE_TYPE, RADIUS_USER_NAME, RADIUS_VECTOR_LENGTH, HbaLine::radiusidentifier, HbaLine::radiusport, HbaLine::radiussecret, HbaLine::radiusserver, random(), recv_password_packet(), select, sendAuthRequest(), snprintf(), socket, Port::user_name, and radius_packet::vector.

Referenced by ClientAuthentication().

{
    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;

#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,
                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(&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 */
    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(serveraddrs[0].ai_family, SOCK_DGRAM, 0);
    if (sock < 0)
    {
        ereport(LOG,
                (errmsg("could not create RADIUS socket: %m")));
        pg_freeaddrinfo_all(hint.ai_family, serveraddrs);
        return STATUS_ERROR;
    }

    memset(&localaddr, 0, sizeof(localaddr));
#ifdef HAVE_IPV6
    localaddr.sin6_family = serveraddrs[0].ai_family;
    localaddr.sin6_addr = in6addr_any;
    if (localaddr.sin6_family == AF_INET6)
        addrsize = sizeof(struct sockaddr_in6);
    else
        addrsize = sizeof(struct sockaddr_in);
#else
    localaddr.sin_family = serveraddrs[0].ai_family;
    localaddr.sin_addr.s_addr = INADDR_ANY;
    addrsize = sizeof(struct sockaddr_in);
#endif
    if (bind(sock, (struct sockaddr *) & localaddr, addrsize))
    {
        ereport(LOG,
                (errmsg("could not bind local RADIUS socket: %m")));
        closesocket(sock);
        pg_freeaddrinfo_all(hint.ai_family, serveraddrs);
        return STATUS_ERROR;
    }

    if (sendto(sock, radius_buffer, packetlength, 0,
               serveraddrs[0].ai_addr, serveraddrs[0].ai_addrlen) < 0)
    {
        ereport(LOG,
                (errmsg("could not send RADIUS packet: %m")));
        closesocket(sock);
        pg_freeaddrinfo_all(hint.ai_family, serveraddrs);
        return STATUS_ERROR;
    }

    /* Don't need the server address anymore */
    pg_freeaddrinfo_all(hint.ai_family, serveraddrs);

    /*
     * Figure out at what time we should time out. We can't just use a single
     * call to select() with a timeout, since somebody can be sending invalid
     * packets to our port thus causing us to retry in a loop and never time
     * out.
     */
    gettimeofday(&endtime, NULL);
    endtime.tv_sec += RADIUS_TIMEOUT;

    while (true)
    {
        struct timeval timeout;
        struct timeval now;
        int64       timeoutval;

        gettimeofday(&now, NULL);
        timeoutval = (endtime.tv_sec * 1000000 + endtime.tv_usec) - (now.tv_sec * 1000000 + now.tv_usec);
        if (timeoutval <= 0)
        {
            ereport(LOG,
                    (errmsg("timeout waiting for RADIUS response")));
            closesocket(sock);
            return STATUS_ERROR;
        }
        timeout.tv_sec = timeoutval / 1000000;
        timeout.tv_usec = timeoutval % 1000000;

        FD_ZERO(&fdset);
        FD_SET(sock, &fdset);

        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;
        }

        /*
         * Attempt to read the response packet, and verify the contents.
         *
         * Any packet that's not actually a RADIUS packet, or otherwise does
         * not validate as an explicit reject, is just ignored and we retry
         * for another packet (until we reach the timeout). This is to avoid
         * the possibility to denial-of-service the login by flooding the
         * server with invalid packets on the port that we're expecting the
         * RADIUS response on.
         */

        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")));
            return STATUS_ERROR;
        }

#ifdef HAVE_IPV6
        if (remoteaddr.sin6_port != htons(port->hba->radiusport))
#else
        if (remoteaddr.sin_port != htons(port->hba->radiusport))
#endif
        {
#ifdef HAVE_IPV6
            ereport(LOG,
                  (errmsg("RADIUS response was sent from incorrect port: %d",
                          ntohs(remoteaddr.sin6_port))));
#else
            ereport(LOG,
                  (errmsg("RADIUS response was sent from incorrect port: %d",
                          ntohs(remoteaddr.sin_port))));
#endif
            continue;
        }

        if (packetlength < RADIUS_HEADER_LENGTH)
        {
            ereport(LOG,
                    (errmsg("RADIUS response too short: %d", packetlength)));
            continue;
        }

        if (packetlength != ntohs(receivepacket->length))
        {
            ereport(LOG,
                    (errmsg("RADIUS response has corrupt length: %d (actual length %d)",
                            ntohs(receivepacket->length), packetlength)));
            continue;
        }

        if (packet->id != receivepacket->id)
        {
            ereport(LOG,
                    (errmsg("RADIUS response is to a different request: %d (should be %d)",
                            receivepacket->id, packet->id)));
            continue;
        }

        /*
         * 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);
            continue;
        }
        pfree(cryptvector);

        if (memcmp(receivepacket->vector, encryptedpassword, RADIUS_VECTOR_LENGTH) != 0)
        {
            ereport(LOG,
                    (errmsg("RADIUS response has incorrect MD5 signature")));
            continue;
        }

        if (receivepacket->code == RADIUS_ACCESS_ACCEPT)
        {
            closesocket(sock);
            return STATUS_OK;
        }
        else if (receivepacket->code == RADIUS_ACCESS_REJECT)
        {
            closesocket(sock);
            return STATUS_ERROR;
        }
        else
        {
            ereport(LOG,
             (errmsg("RADIUS response has invalid code (%d) for user \"%s\"",
                     receivepacket->code, port->user_name)));
            continue;
        }
    }                           /* while (true) */
}

void ClientAuthentication ( Port port  ) 

Definition at line 319 of file auth.c.

References _, SockAddr::addr, am_walsender, Assert, auth_failed(), HbaLine::auth_method, AUTH_REQ_GSS, AUTH_REQ_KRB5, AUTH_REQ_MD5, AUTH_REQ_OK, AUTH_REQ_PASSWORD, AUTH_REQ_SSPI, CHECK_FOR_INTERRUPTS, CheckRADIUSAuth(), ClientAuthentication_hook, HbaLine::clientcert, Port::database_name, Db_user_namespace, ereport, errcode(), errmsg(), FATAL, Port::hba, hba_getauthmethod(), HOSTNAME_LOOKUP_DETAIL, ident_inet(), ImmediateInterruptOK, NI_NUMERICHOST, NULL, pg_getnameinfo_all(), Port::raddr, recv_and_check_password_packet(), SockAddr::salen, sendAuthRequest(), status(), STATUS_OK, uaCert, uaGSS, uaIdent, uaImplicitReject, uaKrb5, uaLDAP, uaMD5, uaPAM, uaPassword, uaPeer, uaRADIUS, uaReject, uaSSPI, uaTrust, and Port::user_name.

Referenced by PerformAuthentication().

{
    int         status = STATUS_ERROR;

    /*
     * Get the authentication method to use for this frontend/database
     * combination.  Note: we do not parse the file at this point; this has
     * already been done elsewhere.  hba.c dropped an error message into the
     * server logfile if parsing the hba config file failed.
     */
    hba_getauthmethod(port);

    /*
     * Enable immediate response to SIGTERM/SIGINT/timeout interrupts. (We
     * don't want this during hba_getauthmethod() because it might have to do
     * database access, eg for role membership checks.)
     */
    ImmediateInterruptOK = true;
    /* And don't forget to detect one that already arrived */
    CHECK_FOR_INTERRUPTS();

    /*
     * This is the first point where we have access to the hba record for the
     * current connection, so perform any verifications based on the hba
     * options field that should be done *before* the authentication here.
     */
    if (port->hba->clientcert)
    {
        /*
         * When we parse pg_hba.conf, we have already made sure that we have
         * been able to load a certificate store. Thus, if a certificate is
         * present on the client, it has been verified against our root
         * certificate store, and the connection would have been aborted
         * already if it didn't verify ok.
         */
#ifdef USE_SSL
        if (!port->peer)
        {
            ereport(FATAL,
                    (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
                  errmsg("connection requires a valid client certificate")));
        }
#else

        /*
         * hba.c makes sure hba->clientcert can't be set unless OpenSSL is
         * present.
         */
        Assert(false);
#endif
    }

    /*
     * Now proceed to do the actual authentication check
     */
    switch (port->hba->auth_method)
    {
        case uaReject:

            /*
             * An explicit "reject" entry in pg_hba.conf.  This report exposes
             * the fact that there's an explicit reject entry, which is
             * perhaps not so desirable from a security standpoint; but the
             * message for an implicit reject could confuse the DBA a lot when
             * the true situation is a match to an explicit reject.  And we
             * don't want to change the message for an implicit reject.  As
             * noted below, the additional information shown here doesn't
             * expose anything not known to an attacker.
             */
            {
                char        hostinfo[NI_MAXHOST];

                pg_getnameinfo_all(&port->raddr.addr, port->raddr.salen,
                                   hostinfo, sizeof(hostinfo),
                                   NULL, 0,
                                   NI_NUMERICHOST);

                if (am_walsender)
                {
#ifdef USE_SSL
                    ereport(FATAL,
                       (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
                        errmsg("pg_hba.conf rejects replication connection for host \"%s\", user \"%s\", %s",
                               hostinfo, port->user_name,
                               port->ssl ? _("SSL on") : _("SSL off"))));
#else
                    ereport(FATAL,
                       (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
                        errmsg("pg_hba.conf rejects replication connection for host \"%s\", user \"%s\"",
                               hostinfo, port->user_name)));
#endif
                }
                else
                {
#ifdef USE_SSL
                    ereport(FATAL,
                       (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
                        errmsg("pg_hba.conf rejects connection for host \"%s\", user \"%s\", database \"%s\", %s",
                               hostinfo, port->user_name,
                               port->database_name,
                               port->ssl ? _("SSL on") : _("SSL off"))));
#else
                    ereport(FATAL,
                       (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
                        errmsg("pg_hba.conf rejects connection for host \"%s\", user \"%s\", database \"%s\"",
                               hostinfo, port->user_name,
                               port->database_name)));
#endif
                }
                break;
            }

        case uaImplicitReject:

            /*
             * No matching entry, so tell the user we fell through.
             *
             * NOTE: the extra info reported here is not a security breach,
             * because all that info is known at the frontend and must be
             * assumed known to bad guys.  We're merely helping out the less
             * clueful good guys.
             */
            {
                char        hostinfo[NI_MAXHOST];

                pg_getnameinfo_all(&port->raddr.addr, port->raddr.salen,
                                   hostinfo, sizeof(hostinfo),
                                   NULL, 0,
                                   NI_NUMERICHOST);

#define HOSTNAME_LOOKUP_DETAIL(port) \
                (port->remote_hostname                \
                 ? (port->remote_hostname_resolv == +1                  \
                    ? errdetail_log("Client IP address resolved to \"%s\", forward lookup matches.", port->remote_hostname) \
                    : (port->remote_hostname_resolv == 0                \
                       ? errdetail_log("Client IP address resolved to \"%s\", forward lookup not checked.", port->remote_hostname) \
                       : (port->remote_hostname_resolv == -1            \
                          ? errdetail_log("Client IP address resolved to \"%s\", forward lookup does not match.", port->remote_hostname) \
                          : 0)))                                        \
                 : 0)

                if (am_walsender)
                {
#ifdef USE_SSL
                    ereport(FATAL,
                       (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
                        errmsg("no pg_hba.conf entry for replication connection from host \"%s\", user \"%s\", %s",
                               hostinfo, port->user_name,
                               port->ssl ? _("SSL on") : _("SSL off")),
                        HOSTNAME_LOOKUP_DETAIL(port)));
#else
                    ereport(FATAL,
                       (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
                        errmsg("no pg_hba.conf entry for replication connection from host \"%s\", user \"%s\"",
                               hostinfo, port->user_name),
                        HOSTNAME_LOOKUP_DETAIL(port)));
#endif
                }
                else
                {
#ifdef USE_SSL
                    ereport(FATAL,
                       (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
                        errmsg("no pg_hba.conf entry for host \"%s\", user \"%s\", database \"%s\", %s",
                               hostinfo, port->user_name,
                               port->database_name,
                               port->ssl ? _("SSL on") : _("SSL off")),
                        HOSTNAME_LOOKUP_DETAIL(port)));
#else
                    ereport(FATAL,
                       (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
                        errmsg("no pg_hba.conf entry for host \"%s\", user \"%s\", database \"%s\"",
                               hostinfo, port->user_name,
                               port->database_name),
                        HOSTNAME_LOOKUP_DETAIL(port)));
#endif
                }
                break;
            }

        case uaKrb5:
#ifdef KRB5
            sendAuthRequest(port, AUTH_REQ_KRB5);
            status = pg_krb5_recvauth(port);
#else
            Assert(false);
#endif
            break;

        case uaGSS:
#ifdef ENABLE_GSS
            sendAuthRequest(port, AUTH_REQ_GSS);
            status = pg_GSS_recvauth(port);
#else
            Assert(false);
#endif
            break;

        case uaSSPI:
#ifdef ENABLE_SSPI
            sendAuthRequest(port, AUTH_REQ_SSPI);
            status = pg_SSPI_recvauth(port);
#else
            Assert(false);
#endif
            break;

        case uaPeer:
#ifdef HAVE_UNIX_SOCKETS
            status = auth_peer(port);
#else
            Assert(false);
#endif
            break;

        case uaIdent:
            status = ident_inet(port);
            break;

        case uaMD5:
            if (Db_user_namespace)
                ereport(FATAL,
                        (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
                         errmsg("MD5 authentication is not supported when \"db_user_namespace\" is enabled")));
            sendAuthRequest(port, AUTH_REQ_MD5);
            status = recv_and_check_password_packet(port);
            break;

        case uaPassword:
            sendAuthRequest(port, AUTH_REQ_PASSWORD);
            status = recv_and_check_password_packet(port);
            break;

        case uaPAM:
#ifdef USE_PAM
            status = CheckPAMAuth(port, port->user_name, "");
#else
            Assert(false);
#endif   /* USE_PAM */
            break;

        case uaLDAP:
#ifdef USE_LDAP
            status = CheckLDAPAuth(port);
#else
            Assert(false);
#endif
            break;

        case uaCert:
#ifdef USE_SSL
            status = CheckCertAuth(port);
#else
            Assert(false);
#endif
            break;
        case uaRADIUS:
            status = CheckRADIUSAuth(port);
            break;
        case uaTrust:
            status = STATUS_OK;
            break;
    }

    if (ClientAuthentication_hook)
        (*ClientAuthentication_hook) (port, status);

    if (status == STATUS_OK)
        sendAuthRequest(port, AUTH_REQ_OK);
    else
        auth_failed(port, status);

    /* Done with authentication, so we should turn off immediate interrupts */
    ImmediateInterruptOK = false;
}

static int ident_inet ( hbaPort port  )  [static]

Definition at line 1601 of file auth.c.

References SockAddr::addr, addrinfo::ai_addr, addrinfo::ai_addrlen, addrinfo::ai_family, addrinfo::ai_protocol, addrinfo::ai_socktype, check_usermap(), closesocket, connect, EINTR, ereport, errcode_for_socket_access(), errmsg(), Port::hba, IDENT_PORT, IDENT_USERNAME_MAX, interpret_ident_response(), Port::laddr, LOG, NI_NUMERICHOST, NI_NUMERICSERV, NULL, pg_freeaddrinfo_all(), pg_getaddrinfo_all(), pg_getnameinfo_all(), Port::raddr, recv, SockAddr::salen, send, snprintf(), socket, Port::user_name, and HbaLine::usermap.

Referenced by ClientAuthentication().

{
    const SockAddr remote_addr = port->raddr;
    const SockAddr local_addr = port->laddr;
    char        ident_user[IDENT_USERNAME_MAX + 1];
    pgsocket    sock_fd;        /* File descriptor for socket on which we talk
                                 * to Ident */
    int         rc;             /* Return code from a locally called function */
    bool        ident_return;
    char        remote_addr_s[NI_MAXHOST];
    char        remote_port[NI_MAXSERV];
    char        local_addr_s[NI_MAXHOST];
    char        local_port[NI_MAXSERV];
    char        ident_port[NI_MAXSERV];
    char        ident_query[80];
    char        ident_response[80 + IDENT_USERNAME_MAX];
    struct addrinfo *ident_serv = NULL,
               *la = NULL,
                hints;

    /*
     * Might look a little weird to first convert it to text and then back to
     * sockaddr, but it's protocol independent.
     */
    pg_getnameinfo_all(&remote_addr.addr, remote_addr.salen,
                       remote_addr_s, sizeof(remote_addr_s),
                       remote_port, sizeof(remote_port),
                       NI_NUMERICHOST | NI_NUMERICSERV);
    pg_getnameinfo_all(&local_addr.addr, local_addr.salen,
                       local_addr_s, sizeof(local_addr_s),
                       local_port, sizeof(local_port),
                       NI_NUMERICHOST | NI_NUMERICSERV);

    snprintf(ident_port, sizeof(ident_port), "%d", IDENT_PORT);
    hints.ai_flags = AI_NUMERICHOST;
    hints.ai_family = remote_addr.addr.ss_family;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = 0;
    hints.ai_addrlen = 0;
    hints.ai_canonname = NULL;
    hints.ai_addr = NULL;
    hints.ai_next = NULL;
    rc = pg_getaddrinfo_all(remote_addr_s, ident_port, &hints, &ident_serv);
    if (rc || !ident_serv)
    {
        if (ident_serv)
            pg_freeaddrinfo_all(hints.ai_family, ident_serv);
        return STATUS_ERROR;    /* we don't expect this to happen */
    }

    hints.ai_flags = AI_NUMERICHOST;
    hints.ai_family = local_addr.addr.ss_family;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = 0;
    hints.ai_addrlen = 0;
    hints.ai_canonname = NULL;
    hints.ai_addr = NULL;
    hints.ai_next = NULL;
    rc = pg_getaddrinfo_all(local_addr_s, NULL, &hints, &la);
    if (rc || !la)
    {
        if (la)
            pg_freeaddrinfo_all(hints.ai_family, la);
        return STATUS_ERROR;    /* we don't expect this to happen */
    }

    sock_fd = socket(ident_serv->ai_family, ident_serv->ai_socktype,
                     ident_serv->ai_protocol);
    if (sock_fd < 0)
    {
        ereport(LOG,
                (errcode_for_socket_access(),
                 errmsg("could not create socket for Ident connection: %m")));
        ident_return = false;
        goto ident_inet_done;
    }

    /*
     * Bind to the address which the client originally contacted, otherwise
     * the ident server won't be able to match up the right connection. This
     * is necessary if the PostgreSQL server is running on an IP alias.
     */
    rc = bind(sock_fd, la->ai_addr, la->ai_addrlen);
    if (rc != 0)
    {
        ereport(LOG,
                (errcode_for_socket_access(),
                 errmsg("could not bind to local address \"%s\": %m",
                        local_addr_s)));
        ident_return = false;
        goto ident_inet_done;
    }

    rc = connect(sock_fd, ident_serv->ai_addr,
                 ident_serv->ai_addrlen);
    if (rc != 0)
    {
        ereport(LOG,
                (errcode_for_socket_access(),
                 errmsg("could not connect to Ident server at address \"%s\", port %s: %m",
                        remote_addr_s, ident_port)));
        ident_return = false;
        goto ident_inet_done;
    }

    /* The query we send to the Ident server */
    snprintf(ident_query, sizeof(ident_query), "%s,%s\r\n",
             remote_port, local_port);

    /* loop in case send is interrupted */
    do
    {
        rc = send(sock_fd, ident_query, strlen(ident_query), 0);
    } while (rc < 0 && errno == EINTR);

    if (rc < 0)
    {
        ereport(LOG,
                (errcode_for_socket_access(),
                 errmsg("could not send query to Ident server at address \"%s\", port %s: %m",
                        remote_addr_s, ident_port)));
        ident_return = false;
        goto ident_inet_done;
    }

    do
    {
        rc = recv(sock_fd, ident_response, sizeof(ident_response) - 1, 0);
    } while (rc < 0 && errno == EINTR);

    if (rc < 0)
    {
        ereport(LOG,
                (errcode_for_socket_access(),
                 errmsg("could not receive response from Ident server at address \"%s\", port %s: %m",
                        remote_addr_s, ident_port)));
        ident_return = false;
        goto ident_inet_done;
    }

    ident_response[rc] = '\0';
    ident_return = interpret_ident_response(ident_response, ident_user);
    if (!ident_return)
        ereport(LOG,
            (errmsg("invalidly formatted response from Ident server: \"%s\"",
                    ident_response)));

ident_inet_done:
    if (sock_fd >= 0)
        closesocket(sock_fd);
    pg_freeaddrinfo_all(remote_addr.addr.ss_family, ident_serv);
    pg_freeaddrinfo_all(local_addr.addr.ss_family, la);

    if (ident_return)
        /* Success! Check the usermap */
        return check_usermap(port->hba->usermap, port->user_name, ident_user, false);
    return STATUS_ERROR;
}

static bool interpret_ident_response ( const char *  ident_response,
char *  ident_user 
) [static]

Definition at line 1516 of file auth.c.

References i, IDENT_USERNAME_MAX, and pg_isblank().

Referenced by ident_inet().

{
    const char *cursor = ident_response;        /* Cursor into *ident_response */

    /*
     * Ident's response, in the telnet tradition, should end in crlf (\r\n).
     */
    if (strlen(ident_response) < 2)
        return false;
    else if (ident_response[strlen(ident_response) - 2] != '\r')
        return false;
    else
    {
        while (*cursor != ':' && *cursor != '\r')
            cursor++;           /* skip port field */

        if (*cursor != ':')
            return false;
        else
        {
            /* We're positioned to colon before response type field */
            char        response_type[80];
            int         i;      /* Index into *response_type */

            cursor++;           /* Go over colon */
            while (pg_isblank(*cursor))
                cursor++;       /* skip blanks */
            i = 0;
            while (*cursor != ':' && *cursor != '\r' && !pg_isblank(*cursor) &&
                   i < (int) (sizeof(response_type) - 1))
                response_type[i++] = *cursor++;
            response_type[i] = '\0';
            while (pg_isblank(*cursor))
                cursor++;       /* skip blanks */
            if (strcmp(response_type, "USERID") != 0)
                return false;
            else
            {
                /*
                 * It's a USERID response.  Good.  "cursor" should be pointing
                 * to the colon that precedes the operating system type.
                 */
                if (*cursor != ':')
                    return false;
                else
                {
                    cursor++;   /* Go over colon */
                    /* Skip over operating system field. */
                    while (*cursor != ':' && *cursor != '\r')
                        cursor++;
                    if (*cursor != ':')
                        return false;
                    else
                    {
                        int         i;  /* Index into *ident_user */

                        cursor++;       /* Go over colon */
                        while (pg_isblank(*cursor))
                            cursor++;   /* skip blanks */
                        /* Rest of line is user name.  Copy it over. */
                        i = 0;
                        while (*cursor != '\r' && i < IDENT_USERNAME_MAX)
                            ident_user[i++] = *cursor++;
                        ident_user[i] = '\0';
                        return true;
                    }
                }
            }
        }
    }
}

static void radius_add_attribute ( radius_packet packet,
uint8  type,
const unsigned char *  data,
int  len 
) [static]

Definition at line 2408 of file auth.c.

References radius_attribute::attribute, radius_attribute::data, elog, radius_attribute::length, radius_packet::length, RADIUS_BUFFER_SIZE, and WARNING.

Referenced by CheckRADIUSAuth().

{
    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 %d with length %d 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 recv_and_check_password_packet ( Port port  )  [static]

Definition at line 719 of file auth.c.

References md5_crypt_verify(), NULL, pfree(), recv_password_packet(), and Port::user_name.

Referenced by ClientAuthentication().

{
    char       *passwd;
    int         result;

    passwd = recv_password_packet(port);

    if (passwd == NULL)
        return STATUS_EOF;      /* client wouldn't send password */

    result = md5_crypt_verify(port, port->user_name, passwd);

    pfree(passwd);

    return result;
}

static char * recv_password_packet ( Port port  )  [static]

Definition at line 645 of file auth.c.

References buf, COMMERROR, StringInfoData::data, DEBUG5, ereport, errcode(), errmsg(), initStringInfo(), StringInfoData::len, pfree(), PG_PROTOCOL_MAJOR, pq_getbyte(), pq_getmessage(), pq_peekbyte(), and Port::proto.

Referenced by CheckRADIUSAuth(), and recv_and_check_password_packet().

{
    StringInfoData buf;

    if (PG_PROTOCOL_MAJOR(port->proto) >= 3)
    {
        /* Expect 'p' message type */
        int         mtype;

        mtype = pq_getbyte();
        if (mtype != 'p')
        {
            /*
             * If the client just disconnects without offering a password,
             * don't make a log entry.  This is legal per protocol spec and in
             * fact commonly done by psql, so complaining just clutters the
             * log.
             */
            if (mtype != EOF)
                ereport(COMMERROR,
                        (errcode(ERRCODE_PROTOCOL_VIOLATION),
                    errmsg("expected password response, got message type %d",
                           mtype)));
            return NULL;        /* EOF or bad message type */
        }
    }
    else
    {
        /* For pre-3.0 clients, avoid log entry if they just disconnect */
        if (pq_peekbyte() == EOF)
            return NULL;        /* EOF */
    }

    initStringInfo(&buf);
    if (pq_getmessage(&buf, 1000))      /* receive password */
    {
        /* EOF - pq_getmessage already logged a suitable message */
        pfree(buf.data);
        return NULL;
    }

    /*
     * Apply sanity check: password packet length should agree with length of
     * contained string.  Note it is safe to use strlen here because
     * StringInfo is guaranteed to have an appended '\0'.
     */
    if (strlen(buf.data) + 1 != buf.len)
        ereport(COMMERROR,
                (errcode(ERRCODE_PROTOCOL_VIOLATION),
                 errmsg("invalid password packet size")));

    /* Do not echo password to logs, for security. */
    ereport(DEBUG5,
            (errmsg("received password packet")));

    /*
     * Return the received string.  Note we do not attempt to do any
     * character-set conversion on it; since we don't yet know the client's
     * encoding, there wouldn't be much point.
     */
    return buf.data;
}

static void sendAuthRequest ( Port port,
AuthRequest  areq 
) [static]

Definition at line 600 of file auth.c.

References AUTH_REQ_GSS_CONT, AUTH_REQ_MD5, AUTH_REQ_OK, buf, DEBUG4, elog, Port::gss, Port::md5Salt, pq_beginmessage(), pq_endmessage(), pq_flush(), pq_sendbytes(), and pq_sendint().

Referenced by CheckRADIUSAuth(), and ClientAuthentication().

{
    StringInfoData buf;

    pq_beginmessage(&buf, 'R');
    pq_sendint(&buf, (int32) areq, sizeof(int32));

    /* Add the salt for encrypted passwords. */
    if (areq == AUTH_REQ_MD5)
        pq_sendbytes(&buf, port->md5Salt, 4);

#if defined(ENABLE_GSS) || defined(ENABLE_SSPI)

    /*
     * Add the authentication data for the next step of the GSSAPI or SSPI
     * negotiation.
     */
    else if (areq == AUTH_REQ_GSS_CONT)
    {
        if (port->gss->outbuf.length > 0)
        {
            elog(DEBUG4, "sending GSS token of length %u",
                 (unsigned int) port->gss->outbuf.length);

            pq_sendbytes(&buf, port->gss->outbuf.value, port->gss->outbuf.length);
        }
    }
#endif

    pq_endmessage(&buf);

    /*
     * Flush message so client will see it, except for AUTH_REQ_OK, which need
     * not be sent until we are ready for queries.
     */
    if (areq != AUTH_REQ_OK)
        pq_flush();
}


Variable Documentation

Definition at line 220 of file auth.c.

Referenced by _PG_init(), ClientAuthentication(), and sepgsql_init_client_label().

Definition at line 132 of file auth.c.

Definition at line 130 of file auth.c.

Definition at line 131 of file auth.c.