aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/init/postinit.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2009-08-29 19:26:52 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2009-08-29 19:26:52 +0000
commite710b65c1c56ca7b91f662c63d37ff2e72862a94 (patch)
tree35f0571a317a0f6d9a0e50a84d7d4157a811807d /src/backend/utils/init/postinit.c
parent585806cb9fa0deeec94c8d76c20316ad0dfdd7eb (diff)
downloadpostgresql-e710b65c1c56ca7b91f662c63d37ff2e72862a94.tar.gz
postgresql-e710b65c1c56ca7b91f662c63d37ff2e72862a94.zip
Remove the use of the pg_auth flat file for client authentication.
(That flat file is now completely useless, but removal will come later.) To do this, postpone client authentication into the startup transaction that's run by InitPostgres. We still collect the startup packet and do SSL initialization (if needed) at the same time we did before. The AuthenticationTimeout is applied separately to startup packet collection and the actual authentication cycle. (This is a bit annoying, since it means a couple extra syscalls; but the signal handling requirements inside and outside a transaction are sufficiently different that it seems best to treat the timeouts as completely independent.) A small security disadvantage is that if the given database name is invalid, this will be reported to the client before any authentication happens. We could work around that by connecting to database "postgres" instead, but consensus seems to be that it's not worth introducing such surprising behavior. Processing of all command-line switches and GUC options received from the client is now postponed until after authentication. This means that PostAuthDelay is much less useful than it used to be --- if you need to investigate problems during InitPostgres you'll have to set PreAuthDelay instead. However, allowing an unauthenticated user to set any GUC options whatever seems a bit too risky, so we'll live with that.
Diffstat (limited to 'src/backend/utils/init/postinit.c')
-rw-r--r--src/backend/utils/init/postinit.c104
1 files changed, 102 insertions, 2 deletions
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index bd8598ce13d..11eeec353c9 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -8,13 +8,14 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/init/postinit.c,v 1.194 2009/08/12 20:53:30 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/init/postinit.c,v 1.195 2009/08/29 19:26:51 tgl Exp $
*
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
+#include <ctype.h>
#include <fcntl.h>
#include <unistd.h>
@@ -27,6 +28,7 @@
#include "catalog/pg_authid.h"
#include "catalog/pg_database.h"
#include "catalog/pg_tablespace.h"
+#include "libpq/auth.h"
#include "libpq/libpq-be.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
@@ -54,6 +56,7 @@
static HeapTuple GetDatabaseTuple(const char *dbname);
static HeapTuple GetDatabaseTupleByOid(Oid dboid);
+static void PerformAuthentication(Port *port);
static void CheckMyDatabase(const char *name, bool am_superuser);
static void InitCommunication(void);
static void ShutdownPostgres(int code, Datum arg);
@@ -159,6 +162,66 @@ GetDatabaseTupleByOid(Oid dboid)
/*
+ * PerformAuthentication -- authenticate a remote client
+ *
+ * returns: nothing. Will not return at all if there's any failure.
+ */
+static void
+PerformAuthentication(Port *port)
+{
+ /* This should be set already, but let's make sure */
+ ClientAuthInProgress = true; /* limit visibility of log messages */
+
+ /*
+ * In EXEC_BACKEND case, we didn't inherit the contents of pg_hba.conf
+ * etcetera from the postmaster, and have to load them ourselves. Note
+ * we are loading them into the startup transaction's memory context,
+ * not PostmasterContext, but that shouldn't matter.
+ *
+ * FIXME: [fork/exec] Ugh. Is there a way around this overhead?
+ */
+#ifdef EXEC_BACKEND
+ if (!load_hba())
+ {
+ /*
+ * It makes no sense to continue if we fail to load the HBA file,
+ * since there is no way to connect to the database in this case.
+ */
+ ereport(FATAL,
+ (errmsg("could not load pg_hba.conf")));
+ }
+ load_ident();
+#endif
+
+ /*
+ * Set up a timeout in case a buggy or malicious client fails to respond
+ * during authentication. Since we're inside a transaction and might do
+ * database access, we have to use the statement_timeout infrastructure.
+ */
+ if (!enable_sig_alarm(AuthenticationTimeout * 1000, true))
+ elog(FATAL, "could not set timer for authorization timeout");
+
+ /*
+ * Now perform authentication exchange.
+ */
+ ClientAuthentication(port); /* might not return, if failure */
+
+ /*
+ * Done with authentication. Disable the timeout, and log if needed.
+ */
+ if (!disable_sig_alarm(true))
+ elog(FATAL, "could not disable timer for authorization timeout");
+
+ if (Log_connections)
+ ereport(LOG,
+ (errmsg("connection authorized: user=%s database=%s",
+ port->user_name, port->database_name)));
+
+ ClientAuthInProgress = false; /* client_min_messages is active now */
+}
+
+
+/*
* CheckMyDatabase -- fetch information from the pg_database entry for our DB
*/
static void
@@ -330,6 +393,38 @@ InitCommunication(void)
/*
+ * pg_split_opts -- split a string of options and append it to an argv array
+ *
+ * NB: the input string is destructively modified! Also, caller is responsible
+ * for ensuring the argv array is large enough. The maximum possible number
+ * of arguments added by this routine is (strlen(optstr) + 1) / 2.
+ *
+ * Since no current POSTGRES arguments require any quoting characters,
+ * we can use the simple-minded tactic of assuming each set of space-
+ * delimited characters is a separate argv element.
+ *
+ * If you don't like that, well, we *used* to pass the whole option string
+ * as ONE argument to execl(), which was even less intelligent...
+ */
+void
+pg_split_opts(char **argv, int *argcp, char *optstr)
+{
+ while (*optstr)
+ {
+ while (isspace((unsigned char) *optstr))
+ optstr++;
+ if (*optstr == '\0')
+ break;
+ argv[(*argcp)++] = optstr;
+ while (*optstr && !isspace((unsigned char) *optstr))
+ optstr++;
+ if (*optstr)
+ *optstr++ = '\0';
+ }
+}
+
+
+/*
* Early initialization of a backend (either standalone or under postmaster).
* This happens even before InitPostgres.
*
@@ -388,6 +483,8 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
char *fullpath;
char dbname[NAMEDATALEN];
+ elog(DEBUG3, "InitPostgres");
+
/*
* Add my PGPROC struct to the ProcArray.
*
@@ -599,7 +696,8 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
RelationCacheInitializePhase3();
/*
- * Figure out our postgres user id, and see if we are a superuser.
+ * Perform client authentication if necessary, then figure out our
+ * postgres user id, and see if we are a superuser.
*
* In standalone mode and in the autovacuum process, we use a fixed id,
* otherwise we figure it out from the authenticated user name.
@@ -623,6 +721,8 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
else
{
/* normal multiuser case */
+ Assert(MyProcPort != NULL);
+ PerformAuthentication(MyProcPort);
InitializeSessionUserId(username);
am_superuser = superuser();
}