OSDN Git Service

Slightly delayed patches from Todd...damn holidays :)
authorMarc G. Fournier <scrappy@hub.org>
Tue, 30 Dec 1997 02:26:56 +0000 (02:26 +0000)
committerMarc G. Fournier <scrappy@hub.org>
Tue, 30 Dec 1997 02:26:56 +0000 (02:26 +0000)
  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
src/backend/libpq/crypt.c
src/bin/initdb/initdb.sh
src/include/libpq/crypt.h

index df1c804..0cb18e2 100644 (file)
  */
 #include <stdio.h>                             /* for sprintf() */
 #include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
 
 #include <postgres.h>
 
@@ -25,7 +29,6 @@
 #include <storage/lmgr.h>
 #include <tcop/tcopprot.h>
 #include <utils/acl.h>
-#include <utils/palloc.h>
 #include <utils/rel.h>
 #include <commands/user.h>
 
@@ -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.
index 1386ff7..6e65555 100644 (file)
@@ -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.
+ *
  *
  *-------------------------------------------------------------------------
  */
 
 #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;
 }
index 76ebc8e..48ac278 100644 (file)
@@ -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"
index 35e65ed..4560039 100644 (file)
 #include <libpq/pqcomm.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();
 extern MsgType crypt_salt(const char* user);
 extern int crypt_verify(Port* port, const char* user, const char* pgpass);