Please review: Authentication after fork - Mailing list pgsql-patches
From | Peter Eisentraut |
---|---|
Subject | Please review: Authentication after fork |
Date | |
Msg-id | Pine.LNX.4.30.0106161829250.755-100000@peter.localdomain Whole thread Raw |
Responses |
Re: Please review: Authentication after fork
|
List | 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@gmx.net http://funkturm.homeip.net/~peter
pgsql-patches by date: