1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-27 12:41:57 +03:00

Authentication improvements:

A new pg_hba.conf column, USER
Allow specifiction of lists of users separated by commas
Allow group names specified by +
Allow include files containing lists of users specified by @
Allow lists of databases, and database files
Allow samegroup in database column to match group name matching dbname
Removal of secondary password files
Remove pg_passwd utility
Lots of code cleanup in user.c and hba.c
New data/global/pg_pwd format
New data/global/pg_group file
This commit is contained in:
Bruce Momjian
2002-04-04 04:25:54 +00:00
parent af10378ab0
commit 43a3543a4e
25 changed files with 1453 additions and 1931 deletions

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.94 2002/03/26 19:15:48 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/user.c,v 1.95 2002/04/04 04:25:45 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -15,6 +15,7 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include "access/heapam.h"
@ -27,6 +28,7 @@
#include "libpq/crypt.h"
#include "miscadmin.h"
#include "storage/pmsignal.h"
#include "utils/acl.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
@ -39,22 +41,75 @@ extern bool Password_encryption;
static void CheckPgUserAclNotNull(void);
/*---------------------------------------------------------------------
* write_password_file / update_pg_pwd
/*
* fputs_quote
*
* copy the modified contents of pg_shadow to a file used by the postmaster
* for user authentication. The file is stored as $PGDATA/global/pg_pwd.
* Outputs string in quotes, with double-quotes duplicated.
* We could use quote_ident(), but that expects varlena.
*/
static void fputs_quote(char *str, FILE *fp)
{
fputc('"', fp);
while (*str)
{
fputc(*str, fp);
if (*str == '"')
fputc('"', fp);
str++;
}
fputc('"', fp);
}
/*
* group_getfilename --- get full pathname of group file
*
* 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.
*
* We raise an error to force transaction rollback if we detect an illegal
* username or password --- illegal being defined as values that would
* mess up the pg_pwd parser.
*---------------------------------------------------------------------
* Note that result string is palloc'd, and should be freed by the caller.
*/
char *
group_getfilename(void)
{
int bufsize;
char *pfnam;
bufsize = strlen(DataDir) + strlen("/global/") +
strlen(USER_GROUP_FILE) + 1;
pfnam = (char *) palloc(bufsize);
snprintf(pfnam, bufsize, "%s/global/%s", DataDir, USER_GROUP_FILE);
return pfnam;
}
/*
* Get full pathname of password file.
* Note that result string is palloc'd, and should be freed by the caller.
*/
char *
user_getfilename(void)
{
int bufsize;
char *pfnam;
bufsize = strlen(DataDir) + strlen("/global/") +
strlen(PWD_FILE) + 1;
pfnam = (char *) palloc(bufsize);
snprintf(pfnam, bufsize, "%s/global/%s", DataDir, PWD_FILE);
return pfnam;
}
/*
* write_group_file for trigger update_pg_pwd_and_pg_group
*/
static void
write_password_file(Relation rel)
write_group_file(Relation urel, Relation grel)
{
char *filename,
*tempname;
@ -63,14 +118,14 @@ write_password_file(Relation rel)
mode_t oumask;
HeapScanDesc scan;
HeapTuple tuple;
TupleDesc dsc = RelationGetDescr(rel);
TupleDesc dsc = RelationGetDescr(grel);
/*
* Create a temporary filename to be renamed later. This prevents the
* backend from clobbering the pg_pwd file while the postmaster might
* backend from clobbering the pg_group file while the postmaster might
* be reading from it.
*/
filename = crypt_getpwdfilename();
filename = group_getfilename();
bufsize = strlen(filename) + 12;
tempname = (char *) palloc(bufsize);
@ -79,88 +134,89 @@ write_password_file(Relation rel)
fp = AllocateFile(tempname, "w");
umask(oumask);
if (fp == NULL)
elog(ERROR, "write_password_file: unable to write %s: %m", tempname);
elog(ERROR, "write_group_file: unable to write %s: %m", tempname);
/* read table */
scan = heap_beginscan(rel, false, SnapshotSelf, 0, NULL);
scan = heap_beginscan(grel, false, SnapshotSelf, 0, NULL);
while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
{
Datum datum_n,
datum_p,
datum_v;
bool null_n,
null_p,
null_v;
char *str_n,
*str_p,
*str_v;
int i;
Datum datum, grolist_datum;
bool isnull;
char *groname;
IdList *grolist_p;
AclId *aidp;
int i, j,
num;
char *usename;
bool first_user = true;
datum_n = heap_getattr(tuple, Anum_pg_shadow_usename, dsc, &null_n);
if (null_n)
continue; /* ignore NULL usernames */
str_n = DatumGetCString(DirectFunctionCall1(nameout, datum_n));
datum = heap_getattr(tuple, Anum_pg_group_groname, dsc, &isnull);
if (isnull)
continue; /* ignore NULL groupnames */
groname = (char *) DatumGetName(datum);
datum_p = heap_getattr(tuple, Anum_pg_shadow_passwd, dsc, &null_p);
grolist_datum = heap_getattr(tuple, Anum_pg_group_grolist, dsc, &isnull);
/* Ignore NULL group lists */
if (isnull)
continue;
grolist_p = DatumGetIdListP(grolist_datum);
/*
* It can be argued that people having a null password shouldn't
* be allowed to connect under password authentication, because
* they need to have a password set up first. If you think
* assuming an empty password in that case is better, change this
* logic to look something like the code for valuntil.
* Check for illegal characters in the group name.
*/
if (null_p)
i = strcspn(groname, "\n");
if (groname[i] != '\0')
{
pfree(str_n);
elog(LOG, "Invalid group name '%s'", groname);
continue;
}
str_p = DatumGetCString(DirectFunctionCall1(textout, datum_p));
datum_v = heap_getattr(tuple, Anum_pg_shadow_valuntil, dsc, &null_v);
if (null_v)
str_v = pstrdup("\\N");
else
str_v = DatumGetCString(DirectFunctionCall1(nabstimeout, datum_v));
/* be sure the IdList is not toasted */
/* scan it */
num = IDLIST_NUM(grolist_p);
aidp = IDLIST_DAT(grolist_p);
for (i = 0; i < num; ++i)
{
tuple = SearchSysCache(SHADOWSYSID,
PointerGetDatum(aidp[i]),
0, 0, 0);
if (HeapTupleIsValid(tuple))
{
usename = NameStr(((Form_pg_shadow) GETSTRUCT(tuple))->usename);
/*
* Check for illegal characters in the username and password.
*/
i = strcspn(str_n, CRYPT_PWD_FILE_SEPSTR "\n");
if (str_n[i] != '\0')
elog(ERROR, "Invalid user name '%s'", str_n);
i = strcspn(str_p, CRYPT_PWD_FILE_SEPSTR "\n");
if (str_p[i] != '\0')
elog(ERROR, "Invalid user password '%s'", str_p);
/*
* Check for illegal characters in the user name.
*/
j = strcspn(usename, "\n");
if (usename[j] != '\0')
{
elog(LOG, "Invalid user name '%s'", usename);
continue;
}
/*
* 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.
*/
fprintf(fp,
"%s"
CRYPT_PWD_FILE_SEPSTR
"0"
CRYPT_PWD_FILE_SEPSTR
"x"
CRYPT_PWD_FILE_SEPSTR
"x"
CRYPT_PWD_FILE_SEPSTR
"x"
CRYPT_PWD_FILE_SEPSTR
"x"
CRYPT_PWD_FILE_SEPSTR
"%s"
CRYPT_PWD_FILE_SEPSTR
"%s\n",
str_n,
str_p,
str_v);
/* File format is:
* "dbname" "user1","user2","user3"
* This matches pg_hba.conf.
*/
if (first_user)
{
fputs_quote(groname, fp);
fputs("\t", fp);
}
else
fputs(" ", fp);
pfree(str_n);
pfree(str_p);
pfree(str_v);
first_user = false;
fputs_quote(usename, fp);
ReleaseSysCache(tuple);
}
}
if (!first_user)
fputs("\n", fp);
/* if IdList was toasted, free detoasted copy */
if ((Pointer) grolist_p != DatumGetPointer(grolist_datum))
pfree(grolist_p);
}
heap_endscan(scan);
@ -178,29 +234,154 @@ write_password_file(Relation rel)
pfree((void *) tempname);
pfree((void *) filename);
}
/*
* write_password_file for trigger update_pg_pwd_and_pg_group
*
* copy the modified contents of pg_shadow to a file used by the postmaster
* 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.
*
* We raise an error to force transaction rollback if we detect an illegal
* username or password --- illegal being defined as values that would
* mess up the pg_pwd parser.
*/
static void
write_user_file(Relation urel)
{
char *filename,
*tempname;
int bufsize;
FILE *fp;
mode_t oumask;
HeapScanDesc scan;
HeapTuple tuple;
TupleDesc dsc = RelationGetDescr(urel);
/*
* Signal the postmaster to reload its password-file cache.
* Create a temporary filename to be renamed later. This prevents the
* backend from clobbering the pg_pwd file while the postmaster might
* be reading from it.
*/
SendPostmasterSignal(PMSIGNAL_PASSWORD_CHANGE);
filename = user_getfilename();
bufsize = strlen(filename) + 12;
tempname = (char *) palloc(bufsize);
snprintf(tempname, bufsize, "%s.%d", filename, MyProcPid);
oumask = umask((mode_t) 077);
fp = AllocateFile(tempname, "w");
umask(oumask);
if (fp == NULL)
elog(ERROR, "write_password_file: unable to write %s: %m", tempname);
/* read table */
scan = heap_beginscan(urel, false, SnapshotSelf, 0, NULL);
while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
{
Datum datum;
bool isnull;
char *usename,
*passwd,
*valuntil;
int i;
datum = heap_getattr(tuple, Anum_pg_shadow_usename, dsc, &isnull);
if (isnull)
continue; /* ignore NULL usernames */
usename = (char *) DatumGetName(datum);
datum = heap_getattr(tuple, Anum_pg_shadow_passwd, dsc, &isnull);
/*
* It can be argued that people having a null password shouldn't
* be allowed to connect under password authentication, because
* they need to have a password set up first. If you think
* assuming an empty password in that case is better, change this
* logic to look something like the code for valuntil.
*/
if (isnull)
continue;
passwd = DatumGetCString(DirectFunctionCall1(textout, datum));
datum = heap_getattr(tuple, Anum_pg_shadow_valuntil, dsc, &isnull);
if (isnull)
valuntil = pstrdup("");
else
valuntil = DatumGetCString(DirectFunctionCall1(nabstimeout, datum));
/*
* Check for illegal characters in the username and password.
*/
i = strcspn(usename, "\n");
if (usename[i] != '\0')
elog(ERROR, "Invalid user name '%s'", usename);
i = strcspn(passwd, "\n");
if (passwd[i] != '\0')
elog(ERROR, "Invalid user password '%s'", passwd);
/*
* 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.
*/
fputs_quote(usename, fp);
fputs(" ", fp);
fputs_quote(passwd, fp);
fputs(" ", fp);
fputs_quote(valuntil, fp);
pfree(passwd);
pfree(valuntil);
}
heap_endscan(scan);
fflush(fp);
if (ferror(fp))
elog(ERROR, "%s: %m", tempname);
FreeFile(fp);
/*
* 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 *) tempname);
pfree((void *) filename);
}
/* This is the wrapper for triggers. */
Datum
update_pg_pwd(PG_FUNCTION_ARGS)
update_pg_pwd_and_pg_group(PG_FUNCTION_ARGS)
{
/*
* ExclusiveLock ensures no one modifies pg_shadow while we read it,
* and that only one backend rewrites the flat file at a time. It's
* OK to allow normal reads of pg_shadow in parallel, however.
*/
Relation rel = heap_openr(ShadowRelationName, ExclusiveLock);
Relation urel = heap_openr(ShadowRelationName, ExclusiveLock);
Relation grel = heap_openr(GroupRelationName, ExclusiveLock);
write_password_file(rel);
write_user_file(urel);
write_group_file(urel, grel);
/* OK to release lock, since we did not modify the relation */
heap_close(rel, ExclusiveLock);
heap_close(grel, ExclusiveLock);
heap_close(urel, ExclusiveLock);
/*
* Signal the postmaster to reload its password & group-file cache.
*/
SendPostmasterSignal(PMSIGNAL_PASSWORD_CHANGE);
return PointerGetDatum(NULL);
}
@ -445,15 +626,15 @@ CreateUser(CreateUserStmt *stmt)
AlterGroup(&ags, "CREATE USER");
}
/*
* Write the updated pg_shadow data to the flat password file.
*/
write_password_file(pg_shadow_rel);
/*
* Now we can clean up; but keep lock until commit.
*/
heap_close(pg_shadow_rel, NoLock);
/*
* Write the updated pg_shadow and pg_group data to the flat file.
*/
update_pg_pwd_and_pg_group(NULL);
}
@ -679,15 +860,15 @@ AlterUser(AlterUserStmt *stmt)
ReleaseSysCache(tuple);
heap_freetuple(new_tuple);
/*
* Write the updated pg_shadow data to the flat password file.
*/
write_password_file(pg_shadow_rel);
/*
* Now we can clean up.
*/
heap_close(pg_shadow_rel, NoLock);
/*
* Write the updated pg_shadow and pg_group data to the flat file.
*/
update_pg_pwd_and_pg_group(NULL);
}
@ -733,7 +914,7 @@ AlterUserSet(AlterUserSetStmt *stmt)
{
Datum datum;
bool isnull;
ArrayType *a;
ArrayType *array;
repl_null[Anum_pg_shadow_useconfig-1] = ' ';
@ -741,17 +922,17 @@ AlterUserSet(AlterUserSetStmt *stmt)
Anum_pg_shadow_useconfig, &isnull);
if (valuestr)
a = GUCArrayAdd(isnull
array = GUCArrayAdd(isnull
? NULL
: (ArrayType *) pg_detoast_datum((struct varlena *)datum),
stmt->variable, valuestr);
else
a = GUCArrayDelete(isnull
array = GUCArrayDelete(isnull
? NULL
: (ArrayType *) pg_detoast_datum((struct varlena *)datum),
stmt->variable);
repl_val[Anum_pg_shadow_useconfig-1] = PointerGetDatum(a);
repl_val[Anum_pg_shadow_useconfig-1] = PointerGetDatum(array);
}
newtuple = heap_modifytuple(oldtuple, rel, repl_val, repl_null, repl_repl);
@ -846,7 +1027,7 @@ DropUser(DropUserStmt *stmt)
datum = heap_getattr(tmp_tuple, Anum_pg_database_datname,
pg_dsc, &null);
Assert(!null);
dbname = DatumGetCString(DirectFunctionCall1(nameout, datum));
dbname = (char *) DatumGetName(datum);
elog(ERROR, "DROP USER: user \"%s\" owns database \"%s\", cannot be removed%s",
user, dbname,
(length(stmt->users) > 1) ? " (no users removed)" : "");
@ -900,15 +1081,15 @@ DropUser(DropUserStmt *stmt)
CommandCounterIncrement();
}
/*
* Write the updated pg_shadow data to the flat password file.
*/
write_password_file(pg_shadow_rel);
/*
* Now we can clean up.
*/
heap_close(pg_shadow_rel, NoLock);
/*
* Write the updated pg_shadow and pg_group data to the flat file.
*/
update_pg_pwd_and_pg_group(NULL);
}
@ -1111,6 +1292,11 @@ CreateGroup(CreateGroupStmt *stmt)
}
heap_close(pg_group_rel, NoLock);
/*
* Write the updated pg_shadow and pg_group data to the flat file.
*/
update_pg_pwd_and_pg_group(NULL);
}
@ -1366,7 +1552,15 @@ AlterGroup(AlterGroupStmt *stmt, const char *tag)
ReleaseSysCache(group_tuple);
/*
* Write the updated pg_shadow and pg_group data to the flat files.
*/
heap_close(pg_group_rel, NoLock);
/*
* Write the updated pg_shadow and pg_group data to the flat file.
*/
update_pg_pwd_and_pg_group(NULL);
}
@ -1419,4 +1613,9 @@ DropGroup(DropGroupStmt *stmt)
elog(ERROR, "DROP GROUP: group \"%s\" does not exist", stmt->name);
heap_close(pg_group_rel, NoLock);
/*
* Write the updated pg_shadow and pg_group data to the flat file.
*/
update_pg_pwd_and_pg_group(NULL);
}

