*/
#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>
#include <storage/lmgr.h>
#include <tcop/tcopprot.h>
#include <utils/acl.h>
-#include <utils/palloc.h>
#include <utils/rel.h>
#include <commands/user.h>
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);
}
/*---------------------------------------------------------------------
HeapTuple tuple;
Datum datum;
Buffer buffer;
- char sql[256];
+ char sql[512];
bool n,
inblock;
int usesysid = -1,
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';
}
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.
* 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);
/*-------------------------------------------------------------------------*/
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;
}
/*-------------------------------------------------------------------------*/
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;
}
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;
}
retval = STATUS_OK;
}
- pfree((void*)passwd);
- if (valuntil) pfree((void*)valuntil);
+ free((void*)passwd);
+ if (valuntil) free((void*)valuntil);
return retval;
}