diff options
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/commands/user.c | 369 | ||||
-rw-r--r-- | src/backend/libpq/Makefile | 6 | ||||
-rw-r--r-- | src/backend/libpq/auth.c | 24 | ||||
-rw-r--r-- | src/backend/libpq/crypt.c | 232 | ||||
-rw-r--r-- | src/backend/libpq/hba.c | 808 | ||||
-rw-r--r-- | src/backend/libpq/password.c | 109 | ||||
-rw-r--r-- | src/backend/libpq/pg_hba.conf.sample | 179 | ||||
-rw-r--r-- | src/backend/postmaster/postmaster.c | 16 | ||||
-rw-r--r-- | src/backend/utils/adt/quote.c | 6 | ||||
-rw-r--r-- | src/backend/utils/init/miscinit.c | 317 |
10 files changed, 1098 insertions, 968 deletions
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c index 98ee4308975..9ebd6fa3a2d 100644 --- a/src/backend/commands/user.c +++ b/src/backend/commands/user.c @@ -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,8 +41,205 @@ extern bool Password_encryption; static void CheckPgUserAclNotNull(void); -/*--------------------------------------------------------------------- - * write_password_file / update_pg_pwd + + +/* + * fputs_quote + * + * 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 + * + * 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_group_file(Relation urel, Relation grel) +{ + char *filename, + *tempname; + int bufsize; + FILE *fp; + mode_t oumask; + HeapScanDesc scan; + HeapTuple tuple; + TupleDesc dsc = RelationGetDescr(grel); + + /* + * Create a temporary filename to be renamed later. This prevents the + * backend from clobbering the pg_group file while the postmaster might + * be reading from it. + */ + filename = group_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_group_file: unable to write %s: %m", tempname); + + /* read table */ + scan = heap_beginscan(grel, false, SnapshotSelf, 0, NULL); + while (HeapTupleIsValid(tuple = heap_getnext(scan, 0))) + { + Datum datum, grolist_datum; + bool isnull; + char *groname; + IdList *grolist_p; + AclId *aidp; + int i, j, + num; + char *usename; + bool first_user = true; + + datum = heap_getattr(tuple, Anum_pg_group_groname, dsc, &isnull); + if (isnull) + continue; /* ignore NULL groupnames */ + groname = (char *) DatumGetName(datum); + + grolist_datum = heap_getattr(tuple, Anum_pg_group_grolist, dsc, &isnull); + /* Ignore NULL group lists */ + if (isnull) + continue; + + grolist_p = DatumGetIdListP(grolist_datum); + /* + * Check for illegal characters in the group name. + */ + i = strcspn(groname, "\n"); + if (groname[i] != '\0') + { + elog(LOG, "Invalid group name '%s'", groname); + continue; + } + + /* 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 user name. + */ + j = strcspn(usename, "\n"); + if (usename[j] != '\0') + { + elog(LOG, "Invalid user name '%s'", usename); + continue; + } + + /* 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); + + 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); + + 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); +} + + + +/* + * 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. @@ -51,10 +250,9 @@ static void CheckPgUserAclNotNull(void); * 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_password_file(Relation rel) +write_user_file(Relation urel) { char *filename, *tempname; @@ -63,14 +261,14 @@ write_password_file(Relation rel) mode_t oumask; HeapScanDesc scan; HeapTuple tuple; - TupleDesc dsc = RelationGetDescr(rel); + TupleDesc dsc = RelationGetDescr(urel); /* * 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(); + filename = user_getfilename(); bufsize = strlen(filename) + 12; tempname = (char *) palloc(bufsize); @@ -82,26 +280,22 @@ write_password_file(Relation rel) elog(ERROR, "write_password_file: unable to write %s: %m", tempname); /* read table */ - scan = heap_beginscan(rel, false, SnapshotSelf, 0, NULL); + scan = heap_beginscan(urel, 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; + Datum datum; + bool isnull; + char *usename, + *passwd, + *valuntil; int i; - datum_n = heap_getattr(tuple, Anum_pg_shadow_usename, dsc, &null_n); - if (null_n) + datum = heap_getattr(tuple, Anum_pg_shadow_usename, dsc, &isnull); + if (isnull) continue; /* ignore NULL usernames */ - str_n = DatumGetCString(DirectFunctionCall1(nameout, datum_n)); + usename = (char *) DatumGetName(datum); - datum_p = heap_getattr(tuple, Anum_pg_shadow_passwd, dsc, &null_p); + datum = heap_getattr(tuple, Anum_pg_shadow_passwd, dsc, &isnull); /* * It can be argued that people having a null password shouldn't @@ -110,57 +304,40 @@ write_password_file(Relation rel) * assuming an empty password in that case is better, change this * logic to look something like the code for valuntil. */ - if (null_p) - { - pfree(str_n); + if (isnull) 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"); + passwd = DatumGetCString(DirectFunctionCall1(textout, datum)); + + datum = heap_getattr(tuple, Anum_pg_shadow_valuntil, dsc, &isnull); + if (isnull) + valuntil = pstrdup(""); else - str_v = DatumGetCString(DirectFunctionCall1(nabstimeout, datum_v)); + valuntil = DatumGetCString(DirectFunctionCall1(nabstimeout, datum)); /* * 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); + 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. */ - 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); - - pfree(str_n); - pfree(str_p); - pfree(str_v); + fputs_quote(usename, fp); + fputs(" ", fp); + fputs_quote(passwd, fp); + fputs(" ", fp); + fputs_quote(valuntil, fp); + + pfree(passwd); + pfree(valuntil); } heap_endscan(scan); @@ -178,29 +355,33 @@ write_password_file(Relation rel) pfree((void *) tempname); pfree((void *) filename); - - /* - * Signal the postmaster to reload its password-file cache. - */ - SendPostmasterSignal(PMSIGNAL_PASSWORD_CHANGE); } /* 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); } @@ -446,14 +627,14 @@ CreateUser(CreateUserStmt *stmt) } /* - * Write the updated pg_shadow data to the flat password file. + * Now we can clean up; but keep lock until commit. */ - write_password_file(pg_shadow_rel); + heap_close(pg_shadow_rel, NoLock); /* - * Now we can clean up; but keep lock until commit. + * Write the updated pg_shadow and pg_group data to the flat file. */ - heap_close(pg_shadow_rel, NoLock); + update_pg_pwd_and_pg_group(NULL); } @@ -680,14 +861,14 @@ AlterUser(AlterUserStmt *stmt) heap_freetuple(new_tuple); /* - * Write the updated pg_shadow data to the flat password file. + * Now we can clean up. */ - write_password_file(pg_shadow_rel); + heap_close(pg_shadow_rel, NoLock); /* - * Now we can clean up. + * Write the updated pg_shadow and pg_group data to the flat file. */ - heap_close(pg_shadow_rel, NoLock); + 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)" : ""); @@ -901,14 +1082,14 @@ DropUser(DropUserStmt *stmt) } /* - * Write the updated pg_shadow data to the flat password file. + * Now we can clean up. */ - write_password_file(pg_shadow_rel); + heap_close(pg_shadow_rel, NoLock); /* - * Now we can clean up. + * Write the updated pg_shadow and pg_group data to the flat file. */ - heap_close(pg_shadow_rel, NoLock); + 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); } diff --git a/src/backend/libpq/Makefile b/src/backend/libpq/Makefile index f5c2339f1c3..d0f05e802d9 100644 --- a/src/backend/libpq/Makefile +++ b/src/backend/libpq/Makefile @@ -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 diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c index 637e2a623eb..81a494905ea 100644 --- a/src/backend/libpq/auth.c +++ b/src/backend/libpq/auth.c @@ -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,27 +809,14 @@ 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. */ diff --git a/src/backend/libpq/crypt.c b/src/backend/libpq/crypt.c index 6ab63ddffe0..7c665300c85 100644 --- a/src/backend/libpq/crypt.c +++ b/src/backend/libpq/crypt.c @@ -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) diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c index b86cb817302..fce63ab2436 100644 --- a/src/backend/libpq/hba.c +++ b/src/backend/libpq/hba.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/libpq/hba.c,v 1.80 2002/03/04 01:46:03 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/libpq/hba.c,v 1.81 2002/04/04 04:25:47 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -30,18 +30,20 @@ #include <arpa/inet.h> #include <unistd.h> +#include "commands/user.h" +#include "libpq/crypt.h" #include "libpq/libpq.h" #include "miscadmin.h" #include "nodes/pg_list.h" #include "storage/fd.h" -#define MAX_TOKEN 80 -/* Maximum size of one token in the configuration file */ - #define IDENT_USERNAME_MAX 512 /* Max size of username ident server can return */ +/* This is used to separate values in multi-valued column strings */ +#define MULTI_VALUE_SEP "\001" + /* * These variables hold the pre-parsed contents of the hba and ident * configuration files. Each is a list of sublists, one sublist for @@ -53,7 +55,17 @@ */ static List *hba_lines = NIL; /* pre-parsed contents of hba file */ static List *ident_lines = NIL; /* pre-parsed contents of ident file */ +static List *group_lines = NIL; /* pre-parsed contents of group file */ +static List *user_lines = NIL; /* pre-parsed contents of user password file */ + +/* sorted entries so we can do binary search lookups */ +static List **user_sorted = NULL; /* sorted user list, for bsearch() */ +static List **group_sorted = NULL; /* sorted group list, for bsearch() */ +static int user_length; +static int group_length; +static List *tokenize_file(FILE *file); +static char *tokenize_inc_file(const char *inc_filename); /* * Some standard C libraries, including GNU, have an isblank() function. @@ -67,41 +79,76 @@ isblank(const char c) /* - * 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. + * 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. Allow spaces in quoted + * strings. Terminate on unquoted commas. Handle comments. */ -static void +void next_token(FILE *fp, char *buf, const int bufsz) { int c; - char *eb = buf + (bufsz - 1); + char *end_buf = buf + (bufsz - 1); + bool in_quote = false; + bool was_quote = false; - /* Move over initial token-delimiting blanks */ - while ((c = getc(fp)) != EOF && isblank(c)) + /* Move over initial whitespace and commas */ + while ((c = getc(fp)) != EOF && (isblank(c) || 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. + * Build a token in buf of next characters up to EOF, EOL, unquoted + * comma, or unquoted whitespace. */ - while (c != EOF && c != '\n' && !isblank(c)) + while (c != EOF && c != '\n' && + (!isblank(c) || in_quote == true)) { - if (buf < eb) + if (c == '"') + in_quote = !in_quote; + + /* skip comments to EOL */ + if (c == '#' && !in_quote) + { + while ((c = getc(fp)) != EOF && c != '\n') + ; + continue; + } + + if (buf >= end_buf) + { + elog(LOG, "Token too long in authentication file, skipping, %s", buf); + /* Discard remainder of line */ + while ((c = getc(fp)) != EOF && c != '\n') + ; + buf[0] = '\0'; + break; + } + + if (c != '"' || (c == '"' && was_quote)) *buf++ = c; + + /* We pass back the comma so the caller knows there is more */ + if ((isblank(c) || c == ',') && !in_quote) + break; + + /* Literal double-quote is two double-quotes */ + if (c == '"') + was_quote = !was_quote; + else + was_quote = false; + 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). + * EOL, since we need to detect end-of-line at next call). */ if (c != EOF) ungetc(c, fp); @@ -109,17 +156,142 @@ next_token(FILE *fp, char *buf, const int bufsz) *buf = '\0'; } +/* + * Tokenize file and handle file inclusion and comma lists. We have + * to break apart the commas to expand any file names then + * reconstruct with commas. + */ +static char * +next_token_expand(FILE *file) +{ + char buf[MAX_TOKEN]; + char *comma_str = pstrdup(""); + bool trailing_comma; + char *incbuf; + + do + { + next_token(file, buf, sizeof(buf)); + if (!*buf) + break; + + if (buf[strlen(buf)-1] == ',') + { + trailing_comma = true; + buf[strlen(buf)-1] = '\0'; + } + else + trailing_comma = false; + + /* Is this referencing a file? */ + if (buf[0] == '@') + incbuf = tokenize_inc_file(buf+1); + else + incbuf = pstrdup(buf); + + comma_str = repalloc(comma_str, + strlen(comma_str) + strlen(incbuf) + 1); + strcat(comma_str, incbuf); + pfree(incbuf); + + if (trailing_comma) + { + comma_str = repalloc(comma_str, strlen(comma_str) + 1 + 1); + strcat(comma_str, MULTI_VALUE_SEP); + } + } while (trailing_comma); + + return comma_str; +} + +/* + * Free memory used by lines/tokens (i.e., structure built by tokenize_file) + */ static void -read_through_eol(FILE *file) +free_lines(List **lines) { - int c; + if (*lines) + { + List *line, + *token; - while ((c = getc(file)) != EOF && c != '\n') - ; + foreach(line, *lines) + { + List *ln = lfirst(line); + + /* free the pstrdup'd tokens (don't try it on the line number) */ + foreach(token, lnext(ln)) + pfree(lfirst(token)); + /* free the sublist structure itself */ + freeList(ln); + } + /* free the list structure itself */ + freeList(*lines); + /* clear the static variable */ + *lines = NIL; + } +} + + +static char * +tokenize_inc_file(const char *inc_filename) +{ + char *inc_fullname; + FILE *inc_file; + List *inc_lines; + List *line; + char *comma_str = pstrdup(""); + + inc_fullname = (char *) palloc(strlen(DataDir) + 1 + + strlen(inc_filename) + 1); + strcpy(inc_fullname, DataDir); + strcat(inc_fullname, "/"); + strcat(inc_fullname, inc_filename); + + inc_file = AllocateFile(inc_fullname, "r"); + if (!inc_file) + { + elog(LOG, "tokenize_inc_file: Unable to open secondary authentication file \"@%s\" as \"%s\": %m", + inc_filename, inc_fullname); + pfree(inc_fullname); + + /* return empty string, it matches nothing */ + return pstrdup(""); + } + pfree(inc_fullname); + + /* There is possible recursion here if the file contains @ */ + inc_lines = tokenize_file(inc_file); + FreeFile(inc_file); + + /* Create comma-separate string from List */ + foreach(line, inc_lines) + { + List *ln = lfirst(line); + List *token; + + /* First entry is line number */ + foreach(token, lnext(ln)) + { + if (strlen(comma_str)) + { + comma_str = repalloc(comma_str, strlen(comma_str) + 1); + strcat(comma_str, MULTI_VALUE_SEP); + } + comma_str = repalloc(comma_str, + strlen(comma_str) + strlen(lfirst(token)) + 1); + strcat(comma_str, lfirst(token)); + } + } + + free_lines(&inc_lines); + + return comma_str; } + /* * Read the given file and create a list of line sublists. */ @@ -129,19 +301,13 @@ tokenize_file(FILE *file) List *lines = NIL; List *next_line = NIL; int line_number = 1; - char buf[MAX_TOKEN]; - char *comment_ptr; + char *buf; while (!feof(file)) { - next_token(file, buf, sizeof(buf)); - - /* trim off comment, even if inside a token */ - comment_ptr = strchr(buf, '#'); - if (comment_ptr != NULL) - *comment_ptr = '\0'; + buf = next_token_expand(file); - /* add token to list, unless we are at eol or comment start */ + /* add token to list, unless we are at EOL or comment start */ if (buf[0] != '\0') { if (next_line == NIL) @@ -151,22 +317,15 @@ tokenize_file(FILE *file) lines = lappend(lines, next_line); } /* append token to current line's list */ - next_line = lappend(next_line, pstrdup(buf)); + next_line = lappend(next_line, buf); } else { - /* we are at real or logical eol, so force a new line List */ + /* we are at real or logical EOL, so force a new line List */ next_line = NIL; } - if (comment_ptr != NULL) - { - /* Found a comment, so skip the rest of the line */ - read_through_eol(file); - next_line = NIL; - } - - /* Advance line number whenever we reach eol */ + /* Advance line number whenever we reach EOL */ if (next_line == NIL) line_number++; } @@ -176,31 +335,116 @@ tokenize_file(FILE *file) /* - * Free memory used by lines/tokens (ie, structure built by tokenize_file) + * Compare two password-file lines on the basis of their user names. + * + * Used for qsort() sorting and bsearch() lookup. */ -static void -free_lines(List **lines) +static int +user_group_cmp(const void *user, const void *list) { - if (*lines) + /* first node is line number */ + char *user1 = (char *)user; + char *user2 = lfirst(lnext(*(List **)list)); + + return strcmp(user1, user2); +} + + +/* + * Lookup a group name in the pg_group file + */ +static List ** +get_group_line(const char *group) +{ + return (List **) bsearch((void *) group, + (void *) group_sorted, + group_length, + sizeof(List *), + user_group_cmp); +} + + +/* + * Lookup a user name in the pg_shadow file + */ +List ** +get_user_line(const char *user) +{ + return (List **) bsearch((void *) user, + (void *) user_sorted, + user_length, + sizeof(List *), + user_group_cmp); +} + + +/* + * Check group for a specific user. + */ +static int +check_group(char *group, char *user) +{ + List **line, *l; + + if ((line = get_group_line(group)) != NULL) { - List *line, - *token; + foreach(l, lnext(lnext(*line))) + if (strcmp(lfirst(l), user) == 0) + return 1; + } - foreach(line, *lines) + return 0; +} + +/* + * Check comma user list for a specific user, handle group names. + */ +static int +check_user(char *user, char *param_str) +{ + char *tok; + + for (tok = strtok(param_str, MULTI_VALUE_SEP); tok != NULL; tok = strtok(NULL, MULTI_VALUE_SEP)) + { + if (tok[0] == '+') { - List *ln = lfirst(line); + if (check_group(tok+1, user)) + return 1; + } + else if (strcmp(tok, user) == 0 || + strcmp(tok, "all") == 0) + return 1; + } - /* free the pstrdup'd tokens (don't try it on the line number) */ - foreach(token, lnext(ln)) - pfree(lfirst(token)); - /* free the sublist structure itself */ - freeList(ln); + return 0; +} + +/* + * Check to see if db/user combination matches param string. + */ +static int +check_db(char *dbname, char *user, char *param_str) +{ + char *tok; + + for (tok = strtok(param_str, MULTI_VALUE_SEP); tok != NULL; tok = strtok(NULL, MULTI_VALUE_SEP)) + { + if (strcmp(tok, "all") == 0) + return 1; + else if (strcmp(tok, "sameuser") == 0) + { + if (strcmp(dbname, user) == 0) + return 1; } - /* free the list structure itself */ - freeList(*lines); - /* clear the static variable */ - *lines = NIL; + else if (strcmp(tok, "samegroup") == 0) + { + if (check_group(dbname, user)) + return 1; + } + else if (strcmp(tok, dbname) == 0) + return 1; } + return 0; } @@ -278,6 +522,7 @@ parse_hba(List *line, hbaPort *port, bool *found_p, bool *error_p) int line_number; char *token; char *db; + char *user; Assert(line != NIL); line_number = lfirsti(line); @@ -293,10 +538,17 @@ parse_hba(List *line, hbaPort *port, bool *found_p, bool *error_p) goto hba_syntax; db = lfirst(line); - /* Read the rest of the line. */ + /* Get the user. */ + line = lnext(line); + if (!line) + goto hba_syntax; + user = lfirst(line); + line = lnext(line); if (!line) goto hba_syntax; + + /* Read the rest of the line. */ parse_hba_auth(line, &port->auth_method, port->auth_arg, error_p); if (*error_p) goto hba_syntax; @@ -308,15 +560,7 @@ parse_hba(List *line, hbaPort *port, bool *found_p, bool *error_p) port->auth_method == uaKrb5) goto hba_syntax; - /* - * If this record doesn't match the parameters of the connection - * attempt, ignore it. - */ - if ((strcmp(db, port->database) != 0 && - strcmp(db, "all") != 0 && - (strcmp(db, "sameuser") != 0 || - strcmp(port->database, port->user) != 0)) || - port->raddr.sa.sa_family != AF_UNIX) + if (port->raddr.sa.sa_family != AF_UNIX) return; } else if (strcmp(token, "host") == 0 || strcmp(token, "hostssl") == 0) @@ -347,6 +591,12 @@ parse_hba(List *line, hbaPort *port, bool *found_p, bool *error_p) goto hba_syntax; db = lfirst(line); + /* Get the user. */ + line = lnext(line); + if (!line) + goto hba_syntax; + user = lfirst(line); + /* Read the IP address field. */ line = lnext(line); if (!line) @@ -371,21 +621,19 @@ parse_hba(List *line, hbaPort *port, bool *found_p, bool *error_p) if (*error_p) goto hba_syntax; - /* - * If this record doesn't match the parameters of the connection - * attempt, ignore it. - */ - if ((strcmp(db, port->database) != 0 && - strcmp(db, "all") != 0 && - (strcmp(db, "sameuser") != 0 || - strcmp(port->database, port->user) != 0)) || - port->raddr.sa.sa_family != AF_INET || + /* Must meet network restrictions */ + if (port->raddr.sa.sa_family != AF_INET || ((file_ip_addr.s_addr ^ port->raddr.in.sin_addr.s_addr) & mask.s_addr) != 0) return; } else goto hba_syntax; + if (!check_db(port->database, port->user, db)) + return; + if (!check_user(port->user, user)) + return; + /* Success */ *found_p = true; return; @@ -430,6 +678,127 @@ check_hba(hbaPort *port) } + +/* + * Open the group file if possible (return NULL if not) + */ +static FILE * +group_openfile(void) +{ + char *filename; + FILE *groupfile; + + filename = group_getfilename(); + groupfile = AllocateFile(filename, "r"); + + if (groupfile == NULL && errno != ENOENT) + elog(LOG, "could not open %s: %m", filename); + + pfree(filename); + + return groupfile; +} + + + +/* + * Open the password file if possible (return NULL if not) + */ +static FILE * +user_openfile(void) +{ + char *filename; + FILE *pwdfile; + + filename = user_getfilename(); + pwdfile = AllocateFile(filename, "r"); + + if (pwdfile == NULL && errno != ENOENT) + elog(LOG, "could not open %s: %m", filename); + + pfree(filename); + + return pwdfile; +} + + + +/* + * Load group/user name mapping file + */ +void +load_group() +{ + FILE *group_file; + List *line; + + if (group_lines) + free_lines(&group_lines); + + group_file = group_openfile(); + if (!group_file) + return; + group_lines = tokenize_file(group_file); + FreeFile(group_file); + + /* create sorted lines for binary searching */ + if (group_sorted) + pfree(group_sorted); + group_length = length(group_lines); + if (group_length) + { + int i = 0; + + group_sorted = palloc(group_length * sizeof(List *)); + + foreach(line, group_lines) + group_sorted[i++] = lfirst(line); + + qsort((void *) group_sorted, group_length, sizeof(List *), user_group_cmp); + } + else + group_sorted = NULL; +} + + +/* + * Load user/password mapping file + */ +void +load_user() +{ + FILE *user_file; + List *line; + + if (user_lines) + free_lines(&user_lines); + + user_file = user_openfile(); + if (!user_file) + return; + user_lines = tokenize_file(user_file); + FreeFile(user_file); + + /* create sorted lines for binary searching */ + if (user_sorted) + pfree(user_sorted); + user_length = length(user_lines); + if (user_length) + { + int i = 0; + + user_sorted = palloc(user_length * sizeof(List *)); + + foreach(line, user_lines) + user_sorted[i++] = lfirst(line); + + qsort((void *) user_sorted, user_length, sizeof(List *), user_group_cmp); + } + else + user_sorted = NULL; +} + + /* * Read the config file and create a List of Lists of tokens in the file. * If we find a file by the old name of the config file (pg_hba), we issue @@ -437,60 +806,35 @@ check_hba(hbaPort *port) * follow directions and just installed his old hba file in the new database * system. */ -static void +void load_hba(void) { - int fd, - bufsize; + int bufsize; FILE *file; /* The config file we have to read */ - char *old_conf_file; + char *conf_file; /* The name of the config file */ if (hba_lines) free_lines(&hba_lines); - /* - * The name of old config file that better not exist. Fail if config - * file by old name exists. Put together the full pathname to the old - * config file. - */ - bufsize = (strlen(DataDir) + strlen(OLD_CONF_FILE) + 2) * sizeof(char); - old_conf_file = (char *) palloc(bufsize); - snprintf(old_conf_file, bufsize, "%s/%s", DataDir, OLD_CONF_FILE); + /* Put together the full pathname to the config file. */ + bufsize = (strlen(DataDir) + strlen(CONF_FILE) + 2) * sizeof(char); + conf_file = (char *) palloc(bufsize); + snprintf(conf_file, bufsize, "%s/%s", DataDir, CONF_FILE); - if ((fd = open(old_conf_file, O_RDONLY | PG_BINARY, 0)) != -1) + file = AllocateFile(conf_file, "r"); + if (file == NULL) { - /* Old config file exists. Tell this guy he needs to upgrade. */ - close(fd); - elog(LOG, "A file exists by the name used for host-based authentication " - "in prior releases of Postgres (%s). The name and format of " - "the configuration file have changed, so this file should be " - "converted.", old_conf_file); + /* The open of the config file failed. */ + elog(LOG, "load_hba: Unable to open authentication config file \"%s\": %m", + conf_file); + pfree(conf_file); } else { - char *conf_file; /* The name of the config file we have to - * read */ - - /* put together the full pathname to the config file */ - bufsize = (strlen(DataDir) + strlen(CONF_FILE) + 2) * sizeof(char); - conf_file = (char *) palloc(bufsize); - snprintf(conf_file, bufsize, "%s/%s", DataDir, CONF_FILE); - - file = AllocateFile(conf_file, "r"); - if (file == NULL) - { - /* The open of the config file failed. */ - elog(LOG, "load_hba: Unable to open authentication config file \"%s\": %m", - conf_file); - } - else - { - hba_lines = tokenize_file(file); - FreeFile(file); - } - pfree(conf_file); + hba_lines = tokenize_file(file); + FreeFile(file); } - pfree(old_conf_file); + pfree(conf_file); } @@ -606,7 +950,7 @@ check_ident_usermap(const char *usermap_name, /* * Read the ident config file and create a List of Lists of tokens in the file. */ -static void +void load_ident(void) { FILE *file; /* The map file we have to read */ @@ -622,7 +966,7 @@ load_ident(void) map_file = (char *) palloc(bufsize); snprintf(map_file, bufsize, "%s/%s", DataDir, USERMAP_FILE); - file = AllocateFile(map_file, PG_BINARY_R); + file = AllocateFile(map_file, "r"); if (file == NULL) { /* The open of the map file failed. */ @@ -640,8 +984,8 @@ load_ident(void) /* * Parse the string "*ident_response" as a response from a query to an Ident - * server. If it's a normal response indicating a username, return true - * and store the username at *ident_user. If it's anything else, + * server. If it's a normal response indicating a user name, return true + * and store the user name at *ident_user. If it's anything else, * return false. */ static bool @@ -708,7 +1052,7 @@ interpret_ident_response(char *ident_response, cursor++; /* Go over colon */ while (isblank(*cursor)) cursor++; /* skip blanks */ - /* Rest of line is username. Copy it over. */ + /* Rest of line is user name. Copy it over. */ i = 0; while (*cursor != '\r' && i < IDENT_USERNAME_MAX) ident_user[i++] = *cursor++; @@ -725,7 +1069,7 @@ interpret_ident_response(char *ident_response, /* * Talk to the ident server on host "remote_ip_addr" and find out who * owns the tcp connection from his port "remote_port" to port - * "local_port_addr" on host "local_ip_addr". Return the username the + * "local_port_addr" on host "local_ip_addr". Return the user name the * ident server gives as "*ident_user". * * IP addresses and port numbers are in network byte order. @@ -955,6 +1299,8 @@ ident_unix(int sock, char *ident_user) #endif } + + /* * Determine the username of the initiator of the connection described * by "port". Then look in the usermap file under the usermap @@ -1010,211 +1356,3 @@ hba_getauthmethod(hbaPort *port) return STATUS_ERROR; } -/* - * Clear and reload tokenized file contents. - */ -void -load_hba_and_ident(void) -{ - load_hba(); - load_ident(); -} - - -/* Character set stuff. Not sure it really belongs in this file. */ - -#ifdef CYR_RECODE - -#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; -} - -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, PG_BINARY_R); - pfree(map_file); - if (file == NULL) - { - /* XXX should we log a complaint? */ - return; - } - while ((c = getc(file)) != EOF) - { - if (c == '#') - read_through_eol(file); - else - { - /* Read the key */ - ungetc(c, file); - next_token(file, buf, sizeof(buf)); - if (buf[0] != '\0') - { - key = 0; - if (strcasecmp(buf, "HostCharset") == 0) - key = KEY_HOST; - if (strcasecmp(buf, "BaseCharset") == 0) - key = KEY_BASE; - if (strcasecmp(buf, "RecodeTable") == 0) - key = KEY_TABLE; - 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_through_eol(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 */ diff --git a/src/backend/libpq/password.c b/src/backend/libpq/password.c deleted file mode 100644 index f8490f8bd57..00000000000 --- a/src/backend/libpq/password.c +++ /dev/null @@ -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; -} diff --git a/src/backend/libpq/pg_hba.conf.sample b/src/backend/libpq/pg_hba.conf.sample index 669588e517c..05e6959b4de 100644 --- a/src/backend/libpq/pg_hba.conf.sample +++ b/src/backend/libpq/pg_hba.conf.sample @@ -42,22 +42,36 @@ # # Format: # -# host DBNAME IP_ADDRESS ADDRESS_MASK AUTH_TYPE [AUTH_ARGUMENT] -# -# 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 +# host DATABASE USER IP_ADDRESS MASK AUTH_TYPE +# +# 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 '@' +# +# 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 ADDRESS_MASK are standard dotted decimal IP address and +# 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": +# See $PGDATA/pg_ident.conf for more information on Ident maps. # -# TYPE DATABASE IP_ADDRESS MASK AUTH_TYPE AUTH_ARGUMENT -# host all 192.168.0.0 255.255.0.0 ident phoenix +# 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 # -# 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. # # # @@ -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 diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index a6d6097a54c..0ce817b5b39 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -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)) diff --git a/src/backend/utils/adt/quote.c b/src/backend/utils/adt/quote.c index 39a1ec4df16..537e97a50ee 100644 --- a/src/backend/utils/adt/quote.c +++ b/src/backend/utils/adt/quote.c @@ -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--; diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index dceb8b9cd6f..9ef8b1c87c5 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -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 */ |