diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c index 7e328f7bcf..e0bd078d3e 100644 --- a/src/backend/libpq/auth.c +++ b/src/backend/libpq/auth.c @@ -8,7 +8,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -33,8 +33,10 @@ #include "libpq/ip.h" #include "libpq/libpq.h" #include "libpq/pqformat.h" +#include "miscadmin.h" #include "storage/ipc.h" + /*---------------------------------------------------------------- * Global authentication functions *---------------------------------------------------------------- @@ -281,6 +283,15 @@ ClientAuthentication(Port *port) 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 @@ -458,6 +469,9 @@ ClientAuthentication(Port *port) sendAuthRequest(port, AUTH_REQ_OK); else auth_failed(port, status); + + /* Done with authentication, so we should turn off immediate interrupts */ + ImmediateInterruptOK = false; } @@ -690,9 +704,6 @@ pg_krb5_recvauth(Port *port) 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; @@ -1823,9 +1834,6 @@ authident(hbaPort *port) { 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: diff --git a/src/backend/libpq/crypt.c b/src/backend/libpq/crypt.c index e230d53577..77250b4031 100644 --- a/src/backend/libpq/crypt.c +++ b/src/backend/libpq/crypt.c @@ -9,7 +9,7 @@ * 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 $ * *------------------------------------------------------------------------- */ @@ -20,38 +20,63 @@ #include #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; - if ((line = get_role_line(role)) == NULL) - return STATUS_ERROR; + /* + * 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; - /* Skip over rolename */ - token = list_head(*line); - if (token) - token = lnext(token); - if (token) + /* Get role info from pg_authid */ + roleTup = SearchSysCache(AUTHNAME, + PointerGetDatum(role), + 0, 0, 0); + if (!HeapTupleIsValid(roleTup)) + return STATUS_ERROR; /* no such user */ + + 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); - if (shadow_pass == NULL || *shadow_pass == '\0') - return STATUS_ERROR; + datum = SysCacheGetAttr(AUTHNAME, roleTup, + Anum_pg_authid_rolvaliduntil, &isnull); + if (!isnull) + vuntil = DatumGetTimestampTz(datum); + + 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 @@ -119,24 +144,14 @@ md5_crypt_verify(const Port *port, const char *role, char *client_pass) 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) diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c index 27e0c431ca..dbd6191682 100644 --- a/src/backend/libpq/hba.c +++ b/src/backend/libpq/hba.c @@ -10,7 +10,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -29,9 +29,9 @@ #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)) @@ -42,31 +42,21 @@ #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); @@ -434,70 +424,28 @@ tokenize_file(const char *filename, FILE *file, } } -/* - * 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); } /* @@ -508,7 +456,7 @@ is_member(const char *user, const char *role) * 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; @@ -518,7 +466,7 @@ check_role(const char *role, char *param_str) { if (tok[0] == '+') { - if (is_member(role, tok + 1)) + if (is_member(roleid, tok + 1)) return true; } else if (strcmp(tok, role) == 0 || @@ -537,7 +485,7 @@ check_role(const char *role, char *param_str) * 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; @@ -555,7 +503,7 @@ check_db(const char *dbname, const char *role, char *param_str) 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) @@ -1106,9 +1054,13 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline) 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); @@ -1177,10 +1129,11 @@ check_hba(hbaPort *port) } /* != 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! */ @@ -1200,58 +1153,6 @@ check_hba(hbaPort *port) */ } - -/* - * 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 */ @@ -1613,7 +1514,7 @@ ident_syntax: * 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. * diff --git a/src/backend/libpq/pqsignal.c b/src/backend/libpq/pqsignal.c index ed26fbdb51..a6f0e109cf 100644 --- a/src/backend/libpq/pqsignal.c +++ b/src/backend/libpq/pqsignal.c @@ -9,7 +9,7 @@ * * * 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 @@ -49,23 +49,23 @@ #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??) @@ -79,7 +79,7 @@ pqinitmask(void) /* 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 @@ -88,46 +88,46 @@ pqinitmask(void) */ #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. */ @@ -139,7 +139,7 @@ pqinitmask(void) 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); diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index 8ea5c18758..3f77333641 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -37,7 +37,7 @@ * * * 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 * @@ -327,6 +327,7 @@ static void SIGHUP_handler(SIGNAL_ARGS); 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); @@ -1561,7 +1562,8 @@ ProcessStartupPacket(Port *port, bool SSLdone) 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) @@ -1623,10 +1625,10 @@ retry1: /* * 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); @@ -2291,13 +2293,6 @@ reaper(SIGNAL_ARGS) 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 @@ -3054,7 +3049,7 @@ BackendStartup(Port *port) /* 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 */ @@ -3129,39 +3124,9 @@ report_fork_failure_to_client(Port *port, int errnum) } -/* - * 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. * @@ -3183,13 +3148,14 @@ BackendInitialize(Port *port) /* * 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 */ @@ -3218,15 +3184,15 @@ BackendInitialize(Port *port) #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. @@ -3265,42 +3231,13 @@ BackendInitialize(Port *port) 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 @@ -3308,6 +3245,10 @@ BackendInitialize(Port *port) */ 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); @@ -3319,22 +3260,11 @@ BackendInitialize(Port *port) 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))); } @@ -3366,22 +3296,15 @@ BackendRun(Port *port) 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 *)); @@ -3390,41 +3313,21 @@ BackendRun(Port *port) 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 */ @@ -3437,7 +3340,11 @@ BackendRun(Port *port) 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)); } @@ -3833,7 +3740,6 @@ SubPostmasterMain(int argc, char *argv[]) errmsg("out of memory"))); #endif - /* Check we got appropriate args */ if (argc < 3) elog(FATAL, "invalid subpostmaster invocation"); @@ -3901,7 +3807,7 @@ SubPostmasterMain(int argc, char *argv[]) #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 @@ -4074,13 +3980,6 @@ sigusr1_handler(SIGNAL_ARGS) 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. */ @@ -4094,14 +3993,6 @@ sigusr1_handler(SIGNAL_ARGS) pmState = PM_RECOVERY_CONSISTENT; } - if (CheckPostmasterSignal(PMSIGNAL_PASSWORD_CHANGE)) - { - /* - * Authorization file has changed. - */ - load_role(); - } - if (CheckPostmasterSignal(PMSIGNAL_WAKEN_ARCHIVER) && PgArchPID != 0) { @@ -4144,6 +4035,20 @@ sigusr1_handler(SIGNAL_ARGS) 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 diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index 79e5aa9420..a3ed96689d 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -8,7 +8,7 @@ * * * 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 @@ -55,6 +55,7 @@ #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" @@ -73,8 +74,13 @@ #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 @@ -151,7 +157,10 @@ static CachedPlanSource *unnamed_stmt_psrc = NULL; 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 @@ -2481,6 +2490,15 @@ quickdie(SIGNAL_ARGS) { 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... @@ -2552,20 +2570,6 @@ die(SIGNAL_ARGS) 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 @@ -2646,6 +2650,9 @@ ProcessInterrupts(void) 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), @@ -2661,7 +2668,14 @@ ProcessInterrupts(void) 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"))); @@ -2840,116 +2854,55 @@ get_stats_option_name(const char *arg) /* ---------------------------------------------------------------- - * 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; + int flag; -#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; - - /* 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 */ + + /* Ignore the initial --single argument, if present */ + if (argc > 1 && strcmp(argv[1], "--single") == 0) + { + argv++; + argc--; + } } - - 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) + 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) { @@ -2963,11 +2916,11 @@ PostgresMain(int argc, char *argv[], const char *username) 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': @@ -3042,16 +2995,7 @@ PostgresMain(int argc, char *argv[], const char *username) 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': @@ -3063,12 +3007,7 @@ PostgresMain(int argc, char *argv[], const char *username) 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; @@ -3090,23 +3029,6 @@ PostgresMain(int argc, char *argv[], const char *username) 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 '-': { @@ -3127,15 +3049,7 @@ PostgresMain(int argc, char *argv[], const char *username) 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); @@ -3149,29 +3063,120 @@ PostgresMain(int argc, char *argv[], const char *username) } /* - * 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 */ @@ -3185,9 +3190,6 @@ PostgresMain(int argc, char *argv[], const char *username) 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. @@ -3254,38 +3256,10 @@ PostgresMain(int argc, char *argv[], const char *username) 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). @@ -3330,6 +3304,9 @@ PostgresMain(int argc, char *argv[], const char *username) InitProcess(); #endif + /* We need to allow SIGINT, etc during the initial transaction */ + PG_SETMASK(&UnBlockSig); + /* * General initialization. * @@ -3337,37 +3314,87 @@ PostgresMain(int argc, char *argv[], const char *username) * 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 (debug_flag >= 0) - set_debug_options(debug_flag, ctx, PGC_S_CLIENT); - - if (guc_names != NIL) + if (MyProcPort != NULL && + MyProcPort->cmdline_options != NULL) { - ListCell *namcell, - *valcell; + /* + * 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; - forboth(namcell, guc_names, valcell, guc_values) + maxac = 2 + (strlen(MyProcPort->cmdline_options) + 1) / 2; + + av = (char **) palloc(maxac * sizeof(char *)); + ac = 0; + + av[ac++] = argv[0]; + + /* Note this mangles MyProcPort->cmdline_options */ + pg_split_opts(av, &ac, MyProcPort->cmdline_options); + + 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 *gucopts = list_head(MyProcPort->guc_options); + + 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. @@ -3514,8 +3541,6 @@ PostgresMain(int argc, char *argv[], const char *username) /* 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 */ diff --git a/src/backend/utils/init/flatfiles.c b/src/backend/utils/init/flatfiles.c index 5a9c8bd47b..fbbd372b14 100644 --- a/src/backend/utils/init/flatfiles.c +++ b/src/backend/utils/init/flatfiles.c @@ -23,7 +23,7 @@ * 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 $ * *------------------------------------------------------------------------- */ @@ -824,11 +824,6 @@ AtEOXact_UpdateFlatFiles(bool isCommit) 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 diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c index bd8598ce13..11eeec353c 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 #include #include @@ -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); @@ -158,6 +161,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 */ @@ -329,6 +392,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(); } diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 0fe3b4b98c..6bc0c197b0 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -10,7 +10,7 @@ * Written by Peter Eisentraut . * * 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 $ * *-------------------------------------------------------------------- */ @@ -4635,7 +4635,8 @@ set_config_option(const char *name, const char *value, 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), @@ -5243,22 +5244,6 @@ GetConfigOptionResetString(const char *name) 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 diff --git a/src/include/libpq/hba.h b/src/include/libpq/hba.h index f626342330..85849d42cf 100644 --- a/src/include/libpq/hba.h +++ b/src/include/libpq/hba.h @@ -4,7 +4,7 @@ * 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 $ * *------------------------------------------------------------------------- */ @@ -61,12 +61,11 @@ typedef struct 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); diff --git a/src/include/libpq/pqsignal.h b/src/include/libpq/pqsignal.h index de1536cf50..cd631c19e2 100644 --- a/src/include/libpq/pqsignal.h +++ b/src/include/libpq/pqsignal.h @@ -7,7 +7,7 @@ * 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 @@ -23,13 +23,13 @@ #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))) diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h index eac73aae81..cd787b87da 100644 --- a/src/include/miscadmin.h +++ b/src/include/miscadmin.h @@ -13,7 +13,7 @@ * 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. @@ -323,6 +323,7 @@ extern ProcessingMode Mode; *****************************************************************************/ /* 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); diff --git a/src/include/storage/pmsignal.h b/src/include/storage/pmsignal.h index db47cd69f5..2a75ba7ee6 100644 --- a/src/include/storage/pmsignal.h +++ b/src/include/storage/pmsignal.h @@ -7,7 +7,7 @@ * 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 $ * *------------------------------------------------------------------------- */ @@ -25,7 +25,6 @@ typedef enum 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 */ diff --git a/src/include/tcop/tcopprot.h b/src/include/tcop/tcopprot.h index 3368e6539c..674e680284 100644 --- a/src/include/tcop/tcopprot.h +++ b/src/include/tcop/tcopprot.h @@ -7,7 +7,7 @@ * 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 @@ -58,7 +58,6 @@ extern bool assign_max_stack_depth(int newval, bool doit, GucSource source); 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); diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h index 0705ae05de..10eb70d018 100644 --- a/src/include/utils/guc.h +++ b/src/include/utils/guc.h @@ -7,7 +7,7 @@ * Copyright (c) 2000-2009, PostgreSQL Global Development Group * Written by Peter Eisentraut . * - * $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 @@ -247,7 +247,6 @@ extern void EmitWarningsOnPlaceholders(const char *className); 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);