*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/libpq/auth.c,v 1.183 2009/06/25 11:30:08 mha Exp $
+ * $PostgreSQL: pgsql/src/backend/libpq/auth.c,v 1.184 2009/08/29 19:26:51 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "libpq/ip.h"
#include "libpq/libpq.h"
#include "libpq/pqformat.h"
+#include "miscadmin.h"
#include "storage/ipc.h"
+
/*----------------------------------------------------------------
* Global authentication functions
*----------------------------------------------------------------
errmsg("missing or erroneous pg_hba.conf file"),
errhint("See server log for details.")));
+ /*
+ * 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
sendAuthRequest(port, AUTH_REQ_OK);
else
auth_failed(port, status);
+
+ /* Done with authentication, so we should turn off immediate interrupts */
+ ImmediateInterruptOK = false;
}
char *kusername;
char *cp;
- if (get_role_line(port->user_name) == NULL)
- return STATUS_ERROR;
-
ret = pg_krb5_init(port);
if (ret != STATUS_OK)
return ret;
{
char ident_user[IDENT_USERNAME_MAX + 1];
- if (get_role_line(port->user_name) == NULL)
- return STATUS_ERROR;
-
switch (port->raddr.addr.ss_family)
{
case AF_INET:
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/backend/libpq/crypt.c,v 1.77 2009/01/01 17:23:42 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/libpq/crypt.c,v 1.78 2009/08/29 19:26:51 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include <crypt.h>
#endif
+#include "catalog/pg_authid.h"
#include "libpq/crypt.h"
#include "libpq/md5.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/syscache.h"
int
md5_crypt_verify(const Port *port, const char *role, char *client_pass)
{
- char *shadow_pass = NULL,
- *valuntil = NULL,
- *crypt_pwd;
int retval = STATUS_ERROR;
- List **line;
- ListCell *token;
+ char *shadow_pass,
+ *crypt_pwd;
+ TimestampTz vuntil = 0;
char *crypt_client_pass = client_pass;
+ HeapTuple roleTup;
+ Datum datum;
+ bool isnull;
+
+ /*
+ * Disable immediate interrupts while doing database access. (Note
+ * we don't bother to turn this back on if we hit one of the failure
+ * conditions, since we can expect we'll just exit right away anyway.)
+ */
+ ImmediateInterruptOK = false;
- if ((line = get_role_line(role)) == NULL)
- return STATUS_ERROR;
+ /* Get role info from pg_authid */
+ roleTup = SearchSysCache(AUTHNAME,
+ PointerGetDatum(role),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(roleTup))
+ return STATUS_ERROR; /* no such user */
- /* Skip over rolename */
- token = list_head(*line);
- if (token)
- token = lnext(token);
- if (token)
+ datum = SysCacheGetAttr(AUTHNAME, roleTup,
+ Anum_pg_authid_rolpassword, &isnull);
+ if (isnull)
{
- shadow_pass = (char *) lfirst(token);
- token = lnext(token);
- if (token)
- valuntil = (char *) lfirst(token);
+ ReleaseSysCache(roleTup);
+ return STATUS_ERROR; /* user has no password */
}
+ shadow_pass = TextDatumGetCString(datum);
+
+ datum = SysCacheGetAttr(AUTHNAME, roleTup,
+ Anum_pg_authid_rolvaliduntil, &isnull);
+ if (!isnull)
+ vuntil = DatumGetTimestampTz(datum);
- if (shadow_pass == NULL || *shadow_pass == '\0')
- return STATUS_ERROR;
+ ReleaseSysCache(roleTup);
+
+ if (*shadow_pass == '\0')
+ return STATUS_ERROR; /* empty password */
+
+ /* Re-enable immediate response to SIGTERM/SIGINT/timeout interrupts */
+ ImmediateInterruptOK = true;
+ /* And don't forget to detect one that already arrived */
+ CHECK_FOR_INTERRUPTS();
/*
* Compare with the encrypted or plain password depending on the
if (strcmp(crypt_client_pass, crypt_pwd) == 0)
{
/*
- * Password OK, now check to be sure we are not past valuntil
+ * Password OK, now check to be sure we are not past rolvaliduntil
*/
- if (valuntil == NULL || *valuntil == '\0')
+ if (isnull)
retval = STATUS_OK;
+ else if (vuntil < GetCurrentTimestamp())
+ retval = STATUS_ERROR;
else
- {
- TimestampTz vuntil;
-
- vuntil = DatumGetTimestampTz(DirectFunctionCall3(timestamptz_in,
- CStringGetDatum(valuntil),
- ObjectIdGetDatum(InvalidOid),
- Int32GetDatum(-1)));
-
- if (vuntil < GetCurrentTimestamp())
- retval = STATUS_ERROR;
- else
- retval = STATUS_OK;
- }
+ retval = STATUS_OK;
}
if (port->hba->auth_method == uaMD5)
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.188 2009/06/24 13:39:42 mha Exp $
+ * $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.189 2009/08/29 19:26:51 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "libpq/libpq.h"
#include "regex/regex.h"
#include "storage/fd.h"
-#include "utils/flatfiles.h"
+#include "utils/acl.h"
#include "utils/guc.h"
-
+#include "utils/lsyscache.h"
#define atooid(x) ((Oid) strtoul((x), NULL, 10))
#define MAX_TOKEN 256
-/* pre-parsed content of HBA config file */
+/* pre-parsed content of HBA config file: list of HbaLine structs */
static List *parsed_hba_lines = NIL;
/*
- * These variables hold the pre-parsed contents of the ident
- * configuration files, as well as the flat auth file.
- * Each is a list of sublists, one sublist for
- * each (non-empty, non-comment) line of the file. Each sublist's
- * first item is an integer line number (so we can give somewhat-useful
- * location info in error messages). Remaining items are palloc'd strings,
- * one string per token on the line. Note there will always be at least
- * one token, since blank lines are not entered in the data structure.
+ * These variables hold the pre-parsed contents of the ident usermap
+ * configuration file. ident_lines is a list of sublists, one sublist for
+ * each (non-empty, non-comment) line of the file. The sublist items are
+ * palloc'd strings, one string per token on the line. Note there will always
+ * be at least one token, since blank lines are not entered in the data
+ * structure. ident_line_nums is an integer list containing the actual line
+ * number for each line represented in ident_lines.
*/
-
-/* pre-parsed content of ident usermap file and corresponding line #s */
static List *ident_lines = NIL;
static List *ident_line_nums = NIL;
-/* pre-parsed content of flat auth file and corresponding line #s */
-static List *role_lines = NIL;
-static List *role_line_nums = NIL;
-
-/* sorted entries so we can do binary search lookups */
-static List **role_sorted = NULL; /* sorted role list, for bsearch() */
-static int role_length;
static void tokenize_file(const char *filename, FILE *file,
List **lines, List **line_nums);
}
}
-/*
- * Compare two lines based on their role/member names.
- *
- * Used for bsearch() lookup.
- */
-static int
-role_bsearch_cmp(const void *role, const void *list)
-{
- char *role2 = linitial(*(List **) list);
-
- return strcmp(role, role2);
-}
-
-
-/*
- * Lookup a role name in the pg_auth file
- */
-List **
-get_role_line(const char *role)
-{
- /* On some versions of Solaris, bsearch of zero items dumps core */
- if (role_length == 0)
- return NULL;
-
- return (List **) bsearch((void *) role,
- (void *) role_sorted,
- role_length,
- sizeof(List *),
- role_bsearch_cmp);
-}
-
/*
* Does user belong to role?
*
- * user is always the name given as the attempted login identifier.
+ * userid is the OID of the role given as the attempted login identifier.
* We check to see if it is a member of the specified role name.
*/
static bool
-is_member(const char *user, const char *role)
+is_member(Oid userid, const char *role)
{
- List **line;
- ListCell *line_item;
+ Oid roleid;
- if ((line = get_role_line(user)) == NULL)
+ if (!OidIsValid(userid))
return false; /* if user not exist, say "no" */
- /* A user always belongs to its own role */
- if (strcmp(user, role) == 0)
- return true;
+ roleid = get_roleid(role);
- /*
- * skip over the role name, password, valuntil, examine all the membership
- * entries
- */
- if (list_length(*line) < 4)
- return false;
- for_each_cell(line_item, lnext(lnext(lnext(list_head(*line)))))
- {
- if (strcmp((char *) lfirst(line_item), role) == 0)
- return true;
- }
+ if (!OidIsValid(roleid))
+ return false; /* if target role not exist, say "no" */
- return false;
+ /* See if user is directly or indirectly a member of role */
+ return is_member_of_role(userid, roleid);
}
/*
* and so it doesn't matter that we clobber the stored hba info.
*/
static bool
-check_role(const char *role, char *param_str)
+check_role(const char *role, Oid roleid, char *param_str)
{
char *tok;
{
if (tok[0] == '+')
{
- if (is_member(role, tok + 1))
+ if (is_member(roleid, tok + 1))
return true;
}
else if (strcmp(tok, role) == 0 ||
* and so it doesn't matter that we clobber the stored hba info.
*/
static bool
-check_db(const char *dbname, const char *role, char *param_str)
+check_db(const char *dbname, const char *role, Oid roleid, char *param_str)
{
char *tok;
else if (strcmp(tok, "samegroup\n") == 0 ||
strcmp(tok, "samerole\n") == 0)
{
- if (is_member(role, dbname))
+ if (is_member(roleid, dbname))
return true;
}
else if (strcmp(tok, dbname) == 0)
static bool
check_hba(hbaPort *port)
{
+ Oid roleid;
ListCell *line;
HbaLine *hba;
+ /* Get the target role's OID. Note we do not error out for bad role. */
+ roleid = get_roleid(port->user_name);
+
foreach(line, parsed_hba_lines)
{
hba = (HbaLine *) lfirst(line);
} /* != ctLocal */
/* Check database and role */
- if (!check_db(port->database_name, port->user_name, hba->database))
+ if (!check_db(port->database_name, port->user_name, roleid,
+ hba->database))
continue;
- if (!check_role(port->user_name, hba->role))
+ if (!check_role(port->user_name, roleid, hba->role))
continue;
/* Found a record that matched! */
*/
}
-
-/*
- * Load role/password mapping file
- */
-void
-load_role(void)
-{
- char *filename;
- FILE *role_file;
-
- /* Discard any old data */
- if (role_lines || role_line_nums)
- free_lines(&role_lines, &role_line_nums);
- if (role_sorted)
- pfree(role_sorted);
- role_sorted = NULL;
- role_length = 0;
-
- /* Read in the file contents */
- filename = auth_getflatfilename();
- role_file = AllocateFile(filename, "r");
-
- if (role_file == NULL)
- {
- /* no complaint if not there */
- if (errno != ENOENT)
- ereport(LOG,
- (errcode_for_file_access(),
- errmsg("could not open file \"%s\": %m", filename)));
- pfree(filename);
- return;
- }
-
- tokenize_file(filename, role_file, &role_lines, &role_line_nums);
-
- FreeFile(role_file);
- pfree(filename);
-
- /* create array for binary searching */
- role_length = list_length(role_lines);
- if (role_length)
- {
- int i = 0;
- ListCell *line;
-
- /* We assume the flat file was written already-sorted */
- role_sorted = palloc(role_length * sizeof(List *));
- foreach(line, role_lines)
- role_sorted[i++] = lfirst(line);
- }
-}
-
/*
* Free the contents of a hba record
*/
* as Postgres user "pgrole" according to usermap "usermap_name".
*
* Special case: Usermap NULL, equivalent to what was previously called
- * "sameuser" or "samerole", don't look in the usermap
+ * "sameuser" or "samerole", means don't look in the usermap
* file. That's an implied map where "pgrole" must be identical to
* "ident_user" in order to be authorized.
*
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/libpq/pqsignal.c,v 1.45 2009/01/01 17:23:42 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/libpq/pqsignal.c,v 1.46 2009/08/29 19:26:51 tgl Exp $
*
* NOTES
* This shouldn't be in libpq, but the monitor and some other
#ifdef HAVE_SIGPROCMASK
sigset_t UnBlockSig,
BlockSig,
- AuthBlockSig;
+ StartupBlockSig;
#else
int UnBlockSig,
BlockSig,
- AuthBlockSig;
+ StartupBlockSig;
#endif
/*
- * Initialize BlockSig, UnBlockSig, and AuthBlockSig.
+ * Initialize BlockSig, UnBlockSig, and StartupBlockSig.
*
* BlockSig is the set of signals to block when we are trying to block
* signals. This includes all signals we normally expect to get, but NOT
* signals that should never be turned off.
*
- * AuthBlockSig is the set of signals to block during authentication;
- * it's essentially BlockSig minus SIGTERM, SIGQUIT, SIGALRM.
+ * StartupBlockSig is the set of signals to block during startup packet
+ * collection; it's essentially BlockSig minus SIGTERM, SIGQUIT, SIGALRM.
*
* UnBlockSig is the set of signals to block when we don't want to block
* signals (is this ever nonzero??)
/* First set all signals, then clear some. */
sigfillset(&BlockSig);
- sigfillset(&AuthBlockSig);
+ sigfillset(&StartupBlockSig);
/*
* Unmark those signals that should never be blocked. Some of these signal
*/
#ifdef SIGTRAP
sigdelset(&BlockSig, SIGTRAP);
- sigdelset(&AuthBlockSig, SIGTRAP);
+ sigdelset(&StartupBlockSig, SIGTRAP);
#endif
#ifdef SIGABRT
sigdelset(&BlockSig, SIGABRT);
- sigdelset(&AuthBlockSig, SIGABRT);
+ sigdelset(&StartupBlockSig, SIGABRT);
#endif
#ifdef SIGILL
sigdelset(&BlockSig, SIGILL);
- sigdelset(&AuthBlockSig, SIGILL);
+ sigdelset(&StartupBlockSig, SIGILL);
#endif
#ifdef SIGFPE
sigdelset(&BlockSig, SIGFPE);
- sigdelset(&AuthBlockSig, SIGFPE);
+ sigdelset(&StartupBlockSig, SIGFPE);
#endif
#ifdef SIGSEGV
sigdelset(&BlockSig, SIGSEGV);
- sigdelset(&AuthBlockSig, SIGSEGV);
+ sigdelset(&StartupBlockSig, SIGSEGV);
#endif
#ifdef SIGBUS
sigdelset(&BlockSig, SIGBUS);
- sigdelset(&AuthBlockSig, SIGBUS);
+ sigdelset(&StartupBlockSig, SIGBUS);
#endif
#ifdef SIGSYS
sigdelset(&BlockSig, SIGSYS);
- sigdelset(&AuthBlockSig, SIGSYS);
+ sigdelset(&StartupBlockSig, SIGSYS);
#endif
#ifdef SIGCONT
sigdelset(&BlockSig, SIGCONT);
- sigdelset(&AuthBlockSig, SIGCONT);
+ sigdelset(&StartupBlockSig, SIGCONT);
#endif
-/* Signals unique to Auth */
+/* Signals unique to startup */
#ifdef SIGQUIT
- sigdelset(&AuthBlockSig, SIGQUIT);
+ sigdelset(&StartupBlockSig, SIGQUIT);
#endif
#ifdef SIGTERM
- sigdelset(&AuthBlockSig, SIGTERM);
+ sigdelset(&StartupBlockSig, SIGTERM);
#endif
#ifdef SIGALRM
- sigdelset(&AuthBlockSig, SIGALRM);
+ sigdelset(&StartupBlockSig, SIGALRM);
#endif
#else
/* Set the signals we want. */
sigmask(SIGINT) | sigmask(SIGUSR1) |
sigmask(SIGUSR2) | sigmask(SIGCHLD) |
sigmask(SIGWINCH) | sigmask(SIGFPE);
- AuthBlockSig = sigmask(SIGHUP) |
+ StartupBlockSig = sigmask(SIGHUP) |
sigmask(SIGINT) | sigmask(SIGUSR1) |
sigmask(SIGUSR2) | sigmask(SIGCHLD) |
sigmask(SIGWINCH) | sigmask(SIGFPE);
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.592 2009/08/28 18:23:53 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.593 2009/08/29 19:26:51 tgl Exp $
*
* NOTES
*
static void pmdie(SIGNAL_ARGS);
static void reaper(SIGNAL_ARGS);
static void sigusr1_handler(SIGNAL_ARGS);
+static void startup_die(SIGNAL_ARGS);
static void dummy_handler(SIGNAL_ARGS);
static void CleanupBackend(int pid, int exitstatus);
static void HandleChildCrash(int pid, int exitstatus, const char *procname);
if (proto == CANCEL_REQUEST_CODE)
{
processCancelRequest(port, buf);
- return 127; /* XXX */
+ /* Not really an error, but we don't want to proceed further */
+ return STATUS_ERROR;
}
if (proto == NEGOTIATE_SSL_CODE && !SSLdone)
/*
* Now fetch parameters out of startup packet and save them into the Port
* structure. All data structures attached to the Port struct must be
- * allocated in TopMemoryContext so that they won't disappear when we pass
- * them to PostgresMain (see BackendRun). We need not worry about leaking
- * this storage on failure, since we aren't in the postmaster process
- * anymore.
+ * allocated in TopMemoryContext so that they will remain available in
+ * a running backend (even after PostmasterContext is destroyed). We need
+ * not worry about leaking this storage on failure, since we aren't in the
+ * postmaster process anymore.
*/
oldcontext = MemoryContextSwitchTo(TopMemoryContext);
FatalError = false;
pmState = PM_RUN;
- /*
- * Load the flat authorization file into postmaster's cache. The
- * startup process has recomputed this from the database contents,
- * so we wait till it finishes before loading it.
- */
- load_role();
-
/*
* Crank up the background writer, if we didn't do that already
* when we entered consistent recovery state. It doesn't matter
/* Close the postmaster's sockets */
ClosePostmasterPorts(false);
- /* Perform additional initialization and client authentication */
+ /* Perform additional initialization and collect startup packet */
BackendInitialize(port);
/* And run the backend */
}
-/*
- * split_opts -- split a string of options and append it to an argv array
- *
- * NB: the string is destructively modified!
- *
- * 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...
- */
-static void
-split_opts(char **argv, int *argcp, char *s)
-{
- while (s && *s)
- {
- while (isspace((unsigned char) *s))
- ++s;
- if (*s == '\0')
- break;
- argv[(*argcp)++] = s;
- while (*s && !isspace((unsigned char) *s))
- ++s;
- if (*s)
- *s++ = '\0';
- }
-}
-
-
/*
* BackendInitialize -- initialize an interactive (postmaster-child)
- * backend process, and perform client authentication.
+ * backend process, and collect the client's startup packet.
*
* returns: nothing. Will not return at all if there's any failure.
*
/*
* PreAuthDelay is a debugging aid for investigating problems in the
* authentication cycle: it can be set in postgresql.conf to allow time to
- * attach to the newly-forked backend with a debugger. (See also the -W
- * backend switch, which we allow clients to pass through PGOPTIONS, but
+ * attach to the newly-forked backend with a debugger. (See also
+ * PostAuthDelay, which we allow clients to pass through PGOPTIONS, but
* it is not honored until after authentication.)
*/
if (PreAuthDelay > 0)
pg_usleep(PreAuthDelay * 1000000L);
+ /* This flag will remain set until InitPostgres finishes authentication */
ClientAuthInProgress = true; /* limit visibility of log messages */
/* save process start time */
#endif
/*
- * We arrange for a simple exit(1) if we receive SIGTERM or SIGQUIT during
- * any client authentication related communication. Otherwise the
+ * We arrange for a simple exit(1) if we receive SIGTERM or SIGQUIT
+ * or timeout while trying to collect the startup packet. Otherwise the
* postmaster cannot shutdown the database FAST or IMMED cleanly if a
- * buggy client blocks a backend during authentication.
+ * buggy client fails to send the packet promptly.
*/
- pqsignal(SIGTERM, authdie);
- pqsignal(SIGQUIT, authdie);
- pqsignal(SIGALRM, authdie);
- PG_SETMASK(&AuthBlockSig);
+ pqsignal(SIGTERM, startup_die);
+ pqsignal(SIGQUIT, startup_die);
+ pqsignal(SIGALRM, startup_die);
+ PG_SETMASK(&StartupBlockSig);
/*
* Get the remote host name and port for logging and status display.
port->remote_port = strdup(remote_port);
/*
- * In EXEC_BACKEND case, we didn't inherit the contents of pg_hba.conf
- * etcetera from the postmaster, and have to load them ourselves. Build
- * the PostmasterContext (which didn't exist before, in this process) to
- * contain the data.
- *
- * FIXME: [fork/exec] Ugh. Is there a way around this overhead?
- */
-#ifdef EXEC_BACKEND
- Assert(PostmasterContext == NULL);
- PostmasterContext = AllocSetContextCreate(TopMemoryContext,
- "Postmaster",
- ALLOCSET_DEFAULT_MINSIZE,
- ALLOCSET_DEFAULT_INITSIZE,
- ALLOCSET_DEFAULT_MAXSIZE);
- MemoryContextSwitchTo(PostmasterContext);
-
- 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();
- load_role();
-#endif
-
- /*
- * Ready to begin client interaction. We will give up and exit(0) after a
+ * Ready to begin client interaction. We will give up and exit(1) after a
* time delay, so that a broken client can't hog a connection
- * indefinitely. PreAuthDelay doesn't count against the time limit.
+ * indefinitely. PreAuthDelay and any DNS interactions above don't count
+ * against the time limit.
*/
if (!enable_sig_alarm(AuthenticationTimeout * 1000, false))
- elog(FATAL, "could not set timer for authorization timeout");
+ elog(FATAL, "could not set timer for startup packet timeout");
/*
* Receive the startup packet (which might turn out to be a cancel request
*/
status = ProcessStartupPacket(port, false);
+ /*
+ * Stop here if it was bad or a cancel packet. ProcessStartupPacket
+ * already did any appropriate error reporting.
+ */
if (status != STATUS_OK)
proc_exit(0);
update_process_title ? "authentication" : "");
/*
- * Now perform authentication exchange.
- */
- ClientAuthentication(port); /* might not return, if failure */
-
- /*
- * Done with authentication. Disable timeout, and prevent SIGTERM/SIGQUIT
- * again until backend startup is complete.
+ * Disable the timeout, and prevent SIGTERM/SIGQUIT again.
*/
if (!disable_sig_alarm(false))
- elog(FATAL, "could not disable timer for authorization timeout");
+ elog(FATAL, "could not disable timer for startup packet timeout");
PG_SETMASK(&BlockSig);
-
- if (Log_connections)
- ereport(LOG,
- (errmsg("connection authorized: user=%s database=%s",
- port->user_name, port->database_name)));
}
TimestampDifference(0, port->SessionStartTime, &secs, &usecs);
srandom((unsigned int) (MyProcPid ^ usecs));
- /* ----------------
+ /*
* Now, build the argv vector that will be given to PostgresMain.
*
- * The layout of the command line is
- * postgres [secure switches] -y databasename [insecure switches]
- * where the switches after -y come from the client request.
- *
* The maximum possible number of commandline arguments that could come
- * from ExtraOptions or port->cmdline_options is (strlen + 1) / 2; see
- * split_opts().
- * ----------------
+ * from ExtraOptions is (strlen(ExtraOptions) + 1) / 2; see
+ * pg_split_opts().
*/
- maxac = 10; /* for fixed args supplied below */
+ maxac = 5; /* for fixed args supplied below */
maxac += (strlen(ExtraOptions) + 1) / 2;
- if (port->cmdline_options)
- maxac += (strlen(port->cmdline_options) + 1) / 2;
av = (char **) MemoryContextAlloc(TopMemoryContext,
maxac * sizeof(char *));
av[ac++] = "postgres";
/*
- * Pass any backend switches specified with -o in the postmaster's own
+ * Pass any backend switches specified with -o on the postmaster's own
* command line. We assume these are secure. (It's OK to mangle
* ExtraOptions now, since we're safely inside a subprocess.)
*/
- split_opts(av, &ac, ExtraOptions);
+ pg_split_opts(av, &ac, ExtraOptions);
/*
- * Tell the backend it is being called from the postmaster, and which
- * database to use. -y marks the end of secure switches.
+ * Tell the backend which database to use.
*/
- av[ac++] = "-y";
av[ac++] = port->database_name;
- /*
- * Pass the (insecure) option switches from the connection request. (It's
- * OK to mangle port->cmdline_options now.)
- */
- if (port->cmdline_options)
- split_opts(av, &ac, port->cmdline_options);
-
av[ac] = NULL;
Assert(ac < maxac);
- /*
- * Release postmaster's working memory context so that backend can recycle
- * the space. Note this does not trash *MyProcPort, because ConnCreate()
- * allocated that space with malloc() ... else we'd need to copy the Port
- * data here. Also, subsidiary data such as the username isn't lost
- * either; see ProcessStartupPacket().
- */
- MemoryContextSwitchTo(TopMemoryContext);
- MemoryContextDelete(PostmasterContext);
- PostmasterContext = NULL;
-
/*
* Debug: print arguments being passed to backend
*/
ereport(DEBUG3,
(errmsg_internal(")")));
- ClientAuthInProgress = false; /* client_min_messages is active now */
+ /*
+ * Make sure we aren't in PostmasterContext anymore. (We can't delete it
+ * just yet, though, because InitPostgres will need the HBA data.)
+ */
+ MemoryContextSwitchTo(TopMemoryContext);
return (PostgresMain(ac, av, port->user_name));
}
errmsg("out of memory")));
#endif
-
/* Check we got appropriate args */
if (argc < 3)
elog(FATAL, "invalid subpostmaster invocation");
#endif
/*
- * Perform additional initialization and client authentication.
+ * Perform additional initialization and collect startup packet.
*
* We want to do this before InitProcess() for a couple of reasons: 1.
* so that we aren't eating up a PGPROC slot while waiting on the
if (CheckPostmasterSignal(PMSIGNAL_RECOVERY_CONSISTENT) &&
pmState == PM_RECOVERY)
{
- /*
- * Load the flat authorization file into postmaster's cache. The
- * startup process won't have recomputed this from the database yet,
- * so it may change following recovery.
- */
- load_role();
-
/*
* Likewise, start other special children as needed.
*/
pmState = PM_RECOVERY_CONSISTENT;
}
- if (CheckPostmasterSignal(PMSIGNAL_PASSWORD_CHANGE))
- {
- /*
- * Authorization file has changed.
- */
- load_role();
- }
-
if (CheckPostmasterSignal(PMSIGNAL_WAKEN_ARCHIVER) &&
PgArchPID != 0)
{
errno = save_errno;
}
+/*
+ * Timeout or shutdown signal from postmaster while processing startup packet.
+ * Cleanup and exit(1).
+ *
+ * XXX: possible future improvement: try to send a message indicating
+ * why we are disconnecting. Problem is to be sure we don't block while
+ * doing so, nor mess up SSL initialization. In practice, if the client
+ * has wedged here, it probably couldn't do anything with the message anyway.
+ */
+static void
+startup_die(SIGNAL_ARGS)
+{
+ proc_exit(1);
+}
/*
* Dummy signal handler
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.570 2009/08/28 18:23:53 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.571 2009/08/29 19:26:51 tgl Exp $
*
* NOTES
* this is the "main" module of the postgres backend and
#include "parser/analyze.h"
#include "parser/parser.h"
#include "postmaster/autovacuum.h"
+#include "postmaster/postmaster.h"
#include "rewrite/rewriteHandler.h"
#include "storage/bufmgr.h"
#include "storage/ipc.h"
#include "mb/pg_wchar.h"
-extern int optind;
extern char *optarg;
+extern int optind;
+
+#ifdef HAVE_INT_OPTRESET
+extern int optreset; /* might not be declared by system headers */
+#endif
+
/* ----------------
* global variables
static MemoryContext unnamed_stmt_context = NULL;
-static bool EchoQuery = false; /* default don't echo */
+/* assorted command-line switches */
+static const char *userDoption = NULL; /* -D switch */
+
+static bool EchoQuery = false; /* -E switch */
/*
* people who want to use EOF should #define DONTUSENEWLINE in
{
PG_SETMASK(&BlockSig);
+ /*
+ * If we're aborting out of client auth, don't risk trying to send
+ * anything to the client; we will likely violate the protocol,
+ * not to mention that we may have interrupted the guts of OpenSSL
+ * or some authentication library.
+ */
+ if (ClientAuthInProgress && whereToSendOutput == DestRemote)
+ whereToSendOutput = DestNone;
+
/*
* Ideally this should be ereport(FATAL), but then we'd not get control
* back...
errno = save_errno;
}
-/*
- * Timeout or shutdown signal from postmaster during client authentication.
- * Simply exit(1).
- *
- * XXX: possible future improvement: try to send a message indicating
- * why we are disconnecting. Problem is to be sure we don't block while
- * doing so, nor mess up the authentication message exchange.
- */
-void
-authdie(SIGNAL_ARGS)
-{
- proc_exit(1);
-}
-
/*
* Query-cancel signal from postmaster: abort current transaction
* at soonest convenient time
ImmediateInterruptOK = false; /* not idle anymore */
DisableNotifyInterrupt();
DisableCatchupInterrupt();
+ /* As in quickdie, don't risk sending to client during auth */
+ if (ClientAuthInProgress && whereToSendOutput == DestRemote)
+ whereToSendOutput = DestNone;
if (IsAutoVacuumWorkerProcess())
ereport(FATAL,
(errcode(ERRCODE_ADMIN_SHUTDOWN),
ImmediateInterruptOK = false; /* not idle anymore */
DisableNotifyInterrupt();
DisableCatchupInterrupt();
- if (cancel_from_timeout)
+ /* As in quickdie, don't risk sending to client during auth */
+ if (ClientAuthInProgress && whereToSendOutput == DestRemote)
+ whereToSendOutput = DestNone;
+ if (ClientAuthInProgress)
+ ereport(ERROR,
+ (errcode(ERRCODE_QUERY_CANCELED),
+ errmsg("canceling authentication due to timeout")));
+ else if (cancel_from_timeout)
ereport(ERROR,
(errcode(ERRCODE_QUERY_CANCELED),
errmsg("canceling statement due to statement timeout")));
/* ----------------------------------------------------------------
- * PostgresMain
- * postgres main loop -- all backends, interactive or otherwise start here
+ * process_postgres_switches
+ * Parse command line arguments for PostgresMain
*
- * argc/argv are the command line arguments to be used. (When being forked
- * by the postmaster, these are not the original argv array of the process.)
- * username is the (possibly authenticated) PostgreSQL user name to be used
- * for the session.
+ * This is called twice, once for the "secure" options coming from the
+ * postmaster or command line, and once for the "insecure" options coming
+ * from the client's startup packet. The latter have the same syntax but
+ * may be restricted in what they can do.
+ *
+ * argv[0] is the program name either way.
+ *
+ * ctx is PGC_POSTMASTER for secure options, PGC_BACKEND for insecure options
+ * coming from the client, or PGC_SUSET for insecure options coming from
+ * a superuser client.
+ *
+ * Returns the database name extracted from the command line, if any.
* ----------------------------------------------------------------
*/
-int
-PostgresMain(int argc, char *argv[], const char *username)
+static const char *
+process_postgres_switches(int argc, char *argv[], GucContext ctx)
{
- int flag;
- const char *dbname = NULL;
- char *userDoption = NULL;
- bool secure;
+ const char *dbname;
+ const char *argv0 = argv[0];
+ bool secure = (ctx == PGC_POSTMASTER);
int errs = 0;
- int debug_flag = -1; /* -1 means not given */
- List *guc_names = NIL; /* for SUSET options */
- List *guc_values = NIL;
- GucContext ctx;
GucSource gucsource;
- bool am_superuser;
- int firstchar;
- char stack_base;
- StringInfoData input_message;
- sigjmp_buf local_sigjmp_buf;
- volatile bool send_ready_for_query = true;
-
-#define PendingConfigOption(name,val) \
- (guc_names = lappend(guc_names, pstrdup(name)), \
- guc_values = lappend(guc_values, pstrdup(val)))
-
- /*
- * initialize globals (already done if under postmaster, but not if
- * standalone; cheap enough to do over)
- */
- MyProcPid = getpid();
-
- MyStartTime = time(NULL);
-
- /*
- * Fire up essential subsystems: error and memory management
- *
- * If we are running under the postmaster, this is done already.
- */
- if (!IsUnderPostmaster)
- MemoryContextInit();
-
- set_ps_display("startup", false);
-
- SetProcessingMode(InitProcessing);
-
- /* Set up reference point for stack depth checking */
- stack_base_ptr = &stack_base;
+ int flag;
- /* Compute paths, if we didn't inherit them from postmaster */
- if (my_exec_path[0] == '\0')
+ if (secure)
{
- if (find_my_exec(argv[0], my_exec_path) < 0)
- elog(FATAL, "%s: could not locate my own executable path",
- argv[0]);
- }
+ gucsource = PGC_S_ARGV; /* switches came from command line */
- if (pkglib_path[0] == '\0')
- get_pkglib_path(my_exec_path, pkglib_path);
-
- /*
- * Set default values for command-line options.
- */
- EchoQuery = false;
-
- if (!IsUnderPostmaster)
- InitializeGUCOptions();
-
- /* ----------------
- * parse command line arguments
- *
- * There are now two styles of command line layout for the backend:
- *
- * For interactive use (not started from postmaster) the format is
- * postgres [switches] [databasename]
- * If the databasename is omitted it is taken to be the user name.
- *
- * When started from the postmaster, the format is
- * postgres [secure switches] -y databasename [insecure switches]
- * Switches appearing after -y came from the client (via "options"
- * field of connection request). For security reasons we restrict
- * what these switches can do.
- * ----------------
- */
-
- /* Ignore the initial --single argument, if present */
- if (argc > 1 && strcmp(argv[1], "--single") == 0)
+ /* Ignore the initial --single argument, if present */
+ if (argc > 1 && strcmp(argv[1], "--single") == 0)
+ {
+ argv++;
+ argc--;
+ }
+ }
+ else
{
- argv++;
- argc--;
+ gucsource = PGC_S_CLIENT; /* switches came from client */
}
- /* all options are allowed until '-y' */
- secure = true;
- ctx = PGC_POSTMASTER;
- gucsource = PGC_S_ARGV; /* initial switches came from command line */
-
/*
* Parse command-line options. CAUTION: keep this in sync with
* postmaster/postmaster.c (the option sets should not conflict) and with
* the common help() function in main/main.c.
*/
- while ((flag = getopt(argc, argv, "A:B:c:D:d:EeFf:h:ijk:lN:nOo:Pp:r:S:sTt:v:W:y:-:")) != -1)
+ while ((flag = getopt(argc, argv, "A:B:c:D:d:EeFf:h:ijk:lN:nOo:Pp:r:S:sTt:v:W:-:")) != -1)
{
switch (flag)
{
case 'D':
if (secure)
- userDoption = optarg;
+ userDoption = strdup(optarg);
break;
case 'd':
- debug_flag = atoi(optarg);
+ set_debug_options(atoi(optarg), ctx, gucsource);
break;
case 'E':
break;
case 's':
-
- /*
- * Since log options are SUSET, we need to postpone unless
- * still in secure context
- */
- if (ctx == PGC_BACKEND)
- PendingConfigOption("log_statement_stats", "true");
- else
- SetConfigOption("log_statement_stats", "true",
- ctx, gucsource);
+ SetConfigOption("log_statement_stats", "true", ctx, gucsource);
break;
case 'T':
const char *tmp = get_stats_option_name(optarg);
if (tmp)
- {
- if (ctx == PGC_BACKEND)
- PendingConfigOption(tmp, "true");
- else
- SetConfigOption(tmp, "true", ctx, gucsource);
- }
+ SetConfigOption(tmp, "true", ctx, gucsource);
else
errs++;
break;
SetConfigOption("post_auth_delay", optarg, ctx, gucsource);
break;
-
- case 'y':
-
- /*
- * y - special flag passed if backend was forked by a
- * postmaster.
- */
- if (secure)
- {
- dbname = strdup(optarg);
-
- secure = false; /* subsequent switches are NOT secure */
- ctx = PGC_BACKEND;
- gucsource = PGC_S_CLIENT;
- }
- break;
-
case 'c':
case '-':
{
errmsg("-c %s requires a value",
optarg)));
}
-
- /*
- * If a SUSET option, must postpone evaluation, unless we
- * are still reading secure switches.
- */
- if (ctx == PGC_BACKEND && IsSuperuserConfigOption(name))
- PendingConfigOption(name, value);
- else
- SetConfigOption(name, value, ctx, gucsource);
+ SetConfigOption(name, value, ctx, gucsource);
free(name);
if (value)
free(value);
}
/*
- * Process any additional GUC variable settings passed in startup packet.
- * These are handled exactly like command-line variables.
+ * Should be no more arguments except an optional database name, and
+ * that's only in the secure case.
*/
- if (MyProcPort != NULL)
+ if (errs || argc - optind > 1 || (argc != optind && !secure))
{
- ListCell *gucopts = list_head(MyProcPort->guc_options);
+ /* spell the error message a bit differently depending on context */
+ if (IsUnderPostmaster)
+ ereport(FATAL,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("invalid command-line arguments for server process"),
+ errhint("Try \"%s --help\" for more information.", argv0)));
+ else
+ ereport(FATAL,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("%s: invalid command-line arguments",
+ argv0),
+ errhint("Try \"%s --help\" for more information.", argv0)));
+ }
- while (gucopts)
- {
- char *name;
- char *value;
+ if (argc - optind == 1)
+ dbname = strdup(argv[optind]);
+ else
+ dbname = NULL;
- name = lfirst(gucopts);
- gucopts = lnext(gucopts);
+ /*
+ * Reset getopt(3) library so that it will work correctly in subprocesses
+ * or when this function is called a second time with another array.
+ */
+ optind = 1;
+#ifdef HAVE_INT_OPTRESET
+ optreset = 1; /* some systems need this too */
+#endif
- value = lfirst(gucopts);
- gucopts = lnext(gucopts);
+ return dbname;
+}
- if (IsSuperuserConfigOption(name))
- PendingConfigOption(name, value);
- else
- SetConfigOption(name, value, PGC_BACKEND, PGC_S_CLIENT);
- }
+
+/* ----------------------------------------------------------------
+ * PostgresMain
+ * postgres main loop -- all backends, interactive or otherwise start here
+ *
+ * argc/argv are the command line arguments to be used. (When being forked
+ * by the postmaster, these are not the original argv array of the process.)
+ * username is the (possibly authenticated) PostgreSQL user name to be used
+ * for the session.
+ * ----------------------------------------------------------------
+ */
+int
+PostgresMain(int argc, char *argv[], const char *username)
+{
+ const char *dbname;
+ bool am_superuser;
+ GucContext ctx;
+ int firstchar;
+ char stack_base;
+ StringInfoData input_message;
+ sigjmp_buf local_sigjmp_buf;
+ volatile bool send_ready_for_query = true;
+
+ /*
+ * Initialize globals (already done if under postmaster, but not if
+ * standalone).
+ */
+ if (!IsUnderPostmaster)
+ {
+ MyProcPid = getpid();
+
+ MyStartTime = time(NULL);
+ }
+
+ /*
+ * Fire up essential subsystems: error and memory management
+ *
+ * If we are running under the postmaster, this is done already.
+ */
+ if (!IsUnderPostmaster)
+ MemoryContextInit();
+
+ SetProcessingMode(InitProcessing);
+
+ /* Set up reference point for stack depth checking */
+ stack_base_ptr = &stack_base;
+
+ /* Compute paths, if we didn't inherit them from postmaster */
+ if (my_exec_path[0] == '\0')
+ {
+ if (find_my_exec(argv[0], my_exec_path) < 0)
+ elog(FATAL, "%s: could not locate my own executable path",
+ argv[0]);
+ }
+
+ if (pkglib_path[0] == '\0')
+ get_pkglib_path(my_exec_path, pkglib_path);
+
+ /*
+ * Set default values for command-line options.
+ */
+ if (!IsUnderPostmaster)
+ InitializeGUCOptions();
+
+ /*
+ * Parse command-line options.
+ */
+ dbname = process_postgres_switches(argc, argv, PGC_POSTMASTER);
+
+ /* Must have gotten a database name, or have a default (the username) */
+ if (dbname == NULL)
+ {
+ dbname = username;
+ if (dbname == NULL)
+ ereport(FATAL,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("%s: no database nor user name specified",
+ argv[0])));
}
/* Acquire configuration parameters, unless inherited from postmaster */
pg_timezone_abbrev_initialize();
}
- if (PostAuthDelay)
- pg_usleep(PostAuthDelay * 1000000L);
-
/*
* You might expect to see a setsid() call here, but it's not needed,
* because if we are under a postmaster then BackendInitialize() did it.
if (IsUnderPostmaster)
{
- /* noninteractive case: nothing should be left after switches */
- if (errs || argc != optind || dbname == NULL)
- {
- ereport(FATAL,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("invalid command-line arguments for server process"),
- errhint("Try \"%s --help\" for more information.", argv[0])));
- }
-
BaseInit();
}
else
{
- /* interactive case: database name can be last arg on command line */
- if (errs || argc - optind > 1)
- {
- ereport(FATAL,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("%s: invalid command-line arguments",
- argv[0]),
- errhint("Try \"%s --help\" for more information.", argv[0])));
- }
- else if (argc - optind == 1)
- dbname = argv[optind];
- else if ((dbname = username) == NULL)
- {
- ereport(FATAL,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("%s: no database nor user name specified",
- argv[0])));
- }
-
/*
* Validate we have been given a reasonable-looking DataDir (if under
* postmaster, assume postmaster did this already).
InitProcess();
#endif
+ /* We need to allow SIGINT, etc during the initial transaction */
+ PG_SETMASK(&UnBlockSig);
+
/*
* General initialization.
*
* it inside InitPostgres() instead. In particular, anything that
* involves database access should be there, not here.
*/
- ereport(DEBUG3,
- (errmsg_internal("InitPostgres")));
am_superuser = InitPostgres(dbname, InvalidOid, username, NULL);
+ /*
+ * If the PostmasterContext is still around, recycle the space; we don't
+ * need it anymore after InitPostgres completes. Note this does not trash
+ * *MyProcPort, because ConnCreate() allocated that space with malloc()
+ * ... else we'd need to copy the Port data first. Also, subsidiary data
+ * such as the username isn't lost either; see ProcessStartupPacket().
+ */
+ if (PostmasterContext)
+ {
+ MemoryContextDelete(PostmasterContext);
+ PostmasterContext = NULL;
+ }
+
SetProcessingMode(NormalProcessing);
+ set_ps_display("startup", false);
+
/*
- * Now that we know if client is a superuser, we can try to apply SUSET
- * GUC options that came from the client.
+ * Now that we know if client is a superuser, we can try to apply any
+ * command-line options passed in the startup packet.
*/
- ctx = am_superuser ? PGC_SUSET : PGC_USERSET;
+ ctx = am_superuser ? PGC_SUSET : PGC_BACKEND;
+
+ if (MyProcPort != NULL &&
+ MyProcPort->cmdline_options != NULL)
+ {
+ /*
+ * The maximum possible number of commandline arguments that could
+ * come from MyProcPort->cmdline_options is (strlen + 1) / 2; see
+ * pg_split_opts().
+ */
+ char **av;
+ int maxac;
+ int ac;
+
+ maxac = 2 + (strlen(MyProcPort->cmdline_options) + 1) / 2;
+
+ av = (char **) palloc(maxac * sizeof(char *));
+ ac = 0;
+
+ av[ac++] = argv[0];
- if (debug_flag >= 0)
- set_debug_options(debug_flag, ctx, PGC_S_CLIENT);
+ /* Note this mangles MyProcPort->cmdline_options */
+ pg_split_opts(av, &ac, MyProcPort->cmdline_options);
- if (guc_names != NIL)
+ av[ac] = NULL;
+
+ Assert(ac < maxac);
+
+ (void) process_postgres_switches(ac, av, ctx);
+ }
+
+ /*
+ * Process any additional GUC variable settings passed in startup packet.
+ * These are handled exactly like command-line variables.
+ */
+ if (MyProcPort != NULL)
{
- ListCell *namcell,
- *valcell;
+ ListCell *gucopts = list_head(MyProcPort->guc_options);
- forboth(namcell, guc_names, valcell, guc_values)
+ while (gucopts)
{
- char *name = (char *) lfirst(namcell);
- char *value = (char *) lfirst(valcell);
+ char *name;
+ char *value;
+
+ name = lfirst(gucopts);
+ gucopts = lnext(gucopts);
+
+ value = lfirst(gucopts);
+ gucopts = lnext(gucopts);
SetConfigOption(name, value, ctx, PGC_S_CLIENT);
- pfree(name);
- pfree(value);
}
}
+ /* Apply PostAuthDelay as soon as we've read all options */
+ if (PostAuthDelay > 0)
+ pg_usleep(PostAuthDelay * 1000000L);
+
/*
* Now all GUC states are fully set up. Report them to client if
* appropriate.
/* We can now handle ereport(ERROR) */
PG_exception_stack = &local_sigjmp_buf;
- PG_SETMASK(&UnBlockSig);
-
if (!ignore_till_sync)
send_ready_for_query = true; /* initially, or after error */
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/backend/utils/init/flatfiles.c,v 1.37 2009/08/12 20:53:30 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/init/flatfiles.c,v 1.38 2009/08/29 19:26:51 tgl Exp $
*
*-------------------------------------------------------------------------
*/
heap_close(mrel, NoLock);
}
- /*
- * Signal the postmaster to reload its caches.
- */
- SendPostmasterSignal(PMSIGNAL_PASSWORD_CHANGE);
-
/*
* Force synchronous commit, to minimize the window between changing the
* flat files on-disk and marking the transaction committed. It's not
*
*
* 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>
#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"
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);
}
+/*
+ * 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
*/
}
+/*
+ * 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.
char *fullpath;
char dbname[NAMEDATALEN];
+ elog(DEBUG3, "InitPostgres");
+
/*
* Add my PGPROC struct to the ProcArray.
*
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.
else
{
/* normal multiuser case */
+ Assert(MyProcPort != NULL);
+ PerformAuthentication(MyProcPort);
InitializeSessionUserId(username);
am_superuser = superuser();
}
* Written by Peter Eisentraut <peter_e@gmx.net>.
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.511 2009/08/24 20:08:32 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.512 2009/08/29 19:26:51 tgl Exp $
*
*--------------------------------------------------------------------
*/
if (IsUnderPostmaster)
return true;
}
- else if (context != PGC_BACKEND && context != PGC_POSTMASTER)
+ else if (context != PGC_POSTMASTER && context != PGC_BACKEND &&
+ source != PGC_S_CLIENT)
{
ereport(elevel,
(errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
return NULL;
}
-/*
- * Detect whether the given configuration option can only be set by
- * a superuser.
- */
-bool
-IsSuperuserConfigOption(const char *name)
-{
- struct config_generic *record;
-
- record = find_option(name, false, ERROR);
- /* On an unrecognized name, don't error, just return false. */
- if (record == NULL)
- return false;
- return (record->context == PGC_SUSET);
-}
-
/*
* GUC_complaint_elevel
* Interface to hba.c
*
*
- * $PostgreSQL: pgsql/src/include/libpq/hba.h,v 1.56 2009/06/11 14:49:11 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/libpq/hba.h,v 1.57 2009/08/29 19:26:51 tgl Exp $
*
*-------------------------------------------------------------------------
*/
bool include_realm;
} HbaLine;
+/* kluge to avoid including libpq/libpq-be.h here */
typedef struct Port hbaPort;
-extern List **get_role_line(const char *role);
extern bool load_hba(void);
extern void load_ident(void);
-extern void load_role(void);
extern int hba_getauthmethod(hbaPort *port);
extern bool read_pg_database_line(FILE *fp, char *dbname, Oid *dboid,
Oid *dbtablespace, TransactionId *dbfrozenxid);
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/libpq/pqsignal.h,v 1.33 2009/01/01 17:23:59 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/libpq/pqsignal.h,v 1.34 2009/08/29 19:26:51 tgl Exp $
*
* NOTES
* This shouldn't be in libpq, but the monitor and some other
#ifdef HAVE_SIGPROCMASK
extern sigset_t UnBlockSig,
BlockSig,
- AuthBlockSig;
+ StartupBlockSig;
#define PG_SETMASK(mask) sigprocmask(SIG_SETMASK, mask, NULL)
#else
extern int UnBlockSig,
BlockSig,
- AuthBlockSig;
+ StartupBlockSig;
#ifndef WIN32
#define PG_SETMASK(mask) sigsetmask(*((int*)(mask)))
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/miscadmin.h,v 1.212 2009/08/12 20:53:30 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/miscadmin.h,v 1.213 2009/08/29 19:26:51 tgl Exp $
*
* NOTES
* some of the information in this file should be moved to other files.
*****************************************************************************/
/* in utils/init/postinit.c */
+extern void pg_split_opts(char **argv, int *argcp, char *optstr);
extern bool InitPostgres(const char *in_dbname, Oid dboid, const char *username,
char *out_dbname);
extern void BaseInit(void);
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/storage/pmsignal.h,v 1.25 2009/06/11 14:49:12 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/storage/pmsignal.h,v 1.26 2009/08/29 19:26:52 tgl Exp $
*
*-------------------------------------------------------------------------
*/
PMSIGNAL_RECOVERY_STARTED, /* recovery has started */
PMSIGNAL_RECOVERY_CONSISTENT, /* recovery has reached consistent
* state */
- PMSIGNAL_PASSWORD_CHANGE, /* pg_auth file has changed */
PMSIGNAL_WAKEN_ARCHIVER, /* send a NOTIFY signal to xlog archiver */
PMSIGNAL_ROTATE_LOGFILE, /* send SIGUSR1 to syslogger to rotate logfile */
PMSIGNAL_START_AUTOVAC_LAUNCHER, /* start an autovacuum launcher */
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/tcop/tcopprot.h,v 1.98 2009/06/11 14:49:12 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/tcop/tcopprot.h,v 1.99 2009/08/29 19:26:52 tgl Exp $
*
* OLD COMMENTS
* This file was created so that other c files could get the two
extern void die(SIGNAL_ARGS);
extern void quickdie(SIGNAL_ARGS);
-extern void authdie(SIGNAL_ARGS);
extern void StatementCancelHandler(SIGNAL_ARGS);
extern void FloatExceptionHandler(SIGNAL_ARGS);
extern void prepare_for_client_read(void);
* Copyright (c) 2000-2009, PostgreSQL Global Development Group
* Written by Peter Eisentraut <peter_e@gmx.net>.
*
- * $PostgreSQL: pgsql/src/include/utils/guc.h,v 1.102 2009/06/11 14:49:13 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/utils/guc.h,v 1.103 2009/08/29 19:26:52 tgl Exp $
*--------------------------------------------------------------------
*/
#ifndef GUC_H
extern const char *GetConfigOption(const char *name);
extern const char *GetConfigOptionResetString(const char *name);
-extern bool IsSuperuserConfigOption(const char *name);
extern void ProcessConfigFile(GucContext context);
extern void InitializeGUCOptions(void);
extern bool SelectConfigFiles(const char *userDoption, const char *progname);