Commit 462b7d46 authored by Bruce Momjian's avatar Bruce Momjian

Load pg_hba.conf and pg_ident.conf on startup and SIGHUP into List of

Lists, and use that for user validation.

Bruce Momjian
parent 8d464d05
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
* wherein you authenticate a user by seeing what IP address the system * wherein you authenticate a user by seeing what IP address the system
* says he comes from and possibly using ident). * says he comes from and possibly using ident).
* *
* $Id: hba.c,v 1.55 2001/02/10 02:31:26 tgl Exp $ * $Id: hba.c,v 1.56 2001/07/30 14:50:21 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include "libpq/libpq.h" #include "libpq/libpq.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "nodes/pg_list.h"
#include "storage/fd.h" #include "storage/fd.h"
...@@ -31,6 +32,13 @@ ...@@ -31,6 +32,13 @@
#define IDENT_USERNAME_MAX 512 #define IDENT_USERNAME_MAX 512
/* Max size of username ident server can return */ /* 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. /* Some standard C libraries, including GNU, have an isblank() function.
Others, including Solaris, do not. So we have our own. Others, including Solaris, do not. So we have our own.
...@@ -38,31 +46,31 @@ ...@@ -38,31 +46,31 @@
static bool static bool
isblank(const char c) isblank(const char c)
{ {
return c == ' ' || c == 9 /* tab */ ; 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 static void
next_token(FILE *fp, char *buf, const int bufsz) 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; int c;
char *eb = buf + (bufsz - 1); char *eb = buf + (bufsz - 1);
/* Move over inital token-delimiting blanks */ /* Move over inital token-delimiting blanks */
while (isblank(c = getc(fp))); while (isblank(c = getc(fp)))
;
if (c != '\n') if (c != '\n')
{ {
/* /*
* build a token in buf of next characters up to EOF, eol, or * build a token in buf of next characters up to EOF, eol, or
* blank. * blank.
...@@ -72,160 +80,214 @@ next_token(FILE *fp, char *buf, const int bufsz) ...@@ -72,160 +80,214 @@ next_token(FILE *fp, char *buf, const int bufsz)
if (buf < eb) if (buf < eb)
*buf++ = c; *buf++ = c;
c = getc(fp); c = getc(fp);
}
/* /*
* Put back the char right after the token (putting back EOF * Put back the char right after the token (putting back EOF
* is ok) * is ok)
*/ */
}
ungetc(c, fp); ungetc(c, fp);
} }
*buf = '\0'; *buf = '\0';
} }
static void static void
read_through_eol(FILE *file) read_to_eol(FILE *file)
{ {
int c; int c;
do while ((c = getc(file)) != '\n' && c != EOF)
c = getc(file); ;
while (c != '\n' && c != EOF);
} }
/*
* Process the file line by line and create a list of list of tokens.
*/
static void static void
read_hba_entry2(FILE *file, UserAuth *userauth_p, char *auth_arg, tokenize_file(FILE *file, List **lines)
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]; char buf[MAX_TOKEN];
List *next_line = NIL;
bool comment_found = false;
/* Get authentication type token. */ while (1)
{
next_token(file, buf, sizeof(buf)); 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;
}
}
if (strcmp(buf, "trust") == 0)
/*
* 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; *userauth_p = uaTrust;
else if (strcmp(buf, "ident") == 0) else if (strcmp(token, "ident") == 0)
*userauth_p = uaIdent; *userauth_p = uaIdent;
else if (strcmp(buf, "password") == 0) else if (strcmp(token, "password") == 0)
*userauth_p = uaPassword; *userauth_p = uaPassword;
else if (strcmp(buf, "krb4") == 0) else if (strcmp(token, "krb4") == 0)
*userauth_p = uaKrb4; *userauth_p = uaKrb4;
else if (strcmp(buf, "krb5") == 0) else if (strcmp(token, "krb5") == 0)
*userauth_p = uaKrb5; *userauth_p = uaKrb5;
else if (strcmp(buf, "reject") == 0) else if (strcmp(token, "reject") == 0)
*userauth_p = uaReject; *userauth_p = uaReject;
else if (strcmp(buf, "crypt") == 0) else if (strcmp(token, "crypt") == 0)
*userauth_p = uaCrypt; *userauth_p = uaCrypt;
else else
{
*error_p = true; *error_p = true;
if (buf[0] != '\0')
read_through_eol(file);
} }
if (!*error_p) if (!*error_p)
{ {
/* Get the authentication argument token, if any */ /* Get the authentication argument token, if any */
next_token(file, buf, sizeof(buf)); line = lnext(line);
if (buf[0] == '\0') if (!line)
auth_arg[0] = '\0'; auth_arg[0] = '\0';
else else
{ {
StrNCpy(auth_arg, buf, MAX_AUTH_ARG - 1); StrNCpy(auth_arg, token, MAX_AUTH_ARG - 1);
next_token(file, buf, sizeof(buf)); /* If there is more on the line, it is an error */
if (buf[0] != '\0') if (lnext(line))
{
*error_p = true; *error_p = true;
read_through_eol(file);
}
} }
} }
} }
/*
* 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 static void
process_hba_record(FILE *file, hbaPort *port, bool *matches_p, bool *error_p) parse_hba(List *line, hbaPort *port, bool *found_p, bool *error_p)
{ {
/*--------------------------------------------------------------------------- char *db;
Process the non-comment record in the config file that is next on the file. char *token;
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;
Assert(line != NIL);
token = lfirst(line);
/* Check the record type. */ /* Check the record type. */
if (strcmp(token, "local") == 0)
if (strcmp(buf, "local") == 0)
{ {
/* Get the database. */ /* Get the database. */
line = lnext(line);
next_token(file, db, sizeof(db)); if (!line)
goto hba_syntax;
if (db[0] == '\0') db = lfirst(line);
goto syntax;
line = lnext(line);
if (!line)
goto hba_syntax;
/* Read the rest of the line. */ /* Read the rest of the line. */
parse_hba_auth(line, &port->auth_method, port->auth_arg, error_p);
read_hba_entry2(file, &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. * For now, disallow methods that need AF_INET sockets to work.
*/ */
if (!*error_p && if (!*error_p &&
(port->auth_method == uaIdent || (port->auth_method == uaIdent ||
port->auth_method == uaKrb4 || port->auth_method == uaKrb4 ||
port->auth_method == uaKrb5)) port->auth_method == uaKrb5))
*error_p = true; goto hba_syntax;
if (*error_p)
goto syntax;
/* /*
* If this record isn't for our database, or this is the wrong * If this record isn't for our database, or this is the wrong
* sort of connection, ignore it. * sort of connection, ignore it.
*/ */
if ((strcmp(db, port->database) != 0 && strcmp(db, "all") != 0 && if ((strcmp(db, port->database) != 0 && strcmp(db, "all") != 0 &&
(strcmp(db, "sameuser") != 0 || strcmp(port->database, port->user) != 0)) || (strcmp(db, "sameuser") != 0 || strcmp(port->database, port->user) != 0)) ||
port->raddr.sa.sa_family != AF_UNIX) port->raddr.sa.sa_family != AF_UNIX)
return; return;
} }
else if (strcmp(buf, "host") == 0 || strcmp(buf, "hostssl") == 0) else if (strcmp(token, "host") == 0 || strcmp(token, "hostssl") == 0)
{ {
struct in_addr file_ip_addr, struct in_addr file_ip_addr, mask;
mask;
bool discard = 0;/* Discard this entry */
#ifdef USE_SSL #ifdef USE_SSL
/* If SSL, then check that we are on SSL */ /* If SSL, then check that we are on SSL */
if (strcmp(buf, "hostssl") == 0) if (strcmp(token, "hostssl") == 0)
{ {
if (!port->ssl) if (!port->ssl)
discard = 1; return;
/* Placeholder to require specific SSL level, perhaps? */ /* Placeholder to require specific SSL level, perhaps? */
/* Or a client certificate */ /* Or a client certificate */
...@@ -234,67 +296,50 @@ process_hba_record(FILE *file, hbaPort *port, bool *matches_p, bool *error_p) ...@@ -234,67 +296,50 @@ process_hba_record(FILE *file, hbaPort *port, bool *matches_p, bool *error_p)
} }
#else #else
/* If not SSL, we don't support this */ /* If not SSL, we don't support this */
if (strcmp(buf, "hostssl") == 0) if (strcmp(token, "hostssl") == 0)
goto syntax; goto hba_syntax;
#endif #endif
/* Get the database. */ /* Get the database. */
line = lnext(line);
next_token(file, db, sizeof(db)); if (!line)
goto hba_syntax;
if (db[0] == '\0') db = lfirst(line);
goto syntax;
/* Read the IP address field. */ /* Read the IP address field. */
line = lnext(line);
next_token(file, buf, sizeof(buf)); if (!line)
goto hba_syntax;
if (buf[0] == '\0') token = lfirst(line);
goto syntax;
/* Remember the IP address field and go get mask field. */ /* Remember the IP address field and go get mask field. */
if (!inet_aton(token, &file_ip_addr))
if (!inet_aton(buf, &file_ip_addr)) goto hba_syntax;
{
read_through_eol(file);
goto syntax;
}
/* Read the mask field. */ /* Read the mask field. */
line = lnext(line);
if (!line)
goto hba_syntax;
token = lfirst(line);
next_token(file, buf, sizeof(buf)); if (!inet_aton(token, &mask))
goto hba_syntax;
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 * This is the record we're looking for. Read the rest of the
* info from it. * info from it.
*/ */
line = lnext(line);
read_hba_entry2(file, &port->auth_method, port->auth_arg, error_p); if (!line)
goto hba_syntax;
parse_hba_auth(line, &port->auth_method, port->auth_arg, error_p);
if (*error_p) if (*error_p)
goto syntax; goto hba_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 * If this record isn't for our database, or this is the wrong
* sort of connection, ignore it. * sort of connection, ignore it.
*/ */
if ((strcmp(db, port->database) != 0 && strcmp(db, "all") != 0 && if ((strcmp(db, port->database) != 0 && strcmp(db, "all") != 0 &&
(strcmp(db, "sameuser") != 0 || strcmp(port->database, port->user) != 0)) || (strcmp(db, "sameuser") != 0 || strcmp(port->database, port->user) != 0)) ||
port->raddr.sa.sa_family != AF_INET || port->raddr.sa.sa_family != AF_INET ||
...@@ -302,98 +347,75 @@ process_hba_record(FILE *file, hbaPort *port, bool *matches_p, bool *error_p) ...@@ -302,98 +347,75 @@ process_hba_record(FILE *file, hbaPort *port, bool *matches_p, bool *error_p)
return; return;
} }
else else
{ goto hba_syntax;
read_through_eol(file);
goto syntax;
}
*matches_p = true;
/* Success */
*found_p = true;
return; return;
syntax: hba_syntax:
snprintf(PQerrormsg, PQERRORMSG_LENGTH, snprintf(PQerrormsg, PQERRORMSG_LENGTH,
"process_hba_record: invalid syntax in pg_hba.conf file\n"); "parse_hba: invalid syntax in pg_hba.conf file\n");
fputs(PQerrormsg, stderr); fputs(PQerrormsg, stderr);
pqdebug("%s", PQerrormsg); pqdebug("%s", PQerrormsg);
*error_p = true; *error_p = true;
return;
} }
/*
static void * Process the hba file line by line.
process_open_config_file(FILE *file, hbaPort *port, bool *hba_ok_p) */
static bool
check_hba(hbaPort *port)
{ {
/*--------------------------------------------------------------------------- List *line;
This function does the same thing as find_hba_entry, only with bool found_entry = false;
the config file already open on stream descriptor "file". bool error = false;
----------------------------------------------------------------------------*/
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) foreach (line, hba_lines)
{ {
/* Process a line from the config file */ parse_hba(lfirst(line), port, &found_entry, &error);
int c = getc(file); if (found_entry || error)
break;
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 (!error)
{ {
/* If no matching entry was found, synthesize 'reject' entry. */ /* If no matching entry was found, synthesize 'reject' entry. */
if (!found_entry) if (!found_entry)
port->auth_method = uaReject; port->auth_method = uaReject;
return true;
*hba_ok_p = true;
} }
else
return false;
} }
static void
find_hba_entry(hbaPort *port, bool *hba_ok_p)
{
/* /*
* Read the config file and find an entry that allows connection from * Read the config file and create a List of Lists of tokens in the file.
* 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 * 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 * 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 * follow directions and just installed his old hba file in the new database
* system. * system.
*/ */
static void
load_hba()
{
int fd, int fd,
bufsize; bufsize;
FILE *file; /* The config file we have to read */ FILE *file; /* The config file we have to read */
char *old_conf_file; char *old_conf_file;
/* The name of old config file that better not exist. */ if (hba_lines)
free_lines(&hba_lines);
/* Fail if config file by old name exists. */ /*
* 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 */ * Put together the full pathname to the old config file.
*/
bufsize = (strlen(DataDir) + strlen(OLD_CONF_FILE) + 2) * sizeof(char); bufsize = (strlen(DataDir) + strlen(OLD_CONF_FILE) + 2) * sizeof(char);
old_conf_file = (char *) palloc(bufsize); old_conf_file = (char *) palloc(bufsize);
snprintf(old_conf_file, bufsize, "%s/%s", DataDir, OLD_CONF_FILE); snprintf(old_conf_file, bufsize, "%s/%s", DataDir, OLD_CONF_FILE);
...@@ -406,8 +428,7 @@ find_hba_entry(hbaPort *port, bool *hba_ok_p) ...@@ -406,8 +428,7 @@ find_hba_entry(hbaPort *port, bool *hba_ok_p)
"A file exists by the name used for host-based authentication " "A file exists by the name used for host-based authentication "
"in prior releases of Postgres (%s). The name and format of " "in prior releases of Postgres (%s). The name and format of "
"the configuration file have changed, so this file should be " "the configuration file have changed, so this file should be "
"converted.\n", "converted.\n", old_conf_file);
old_conf_file);
fputs(PQerrormsg, stderr); fputs(PQerrormsg, stderr);
pqdebug("%s", PQerrormsg); pqdebug("%s", PQerrormsg);
} }
...@@ -425,16 +446,15 @@ find_hba_entry(hbaPort *port, bool *hba_ok_p) ...@@ -425,16 +446,15 @@ find_hba_entry(hbaPort *port, bool *hba_ok_p)
if (file == NULL) if (file == NULL)
{ {
/* The open of the config file failed. */ /* The open of the config file failed. */
snprintf(PQerrormsg, PQERRORMSG_LENGTH, snprintf(PQerrormsg, PQERRORMSG_LENGTH,
"find_hba_entry: Unable to open authentication config file \"%s\": %s\n", "load_hba: Unable to open authentication config file \"%s\": %s\n",
conf_file, strerror(errno)); conf_file, strerror(errno));
fputs(PQerrormsg, stderr); fputs(PQerrormsg, stderr);
pqdebug("%s", PQerrormsg); pqdebug("%s", PQerrormsg);
} }
else else
{ {
process_open_config_file(file, port, hba_ok_p); tokenize_file(file, &hba_lines);
FreeFile(file); FreeFile(file);
} }
pfree(conf_file); pfree(conf_file);
...@@ -443,35 +463,175 @@ find_hba_entry(hbaPort *port, bool *hba_ok_p) ...@@ -443,35 +463,175 @@ find_hba_entry(hbaPort *port, bool *hba_ok_p)
} }
/*
* Take the line and compare it to the needed map, pg_user and ident_user.
*/
static void static void
interpret_ident_response(char *ident_response, parse_ident_usermap(List *line, const char *usermap_name, const char *pg_user,
bool *error_p, char *ident_username) const char *ident_user, bool *found_p, bool *error_p)
{ {
/*---------------------------------------------------------------------------- char *token;
Parse the string "*ident_response" as a response from a query to an Ident char *file_map;
server. If it's a normal response indicating a username, return char *file_pguser;
*error_p == false and the username as *ident_username. If it's anything char *file_ident_user;
else, return *error_p == true and *ident_username undefined.
----------------------------------------------------------------------------*/
char *cursor; /* Cursor into *ident_response */
cursor = &ident_response[0]; *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 * Ident's response, in the telnet tradition, should end in crlf
* (\r\n). * (\r\n).
*/ */
if (strlen(ident_response) < 2) if (strlen(ident_response) < 2)
*error_p = true; return false;
else if (ident_response[strlen(ident_response) - 2] != '\r') else if (ident_response[strlen(ident_response) - 2] != '\r')
*error_p = true; return false;
else else
{ {
while (*cursor != ':' && *cursor != '\r') while (*cursor != ':' && *cursor != '\r')
cursor++; /* skip port field */ cursor++; /* skip port field */
if (*cursor != ':') if (*cursor != ':')
*error_p = true; return false;
else else
{ {
/* We're positioned to colon before response type field */ /* We're positioned to colon before response type field */
...@@ -482,24 +642,23 @@ interpret_ident_response(char *ident_response, ...@@ -482,24 +642,23 @@ interpret_ident_response(char *ident_response,
while (isblank(*cursor)) while (isblank(*cursor))
cursor++; /* skip blanks */ cursor++; /* skip blanks */
i = 0; i = 0;
while (*cursor != ':' && *cursor != '\r' && !isblank(*cursor) while (*cursor != ':' && *cursor != '\r' && !isblank(*cursor) &&
&& i < (int) (sizeof(response_type) - 1)) i < (int) (sizeof(response_type) - 1))
response_type[i++] = *cursor++; response_type[i++] = *cursor++;
response_type[i] = '\0'; response_type[i] = '\0';
while (isblank(*cursor)) while (isblank(*cursor))
cursor++; /* skip blanks */ cursor++; /* skip blanks */
if (strcmp(response_type, "USERID") != 0) if (strcmp(response_type, "USERID") != 0)
*error_p = true; return false;
else else
{ {
/* /*
* It's a USERID response. Good. "cursor" should be * It's a USERID response. Good. "cursor" should be
* pointing to the colon that precedes the operating * pointing to the colon that precedes the operating
* system type. * system type.
*/ */
if (*cursor != ':') if (*cursor != ':')
*error_p = true; return false;
else else
{ {
cursor++; /* Go over colon */ cursor++; /* Go over colon */
...@@ -507,10 +666,10 @@ interpret_ident_response(char *ident_response, ...@@ -507,10 +666,10 @@ interpret_ident_response(char *ident_response,
while (*cursor != ':' && *cursor != '\r') while (*cursor != ':' && *cursor != '\r')
cursor++; cursor++;
if (*cursor != ':') if (*cursor != ':')
*error_p = true; return false;
else else
{ {
int i; /* Index into *ident_username */ int i; /* Index into *ident_user */
cursor++; /* Go over colon */ cursor++; /* Go over colon */
while (isblank(*cursor)) while (isblank(*cursor))
...@@ -518,9 +677,9 @@ interpret_ident_response(char *ident_response, ...@@ -518,9 +677,9 @@ interpret_ident_response(char *ident_response,
/* Rest of line is username. Copy it over. */ /* Rest of line is username. Copy it over. */
i = 0; i = 0;
while (*cursor != '\r' && i < IDENT_USERNAME_MAX) while (*cursor != '\r' && i < IDENT_USERNAME_MAX)
ident_username[i++] = *cursor++; ident_user[i++] = *cursor++;
ident_username[i] = '\0'; ident_user[i] = '\0';
*error_p = false; return true;
} }
} }
} }
...@@ -529,39 +688,39 @@ interpret_ident_response(char *ident_response, ...@@ -529,39 +688,39 @@ 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
* ident server gives as "*ident_user".
static void * IP addresses and port numbers are in network byte order.
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).
----------------------------------------------------------------------------*/
* 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 int sock_fd, /* File descriptor for socket on which we
* talk to Ident */ * talk to Ident */
rc; /* Return code from a locally called rc; /* Return code from a locally called
* function */ * function */
bool ident_return;
sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
if (sock_fd == -1) if (sock_fd == -1)
{ {
snprintf(PQerrormsg, PQERRORMSG_LENGTH, snprintf(PQerrormsg, PQERRORMSG_LENGTH,
"Failed to create socket on which to talk to Ident server. " "Failed to create socket on which to talk to Ident server. "
"socket() returned errno = %s (%d)\n", "socket() returned errno = %s (%d)\n", strerror(errno), errno);
strerror(errno), errno);
fputs(PQerrormsg, stderr); fputs(PQerrormsg, stderr);
pqdebug("%s", PQerrormsg); pqdebug("%s", PQerrormsg);
ident_return = false;
} }
else else
{ {
...@@ -601,7 +760,7 @@ ident(const struct in_addr remote_ip_addr, const struct in_addr local_ip_addr, ...@@ -601,7 +760,7 @@ ident(const struct in_addr remote_ip_addr, const struct in_addr local_ip_addr,
inet_ntoa(remote_ip_addr), IDENT_PORT, strerror(errno), errno); inet_ntoa(remote_ip_addr), IDENT_PORT, strerror(errno), errno);
fputs(PQerrormsg, stderr); fputs(PQerrormsg, stderr);
pqdebug("%s", PQerrormsg); pqdebug("%s", PQerrormsg);
*ident_failed = true; ident_return = false;
} }
else else
{ {
...@@ -621,13 +780,14 @@ ident(const struct in_addr remote_ip_addr, const struct in_addr local_ip_addr, ...@@ -621,13 +780,14 @@ ident(const struct in_addr remote_ip_addr, const struct in_addr local_ip_addr,
inet_ntoa(remote_ip_addr), IDENT_PORT, strerror(errno), errno); inet_ntoa(remote_ip_addr), IDENT_PORT, strerror(errno), errno);
fputs(PQerrormsg, stderr); fputs(PQerrormsg, stderr);
pqdebug("%s", PQerrormsg); pqdebug("%s", PQerrormsg);
*ident_failed = true; ident_return = false;
} }
else else
{ {
char ident_response[80 + IDENT_USERNAME_MAX]; char ident_response[80 + IDENT_USERNAME_MAX];
rc = recv(sock_fd, ident_response, sizeof(ident_response) - 1, 0); rc = recv(sock_fd, ident_response,
sizeof(ident_response) - 1, 0);
if (rc < 0) if (rc < 0)
{ {
snprintf(PQerrormsg, PQERRORMSG_LENGTH, snprintf(PQerrormsg, PQERRORMSG_LENGTH,
...@@ -640,234 +800,79 @@ ident(const struct in_addr remote_ip_addr, const struct in_addr local_ip_addr, ...@@ -640,234 +800,79 @@ ident(const struct in_addr remote_ip_addr, const struct in_addr local_ip_addr,
strerror(errno), errno); strerror(errno), errno);
fputs(PQerrormsg, stderr); fputs(PQerrormsg, stderr);
pqdebug("%s", PQerrormsg); pqdebug("%s", PQerrormsg);
*ident_failed = true; ident_return = false;
} }
else else
{ {
bool error; /* response from Ident is garbage. */
ident_response[rc] = '\0'; ident_response[rc] = '\0';
interpret_ident_response(ident_response, &error, ident_username); ident_return = interpret_ident_response(ident_response,
*ident_failed = error; ident_user);
} }
} }
close(sock_fd); close(sock_fd);
} }
} }
return ident_return;
} }
/*
static void * Talk to the ident server on the remote host and find out who owns the
parse_map_record(FILE *file, * connection described by "port". Then look in the usermap file under
char *file_map, char *file_pguser, char *file_iuser) * the usermap *auth_arg and see if that user is equivalent to
{ * Postgres user *user.
/*--------------------------------------------------------------------------- *
Take the noncomment line which is next on file "file" and interpret * Return STATUS_OK if yes.
it as a line in a usermap file. Specifically, return the first */
3 tokens as file_map, file_iuser, and file_pguser, respectively. If int
there are fewer than 3 tokens, return null strings for the missing authident(struct sockaddr_in *raddr, struct sockaddr_in *laddr,
ones. const char *pg_user, const char *auth_arg)
---------------------------------------------------------------------------*/
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)
{ {
/*-------------------------------------------------------------------------- /* We were unable to get ident to give us a username */
This function does the same thing as verify_against_usermap, char ident_user[IDENT_USERNAME_MAX + 1];
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 */ /* The username returned by ident */
if (!ident(raddr->sin_addr, laddr->sin_addr,
raddr->sin_port, laddr->sin_port, ident_user))
return STATUS_ERROR;
c = getc(file); if (check_ident_usermap(auth_arg, pg_user, ident_user))
ungetc(c, file); return STATUS_OK;
if (c == EOF)
eof = true;
else
{
if (c == '#')
read_through_eol(file);
else else
{ return STATUS_ERROR;
/* 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 * Determine what authentication method should be used when accessing database
verify_against_usermap(const char *pguser, * "database" from frontend "raddr", user "user". Return the method,
const char *ident_username, * an optional argument, and STATUS_OK.
const char *usermap_name, * Note that STATUS_ERROR indicates a problem with the hba config file.
bool *checks_out_p) * 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)
{ {
/*--------------------------------------------------------------------------
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; if (check_hba(port))
} return STATUS_OK;
else else
{ return STATUS_ERROR;
verify_against_open_usermap(file,
pguser, ident_username, usermap_name,
checks_out_p);
FreeFile(file);
}
pfree(map_file);
}
} }
/*
* Clear tokenized file contents and force reload on next use.
int */
authident(struct sockaddr_in * raddr, struct sockaddr_in * laddr, void load_hba_and_ident(void)
const char *postgres_username,
const char *auth_arg)
{ {
/*--------------------------------------------------------------------------- load_hba();
Talk to the ident server on the remote host and find out who owns the load_ident();
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;
} }
/* Character set stuff. Not sure it really belongs in this file. */
#ifdef CYR_RECODE #ifdef CYR_RECODE
#define CHARSET_FILE "charset.conf" #define CHARSET_FILE "charset.conf"
#define MAX_CHARSETS 10 #define MAX_CHARSETS 10
...@@ -882,8 +887,9 @@ struct CharsetItem ...@@ -882,8 +887,9 @@ struct CharsetItem
char Table[MAX_TOKEN]; char Table[MAX_TOKEN];
}; };
static bool static bool
InRange(char *buf, int host) CharSetInRange(char *buf, int host)
{ {
int valid, int valid,
i, i,
...@@ -989,7 +995,7 @@ GetCharSetByHost(char *TableName, int host, const char *DataDir) ...@@ -989,7 +995,7 @@ GetCharSetByHost(char *TableName, int host, const char *DataDir)
else else
{ {
if (c == '#') if (c == '#')
read_through_eol(file); read_to_eol(file);
else else
{ {
/* Read the key */ /* Read the key */
...@@ -1009,7 +1015,7 @@ GetCharSetByHost(char *TableName, int host, const char *DataDir) ...@@ -1009,7 +1015,7 @@ GetCharSetByHost(char *TableName, int host, const char *DataDir)
next_token(file, buf, sizeof(buf)); next_token(file, buf, sizeof(buf));
if (buf[0] != '\0') if (buf[0] != '\0')
{ {
if (InRange(buf, host)) if (CharSetInRange(buf, host))
{ {
/* Read the charset */ /* Read the charset */
next_token(file, buf, sizeof(buf)); next_token(file, buf, sizeof(buf));
...@@ -1050,7 +1056,7 @@ GetCharSetByHost(char *TableName, int host, const char *DataDir) ...@@ -1050,7 +1056,7 @@ GetCharSetByHost(char *TableName, int host, const char *DataDir)
} }
break; break;
} }
read_through_eol(file); read_to_eol(file);
} }
} }
} }
...@@ -1066,23 +1072,7 @@ GetCharSetByHost(char *TableName, int host, const char *DataDir) ...@@ -1066,23 +1072,7 @@ GetCharSetByHost(char *TableName, int host, const char *DataDir)
pfree((struct CharsetItem *) ChArray[i]); pfree((struct CharsetItem *) ChArray[i]);
} }
} }
#endif #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;
}
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.231 2001/07/03 16:52:12 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.232 2001/07/30 14:50:22 momjian Exp $
* *
* NOTES * NOTES
* *
...@@ -809,6 +809,8 @@ ServerLoop(void) ...@@ -809,6 +809,8 @@ ServerLoop(void)
nSockets = initMasks(&readmask, &writemask); nSockets = initMasks(&readmask, &writemask);
load_hba_and_ident();
for (;;) for (;;)
{ {
Port *port; Port *port;
...@@ -874,6 +876,7 @@ ServerLoop(void) ...@@ -874,6 +876,7 @@ ServerLoop(void)
if (got_SIGHUP) if (got_SIGHUP)
{ {
got_SIGHUP = false; got_SIGHUP = false;
load_hba_and_ident();
ProcessConfigFile(PGC_SIGHUP); ProcessConfigFile(PGC_SIGHUP);
} }
...@@ -1987,19 +1990,8 @@ DoBackend(Port *port) ...@@ -1987,19 +1990,8 @@ DoBackend(Port *port)
av[ac++] = "-o"; av[ac++] = "-o";
av[ac++] = ttybuf; av[ac++] = ttybuf;
} }
av[ac] = (char *) NULL; 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 * Debug: print arguments being passed to backend
*/ */
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.227 2001/06/29 16:05:56 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.228 2001/07/30 14:50:24 momjian Exp $
* *
* NOTES * NOTES
* this is the "main" module of the postgres backend and * this is the "main" module of the postgres backend and
...@@ -1144,16 +1144,27 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[], const cha ...@@ -1144,16 +1144,27 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[], const cha
* *
* If we are running under the postmaster, this is done already. * If we are running under the postmaster, this is done already.
*/ */
if (!IsUnderPostmaster) 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); SetProcessingMode(InitProcessing);
EnableExceptionHandling(true); EnableExceptionHandling(true);
MemoryContextInit(); MemoryContextInit();
} }
if (IsUnderPostmaster)
ClientAuthentication(MyProcPort); /* might not return */
/* /*
* Set default values for command-line options. * Set default values for command-line options.
*/ */
...@@ -1714,7 +1725,7 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[], const cha ...@@ -1714,7 +1725,7 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[], const cha
if (!IsUnderPostmaster) if (!IsUnderPostmaster)
{ {
puts("\nPOSTGRES backend interactive interface "); puts("\nPOSTGRES backend interactive interface ");
puts("$Revision: 1.227 $ $Date: 2001/06/29 16:05:56 $\n"); puts("$Revision: 1.228 $ $Date: 2001/07/30 14:50:24 $\n");
} }
/* /*
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
* Interface to hba.c * Interface to hba.c
* *
* *
* $Id: hba.h,v 1.19 2001/03/22 04:00:47 momjian Exp $ * $Id: hba.h,v 1.20 2001/07/30 14:50:24 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -43,5 +43,6 @@ typedef struct Port hbaPort; ...@@ -43,5 +43,6 @@ typedef struct Port hbaPort;
int hba_getauthmethod(hbaPort *port); int hba_getauthmethod(hbaPort *port);
int authident(struct sockaddr_in * raddr, struct sockaddr_in * laddr, int authident(struct sockaddr_in * raddr, struct sockaddr_in * laddr,
const char *postgres_username, const char *auth_arg); const char *postgres_username, const char *auth_arg);
void load_hba_and_ident(void);
#endif #endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment