Please review: Authentication after fork

From: Peter Eisentraut <peter_e(at)gmx(dot)net>
To: <pgsql-patches(at)postgresql(dot)org>
Subject: Please review: Authentication after fork
Date: 2001-06-16 16:46:01
Message-ID: Pine.LNX.4.30.0106161829250.755-100000@peter.localdomain
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-patches

Open issues:

* The postmaster will allow max_backends * 2 connections to exist, to
prevent unauthenticated backends to fill up the limit. When the
max_backend+1'th backend tries to register itself for the shared
resources I get:

NOTICE: SIBackendInit: no free procState slot available
psql: FATAL 1: Backend cache invalidation initialization failed
[terminates]

I'd like to catch this case earlier, to avoid having to wire in such a
fundamental setting so deeply into the resource management. But I
couldn't find a good interface to count the already-registered backends.

* Is it okay to ignore the count field in the password packet and read
the actual password like a null-terminated string? That was only there
for the postmaster way of handling incomplete packets, right?

* Maybe we need to keep the "poor man's multitasking" code in the
postmaster to allow for piecewise-arriving startup packets. Surely it
could be simplified a lot, but I didn't bother with this yet.

Index: src/backend/libpq/auth.c
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/libpq/auth.c,v
retrieving revision 1.52
diff -c -r1.52 auth.c
*** src/backend/libpq/auth.c 2001/03/22 03:59:30 1.52
--- src/backend/libpq/auth.c 2001/06/16 16:38:42
***************
*** 12,55 ****
*
*-------------------------------------------------------------------------
*/
! /*
! * 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 <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 "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 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);

char *pg_krb_server_keyfile;

--- 12,41 ----
*
*-------------------------------------------------------------------------
*/
!
! #include "postgres.h"

#include <sys/types.h> /* needed by in.h on Ultrix */
#include <netinet/in.h>
#include <arpa/inet.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);

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,349 ****
/*
* Handle a v0 password packet.
*/
-
static int
! pg_passwordv0_recvauth(void *arg, PacketLen len, void *pkt)
{
! Port *port;
PasswordPacketV0 *pp;
char *user,
*password,
*cp,
*start;

! port = (Port *) arg;
! pp = (PasswordPacketV0 *) pkt;

/*
* 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);
--- 311,338 ----
/*
* Handle a v0 password packet.
*/
static int
! recv_and_check_passwordv0(Port *port)
{
! int32 len;
! char *buf;
PasswordPacketV0 *pp;
char *user,
*password,
*cp,
*start;
+
+ 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,376 ****
--- 360,366 ----
fputs(PQerrormsg, stderr);
pqdebug("%s", PQerrormsg);

+ pfree(buf);
auth_failed(port);
}
else
***************
*** 385,399 ****

status = checkPassword(port, user, password);

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 */
}

--- 375,389 ----

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

***************
*** 413,419 ****
static void
auth_failed(Port *port)
{
- char buffer[512];
const char *authmethod = "Unknown auth method:";

switch (port->auth_method)
--- 403,408 ----
***************
*** 440,459 ****
authmethod = "Password";
break;
}
-
- sprintf(buffer, "%s authentication failed for user '%s'",
- authmethod, port->user);

! PacketSendError(&port->pktInfo, buffer);
}

/*
! * be_recvauth -- server demux routine for incoming authentication information
*/
void
! be_recvauth(Port *port)
{

/*
* Get the authentication method to use for this frontend/database
--- 429,449 ----
authmethod = "Password";
break;
}

! elog(FATAL, "%s authentication failed for user '%s'",
! authmethod, port->user);
}

+
/*
! * Client authentication starts here. If there is an error, this
! * function does not return and the backend process is terminated.
*/
void
! ClientAuthentication(Port *port)
{
+ int status;

/*
* Get the authentication method to use for this frontend/database
***************
*** 463,559 ****
*/

if (hba_getauthmethod(port) != STATUS_OK)
! PacketSendError(&port->pktInfo,
! "Missing or erroneous pg_hba.conf file, see postmaster log for details");

else if (PG_PROTOCOL_MAJOR(port->proto) == 0)
{
- /* Handle old style authentication. */
-
if (old_be_recvauth(port) != STATUS_OK)
auth_failed(port);
}
- 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;

! case uaKrb5:
! areq = AUTH_REQ_KRB5;
! auth_handler = handle_krb5_auth;
! break;

! case uaTrust:
! areq = AUTH_REQ_OK;
! auth_handler = handle_done_auth;
! 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;
! }

! break;

! case uaPassword:
! areq = AUTH_REQ_PASSWORD;
! auth_handler = handle_password_auth;
! break;

! case uaCrypt:
! areq = AUTH_REQ_CRYPT;
! auth_handler = handle_password_auth;
! break;
! }

! /* Tell the frontend what we want next. */

! if (auth_handler != NULL)
! sendAuthRequest(port, areq, auth_handler);
! else
! auth_failed(port);
}
}

--- 453,529 ----
*/

if (hba_getauthmethod(port) != STATUS_OK)
! 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)
{
if (old_be_recvauth(port) != STATUS_OK)
auth_failed(port);
+ return;
}

! /* Handle new style authentication. */

! 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.
! */
! {
! 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 uaKrb4:
! sendAuthRequest(port, AUTH_REQ_KRB4);
! status = pg_krb4_recvauth(port);
! break;

! case uaKrb5:
! sendAuthRequest(port, AUTH_REQ_KRB5);
! status = pg_krb5_recvauth(port);
! break;

! case uaIdent:
! status = authident(&port->raddr.in, &port->laddr.in,
! port->user, port->auth_arg);
! break;

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

! case uaCrypt:
! sendAuthRequest(port, AUTH_REQ_CRYPT);
! status = recv_and_check_password_packet(port);
! break;

! case uaTrust:
! status = STATUS_OK;
! break;
}
+
+ if (status == STATUS_OK)
+ sendAuthRequest(port, AUTH_REQ_OK);
+ else
+ auth_failed(port);
}

