Fix pg_pwd caching mechanism, which was broken by changes to fork

postmaster children before client auth step.  Postmaster now rereads
pg_pwd on receipt of SIGHUP, the same way that pg_hba.conf is handled.
No cycles need be expended to validate password cache validity during
connection startup.
This commit is contained in:
Tom Lane 2001-11-02 18:39:57 +00:00
parent 6babf6eab7
commit 8a069abd18
6 changed files with 187 additions and 146 deletions

View File

@ -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>
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>
<para>
The <filename>pg_hba.conf</filename> file is loaded only on startup
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>
<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

View File

@ -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>

View File

@ -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);
}

View File

@ -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);
/*
* 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_file = crypt_openpwdfile()))
return;
/* free any old data */
if (pwd_cache)
{
while (--pwd_cache_count >= 0)
pfree(pwd_cache[pwd_cache_count]);
pfree(pwd_cache);
pwd_cache = NULL;
pwd_cache_count = 0;
}
/*
* 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.
* 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.
*/
if (!pwd_cache || result == 0)
while (fgets(buffer, sizeof(buffer), pwd_file) != NULL)
{
/* 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;
}
if (!(pwd_file = crypt_openpwdfile()))
return;
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')

View File

@ -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();
}
/*

View File

@ -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;
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 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)
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);
#endif