1996-10-12 09:47:12 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
1999-02-14 00:22:53 +01:00
|
|
|
* hba.c
|
1997-09-07 07:04:48 +02:00
|
|
|
* Routines to handle host based authentication (that's the scheme
|
|
|
|
* wherein you authenticate a user by seeing what IP address the system
|
2008-08-01 11:09:49 +02:00
|
|
|
* says he comes from and choosing authentication method based on it).
|
1996-10-12 09:47:12 +02:00
|
|
|
*
|
2008-01-01 20:46:01 +01:00
|
|
|
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
|
2001-08-02 01:52:50 +02:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2008-08-01 11:09:49 +02:00
|
|
|
* $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.166 2008/08/01 09:09:49 mha Exp $
|
1996-10-12 09:47:12 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
2001-08-01 00:55:45 +02:00
|
|
|
#include "postgres.h"
|
|
|
|
|
2005-02-26 19:43:34 +01:00
|
|
|
#include <ctype.h>
|
1996-10-12 09:47:12 +02:00
|
|
|
#include <pwd.h>
|
1997-08-27 05:48:50 +02:00
|
|
|
#include <fcntl.h>
|
2001-08-21 17:21:25 +02:00
|
|
|
#include <sys/param.h>
|
2001-09-07 21:52:54 +02:00
|
|
|
#include <sys/socket.h>
|
1996-10-12 09:47:12 +02:00
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <arpa/inet.h>
|
1996-11-03 08:00:57 +01:00
|
|
|
#include <unistd.h>
|
1996-10-12 09:47:12 +02:00
|
|
|
|
2006-07-13 18:49:20 +02:00
|
|
|
#include "libpq/ip.h"
|
1999-07-16 01:04:24 +02:00
|
|
|
#include "libpq/libpq.h"
|
2000-07-08 05:04:41 +02:00
|
|
|
#include "storage/fd.h"
|
2005-02-20 03:22:07 +01:00
|
|
|
#include "utils/flatfiles.h"
|
2004-07-11 02:18:45 +02:00
|
|
|
#include "utils/guc.h"
|
1996-10-12 09:47:12 +02:00
|
|
|
|
1999-10-23 05:13:33 +02:00
|
|
|
|
2006-07-13 18:49:20 +02:00
|
|
|
|
2005-02-26 19:43:34 +01:00
|
|
|
#define atooid(x) ((Oid) strtoul((x), NULL, 10))
|
2005-07-29 21:30:09 +02:00
|
|
|
#define atoxid(x) ((TransactionId) strtoul((x), NULL, 10))
|
2005-02-26 19:43:34 +01:00
|
|
|
|
2002-04-04 06:25:54 +02:00
|
|
|
/* This is used to separate values in multi-valued column strings */
|
2002-09-04 22:31:48 +02:00
|
|
|
#define MULTI_VALUE_SEP "\001"
|
2002-04-04 06:25:54 +02:00
|
|
|
|
2004-02-02 17:58:30 +01:00
|
|
|
#define MAX_TOKEN 256
|
|
|
|
|
2001-08-01 00:55:45 +02:00
|
|
|
/*
|
|
|
|
* These variables hold the pre-parsed contents of the hba and ident
|
2005-06-28 07:09:14 +02:00
|
|
|
* configuration files, as well as the flat auth file.
|
|
|
|
* Each is a list of sublists, one sublist for
|
2001-10-25 07:50:21 +02:00
|
|
|
* each (non-empty, non-comment) line of the file. Each sublist's
|
2001-08-01 00:55:45 +02:00
|
|
|
* first item is an integer line number (so we can give somewhat-useful
|
|
|
|
* location info in error messages). Remaining items are palloc'd strings,
|
|
|
|
* one string per token on the line. Note there will always be at least
|
|
|
|
* one token, since blank lines are not entered in the data structure.
|
|
|
|
*/
|
2004-05-26 06:41:50 +02:00
|
|
|
|
2004-10-10 01:13:22 +02:00
|
|
|
/* pre-parsed content of HBA config file and corresponding line #s */
|
2004-08-29 07:07:03 +02:00
|
|
|
static List *hba_lines = NIL;
|
|
|
|
static List *hba_line_nums = NIL;
|
|
|
|
|
2004-10-10 01:13:22 +02:00
|
|
|
/* pre-parsed content of ident usermap file and corresponding line #s */
|
2004-08-29 07:07:03 +02:00
|
|
|
static List *ident_lines = NIL;
|
|
|
|
static List *ident_line_nums = NIL;
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
/* pre-parsed content of flat auth file and corresponding line #s */
|
|
|
|
static List *role_lines = NIL;
|
|
|
|
static List *role_line_nums = NIL;
|
2002-04-04 06:25:54 +02:00
|
|
|
|
|
|
|
/* sorted entries so we can do binary search lookups */
|
2005-06-28 07:09:14 +02:00
|
|
|
static List **role_sorted = NULL; /* sorted role list, for bsearch() */
|
|
|
|
static int role_length;
|
2001-07-30 16:50:24 +02:00
|
|
|
|
2004-12-27 20:19:24 +01:00
|
|
|
static void tokenize_file(const char *filename, FILE *file,
|
2005-10-15 04:49:52 +02:00
|
|
|
List **lines, List **line_nums);
|
2004-12-27 20:19:24 +01:00
|
|
|
static char *tokenize_inc_file(const char *outer_filename,
|
2005-10-15 04:49:52 +02:00
|
|
|
const char *inc_filename);
|
1999-10-23 05:13:33 +02:00
|
|
|
|
2001-08-01 00:55:45 +02:00
|
|
|
/*
|
2003-04-13 06:07:17 +02:00
|
|
|
* isblank() exists in the ISO C99 spec, but it's not very portable yet,
|
|
|
|
* so provide our own version.
|
2001-08-01 00:55:45 +02:00
|
|
|
*/
|
2008-08-01 11:09:49 +02:00
|
|
|
bool
|
2003-04-13 06:07:17 +02:00
|
|
|
pg_isblank(const char c)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2002-06-26 16:52:08 +02:00
|
|
|
return c == ' ' || c == '\t' || c == '\r';
|
1996-10-28 10:03:50 +01:00
|
|
|
}
|
|
|
|
|
1996-10-12 09:47:12 +02:00
|
|
|
|
2001-07-30 16:50:24 +02:00
|
|
|
/*
|
2004-02-02 17:58:30 +01:00
|
|
|
* Grab one token out of fp. Tokens are strings of non-blank
|
2005-06-29 00:16:45 +02:00
|
|
|
* characters bounded by blank characters, commas, beginning of line, and
|
|
|
|
* end of line. Blank means space or tab. Tokens can be delimited by
|
2008-07-24 19:43:45 +02:00
|
|
|
* double quotes (this allows the inclusion of blanks, but not newlines).
|
2005-06-29 00:16:45 +02:00
|
|
|
*
|
|
|
|
* The token, if any, is returned at *buf (a buffer of size bufsz).
|
|
|
|
*
|
|
|
|
* If successful: store null-terminated token at *buf and return TRUE.
|
|
|
|
* If no more tokens on line: set *buf = '\0' and return FALSE.
|
|
|
|
*
|
|
|
|
* Leave file positioned at the character immediately after the token or EOF,
|
|
|
|
* whichever comes first. If no more tokens on line, position the file to the
|
|
|
|
* beginning of the next line or EOF, whichever comes first.
|
|
|
|
*
|
|
|
|
* Handle comments. Treat unquoted keywords that might be role names or
|
2008-07-24 19:43:45 +02:00
|
|
|
* database names specially, by appending a newline to them. Also, when
|
|
|
|
* a token is terminated by a comma, the comma is included in the returned
|
|
|
|
* token.
|
2001-07-30 16:50:24 +02:00
|
|
|
*/
|
2005-06-29 00:16:45 +02:00
|
|
|
static bool
|
2004-02-02 17:58:30 +01:00
|
|
|
next_token(FILE *fp, char *buf, int bufsz)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
int c;
|
2002-12-11 23:17:11 +01:00
|
|
|
char *start_buf = buf;
|
2003-12-25 04:44:05 +01:00
|
|
|
char *end_buf = buf + (bufsz - 2);
|
2002-04-04 06:25:54 +02:00
|
|
|
bool in_quote = false;
|
|
|
|
bool was_quote = false;
|
2004-08-29 07:07:03 +02:00
|
|
|
bool saw_quote = false;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2004-02-02 17:58:30 +01:00
|
|
|
Assert(end_buf > start_buf);
|
|
|
|
|
2002-04-04 06:25:54 +02:00
|
|
|
/* Move over initial whitespace and commas */
|
2003-04-13 06:07:17 +02:00
|
|
|
while ((c = getc(fp)) != EOF && (pg_isblank(c) || c == ','))
|
2001-07-30 16:50:24 +02:00
|
|
|
;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2004-02-02 17:58:30 +01:00
|
|
|
if (c == EOF || c == '\n')
|
|
|
|
{
|
|
|
|
*buf = '\0';
|
2005-06-29 00:16:45 +02:00
|
|
|
return false;
|
2004-02-02 17:58:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* Build a token in buf of next characters up to EOF, EOL, unquoted comma,
|
|
|
|
* or unquoted whitespace.
|
2004-02-02 17:58:30 +01:00
|
|
|
*/
|
|
|
|
while (c != EOF && c != '\n' &&
|
2008-07-24 19:43:45 +02:00
|
|
|
(!pg_isblank(c) || in_quote))
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2004-02-02 17:58:30 +01:00
|
|
|
/* skip comments to EOL */
|
|
|
|
if (c == '#' && !in_quote)
|
|
|
|
{
|
|
|
|
while ((c = getc(fp)) != EOF && c != '\n')
|
|
|
|
;
|
|
|
|
/* If only comment, consume EOL too; return EOL */
|
|
|
|
if (c != EOF && buf == start_buf)
|
|
|
|
c = getc(fp);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (buf >= end_buf)
|
|
|
|
{
|
2004-05-25 21:11:14 +02:00
|
|
|
*buf = '\0';
|
2004-02-02 17:58:30 +01:00
|
|
|
ereport(LOG,
|
|
|
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
2005-10-15 04:49:52 +02:00
|
|
|
errmsg("authentication file token too long, skipping: \"%s\"",
|
|
|
|
start_buf)));
|
2004-02-02 17:58:30 +01:00
|
|
|
/* Discard remainder of line */
|
|
|
|
while ((c = getc(fp)) != EOF && c != '\n')
|
|
|
|
;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2008-07-24 19:43:45 +02:00
|
|
|
if (c != '"' || was_quote)
|
2004-02-02 17:58:30 +01:00
|
|
|
*buf++ = c;
|
|
|
|
|
|
|
|
/* We pass back the comma so the caller knows there is more */
|
2008-07-24 19:43:45 +02:00
|
|
|
if (c == ',' && !in_quote)
|
2004-02-02 17:58:30 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
/* Literal double-quote is two double-quotes */
|
|
|
|
if (in_quote && c == '"')
|
|
|
|
was_quote = !was_quote;
|
|
|
|
else
|
|
|
|
was_quote = false;
|
|
|
|
|
|
|
|
if (c == '"')
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2004-02-02 17:58:30 +01:00
|
|
|
in_quote = !in_quote;
|
|
|
|
saw_quote = true;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
2001-10-25 07:50:21 +02:00
|
|
|
|
2004-02-02 17:58:30 +01:00
|
|
|
c = getc(fp);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
2003-12-25 04:44:05 +01:00
|
|
|
|
2004-02-02 17:58:30 +01:00
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* 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).
|
2004-02-02 17:58:30 +01:00
|
|
|
*/
|
|
|
|
if (c != EOF)
|
|
|
|
ungetc(c, fp);
|
|
|
|
|
|
|
|
*buf = '\0';
|
2003-12-25 04:44:05 +01:00
|
|
|
|
2004-08-29 07:07:03 +02:00
|
|
|
if (!saw_quote &&
|
|
|
|
(strcmp(start_buf, "all") == 0 ||
|
|
|
|
strcmp(start_buf, "sameuser") == 0 ||
|
2005-06-28 07:09:14 +02:00
|
|
|
strcmp(start_buf, "samegroup") == 0 ||
|
|
|
|
strcmp(start_buf, "samerole") == 0))
|
2003-12-25 04:44:05 +01:00
|
|
|
{
|
|
|
|
/* append newline to a magical keyword */
|
|
|
|
*buf++ = '\n';
|
2004-02-02 17:58:30 +01:00
|
|
|
*buf = '\0';
|
2003-12-25 04:44:05 +01:00
|
|
|
}
|
2005-06-29 00:16:45 +02:00
|
|
|
|
|
|
|
return (saw_quote || buf > start_buf);
|
1996-10-12 09:47:12 +02:00
|
|
|
}
|
|
|
|
|
2002-04-04 06:25:54 +02:00
|
|
|
/*
|
2002-09-04 22:31:48 +02:00
|
|
|
* 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.
|
2004-09-18 03:22:58 +02:00
|
|
|
*
|
2005-06-29 00:16:45 +02:00
|
|
|
* The result is a palloc'd string, or NULL if we have reached EOL.
|
2002-04-04 06:25:54 +02:00
|
|
|
*/
|
|
|
|
static char *
|
2004-12-27 20:19:24 +01:00
|
|
|
next_token_expand(const char *filename, FILE *file)
|
2002-04-04 06:25:54 +02:00
|
|
|
{
|
|
|
|
char buf[MAX_TOKEN];
|
|
|
|
char *comma_str = pstrdup("");
|
2005-06-29 00:16:45 +02:00
|
|
|
bool got_something = false;
|
2002-04-04 06:25:54 +02:00
|
|
|
bool trailing_comma;
|
|
|
|
char *incbuf;
|
2004-11-17 20:54:24 +01:00
|
|
|
int needed;
|
2002-04-04 06:25:54 +02:00
|
|
|
|
|
|
|
do
|
|
|
|
{
|
2005-06-29 00:16:45 +02:00
|
|
|
if (!next_token(file, buf, sizeof(buf)))
|
2002-04-04 06:25:54 +02:00
|
|
|
break;
|
|
|
|
|
2005-06-29 00:16:45 +02:00
|
|
|
got_something = true;
|
|
|
|
|
|
|
|
if (strlen(buf) > 0 && buf[strlen(buf) - 1] == ',')
|
2002-04-04 06:25:54 +02:00
|
|
|
{
|
|
|
|
trailing_comma = true;
|
2002-09-04 22:31:48 +02:00
|
|
|
buf[strlen(buf) - 1] = '\0';
|
2002-04-04 06:25:54 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
trailing_comma = false;
|
|
|
|
|
|
|
|
/* Is this referencing a file? */
|
|
|
|
if (buf[0] == '@')
|
2004-12-27 20:19:24 +01:00
|
|
|
incbuf = tokenize_inc_file(filename, buf + 1);
|
2002-04-04 06:25:54 +02:00
|
|
|
else
|
|
|
|
incbuf = pstrdup(buf);
|
|
|
|
|
2004-11-17 20:54:24 +01:00
|
|
|
needed = strlen(comma_str) + strlen(incbuf) + 1;
|
|
|
|
if (trailing_comma)
|
|
|
|
needed++;
|
|
|
|
comma_str = repalloc(comma_str, needed);
|
2002-04-04 06:25:54 +02:00
|
|
|
strcat(comma_str, incbuf);
|
|
|
|
if (trailing_comma)
|
|
|
|
strcat(comma_str, MULTI_VALUE_SEP);
|
2004-11-17 20:54:24 +01:00
|
|
|
pfree(incbuf);
|
2002-04-04 06:25:54 +02:00
|
|
|
} while (trailing_comma);
|
|
|
|
|
2005-06-29 00:16:45 +02:00
|
|
|
if (!got_something)
|
|
|
|
{
|
|
|
|
pfree(comma_str);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2002-04-04 06:25:54 +02:00
|
|
|
return comma_str;
|
|
|
|
}
|
|
|
|
|
1996-10-12 09:47:12 +02:00
|
|
|
|
2002-04-04 06:25:54 +02:00
|
|
|
/*
|
|
|
|
* Free memory used by lines/tokens (i.e., structure built by tokenize_file)
|
|
|
|
*/
|
1996-10-12 09:47:12 +02:00
|
|
|
static void
|
2004-05-26 06:41:50 +02:00
|
|
|
free_lines(List **lines, List **line_nums)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2004-05-26 06:41:50 +02:00
|
|
|
/*
|
|
|
|
* Either both must be non-NULL, or both must be NULL
|
|
|
|
*/
|
|
|
|
Assert((*lines != NIL && *line_nums != NIL) ||
|
|
|
|
(*lines == NIL && *line_nums == NIL));
|
|
|
|
|
2002-04-04 06:25:54 +02:00
|
|
|
if (*lines)
|
|
|
|
{
|
2004-05-26 06:41:50 +02:00
|
|
|
/*
|
2004-08-29 07:07:03 +02:00
|
|
|
* "lines" is a list of lists; each of those sublists consists of
|
|
|
|
* palloc'ed tokens, so we want to free each pointed-to token in a
|
|
|
|
* sublist, followed by the sublist itself, and finally the whole
|
|
|
|
* list.
|
2004-05-26 06:41:50 +02:00
|
|
|
*/
|
|
|
|
ListCell *line;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2002-04-04 06:25:54 +02:00
|
|
|
foreach(line, *lines)
|
|
|
|
{
|
|
|
|
List *ln = lfirst(line);
|
2004-05-26 06:41:50 +02:00
|
|
|
ListCell *token;
|
2002-04-04 06:25:54 +02:00
|
|
|
|
2004-05-26 06:41:50 +02:00
|
|
|
foreach(token, ln)
|
2002-04-04 06:25:54 +02:00
|
|
|
pfree(lfirst(token));
|
|
|
|
/* free the sublist structure itself */
|
2004-05-26 06:41:50 +02:00
|
|
|
list_free(ln);
|
2002-04-04 06:25:54 +02:00
|
|
|
}
|
|
|
|
/* free the list structure itself */
|
2004-05-26 06:41:50 +02:00
|
|
|
list_free(*lines);
|
2002-04-04 06:25:54 +02:00
|
|
|
/* clear the static variable */
|
|
|
|
*lines = NIL;
|
|
|
|
}
|
2004-05-26 06:41:50 +02:00
|
|
|
|
|
|
|
if (*line_nums)
|
|
|
|
{
|
|
|
|
list_free(*line_nums);
|
|
|
|
*line_nums = NIL;
|
|
|
|
}
|
2002-04-04 06:25:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static char *
|
2004-12-27 20:19:24 +01:00
|
|
|
tokenize_inc_file(const char *outer_filename,
|
|
|
|
const char *inc_filename)
|
2002-04-04 06:25:54 +02:00
|
|
|
{
|
|
|
|
char *inc_fullname;
|
|
|
|
FILE *inc_file;
|
2002-09-04 22:31:48 +02:00
|
|
|
List *inc_lines;
|
2004-05-26 06:41:50 +02:00
|
|
|
List *inc_line_nums;
|
|
|
|
ListCell *line;
|
2004-12-27 20:19:24 +01:00
|
|
|
char *comma_str;
|
2002-04-04 06:25:54 +02:00
|
|
|
|
2004-12-27 20:19:24 +01:00
|
|
|
if (is_absolute_path(inc_filename))
|
|
|
|
{
|
|
|
|
/* absolute path is taken as-is */
|
|
|
|
inc_fullname = pstrdup(inc_filename);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* relative path is relative to dir of calling file */
|
|
|
|
inc_fullname = (char *) palloc(strlen(outer_filename) + 1 +
|
|
|
|
strlen(inc_filename) + 1);
|
|
|
|
strcpy(inc_fullname, outer_filename);
|
|
|
|
get_parent_directory(inc_fullname);
|
|
|
|
join_path_components(inc_fullname, inc_fullname, inc_filename);
|
|
|
|
canonicalize_path(inc_fullname);
|
|
|
|
}
|
2002-04-04 06:25:54 +02:00
|
|
|
|
|
|
|
inc_file = AllocateFile(inc_fullname, "r");
|
2004-12-27 20:19:24 +01:00
|
|
|
if (inc_file == NULL)
|
2002-04-04 06:25:54 +02:00
|
|
|
{
|
2003-07-22 21:00:12 +02:00
|
|
|
ereport(LOG,
|
|
|
|
(errcode_for_file_access(),
|
|
|
|
errmsg("could not open secondary authentication file \"@%s\" as \"%s\": %m",
|
|
|
|
inc_filename, inc_fullname)));
|
2002-04-04 06:25:54 +02:00
|
|
|
pfree(inc_fullname);
|
|
|
|
|
2004-12-27 20:19:24 +01:00
|
|
|
/* return single space, it matches nothing */
|
|
|
|
return pstrdup(" ");
|
2002-04-04 06:25:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* There is possible recursion here if the file contains @ */
|
2004-12-27 20:19:24 +01:00
|
|
|
tokenize_file(inc_fullname, inc_file, &inc_lines, &inc_line_nums);
|
|
|
|
|
2002-04-04 06:25:54 +02:00
|
|
|
FreeFile(inc_file);
|
2004-12-27 20:19:24 +01:00
|
|
|
pfree(inc_fullname);
|
2002-04-04 06:25:54 +02:00
|
|
|
|
2004-11-17 20:54:24 +01:00
|
|
|
/* Create comma-separated string from List */
|
2004-12-27 20:19:24 +01:00
|
|
|
comma_str = pstrdup("");
|
2002-04-04 06:25:54 +02:00
|
|
|
foreach(line, inc_lines)
|
|
|
|
{
|
2004-08-29 07:07:03 +02:00
|
|
|
List *token_list = (List *) lfirst(line);
|
|
|
|
ListCell *token;
|
2002-04-04 06:25:54 +02:00
|
|
|
|
2004-05-26 06:41:50 +02:00
|
|
|
foreach(token, token_list)
|
2002-04-04 06:25:54 +02:00
|
|
|
{
|
2005-10-15 04:49:52 +02:00
|
|
|
int oldlen = strlen(comma_str);
|
|
|
|
int needed;
|
2004-11-17 20:54:24 +01:00
|
|
|
|
|
|
|
needed = oldlen + strlen(lfirst(token)) + 1;
|
|
|
|
if (oldlen > 0)
|
|
|
|
needed++;
|
|
|
|
comma_str = repalloc(comma_str, needed);
|
|
|
|
if (oldlen > 0)
|
2002-04-04 06:25:54 +02:00
|
|
|
strcat(comma_str, MULTI_VALUE_SEP);
|
|
|
|
strcat(comma_str, lfirst(token));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-05-26 06:41:50 +02:00
|
|
|
free_lines(&inc_lines, &inc_line_nums);
|
2002-04-04 06:25:54 +02:00
|
|
|
|
2004-12-27 20:19:24 +01:00
|
|
|
/* if file is empty, return single space rather than empty string */
|
|
|
|
if (strlen(comma_str) == 0)
|
|
|
|
{
|
|
|
|
pfree(comma_str);
|
|
|
|
return pstrdup(" ");
|
|
|
|
}
|
|
|
|
|
2002-04-04 06:25:54 +02:00
|
|
|
return comma_str;
|
1996-10-12 09:47:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2001-07-30 16:50:24 +02:00
|
|
|
/*
|
2004-05-26 06:41:50 +02:00
|
|
|
* Tokenize the given file, storing the resulting data into two lists:
|
|
|
|
* a list of sublists, each sublist containing the tokens in a line of
|
|
|
|
* the file, and a list of line numbers.
|
2004-12-27 20:19:24 +01:00
|
|
|
*
|
|
|
|
* filename must be the absolute path to the target file.
|
2001-07-30 16:50:24 +02:00
|
|
|
*/
|
2004-05-26 06:41:50 +02:00
|
|
|
static void
|
2004-12-27 20:19:24 +01:00
|
|
|
tokenize_file(const char *filename, FILE *file,
|
|
|
|
List **lines, List **line_nums)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2004-05-26 06:41:50 +02:00
|
|
|
List *current_line = NIL;
|
2001-08-01 00:55:45 +02:00
|
|
|
int line_number = 1;
|
2002-04-04 06:25:54 +02:00
|
|
|
char *buf;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2004-05-26 06:41:50 +02:00
|
|
|
*lines = *line_nums = NIL;
|
|
|
|
|
2001-08-01 00:55:45 +02:00
|
|
|
while (!feof(file))
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2004-12-27 20:19:24 +01:00
|
|
|
buf = next_token_expand(filename, file);
|
2001-07-30 16:50:24 +02:00
|
|
|
|
2002-04-04 06:25:54 +02:00
|
|
|
/* add token to list, unless we are at EOL or comment start */
|
2005-06-29 00:16:45 +02:00
|
|
|
if (buf)
|
2001-07-30 16:50:24 +02:00
|
|
|
{
|
2004-05-26 06:41:50 +02:00
|
|
|
if (current_line == NIL)
|
|
|
|
{
|
|
|
|
/* make a new line List, record its line number */
|
|
|
|
current_line = lappend(current_line, buf);
|
|
|
|
*lines = lappend(*lines, current_line);
|
|
|
|
*line_nums = lappend_int(*line_nums, line_number);
|
|
|
|
}
|
|
|
|
else
|
2001-07-30 16:50:24 +02:00
|
|
|
{
|
2004-05-26 06:41:50 +02:00
|
|
|
/* append token to current line's list */
|
|
|
|
current_line = lappend(current_line, buf);
|
2001-07-30 16:50:24 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
2001-08-01 00:55:45 +02:00
|
|
|
{
|
2002-04-04 06:25:54 +02:00
|
|
|
/* we are at real or logical EOL, so force a new line List */
|
2004-05-26 06:41:50 +02:00
|
|
|
current_line = NIL;
|
|
|
|
/* Advance line number whenever we reach EOL */
|
2001-08-01 00:55:45 +02:00
|
|
|
line_number++;
|
2004-05-26 06:41:50 +02:00
|
|
|
}
|
2001-07-30 16:50:24 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2005-06-28 07:09:14 +02:00
|
|
|
* Compare two lines based on their role/member names.
|
2002-04-29 00:49:07 +02:00
|
|
|
*
|
|
|
|
* Used for bsearch() lookup.
|
|
|
|
*/
|
|
|
|
static int
|
2005-06-28 07:09:14 +02:00
|
|
|
role_bsearch_cmp(const void *role, const void *list)
|
2002-04-29 00:49:07 +02:00
|
|
|
{
|
2005-06-28 07:09:14 +02:00
|
|
|
char *role2 = linitial(*(List **) list);
|
2002-04-29 00:49:07 +02:00
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
return strcmp(role, role2);
|
2002-04-29 00:49:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-04-04 06:25:54 +02:00
|
|
|
/*
|
2005-06-28 07:09:14 +02:00
|
|
|
* Lookup a role name in the pg_auth file
|
2002-04-04 06:25:54 +02:00
|
|
|
*/
|
2005-10-15 04:49:52 +02:00
|
|
|
List **
|
2005-06-28 07:09:14 +02:00
|
|
|
get_role_line(const char *role)
|
2002-04-04 06:25:54 +02:00
|
|
|
{
|
2003-12-05 16:50:31 +01:00
|
|
|
/* On some versions of Solaris, bsearch of zero items dumps core */
|
2005-06-28 07:09:14 +02:00
|
|
|
if (role_length == 0)
|
2003-12-05 16:50:31 +01:00
|
|
|
return NULL;
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
return (List **) bsearch((void *) role,
|
|
|
|
(void *) role_sorted,
|
|
|
|
role_length,
|
2002-09-04 22:31:48 +02:00
|
|
|
sizeof(List *),
|
2005-06-28 07:09:14 +02:00
|
|
|
role_bsearch_cmp);
|
2002-04-04 06:25:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2005-06-29 00:16:45 +02:00
|
|
|
* Does user belong to role?
|
|
|
|
*
|
|
|
|
* user is always the name given as the attempted login identifier.
|
|
|
|
* We check to see if it is a member of the specified role name.
|
2002-04-04 06:25:54 +02:00
|
|
|
*/
|
2003-04-03 23:25:02 +02:00
|
|
|
static bool
|
2005-06-29 00:16:45 +02:00
|
|
|
is_member(const char *user, const char *role)
|
2002-04-04 06:25:54 +02:00
|
|
|
{
|
2004-05-26 06:41:50 +02:00
|
|
|
List **line;
|
2005-02-20 05:45:59 +01:00
|
|
|
ListCell *line_item;
|
2002-04-04 06:25:54 +02:00
|
|
|
|
2005-06-29 00:16:45 +02:00
|
|
|
if ((line = get_role_line(user)) == NULL)
|
|
|
|
return false; /* if user not exist, say "no" */
|
2004-05-26 06:41:50 +02:00
|
|
|
|
2005-06-29 00:16:45 +02:00
|
|
|
/* A user always belongs to its own role */
|
|
|
|
if (strcmp(user, role) == 0)
|
|
|
|
return true;
|
2005-02-20 05:45:59 +01:00
|
|
|
|
2005-06-29 00:16:45 +02:00
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* skip over the role name, password, valuntil, examine all the membership
|
|
|
|
* entries
|
2005-06-29 00:16:45 +02:00
|
|
|
*/
|
|
|
|
if (list_length(*line) < 4)
|
|
|
|
return false;
|
|
|
|
for_each_cell(line_item, lnext(lnext(lnext(list_head(*line)))))
|
2005-02-20 05:45:59 +01:00
|
|
|
{
|
2005-06-29 00:16:45 +02:00
|
|
|
if (strcmp((char *) lfirst(line_item), role) == 0)
|
2005-02-20 05:45:59 +01:00
|
|
|
return true;
|
2002-04-04 06:25:54 +02:00
|
|
|
}
|
2001-07-30 16:50:24 +02:00
|
|
|
|
2003-04-03 23:25:02 +02:00
|
|
|
return false;
|
2002-04-04 06:25:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2005-06-29 00:16:45 +02:00
|
|
|
* Check comma-separated list for a match to role, allowing group names.
|
|
|
|
*
|
|
|
|
* NB: param_str is destructively modified! In current usage, this is
|
|
|
|
* okay only because this code is run after forking off from the postmaster,
|
|
|
|
* and so it doesn't matter that we clobber the stored hba info.
|
2002-04-04 06:25:54 +02:00
|
|
|
*/
|
2003-04-03 23:25:02 +02:00
|
|
|
static bool
|
2005-06-29 00:16:45 +02:00
|
|
|
check_role(const char *role, char *param_str)
|
2002-04-04 06:25:54 +02:00
|
|
|
{
|
2002-09-04 22:31:48 +02:00
|
|
|
char *tok;
|
2002-04-04 06:25:54 +02:00
|
|
|
|
2005-06-29 00:16:45 +02:00
|
|
|
for (tok = strtok(param_str, MULTI_VALUE_SEP);
|
|
|
|
tok != NULL;
|
|
|
|
tok = strtok(NULL, MULTI_VALUE_SEP))
|
2002-04-04 06:25:54 +02:00
|
|
|
{
|
|
|
|
if (tok[0] == '+')
|
2001-07-30 16:50:24 +02:00
|
|
|
{
|
2005-06-29 00:16:45 +02:00
|
|
|
if (is_member(role, tok + 1))
|
2003-04-03 23:25:02 +02:00
|
|
|
return true;
|
2002-04-04 06:25:54 +02:00
|
|
|
}
|
2005-06-28 07:09:14 +02:00
|
|
|
else if (strcmp(tok, role) == 0 ||
|
2003-12-25 04:44:05 +01:00
|
|
|
strcmp(tok, "all\n") == 0)
|
2003-04-03 23:25:02 +02:00
|
|
|
return true;
|
2002-04-04 06:25:54 +02:00
|
|
|
}
|
2001-08-01 00:55:45 +02:00
|
|
|
|
2003-04-03 23:25:02 +02:00
|
|
|
return false;
|
2002-04-04 06:25:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2005-06-28 07:09:14 +02:00
|
|
|
* Check to see if db/role combination matches param string.
|
2005-06-29 00:16:45 +02:00
|
|
|
*
|
|
|
|
* NB: param_str is destructively modified! In current usage, this is
|
|
|
|
* okay only because this code is run after forking off from the postmaster,
|
|
|
|
* and so it doesn't matter that we clobber the stored hba info.
|
2002-04-04 06:25:54 +02:00
|
|
|
*/
|
2003-04-03 23:25:02 +02:00
|
|
|
static bool
|
2005-06-29 00:16:45 +02:00
|
|
|
check_db(const char *dbname, const char *role, char *param_str)
|
2002-04-04 06:25:54 +02:00
|
|
|
{
|
2002-09-04 22:31:48 +02:00
|
|
|
char *tok;
|
2002-04-04 06:25:54 +02:00
|
|
|
|
2005-06-29 00:16:45 +02:00
|
|
|
for (tok = strtok(param_str, MULTI_VALUE_SEP);
|
|
|
|
tok != NULL;
|
|
|
|
tok = strtok(NULL, MULTI_VALUE_SEP))
|
2002-04-04 06:25:54 +02:00
|
|
|
{
|
2003-12-25 04:44:05 +01:00
|
|
|
if (strcmp(tok, "all\n") == 0)
|
2003-04-03 23:25:02 +02:00
|
|
|
return true;
|
2003-12-25 04:44:05 +01:00
|
|
|
else if (strcmp(tok, "sameuser\n") == 0)
|
2002-04-04 06:25:54 +02:00
|
|
|
{
|
2005-06-28 07:09:14 +02:00
|
|
|
if (strcmp(dbname, role) == 0)
|
2003-04-03 23:25:02 +02:00
|
|
|
return true;
|
2001-07-30 16:50:24 +02:00
|
|
|
}
|
2005-06-28 07:09:14 +02:00
|
|
|
else if (strcmp(tok, "samegroup\n") == 0 ||
|
|
|
|
strcmp(tok, "samerole\n") == 0)
|
2002-04-04 06:25:54 +02:00
|
|
|
{
|
2005-06-29 00:16:45 +02:00
|
|
|
if (is_member(role, dbname))
|
2003-04-03 23:25:02 +02:00
|
|
|
return true;
|
2002-04-04 06:25:54 +02:00
|
|
|
}
|
|
|
|
else if (strcmp(tok, dbname) == 0)
|
2003-04-03 23:25:02 +02:00
|
|
|
return true;
|
2001-07-30 16:50:24 +02:00
|
|
|
}
|
2003-04-03 23:25:02 +02:00
|
|
|
return false;
|
2001-07-30 16:50:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2001-10-25 07:50:21 +02:00
|
|
|
* Scan the rest of a host record (after the mask field)
|
2003-04-18 00:26:02 +02:00
|
|
|
* and return the interpretation of it as *userauth_p, *auth_arg_p, and
|
2004-05-26 06:41:50 +02:00
|
|
|
* *error_p. *line_item points to the next token of the line, and is
|
2004-05-20 00:06:16 +02:00
|
|
|
* advanced over successfully-read tokens.
|
2001-07-30 16:50:24 +02:00
|
|
|
*/
|
|
|
|
static void
|
2004-05-26 06:41:50 +02:00
|
|
|
parse_hba_auth(ListCell **line_item, UserAuth *userauth_p,
|
|
|
|
char **auth_arg_p, bool *error_p)
|
2001-07-30 16:50:24 +02:00
|
|
|
{
|
2001-10-25 07:50:21 +02:00
|
|
|
char *token;
|
2001-07-30 16:50:24 +02:00
|
|
|
|
2003-04-18 00:26:02 +02:00
|
|
|
*auth_arg_p = NULL;
|
|
|
|
|
2004-05-26 06:41:50 +02:00
|
|
|
if (!*line_item)
|
2001-07-30 16:50:24 +02:00
|
|
|
{
|
2004-05-20 00:06:16 +02:00
|
|
|
*error_p = true;
|
|
|
|
return;
|
|
|
|
}
|
2004-05-26 06:41:50 +02:00
|
|
|
|
|
|
|
token = lfirst(*line_item);
|
2004-05-20 00:06:16 +02:00
|
|
|
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, "krb5") == 0)
|
|
|
|
*userauth_p = uaKrb5;
|
2007-07-10 15:14:22 +02:00
|
|
|
else if (strcmp(token, "gss") == 0)
|
|
|
|
*userauth_p = uaGSS;
|
2007-07-23 12:16:54 +02:00
|
|
|
else if (strcmp(token, "sspi") == 0)
|
|
|
|
*userauth_p = uaSSPI;
|
2004-05-20 00:06:16 +02:00
|
|
|
else if (strcmp(token, "reject") == 0)
|
|
|
|
*userauth_p = uaReject;
|
|
|
|
else if (strcmp(token, "md5") == 0)
|
|
|
|
*userauth_p = uaMD5;
|
|
|
|
else if (strcmp(token, "crypt") == 0)
|
|
|
|
*userauth_p = uaCrypt;
|
2001-09-06 05:23:38 +02:00
|
|
|
#ifdef USE_PAM
|
2004-05-20 00:06:16 +02:00
|
|
|
else if (strcmp(token, "pam") == 0)
|
|
|
|
*userauth_p = uaPAM;
|
2006-03-06 18:41:44 +01:00
|
|
|
#endif
|
|
|
|
#ifdef USE_LDAP
|
2006-10-04 02:30:14 +02:00
|
|
|
else if (strcmp(token, "ldap") == 0)
|
|
|
|
*userauth_p = uaLDAP;
|
2001-09-06 05:23:38 +02:00
|
|
|
#endif
|
2004-05-20 00:06:16 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
*error_p = true;
|
|
|
|
return;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
2004-05-26 06:41:50 +02:00
|
|
|
*line_item = lnext(*line_item);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2004-05-20 00:06:16 +02:00
|
|
|
/* Get the authentication argument token, if any */
|
2004-05-26 06:41:50 +02:00
|
|
|
if (*line_item)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2004-05-26 06:41:50 +02:00
|
|
|
token = lfirst(*line_item);
|
2004-05-20 00:06:16 +02:00
|
|
|
*auth_arg_p = pstrdup(token);
|
2004-05-26 06:41:50 +02:00
|
|
|
*line_item = lnext(*line_item);
|
2004-05-20 00:06:16 +02:00
|
|
|
/* If there is more on the line, it is an error */
|
2004-05-26 06:41:50 +02:00
|
|
|
if (*line_item)
|
2004-05-20 00:06:16 +02:00
|
|
|
*error_p = true;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1996-10-12 09:47:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2001-07-30 16:50:24 +02:00
|
|
|
/*
|
2001-10-25 07:50:21 +02:00
|
|
|
* Process one line from the hba config file.
|
2001-07-30 16:50:24 +02:00
|
|
|
*
|
2001-10-25 07:50:21 +02:00
|
|
|
* See if it applies to a connection from a host with IP address port->raddr
|
|
|
|
* to a database named port->database. If so, return *found_p true
|
|
|
|
* and fill in the auth arguments into the appropriate port fields.
|
|
|
|
* If not, leave *found_p as it was. If the record has a syntax error,
|
2002-03-04 02:46:04 +01:00
|
|
|
* return *error_p true, after issuing a message to the log. If no error,
|
2001-10-25 07:50:21 +02:00
|
|
|
* leave *error_p as it was.
|
2001-07-30 16:50:24 +02:00
|
|
|
*/
|
1996-10-12 09:47:12 +02:00
|
|
|
static void
|
2004-05-26 06:41:50 +02:00
|
|
|
parse_hba(List *line, int line_num, hbaPort *port,
|
|
|
|
bool *found_p, bool *error_p)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2003-08-04 02:43:34 +02:00
|
|
|
char *token;
|
|
|
|
char *db;
|
2005-06-28 07:09:14 +02:00
|
|
|
char *role;
|
2003-09-06 01:07:21 +02:00
|
|
|
struct addrinfo *gai_result;
|
2003-08-04 02:43:34 +02:00
|
|
|
struct addrinfo hints;
|
2003-06-12 09:36:51 +02:00
|
|
|
int ret;
|
2003-09-06 01:07:21 +02:00
|
|
|
struct sockaddr_storage addr;
|
|
|
|
struct sockaddr_storage mask;
|
|
|
|
char *cidr_slash;
|
2004-05-26 06:41:50 +02:00
|
|
|
ListCell *line_item;
|
1998-01-26 02:42:53 +01:00
|
|
|
|
2004-05-26 06:41:50 +02:00
|
|
|
line_item = list_head(line);
|
1998-01-26 02:42:53 +01:00
|
|
|
/* Check the record type. */
|
2004-05-26 06:41:50 +02:00
|
|
|
token = lfirst(line_item);
|
2001-07-30 16:50:24 +02:00
|
|
|
if (strcmp(token, "local") == 0)
|
1998-01-26 02:42:53 +01:00
|
|
|
{
|
|
|
|
/* Get the database. */
|
2004-05-26 06:41:50 +02:00
|
|
|
line_item = lnext(line_item);
|
|
|
|
if (!line_item)
|
2001-07-30 16:50:24 +02:00
|
|
|
goto hba_syntax;
|
2004-05-26 06:41:50 +02:00
|
|
|
db = lfirst(line_item);
|
2001-07-30 16:50:24 +02:00
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
/* Get the role. */
|
2004-05-26 06:41:50 +02:00
|
|
|
line_item = lnext(line_item);
|
|
|
|
if (!line_item)
|
2002-04-04 06:25:54 +02:00
|
|
|
goto hba_syntax;
|
2005-06-28 07:09:14 +02:00
|
|
|
role = lfirst(line_item);
|
2002-04-04 06:25:54 +02:00
|
|
|
|
2004-05-26 06:41:50 +02:00
|
|
|
line_item = lnext(line_item);
|
|
|
|
if (!line_item)
|
2001-07-30 16:50:24 +02:00
|
|
|
goto hba_syntax;
|
2002-04-04 06:25:54 +02:00
|
|
|
|
|
|
|
/* Read the rest of the line. */
|
2004-05-26 06:41:50 +02:00
|
|
|
parse_hba_auth(&line_item, &port->auth_method,
|
|
|
|
&port->auth_arg, error_p);
|
2001-07-30 16:50:24 +02:00
|
|
|
if (*error_p)
|
|
|
|
goto hba_syntax;
|
1998-01-26 02:42:53 +01:00
|
|
|
|
2003-08-02 01:24:28 +02:00
|
|
|
/* Disallow auth methods that always need TCP/IP sockets to work */
|
2005-06-27 04:04:26 +02:00
|
|
|
if (port->auth_method == uaKrb5)
|
2001-07-30 16:50:24 +02:00
|
|
|
goto hba_syntax;
|
1998-01-26 02:42:53 +01:00
|
|
|
|
2003-08-02 01:24:28 +02:00
|
|
|
/* Does not match if connection isn't AF_UNIX */
|
|
|
|
if (!IS_AF_UNIX(port->raddr.addr.ss_family))
|
1998-01-26 02:42:53 +01:00
|
|
|
return;
|
|
|
|
}
|
At long last I put together a patch to support 4 client SSL negotiation
modes (and replace the requiressl boolean). The four options were first
spelled out by Magnus Hagander <mha@sollentuna.net> on 2000-08-23 in email
to pgsql-hackers, archived here:
http://archives.postgresql.org/pgsql-hackers/2000-08/msg00639.php
My original less-flexible patch and the ensuing thread are archived at:
http://dbforums.com/t623845.html
Attached is a new patch, including documentation.
To sum up, there's a new client parameter "sslmode" and environment
variable "PGSSLMODE", with these options:
sslmode description
------- -----------
disable Unencrypted non-SSL only
allow Negotiate, prefer non-SSL
prefer Negotiate, prefer SSL (default)
require Require SSL
The only change to the server is a new pg_hba.conf line type,
"hostnossl", for specifying connections that are not allowed to use SSL
(for example, to prevent servers on a local network from accidentally
using SSL and wasting cycles). Thus the 3 pg_hba.conf line types are:
pg_hba.conf line types
----------------------
host applies to either SSL or regular connections
hostssl applies only to SSL connections
hostnossl applies only to regular connections
These client and server options, the postgresql.conf ssl = false option,
and finally the possibility of compiling with no SSL support at all,
make quite a range of combinations to test. I threw together a test
script to try many of them out. It's in a separate tarball with its
config files, a patch to psql so it'll announce SSL connections even in
absence of a tty, and the test output. The test is especially informative
when run on the same tty the postmaster was started on, so the FATAL:
errors during negotiation are interleaved with the psql client output.
I saw Tom write that new submissions for 7.4 have to be in before midnight
local time, and since I'm on the east coast in the US, this just makes it
in before the bell. :)
Jon Jensen
2003-07-26 15:50:02 +02:00
|
|
|
else if (strcmp(token, "host") == 0
|
2003-08-04 02:43:34 +02:00
|
|
|
|| strcmp(token, "hostssl") == 0
|
|
|
|
|| strcmp(token, "hostnossl") == 0)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2002-12-06 05:37:05 +01:00
|
|
|
|
2003-08-04 02:43:34 +02:00
|
|
|
if (token[4] == 's') /* "hostssl" */
|
2000-04-12 19:17:23 +02:00
|
|
|
{
|
2001-08-02 16:27:40 +02:00
|
|
|
#ifdef USE_SSL
|
|
|
|
/* Record does not match if we are not on an SSL connection */
|
2000-04-12 19:17:23 +02:00
|
|
|
if (!port->ssl)
|
2001-07-30 16:50:24 +02:00
|
|
|
return;
|
2000-04-12 19:17:23 +02:00
|
|
|
|
|
|
|
/* Placeholder to require specific SSL level, perhaps? */
|
|
|
|
/* Or a client certificate */
|
|
|
|
|
|
|
|
/* Since we were on SSL, proceed as with normal 'host' mode */
|
1999-09-27 05:13:16 +02:00
|
|
|
#else
|
2001-08-02 16:27:40 +02:00
|
|
|
/* We don't accept this keyword at all if no SSL support */
|
2001-07-30 16:50:24 +02:00
|
|
|
goto hba_syntax;
|
1999-09-27 05:13:16 +02:00
|
|
|
#endif
|
2001-08-02 16:27:40 +02:00
|
|
|
}
|
At long last I put together a patch to support 4 client SSL negotiation
modes (and replace the requiressl boolean). The four options were first
spelled out by Magnus Hagander <mha@sollentuna.net> on 2000-08-23 in email
to pgsql-hackers, archived here:
http://archives.postgresql.org/pgsql-hackers/2000-08/msg00639.php
My original less-flexible patch and the ensuing thread are archived at:
http://dbforums.com/t623845.html
Attached is a new patch, including documentation.
To sum up, there's a new client parameter "sslmode" and environment
variable "PGSSLMODE", with these options:
sslmode description
------- -----------
disable Unencrypted non-SSL only
allow Negotiate, prefer non-SSL
prefer Negotiate, prefer SSL (default)
require Require SSL
The only change to the server is a new pg_hba.conf line type,
"hostnossl", for specifying connections that are not allowed to use SSL
(for example, to prevent servers on a local network from accidentally
using SSL and wasting cycles). Thus the 3 pg_hba.conf line types are:
pg_hba.conf line types
----------------------
host applies to either SSL or regular connections
hostssl applies only to SSL connections
hostnossl applies only to regular connections
These client and server options, the postgresql.conf ssl = false option,
and finally the possibility of compiling with no SSL support at all,
make quite a range of combinations to test. I threw together a test
script to try many of them out. It's in a separate tarball with its
config files, a patch to psql so it'll announce SSL connections even in
absence of a tty, and the test output. The test is especially informative
when run on the same tty the postmaster was started on, so the FATAL:
errors during negotiation are interleaved with the psql client output.
I saw Tom write that new submissions for 7.4 have to be in before midnight
local time, and since I'm on the east coast in the US, this just makes it
in before the bell. :)
Jon Jensen
2003-07-26 15:50:02 +02:00
|
|
|
#ifdef USE_SSL
|
2003-08-04 02:43:34 +02:00
|
|
|
else if (token[4] == 'n') /* "hostnossl" */
|
At long last I put together a patch to support 4 client SSL negotiation
modes (and replace the requiressl boolean). The four options were first
spelled out by Magnus Hagander <mha@sollentuna.net> on 2000-08-23 in email
to pgsql-hackers, archived here:
http://archives.postgresql.org/pgsql-hackers/2000-08/msg00639.php
My original less-flexible patch and the ensuing thread are archived at:
http://dbforums.com/t623845.html
Attached is a new patch, including documentation.
To sum up, there's a new client parameter "sslmode" and environment
variable "PGSSLMODE", with these options:
sslmode description
------- -----------
disable Unencrypted non-SSL only
allow Negotiate, prefer non-SSL
prefer Negotiate, prefer SSL (default)
require Require SSL
The only change to the server is a new pg_hba.conf line type,
"hostnossl", for specifying connections that are not allowed to use SSL
(for example, to prevent servers on a local network from accidentally
using SSL and wasting cycles). Thus the 3 pg_hba.conf line types are:
pg_hba.conf line types
----------------------
host applies to either SSL or regular connections
hostssl applies only to SSL connections
hostnossl applies only to regular connections
These client and server options, the postgresql.conf ssl = false option,
and finally the possibility of compiling with no SSL support at all,
make quite a range of combinations to test. I threw together a test
script to try many of them out. It's in a separate tarball with its
config files, a patch to psql so it'll announce SSL connections even in
absence of a tty, and the test output. The test is especially informative
when run on the same tty the postmaster was started on, so the FATAL:
errors during negotiation are interleaved with the psql client output.
I saw Tom write that new submissions for 7.4 have to be in before midnight
local time, and since I'm on the east coast in the US, this just makes it
in before the bell. :)
Jon Jensen
2003-07-26 15:50:02 +02:00
|
|
|
{
|
|
|
|
/* Record does not match if we are on an SSL connection */
|
|
|
|
if (port->ssl)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif
|
1998-01-26 02:42:53 +01:00
|
|
|
|
|
|
|
/* Get the database. */
|
2004-05-26 06:41:50 +02:00
|
|
|
line_item = lnext(line_item);
|
|
|
|
if (!line_item)
|
2001-07-30 16:50:24 +02:00
|
|
|
goto hba_syntax;
|
2004-05-26 06:41:50 +02:00
|
|
|
db = lfirst(line_item);
|
1998-01-26 02:42:53 +01:00
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
/* Get the role. */
|
2004-05-26 06:41:50 +02:00
|
|
|
line_item = lnext(line_item);
|
|
|
|
if (!line_item)
|
2002-04-04 06:25:54 +02:00
|
|
|
goto hba_syntax;
|
2005-06-28 07:09:14 +02:00
|
|
|
role = lfirst(line_item);
|
2002-04-04 06:25:54 +02:00
|
|
|
|
2003-06-12 09:36:51 +02:00
|
|
|
/* Read the IP address field. (with or without CIDR netmask) */
|
2004-05-26 06:41:50 +02:00
|
|
|
line_item = lnext(line_item);
|
|
|
|
if (!line_item)
|
2001-07-30 16:50:24 +02:00
|
|
|
goto hba_syntax;
|
2004-05-26 06:41:50 +02:00
|
|
|
token = lfirst(line_item);
|
2003-01-06 04:18:27 +01:00
|
|
|
|
2003-06-12 09:36:51 +02:00
|
|
|
/* Check if it has a CIDR suffix and if so isolate it */
|
2003-08-04 02:43:34 +02:00
|
|
|
cidr_slash = strchr(token, '/');
|
2003-06-12 09:36:51 +02:00
|
|
|
if (cidr_slash)
|
|
|
|
*cidr_slash = '\0';
|
2003-06-12 04:12:58 +02:00
|
|
|
|
2003-09-06 01:07:21 +02:00
|
|
|
/* Get the IP address either way */
|
2003-06-12 09:36:51 +02:00
|
|
|
hints.ai_flags = AI_NUMERICHOST;
|
|
|
|
hints.ai_family = PF_UNSPEC;
|
|
|
|
hints.ai_socktype = 0;
|
|
|
|
hints.ai_protocol = 0;
|
|
|
|
hints.ai_addrlen = 0;
|
|
|
|
hints.ai_canonname = NULL;
|
|
|
|
hints.ai_addr = NULL;
|
|
|
|
hints.ai_next = NULL;
|
|
|
|
|
2005-10-17 18:24:20 +02:00
|
|
|
ret = pg_getaddrinfo_all(token, NULL, &hints, &gai_result);
|
2003-09-06 01:07:21 +02:00
|
|
|
if (ret || !gai_result)
|
2003-06-12 09:36:51 +02:00
|
|
|
{
|
2003-07-22 21:00:12 +02:00
|
|
|
ereport(LOG,
|
|
|
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
2005-10-15 04:49:52 +02:00
|
|
|
errmsg("invalid IP address \"%s\" in file \"%s\" line %d: %s",
|
|
|
|
token, HbaFileName, line_num,
|
|
|
|
gai_strerror(ret))));
|
2003-06-12 09:36:51 +02:00
|
|
|
if (cidr_slash)
|
|
|
|
*cidr_slash = '/';
|
2003-09-06 01:07:21 +02:00
|
|
|
if (gai_result)
|
2005-10-17 18:24:20 +02:00
|
|
|
pg_freeaddrinfo_all(hints.ai_family, gai_result);
|
2004-05-20 00:06:16 +02:00
|
|
|
goto hba_other_error;
|
2003-06-12 09:36:51 +02:00
|
|
|
}
|
2003-01-06 04:18:27 +01:00
|
|
|
|
2003-07-24 01:30:41 +02:00
|
|
|
if (cidr_slash)
|
|
|
|
*cidr_slash = '/';
|
|
|
|
|
2003-09-06 01:07:21 +02:00
|
|
|
memcpy(&addr, gai_result->ai_addr, gai_result->ai_addrlen);
|
2005-10-17 18:24:20 +02:00
|
|
|
pg_freeaddrinfo_all(hints.ai_family, gai_result);
|
2003-09-06 01:07:21 +02:00
|
|
|
|
2003-06-12 09:36:51 +02:00
|
|
|
/* Get the netmask */
|
|
|
|
if (cidr_slash)
|
|
|
|
{
|
2005-10-17 18:24:20 +02:00
|
|
|
if (pg_sockaddr_cidr_mask(&mask, cidr_slash + 1,
|
|
|
|
addr.ss_family) < 0)
|
2003-07-22 21:00:12 +02:00
|
|
|
goto hba_syntax;
|
2003-06-12 09:36:51 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Read the mask field. */
|
2004-05-26 06:41:50 +02:00
|
|
|
line_item = lnext(line_item);
|
|
|
|
if (!line_item)
|
2003-06-12 09:36:51 +02:00
|
|
|
goto hba_syntax;
|
2004-05-26 06:41:50 +02:00
|
|
|
token = lfirst(line_item);
|
2003-06-12 09:36:51 +02:00
|
|
|
|
2005-10-17 18:24:20 +02:00
|
|
|
ret = pg_getaddrinfo_all(token, NULL, &hints, &gai_result);
|
2003-09-06 01:07:21 +02:00
|
|
|
if (ret || !gai_result)
|
|
|
|
{
|
2004-05-20 00:06:16 +02:00
|
|
|
ereport(LOG,
|
|
|
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
2005-10-15 04:49:52 +02:00
|
|
|
errmsg("invalid IP mask \"%s\" in file \"%s\" line %d: %s",
|
|
|
|
token, HbaFileName, line_num,
|
|
|
|
gai_strerror(ret))));
|
2003-09-06 01:07:21 +02:00
|
|
|
if (gai_result)
|
2005-10-17 18:24:20 +02:00
|
|
|
pg_freeaddrinfo_all(hints.ai_family, gai_result);
|
2004-05-20 00:06:16 +02:00
|
|
|
goto hba_other_error;
|
2003-09-06 01:07:21 +02:00
|
|
|
}
|
2003-07-22 21:00:12 +02:00
|
|
|
|
2003-09-06 01:07:21 +02:00
|
|
|
memcpy(&mask, gai_result->ai_addr, gai_result->ai_addrlen);
|
2005-10-17 18:24:20 +02:00
|
|
|
pg_freeaddrinfo_all(hints.ai_family, gai_result);
|
2003-06-12 09:36:51 +02:00
|
|
|
|
2003-09-06 01:07:21 +02:00
|
|
|
if (addr.ss_family != mask.ss_family)
|
2004-05-20 00:06:16 +02:00
|
|
|
{
|
|
|
|
ereport(LOG,
|
|
|
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
2004-10-12 23:54:45 +02:00
|
|
|
errmsg("IP address and mask do not match in file \"%s\" line %d",
|
2004-10-10 01:13:22 +02:00
|
|
|
HbaFileName, line_num)));
|
2004-05-20 00:06:16 +02:00
|
|
|
goto hba_other_error;
|
|
|
|
}
|
2003-06-12 09:36:51 +02:00
|
|
|
}
|
1998-01-26 02:42:53 +01:00
|
|
|
|
2003-09-06 01:07:21 +02:00
|
|
|
if (addr.ss_family != port->raddr.addr.ss_family)
|
2003-09-05 22:31:36 +02:00
|
|
|
{
|
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* Wrong address family. We allow only one case: if the file has
|
|
|
|
* IPv4 and the port is IPv6, promote the file address to IPv6 and
|
|
|
|
* try to match that way.
|
2003-09-05 22:31:36 +02:00
|
|
|
*/
|
|
|
|
#ifdef HAVE_IPV6
|
2003-09-06 01:07:21 +02:00
|
|
|
if (addr.ss_family == AF_INET &&
|
2003-09-05 22:31:36 +02:00
|
|
|
port->raddr.addr.ss_family == AF_INET6)
|
|
|
|
{
|
2005-10-17 18:24:20 +02:00
|
|
|
pg_promote_v4_to_v6_addr(&addr);
|
|
|
|
pg_promote_v4_to_v6_mask(&mask);
|
2003-09-05 22:31:36 +02:00
|
|
|
}
|
|
|
|
else
|
2004-08-29 07:07:03 +02:00
|
|
|
#endif /* HAVE_IPV6 */
|
2003-09-05 22:31:36 +02:00
|
|
|
{
|
2003-09-06 01:07:21 +02:00
|
|
|
/* Line doesn't match client port, so ignore it. */
|
2003-09-05 22:31:36 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-09-06 01:07:21 +02:00
|
|
|
/* Ignore line if client port is not in the matching addr range. */
|
2005-10-17 18:24:20 +02:00
|
|
|
if (!pg_range_sockaddr(&port->raddr.addr, &addr, &mask))
|
2003-09-06 01:07:21 +02:00
|
|
|
return;
|
|
|
|
|
2001-08-01 00:55:45 +02:00
|
|
|
/* Read the rest of the line. */
|
2004-05-26 06:41:50 +02:00
|
|
|
line_item = lnext(line_item);
|
|
|
|
if (!line_item)
|
2001-07-30 16:50:24 +02:00
|
|
|
goto hba_syntax;
|
2004-05-26 06:41:50 +02:00
|
|
|
parse_hba_auth(&line_item, &port->auth_method,
|
|
|
|
&port->auth_arg, error_p);
|
1998-01-26 02:42:53 +01:00
|
|
|
if (*error_p)
|
2001-07-30 16:50:24 +02:00
|
|
|
goto hba_syntax;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1998-01-26 02:42:53 +01:00
|
|
|
else
|
2001-07-30 16:50:24 +02:00
|
|
|
goto hba_syntax;
|
1998-01-26 02:42:53 +01:00
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
/* Does the entry match database and role? */
|
2003-04-18 00:26:02 +02:00
|
|
|
if (!check_db(port->database_name, port->user_name, db))
|
2002-04-04 06:25:54 +02:00
|
|
|
return;
|
2005-06-28 07:09:14 +02:00
|
|
|
if (!check_role(port->user_name, role))
|
2002-04-04 06:25:54 +02:00
|
|
|
return;
|
|
|
|
|
2001-07-30 16:50:24 +02:00
|
|
|
/* Success */
|
|
|
|
*found_p = true;
|
1998-01-26 02:42:53 +01:00
|
|
|
return;
|
|
|
|
|
2001-07-30 16:50:24 +02:00
|
|
|
hba_syntax:
|
2004-05-26 06:41:50 +02:00
|
|
|
if (line_item)
|
2003-07-22 21:00:12 +02:00
|
|
|
ereport(LOG,
|
|
|
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
2005-10-15 04:49:52 +02:00
|
|
|
errmsg("invalid entry in file \"%s\" at line %d, token \"%s\"",
|
|
|
|
HbaFileName, line_num,
|
|
|
|
(char *) lfirst(line_item))));
|
2003-07-22 21:00:12 +02:00
|
|
|
else
|
|
|
|
ereport(LOG,
|
|
|
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
2005-10-15 04:49:52 +02:00
|
|
|
errmsg("missing field in file \"%s\" at end of line %d",
|
|
|
|
HbaFileName, line_num)));
|
1998-01-26 02:42:53 +01:00
|
|
|
|
2004-05-20 00:06:16 +02:00
|
|
|
/* Come here if suitable message already logged */
|
|
|
|
hba_other_error:
|
1998-01-26 02:42:53 +01:00
|
|
|
*error_p = true;
|
1996-10-12 09:47:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2001-07-30 16:50:24 +02:00
|
|
|
/*
|
2001-10-25 07:50:21 +02:00
|
|
|
* Scan the (pre-parsed) hba file line by line, looking for a match
|
2001-08-01 00:55:45 +02:00
|
|
|
* to the port's connection request.
|
2001-07-30 16:50:24 +02:00
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
check_hba(hbaPort *port)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2001-10-25 07:50:21 +02:00
|
|
|
bool found_entry = false;
|
|
|
|
bool error = false;
|
2004-05-26 06:41:50 +02:00
|
|
|
ListCell *line;
|
|
|
|
ListCell *line_num;
|
1999-05-25 18:15:34 +02:00
|
|
|
|
2004-05-26 06:41:50 +02:00
|
|
|
forboth(line, hba_lines, line_num, hba_line_nums)
|
2001-07-30 16:50:24 +02:00
|
|
|
{
|
2004-05-26 06:41:50 +02:00
|
|
|
parse_hba(lfirst(line), lfirst_int(line_num),
|
|
|
|
port, &found_entry, &error);
|
2001-07-30 16:50:24 +02:00
|
|
|
if (found_entry || error)
|
|
|
|
break;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1998-01-26 02:42:53 +01:00
|
|
|
|
1998-01-27 04:25:14 +01:00
|
|
|
if (!error)
|
|
|
|
{
|
1999-04-16 06:59:03 +02:00
|
|
|
/* If no matching entry was found, synthesize 'reject' entry. */
|
1998-01-27 04:25:14 +01:00
|
|
|
if (!found_entry)
|
2000-04-12 19:17:23 +02:00
|
|
|
port->auth_method = uaReject;
|
2001-07-30 16:50:24 +02:00
|
|
|
return true;
|
1998-01-27 04:25:14 +01:00
|
|
|
}
|
2001-07-30 16:50:24 +02:00
|
|
|
else
|
|
|
|
return false;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1996-10-12 09:47:12 +02:00
|
|
|
|
|
|
|
|
2002-04-04 06:25:54 +02:00
|
|
|
/*
|
2005-06-28 07:09:14 +02:00
|
|
|
* Load role/password mapping file
|
2002-04-04 06:25:54 +02:00
|
|
|
*/
|
|
|
|
void
|
2005-06-28 07:09:14 +02:00
|
|
|
load_role(void)
|
2002-04-04 06:25:54 +02:00
|
|
|
{
|
2004-12-27 20:19:24 +01:00
|
|
|
char *filename;
|
2005-06-28 07:09:14 +02:00
|
|
|
FILE *role_file;
|
2002-04-04 06:25:54 +02:00
|
|
|
|
2003-04-03 23:25:02 +02:00
|
|
|
/* Discard any old data */
|
2005-06-28 07:09:14 +02:00
|
|
|
if (role_lines || role_line_nums)
|
|
|
|
free_lines(&role_lines, &role_line_nums);
|
2005-10-15 04:49:52 +02:00
|
|
|
if (role_sorted)
|
2005-06-28 07:09:14 +02:00
|
|
|
pfree(role_sorted);
|
|
|
|
role_sorted = NULL;
|
|
|
|
role_length = 0;
|
2002-04-04 06:25:54 +02:00
|
|
|
|
2004-12-27 20:19:24 +01:00
|
|
|
/* Read in the file contents */
|
2005-06-28 07:09:14 +02:00
|
|
|
filename = auth_getflatfilename();
|
|
|
|
role_file = AllocateFile(filename, "r");
|
2004-12-27 20:19:24 +01:00
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
if (role_file == NULL)
|
2004-12-27 20:19:24 +01:00
|
|
|
{
|
|
|
|
/* no complaint if not there */
|
|
|
|
if (errno != ENOENT)
|
|
|
|
ereport(LOG,
|
|
|
|
(errcode_for_file_access(),
|
|
|
|
errmsg("could not open file \"%s\": %m", filename)));
|
|
|
|
pfree(filename);
|
2002-04-04 06:25:54 +02:00
|
|
|
return;
|
2004-12-27 20:19:24 +01:00
|
|
|
}
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
tokenize_file(filename, role_file, &role_lines, &role_line_nums);
|
2004-12-27 20:19:24 +01:00
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
FreeFile(role_file);
|
2004-12-27 20:19:24 +01:00
|
|
|
pfree(filename);
|
2002-04-04 06:25:54 +02:00
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
/* create array for binary searching */
|
|
|
|
role_length = list_length(role_lines);
|
|
|
|
if (role_length)
|
2002-04-04 06:25:54 +02:00
|
|
|
{
|
2005-10-15 04:49:52 +02:00
|
|
|
int i = 0;
|
|
|
|
ListCell *line;
|
2002-04-04 06:25:54 +02:00
|
|
|
|
2005-07-28 17:30:55 +02:00
|
|
|
/* We assume the flat file was written already-sorted */
|
2005-06-28 07:09:14 +02:00
|
|
|
role_sorted = palloc(role_length * sizeof(List *));
|
|
|
|
foreach(line, role_lines)
|
|
|
|
role_sorted[i++] = lfirst(line);
|
2002-04-04 06:25:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1998-12-14 07:50:32 +01:00
|
|
|
/*
|
2001-07-30 16:50:24 +02:00
|
|
|
* Read the config file and create a List of Lists of tokens in the file.
|
1998-12-14 07:50:32 +01:00
|
|
|
*/
|
2002-04-04 06:25:54 +02:00
|
|
|
void
|
2001-08-01 00:55:45 +02:00
|
|
|
load_hba(void)
|
2001-07-30 16:50:24 +02:00
|
|
|
{
|
2004-10-10 01:13:22 +02:00
|
|
|
FILE *file;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2004-05-26 06:41:50 +02:00
|
|
|
if (hba_lines || hba_line_nums)
|
|
|
|
free_lines(&hba_lines, &hba_line_nums);
|
2001-10-25 07:50:21 +02:00
|
|
|
|
2004-10-10 01:13:22 +02:00
|
|
|
file = AllocateFile(HbaFileName, "r");
|
|
|
|
/* Failure is fatal since with no HBA entries we can do nothing... */
|
2002-04-04 06:25:54 +02:00
|
|
|
if (file == NULL)
|
2003-07-22 21:00:12 +02:00
|
|
|
ereport(FATAL,
|
|
|
|
(errcode_for_file_access(),
|
2003-09-25 08:58:07 +02:00
|
|
|
errmsg("could not open configuration file \"%s\": %m",
|
2004-10-10 01:13:22 +02:00
|
|
|
HbaFileName)));
|
2002-12-14 19:49:37 +01:00
|
|
|
|
2004-12-27 20:19:24 +01:00
|
|
|
tokenize_file(HbaFileName, file, &hba_lines, &hba_line_nums);
|
2002-12-14 19:49:37 +01:00
|
|
|
FreeFile(file);
|
1996-10-12 09:47:12 +02:00
|
|
|
}
|
|
|
|
|
2005-02-26 19:43:34 +01:00
|
|
|
/*
|
|
|
|
* Read and parse one line from the flat pg_database file.
|
|
|
|
*
|
|
|
|
* Returns TRUE on success, FALSE if EOF; bad data causes elog(FATAL).
|
|
|
|
*
|
|
|
|
* Output parameters:
|
|
|
|
* dbname: gets database name (must be of size NAMEDATALEN bytes)
|
|
|
|
* dboid: gets database OID
|
|
|
|
* dbtablespace: gets database's default tablespace's OID
|
Fix recently-understood problems with handling of XID freezing, particularly
in PITR scenarios. We now WAL-log the replacement of old XIDs with
FrozenTransactionId, so that such replacement is guaranteed to propagate to
PITR slave databases. Also, rather than relying on hint-bit updates to be
preserved, pg_clog is not truncated until all instances of an XID are known to
have been replaced by FrozenTransactionId. Add new GUC variables and
pg_autovacuum columns to allow management of the freezing policy, so that
users can trade off the size of pg_clog against the amount of freezing work
done. Revise the already-existing code that forces autovacuum of tables
approaching the wraparound point to make it more bulletproof; also, revise the
autovacuum logic so that anti-wraparound vacuuming is done per-table rather
than per-database. initdb forced because of changes in pg_class, pg_database,
and pg_autovacuum catalogs. Heikki Linnakangas, Simon Riggs, and Tom Lane.
2006-11-05 23:42:10 +01:00
|
|
|
* dbfrozenxid: gets database's frozen XID
|
2005-02-26 19:43:34 +01:00
|
|
|
*
|
|
|
|
* This is not much related to the other functions in hba.c, but we put it
|
|
|
|
* here because it uses the next_token() infrastructure.
|
|
|
|
*/
|
|
|
|
bool
|
2005-07-29 21:30:09 +02:00
|
|
|
read_pg_database_line(FILE *fp, char *dbname, Oid *dboid,
|
Fix recently-understood problems with handling of XID freezing, particularly
in PITR scenarios. We now WAL-log the replacement of old XIDs with
FrozenTransactionId, so that such replacement is guaranteed to propagate to
PITR slave databases. Also, rather than relying on hint-bit updates to be
preserved, pg_clog is not truncated until all instances of an XID are known to
have been replaced by FrozenTransactionId. Add new GUC variables and
pg_autovacuum columns to allow management of the freezing policy, so that
users can trade off the size of pg_clog against the amount of freezing work
done. Revise the already-existing code that forces autovacuum of tables
approaching the wraparound point to make it more bulletproof; also, revise the
autovacuum logic so that anti-wraparound vacuuming is done per-table rather
than per-database. initdb forced because of changes in pg_class, pg_database,
and pg_autovacuum catalogs. Heikki Linnakangas, Simon Riggs, and Tom Lane.
2006-11-05 23:42:10 +01:00
|
|
|
Oid *dbtablespace, TransactionId *dbfrozenxid)
|
2005-02-26 19:43:34 +01:00
|
|
|
{
|
|
|
|
char buf[MAX_TOKEN];
|
|
|
|
|
|
|
|
if (feof(fp))
|
|
|
|
return false;
|
2005-06-29 00:16:45 +02:00
|
|
|
if (!next_token(fp, buf, sizeof(buf)))
|
2005-02-26 19:43:34 +01:00
|
|
|
return false;
|
|
|
|
if (strlen(buf) >= NAMEDATALEN)
|
|
|
|
elog(FATAL, "bad data in flat pg_database file");
|
|
|
|
strcpy(dbname, buf);
|
|
|
|
next_token(fp, buf, sizeof(buf));
|
|
|
|
if (!isdigit((unsigned char) buf[0]))
|
|
|
|
elog(FATAL, "bad data in flat pg_database file");
|
|
|
|
*dboid = atooid(buf);
|
|
|
|
next_token(fp, buf, sizeof(buf));
|
|
|
|
if (!isdigit((unsigned char) buf[0]))
|
|
|
|
elog(FATAL, "bad data in flat pg_database file");
|
|
|
|
*dbtablespace = atooid(buf);
|
|
|
|
next_token(fp, buf, sizeof(buf));
|
|
|
|
if (!isdigit((unsigned char) buf[0]))
|
|
|
|
elog(FATAL, "bad data in flat pg_database file");
|
Fix recently-understood problems with handling of XID freezing, particularly
in PITR scenarios. We now WAL-log the replacement of old XIDs with
FrozenTransactionId, so that such replacement is guaranteed to propagate to
PITR slave databases. Also, rather than relying on hint-bit updates to be
preserved, pg_clog is not truncated until all instances of an XID are known to
have been replaced by FrozenTransactionId. Add new GUC variables and
pg_autovacuum columns to allow management of the freezing policy, so that
users can trade off the size of pg_clog against the amount of freezing work
done. Revise the already-existing code that forces autovacuum of tables
approaching the wraparound point to make it more bulletproof; also, revise the
autovacuum logic so that anti-wraparound vacuuming is done per-table rather
than per-database. initdb forced because of changes in pg_class, pg_database,
and pg_autovacuum catalogs. Heikki Linnakangas, Simon Riggs, and Tom Lane.
2006-11-05 23:42:10 +01:00
|
|
|
*dbfrozenxid = atoxid(buf);
|
2005-02-26 19:43:34 +01:00
|
|
|
/* expect EOL next */
|
2005-06-29 00:16:45 +02:00
|
|
|
if (next_token(fp, buf, sizeof(buf)))
|
2005-02-26 19:43:34 +01:00
|
|
|
elog(FATAL, "bad data in flat pg_database file");
|
|
|
|
return true;
|
|
|
|
}
|
1996-10-12 09:47:12 +02:00
|
|
|
|
2001-07-30 16:50:24 +02:00
|
|
|
/*
|
2001-08-01 00:55:45 +02:00
|
|
|
* Process one line from the ident config file.
|
|
|
|
*
|
2005-06-28 07:09:14 +02:00
|
|
|
* Take the line and compare it to the needed map, pg_role and ident_user.
|
2001-08-01 00:55:45 +02:00
|
|
|
* *found_p and *error_p are set according to our results.
|
2001-07-30 16:50:24 +02:00
|
|
|
*/
|
1996-10-12 09:47:12 +02:00
|
|
|
static void
|
2004-05-26 06:41:50 +02:00
|
|
|
parse_ident_usermap(List *line, int line_number, const char *usermap_name,
|
2005-06-28 07:09:14 +02:00
|
|
|
const char *pg_role, const char *ident_user,
|
2004-05-26 06:41:50 +02:00
|
|
|
bool *found_p, bool *error_p)
|
2001-07-30 16:50:24 +02:00
|
|
|
{
|
2004-05-26 06:41:50 +02:00
|
|
|
ListCell *line_item;
|
2001-10-25 07:50:21 +02:00
|
|
|
char *token;
|
|
|
|
char *file_map;
|
2005-06-28 07:09:14 +02:00
|
|
|
char *file_pgrole;
|
2001-10-25 07:50:21 +02:00
|
|
|
char *file_ident_user;
|
2001-07-30 16:50:24 +02:00
|
|
|
|
|
|
|
*found_p = false;
|
2001-08-01 00:55:45 +02:00
|
|
|
*error_p = false;
|
2001-07-30 16:50:24 +02:00
|
|
|
|
|
|
|
Assert(line != NIL);
|
2004-05-26 06:41:50 +02:00
|
|
|
line_item = list_head(line);
|
2001-08-01 00:55:45 +02:00
|
|
|
|
|
|
|
/* Get the map token (must exist) */
|
2004-05-26 06:41:50 +02:00
|
|
|
token = lfirst(line_item);
|
2001-07-30 16:50:24 +02:00
|
|
|
file_map = token;
|
|
|
|
|
2005-06-21 03:20:09 +02:00
|
|
|
/* Get the ident user token */
|
2004-05-26 06:41:50 +02:00
|
|
|
line_item = lnext(line_item);
|
2005-06-21 03:20:09 +02:00
|
|
|
if (!line_item)
|
2001-07-30 16:50:24 +02:00
|
|
|
goto ident_syntax;
|
2004-05-26 06:41:50 +02:00
|
|
|
token = lfirst(line_item);
|
2001-08-01 00:55:45 +02:00
|
|
|
file_ident_user = token;
|
2001-07-30 16:50:24 +02:00
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
/* Get the PG rolename token */
|
2004-05-26 06:41:50 +02:00
|
|
|
line_item = lnext(line_item);
|
2005-06-21 03:20:09 +02:00
|
|
|
if (!line_item)
|
2001-08-01 00:55:45 +02:00
|
|
|
goto ident_syntax;
|
2004-05-26 06:41:50 +02:00
|
|
|
token = lfirst(line_item);
|
2005-06-28 07:09:14 +02:00
|
|
|
file_pgrole = token;
|
2001-08-01 00:55:45 +02:00
|
|
|
|
|
|
|
/* Match? */
|
|
|
|
if (strcmp(file_map, usermap_name) == 0 &&
|
2005-06-28 07:09:14 +02:00
|
|
|
strcmp(file_pgrole, pg_role) == 0 &&
|
2001-08-01 00:55:45 +02:00
|
|
|
strcmp(file_ident_user, ident_user) == 0)
|
|
|
|
*found_p = true;
|
2005-06-21 03:20:09 +02:00
|
|
|
|
2001-07-30 16:50:24 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
ident_syntax:
|
2005-06-21 03:20:09 +02:00
|
|
|
ereport(LOG,
|
|
|
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
|
|
|
errmsg("missing entry in file \"%s\" at end of line %d",
|
|
|
|
IdentFileName, line_number)));
|
2001-07-30 16:50:24 +02:00
|
|
|
*error_p = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2001-10-25 07:50:21 +02:00
|
|
|
* Scan the (pre-parsed) ident usermap file line by line, looking for a match
|
2001-08-01 00:55:45 +02:00
|
|
|
*
|
2001-10-25 07:50:21 +02:00
|
|
|
* See if the user with ident username "ident_user" is allowed to act
|
2005-06-28 07:09:14 +02:00
|
|
|
* as Postgres user "pgrole" according to usermap "usermap_name".
|
2001-08-01 00:55:45 +02:00
|
|
|
*
|
2005-06-28 07:09:14 +02:00
|
|
|
* Special case: For usermap "samerole", don't look in the usermap
|
|
|
|
* file. That's an implied map where "pgrole" must be identical to
|
2001-10-25 07:50:21 +02:00
|
|
|
* "ident_user" in order to be authorized.
|
2001-08-01 00:55:45 +02:00
|
|
|
*
|
2001-10-25 07:50:21 +02:00
|
|
|
* Iff authorized, return true.
|
2001-07-30 16:50:24 +02:00
|
|
|
*/
|
2008-08-01 11:09:49 +02:00
|
|
|
bool
|
2001-07-30 16:50:24 +02:00
|
|
|
check_ident_usermap(const char *usermap_name,
|
2005-06-28 07:09:14 +02:00
|
|
|
const char *pg_role,
|
2001-07-30 16:50:24 +02:00
|
|
|
const char *ident_user)
|
|
|
|
{
|
2001-10-25 07:50:21 +02:00
|
|
|
bool found_entry = false,
|
|
|
|
error = false;
|
2001-07-30 16:50:24 +02:00
|
|
|
|
2003-04-18 00:26:02 +02:00
|
|
|
if (usermap_name == NULL || usermap_name[0] == '\0')
|
2001-07-30 16:50:24 +02:00
|
|
|
{
|
2003-07-22 21:00:12 +02:00
|
|
|
ereport(LOG,
|
|
|
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
2005-10-15 04:49:52 +02:00
|
|
|
errmsg("cannot use Ident authentication without usermap field")));
|
2001-07-30 16:50:24 +02:00
|
|
|
found_entry = false;
|
|
|
|
}
|
2005-06-28 07:09:14 +02:00
|
|
|
else if (strcmp(usermap_name, "sameuser\n") == 0 ||
|
|
|
|
strcmp(usermap_name, "samerole\n") == 0)
|
2001-07-30 16:50:24 +02:00
|
|
|
{
|
2005-06-28 07:09:14 +02:00
|
|
|
if (strcmp(pg_role, ident_user) == 0)
|
2001-07-30 16:50:24 +02:00
|
|
|
found_entry = true;
|
|
|
|
else
|
|
|
|
found_entry = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2004-08-29 07:07:03 +02:00
|
|
|
ListCell *line_cell,
|
|
|
|
*num_cell;
|
2004-05-26 06:41:50 +02:00
|
|
|
|
|
|
|
forboth(line_cell, ident_lines, num_cell, ident_line_nums)
|
2001-07-30 16:50:24 +02:00
|
|
|
{
|
2004-05-26 06:41:50 +02:00
|
|
|
parse_ident_usermap(lfirst(line_cell), lfirst_int(num_cell),
|
2005-06-28 07:09:14 +02:00
|
|
|
usermap_name, pg_role, ident_user,
|
2004-05-26 06:41:50 +02:00
|
|
|
&found_entry, &error);
|
2001-07-30 16:50:24 +02:00
|
|
|
if (found_entry || error)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return found_entry;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2001-08-01 00:55:45 +02:00
|
|
|
* Read the ident config file and create a List of Lists of tokens in the file.
|
2001-07-30 16:50:24 +02:00
|
|
|
*/
|
2002-04-04 06:25:54 +02:00
|
|
|
void
|
2001-08-01 00:55:45 +02:00
|
|
|
load_ident(void)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2004-10-10 01:13:22 +02:00
|
|
|
FILE *file;
|
2004-08-29 07:07:03 +02:00
|
|
|
|
2004-05-26 06:41:50 +02:00
|
|
|
if (ident_lines || ident_line_nums)
|
|
|
|
free_lines(&ident_lines, &ident_line_nums);
|
2001-07-30 16:50:24 +02:00
|
|
|
|
2004-10-10 01:13:22 +02:00
|
|
|
file = AllocateFile(IdentFileName, "r");
|
2001-07-30 16:50:24 +02:00
|
|
|
if (file == NULL)
|
|
|
|
{
|
2004-10-10 01:13:22 +02:00
|
|
|
/* not fatal ... we just won't do any special ident maps */
|
2003-07-22 21:00:12 +02:00
|
|
|
ereport(LOG,
|
|
|
|
(errcode_for_file_access(),
|
2003-09-25 08:58:07 +02:00
|
|
|
errmsg("could not open Ident usermap file \"%s\": %m",
|
2004-10-10 01:13:22 +02:00
|
|
|
IdentFileName)));
|
2001-07-30 16:50:24 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2004-12-27 20:19:24 +01:00
|
|
|
tokenize_file(IdentFileName, file, &ident_lines, &ident_line_nums);
|
2001-07-30 16:50:24 +02:00
|
|
|
FreeFile(file);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1996-10-12 09:47:12 +02:00
|
|
|
|
2001-07-30 16:50:24 +02:00
|
|
|
/*
|
2001-10-25 07:50:21 +02:00
|
|
|
* Determine what authentication method should be used when accessing database
|
|
|
|
* "database" from frontend "raddr", user "user". Return the method and
|
|
|
|
* an optional argument (stored in fields of *port), and STATUS_OK.
|
2001-08-01 00:55:45 +02:00
|
|
|
*
|
2001-10-25 07:50:21 +02:00
|
|
|
* 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.
|
2001-07-30 16:50:24 +02:00
|
|
|
*/
|
1998-01-26 02:42:53 +01:00
|
|
|
int
|
2001-07-30 16:50:24 +02:00
|
|
|
hba_getauthmethod(hbaPort *port)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2001-07-30 16:50:24 +02:00
|
|
|
if (check_hba(port))
|
|
|
|
return STATUS_OK;
|
|
|
|
else
|
1998-01-26 02:42:53 +01:00
|
|
|
return STATUS_ERROR;
|
2001-07-30 16:50:24 +02:00
|
|
|
}
|