-<!-- $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>
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>
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>
<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
<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>
</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</>
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.
<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</>
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
<!--
-$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">
<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>
* 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 $
*
*-------------------------------------------------------------------------
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
+#include <signal.h>
#include <unistd.h>
#include "access/heapam.h"
#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.
*tempname;
int bufsize;
FILE *fp;
- int flagfd;
mode_t oumask;
HeapScanDesc scan;
HeapTuple tuple;
/*
* 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"
/*
* 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);
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);
}
* 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 $
*
*-------------------------------------------------------------------------
*/
#include <errno.h>
#include <unistd.h>
+#ifdef HAVE_CRYPT_H
+#include <crypt.h>
+#endif
#include "libpq/crypt.h"
#include "libpq/libpq.h"
#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.
*/
}
/*
- * 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)
{
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)
{
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;
}
/*-------------------------------------------------------------------------*/
*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')
*
*
* 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
*
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...
*/
later;
struct timezone tz;
- load_hba_and_ident();
-
gettimeofday(&now, &tz);
nSockets = initMasks(&readmask, &writemask);
got_SIGHUP = false;
ProcessConfigFile(PGC_SIGHUP);
load_hba_and_ident();
+ load_password_cache();
}
/*
/*-------------------------------------------------------------------------
*
* 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
#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