#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"
Go to the source code of this file.
#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)
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 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_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_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().
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(); }
Definition at line 220 of file auth.c.
Referenced by _PG_init(), ClientAuthentication(), and sepgsql_init_client_label().
char* pg_krb_server_keyfile |
char* pg_krb_srvnam |