diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml index 19d81f960bedeabfc784834d224fa6e7a030e71a..1f1674904afc819c8693e445a74b3c3ccc5ce755 100644 --- a/doc/src/sgml/client-auth.sgml +++ b/doc/src/sgml/client-auth.sgml @@ -1,4 +1,4 @@ -<!-- $Header: /cvsroot/pgsql/doc/src/sgml/client-auth.sgml,v 1.22 2001/10/04 22:27:18 petere Exp $ --> +<!-- $Header: /cvsroot/pgsql/doc/src/sgml/client-auth.sgml,v 1.23 2001/11/02 18:39:57 tgl Exp $ --> <chapter id="client-authentication"> <title>Client Authentication</title> @@ -67,6 +67,19 @@ tabs. Records cannot be continued across lines. </para> + <para> + Each record specifies a connection type, a client IP address range + (if relevant for the connection type), a database name or names, + and the authentication method to be used for connections matching + these parameters. + The first record that matches the type, client address and requested + database name of a connection attempt is used to do the + authentication step. There is no <quote>fall-through</> or + <quote>backup</>: if one record is chosen and the authentication + fails, the following records are not considered. If no record + matches, the access will be denied. + </para> + <para> A record may have one of the three formats <synopsis> @@ -107,7 +120,9 @@ hostssl <replaceable>database</replaceable> <replaceable>IP-address</replaceable TCP/IP. To make use of this option the server must be built with SSL support enabled. Furthermore, SSL must be enabled with the <option>-l</> option or equivalent configuration - setting when the server is started. + setting when the server is started. (Note: <literal>host</literal> + records will match either SSL or non-SSL connection attempts, but + <literal>hostssl</literal> records match only SSL connections.) </para> </listitem> </varlistentry> @@ -131,8 +146,9 @@ hostssl <replaceable>database</replaceable> <replaceable>IP-address</replaceable <term><replaceable>IP mask</replaceable></term> <listitem> <para> - These two fields control to which hosts a - <literal>host</literal> record applies, based on their IP + These two fields specify to which client machines a + <literal>host</literal> or <literal>hostssl</literal> + record applies, based on their IP address. (Of course IP addresses can be spoofed but this consideration is beyond the scope of <productname>Postgres</productname>.) The precise logic is that @@ -151,7 +167,8 @@ hostssl <replaceable>database</replaceable> <replaceable>IP-address</replaceable <listitem> <para> Specifies the method that users must use to authenticate themselves - when connecting to that database. The possible choices follow, + when connecting under the control of this authentication record. + The possible choices are summarized here, details are in <xref linkend="auth-methods">. <variablelist> @@ -322,17 +339,27 @@ hostssl <replaceable>database</replaceable> <replaceable>IP-address</replaceable </listitem> </varlistentry> </variablelist> + </para> - The first record that matches the client IP address and requested - database name of a connection attempt is used to do the - authentication step. There is no <quote>fall-through</> or - <quote>backup</>: if one record is chosen and the authentication - fails, the following records are not considered. If no record - matches, the access will be denied. + <para> + Since the <filename>pg_hba.conf</filename> records are examined + sequentially for each connection attempt, order of the records is + very significant. Typically, earlier records will have tight + connection match parameters and weaker authentication methods, + while later records will have looser match parameters and stronger + authentication methods. For example, one might wish to use + <literal>trust</> authentication for local TCP connections but + require a password for remote TCP connections. In this case a + record specifying <literal>trust</> authentication for connections + from 127.0.0.1 would appear before a record specifying password + authentication for a wider range of allowed client IP addresses. </para> <para> - The <filename>pg_hba.conf</filename> file is loaded only on startup + <indexterm> + <primary>SIGHUP</primary> + </indexterm> + The <filename>pg_hba.conf</filename> file is read on startup and when the <application>postmaster</> receives a <systemitem>SIGHUP</systemitem> signal. If you edit the file on an active system, you will need to signal the <application>postmaster</> @@ -632,7 +659,7 @@ host all 192.168.0.0 255.255.0.0 ident omicron to connect as the database user he is requesting to connect as. This is controlled by the ident map argument that follows the <literal>ident</> keyword in the - <filename>pg_hba.conf</filename> file. The simplest ident map is + <filename>pg_hba.conf</filename> file. There is a predefined ident map <literal>sameuser</literal>, which allows any operating system user to connect as the database user of the same name (if the latter exists). Other maps must be created manually. @@ -640,7 +667,8 @@ host all 192.168.0.0 255.255.0.0 ident omicron <para> <indexterm><primary>pg_ident.conf</primary></indexterm> - Ident maps are held in the file <filename>pg_ident.conf</filename> + Ident maps other than <literal>sameuser</literal> are defined + in the file <filename>pg_ident.conf</filename> in the data directory, which contains lines of the general form: <synopsis> <replaceable>map-name</> <replaceable>ident-username</> <replaceable>database-username</> @@ -657,6 +685,18 @@ host all 192.168.0.0 255.255.0.0 ident omicron versa. </para> + <para> + <indexterm> + <primary>SIGHUP</primary> + </indexterm> + The <filename>pg_ident.conf</filename> file is read on startup + and when the <application>postmaster</> receives a + <systemitem>SIGHUP</systemitem> signal. If you edit the file on an + active system, you will need to signal the <application>postmaster</> + (using <application>pg_ctl reload</> or <application>kill -HUP</>) + to make it re-read the file. + </para> + <para> A <filename>pg_ident.conf</filename> file that could be used in conjunction with the <filename>pg_hba.conf</> file in <xref diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml index fcbb97d00b81c98f9476ba3029b356c2eb945db8..8284d010e646986bf13afb18cf6635431fbd0314 100644 --- a/doc/src/sgml/runtime.sgml +++ b/doc/src/sgml/runtime.sgml @@ -1,5 +1,5 @@ <!-- -$Header: /cvsroot/pgsql/doc/src/sgml/runtime.sgml,v 1.91 2001/10/31 20:35:02 petere Exp $ +$Header: /cvsroot/pgsql/doc/src/sgml/runtime.sgml,v 1.92 2001/11/02 18:39:57 tgl Exp $ --> <Chapter Id="runtime"> @@ -479,8 +479,10 @@ syslog = 2 <primary>SIGHUP</primary> </indexterm> The configuration file is reread whenever the postmaster receives - a <systemitem>SIGHUP</> signal. This signal is also propagated to all running - backend processes, so that running sessions get the new default. + a <systemitem>SIGHUP</> signal (which is most easily sent by means + of <application>pg_ctl reload</>). The postmaster also propagates + this signal to all already-running backend processes, so that + existing sessions also get the new default. Alternatively, you can send the signal to only one backend process directly. </para> diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c index 758cf365c80841e6f10a090d8ebb4fda8524fafd..3897a5c75ec7e582a7bda3fdbffe592fd3efef34 100644 --- a/src/backend/commands/user.c +++ b/src/backend/commands/user.c @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Header: /cvsroot/pgsql/src/backend/commands/user.c,v 1.87 2001/11/01 18:09:58 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/user.c,v 1.88 2001/11/02 18:39:57 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -15,6 +15,7 @@ #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> +#include <signal.h> #include <unistd.h> #include "access/heapam.h" @@ -33,14 +34,15 @@ #include "utils/syscache.h" -static void CheckPgUserAclNotNull(void); extern bool Password_encryption; +static void CheckPgUserAclNotNull(void); + /*--------------------------------------------------------------------- * write_password_file / update_pg_pwd * * copy the modified contents of pg_shadow to a file used by the postmaster - * for user authentication. The file is stored as $PGDATA/pg_pwd. + * for user authentication. The file is stored as $PGDATA/global/pg_pwd. * * This function set is both a trigger function for direct updates to pg_shadow * as well as being called directly from create/alter/drop user. @@ -57,7 +59,6 @@ write_password_file(Relation rel) *tempname; int bufsize; FILE *fp; - int flagfd; mode_t oumask; HeapScanDesc scan; HeapTuple tuple; @@ -133,7 +134,7 @@ write_password_file(Relation rel) /* * The extra columns we emit here are not really necessary. To remove * them, the parser in backend/libpq/crypt.c would need to be - * adjusted. Initdb might also need adjustments. + * adjusted. */ fprintf(fp, "%s" @@ -168,6 +169,7 @@ write_password_file(Relation rel) /* * Rename the temp file to its final name, deleting the old pg_pwd. + * We expect that rename(2) is an atomic action. */ if (rename(tempname, filename)) elog(ERROR, "rename %s to %s: %m", tempname, filename); @@ -176,19 +178,10 @@ write_password_file(Relation rel) pfree((void *) filename); /* - * Create a flag file the postmaster will detect the next time it - * tries to authenticate a user. The postmaster will know to reload - * the pg_pwd file contents. Note: we used to elog(ERROR) if the file - * creation failed, but it's a little silly to abort the transaction - * at this point, so let's just make it a NOTICE. + * Signal the postmaster to reload its password-file cache. */ - filename = crypt_getpwdreloadfilename(); - flagfd = BasicOpenFile(filename, O_WRONLY | O_CREAT, 0600); - if (flagfd < 0) - elog(NOTICE, "write_password_file: unable to write %s: %m", filename); - else - close(flagfd); - pfree((void *) filename); + if (IsUnderPostmaster) + kill(getppid(), SIGHUP); } diff --git a/src/backend/libpq/crypt.c b/src/backend/libpq/crypt.c index 8decc40d0ba3f70589d25ec749bf0d609cca7c31..83921ee014f0738f2babce0383134d69f79d736a 100644 --- a/src/backend/libpq/crypt.c +++ b/src/backend/libpq/crypt.c @@ -9,7 +9,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Header: /cvsroot/pgsql/src/backend/libpq/crypt.c,v 1.40 2001/11/01 18:10:48 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/libpq/crypt.c,v 1.41 2001/11/02 18:39:57 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -17,6 +17,9 @@ #include <errno.h> #include <unistd.h> +#ifdef HAVE_CRYPT_H +#include <crypt.h> +#endif #include "libpq/crypt.h" #include "libpq/libpq.h" @@ -24,15 +27,15 @@ #include "storage/fd.h" #include "utils/nabstime.h" -#ifdef HAVE_CRYPT_H -#include <crypt.h> -#endif -char **pwd_cache = NULL; -int pwd_cache_count = 0; +#define CRYPT_PWD_FILE "pg_pwd" + + +static char **pwd_cache = NULL; +static int pwd_cache_count = 0; /* - * crypt_getpwdfilename --- get name of password file + * crypt_getpwdfilename --- get full pathname of password file * * Note that result string is palloc'd, and should be freed by the caller. */ @@ -50,28 +53,8 @@ crypt_getpwdfilename(void) } /* - * crypt_getpwdreloadfilename --- get name of password-reload-needed flag file - * - * Note that result string is palloc'd, and should be freed by the caller. + * Open the password file if possible (return NULL if not) */ -char * -crypt_getpwdreloadfilename(void) -{ - char *pwdfilename; - int bufsize; - char *rpfnam; - - pwdfilename = crypt_getpwdfilename(); - bufsize = strlen(pwdfilename) + strlen(CRYPT_PWD_RELOAD_SUFX) + 1; - rpfnam = (char *) palloc(bufsize); - snprintf(rpfnam, bufsize, "%s%s", pwdfilename, CRYPT_PWD_RELOAD_SUFX); - pfree(pwdfilename); - - return rpfnam; -} - -/*-------------------------------------------------------------------------*/ - static FILE * crypt_openpwdfile(void) { @@ -123,107 +106,128 @@ compar_user(const void *user_a, const void *user_b) return result; } -/*-------------------------------------------------------------------------*/ - -static void -crypt_loadpwdfile(void) +/* + * Load or reload the password-file cache + */ +void +load_password_cache(void) { - char *filename; - int result; FILE *pwd_file; char buffer[1024]; - filename = crypt_getpwdreloadfilename(); - result = unlink(filename); - pfree(filename); - /* - * We want to delete the flag file before reading the contents of the - * pg_pwd file. If result == 0 then the unlink of the reload file was - * successful. This means that a backend performed a COPY of the - * pg_shadow file to pg_pwd. Therefore we must now do a reload. + * If for some reason we fail to open the password file, preserve the + * old cache contents; this seems better than dropping the cache if, + * say, we are temporarily out of filetable slots. */ - if (!pwd_cache || result == 0) + if (!(pwd_file = crypt_openpwdfile())) + return; + + /* free any old data */ + if (pwd_cache) { - /* free the old data only if this is a reload */ - if (pwd_cache) - { - while (pwd_cache_count--) - free((void *) pwd_cache[pwd_cache_count]); - free((void *) pwd_cache); - pwd_cache = NULL; - pwd_cache_count = 0; - } + while (--pwd_cache_count >= 0) + pfree(pwd_cache[pwd_cache_count]); + pfree(pwd_cache); + pwd_cache = NULL; + pwd_cache_count = 0; + } - if (!(pwd_file = crypt_openpwdfile())) - return; + /* + * Read the file and store its lines in current memory context, + * which we expect will be PostmasterContext. That context will + * live as long as we need the cache to live, ie, until just after + * each postmaster child has completed client authentication. + */ + while (fgets(buffer, sizeof(buffer), pwd_file) != NULL) + { + int blen; /* - * Here is where we load the data from pg_pwd. + * We must remove the return char at the end of the string, as + * this will affect the correct parsing of the password entry. */ - while (fgets(buffer, sizeof(buffer), pwd_file) != NULL) - { - /* - * We must remove the return char at the end of the string, as - * this will affect the correct parsing of the password entry. - */ - if (buffer[(result = strlen(buffer) - 1)] == '\n') - buffer[result] = '\0'; + if (buffer[(blen = strlen(buffer) - 1)] == '\n') + buffer[blen] = '\0'; + if (pwd_cache == NULL) pwd_cache = (char **) - realloc((void *) pwd_cache, - sizeof(char *) * (pwd_cache_count + 1)); - pwd_cache[pwd_cache_count++] = strdup(buffer); - } - FreeFile(pwd_file); - - /* - * Now sort the entries in the cache for faster searching later. - */ - qsort((void *) pwd_cache, pwd_cache_count, sizeof(char *), compar_user); + palloc(sizeof(char *) * (pwd_cache_count + 1)); + else + pwd_cache = (char **) + repalloc((void *) pwd_cache, + sizeof(char *) * (pwd_cache_count + 1)); + pwd_cache[pwd_cache_count++] = pstrdup(buffer); } -} -/*-------------------------------------------------------------------------*/ + FreeFile(pwd_file); + + /* + * Now sort the entries in the cache for faster searching later. + */ + qsort((void *) pwd_cache, pwd_cache_count, sizeof(char *), compar_user); +} -static void +/* + * Parse a line of the password file to extract password and valid-until date. + */ +static bool crypt_parsepwdentry(char *buffer, char **pwd, char **valdate) { char *parse = buffer; int count, i; + *pwd = NULL; + *valdate = NULL; + /* * skip to the password field */ for (i = 0; i < 6; i++) - parse += (strcspn(parse, CRYPT_PWD_FILE_SEPSTR) + 1); + { + parse += strcspn(parse, CRYPT_PWD_FILE_SEPSTR); + if (*parse == '\0') + return false; + parse++; + } /* * store a copy of user password to return */ count = strcspn(parse, CRYPT_PWD_FILE_SEPSTR); *pwd = (char *) palloc(count + 1); - strncpy(*pwd, parse, count); + memcpy(*pwd, parse, count); (*pwd)[count] = '\0'; - parse += (count + 1); + parse += count; + if (*parse == '\0') + { + pfree(*pwd); + *pwd = NULL; + return false; + } + parse++; /* * store a copy of the date login becomes invalid */ count = strcspn(parse, CRYPT_PWD_FILE_SEPSTR); *valdate = (char *) palloc(count + 1); - strncpy(*valdate, parse, count); + memcpy(*valdate, parse, count); (*valdate)[count] = '\0'; - parse += (count + 1); -} -/*-------------------------------------------------------------------------*/ + return true; +} -static int +/* + * Lookup a username in the password-file cache, + * return his password and valid-until date. + */ +static bool crypt_getloginfo(const char *user, char **passwd, char **valuntil) { - crypt_loadpwdfile(); + *passwd = NULL; + *valuntil = NULL; if (pwd_cache) { @@ -236,14 +240,12 @@ crypt_getloginfo(const char *user, char **passwd, char **valuntil) compar_user); if (pwd_entry) { - crypt_parsepwdentry(*pwd_entry, passwd, valuntil); - return STATUS_OK; + if (crypt_parsepwdentry(*pwd_entry, passwd, valuntil)) + return true; } } - *passwd = NULL; - *valuntil = NULL; - return STATUS_ERROR; + return false; } /*-------------------------------------------------------------------------*/ @@ -256,7 +258,7 @@ md5_crypt_verify(const Port *port, const char *user, const char *pgpass) *crypt_pwd; int retval = STATUS_ERROR; - if (crypt_getloginfo(user, &passwd, &valuntil) == STATUS_ERROR) + if (!crypt_getloginfo(user, &passwd, &valuntil)) return STATUS_ERROR; if (passwd == NULL || *passwd == '\0') diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index 1f232bfc3014f3291aca4726cc036a63bfcf0b6b..2c96486cd06e3c032e69a85741b9338c96d12d47 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -37,7 +37,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.253 2001/10/28 06:25:47 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.254 2001/11/02 18:39:57 tgl Exp $ * * NOTES * @@ -746,6 +746,12 @@ PostmasterMain(int argc, char *argv[]) if (pgstat_start() < 0) ExitPostmaster(1); + /* + * Load cached files for client authentication. + */ + load_hba_and_ident(); + load_password_cache(); + /* * We're ready to rock and roll... */ @@ -852,8 +858,6 @@ ServerLoop(void) later; struct timezone tz; - load_hba_and_ident(); - gettimeofday(&now, &tz); nSockets = initMasks(&readmask, &writemask); @@ -925,6 +929,7 @@ ServerLoop(void) got_SIGHUP = false; ProcessConfigFile(PGC_SIGHUP); load_hba_and_ident(); + load_password_cache(); } /* diff --git a/src/include/libpq/crypt.h b/src/include/libpq/crypt.h index 39d677e166b2d385ae6716a2971f8184b9171f98..76dabc89bd45d732de43e654b552a53967c67623 100644 --- a/src/include/libpq/crypt.h +++ b/src/include/libpq/crypt.h @@ -1,9 +1,13 @@ /*------------------------------------------------------------------------- * * crypt.h - * Interface to hba.c + * Interface to libpq/crypt.c * + * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California * + * $Id: crypt.h,v 1.17 2001/11/02 18:39:57 tgl Exp $ + * *------------------------------------------------------------------------- */ #ifndef PG_CRYPT_H @@ -11,27 +15,22 @@ #include "libpq/libpq-be.h" -#define CRYPT_PWD_FILE "pg_pwd" -#define CRYPT_PWD_FILE_SEPCHAR "'\\t'" #define CRYPT_PWD_FILE_SEPSTR "\t" -#define CRYPT_PWD_RELOAD_SUFX ".reload" -extern char **pwd_cache; -extern int pwd_cache_count; +#define MD5_PASSWD_LEN 35 + +#define isMD5(passwd) (strncmp((passwd),"md5",3) == 0 && \ + strlen(passwd) == MD5_PASSWD_LEN) -extern char *crypt_getpwdfilename(void); -extern char *crypt_getpwdreloadfilename(void); -extern int md5_crypt_verify(const Port *port, const char *user, const char *pgpass); +extern char *crypt_getpwdfilename(void); +extern void load_password_cache(void); +extern int md5_crypt_verify(const Port *port, const char *user, + const char *pgpass); extern bool md5_hash(const void *buff, size_t len, char *hexsum); extern bool CheckMD5Pwd(char *passwd, char *storedpwd, char *seed); extern bool EncryptMD5(const char *passwd, const char *salt, size_t salt_len, char *buf); -#define MD5_PASSWD_LEN 35 - -#define isMD5(passwd) (strncmp((passwd),"md5",3) == 0 && \ - strlen(passwd) == MD5_PASSWD_LEN) - #endif