View File

@ -4,7 +4,7 @@
# Makefile for libpq subsystem (backend half of libpq interface)
#
# IDENTIFICATION
# $Header: /cvsroot/pgsql/src/backend/libpq/Makefile,v 1.29 2002/03/04 01:46:02 tgl Exp $
# $Header: /cvsroot/pgsql/src/backend/libpq/Makefile,v 1.30 2002/04/04 04:25:46 momjian Exp $
#
#-------------------------------------------------------------------------
@ -14,9 +14,7 @@ include $(top_builddir)/src/Makefile.global
# be-fsstubs is here for historical reasons, probably belongs elsewhere
OBJS = be-fsstubs.o \
auth.o crypt.o hba.o md5.o password.o \
pqcomm.o pqformat.o pqsignal.o
OBJS = be-fsstubs.o auth.o crypt.o hba.o md5.o pqcomm.o pqformat.o pqsignal.o
all: SUBSYS.o

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.79 2002/03/05 07:57:45 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.80 2002/04/04 04:25:47 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -34,7 +34,6 @@
#include "miscadmin.h"
static void sendAuthRequest(Port *port, AuthRequest areq);
static int checkPassword(Port *port, char *user, char *password);
static int old_be_recvauth(Port *port);
static int map_old_to_new(Port *port, UserAuth old, int status);
static void auth_failed(Port *port, int status);
@ -381,7 +380,7 @@ recv_and_check_passwordv0(Port *port)
saved = port->auth_method;
port->auth_method = uaPassword;
status = checkPassword(port, user, password);
status = md5_crypt_verify(port, user, password);
port->auth_method = saved;
@ -663,7 +662,7 @@ pam_passwd_conv_proc(int num_msg, const struct pam_message ** msg, struct pam_re
initStringInfo(&buf);
pq_getstr(&buf);
/* Do not echo failed password to logs, for security. */
elog(DEBUG5, "received PAM packet");
@ -810,26 +809,13 @@ recv_and_check_password_packet(Port *port)
/* Do not echo failed password to logs, for security. */
elog(DEBUG5, "received password packet");
result = checkPassword(port, port->user, buf.data);
result = md5_crypt_verify(port, port->user, buf.data);
pfree(buf.data);
return result;
}
/*
* Handle `password' and `crypt' records. If an auth argument was
* specified, use the respective file. Else use pg_shadow passwords.
*/
static int
checkPassword(Port *port, char *user, char *password)
{
if (port->auth_arg[0] != '\0')
return verify_password(port, user, password);
return md5_crypt_verify(port, user, password);
}
/*
* Server demux routine for incoming authentication information for protocol
* version 0.

View File

@ -9,13 +9,12 @@
* 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.44 2002/03/04 01:46:03 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/libpq/crypt.c,v 1.45 2002/04/04 04:25:47 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include <errno.h>
#include <unistd.h>
#ifdef HAVE_CRYPT_H
#include <crypt.h>
@ -25,231 +24,10 @@
#include "libpq/libpq.h"
#include "miscadmin.h"
#include "storage/fd.h"
#include "nodes/pg_list.h"
#include "utils/nabstime.h"
#define CRYPT_PWD_FILE "pg_pwd"
static char **pwd_cache = NULL;
static int pwd_cache_count = 0;
/*
* crypt_getpwdfilename --- get full pathname of password file
*
* Note that result string is palloc'd, and should be freed by the caller.
*/
char *
crypt_getpwdfilename(void)
{
int bufsize;
char *pfnam;
bufsize = strlen(DataDir) + 8 + strlen(CRYPT_PWD_FILE) + 1;
pfnam = (char *) palloc(bufsize);
snprintf(pfnam, bufsize, "%s/global/%s", DataDir, CRYPT_PWD_FILE);
return pfnam;
}
/*
* Open the password file if possible (return NULL if not)
*/
static FILE *
crypt_openpwdfile(void)
{
char *filename;
FILE *pwdfile;
filename = crypt_getpwdfilename();
pwdfile = AllocateFile(filename, "r");
if (pwdfile == NULL && errno != ENOENT)
elog(LOG, "could not open %s: %m", filename);
pfree(filename);
return pwdfile;
}
/*
* Compare two password-file lines on the basis of their usernames.
*
* Can also be used to compare just a username against a password-file
* line (for bsearch).
*/
static int
compar_user(const void *user_a, const void *user_b)
{
char *login_a;
char *login_b;
int len_a,
len_b,
result;
login_a = *((char **) user_a);
login_b = *((char **) user_b);
/*
* We only really want to compare the user logins which are first and
* are terminated by CRYPT_PWD_FILE_SEPSTR. (NB: this code
* effectively assumes that CRYPT_PWD_FILE_SEPSTR is just one char.)
*/
len_a = strcspn(login_a, CRYPT_PWD_FILE_SEPSTR);
len_b = strcspn(login_b, CRYPT_PWD_FILE_SEPSTR);
result = strncmp(login_a, login_b, Min(len_a, len_b));
if (result == 0) /* one could be a prefix of the other */
result = (len_a - len_b);
return result;
}
/*
* Load or reload the password-file cache
*/
void
load_password_cache(void)
{
FILE *pwd_file;
char buffer[1024];
/*
* 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;
}
/*
* 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;
/*
* 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[(blen = strlen(buffer) - 1)] == '\n')
buffer[blen] = '\0';
if (pwd_cache == NULL)
pwd_cache = (char **)
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);
}
/*
* 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);
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);
memcpy(*pwd, parse, count);
(*pwd)[count] = '\0';
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);
memcpy(*valdate, parse, count);
(*valdate)[count] = '\0';
return true;
}
/*
* 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)
{
*passwd = NULL;
*valuntil = NULL;
if (pwd_cache)
{
char **pwd_entry;
pwd_entry = (char **) bsearch((void *) &user,
(void *) pwd_cache,
pwd_cache_count,
sizeof(char *),
compar_user);
if (pwd_entry)
{
if (crypt_parsepwdentry(*pwd_entry, passwd, valuntil))
return true;
}
}
return false;
}
/*-------------------------------------------------------------------------*/
int
md5_crypt_verify(const Port *port, const char *user, const char *pgpass)
{
@ -257,10 +35,14 @@ md5_crypt_verify(const Port *port, const char *user, const char *pgpass)
*valuntil,
*crypt_pwd;
int retval = STATUS_ERROR;
List **line;
if (!crypt_getloginfo(user, &passwd, &valuntil))
if ((line = get_user_line(user)) == NULL)
return STATUS_ERROR;
passwd = lfirst(lnext(lnext(*line)));
valuntil = lfirst(lnext(lnext(lnext(*line))));
if (passwd == NULL || *passwd == '\0')
{
if (passwd)

File diff suppressed because it is too large Load Diff

View File

@ -1,109 +0,0 @@
/*
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: password.c,v 1.41 2002/03/04 01:46:03 tgl Exp $
*
*/
#include <errno.h>
#include <unistd.h>
#include "postgres.h"
#ifdef HAVE_CRYPT_H
#include <crypt.h>
#endif
#include "libpq/libpq.h"
#include "libpq/password.h"
#include "libpq/crypt.h"
#include "miscadmin.h"
#include "storage/fd.h"
int
verify_password(const Port *port, const char *user, const char *password)
{
char *pw_file_fullname;
FILE *pw_file;
pw_file_fullname = (char *) palloc(strlen(DataDir) + strlen(port->auth_arg) + 2);
strcpy(pw_file_fullname, DataDir);
strcat(pw_file_fullname, "/");
strcat(pw_file_fullname, port->auth_arg);
pw_file = AllocateFile(pw_file_fullname, PG_BINARY_R);
if (!pw_file)
{
elog(LOG, "verify_password: Unable to open password file \"%s\": %m",
pw_file_fullname);
pfree(pw_file_fullname);
return STATUS_ERROR;
}
pfree(pw_file_fullname);
while (!feof(pw_file))
{
char pw_file_line[255],
*p,
*test_user,
*test_pw;
if (fgets(pw_file_line, sizeof(pw_file_line), pw_file) == NULL)
pw_file_line[0] = '\0';
/* kill the newline */
if (strlen(pw_file_line) > 0 &&
pw_file_line[strlen(pw_file_line) - 1] == '\n')
pw_file_line[strlen(pw_file_line) - 1] = '\0';
p = pw_file_line;
test_user = strtok(p, ":");
if (!test_user || test_user[0] == '\0')
continue;
test_pw = strtok(NULL, ":");
if (strcmp(user, test_user) == 0)
{
/* we're outta here one way or the other, so close file */
FreeFile(pw_file);
/*
* If the password is empty or "+" then we use the regular
* pg_shadow passwords. If we use crypt then we have to use
* pg_shadow passwords no matter what. This is because the
* current code needs non-encrypted passwords to encrypt with
* a random salt.
*/
if (port->auth_method == uaMD5 ||
port->auth_method == uaCrypt ||
test_pw == NULL ||
test_pw[0] == '\0' ||
strcmp(test_pw, "+") == 0)
return md5_crypt_verify(port, user, password);
/* external password file is crypt-only */
if (strcmp(crypt(password, test_pw), test_pw) == 0)
{
/* it matched. */
return STATUS_OK;
}
elog(LOG, "verify_password: password mismatch for '%s'",
user);
return STATUS_ERROR;
}
}
FreeFile(pw_file);
elog(LOG, "verify_password: user '%s' not found in password file",
user);
return STATUS_ERROR;
}

View File

@ -42,22 +42,36 @@
#
# Format:
#
# host DBNAME IP_ADDRESS ADDRESS_MASK AUTH_TYPE [AUTH_ARGUMENT]
# host DATABASE USER IP_ADDRESS MASK AUTH_TYPE
#
# DBNAME can be:
# o a database name
# o "all", which means the record matches all databases
# o "sameuser", which means users can only access databases whose name
# is the same as their username
# DATABASE can be:
# o a database name
# o "sameuser", which means a user can only access a database with the
# same name as their user name
# o "samegroup", which means a user can only access databases when they
# are members of a group with the same name as the database name
# o "all", which matches all databases
# o a list of database names, separated by commas
# o a file name containing database names, starting with '@'
#
# IP_ADDRESS and ADDRESS_MASK are standard dotted decimal IP address and
# USER can be:
# o a user name
# o "all", which matches all users
# o a list of user names, separated by commas
# o a group name, starting with '+'
# o a file name containing user names, starting with '@'
#
# Files read using '@' can contain comma-separated database/user names,
# or one name per line. The files can also contain comments using '#'.
#
# IP_ADDRESS and MASK are standard dotted decimal IP address and
# mask values. IP addresses can only be specified numerically, not as
# domain or host names.
#
# Do not prevent the superuser from accessing the template1 database.
# Various utility commands need access to template1.
#
# AUTH_TYPE and AUTH_ARGUMENT are described below.
# AUTH_TYPE is described below.
#
#
# hostssl
@ -65,10 +79,8 @@
#
# The format of this record is identical to "host".
#
#
#
# It specifies hosts that required connection via secure SSL. "host"
# records allow SSL connections too, but "hostssl" only allows SSL-secured
# It specifies hosts that require connection via secure SSL. "host"
# allows SSL connections too, but "hostssl" requires SSL-secured
# connections.
#
# This keyword is only available if the server was compiled with SSL
@ -82,10 +94,10 @@
# connections. Without this record, UNIX-socket connections are disallowed
#
# Format:
# local DBNAME AUTH_TYPE [AUTH_ARGUMENT]
# local DATABASE USER AUTH_TYPE
#
# This format is identical to the "host" record type except there are no
# IP_ADDRESS and ADDRESS_MASK fields.
# IP_ADDRESS and MASK fields.
#
#
#
@ -96,57 +108,38 @@
# has an AUTH_TYPE.
#
# trust:
# No authentication is done. Any valid username is accepted,
# No authentication is done. Any valid user name is accepted,
# including the PostgreSQL superuser. This option should
# be used only for hosts where all users are trusted.
#
# password:
# Authentication is done by matching a password supplied
# in clear by the host. If no AUTH_ARGUMENT is used, the
# password is compared with the user's entry in the
# pg_shadow table.
#
# If AUTH_ARGUMENT is specified, the username is looked up
# in that file in the $PGDATA directory. If the username
# is found but there is no password, the password is looked
# up in pg_shadow. If a password exists in the file, it is
# used instead. These secondary files allow fine-grained
# control over who can access which databases and whether
# a non-default password is required. The same file can be
# used in multiple records for easier administration.
# Password files can be maintained with the pg_passwd(1)
# utility. Remember, these passwords override pg_shadow
# passwords. Also, such passwords are passed over the network
# in cleartext, meaning this should not be used on untrusted
# networks.
#
# md5:
# Same as "password", except the password is encrypted over the
# network. This method is preferable to "password" and "crypt"
# except for pre-7.2 clients that don't support it. NOTE: md5 can
# use usernames stored in secondary password files but ignores
# passwords stored there. The pg_shadow password will always be
# used.
# Requires the client to supply an MD5 encrypted password for
# authentication. This is the only method that allows encrypted
# passwords to be stored in pg_shadow.
#
# crypt:
# Same as "md5", but uses crypt for pre-7.2 clients. You can
# not store encrypted passwords in pg_shadow if you use this
# method.
# Same as "md5", but uses crypt for pre-7.2 clients.
#
# password:
# Same as "md5", but the password is sent in cleartext over
# the network. This should not be used on untrusted
# networks.
#
# ident:
# For TCP/IP connections, authentication is done by contacting the
# ident server on the client host. This is only as secure as the
# client machine. On machines that support unix-domain socket
# credentials (currently Linux, FreeBSD, NetBSD, and BSD/OS), this
# method also works for "local" connections.
# client machine. You must specify the map name after the 'ident'
# keyword. It determines how to map remote user names to
# PostgreSQL user names. If you use "sameuser", the user names are
# assumed to be identical. If not, the map name is looked up
# in the $PGDATA/pg_ident.conf file. The connection is accepted if
# that file contains an entry for this map name with the
# ident-supplied username and the requested PostgreSQL username.
#
# AUTH_ARGUMENT is required. It determines how to map remote user
# names to PostgreSQL user names. If you use "sameuser", the user
# names are assumed to be the identical. If not, AUTH_ARGUMENT is
# assumed to be a map name found in the $PGDATA/pg_ident.conf
# file. The connection is accepted if that file contains an entry
# for this map name with the ident-supplied username and the
# requested PostgreSQL username.
# On machines that support unix-domain socket credentials
# (currently Linux, FreeBSD, NetBSD, and BSD/OS), ident allows
# reliable authentication of 'local' connections without ident
# running on the local machine.
#
# krb4:
# Kerberos V4 authentication is used. Allowed only for
@ -157,10 +150,10 @@
# TCP/IP connections, not for local UNIX-domain sockets.
#
# pam:
# Authentication is passed off to PAM (PostgreSQL must be
# configured --with-pam), using the default service name
# "postgresql" - you can specify your own service name by
# setting AUTH_ARGUMENT to the desired service name.
# Authentication is done by PAM using the default service name
# "postgresql". You can specify your own service name by adding
# the service name after the 'pam' keyword. To use this option,
# PostgreSQL must be configured --with-pam.
#
# reject:
# Reject the connection. This is used to reject certain hosts
@ -177,60 +170,70 @@
# Allow any user on the local system to connect to any database under any
# username using Unix-domain sockets (the default for local connections):
#
# TYPE DATABASE IP_ADDRESS MASK AUTH_TYPE AUTH_ARGUMENT
# local all trust
# TYPE DATABASE USER IP_ADDRESS MASK AUTH_TYPE
# local all all trust
#
# The same using local loopback TCP/IP connections:
#
# TYPE DATABASE IP_ADDRESS MASK AUTH_TYPE AUTH_ARGUMENT
# host all 127.0.0.1 255.255.255.255 trust
# TYPE DATABASE USER IP_ADDRESS MASK AUTH_TYPE
# host all all 127.0.0.1 255.255.255.255 trust
#
# Allow any user from any host with IP address 192.168.93.x to
# connect to database "template1" as the same username that ident reports
# for the connection (typically his Unix username):
#
# TYPE DATABASE IP_ADDRESS MASK AUTH_TYPE AUTH_ARGUMENT
# host template1 192.168.93.0 255.255.255.0 ident sameuser
# TYPE DATABASE USER IP_ADDRESS MASK AUTH_TYPE
# host template1 all 192.168.93.0 255.255.255.0 ident sameuser
#
# Allow a user from host 192.168.12.10 to connect to database "template1"
# if the user's password in pg_shadow is correctly supplied:
# if the user's password is correctly supplied:
#
# TYPE DATABASE IP_ADDRESS MASK AUTH_TYPE AUTH_ARGUMENT
# host template1 192.168.12.10 255.255.255.255 md5
# TYPE DATABASE USER IP_ADDRESS MASK AUTH_TYPE
# host template1 all 192.168.12.10 255.255.255.255 md5
#
# In the absence of preceding "host" lines, these two lines will reject
# all connection from 192.168.54.1 (since that entry will be matched
# first), but allow Kerberos V5 connections from anywhere else on the
# Internet. The zero mask means that no bits of the host IP address are
# considered, so it matches any host:
# considered so it matches any host:
#
#
# TYPE DATABASE IP_ADDRESS MASK AUTH_TYPE AUTH_ARGUMENT
# host all 192.168.54.1 255.255.255.255 reject
# host all 0.0.0.0 0.0.0.0 krb5
# TYPE DATABASE USER IP_ADDRESS MASK AUTH_TYPE
# host all all 192.168.54.1 255.255.255.255 reject
# host all all 0.0.0.0 0.0.0.0 krb5
#
# Allow users from 192.168.x.x hosts to connect to any database if they
# pass the ident check. For example, if ident says the user is "james" and
# he requests to connect as PostgreSQL user "guest", the connection is
# allowed if there is an entry in $PGDATA/pg_ident.conf with map name
# "phoenix" that says "james" is allowed to connect as "guest":
#
# TYPE DATABASE IP_ADDRESS MASK AUTH_TYPE AUTH_ARGUMENT
# host all 192.168.0.0 255.255.0.0 ident phoenix
#
# If these are the only two lines for local connections, they will allow
# local users to connect only to their own databases (databases with the
# same name as their user name) except for administrators who may connect
# to all databases. The file $PGDATA/admins lists the user names who are
# permitted to connect to all databases. Passwords are required in all
# cases. (If you prefer to use ident authorization, an ident map can
# serve a parallel purpose to the password list file used here.)
#
# TYPE DATABASE IP_ADDRESS MASK AUTH_TYPE AUTH_ARGUMENT
# local sameuser md5
# local all md5 admins
#
# See $PGDATA/pg_ident.conf for more information on Ident maps.
#
# TYPE DATABASE USER IP_ADDRESS MASK AUTH_TYPE
# host all all 192.168.0.0 255.255.0.0 ident phoenix
#
# If these are the only three lines for local connections, they will
# allow local users to connect only to their own databases (databases
# with the same name as their user name) except for administrators and
# members of group 'support' who may connect to all databases . The file
# $PGDATA/admins contains a list of user names. Passwords are required in
# all cases.
#
# TYPE DATABASE USER IP_ADDRESS MASK AUTH_TYPE
# local sameuser all md5
# local all @admins md5
# local all +support md5
#
# The last two lines above can be combined into a single line:
#
# local all @admins,+support md5
#
# The database column can also use lists and file names, but not groups:
#
# local db1,db2,@demodbs all md5
#
#
#
#
#
#
@ -250,7 +253,7 @@
# configuration is probably too liberal for you. Change it to use
# something other than "trust" authentication.
#
# TYPE DATABASE IP_ADDRESS MASK AUTH_TYPE AUTH_ARGUMENT
# TYPE DATABASE USER IP_ADDRESS MASK AUTH_TYPE
local all trust
host all 127.0.0.1 255.255.255.255 trust
local all all trust
host all all 127.0.0.1 255.255.255.255 trust

View File

@ -37,7 +37,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.271 2002/03/15 19:20:35 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.272 2002/04/04 04:25:48 momjian Exp $
*
* NOTES
*
@ -748,8 +748,10 @@ PostmasterMain(int argc, char *argv[])
/*
* Load cached files for client authentication.
*/
load_hba_and_ident();
load_password_cache();
load_hba();
load_ident();
load_user();
load_group();
/*
* We're ready to rock and roll...
@ -1389,7 +1391,8 @@ SIGHUP_handler(SIGNAL_ARGS)
elog(LOG, "Received SIGHUP, reloading configuration files");
SignalChildren(SIGHUP);
ProcessConfigFile(PGC_SIGHUP);
load_hba_and_ident();
load_hba();
load_ident();
}
PG_SETMASK(&UnBlockSig);
@ -2288,9 +2291,10 @@ sigusr1_handler(SIGNAL_ARGS)
if (CheckPostmasterSignal(PMSIGNAL_PASSWORD_CHANGE))
{
/*
* Password file has changed.
* Password or group file has changed.
*/
load_password_cache();
load_user();
load_group();
}
if (CheckPostmasterSignal(PMSIGNAL_WAKEN_CHILDREN))

View File

@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/quote.c,v 1.6 2001/10/28 06:25:53 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/adt/quote.c,v 1.7 2002/04/04 04:25:49 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -124,8 +124,6 @@ do_quote_ident(text *iptr)
{
if (*cp1 == '"')
*cp2++ = '"';
if (*cp1 == '\\')
*cp2++ = '\\';
*cp2++ = *cp1++;
}
*cp2++ = '"';
@ -234,8 +232,6 @@ do_quote_ident(text *iptr)
if (*cp1 == '"')
*cp2++ = '"';
if (*cp1 == '\\')
*cp2++ = '\\';
*cp2++ = *cp1++;
len--;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/init/miscinit.c,v 1.85 2002/03/04 04:45:27 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/init/miscinit.c,v 1.86 2002/04/04 04:25:49 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -236,85 +236,17 @@ pg_convert2(PG_FUNCTION_ARGS)
#ifdef CYR_RECODE
#define MAX_TOKEN 80
/*
* Some standard C libraries, including GNU, have an isblank() function.
* Others, including Solaris, do not. So we have our own.
*/
static bool
isblank(const char c)
{
return c == ' ' || c == '\t';
}
/*
* Grab one token out of fp. Tokens are strings of non-blank
* characters bounded by blank characters, beginning of line, and end
* of line. Blank means space or tab. Return the token as *buf.
* Leave file positioned to character immediately after the token or
* EOF, whichever comes first. If no more tokens on line, return null
* string as *buf and position file to beginning of next line or EOF,
* whichever comes first.
*/
static void
next_token(FILE *fp, char *buf, const int bufsz)
{
int c;
char *eb = buf + (bufsz - 1);
/* Move over initial token-delimiting blanks */
while ((c = getc(fp)) != EOF && isblank(c))
;
if (c != EOF && c != '\n')
{
/*
* build a token in buf of next characters up to EOF, eol, or
* blank. If the token gets too long, we still parse it
* correctly, but the excess characters are not stored into *buf.
*/
while (c != EOF && c != '\n' && !isblank(c))
{
if (buf < eb)
*buf++ = c;
c = getc(fp);
}
/*
* Put back the char right after the token (critical in case it is
* eol, since we need to detect end-of-line at next call).
*/
if (c != EOF)
ungetc(c, fp);
}
*buf = '\0';
}
static void
read_through_eol(FILE *file)
{
int c;
while ((c = getc(file)) != EOF && c != '\n')
;
}
void
SetCharSet(void)
{
FILE *file;
char *p;
char *filename;
char *map_file;
char buf[MAX_TOKEN];
int i,
c;
unsigned char FromChar,
ToChar;
char ChTable[80];
char ChTable[MAX_TOKEN];
for (i = 0; i < 128; i++)
{
@ -325,39 +257,40 @@ SetCharSet(void)
if (IsUnderPostmaster)
{
GetCharSetByHost(ChTable, MyProcPort->raddr.in.sin_addr.s_addr, DataDir);
p = ChTable;
filename = ChTable;
}
else
p = getenv("PG_RECODETABLE");
filename = getenv("PG_RECODETABLE");
if (p && *p != '\0')
if (filename && *filename != '\0')
{
map_file = palloc(strlen(DataDir) + strlen(p) + 2);
sprintf(map_file, "%s/%s", DataDir, p);
file = AllocateFile(map_file, PG_BINARY_R);
map_file = palloc(strlen(DataDir) + strlen(filename) + 2);
sprintf(map_file, "%s/%s", DataDir, filename);
file = AllocateFile(map_file, "r");
pfree(map_file);
if (file == NULL)
return;
while ((c = getc(file)) != EOF)
while (!feof(file))
{
if (c == '#')
read_through_eol(file);
else
next_token(file, buf, sizeof(buf));
if (buf[0] != '\0')
{
/* Read the FromChar */
ungetc(c, file);
FromChar = strtoul(buf, 0, 0);
/* Read the ToChar */
next_token(file, buf, sizeof(buf));
if (buf[0] != '\0')
{
FromChar = strtoul(buf, 0, 0);
/* Read the ToChar */
next_token(file, buf, sizeof(buf));
if (buf[0] != '\0')
ToChar = strtoul(buf, 0, 0);
RecodeForwTable[FromChar - 128] = ToChar;
RecodeBackTable[ToChar - 128] = FromChar;
/* read to EOL */
while (!feof(file) && buf[0])
{
ToChar = strtoul(buf, 0, 0);
RecodeForwTable[FromChar - 128] = ToChar;
RecodeBackTable[ToChar - 128] = FromChar;
read_through_eol(file);
next_token(file, buf, sizeof(buf));
elog(LOG, "SetCharSet: unknown tag %s in file %s"
buf, filename);
}
}
}
@ -366,6 +299,7 @@ SetCharSet(void)
}
}
char *
convertstr(unsigned char *buff, int len, int dest)
{
@ -384,7 +318,206 @@ convertstr(unsigned char *buff, int len, int dest)
}
return ch;
}
#endif
#define CHARSET_FILE "charset.conf"
#define MAX_CHARSETS 10
#define KEY_HOST 1
#define KEY_BASE 2
#define KEY_TABLE 3
struct CharsetItem
{
char Orig[MAX_TOKEN];
char Dest[MAX_TOKEN];
char Table[MAX_TOKEN];
};
static bool
CharSetInRange(char *buf, int host)
{
int valid,
i,
FromAddr,
ToAddr,
tmp;
struct in_addr file_ip_addr;
char *p;
unsigned int one = 0x80000000,
NetMask = 0;
unsigned char mask;
p = strchr(buf, '/');
if (p)
{
*p++ = '\0';
valid = inet_aton(buf, &file_ip_addr);
if (valid)
{
mask = strtoul(p, 0, 0);
FromAddr = ntohl(file_ip_addr.s_addr);
ToAddr = ntohl(file_ip_addr.s_addr);
for (i = 0; i < mask; i++)
{
NetMask |= one;
one >>= 1;
}
FromAddr &= NetMask;
ToAddr = ToAddr | ~NetMask;
tmp = ntohl(host);
return ((unsigned) tmp >= (unsigned) FromAddr &&
(unsigned) tmp <= (unsigned) ToAddr);
}
}
else
{
p = strchr(buf, '-');
if (p)
{
*p++ = '\0';
valid = inet_aton(buf, &file_ip_addr);
if (valid)
{
FromAddr = ntohl(file_ip_addr.s_addr);
valid = inet_aton(p, &file_ip_addr);
if (valid)
{
ToAddr = ntohl(file_ip_addr.s_addr);
tmp = ntohl(host);
return ((unsigned) tmp >= (unsigned) FromAddr &&
(unsigned) tmp <= (unsigned) ToAddr);
}
}
}
else
{
valid = inet_aton(buf, &file_ip_addr);
if (valid)
{
FromAddr = file_ip_addr.s_addr;
return (unsigned) FromAddr == (unsigned) host;
}
}
}
return false;
}
static void
GetCharSetByHost(char *TableName, int host, const char *DataDir)
{
FILE *file;
char buf[MAX_TOKEN],
BaseCharset[MAX_TOKEN],
OrigCharset[MAX_TOKEN],
DestCharset[MAX_TOKEN],
HostCharset[MAX_TOKEN],
*map_file;
int key,
ChIndex = 0,
c,
i,
bufsize;
struct CharsetItem *ChArray[MAX_CHARSETS];
*TableName = '\0';
bufsize = (strlen(DataDir) + strlen(CHARSET_FILE) + 2) * sizeof(char);
map_file = (char *) palloc(bufsize);
snprintf(map_file, bufsize, "%s/%s", DataDir, CHARSET_FILE);
file = AllocateFile(map_file, "r");
pfree(map_file);
if (file == NULL)
{
/* XXX should we log a complaint? */
return;
}
while (!feof(file))
{
next_token(file, buf, sizeof(buf));
if (buf[0] != '\0')
{
key = 0;
if (strcasecmp(buf, "HostCharset") == 0)
key = KEY_HOST;
else if (strcasecmp(buf, "BaseCharset") == 0)
key = KEY_BASE;
else if (strcasecmp(buf, "RecodeTable") == 0)
key = KEY_TABLE;
else
elog(LOG, "GetCharSetByHost: unknown tag %s in file %s"
buf, CHARSET_FILE);
switch (key)
{
case KEY_HOST:
/* Read the host */
next_token(file, buf, sizeof(buf));
if (buf[0] != '\0')
{
if (CharSetInRange(buf, host))
{
/* Read the charset */
next_token(file, buf, sizeof(buf));
if (buf[0] != '\0')
strcpy(HostCharset, buf);
}
}
break;
case KEY_BASE:
/* Read the base charset */
next_token(file, buf, sizeof(buf));
if (buf[0] != '\0')
strcpy(BaseCharset, buf);
break;
case KEY_TABLE:
/* Read the original charset */
next_token(file, buf, sizeof(buf));
if (buf[0] != '\0')
{
strcpy(OrigCharset, buf);
/* Read the destination charset */
next_token(file, buf, sizeof(buf));
if (buf[0] != '\0')
{
strcpy(DestCharset, buf);
/* Read the table filename */
next_token(file, buf, sizeof(buf));
if (buf[0] != '\0')
{
ChArray[ChIndex] =
(struct CharsetItem *) palloc(sizeof(struct CharsetItem));
strcpy(ChArray[ChIndex]->Orig, OrigCharset);
strcpy(ChArray[ChIndex]->Dest, DestCharset);
strcpy(ChArray[ChIndex]->Table, buf);
ChIndex++;
}
}
}
break;
}
/* read to EOL */
while (!feof(file) && buf[0])
{
next_token(file, buf, sizeof(buf));
elog(LOG, "GetCharSetByHost: unknown tag %s in file %s"
buf, CHARSET_FILE);
}
}
}
FreeFile(file);
for (i = 0; i < ChIndex; i++)
{
if (strcasecmp(BaseCharset, ChArray[i]->Orig) == 0 &&
strcasecmp(HostCharset, ChArray[i]->Dest) == 0)
strncpy(TableName, ChArray[i]->Table, 79);
pfree(ChArray[i]);
}
}
#endif /* CYR_RECODE */

View File

@ -5,7 +5,7 @@
# Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
# Portions Copyright (c) 1994, Regents of the University of California
#
# $Header: /cvsroot/pgsql/src/bin/Makefile,v 1.34 2001/02/18 18:33:59 momjian Exp $
# $Header: /cvsroot/pgsql/src/bin/Makefile,v 1.35 2002/04/04 04:25:50 momjian Exp $
#
#-------------------------------------------------------------------------
@ -14,7 +14,7 @@ top_builddir = ../..
include $(top_builddir)/src/Makefile.global
DIRS := initdb initlocation ipcclean pg_ctl pg_dump pg_id \
pg_passwd psql scripts pg_config
psql scripts pg_config
ifdef MULTIBYTE
DIRS += pg_encoding

View File

@ -27,7 +27,7 @@
# Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
# Portions Copyright (c) 1994, Regents of the University of California
#
# $Header: /cvsroot/pgsql/src/bin/initdb/Attic/initdb.sh,v 1.146 2002/04/03 05:39:32 petere Exp $
# $Header: /cvsroot/pgsql/src/bin/initdb/Attic/initdb.sh,v 1.147 2002/04/04 04:25:50 momjian Exp $
#
#-------------------------------------------------------------------------
@ -603,9 +603,11 @@ $ECHO_N "initializing pg_shadow... "$ECHO_C
"$PGPATH"/postgres $PGSQL_OPT template1 >/dev/null <<EOF
-- Create a trigger so that direct updates to pg_shadow will be written
-- to the flat password file pg_pwd
-- to the flat password/group files pg_pwd and pg_group
CREATE TRIGGER pg_sync_pg_pwd AFTER INSERT OR UPDATE OR DELETE ON pg_shadow \
FOR EACH ROW EXECUTE PROCEDURE update_pg_pwd();
FOR EACH ROW EXECUTE PROCEDURE update_pg_pwd_and_pg_group();
CREATE TRIGGER pg_sync_pg_group AFTER INSERT OR UPDATE OR DELETE ON pg_group \
FOR EACH ROW EXECUTE PROCEDURE update_pg_pwd_and_pg_group();
-- needs to be done before alter user, because alter user checks that
-- pg_shadow is secure ...
REVOKE ALL on pg_shadow FROM public;
@ -643,6 +645,11 @@ EOF
echo "The password file wasn't generated. Please report this problem." 1>&2
exit_nicely
fi
if [ ! -f "$PGDATA"/global/pg_group ]; then
echo
echo "The group file wasn't generated. Please report this problem." 1>&2
exit_nicely
fi
echo "ok"
fi

View File

@ -1,37 +0,0 @@
# $Header: /cvsroot/pgsql/src/bin/pg_passwd/Attic/Makefile,v 1.14 2001/05/12 19:49:47 petere Exp $
subdir = src/bin/pg_passwd
top_builddir = ../../..
include $(top_builddir)/src/Makefile.global
OBJS = pg_passwd.o
ifdef STRDUP
OBJS += $(top_builddir)/src/utils/strdup.o
endif
all: pg_passwd
pg_passwd: $(OBJS)
$(CC) $(CFLAGS) $(LDFLAGS) $^ $(LIBS) -o $@
$(top_builddir)/src/utils/strdup.o:
$(MAKE) -C $(top_builddir)/src/utils strdup.o
install: all installdirs
$(INSTALL_PROGRAM) pg_passwd$(X) $(DESTDIR)$(bindir)/pg_passwd$(X)
installdirs:
$(mkinstalldirs) $(DESTDIR)$(bindir)
uninstall:
rm -f $(DESTDIR)$(bindir)/pg_passwd$(X)
depend dep:
$(CC) -MM $(CFLAGS) *.c >depend
clean distclean maintainer-clean:
rm -f pg_passwd$(X) pg_passwd.o
ifeq (depend,$(wildcard depend))
include depend
endif

View File

@ -1,412 +0,0 @@
/*
* @(#) pg_passwd.c 1.8 09:13:16 97/07/02 Y. Ichikawa
*/
#include "postgres_fe.h"
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include <ctype.h>
#define issaltchar(c) (isalnum((unsigned char) (c)) || (c) == '.' || (c) == '/')
#ifdef HAVE_TERMIOS_H
#include <termios.h>
#endif
#ifdef HAVE_CRYPT_H
#include <crypt.h>
#else
extern char *crypt(const char *, const char *);
#endif
/*
* We assume that the output of crypt(3) is always 13 characters,
* and that at most 8 characters can usefully be sent to it.
*
* Postgres usernames are assumed to be less than NAMEDATALEN chars long.
*/
#define CLEAR_PASSWD_LEN 8 /* not including null */
#define CRYPTED_PASSWD_LEN 13 /* not including null */
const char *progname;
static void usage(void);
static void read_pwd_file(char *filename);
static void write_pwd_file(char *filename, char *bkname);
static void encrypt_pwd(char key[CLEAR_PASSWD_LEN + 1],
char salt[3],
char passwd[CRYPTED_PASSWD_LEN + 1]);
static void prompt_for_username(char *username);
static void prompt_for_password(char *prompt, char *password);
static void
usage(void)
{
printf("%s manipulates flat text password files for PostgreSQL.\n\n", progname);
printf("Usage:\n %s PASSWORD-FILE\n\n", progname);
printf("Report bugs to <pgsql-bugs@postgresql.org>.\n");
}
typedef struct
{
char *uname;
char *pwd;
char *rest;
} pg_pwd;
#define MAXPWDS 1024
pg_pwd pwds[MAXPWDS];
int npwds = 0;
static void
read_pwd_file(char *filename)
{
FILE *fp;
static char line[512];
static char ans[128];
int i;
try_again:
fp = fopen(filename, PG_BINARY_R);
if (fp == NULL)
{
if (errno == ENOENT)
{
printf("File \"%s\" does not exist. Create? (y/n): ", filename);
fflush(stdout);
if (fgets(ans, sizeof(ans), stdin) == NULL)
exit(1);
switch (ans[0])
{
case 'y':
case 'Y':
fp = fopen(filename, PG_BINARY_W);
if (fp == NULL)
{
perror(filename);
exit(1);
}
fclose(fp);
goto try_again;
default:
/* cannot continue */
exit(1);
}
}
else
{
perror(filename);
exit(1);
}
}
/* read all the entries */
for (npwds = 0;
npwds < MAXPWDS && fgets(line, sizeof(line), fp) != NULL;
++npwds)
{
int l;
char *p,
*q;
l = strlen(line);
if (line[l - 1] == '\n')
line[l - 1] = '\0';
else
{
fprintf(stderr, "%s:%d: line too long\n",
filename, npwds + 1);
exit(1);
}
/* get user name */
p = line;
if ((q = strchr(p, ':')) != NULL)
*q = '\0';
if (strlen(p) == 0)
{
fprintf(stderr, "%s:%d: null user name\n",
filename, npwds + 1);
exit(1);
}
pwds[npwds].uname = strdup(p);
/* check for duplicate user name */
for (i = 0; i < npwds; ++i)
{
if (strcmp(pwds[i].uname, pwds[npwds].uname) == 0)
{
fprintf(stderr, "Duplicate username %s in entry %d\n",
pwds[npwds].uname, npwds + 1);
exit(1);
}
}
/* get password field */
if (q)
{
p = q + 1;
q = strchr(p, ':');
if (q != NULL)
*(q++) = '\0';
if (strlen(p) != CRYPTED_PASSWD_LEN && strcmp(p, "+") != 0)
{
fprintf(stderr, "%s:%d: warning: invalid password length\n",
filename, npwds + 1);
}
pwds[npwds].pwd = strdup(p);
}
else
pwds[npwds].pwd = NULL;
/* rest of the line is treated as is */
if (q == NULL)
pwds[npwds].rest = NULL;
else
pwds[npwds].rest = strdup(q);
}
fclose(fp);
}
static void
write_pwd_file(char *filename, char *bkname)
{
FILE *fp;
int i;
/* make the backup file */
link_again:
if (link(filename, bkname))
{
if (errno == EEXIST)
{
unlink(bkname);
goto link_again;
}
perror(bkname);
exit(1);
}
if (unlink(filename))
{
perror(filename);
exit(1);
}
/* open file */
if ((fp = fopen(filename, PG_BINARY_W)) == NULL)
{
perror(filename);
exit(1);
}
/* write file */
for (i = 0; i < npwds; ++i)
{
fprintf(fp, "%s", pwds[i].uname);
if (pwds[i].pwd)
fprintf(fp, ":%s", pwds[i].pwd);
if (pwds[i].rest)
fprintf(fp, ":%s", pwds[i].rest);
fprintf(fp, "\n");
}
fclose(fp);
}
static void
encrypt_pwd(char key[CLEAR_PASSWD_LEN + 1],
char salt[3],
char passwd[CRYPTED_PASSWD_LEN + 1])
{
int n;
/* select a salt, if not already given */
if (salt[0] == '\0')
{
srand(time(NULL));
do
{
n = rand() % 256;
} while (!issaltchar(n));
salt[0] = n;
do
{
n = rand() % 256;
} while (!issaltchar(n));
salt[1] = n;
salt[2] = '\0';
}
/* get encrypted password */
strcpy(passwd, crypt(key, salt));
#ifdef PG_PASSWD_DEBUG
/* show it */
fprintf(stderr, "key = %s, salt = %s, password = %s\n",
key, salt, passwd);
#endif
}
static void
prompt_for_username(char *username)
{
int length;
printf("Username: ");
fflush(stdout);
if (fgets(username, NAMEDATALEN, stdin) == NULL)
username[0] = '\0';
length = strlen(username);
if (length > 0 && username[length - 1] != '\n')
{
/* eat rest of the line */
char buf[128];
int buflen;
do
{
if (fgets(buf, sizeof(buf), stdin) == NULL)
break;
buflen = strlen(buf);
} while (buflen > 0 && buf[buflen - 1] != '\n');
}
if (length > 0 && username[length - 1] == '\n')
username[length - 1] = '\0';
}
static void
prompt_for_password(char *prompt, char *password)
{
int length;
#ifdef HAVE_TERMIOS_H
struct termios t_orig,
t;
#endif
#ifdef HAVE_TERMIOS_H
tcgetattr(0, &t);
t_orig = t;
t.c_lflag &= ~ECHO;
tcsetattr(0, TCSADRAIN, &t);
#endif
printf(prompt);
fflush(stdout);
if (fgets(password, CLEAR_PASSWD_LEN + 1, stdin) == NULL)
password[0] = '\0';
#ifdef HAVE_TERMIOS_H
tcsetattr(0, TCSADRAIN, &t_orig);
#endif
length = strlen(password);
if (length > 0 && password[length - 1] != '\n')
{
/* eat rest of the line */
char buf[128];
int buflen;
do
{
if (fgets(buf, sizeof(buf), stdin) == NULL)
break;
buflen = strlen(buf);
} while (buflen > 0 && buf[buflen - 1] != '\n');
}
if (length > 0 && password[length - 1] == '\n')
password[length - 1] = '\0';
printf("\n");
}
int
main(int argc, char *argv[])
{
char *filename;
char bkname[MAXPGPATH];
char username[NAMEDATALEN];
char salt[3];
char key[CLEAR_PASSWD_LEN + 1],
key2[CLEAR_PASSWD_LEN + 1];
char e_passwd[CRYPTED_PASSWD_LEN + 1];
int i;
progname = argv[0];
if (argc != 2)
{
fprintf(stderr, "%s: too %s arguments\nTry '%s --help' for more information.\n",
progname, argc > 2 ? "many" : "few", progname);
exit(1);
}
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
usage();
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
{
puts("pg_passwd (PostgreSQL) " PG_VERSION);
exit(0);
}
if (argv[1][0] == '-')
{
fprintf(stderr, "%s: invalid option: %s\nTry '%s --help' for more information.\n",
progname, argv[1], progname);
exit(1);
}
filename = argv[1];
/* open file */
read_pwd_file(filename);
/* ask for the user name and the password */
prompt_for_username(username);
prompt_for_password("New password: ", key);
prompt_for_password("Re-enter new password: ", key2);
if (strcmp(key, key2) != 0)
{
fprintf(stderr, "Password mismatch\n");
exit(1);
}
salt[0] = '\0';
encrypt_pwd(key, salt, e_passwd);
/* check password entry */
for (i = 0; i < npwds; ++i)
{
if (strcmp(pwds[i].uname, username) == 0)
{ /* found */
pwds[i].pwd = strdup(e_passwd);
break;
}
}
if (i == npwds)
{ /* did not exist */
if (npwds == MAXPWDS)
{
fprintf(stderr, "Cannot handle so many entries\n");
exit(1);
}
pwds[npwds].uname = strdup(username);
pwds[npwds].pwd = strdup(e_passwd);
pwds[npwds].rest = NULL;
++npwds;
}
/* write back the file */
sprintf(bkname, "%s.bk", filename);
write_pwd_file(filename, bkname);
return 0;
}

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: pg_proc.h,v 1.224 2002/03/29 19:06:19 tgl Exp $
* $Id: pg_proc.h,v 1.225 2002/04/04 04:25:52 momjian Exp $
*
* NOTES
* The script catalog/genbki.sh reads this file and generates .bki
@ -2101,8 +2101,8 @@ DESCR("does not match LIKE expression, case-insensitive");
DATA(insert OID = 1637 ( like_escape PGUID 12 f t t t 2 f 25 "25 25" 100 0 0 100 like_escape - _null_ ));
DESCR("convert match pattern to use backslash escapes");
DATA(insert OID = 1689 ( update_pg_pwd PGUID 12 f t f t 0 f 0 "" 100 0 0 100 update_pg_pwd - _null_ ));
DESCR("update pg_pwd file");
DATA(insert OID = 1689 ( update_pg_pwd_and_pg_group PGUID 12 f t f t 0 f 0 "" 100 0 0 100 update_pg_pwd_and_pg_group - _null_ ));
DESCR("update pg_pwd and pg_group files");
/* Oracle Compatibility Related Functions - By Edmund Mergl <E.Mergl@bawue.de> */
DATA(insert OID = 868 ( strpos PGUID 12 f t t t 2 f 23 "25 25" 100 0 0 100 textpos - _null_ ));

View File

@ -3,15 +3,23 @@
* user.h
*
*
* $Id: user.h,v 1.17 2002/03/01 22:45:17 petere Exp $
* $Id: user.h,v 1.18 2002/04/04 04:25:53 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef USER_H
#define USER_H
#include "fmgr.h"
#include "nodes/parsenodes.h"
#define PWD_FILE "pg_pwd"
#define USER_GROUP_FILE "pg_group"
extern char *group_getfilename(void);
extern char *user_getfilename(void);
extern void CreateUser(CreateUserStmt *stmt);
extern void AlterUser(AlterUserStmt *stmt);
extern void AlterUserSet(AlterUserSetStmt *stmt);
@ -21,6 +29,6 @@ extern void CreateGroup(CreateGroupStmt *stmt);
extern void AlterGroup(AlterGroupStmt *stmt, const char *tag);
extern void DropGroup(DropGroupStmt *stmt);
extern Datum update_pg_pwd(PG_FUNCTION_ARGS);
extern Datum update_pg_pwd_and_pg_group(PG_FUNCTION_ARGS);
#endif /* USER_H */

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
*
* $Id: crypt.h,v 1.19 2001/11/12 01:52:46 momjian Exp $
* $Id: crypt.h,v 1.20 2002/04/04 04:25:53 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -15,8 +15,6 @@
#include "libpq/libpq-be.h"
#define CRYPT_PWD_FILE_SEPSTR "\t"
/* Also defined in interfaces/odbc/md5.h */
#define MD5_PASSWD_LEN 35
@ -24,9 +22,6 @@
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);

View File

@ -4,7 +4,7 @@
* Interface to hba.c
*
*
* $Id: hba.h,v 1.31 2001/11/05 17:46:33 momjian Exp $
* $Id: hba.h,v 1.32 2002/04/04 04:25:54 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -15,15 +15,14 @@
#include <netinet/in.h>
#endif
#include "nodes/pg_list.h"
#define CONF_FILE "pg_hba.conf"
/* Name of the config file */
#define USERMAP_FILE "pg_ident.conf"
/* Name of the usermap file */
#define OLD_CONF_FILE "pg_hba"
/* Name of the config file in prior releases of Postgres. */
#define IDENT_PORT 113
/* Standard TCP port number for Ident service. Assigned by IANA */
@ -46,8 +45,15 @@ typedef enum UserAuth
typedef struct Port hbaPort;
#define MAX_TOKEN 256
extern void next_token(FILE *fp, char *buf, const int bufsz);
extern List **get_user_line(const char *user);
extern void load_hba(void);
extern void load_ident(void);
extern void load_user(void);
extern void load_group(void);
extern int hba_getauthmethod(hbaPort *port);
extern int authident(hbaPort *port);
extern void load_hba_and_ident(void);
#endif

View File

@ -12,7 +12,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: miscadmin.h,v 1.101 2002/03/04 01:46:04 tgl Exp $
* $Id: miscadmin.h,v 1.102 2002/04/04 04:25:51 momjian Exp $
*
* NOTES
* some of the information in this file should be moved to
@ -219,7 +219,6 @@ extern int FindExec(char *full_path, const char *argv0,
extern int CheckPathAccess(char *path, char *name, int open_mode);
#ifdef CYR_RECODE
extern void GetCharSetByHost(char *TableName, int host, const char *DataDir);
extern void SetCharSet(void);
extern char *convertstr(unsigned char *buff, int len, int dest);
#endif

View File

@ -30,7 +30,7 @@ WHERE (p1.prolang = 0 OR p1.prorettype = 0 OR
AND p1.proname !~ '^pl[^_]+_call_handler$'
AND p1.proname !~ '^RI_FKey_'
AND p1.proname !~ 'costestimate$'
AND p1.proname != 'update_pg_pwd';
AND p1.proname != 'update_pg_pwd_and_pg_group';
oid | proname
-----+---------
(0 rows)

View File

@ -33,7 +33,7 @@ WHERE (p1.prolang = 0 OR p1.prorettype = 0 OR
AND p1.proname !~ '^pl[^_]+_call_handler$'
AND p1.proname !~ '^RI_FKey_'
AND p1.proname !~ 'costestimate$'
AND p1.proname != 'update_pg_pwd';
AND p1.proname != 'update_pg_pwd_and_pg_group';
-- Look for conflicting proc definitions (same names and input datatypes).
-- (This test should be dead code now that we have the unique index