Attached is a patch that caches the non-comment contents of pg_hba.conf
as a List of list of tokens. It uses that to test each authentication
request. SIGHUP reloads from the file, and a cache of pg_ident.conf.
I replaced the File reading code with token storage and list traveral.
The only tricky part was moving the Postmaster memory clearing later in
the code so I had the file contents in postgres.c.
--
Bruce Momjian | http://candle.pha.pa.us
pgman@candle.pha.pa.us | (610) 853-3000
+ If your life is a hard drive, | 830 Blythe Avenue
+ Christ can be your backup. | Drexel Hill, Pennsylvania 19026
Index: src/backend/libpq/hba.c
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/libpq/hba.c,v
retrieving revision 1.55
diff -c -r1.55 hba.c
*** src/backend/libpq/hba.c 2001/02/10 02:31:26 1.55
--- src/backend/libpq/hba.c 2001/07/21 00:12:41
***************
*** 22,27 ****
--- 22,28 ----
#include "libpq/libpq.h"
#include "miscadmin.h"
+ #include "nodes/pg_list.h"
#include "storage/fd.h"
***************
*** 31,36 ****
--- 32,44 ----
#define IDENT_USERNAME_MAX 512
/* Max size of username ident server can return */
+ static List *hba_lines = NULL; /* A list of lists: entry for every line,
+ * list of tokens on each line.
+ */
+
+ static List *ident_lines = NULL;/* A list of lists: entry for every line,
+ * list of tokens on each line.
+ */
/* Some standard C libraries, including GNU, have an isblank() function.
Others, including Solaris, do not. So we have our own.
***************
*** 38,68 ****
static bool
isblank(const char c)
{
! return c == ' ' || c == 9 /* tab */ ;
}
static void
next_token(FILE *fp, char *buf, const int bufsz)
{
- /*--------------------------------------------------------------------------
- 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.
- --------------------------------------------------------------------------*/
int c;
char *eb = buf + (bufsz - 1);
/* Move over inital token-delimiting blanks */
! while (isblank(c = getc(fp)));
if (c != '\n')
{
-
/*
* build a token in buf of next characters up to EOF, eol, or
* blank.
--- 46,76 ----
static bool
isblank(const char c)
{
! return c == ' ' || c == 0x09;/* tab */
}
+ /*
+ * 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 inital token-delimiting blanks */
! while (isblank(c = getc(fp)))
! ;
if (c != '\n')
{
/*
* build a token in buf of next characters up to EOF, eol, or
* blank.
***************
*** 72,231 ****
if (buf < eb)
*buf++ = c;
c = getc(fp);
-
- /*
- * Put back the char right after the token (putting back EOF
- * is ok)
- */
}
ungetc(c, fp);
}
*buf = '\0';
}
-
static void
! read_through_eol(FILE *file)
{
int c;
! do
! c = getc(file);
! while (c != '\n' && c != EOF);
}
-
static void
! read_hba_entry2(FILE *file, UserAuth *userauth_p, char *auth_arg,
! bool *error_p)
{
- /*--------------------------------------------------------------------------
- Read from file FILE the rest of a host record, after the mask field,
- and return the interpretation of it as *userauth_p, auth_arg, and
- *error_p.
- ---------------------------------------------------------------------------*/
char buf[MAX_TOKEN];
!
! /* Get authentication type token. */
! next_token(file, buf, sizeof(buf));
! if (strcmp(buf, "trust") == 0)
! *userauth_p = uaTrust;
! else if (strcmp(buf, "ident") == 0)
! *userauth_p = uaIdent;
! else if (strcmp(buf, "password") == 0)
! *userauth_p = uaPassword;
! else if (strcmp(buf, "krb4") == 0)
! *userauth_p = uaKrb4;
! else if (strcmp(buf, "krb5") == 0)
! *userauth_p = uaKrb5;
! else if (strcmp(buf, "reject") == 0)
! *userauth_p = uaReject;
! else if (strcmp(buf, "crypt") == 0)
! *userauth_p = uaCrypt;
! else
{
! *error_p = true;
if (buf[0] != '\0')
! read_through_eol(file);
}
if (!*error_p)
{
/* Get the authentication argument token, if any */
! next_token(file, buf, sizeof(buf));
! if (buf[0] == '\0')
auth_arg[0] = '\0';
else
{
! StrNCpy(auth_arg, buf, MAX_AUTH_ARG - 1);
! next_token(file, buf, sizeof(buf));
! if (buf[0] != '\0')
! {
*error_p = true;
- read_through_eol(file);
- }
}
}
}
-
static void
! process_hba_record(FILE *file, hbaPort *port, bool *matches_p, bool *error_p)
{
! /*---------------------------------------------------------------------------
! Process the non-comment record in the config file that is next on the file.
! See if it applies to a connection to a host with IP address "*raddr"
! to a database named "*database". If so, return *matches_p true
! and *userauth_p and *auth_arg as the values from the entry.
! If not, leave *matches_p as it was. If the record has a syntax error,
! return *error_p true, after issuing a message to stderr. If no error,
! leave *error_p as it was.
! ---------------------------------------------------------------------------*/
! char db[MAX_TOKEN],
! buf[MAX_TOKEN];
! /* Read the record type field. */
!
! next_token(file, buf, sizeof(buf));
!
! if (buf[0] == '\0')
! return;
!
/* Check the record type. */
!
! if (strcmp(buf, "local") == 0)
{
/* Get the database. */
!
! next_token(file, db, sizeof(db));
!
! if (db[0] == '\0')
! goto syntax;
!
/* Read the rest of the line. */
!
! read_hba_entry2(file, &port->auth_method, port->auth_arg, error_p);
/*
* For now, disallow methods that need AF_INET sockets to work.
*/
-
if (!*error_p &&
(port->auth_method == uaIdent ||
port->auth_method == uaKrb4 ||
port->auth_method == uaKrb5))
! *error_p = true;
- if (*error_p)
- goto syntax;
-
/*
* If this record isn't for our database, or this is the wrong
* sort of connection, 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)
return;
}
! else if (strcmp(buf, "host") == 0 || strcmp(buf, "hostssl") == 0)
{
! struct in_addr file_ip_addr,
! mask;
! bool discard = 0;/* Discard this entry */
#ifdef USE_SSL
/* If SSL, then check that we are on SSL */
! if (strcmp(buf, "hostssl") == 0)
{
if (!port->ssl)
! discard = 1;
/* Placeholder to require specific SSL level, perhaps? */
/* Or a client certificate */
--- 80,293 ----
if (buf < eb)
*buf++ = c;
c = getc(fp);
}
+ /*
+ * Put back the char right after the token (putting back EOF
+ * is ok)
+ */
ungetc(c, fp);
}
*buf = '\0';
}
static void
! read_to_eol(FILE *file)
{
int c;
! while ((c = getc(file)) != '\n' && c != EOF)
! ;
}
+ /*
+ * Process the file line by line and create a list of list of tokens.
+ */
static void
! tokenize_file(FILE *file, List **lines)
{
char buf[MAX_TOKEN];
! List *next_line = NIL;
! bool comment_found = false;
! while (1)
{
! next_token(file, buf, sizeof(buf));
! if (feof(file))
! break;
+ /* trim off comment, even if inside a token */
+ if (strstr(buf,"#") != NULL)
+ {
+ *strstr(buf,"#") = '\0';
+ comment_found = true;
+ }
+
+ /* add token to list */
if (buf[0] != '\0')
! {
! if (next_line == NIL)
! {
! /* make a new line List */
! next_line = lcons(pstrdup(buf), NIL);
! *lines = lappend(*lines, next_line);
! }
! else
! /* append token to line */
! next_line = lappend(next_line, pstrdup(buf));
! }
! else
! /* force a new List line */
! next_line = NIL;
!
! if (comment_found)
! {
! /* Skip the rest of the line */
! read_to_eol(file);
! next_line = NIL;
! comment_found = false;
! }
! }
! }
!
!
! /*
! * Free memory used by lines/tokens
! */
! static void free_lines(List **lines)
! {
! if (*lines)
! {
! List *line, *token;
!
! foreach(line, *lines)
! {
! foreach(token,lfirst(line))
! pfree(lfirst(token));
! freeList(lfirst(line));
! }
! freeList(*lines);
! *lines = NULL;
}
+ }
+
+ /*
+ * Read from file FILE the rest of a host record, after the mask field,
+ * and return the interpretation of it as *userauth_p, auth_arg, and
+ * *error_p.
+ */
+ static void
+ parse_hba_auth(List *line, UserAuth *userauth_p, char *auth_arg,
+ bool *error_p)
+ {
+ char *token = NULL;
+
+ if (!line)
+ *error_p = true;
+ else
+ {
+ /* Get authentication type token. */
+ token = lfirst(line);
+ if (strcmp(token, "trust") == 0)
+ *userauth_p = uaTrust;
+ else if (strcmp(token, "ident") == 0)
+ *userauth_p = uaIdent;
+ else if (strcmp(token, "password") == 0)
+ *userauth_p = uaPassword;
+ else if (strcmp(token, "krb4") == 0)
+ *userauth_p = uaKrb4;
+ else if (strcmp(token, "krb5") == 0)
+ *userauth_p = uaKrb5;
+ else if (strcmp(token, "reject") == 0)
+ *userauth_p = uaReject;
+ else if (strcmp(token, "crypt") == 0)
+ *userauth_p = uaCrypt;
+ else
+ *error_p = true;
+ }
+
if (!*error_p)
{
/* Get the authentication argument token, if any */
! line = lnext(line);
! if (!line)
auth_arg[0] = '\0';
else
{
! StrNCpy(auth_arg, token, MAX_AUTH_ARG - 1);
! /* If there is more on the line, it is an error */
! if (lnext(line))
*error_p = true;
}
}
}
+ /*
+ * Process the non-comment lines in the config file.
+ *
+ * See if it applies to a connection to a host with IP address "*raddr"
+ * to a database named "*database". If so, return *found_p true
+ * and *userauth_p and *auth_arg as the values from the entry.
+ * If not, leave *found_p as it was. If the record has a syntax error,
+ * return *error_p true, after issuing a message to stderr. If no error,
+ * leave *error_p as it was.
+ */
static void
! parse_hba(List *line, hbaPort *port, bool *found_p, bool *error_p)
{
! char *db;
! char *token;
! Assert(line != NIL);
! token = lfirst(line);
/* Check the record type. */
! if (strcmp(token, "local") == 0)
{
/* Get the database. */
! line = lnext(line);
! if (!line)
! goto hba_syntax;
! db = 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;
/*
* For now, disallow methods that need AF_INET sockets to work.
*/
if (!*error_p &&
(port->auth_method == uaIdent ||
port->auth_method == uaKrb4 ||
port->auth_method == uaKrb5))
! goto hba_syntax;
/*
* If this record isn't for our database, or this is the wrong
* sort of connection, 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)
return;
}
! else if (strcmp(token, "host") == 0 || strcmp(token, "hostssl") == 0)
{
! struct in_addr file_ip_addr, mask;
#ifdef USE_SSL
/* If SSL, then check that we are on SSL */
! if (strcmp(token, "hostssl") == 0)
{
if (!port->ssl)
! return;
/* Placeholder to require specific SSL level, perhaps? */
/* Or a client certificate */
***************
*** 234,300 ****
}
#else
/* If not SSL, we don't support this */
! if (strcmp(buf, "hostssl") == 0)
! goto syntax;
#endif
/* Get the database. */
!
! next_token(file, db, sizeof(db));
!
! if (db[0] == '\0')
! goto syntax;
/* Read the IP address field. */
- next_token(file, buf, sizeof(buf));
-
- if (buf[0] == '\0')
- goto syntax;
-
/* Remember the IP address field and go get mask field. */
- if (!inet_aton(buf, &file_ip_addr))
- {
- read_through_eol(file);
- goto syntax;
- }
-
/* Read the mask field. */
!
! next_token(file, buf, sizeof(buf));
! if (buf[0] == '\0')
! goto syntax;
- if (!inet_aton(buf, &mask))
- {
- read_through_eol(file);
- goto syntax;
- }
-
/*
* This is the record we're looking for. Read the rest of the
* info from it.
*/
!
! read_hba_entry2(file, &port->auth_method, port->auth_arg, error_p);
!
if (*error_p)
! goto syntax;
!
! /*
! * If told to discard earlier. Moved down here so we don't get
! * "out of sync" with the file.
! */
! if (discard)
! return;
/*
* If this record isn't for our database, or this is the wrong
* sort of connection, 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 ||
--- 296,345 ----
}
#else
/* If not SSL, we don't support this */
! if (strcmp(token, "hostssl") == 0)
! goto hba_syntax;
#endif
/* Get the database. */
! line = lnext(line);
! if (!line)
! goto hba_syntax;
! db = lfirst(line);
/* Read the IP address field. */
+ line = lnext(line);
+ if (!line)
+ goto hba_syntax;
+ token = lfirst(line);
/* Remember the IP address field and go get mask field. */
+ if (!inet_aton(token, &file_ip_addr))
+ goto hba_syntax;
/* Read the mask field. */
! line = lnext(line);
! if (!line)
! goto hba_syntax;
! token = lfirst(line);
! if (!inet_aton(token, &mask))
! goto hba_syntax;
/*
* This is the record we're looking for. Read the rest of the
* info from it.
*/
! line = lnext(line);
! if (!line)
! goto hba_syntax;
! parse_hba_auth(line, &port->auth_method, port->auth_arg, error_p);
if (*error_p)
! goto hba_syntax;
/*
* If this record isn't for our database, or this is the wrong
* sort of connection, 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 ||
***************
*** 302,399 ****
return;
}
else
! {
! read_through_eol(file);
! goto syntax;
! }
! *matches_p = true;
!
return;
! syntax:
snprintf(PQerrormsg, PQERRORMSG_LENGTH,
! "process_hba_record: invalid syntax in pg_hba.conf file\n");
fputs(PQerrormsg, stderr);
pqdebug("%s", PQerrormsg);
*error_p = true;
}
!
! static void
! process_open_config_file(FILE *file, hbaPort *port, bool *hba_ok_p)
{
! /*---------------------------------------------------------------------------
! This function does the same thing as find_hba_entry, only with
! the config file already open on stream descriptor "file".
! ----------------------------------------------------------------------------*/
! bool found_entry = false; /* found an applicable entry? */
! bool error = false; /* found an erroneous entry? */
! bool eof = false; /* end of hba file */
!
! while (!eof && !found_entry && !error)
! {
! /* Process a line from the config file */
! int c = getc(file);
!
! if (c == EOF)
! eof = true;
! else
! {
! ungetc(c, file);
! if (c == '#')
! read_through_eol(file);
! else
! process_hba_record(file, port, &found_entry, &error);
! }
}
if (!error)
{
/* If no matching entry was found, synthesize 'reject' entry. */
-
if (!found_entry)
port->auth_method = uaReject;
!
! *hba_ok_p = true;
}
}
-
- static void
- find_hba_entry(hbaPort *port, bool *hba_ok_p)
- {
/*
! * Read the config file and find an entry that allows connection from
! * host "raddr", user "user", to database "database". If found,
! * return *hba_ok_p = true and *userauth_p and *auth_arg representing
! * the contents of that entry. If there is no matching entry, we
! * set *hba_ok_p = true, *userauth_p = uaReject.
! *
! * If the config file is unreadable or contains invalid syntax, we
! * issue a diagnostic message to stderr (ie, the postmaster log file)
! * and return without changing *hba_ok_p.
! *
* If we find a file by the old name of the config file (pg_hba), we issue
* an error message because it probably needs to be converted. He didn't
* follow directions and just installed his old hba file in the new database
* system.
*/
int fd,
bufsize;
FILE *file; /* The config file we have to read */
char *old_conf_file;
! /* 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);
--- 347,421 ----
return;
}
else
! goto hba_syntax;
! /* Success */
! *found_p = true;
return;
! hba_syntax:
snprintf(PQerrormsg, PQERRORMSG_LENGTH,
! "parse_hba: invalid syntax in pg_hba.conf file\n");
fputs(PQerrormsg, stderr);
pqdebug("%s", PQerrormsg);
*error_p = true;
+ return;
}
! /*
! * Process the hba file line by line.
! */
! static bool
! check_hba(hbaPort *port)
{
! List *line;
! bool found_entry = false;
! bool error = false;
!
! foreach (line, hba_lines)
! {
! parse_hba(lfirst(line), port, &found_entry, &error);
! if (found_entry || error)
! break;
}
if (!error)
{
/* If no matching entry was found, synthesize 'reject' entry. */
if (!found_entry)
port->auth_method = uaReject;
! return true;
}
+ else
+ return false;
}
/*
! * 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
* an error message because it probably needs to be converted. He didn't
* follow directions and just installed his old hba file in the new database
* system.
*/
+ static void
+ load_hba()
+ {
int fd,
bufsize;
FILE *file; /* The config file we have to read */
char *old_conf_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);
***************
*** 403,413 ****
/* Old config file exists. Tell this guy he needs to upgrade. */
close(fd);
snprintf(PQerrormsg, PQERRORMSG_LENGTH,
! "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.\n",
! old_conf_file);
fputs(PQerrormsg, stderr);
pqdebug("%s", PQerrormsg);
}
--- 425,434 ----
/* Old config file exists. Tell this guy he needs to upgrade. */
close(fd);
snprintf(PQerrormsg, PQERRORMSG_LENGTH,
! "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.\n", old_conf_file);
fputs(PQerrormsg, stderr);
pqdebug("%s", PQerrormsg);
}
***************
*** 425,440 ****
if (file == NULL)
{
/* The open of the config file failed. */
-
snprintf(PQerrormsg, PQERRORMSG_LENGTH,
! "find_hba_entry: Unable to open authentication config file \"%s\": %s\n",
conf_file, strerror(errno));
fputs(PQerrormsg, stderr);
pqdebug("%s", PQerrormsg);
}
else
{
! process_open_config_file(file, port, hba_ok_p);
FreeFile(file);
}
pfree(conf_file);
--- 446,460 ----
if (file == NULL)
{
/* The open of the config file failed. */
snprintf(PQerrormsg, PQERRORMSG_LENGTH,
! "load_hba: Unable to open authentication config file \"%s\": %s\n",
conf_file, strerror(errno));
fputs(PQerrormsg, stderr);
pqdebug("%s", PQerrormsg);
}
else
{
! tokenize_file(file, &hba_lines);
FreeFile(file);
}
pfree(conf_file);
***************
*** 443,477 ****
}
static void
! interpret_ident_response(char *ident_response,
! bool *error_p, char *ident_username)
{
! /*----------------------------------------------------------------------------
! 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
! *error_p == false and the username as *ident_username. If it's anything
! else, return *error_p == true and *ident_username undefined.
! ----------------------------------------------------------------------------*/
! char *cursor; /* Cursor into *ident_response */
! cursor = &ident_response[0];
/*
* Ident's response, in the telnet tradition, should end in crlf
* (\r\n).
*/
if (strlen(ident_response) < 2)
! *error_p = true;
else if (ident_response[strlen(ident_response) - 2] != '\r')
! *error_p = true;
else
{
while (*cursor != ':' && *cursor != '\r')
cursor++; /* skip port field */
if (*cursor != ':')
! *error_p = true;
else
{
/* We're positioned to colon before response type field */
--- 463,637 ----
}
+ /*
+ * Take the line and compare it to the needed map, pg_user and ident_user.
+ */
static void
! parse_ident_usermap(List *line, const char *usermap_name, const char *pg_user,
! const char *ident_user, bool *found_p, bool *error_p)
! {
! char *token;
! char *file_map;
! char *file_pguser;
! char *file_ident_user;
!
! *error_p = false;
! *found_p = false;
!
! /* A token read from the file */
! Assert(line != NIL);
! token = lfirst(line);
! file_map = token;
!
! line = lnext(line);
! if (!line)
! goto ident_syntax;
! token = lfirst(line);
! if (token[0] != '\0')
! {
! file_ident_user = token;
! line = lnext(line);
! if (!line)
! goto ident_syntax;
! token = lfirst(line);
! if (token[0] != '\0')
! {
! file_pguser = token;
! if (strcmp(file_map, usermap_name) == 0 &&
! strcmp(file_pguser, pg_user) == 0 &&
! strcmp(file_ident_user, ident_user) == 0)
! *found_p = true;
! }
! }
!
! return;
!
! ident_syntax:
! snprintf(PQerrormsg, PQERRORMSG_LENGTH,
! "parse_ident_usermap: invalid syntax in pg_ident.conf file\n");
! fputs(PQerrormsg, stderr);
! pqdebug("%s", PQerrormsg);
! *error_p = true;
! return;
! }
!
!
! /*
! * Process the ident usermap file line by line.
! */
! static bool
! check_ident_usermap(const char *usermap_name,
! const char *pg_user,
! const char *ident_user)
! {
! List *line;
! bool found_entry = false, error = false;
!
! if (usermap_name[0] == '\0')
! {
! snprintf(PQerrormsg, PQERRORMSG_LENGTH,
! "load_ident_usermap: hba configuration file does not "
! "have the usermap field filled in in the entry that pertains "
! "to this connection. That field is essential for Ident-based "
! "authentication.\n");
! fputs(PQerrormsg, stderr);
! pqdebug("%s", PQerrormsg);
! found_entry = false;
! }
! else if (strcmp(usermap_name, "sameuser") == 0)
! {
! if (strcmp(pg_user, ident_user) == 0)
! found_entry = true;
! else
! found_entry = false;
! }
! else
! {
! foreach(line, ident_lines)
! {
! parse_ident_usermap(lfirst(line), usermap_name, pg_user,
! ident_user, &found_entry, &error);
! if (found_entry || error)
! break;
! }
! }
! return found_entry;
! }
!
!
! /*
! * See if the user with ident username "ident_user" is allowed to act
! * as Postgres user "pguser" according to usermap "usermap_name". Look
! * it up in the usermap file.
! *
! * Special case: For usermap "sameuser", don't look in the usermap
! * file. That's an implied map where "pguser" must be identical to
! * "ident_user" in order to be authorized.
! *
! * Iff authorized, return *checks_out_p == true.
! */
! static void
! load_ident()
{
! FILE *file; /* The map file we have to read */
! char *map_file; /* The name of the map file we have to
! * read */
! int bufsize;
! if (ident_lines)
! free_lines(&ident_lines);
+ /* put together the full pathname to the map file */
+ bufsize = (strlen(DataDir) + strlen(USERMAP_FILE) + 2) * sizeof(char);
+ map_file = (char *) palloc(bufsize);
+ snprintf(map_file, bufsize, "%s/%s", DataDir, USERMAP_FILE);
+
+ file = AllocateFile(map_file, PG_BINARY_R);
+ if (file == NULL)
+ {
+ /* The open of the map file failed. */
+ snprintf(PQerrormsg, PQERRORMSG_LENGTH,
+ "load_ident_usermap: Unable to open usermap file \"%s\": %s\n",
+ map_file, strerror(errno));
+ fputs(PQerrormsg, stderr);
+ pqdebug("%s", PQerrormsg);
+ }
+ else
+ {
+ tokenize_file(file, &ident_lines);
+ FreeFile(file);
+ }
+ pfree(map_file);
+ }
+
+
+ /*
+ * 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
+ * *error_p == false and the username as *ident_user. If it's anything
+ * else, return *error_p == true and *ident_user undefined.
+ */
+ static bool
+ interpret_ident_response(char *ident_response,
+ char *ident_user)
+ {
+ char *cursor = ident_response;/* Cursor into *ident_response */
+
/*
* Ident's response, in the telnet tradition, should end in crlf
* (\r\n).
*/
if (strlen(ident_response) < 2)
! return false;
else if (ident_response[strlen(ident_response) - 2] != '\r')
! return false;
else
{
while (*cursor != ':' && *cursor != '\r')
cursor++; /* skip port field */
if (*cursor != ':')
! return false;
else
{
/* We're positioned to colon before response type field */
***************
*** 482,505 ****
while (isblank(*cursor))
cursor++; /* skip blanks */
i = 0;
! while (*cursor != ':' && *cursor != '\r' && !isblank(*cursor)
! && i < (int) (sizeof(response_type) - 1))
response_type[i++] = *cursor++;
response_type[i] = '\0';
while (isblank(*cursor))
cursor++; /* skip blanks */
if (strcmp(response_type, "USERID") != 0)
! *error_p = true;
else
{
-
/*
* It's a USERID response. Good. "cursor" should be
* pointing to the colon that precedes the operating
* system type.
*/
if (*cursor != ':')
! *error_p = true;
else
{
cursor++; /* Go over colon */
--- 642,664 ----
while (isblank(*cursor))
cursor++; /* skip blanks */
i = 0;
! while (*cursor != ':' && *cursor != '\r' && !isblank(*cursor) &&
! i < (int) (sizeof(response_type) - 1))
response_type[i++] = *cursor++;
response_type[i] = '\0';
while (isblank(*cursor))
cursor++; /* skip blanks */
if (strcmp(response_type, "USERID") != 0)
! return false;
else
{
/*
* It's a USERID response. Good. "cursor" should be
* pointing to the colon that precedes the operating
* system type.
*/
if (*cursor != ':')
! return false;
else
{
cursor++; /* Go over colon */
***************
*** 507,516 ****
while (*cursor != ':' && *cursor != '\r')
cursor++;
if (*cursor != ':')
! *error_p = true;
else
{
! int i; /* Index into *ident_username */
cursor++; /* Go over colon */
while (isblank(*cursor))
--- 666,675 ----
while (*cursor != ':' && *cursor != '\r')
cursor++;
if (*cursor != ':')
! return false;
else
{
! int i; /* Index into *ident_user */
cursor++; /* Go over colon */
while (isblank(*cursor))
***************
*** 518,526 ****
/* Rest of line is username. Copy it over. */
i = 0;
while (*cursor != '\r' && i < IDENT_USERNAME_MAX)
! ident_username[i++] = *cursor++;
! ident_username[i] = '\0';
! *error_p = false;
}
}
}
--- 677,685 ----
/* Rest of line is username. Copy it over. */
i = 0;
while (*cursor != '\r' && i < IDENT_USERNAME_MAX)
! ident_user[i++] = *cursor++;
! ident_user[i] = '\0';
! return true;
}
}
}
***************
*** 528,567 ****
}
}
-
! static void
! ident(const struct in_addr remote_ip_addr, const struct in_addr local_ip_addr,
! const ushort remote_port, const ushort local_port,
! bool *ident_failed, char *ident_username)
! {
! /*--------------------------------------------------------------------------
! 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
! ident server gives as "*ident_username".
!
! IP addresses and port numbers are in network byte order.
!
! But iff we're unable to get the information from ident, return
! *ident_failed == true (and *ident_username undefined).
! ----------------------------------------------------------------------------*/
int sock_fd, /* File descriptor for socket on which we
* talk to Ident */
rc; /* Return code from a locally called
* function */
sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
if (sock_fd == -1)
{
snprintf(PQerrormsg, PQERRORMSG_LENGTH,
! "Failed to create socket on which to talk to Ident server. "
! "socket() returned errno = %s (%d)\n",
! strerror(errno), errno);
fputs(PQerrormsg, stderr);
pqdebug("%s", PQerrormsg);
}
else
{
--- 687,726 ----
}
}
! /*
! * 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
! * ident server gives as "*ident_user".
+ * IP addresses and port numbers are in network byte order.
+ * But iff we're unable to get the information from ident, return
+ * false.
+ */
+ static int
+ ident(const struct in_addr remote_ip_addr,
+ const struct in_addr local_ip_addr,
+ const ushort remote_port,
+ const ushort local_port,
+ char *ident_user)
+ {
int sock_fd, /* File descriptor for socket on which we
* talk to Ident */
rc; /* Return code from a locally called
* function */
+ bool ident_return;
sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
if (sock_fd == -1)
{
snprintf(PQerrormsg, PQERRORMSG_LENGTH,
! "Failed to create socket on which to talk to Ident server. "
! "socket() returned errno = %s (%d)\n", strerror(errno), errno);
fputs(PQerrormsg, stderr);
pqdebug("%s", PQerrormsg);
+ ident_return = false;
}
else
{
***************
*** 595,607 ****
{
snprintf(PQerrormsg, PQERRORMSG_LENGTH,
"Unable to connect to Ident server on the host which is "
! "trying to connect to Postgres "
! "(IP address %s, Port %d). "
! "errno = %s (%d)\n",
! inet_ntoa(remote_ip_addr), IDENT_PORT, strerror(errno), errno);
fputs(PQerrormsg, stderr);
pqdebug("%s", PQerrormsg);
! *ident_failed = true;
}
else
{
--- 754,766 ----
{
snprintf(PQerrormsg, PQERRORMSG_LENGTH,
"Unable to connect to Ident server on the host which is "
! "trying to connect to Postgres "
! "(IP address %s, Port %d). "
! "errno = %s (%d)\n",
! inet_ntoa(remote_ip_addr), IDENT_PORT, strerror(errno), errno);
fputs(PQerrormsg, stderr);
pqdebug("%s", PQerrormsg);
! ident_return = false;
}
else
{
***************
*** 614,873 ****
if (rc < 0)
{
snprintf(PQerrormsg, PQERRORMSG_LENGTH,
! "Unable to send query to Ident server on the host which is "
! "trying to connect to Postgres (Host %s, Port %d),"
! "even though we successfully connected to it. "
! "errno = %s (%d)\n",
! inet_ntoa(remote_ip_addr), IDENT_PORT, strerror(errno), errno);
fputs(PQerrormsg, stderr);
pqdebug("%s", PQerrormsg);
! *ident_failed = true;
}
else
{
char ident_response[80 + IDENT_USERNAME_MAX];
! rc = recv(sock_fd, ident_response, sizeof(ident_response) - 1, 0);
if (rc < 0)
{
snprintf(PQerrormsg, PQERRORMSG_LENGTH,
! "Unable to receive response from Ident server "
! "on the host which is "
! "trying to connect to Postgres (Host %s, Port %d),"
! "even though we successfully sent our query to it. "
! "errno = %s (%d)\n",
! inet_ntoa(remote_ip_addr), IDENT_PORT,
! strerror(errno), errno);
fputs(PQerrormsg, stderr);
pqdebug("%s", PQerrormsg);
! *ident_failed = true;
}
else
{
- bool error; /* response from Ident is garbage. */
-
ident_response[rc] = '\0';
! interpret_ident_response(ident_response, &error, ident_username);
! *ident_failed = error;
}
}
close(sock_fd);
}
- }
- }
-
-
-
- static void
- parse_map_record(FILE *file,
- char *file_map, char *file_pguser, char *file_iuser)
- {
- /*---------------------------------------------------------------------------
- Take the noncomment line which is next on file "file" and interpret
- it as a line in a usermap file. Specifically, return the first
- 3 tokens as file_map, file_iuser, and file_pguser, respectively. If
- there are fewer than 3 tokens, return null strings for the missing
- ones.
-
- ---------------------------------------------------------------------------*/
- char buf[MAX_TOKEN];
-
- /* A token read from the file */
-
- /* Set defaults in case fields not in file */
- file_map[0] = '\0';
- file_pguser[0] = '\0';
- file_iuser[0] = '\0';
-
- next_token(file, buf, sizeof(buf));
- if (buf[0] != '\0')
- {
- strcpy(file_map, buf);
- next_token(file, buf, sizeof(buf));
- if (buf[0] != '\0')
- {
- strcpy(file_iuser, buf);
- next_token(file, buf, sizeof(buf));
- if (buf[0] != '\0')
- {
- strcpy(file_pguser, buf);
- read_through_eol(file);
- return;
- }
- }
- snprintf(PQerrormsg, PQERRORMSG_LENGTH,
- "Incomplete line in pg_ident: %s", file_map);
- fputs(PQerrormsg, stderr);
- pqdebug("%s", PQerrormsg);
}
}
-
! static void
! verify_against_open_usermap(FILE *file,
! const char *pguser,
! const char *ident_username,
! const char *usermap_name,
! bool *checks_out_p)
! {
! /*--------------------------------------------------------------------------
! This function does the same thing as verify_against_usermap,
! only with the config file already open on stream descriptor "file".
! ---------------------------------------------------------------------------*/
! bool match; /* We found a matching entry in the map
! * file */
! bool eof; /* We've reached the end of the file we're
! * reading */
!
! match = false; /* initial value */
! eof = false; /* initial value */
! while (!eof && !match)
! {
! /* Process a line from the map file */
!
! int c; /* a character read from the file */
!
! c = getc(file);
! ungetc(c, file);
! if (c == EOF)
! eof = true;
! else
! {
! if (c == '#')
! read_through_eol(file);
! else
! {
! /* The following are fields read from a record of the file */
! char file_map[MAX_TOKEN + 1];
! char file_pguser[MAX_TOKEN + 1];
! char file_iuser[MAX_TOKEN + 1];
!
! parse_map_record(file, file_map, file_pguser, file_iuser);
! if (strcmp(file_map, usermap_name) == 0 &&
! strcmp(file_pguser, pguser) == 0 &&
! strcmp(file_iuser, ident_username) == 0)
! match = true;
! }
! }
! }
! *checks_out_p = match;
! }
!
!
!
! static void
! verify_against_usermap(const char *pguser,
! const char *ident_username,
! const char *usermap_name,
! bool *checks_out_p)
{
! /*--------------------------------------------------------------------------
! See if the user with ident username "ident_username" is allowed to act
! as Postgres user "pguser" according to usermap "usermap_name". Look
! it up in the usermap file.
!
! Special case: For usermap "sameuser", don't look in the usermap
! file. That's an implied map where "pguser" must be identical to
! "ident_username" in order to be authorized.
!
! Iff authorized, return *checks_out_p == true.
! --------------------------------------------------------------------------*/
! if (usermap_name[0] == '\0')
! {
! *checks_out_p = false;
! snprintf(PQerrormsg, PQERRORMSG_LENGTH,
! "verify_against_usermap: hba configuration file does not "
! "have the usermap field filled in in the entry that pertains "
! "to this connection. That field is essential for Ident-based "
! "authentication.\n");
! fputs(PQerrormsg, stderr);
! pqdebug("%s", PQerrormsg);
! }
! else if (strcmp(usermap_name, "sameuser") == 0)
! {
! if (strcmp(ident_username, pguser) == 0)
! *checks_out_p = true;
! else
! *checks_out_p = false;
! }
else
! {
! FILE *file; /* The map file we have to read */
! char *map_file; /* The name of the map file we have to
! * read */
! int bufsize;
!
! /* put together the full pathname to the map file */
! bufsize = (strlen(DataDir) + strlen(USERMAP_FILE) + 2) * sizeof(char);
! map_file = (char *) palloc(bufsize);
! snprintf(map_file, bufsize, "%s/%s", DataDir, USERMAP_FILE);
!
! file = AllocateFile(map_file, PG_BINARY_R);
! if (file == NULL)
! {
! /* The open of the map file failed. */
!
! snprintf(PQerrormsg, PQERRORMSG_LENGTH,
! "verify_against_usermap: Unable to open usermap file \"%s\": %s\n",
! map_file, strerror(errno));
! fputs(PQerrormsg, stderr);
! pqdebug("%s", PQerrormsg);
!
! *checks_out_p = false;
! }
! else
! {
! verify_against_open_usermap(file,
! pguser, ident_username, usermap_name,
! checks_out_p);
! FreeFile(file);
! }
! pfree(map_file);
!
!
! }
}
!
int
! authident(struct sockaddr_in * raddr, struct sockaddr_in * laddr,
! const char *postgres_username,
! const char *auth_arg)
{
- /*---------------------------------------------------------------------------
- Talk to the ident server on the remote host and find out who owns the
- connection described by "port". Then look in the usermap file under
- the usermap *auth_arg and see if that user is equivalent to
- Postgres user *user.
-
- Return STATUS_OK if yes.
- ---------------------------------------------------------------------------*/
- bool checks_out;
- bool ident_failed;
-
- /* We were unable to get ident to give us a username */
- char ident_username[IDENT_USERNAME_MAX + 1];
-
- /* The username returned by ident */
-
- ident(raddr->sin_addr, laddr->sin_addr,
- raddr->sin_port, laddr->sin_port,
- &ident_failed, ident_username);
! if (ident_failed)
return STATUS_ERROR;
!
! verify_against_usermap(postgres_username, ident_username, auth_arg,
! &checks_out);
! return checks_out ? STATUS_OK : STATUS_ERROR;
}
#ifdef CYR_RECODE
#define CHARSET_FILE "charset.conf"
#define MAX_CHARSETS 10
--- 773,878 ----
if (rc < 0)
{
snprintf(PQerrormsg, PQERRORMSG_LENGTH,
! "Unable to send query to Ident server on the host which is "
! "trying to connect to Postgres (Host %s, Port %d),"
! "even though we successfully connected to it. "
! "errno = %s (%d)\n",
! inet_ntoa(remote_ip_addr), IDENT_PORT, strerror(errno), errno);
fputs(PQerrormsg, stderr);
pqdebug("%s", PQerrormsg);
! ident_return = false;
}
else
{
char ident_response[80 + IDENT_USERNAME_MAX];
! rc = recv(sock_fd, ident_response,
! sizeof(ident_response) - 1, 0);
if (rc < 0)
{
snprintf(PQerrormsg, PQERRORMSG_LENGTH,
! "Unable to receive response from Ident server "
! "on the host which is "
! "trying to connect to Postgres (Host %s, Port %d),"
! "even though we successfully sent our query to it. "
! "errno = %s (%d)\n",
! inet_ntoa(remote_ip_addr), IDENT_PORT,
! strerror(errno), errno);
fputs(PQerrormsg, stderr);
pqdebug("%s", PQerrormsg);
! ident_return = false;
}
else
{
ident_response[rc] = '\0';
! ident_return = interpret_ident_response(ident_response,
! ident_user);
}
}
close(sock_fd);
}
}
+ return ident_return;
}
! /*
! * Talk to the ident server on the remote host and find out who owns the
! * connection described by "port". Then look in the usermap file under
! * the usermap *auth_arg and see if that user is equivalent to
! * Postgres user *user.
! *
! * Return STATUS_OK if yes.
! */
! int
! authident(struct sockaddr_in *raddr, struct sockaddr_in *laddr,
! const char *pg_user, const char *auth_arg)
{
! /* We were unable to get ident to give us a username */
! char ident_user[IDENT_USERNAME_MAX + 1];
! /* The username returned by ident */
! if (!ident(raddr->sin_addr, laddr->sin_addr,
! raddr->sin_port, laddr->sin_port, ident_user))
! return STATUS_ERROR;
! if (check_ident_usermap(auth_arg, pg_user, ident_user))
! return STATUS_OK;
else
! return STATUS_ERROR;
}
! /*
! * Determine what authentication method should be used when accessing database
! * "database" from frontend "raddr", user "user". Return the method,
! * an optional argument, and STATUS_OK.
! * Note that STATUS_ERROR indicates a problem with the hba config file.
! * If the file is OK but does not contain any entry matching the request,
! * we return STATUS_OK and method = uaReject.
! */
int
! hba_getauthmethod(hbaPort *port)
{
! if (check_hba(port))
! return STATUS_OK;
! else
return STATUS_ERROR;
! }
! /*
! * Clear tokenized file contents and force reload on next use.
! */
! 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
***************
*** 882,889 ****
char Table[MAX_TOKEN];
};
static bool
! InRange(char *buf, int host)
{
int valid,
i,
--- 887,895 ----
char Table[MAX_TOKEN];
};
+
static bool
! CharSetInRange(char *buf, int host)
{
int valid,
i,
***************
*** 989,995 ****
else
{
if (c == '#')
! read_through_eol(file);
else
{
/* Read the key */
--- 995,1001 ----
else
{
if (c == '#')
! read_to_eol(file);
else
{
/* Read the key */
***************
*** 1009,1015 ****
next_token(file, buf, sizeof(buf));
if (buf[0] != '\0')
{
! if (InRange(buf, host))
{
/* Read the charset */
next_token(file, buf, sizeof(buf));
--- 1015,1021 ----
next_token(file, buf, sizeof(buf));
if (buf[0] != '\0')
{
! if (CharSetInRange(buf, host))
{
/* Read the charset */
next_token(file, buf, sizeof(buf));
***************
*** 1050,1056 ****
}
break;
}
! read_through_eol(file);
}
}
}
--- 1056,1062 ----
}
break;
}
! read_to_eol(file);
}
}
}
***************
*** 1066,1088 ****
pfree((struct CharsetItem *) ChArray[i]);
}
}
-
#endif
- int
- hba_getauthmethod(hbaPort *port)
- {
- /*---------------------------------------------------------------------------
- Determine what authentication method should be used when accessing database
- "database" from frontend "raddr", user "user". Return the method,
- an optional argument, and STATUS_OK.
- Note that STATUS_ERROR indicates a problem with the hba config file.
- If the file is OK but does not contain any entry matching the request,
- we return STATUS_OK and method = uaReject.
- ----------------------------------------------------------------------------*/
- bool hba_ok = false;
- find_hba_entry(port, &hba_ok);
- return hba_ok ? STATUS_OK : STATUS_ERROR;
- }
--- 1072,1078 ----
Index: src/backend/postmaster/postmaster.c
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/postmaster/postmaster.c,v
retrieving revision 1.231
diff -c -r1.231 postmaster.c
*** src/backend/postmaster/postmaster.c 2001/07/03 16:52:12 1.231
--- src/backend/postmaster/postmaster.c 2001/07/21 00:12:43
***************
*** 809,814 ****
--- 809,816 ----
nSockets = initMasks(&readmask, &writemask);
+ load_hba_and_ident();
+
for (;;)
{
Port *port;
***************
*** 874,879 ****
--- 876,882 ----
if (got_SIGHUP)
{
got_SIGHUP = false;
+ load_hba_and_ident();
ProcessConfigFile(PGC_SIGHUP);
}
***************
*** 993,999 ****
buf = palloc(len);
pq_getbytes(buf, len);
!
packet = buf;
/*
--- 996,1002 ----
buf = palloc(len);
pq_getbytes(buf, len);
!
packet = buf;
/*
***************
*** 1479,1485 ****
#endif
/*
* Check if this child was the statistics collector. If
! * so, start a new one.
*/
if (pgstat_ispgstat(pid))
{
--- 1482,1488 ----
#endif
/*
* Check if this child was the statistics collector. If
! * so, start a new one.
*/
if (pgstat_ispgstat(pid))
{
***************
*** 1987,2004 ****
av[ac++] = "-o";
av[ac++] = ttybuf;
}
-
av[ac] = (char *) NULL;
-
- /*
- * Release postmaster's working memory context so that backend can
- * recycle the space. Note this does not trash *MyProcPort, because
- * ConnCreate() allocated that space with malloc() ... else we'd need
- * to copy the Port data here.
- */
- MemoryContextSwitchTo(TopMemoryContext);
- MemoryContextDelete(PostmasterContext);
- PostmasterContext = NULL;
/*
* Debug: print arguments being passed to backend
--- 1990,1996 ----
Index: src/backend/tcop/postgres.c
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/tcop/postgres.c,v
retrieving revision 1.227
diff -c -r1.227 postgres.c
*** src/backend/tcop/postgres.c 2001/06/29 16:05:56 1.227
--- src/backend/tcop/postgres.c 2001/07/21 00:12:44
***************
*** 1120,1126 ****
unsigned short remote_port;
char *potential_DataDir = NULL;
!
/*
* Catch standard options before doing much else. This even works on
* systems without getopt_long.
--- 1120,1126 ----
unsigned short remote_port;
char *potential_DataDir = NULL;
!
/*
* Catch standard options before doing much else. This even works on
* systems without getopt_long.
***************
*** 1144,1158 ****
*
* If we are running under the postmaster, this is done already.
*/
! if (!IsUnderPostmaster)
{
SetProcessingMode(InitProcessing);
EnableExceptionHandling(true);
MemoryContextInit();
}
-
- if (IsUnderPostmaster)
- ClientAuthentication(MyProcPort); /* might not return */
/*
* Set default values for command-line options.
--- 1144,1169 ----
*
* If we are running under the postmaster, this is done already.
*/
! if (IsUnderPostmaster)
! {
! MemoryContextSwitchTo(TopMemoryContext);
! ClientAuthentication(MyProcPort); /* might not return */
! /*
! * Release postmaster's working memory context so that backend can
! * recycle the space. Note this does not trash *MyProcPort, because
! * ConnCreate() allocated that space with malloc() ... else we'd need
! * to copy the Port data here. We delete it here because the
! * authorization file tokens are stored in this context.
! */
! MemoryContextDelete(PostmasterContext);
! PostmasterContext = NULL;
! }
! else
{
SetProcessingMode(InitProcessing);
EnableExceptionHandling(true);
MemoryContextInit();
}
/*
* Set default values for command-line options.
Index: src/include/libpq/hba.h
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/src/include/libpq/hba.h,v
retrieving revision 1.19
diff -c -r1.19 hba.h
*** src/include/libpq/hba.h 2001/03/22 04:00:47 1.19
--- src/include/libpq/hba.h 2001/07/21 00:12:45
***************
*** 40,47 ****
typedef struct Port hbaPort;
! int hba_getauthmethod(hbaPort *port);
int authident(struct sockaddr_in * raddr, struct sockaddr_in * laddr,
const char *postgres_username, const char *auth_arg);
#endif
--- 40,48 ----
typedef struct Port hbaPort;
! int hba_getauthmethod(hbaPort *port);
int authident(struct sockaddr_in * raddr, struct sockaddr_in * laddr,
const char *postgres_username, const char *auth_arg);
+ void load_hba_and_ident(void);
#endif