From 3c1332e57397713a1ee258a33654755471493948 Mon Sep 17 00:00:00 2001 From: "Marc G. Fournier" Date: Tue, 30 Dec 1997 02:26:56 +0000 Subject: [PATCH] Slightly delayed patches from Todd...damn holidays :) o A new patch that contains the following changes: -- The pg_pwd file is now cached in the postmaster's memory. -- pg_pwd is reloaded when the postmaster detects a flag file creat()'ed by a backend. -- qsort() is used to sort loaded password entries, and bsearch() is is used to find entries in the pg_pwd cache. -- backends now copy the pg_user relation to pg_pwd.pid, and then rename the temp file to be pg_pwd. -- The delimiter for pg_pwd has been changed to a tab character. --- src/backend/commands/user.c | 38 +++++++-- src/backend/libpq/crypt.c | 204 ++++++++++++++++++++++++++++++++------------ src/bin/initdb/initdb.sh | 4 +- src/include/libpq/crypt.h | 7 ++ 4 files changed, 191 insertions(+), 62 deletions(-) diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c index df1c8046d7..0cb18e2b01 100644 --- a/src/backend/commands/user.c +++ b/src/backend/commands/user.c @@ -11,6 +11,10 @@ */ #include /* for sprintf() */ #include +#include +#include +#include +#include #include @@ -25,7 +29,6 @@ #include #include #include -#include #include #include @@ -40,10 +43,31 @@ static void UpdatePgPwdFile(char* sql) { char* filename; + char* tempname; + /* 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. + */ filename = crypt_getpwdfilename(); - sprintf(sql, "copy %s to '%s' using delimiters '#'", UserRelationName, filename); + tempname = (char*)malloc(strlen(filename) + 12); + sprintf(tempname, "%s.%d", filename, getpid()); + + /* Copy the contents of pg_user to the pg_pwd ASCII file using a the SEPCHAR + * character as the delimiter between fields. Then rename the file to its + * final name. + */ + sprintf(sql, "copy %s to '%s' using delimiters %s", UserRelationName, tempname, CRYPT_PWD_FILE_SEPCHAR); pg_exec_query(sql, (char**)NULL, (Oid*)NULL, 0); + rename(tempname, filename); + free((void*)tempname); + + /* 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. + */ + filename = crypt_getpwdreloadfilename(); + creat(filename, S_IRUSR | S_IWUSR); } /*--------------------------------------------------------------------- @@ -283,7 +307,7 @@ extern void RemoveUser(char* user) { HeapTuple tuple; Datum datum; Buffer buffer; - char sql[256]; + char sql[512]; bool n, inblock; int usesysid = -1, @@ -348,8 +372,8 @@ extern void RemoveUser(char* user) { if ((int)datum == usesysid) { datum = heap_getattr(tuple, buffer, Anum_pg_database_datname, pg_dsc, &n); if (memcmp((void*)datum, "template1", 9)) { - dbase = (char**)repalloc((void*)dbase, sizeof(char*) * (ndbase + 1)); - dbase[ndbase] = (char*)palloc(NAMEDATALEN + 1); + dbase = (char**)realloc((void*)dbase, sizeof(char*) * (ndbase + 1)); + dbase[ndbase] = (char*)malloc(NAMEDATALEN + 1); memcpy((void*)dbase[ndbase], (void*)datum, NAMEDATALEN); dbase[ndbase++][NAMEDATALEN] = '\0'; } @@ -362,11 +386,11 @@ extern void RemoveUser(char* user) { while (ndbase--) { elog(NOTICE, "Dropping database %s", dbase[ndbase]); sprintf(sql, "drop database %s", dbase[ndbase]); - pfree((void*)dbase[ndbase]); + free((void*)dbase[ndbase]); pg_exec_query(sql, (char**)NULL, (Oid*)NULL, 0); } if (dbase) - pfree((void*)dbase); + free((void*)dbase); /* Since pg_user is global over all databases, one of two things must be done * to insure complete consistency. First, pg_user could be made non-global. diff --git a/src/backend/libpq/crypt.c b/src/backend/libpq/crypt.c index 1386ff74ac..6e6555503a 100644 --- a/src/backend/libpq/crypt.c +++ b/src/backend/libpq/crypt.c @@ -4,6 +4,11 @@ * Look into pg_user and check the encrypted password with the one * passed in from the frontend. * + * Modification History + * + * Dec 17, 1997 - Todd A. Brandys + * Orignal Version Completed. + * * *------------------------------------------------------------------------- */ @@ -18,72 +23,163 @@ #include "postgres.h" #include "miscadmin.h" -#include "libpq/crypt.h" #include "utils/nabstime.h" -#include "utils/palloc.h" #include "storage/fd.h" +#include "libpq/crypt.h" + +char** pwd_cache = NULL; +int pwd_cache_count = 0; + +/*-------------------------------------------------------------------------*/ char* crypt_getpwdfilename() { - static char* filename = NULL; + static char* pfnam = NULL; - if (!filename) { - filename = (char*)palloc(strlen(DataDir) + strlen(CRYPT_PWD_FILE) + 2); - sprintf(filename, "%s/%s", DataDir, CRYPT_PWD_FILE); + if (!pfnam) { + pfnam = (char*)malloc(strlen(DataDir) + strlen(CRYPT_PWD_FILE) + 2); + sprintf(pfnam, "%s/%s", DataDir, CRYPT_PWD_FILE); } - return filename; + return pfnam; +} + +/*-------------------------------------------------------------------------*/ + +char* crypt_getpwdreloadfilename() { + + static char* rpfnam = NULL; + + if (!rpfnam) { + char* pwdfilename; + + pwdfilename = crypt_getpwdfilename(); + rpfnam = (char*)malloc(strlen(pwdfilename) + strlen(CRYPT_PWD_RELOAD_SUFX) + 1); + sprintf(rpfnam, "%s%s", pwdfilename, CRYPT_PWD_RELOAD_SUFX); + } + + return rpfnam; } /*-------------------------------------------------------------------------*/ static FILE* crypt_openpwdfile() { - char* filename; + FILE* pwdfile; filename = crypt_getpwdfilename(); - return (AllocateFile(filename, "r")); + pwdfile = AllocateFile(filename, "r"); + + return pwdfile; } /*-------------------------------------------------------------------------*/ static -void crypt_parsepwdfile(FILE* datafile, char** login, char** pwd, char** valdate) { +int compar_user(const void* user_a, const void* user_b) { - char buffer[256]; - char* parse; - int count, - i; + int min, + value; + char* login_a; + char* login_b; - fgets(buffer, 256, datafile); - parse = buffer; + login_a = *((char**)user_a); + login_b = *((char**)user_b); - /* store a copy of user login to return + /* We only really want to compare the user logins which are first. We look + * for the first SEPSTR char getting the number of chars there are before it. + * We only need to compare to the min count from the two strings. */ - count = strcspn(parse, "#"); - *login = (char*)palloc(count + 1); - strncpy(*login, parse, count); - (*login)[count] = '\0'; - parse += (count + 1); + min = strcspn(login_a, CRYPT_PWD_FILE_SEPSTR); + value = strcspn(login_b, CRYPT_PWD_FILE_SEPSTR); + if (value < min) + min = value; + + /* We add one to min so that the separator character is included in the + * comparison. Why? I believe this will prevent logins that are proper + * prefixes of other logins from being 'masked out'. Being conservative! + */ + return strncmp(login_a, login_b, min + 1); +} + +/*-------------------------------------------------------------------------*/ + +static +void crypt_loadpwdfile() { + + char* filename; + int result; + FILE* pwd_file; + char buffer[256]; + + filename = crypt_getpwdreloadfilename(); + result = unlink(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_user file to + * pg_pwd. Therefore we must now do a reload. + */ + if (!pwd_cache || !result) { + if (pwd_cache) { /* free the old data only if this is a reload */ + 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; + + /* Here is where we load the data from pg_pwd. + */ + while (fgets(buffer, 256, 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'; + + pwd_cache = (char**)realloc((void*)pwd_cache, sizeof(char*) * (pwd_cache_count + 1)); + pwd_cache[pwd_cache_count++] = strdup(buffer); + } + fclose(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 crypt_parsepwdentry(char* buffer, char** pwd, char** valdate) { + + char* parse = buffer; + int count, + i; /* skip to the password field */ - for (i = 0; i < 5; i++) - parse += (strcspn(parse, "#") + 1); + for (i = 0; i < 6; i++) + parse += (strcspn(parse, CRYPT_PWD_FILE_SEPSTR) + 1); /* store a copy of user password to return */ - count = strcspn(parse, "#"); - *pwd = (char*)palloc(count + 1); + count = strcspn(parse, CRYPT_PWD_FILE_SEPSTR); + *pwd = (char*)malloc(count + 1); strncpy(*pwd, parse, count); (*pwd)[count] = '\0'; parse += (count + 1); /* store a copy of date login becomes invalid */ - count = strcspn(parse, "#"); - *valdate = (char*)palloc(count + 1); + count = strcspn(parse, CRYPT_PWD_FILE_SEPSTR); + *valdate = (char*)malloc(count + 1); strncpy(*valdate, parse, count); (*valdate)[count] = '\0'; parse += (count + 1); @@ -92,33 +188,33 @@ void crypt_parsepwdfile(FILE* datafile, char** login, char** pwd, char** valdate /*-------------------------------------------------------------------------*/ static -void crypt_getloginfo(const char* user, char** passwd, char** valuntil) { +int crypt_getloginfo(const char* user, char** passwd, char** valuntil) { - FILE* datafile; - char* login; char* pwd; char* valdate; + void* fakeout; *passwd = NULL; *valuntil = NULL; + crypt_loadpwdfile(); - if (!(datafile = crypt_openpwdfile())) - return; + if (pwd_cache) { + char** pwd_entry; + char user_search[NAMEDATALEN + 2]; - while (!feof(datafile)) { - crypt_parsepwdfile(datafile, &login, &pwd, &valdate); - if (!strcmp(login, user)) { - pfree((void*)login); + sprintf(user_search, "%s\t", user); + fakeout = (void*)&user_search; + if ((pwd_entry = (char**)bsearch((void*)&fakeout, (void*)pwd_cache, pwd_cache_count, sizeof(char*), compar_user))) { + crypt_parsepwdentry(*pwd_entry, &pwd, &valdate); *passwd = pwd; *valuntil = valdate; - fclose(datafile); - return; + return STATUS_OK; } - pfree((void*)login); - pfree((void*)pwd); - pfree((void*)valdate); + + return STATUS_OK; } - fclose(datafile); + + return STATUS_ERROR; } /*-------------------------------------------------------------------------*/ @@ -128,16 +224,17 @@ MsgType crypt_salt(const char* user) { char* passwd; char* valuntil; - crypt_getloginfo(user, &passwd, &valuntil); + if (crypt_getloginfo(user, &passwd, &valuntil) == STATUS_ERROR) + return STARTUP_UNSALT_MSG; if (passwd == NULL || *passwd == '\0' || !strcmp(passwd, "\\N")) { - if (passwd) pfree((void*)passwd); - if (valuntil) pfree((void*)valuntil); + if (passwd) free((void*)passwd); + if (valuntil) free((void*)valuntil); return STARTUP_UNSALT_MSG; } - pfree((void*)passwd); - if (valuntil) pfree((void*)valuntil); + free((void*)passwd); + if (valuntil) free((void*)valuntil); return STARTUP_SALT_MSG; } @@ -152,11 +249,12 @@ int crypt_verify(Port* port, const char* user, const char* pgpass) { AbsoluteTime vuntil, current; - crypt_getloginfo(user, &passwd, &valuntil); + if (crypt_getloginfo(user, &passwd, &valuntil) == STATUS_ERROR) + return STATUS_ERROR; if (passwd == NULL || *passwd == '\0') { - if (passwd) pfree((void*)passwd); - if (valuntil) pfree((void*)valuntil); + if (passwd) free((void*)passwd); + if (valuntil) free((void*)valuntil); return STATUS_ERROR; } @@ -175,8 +273,8 @@ int crypt_verify(Port* port, const char* user, const char* pgpass) { retval = STATUS_OK; } - pfree((void*)passwd); - if (valuntil) pfree((void*)valuntil); + free((void*)passwd); + if (valuntil) free((void*)valuntil); return retval; } diff --git a/src/bin/initdb/initdb.sh b/src/bin/initdb/initdb.sh index 76ebc8ea66..48ac278f06 100644 --- a/src/bin/initdb/initdb.sh +++ b/src/bin/initdb/initdb.sh @@ -26,7 +26,7 @@ # # # IDENTIFICATION -# $Header: /cvsroot/pgsql/src/bin/initdb/Attic/initdb.sh,v 1.30 1997/12/04 00:27:31 scrappy Exp $ +# $Header: /cvsroot/pgsql/src/bin/initdb/Attic/initdb.sh,v 1.31 1997/12/30 02:26:43 scrappy Exp $ # #------------------------------------------------------------------------- @@ -355,7 +355,7 @@ echo "Altering pg_user acl" echo "REVOKE ALL ON pg_user FROM public" | postgres -F -Q -D$PGDATA template1 2>&1 > /dev/null |\ grep -v "'DEBUG:" -echo "COPY pg_user TO '$PGDATA/pg_pwd' USING DELIMITERS '#'" | postgres -F -Q -D$PGDATA template1 2>&1 > /dev/null |\ +echo "COPY pg_user TO '$PGDATA/pg_pwd' USING DELIMITERS '\\t'" | postgres -F -Q -D$PGDATA template1 2>&1 > /dev/null |\ grep -v "'DEBUG:" echo "loading pg_description" diff --git a/src/include/libpq/crypt.h b/src/include/libpq/crypt.h index 35e65ed642..45600394e0 100644 --- a/src/include/libpq/crypt.h +++ b/src/include/libpq/crypt.h @@ -12,8 +12,15 @@ #include #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(); extern MsgType crypt_salt(const char* user); extern int crypt_verify(Port* port, const char* user, const char* pgpass); -- 2.11.0