***************
*** 562,695 ****
*/

static void
! sendAuthRequest(Port *port, AuthRequest areq, PacketDoneProc handler)
{
! char *dp,
! *sp;
! int i;
! uint32 net_areq;
!
! /* 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++;
-
/* Add the salt for encrypted passwords. */
-
if (areq == AUTH_REQ_CRYPT)
{
! *dp++ = port->salt[0];
! *dp++ = port->salt[1];
! i += 2;
}

! 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;
- }
-
-
/*
- * 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)
{
! 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 */
}

--- 532,576 ----
*/

static void
! sendAuthRequest(Port *port, AuthRequest areq)
{
! StringInfoData buf;

! 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)
{
! pq_sendint(&buf, port->salt[0], 1);
! pq_sendint(&buf, port->salt[1], 1);
}

! pq_endmessage(&buf);
! pq_flush();
}

/*
* Called when we have received the password packet.
*/

static int
! recv_and_check_password_packet(Port *port)
{
! StringInfoData buf;
! int32 len;
! int result;
!
! pq_getint(&len, 4);
! initStringInfo(&buf);
! pq_getstr(&buf);
!
! result = checkPassword(port, port->user, buf.data);
! pfree(buf.data);
! return result;
}

***************
*** 734,743 ****
break;

case STARTUP_PASSWORD_MSG:
! PacketReceiveSetup(&port->pktInfo, pg_passwordv0_recvauth,
! (void *) port);
!
! return STATUS_OK;

default:
fprintf(stderr, "Invalid startup message type: %u\n", msgtype);
--- 615,622 ----
break;

case STARTUP_PASSWORD_MSG:
! status = recv_and_check_passwordv0(port);
! break;

default:
fprintf(stderr, "Invalid startup message type: %u\n", msgtype);
***************
*** 760,767 ****
{
switch (port->auth_method)
{
! case uaCrypt:
! case uaReject:
status = STATUS_ERROR;
break;

--- 639,646 ----
{
switch (port->auth_method)
{
! case uaCrypt:
! case uaReject:
status = STATUS_ERROR;
break;

Index: src/backend/postmaster/postmaster.c
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/postmaster/postmaster.c,v
retrieving revision 1.219
diff -c -r1.219 postmaster.c
*** src/backend/postmaster/postmaster.c 2001/06/12 22:54:05 1.219
--- src/backend/postmaster/postmaster.c 2001/06/16 16:38:46
***************
*** 1200,1209 ****
return STATUS_OK; /* don't close the connection yet */
}

- /* Start the authentication itself. */
-
- be_recvauth(port);
-
return STATUS_OK; /* don't close the connection yet */
}

--- 1200,1205 ----
***************
*** 1280,1287 ****
return "The Data Base System is starting up";
if (FatalError)
return "The Data Base System is in recovery mode";
! /* Can't start backend if max backend count is exceeded. */
! if (CountChildren() >= MaxBackends)
return "Sorry, too many clients already";

return NULL;
--- 1276,1284 ----
return "The Data Base System is starting up";
if (FatalError)
return "The Data Base System is in recovery mode";
! /* We allow more connections than we can have backends here
! because some might fail authentication. */
! if (CountChildren() >= MaxBackends * 2)
return "Sorry, too many clients already";

return NULL;
Index: src/backend/tcop/postgres.c
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/tcop/postgres.c,v
retrieving revision 1.220
diff -c -r1.220 postgres.c
*** src/backend/tcop/postgres.c 2001/06/12 22:54:06 1.220
--- src/backend/tcop/postgres.c 2001/06/16 16:38:50
***************
*** 45,50 ****
--- 45,51 ----
#include "libpq/pqformat.h"
#include "libpq/pqsignal.h"
#include "miscadmin.h"
+ #include "libpq/auth.h"
#include "nodes/print.h"
#include "optimizer/cost.h"
#include "optimizer/planner.h"
***************
*** 1147,1152 ****
--- 1148,1156 ----
}

SetProcessingMode(InitProcessing);
+
+ if (IsUnderPostmaster)
+ ClientAuthentication(MyProcPort); /* might not return */

/*
* Set default values for command-line options.
Index: src/include/libpq/auth.h
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/src/include/libpq/auth.h,v
retrieving revision 1.16
diff -c -r1.16 auth.h
*** src/include/libpq/auth.h 2001/03/22 04:00:47 1.16
--- src/include/libpq/auth.h 2001/06/16 16:38:51
***************
*** 21,27 ****
*----------------------------------------------------------------
*/

! void be_recvauth(Port *port);

#define PG_KRB4_VERSION "PGVER4.1" /* at most KRB_SENDAUTH_VLEN chars */
#define PG_KRB5_VERSION "PGVER5.1"
--- 21,27 ----
*----------------------------------------------------------------
*/

! void ClientAuthentication(Port *port);

#define PG_KRB4_VERSION "PGVER4.1" /* at most KRB_SENDAUTH_VLEN chars */
#define PG_KRB5_VERSION "PGVER5.1"
===THE END

--
Peter Eisentraut peter_e(at)gmx(dot)net http://funkturm.homeip.net/~peter

Responses

Browse pgsql-patches by date

  From Date Subject
Next Message Tom Lane 2001-06-16 17:28:26 Re: Please review: Authentication after fork
Previous Message Marko Kreen 2001-06-15 19:35:15 Re: switch for disabling ecpg & pgeasy