aboutsummaryrefslogtreecommitdiff
path: root/src/backend/libpq/auth.c
diff options
context:
space:
mode:
authorPeter Eisentraut <peter_e@gmx.net>2001-06-20 18:07:56 +0000
committerPeter Eisentraut <peter_e@gmx.net>2001-06-20 18:07:56 +0000
commit9b4bfbdc2cd74342d9febb74c93acad1d4de84ca (patch)
tree0239cbb8e885f8420b5ec967c928f4e6fdc5bed1 /src/backend/libpq/auth.c
parent588463a449e70aff33d799b5c9475ab94fbdf4c9 (diff)
downloadpostgresql-9b4bfbdc2cd74342d9febb74c93acad1d4de84ca.tar.gz
postgresql-9b4bfbdc2cd74342d9febb74c93acad1d4de84ca.zip
Handle reading of startup packet and authentication exchange after forking
a new postmaster child process. This should eliminate problems with authentication blocking (e.g., ident, SSL init) and also reduce problems with the accept queue filling up under heavy load. The option to send elog output to a different file per backend (postgres -o) has been disabled for now because the initialization would have to happen in a different order and it's not clear we want to keep this anyway.
Diffstat (limited to 'src/backend/libpq/auth.c')
-rw-r--r--src/backend/libpq/auth.c340
1 files changed, 112 insertions, 228 deletions
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index dcb702e9596..9d8ffa7db1c 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -8,48 +8,34 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.52 2001/03/22 03:59:30 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.53 2001/06/20 18:07:55 petere Exp $
*
*-------------------------------------------------------------------------
*/
-/*
- * INTERFACE ROUTINES
- *
- * backend (postmaster) routines:
- * be_recvauth receive authentication information
- */
-#include <sys/param.h> /* for MAXHOSTNAMELEN on most */
-#ifndef MAXHOSTNAMELEN
-#include <netdb.h> /* for MAXHOSTNAMELEN on some */
-#endif
-#include <pwd.h>
-#include <ctype.h>
+
+#include "postgres.h"
#include <sys/types.h> /* needed by in.h on Ultrix */
#include <netinet/in.h>
#include <arpa/inet.h>
-#include "postgres.h"
-
#include "libpq/auth.h"
#include "libpq/crypt.h"
#include "libpq/hba.h"
#include "libpq/libpq.h"
#include "libpq/password.h"
+#include "libpq/pqformat.h"
#include "miscadmin.h"
-static void sendAuthRequest(Port *port, AuthRequest areq, PacketDoneProc handler);
-static int handle_done_auth(void *arg, PacketLen len, void *pkt);
-static int handle_krb4_auth(void *arg, PacketLen len, void *pkt);
-static int handle_krb5_auth(void *arg, PacketLen len, void *pkt);
-static int handle_password_auth(void *arg, PacketLen len, void *pkt);
-static int readPasswordPacket(void *arg, PacketLen len, void *pkt);
-static int pg_passwordv0_recvauth(void *arg, PacketLen len, void *pkt);
+static void sendAuthRequest(Port *port, AuthRequest areq);
+
static int checkPassword(Port *port, char *user, char *password);
static int old_be_recvauth(Port *port);
static int map_old_to_new(Port *port, UserAuth old, int status);
static void auth_failed(Port *port);
+static int recv_and_check_password_packet(Port *port);
+static int recv_and_check_passwordv0(Port *port);
char *pg_krb_server_keyfile;
@@ -325,25 +311,28 @@ pg_krb5_recvauth(Port *port)
/*
* Handle a v0 password packet.
*/
-
static int
-pg_passwordv0_recvauth(void *arg, PacketLen len, void *pkt)
+recv_and_check_passwordv0(Port *port)
{
- Port *port;
+ int32 len;
+ char *buf;
PasswordPacketV0 *pp;
char *user,
*password,
*cp,
*start;
- port = (Port *) arg;
- pp = (PasswordPacketV0 *) pkt;
+ pq_getint(&len, 4);
+ len -= 4;
+ buf = palloc(len);
+ pq_getbytes(buf, len);
+
+ pp = (PasswordPacketV0 *) buf;
/*
* The packet is supposed to comprise the user name and the password
* as C strings. Be careful the check that this is the case.
*/
-
user = password = NULL;
len -= sizeof(pp->unused);
@@ -371,6 +360,7 @@ pg_passwordv0_recvauth(void *arg, PacketLen len, void *pkt)
fputs(PQerrormsg, stderr);
pqdebug("%s", PQerrormsg);
+ pfree(buf);
auth_failed(port);
}
else
@@ -385,15 +375,15 @@ pg_passwordv0_recvauth(void *arg, PacketLen len, void *pkt)
status = checkPassword(port, user, password);
+ pfree(buf);
port->auth_method = saved;
/* Adjust the result if necessary. */
-
if (map_old_to_new(port, uaPassword, status) != STATUS_OK)
auth_failed(port);
}
- return STATUS_OK; /* don't close the connection yet */
+ return STATUS_OK;
}
@@ -413,7 +403,6 @@ pg_passwordv0_recvauth(void *arg, PacketLen len, void *pkt)
static void
auth_failed(Port *port)
{
- char buffer[512];
const char *authmethod = "Unknown auth method:";
switch (port->auth_method)
@@ -441,19 +430,20 @@ auth_failed(Port *port)
break;
}
- sprintf(buffer, "%s authentication failed for user '%s'",
- authmethod, port->user);
-
- PacketSendError(&port->pktInfo, buffer);
+ elog(FATAL, "%s authentication failed for user \"%s\"",
+ authmethod, port->user);
}
+
/*
- * be_recvauth -- server demux routine for incoming authentication information
+ * Client authentication starts here. If there is an error, this
+ * function does not return and the backend process is terminated.
*/
void
-be_recvauth(Port *port)
+ClientAuthentication(Port *port)
{
+ int status = STATUS_ERROR;
/*
* Get the authentication method to use for this frontend/database
@@ -463,97 +453,77 @@ be_recvauth(Port *port)
*/
if (hba_getauthmethod(port) != STATUS_OK)
- PacketSendError(&port->pktInfo,
- "Missing or erroneous pg_hba.conf file, see postmaster log for details");
+ elog(FATAL, "Missing or erroneous pg_hba.conf file, see postmaster log for details");
+ /* Handle old style authentication. */
else if (PG_PROTOCOL_MAJOR(port->proto) == 0)
{
- /* Handle old style authentication. */
-
if (old_be_recvauth(port) != STATUS_OK)
auth_failed(port);
+ return;
}
- else
- {
- /* Handle new style authentication. */
-
- AuthRequest areq = AUTH_REQ_OK;
- PacketDoneProc auth_handler = NULL;
- switch (port->auth_method)
- {
- case uaReject:
-
- /*
- * This could have come from an explicit "reject" entry in
- * pg_hba.conf, but more likely it means there was no
- * matching entry. Take pity on the poor user and issue a
- * helpful error message. NOTE: this is not a security
- * breach, because all the info reported here is known at
- * the frontend and must be assumed known to bad guys.
- * We're merely helping out the less clueful good guys.
- * NOTE 2: libpq-be.h defines the maximum error message
- * length as 99 characters. It probably wouldn't hurt
- * anything to increase it, but there might be some client
- * out there that will fail. So, be terse.
- */
- {
- char buffer[512];
- const char *hostinfo = "localhost";
-
- if (port->raddr.sa.sa_family == AF_INET)
- hostinfo = inet_ntoa(port->raddr.in.sin_addr);
- sprintf(buffer,
- "No pg_hba.conf entry for host %s, user %s, database %s",
- hostinfo, port->user, port->database);
- PacketSendError(&port->pktInfo, buffer);
- return;
- }
- break;
-
- case uaKrb4:
- areq = AUTH_REQ_KRB4;
- auth_handler = handle_krb4_auth;
- break;
+ /* Handle new style authentication. */
- case uaKrb5:
- areq = AUTH_REQ_KRB5;
- auth_handler = handle_krb5_auth;
- break;
+ switch (port->auth_method)
+ {
+ case uaReject:
- case uaTrust:
- areq = AUTH_REQ_OK;
- auth_handler = handle_done_auth;
- break;
+ /*
+ * This could have come from an explicit "reject" entry in
+ * pg_hba.conf, but more likely it means there was no
+ * matching entry. Take pity on the poor user and issue a
+ * helpful error message. NOTE: this is not a security
+ * breach, because all the info reported here is known at
+ * the frontend and must be assumed known to bad guys.
+ * We're merely helping out the less clueful good guys.
+ */
+ {
+ const char *hostinfo = "localhost";
+
+ if (port->raddr.sa.sa_family == AF_INET)
+ hostinfo = inet_ntoa(port->raddr.in.sin_addr);
+ elog(FATAL,
+ "No pg_hba.conf entry for host %s, user %s, database %s",
+ hostinfo, port->user, port->database);
+ return;
+ }
+ break;
- case uaIdent:
- if (authident(&port->raddr.in, &port->laddr.in,
- port->user, port->auth_arg) == STATUS_OK)
- {
- areq = AUTH_REQ_OK;
- auth_handler = handle_done_auth;
- }
+ case uaKrb4:
+ sendAuthRequest(port, AUTH_REQ_KRB4);
+ status = pg_krb4_recvauth(port);
+ break;
- break;
+ case uaKrb5:
+ sendAuthRequest(port, AUTH_REQ_KRB5);
+ status = pg_krb5_recvauth(port);
+ break;
- case uaPassword:
- areq = AUTH_REQ_PASSWORD;
- auth_handler = handle_password_auth;
- break;
+ case uaIdent:
+ status = authident(&port->raddr.in, &port->laddr.in,
+ port->user, port->auth_arg);
+ break;
- case uaCrypt:
- areq = AUTH_REQ_CRYPT;
- auth_handler = handle_password_auth;
- break;
- }
+ case uaPassword:
+ sendAuthRequest(port, AUTH_REQ_PASSWORD);
+ status = recv_and_check_password_packet(port);
+ break;
- /* Tell the frontend what we want next. */
+ case uaCrypt:
+ sendAuthRequest(port, AUTH_REQ_CRYPT);
+ status = recv_and_check_password_packet(port);
+ break;
- if (auth_handler != NULL)
- sendAuthRequest(port, areq, auth_handler);
- else
- auth_failed(port);
+ case uaTrust:
+ status = STATUS_OK;
+ break;
}
+
+ if (status == STATUS_OK)
+ sendAuthRequest(port, AUTH_REQ_OK);
+ else
+ auth_failed(port);
}
@@ -562,134 +532,50 @@ be_recvauth(Port *port)
*/
static void
-sendAuthRequest(Port *port, AuthRequest areq, PacketDoneProc handler)
+sendAuthRequest(Port *port, AuthRequest areq)
{
- char *dp,
- *sp;
- int i;
- uint32 net_areq;
+ StringInfoData buf;
- /* Convert to a byte stream. */
-
- net_areq = htonl(areq);
-
- dp = port->pktInfo.pkt.ar.data;
- sp = (char *) &net_areq;
-
- *dp++ = 'R';
-
- for (i = 1; i <= 4; ++i)
- *dp++ = *sp++;
+ pq_beginmessage(&buf);
+ pq_sendbyte(&buf, 'R');
+ pq_sendint(&buf, (int32) areq, sizeof(int32));
/* Add the salt for encrypted passwords. */
-
if (areq == AUTH_REQ_CRYPT)
{
- *dp++ = port->salt[0];
- *dp++ = port->salt[1];
- i += 2;
+ pq_sendint(&buf, port->salt[0], 1);
+ pq_sendint(&buf, port->salt[1], 1);
}
- PacketSendSetup(&port->pktInfo, i, handler, (void *) port);
-}
-
-
-/*
- * Called when we have told the front end that it is authorised.
- */
-
-static int
-handle_done_auth(void *arg, PacketLen len, void *pkt)
-{
-
- /*
- * Don't generate any more traffic. This will cause the backend to
- * start.
- */
-
- return STATUS_OK;
-}
-
-
-/*
- * Called when we have told the front end that it should use Kerberos V4
- * authentication.
- */
-
-static int
-handle_krb4_auth(void *arg, PacketLen len, void *pkt)
-{
- Port *port = (Port *) arg;
-
- if (pg_krb4_recvauth(port) != STATUS_OK)
- auth_failed(port);
- else
- sendAuthRequest(port, AUTH_REQ_OK, handle_done_auth);
-
- return STATUS_OK;
-}
-
-
-/*
- * Called when we have told the front end that it should use Kerberos V5
- * authentication.
- */
-
-static int
-handle_krb5_auth(void *arg, PacketLen len, void *pkt)
-{
- Port *port = (Port *) arg;
-
- if (pg_krb5_recvauth(port) != STATUS_OK)
- auth_failed(port);
- else
- sendAuthRequest(port, AUTH_REQ_OK, handle_done_auth);
-
- return STATUS_OK;
+ pq_endmessage(&buf);
+ pq_flush();
}
-/*
- * Called when we have told the front end that it should use password
- * authentication.
- */
-
-static int
-handle_password_auth(void *arg, PacketLen len, void *pkt)
-{
- Port *port = (Port *) arg;
-
- /* Set up the read of the password packet. */
-
- PacketReceiveSetup(&port->pktInfo, readPasswordPacket, (void *) port);
-
- return STATUS_OK;
-}
-
/*
* Called when we have received the password packet.
*/
static int
-readPasswordPacket(void *arg, PacketLen len, void *pkt)
+recv_and_check_password_packet(Port *port)
{
- char password[sizeof(PasswordPacket) + 1];
- Port *port = (Port *) arg;
-
- /* Silently truncate a password that is too big. */
-
- if (len > sizeof(PasswordPacket))
- len = sizeof(PasswordPacket);
-
- StrNCpy(password, ((PasswordPacket *) pkt)->passwd, len);
-
- if (checkPassword(port, port->user, password) != STATUS_OK)
- auth_failed(port);
- else
- sendAuthRequest(port, AUTH_REQ_OK, handle_done_auth);
-
- return STATUS_OK; /* don't close the connection yet */
+ StringInfoData buf;
+ int32 len;
+ int result;
+
+ if (pq_getint(&len, 4) == EOF)
+ return STATUS_ERROR; /* client didn't want to send password */
+ initStringInfo(&buf);
+ pq_getstr(&buf);
+
+ if (DebugLvl)
+ fprintf(stderr, "received password packet with len=%d, pw=%s\n",
+ len, buf.data);
+
+ result = checkPassword(port, port->user, buf.data);
+ pfree(buf.data);
+ return result;
}
@@ -734,10 +620,8 @@ old_be_recvauth(Port *port)
break;
case STARTUP_PASSWORD_MSG:
- PacketReceiveSetup(&port->pktInfo, pg_passwordv0_recvauth,
- (void *) port);
-
- return STATUS_OK;
+ status = recv_and_check_passwordv0(port);
+ break;
default:
fprintf(stderr, "Invalid startup message type: %u\n", msgtype);
@@ -760,8 +644,8 @@ map_old_to_new(Port *port, UserAuth old, int status)
{
switch (port->auth_method)
{
- case uaCrypt:
- case uaReject:
+ case uaCrypt:
+ case uaReject:
status = STATUS_ERROR;
break;