1996-10-12 07:47:12 +00:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
1999-02-13 23:22:53 +00:00
|
|
|
* hba.c
|
1997-09-07 05:04:48 +00: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 09:09:49 +00:00
|
|
|
* says he comes from and choosing authentication method based on it).
|
1996-10-12 07:47:12 +00:00
|
|
|
*
|
2012-01-01 18:01:58 -05:00
|
|
|
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
|
2001-08-01 23:52:50 +00:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2010-09-20 22:08:53 +02:00
|
|
|
* src/backend/libpq/hba.c
|
1996-10-12 07:47:12 +00:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
2001-07-31 22:55:45 +00:00
|
|
|
#include "postgres.h"
|
|
|
|
|
2005-02-26 18:43:34 +00:00
|
|
|
#include <ctype.h>
|
1996-10-12 07:47:12 +00:00
|
|
|
#include <pwd.h>
|
1997-08-27 03:48:50 +00:00
|
|
|
#include <fcntl.h>
|
2001-08-21 15:21:25 +00:00
|
|
|
#include <sys/param.h>
|
2001-09-07 19:52:54 +00:00
|
|
|
#include <sys/socket.h>
|
1996-10-12 07:47:12 +00:00
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <arpa/inet.h>
|
1996-11-03 07:00:57 +00:00
|
|
|
#include <unistd.h>
|
1996-10-12 07:47:12 +00:00
|
|
|
|
2011-04-10 18:02:17 -04:00
|
|
|
#include "catalog/pg_collation.h"
|
2006-07-13 16:49:20 +00:00
|
|
|
#include "libpq/ip.h"
|
1999-07-15 23:04:24 +00:00
|
|
|
#include "libpq/libpq.h"
|
2011-04-26 15:40:11 -04:00
|
|
|
#include "postmaster/postmaster.h"
|
2008-11-28 14:26:58 +00:00
|
|
|
#include "regex/regex.h"
|
2010-01-15 09:19:10 +00:00
|
|
|
#include "replication/walsender.h"
|
2000-07-08 03:04:41 +00:00
|
|
|
#include "storage/fd.h"
|
2009-08-29 19:26:52 +00:00
|
|
|
#include "utils/acl.h"
|
2004-07-11 00:18:45 +00:00
|
|
|
#include "utils/guc.h"
|
2009-08-29 19:26:52 +00:00
|
|
|
#include "utils/lsyscache.h"
|
2011-06-20 17:20:14 -04:00
|
|
|
#include "utils/memutils.h"
|
1999-10-23 03:13:33 +00:00
|
|
|
|
2006-07-13 16:49:20 +00:00
|
|
|
|
2005-02-26 18:43:34 +00:00
|
|
|
#define atooid(x) ((Oid) strtoul((x), NULL, 10))
|
2005-07-29 19:30:09 +00:00
|
|
|
#define atoxid(x) ((TransactionId) strtoul((x), NULL, 10))
|
2005-02-26 18:43:34 +00:00
|
|
|
|
2004-02-02 16:58:30 +00:00
|
|
|
#define MAX_TOKEN 256
|
|
|
|
|
2009-10-01 01:58:58 +00:00
|
|
|
/* callback data for check_network_callback */
|
|
|
|
typedef struct check_network_data
|
|
|
|
{
|
|
|
|
IPCompareMethod method; /* test method */
|
|
|
|
SockAddr *raddr; /* client's actual address */
|
|
|
|
bool result; /* set to true if match */
|
|
|
|
} check_network_data;
|
|
|
|
|
2011-06-20 17:20:14 -04:00
|
|
|
|
|
|
|
#define token_is_keyword(t, k) (!t->quoted && strcmp(t->string, k) == 0)
|
|
|
|
#define token_matches(t, k) (strcmp(t->string, k) == 0)
|
|
|
|
|
|
|
|
/*
|
|
|
|
* A single string token lexed from the HBA config file, together with whether
|
|
|
|
* the token had been quoted.
|
|
|
|
*/
|
|
|
|
typedef struct HbaToken
|
|
|
|
{
|
|
|
|
char *string;
|
|
|
|
bool quoted;
|
|
|
|
} HbaToken;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* pre-parsed content of HBA config file: list of HbaLine structs.
|
|
|
|
* parsed_hba_context is the memory context where it lives.
|
|
|
|
*/
|
2008-09-15 12:32:57 +00:00
|
|
|
static List *parsed_hba_lines = NIL;
|
2011-06-20 17:20:14 -04:00
|
|
|
static MemoryContext parsed_hba_context = NULL;
|
2008-09-15 12:32:57 +00:00
|
|
|
|
2001-07-31 22:55:45 +00:00
|
|
|
/*
|
2009-08-29 19:26:52 +00:00
|
|
|
* These variables hold the pre-parsed contents of the ident usermap
|
2011-06-20 17:20:14 -04:00
|
|
|
* configuration file. ident_lines is a triple-nested list of lines, fields
|
|
|
|
* and tokens, as returned by tokenize_file. There will be one line in
|
|
|
|
* ident_lines for each (non-empty, non-comment) line of the file. Note there
|
|
|
|
* will always be at least one field, since blank lines are not entered in the
|
|
|
|
* data structure. ident_line_nums is an integer list containing the actual
|
|
|
|
* line number for each line represented in ident_lines. ident_context is
|
|
|
|
* the memory context holding all this.
|
2001-07-31 22:55:45 +00:00
|
|
|
*/
|
2004-08-29 05:07:03 +00:00
|
|
|
static List *ident_lines = NIL;
|
|
|
|
static List *ident_line_nums = NIL;
|
2011-06-20 17:20:14 -04:00
|
|
|
static MemoryContext ident_context = NULL;
|
2004-08-29 05:07:03 +00:00
|
|
|
|
2001-07-30 14:50:24 +00:00
|
|
|
|
2011-06-20 17:20:14 -04:00
|
|
|
static MemoryContext tokenize_file(const char *filename, FILE *file,
|
2005-10-15 02:49:52 +00:00
|
|
|
List **lines, List **line_nums);
|
2011-06-20 17:20:14 -04:00
|
|
|
static List *tokenize_inc_file(List *tokens, const char *outer_filename,
|
2005-10-15 02:49:52 +00:00
|
|
|
const char *inc_filename);
|
2011-06-20 17:20:14 -04:00
|
|
|
static bool parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline,
|
|
|
|
int line_num);
|
1999-10-23 03:13:33 +00:00
|
|
|
|
2001-07-31 22:55:45 +00:00
|
|
|
/*
|
2003-04-13 04:07:17 +00:00
|
|
|
* isblank() exists in the ISO C99 spec, but it's not very portable yet,
|
|
|
|
* so provide our own version.
|
2001-07-31 22:55:45 +00:00
|
|
|
*/
|
2008-08-01 09:09:49 +00:00
|
|
|
bool
|
2003-04-13 04:07:17 +00:00
|
|
|
pg_isblank(const char c)
|
1997-09-07 05:04:48 +00:00
|
|
|
{
|
2002-06-26 14:52:08 +00:00
|
|
|
return c == ' ' || c == '\t' || c == '\r';
|
1996-10-28 09:03:50 +00:00
|
|
|
}
|
|
|
|
|
1996-10-12 07:47:12 +00:00
|
|
|
|
2001-07-30 14:50:24 +00:00
|
|
|
/*
|
2004-02-02 16:58:30 +00:00
|
|
|
* Grab one token out of fp. Tokens are strings of non-blank
|
2005-06-28 22:16:45 +00: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 17:43:45 +00:00
|
|
|
* double quotes (this allows the inclusion of blanks, but not newlines).
|
2005-06-28 22:16:45 +00:00
|
|
|
*
|
|
|
|
* The token, if any, is returned at *buf (a buffer of size bufsz).
|
2010-03-06 00:45:49 +00:00
|
|
|
* Also, we set *initial_quote to indicate whether there was quoting before
|
|
|
|
* the first character. (We use that to prevent "@x" from being treated
|
|
|
|
* as a file inclusion request. Note that @"x" should be so treated;
|
|
|
|
* we want to allow that to support embedded spaces in file paths.)
|
2011-06-20 17:20:14 -04:00
|
|
|
* We set *terminating_comma to indicate whether the token is terminated by a
|
|
|
|
* comma (which is not returned.)
|
2005-06-28 22:16:45 +00:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
2011-06-20 17:20:14 -04:00
|
|
|
* Handle comments.
|
2001-07-30 14:50:24 +00:00
|
|
|
*/
|
2005-06-28 22:16:45 +00:00
|
|
|
static bool
|
2011-06-20 17:20:14 -04:00
|
|
|
next_token(FILE *fp, char *buf, int bufsz, bool *initial_quote,
|
|
|
|
bool *terminating_comma)
|
1997-09-07 05:04:48 +00:00
|
|
|
{
|
1997-09-08 02:41:22 +00:00
|
|
|
int c;
|
2002-12-11 22:17:11 +00:00
|
|
|
char *start_buf = buf;
|
2003-12-25 03:44:05 +00:00
|
|
|
char *end_buf = buf + (bufsz - 2);
|
2002-04-04 04:25:54 +00:00
|
|
|
bool in_quote = false;
|
|
|
|
bool was_quote = false;
|
2004-08-29 05:07:03 +00:00
|
|
|
bool saw_quote = false;
|
1997-09-07 05:04:48 +00:00
|
|
|
|
2010-03-06 00:45:49 +00:00
|
|
|
/* end_buf reserves two bytes to ensure we can append \n and \0 */
|
2004-02-02 16:58:30 +00:00
|
|
|
Assert(end_buf > start_buf);
|
|
|
|
|
2010-03-06 00:45:49 +00:00
|
|
|
*initial_quote = false;
|
2011-06-20 17:20:14 -04:00
|
|
|
*terminating_comma = false;
|
2010-03-06 00:45:49 +00:00
|
|
|
|
2002-04-04 04:25:54 +00:00
|
|
|
/* Move over initial whitespace and commas */
|
2003-04-13 04:07:17 +00:00
|
|
|
while ((c = getc(fp)) != EOF && (pg_isblank(c) || c == ','))
|
2001-07-30 14:50:24 +00:00
|
|
|
;
|
1997-09-07 05:04:48 +00:00
|
|
|
|
2004-02-02 16:58:30 +00:00
|
|
|
if (c == EOF || c == '\n')
|
|
|
|
{
|
|
|
|
*buf = '\0';
|
2005-06-28 22:16:45 +00:00
|
|
|
return false;
|
2004-02-02 16:58:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2005-10-15 02:49:52 +00:00
|
|
|
* Build a token in buf of next characters up to EOF, EOL, unquoted comma,
|
|
|
|
* or unquoted whitespace.
|
2004-02-02 16:58:30 +00:00
|
|
|
*/
|
|
|
|
while (c != EOF && c != '\n' &&
|
2008-07-24 17:43:45 +00:00
|
|
|
(!pg_isblank(c) || in_quote))
|
1997-09-07 05:04:48 +00:00
|
|
|
{
|
2004-02-02 16:58:30 +00: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 19:11:14 +00:00
|
|
|
*buf = '\0';
|
2004-02-02 16:58:30 +00:00
|
|
|
ereport(LOG,
|
|
|
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
2005-10-15 02:49:52 +00:00
|
|
|
errmsg("authentication file token too long, skipping: \"%s\"",
|
|
|
|
start_buf)));
|
2004-02-02 16:58:30 +00:00
|
|
|
/* Discard remainder of line */
|
|
|
|
while ((c = getc(fp)) != EOF && c != '\n')
|
|
|
|
;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2011-06-20 17:20:14 -04:00
|
|
|
/* we do not pass back the comma in the token */
|
2008-07-24 17:43:45 +00:00
|
|
|
if (c == ',' && !in_quote)
|
2011-06-20 17:20:14 -04:00
|
|
|
{
|
|
|
|
*terminating_comma = true;
|
2004-02-02 16:58:30 +00:00
|
|
|
break;
|
2011-06-20 17:20:14 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (c != '"' || was_quote)
|
|
|
|
*buf++ = c;
|
2004-02-02 16:58:30 +00:00
|
|
|
|
|
|
|
/* Literal double-quote is two double-quotes */
|
|
|
|
if (in_quote && c == '"')
|
|
|
|
was_quote = !was_quote;
|
|
|
|
else
|
|
|
|
was_quote = false;
|
|
|
|
|
|
|
|
if (c == '"')
|
1997-09-07 05:04:48 +00:00
|
|
|
{
|
2004-02-02 16:58:30 +00:00
|
|
|
in_quote = !in_quote;
|
|
|
|
saw_quote = true;
|
2010-03-06 00:45:49 +00:00
|
|
|
if (buf == start_buf)
|
|
|
|
*initial_quote = true;
|
1997-09-07 05:04:48 +00:00
|
|
|
}
|
2001-10-25 05:50:21 +00:00
|
|
|
|
2004-02-02 16:58:30 +00:00
|
|
|
c = getc(fp);
|
1997-09-07 05:04:48 +00:00
|
|
|
}
|
2003-12-25 03:44:05 +00:00
|
|
|
|
2004-02-02 16:58:30 +00:00
|
|
|
/*
|
2005-10-15 02:49:52 +00: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 16:58:30 +00:00
|
|
|
*/
|
|
|
|
if (c != EOF)
|
|
|
|
ungetc(c, fp);
|
|
|
|
|
|
|
|
*buf = '\0';
|
2003-12-25 03:44:05 +00:00
|
|
|
|
2005-06-28 22:16:45 +00:00
|
|
|
return (saw_quote || buf > start_buf);
|
1996-10-12 07:47:12 +00:00
|
|
|
}
|
|
|
|
|
2011-06-20 17:20:14 -04:00
|
|
|
static HbaToken *
|
|
|
|
make_hba_token(char *token, bool quoted)
|
|
|
|
{
|
|
|
|
HbaToken *hbatoken;
|
|
|
|
int toklen;
|
|
|
|
|
|
|
|
toklen = strlen(token);
|
|
|
|
hbatoken = (HbaToken *) palloc(sizeof(HbaToken) + toklen + 1);
|
|
|
|
hbatoken->string = (char *) hbatoken + sizeof(HbaToken);
|
|
|
|
hbatoken->quoted = quoted;
|
|
|
|
memcpy(hbatoken->string, token, toklen + 1);
|
|
|
|
|
|
|
|
return hbatoken;
|
|
|
|
}
|
|
|
|
|
2002-04-04 04:25:54 +00:00
|
|
|
/*
|
2011-06-20 17:20:14 -04:00
|
|
|
* Copy a HbaToken struct into freshly palloc'd memory.
|
|
|
|
*/
|
|
|
|
static HbaToken *
|
|
|
|
copy_hba_token(HbaToken *in)
|
|
|
|
{
|
|
|
|
HbaToken *out = make_hba_token(in->string, in->quoted);
|
|
|
|
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Tokenize one HBA field from a file, handling file inclusion and comma lists.
|
2004-09-18 01:22:58 +00:00
|
|
|
*
|
2011-06-20 17:20:14 -04:00
|
|
|
* The result is a List of HbaToken structs for each individual token,
|
|
|
|
* or NIL if we reached EOL.
|
2002-04-04 04:25:54 +00:00
|
|
|
*/
|
2011-06-20 17:20:14 -04:00
|
|
|
static List *
|
|
|
|
next_field_expand(const char *filename, FILE *file)
|
2002-04-04 04:25:54 +00:00
|
|
|
{
|
|
|
|
char buf[MAX_TOKEN];
|
|
|
|
bool trailing_comma;
|
2010-03-06 00:45:49 +00:00
|
|
|
bool initial_quote;
|
2011-06-20 17:20:14 -04:00
|
|
|
List *tokens = NIL;
|
2002-04-04 04:25:54 +00:00
|
|
|
|
|
|
|
do
|
|
|
|
{
|
2011-06-20 17:20:14 -04:00
|
|
|
if (!next_token(file, buf, sizeof(buf), &initial_quote, &trailing_comma))
|
2002-04-04 04:25:54 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
/* Is this referencing a file? */
|
2010-03-06 00:45:49 +00:00
|
|
|
if (!initial_quote && buf[0] == '@' && buf[1] != '\0')
|
2011-06-20 17:20:14 -04:00
|
|
|
tokens = tokenize_inc_file(tokens, filename, buf + 1);
|
2002-04-04 04:25:54 +00:00
|
|
|
else
|
2011-06-20 17:20:14 -04:00
|
|
|
tokens = lappend(tokens, make_hba_token(buf, initial_quote));
|
2002-04-04 04:25:54 +00:00
|
|
|
} while (trailing_comma);
|
|
|
|
|
2011-06-20 17:20:14 -04:00
|
|
|
return tokens;
|
2002-04-04 04:25:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2011-06-20 17:20:14 -04:00
|
|
|
* tokenize_inc_file
|
|
|
|
* Expand a file included from another file into an hba "field"
|
|
|
|
*
|
|
|
|
* Opens and tokenises a file included from another HBA config file with @,
|
|
|
|
* and returns all values found therein as a flat list of HbaTokens. If a
|
|
|
|
* @-token is found, recursively expand it. The given token list is used as
|
|
|
|
* initial contents of list (so foo,bar,@baz does what you expect).
|
2002-04-04 04:25:54 +00:00
|
|
|
*/
|
2011-06-20 17:20:14 -04:00
|
|
|
static List *
|
|
|
|
tokenize_inc_file(List *tokens,
|
|
|
|
const char *outer_filename,
|
2004-12-27 19:19:24 +00:00
|
|
|
const char *inc_filename)
|
2002-04-04 04:25:54 +00:00
|
|
|
{
|
|
|
|
char *inc_fullname;
|
|
|
|
FILE *inc_file;
|
2002-09-04 20:31:48 +00:00
|
|
|
List *inc_lines;
|
2004-05-26 04:41:50 +00:00
|
|
|
List *inc_line_nums;
|
2011-06-20 17:20:14 -04:00
|
|
|
ListCell *inc_line;
|
|
|
|
MemoryContext linecxt;
|
2002-04-04 04:25:54 +00:00
|
|
|
|
2004-12-27 19:19:24 +00: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 04:25:54 +00:00
|
|
|
|
|
|
|
inc_file = AllocateFile(inc_fullname, "r");
|
2004-12-27 19:19:24 +00:00
|
|
|
if (inc_file == NULL)
|
2002-04-04 04:25:54 +00:00
|
|
|
{
|
2003-07-22 19:00:12 +00: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 04:25:54 +00:00
|
|
|
pfree(inc_fullname);
|
2011-06-20 17:20:14 -04:00
|
|
|
return tokens;
|
2002-04-04 04:25:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* There is possible recursion here if the file contains @ */
|
2011-06-20 17:20:14 -04:00
|
|
|
linecxt = tokenize_file(inc_fullname, inc_file, &inc_lines, &inc_line_nums);
|
2004-12-27 19:19:24 +00:00
|
|
|
|
2002-04-04 04:25:54 +00:00
|
|
|
FreeFile(inc_file);
|
2004-12-27 19:19:24 +00:00
|
|
|
pfree(inc_fullname);
|
2002-04-04 04:25:54 +00:00
|
|
|
|
2011-06-20 17:20:14 -04:00
|
|
|
foreach(inc_line, inc_lines)
|
2002-04-04 04:25:54 +00:00
|
|
|
{
|
2011-06-20 17:20:14 -04:00
|
|
|
List *inc_fields = lfirst(inc_line);
|
|
|
|
ListCell *inc_field;
|
2002-04-04 04:25:54 +00:00
|
|
|
|
2011-06-20 17:20:14 -04:00
|
|
|
foreach(inc_field, inc_fields)
|
2002-04-04 04:25:54 +00:00
|
|
|
{
|
2011-06-20 17:20:14 -04:00
|
|
|
List *inc_tokens = lfirst(inc_field);
|
|
|
|
ListCell *inc_token;
|
2002-04-04 04:25:54 +00:00
|
|
|
|
2011-06-20 17:20:14 -04:00
|
|
|
foreach(inc_token, inc_tokens)
|
|
|
|
{
|
|
|
|
HbaToken *token = lfirst(inc_token);
|
2002-04-04 04:25:54 +00:00
|
|
|
|
2011-06-20 17:20:14 -04:00
|
|
|
tokens = lappend(tokens, copy_hba_token(token));
|
|
|
|
}
|
|
|
|
}
|
2004-12-27 19:19:24 +00:00
|
|
|
}
|
|
|
|
|
2011-06-20 17:20:14 -04:00
|
|
|
MemoryContextDelete(linecxt);
|
|
|
|
return tokens;
|
1996-10-12 07:47:12 +00:00
|
|
|
}
|
|
|
|
|
2001-07-30 14:50:24 +00:00
|
|
|
/*
|
2011-06-20 17:20:14 -04:00
|
|
|
* Tokenize the given file, storing the resulting data into two Lists: a
|
|
|
|
* List of lines, and a List of line numbers.
|
|
|
|
*
|
|
|
|
* The list of lines is a triple-nested List structure. Each line is a List of
|
|
|
|
* fields, and each field is a List of HbaTokens.
|
2004-12-27 19:19:24 +00:00
|
|
|
*
|
|
|
|
* filename must be the absolute path to the target file.
|
2011-06-20 17:20:14 -04:00
|
|
|
*
|
|
|
|
* Return value is a memory context which contains all memory allocated by
|
|
|
|
* this function.
|
2001-07-30 14:50:24 +00:00
|
|
|
*/
|
2011-06-20 17:20:14 -04:00
|
|
|
static MemoryContext
|
2004-12-27 19:19:24 +00:00
|
|
|
tokenize_file(const char *filename, FILE *file,
|
|
|
|
List **lines, List **line_nums)
|
1997-09-07 05:04:48 +00:00
|
|
|
{
|
2004-05-26 04:41:50 +00:00
|
|
|
List *current_line = NIL;
|
2011-06-20 17:20:14 -04:00
|
|
|
List *current_field = NIL;
|
2001-07-31 22:55:45 +00:00
|
|
|
int line_number = 1;
|
2011-06-20 17:20:14 -04:00
|
|
|
MemoryContext linecxt;
|
|
|
|
MemoryContext oldcxt;
|
|
|
|
|
|
|
|
linecxt = AllocSetContextCreate(TopMemoryContext,
|
|
|
|
"tokenize file cxt",
|
|
|
|
ALLOCSET_DEFAULT_MINSIZE,
|
|
|
|
ALLOCSET_DEFAULT_INITSIZE,
|
|
|
|
ALLOCSET_DEFAULT_MAXSIZE);
|
|
|
|
oldcxt = MemoryContextSwitchTo(linecxt);
|
1997-09-07 05:04:48 +00:00
|
|
|
|
2004-05-26 04:41:50 +00:00
|
|
|
*lines = *line_nums = NIL;
|
|
|
|
|
2010-03-03 20:31:09 +00:00
|
|
|
while (!feof(file) && !ferror(file))
|
1997-09-07 05:04:48 +00:00
|
|
|
{
|
2011-06-20 17:20:14 -04:00
|
|
|
current_field = next_field_expand(filename, file);
|
2001-07-30 14:50:24 +00:00
|
|
|
|
2011-06-20 17:20:14 -04:00
|
|
|
/* add tokens to list, unless we are at EOL or comment start */
|
|
|
|
if (list_length(current_field) > 0)
|
2001-07-30 14:50:24 +00:00
|
|
|
{
|
2004-05-26 04:41:50 +00:00
|
|
|
if (current_line == NIL)
|
|
|
|
{
|
|
|
|
/* make a new line List, record its line number */
|
2011-06-20 17:20:14 -04:00
|
|
|
current_line = lappend(current_line, current_field);
|
2004-05-26 04:41:50 +00:00
|
|
|
*lines = lappend(*lines, current_line);
|
|
|
|
*line_nums = lappend_int(*line_nums, line_number);
|
|
|
|
}
|
|
|
|
else
|
2001-07-30 14:50:24 +00:00
|
|
|
{
|
2011-06-20 17:20:14 -04:00
|
|
|
/* append tokens to current line's list */
|
|
|
|
current_line = lappend(current_line, current_field);
|
2001-07-30 14:50:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
2001-07-31 22:55:45 +00:00
|
|
|
{
|
2002-04-04 04:25:54 +00:00
|
|
|
/* we are at real or logical EOL, so force a new line List */
|
2004-05-26 04:41:50 +00:00
|
|
|
current_line = NIL;
|
2001-07-31 22:55:45 +00:00
|
|
|
line_number++;
|
2004-05-26 04:41:50 +00:00
|
|
|
}
|
2001-07-30 14:50:24 +00:00
|
|
|
}
|
2011-06-20 17:20:14 -04:00
|
|
|
|
|
|
|
MemoryContextSwitchTo(oldcxt);
|
|
|
|
|
|
|
|
return linecxt;
|
2001-07-30 14:50:24 +00:00
|
|
|
}
|
|
|
|
|
2002-04-04 04:25:54 +00:00
|
|
|
|
|
|
|
/*
|
2005-06-28 22:16:45 +00:00
|
|
|
* Does user belong to role?
|
|
|
|
*
|
2009-08-29 19:26:52 +00:00
|
|
|
* userid is the OID of the role given as the attempted login identifier.
|
2005-06-28 22:16:45 +00:00
|
|
|
* We check to see if it is a member of the specified role name.
|
2002-04-04 04:25:54 +00:00
|
|
|
*/
|
2003-04-03 21:25:02 +00:00
|
|
|
static bool
|
2009-08-29 19:26:52 +00:00
|
|
|
is_member(Oid userid, const char *role)
|
2002-04-04 04:25:54 +00:00
|
|
|
{
|
2009-08-29 19:26:52 +00:00
|
|
|
Oid roleid;
|
2002-04-04 04:25:54 +00:00
|
|
|
|
2009-08-29 19:26:52 +00:00
|
|
|
if (!OidIsValid(userid))
|
2005-06-28 22:16:45 +00:00
|
|
|
return false; /* if user not exist, say "no" */
|
2004-05-26 04:41:50 +00:00
|
|
|
|
2010-08-05 14:45:09 +00:00
|
|
|
roleid = get_role_oid(role, true);
|
2005-02-20 04:45:59 +00:00
|
|
|
|
2009-08-29 19:26:52 +00:00
|
|
|
if (!OidIsValid(roleid))
|
|
|
|
return false; /* if target role not exist, say "no" */
|
2001-07-30 14:50:24 +00:00
|
|
|
|
2011-11-03 12:45:02 -04:00
|
|
|
/*
|
|
|
|
* See if user is directly or indirectly a member of role.
|
|
|
|
* For this purpose, a superuser is not considered to be automatically
|
|
|
|
* a member of the role, so group auth only applies to explicit
|
|
|
|
* membership.
|
|
|
|
*/
|
|
|
|
return is_member_of_role_nosuper(userid, roleid);
|
2002-04-04 04:25:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2011-06-20 17:20:14 -04:00
|
|
|
* Check HbaToken list for a match to role, allowing group names.
|
2002-04-04 04:25:54 +00:00
|
|
|
*/
|
2003-04-03 21:25:02 +00:00
|
|
|
static bool
|
2011-06-20 17:20:14 -04:00
|
|
|
check_role(const char *role, Oid roleid, List *tokens)
|
2002-04-04 04:25:54 +00:00
|
|
|
{
|
2011-06-20 17:20:14 -04:00
|
|
|
ListCell *cell;
|
|
|
|
HbaToken *tok;
|
2002-04-04 04:25:54 +00:00
|
|
|
|
2011-06-20 17:20:14 -04:00
|
|
|
foreach(cell, tokens)
|
2002-04-04 04:25:54 +00:00
|
|
|
{
|
2011-06-20 17:20:14 -04:00
|
|
|
tok = lfirst(cell);
|
|
|
|
if (!tok->quoted && tok->string[0] == '+')
|
2001-07-30 14:50:24 +00:00
|
|
|
{
|
2011-06-20 17:20:14 -04:00
|
|
|
if (is_member(roleid, tok->string + 1))
|
2003-04-03 21:25:02 +00:00
|
|
|
return true;
|
2002-04-04 04:25:54 +00:00
|
|
|
}
|
2011-06-20 17:20:14 -04:00
|
|
|
else if (token_matches(tok, role) ||
|
|
|
|
token_is_keyword(tok, "all"))
|
2003-04-03 21:25:02 +00:00
|
|
|
return true;
|
2002-04-04 04:25:54 +00:00
|
|
|
}
|
2003-04-03 21:25:02 +00:00
|
|
|
return false;
|
2002-04-04 04:25:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2011-06-20 17:20:14 -04:00
|
|
|
* Check to see if db/role combination matches HbaToken list.
|
2002-04-04 04:25:54 +00:00
|
|
|
*/
|
2003-04-03 21:25:02 +00:00
|
|
|
static bool
|
2011-06-20 17:20:14 -04:00
|
|
|
check_db(const char *dbname, const char *role, Oid roleid, List *tokens)
|
2002-04-04 04:25:54 +00:00
|
|
|
{
|
2011-06-20 17:20:14 -04:00
|
|
|
ListCell *cell;
|
|
|
|
HbaToken *tok;
|
2002-04-04 04:25:54 +00:00
|
|
|
|
2011-06-20 17:20:14 -04:00
|
|
|
foreach(cell, tokens)
|
2002-04-04 04:25:54 +00:00
|
|
|
{
|
2011-06-20 17:20:14 -04:00
|
|
|
tok = lfirst(cell);
|
2010-04-21 03:32:53 +00:00
|
|
|
if (am_walsender)
|
|
|
|
{
|
|
|
|
/* walsender connections can only match replication keyword */
|
2011-06-20 17:20:14 -04:00
|
|
|
if (token_is_keyword(tok, "replication"))
|
2010-04-21 03:32:53 +00:00
|
|
|
return true;
|
|
|
|
}
|
2011-06-20 17:20:14 -04:00
|
|
|
else if (token_is_keyword(tok, "all"))
|
2003-04-03 21:25:02 +00:00
|
|
|
return true;
|
2011-06-20 17:20:14 -04:00
|
|
|
else if (token_is_keyword(tok, "sameuser"))
|
2002-04-04 04:25:54 +00:00
|
|
|
{
|
2005-06-28 05:09:14 +00:00
|
|
|
if (strcmp(dbname, role) == 0)
|
2003-04-03 21:25:02 +00:00
|
|
|
return true;
|
2001-07-30 14:50:24 +00:00
|
|
|
}
|
2011-06-20 17:20:14 -04:00
|
|
|
else if (token_is_keyword(tok, "samegroup") ||
|
|
|
|
token_is_keyword(tok, "samerole"))
|
2002-04-04 04:25:54 +00:00
|
|
|
{
|
2009-08-29 19:26:52 +00:00
|
|
|
if (is_member(roleid, dbname))
|
2003-04-03 21:25:02 +00:00
|
|
|
return true;
|
2002-04-04 04:25:54 +00:00
|
|
|
}
|
2011-06-20 17:20:14 -04:00
|
|
|
else if (token_is_keyword(tok, "replication"))
|
2010-04-21 03:32:53 +00:00
|
|
|
continue; /* never match this if not walsender */
|
2011-06-20 17:20:14 -04:00
|
|
|
else if (token_matches(tok, dbname))
|
2003-04-03 21:25:02 +00:00
|
|
|
return true;
|
2001-07-30 14:50:24 +00:00
|
|
|
}
|
2003-04-03 21:25:02 +00:00
|
|
|
return false;
|
2001-07-30 14:50:24 +00:00
|
|
|
}
|
|
|
|
|
2010-10-15 22:53:39 +03:00
|
|
|
static bool
|
2011-04-10 11:42:00 -04:00
|
|
|
ipv4eq(struct sockaddr_in * a, struct sockaddr_in * b)
|
2010-10-15 22:53:39 +03:00
|
|
|
{
|
|
|
|
return (a->sin_addr.s_addr == b->sin_addr.s_addr);
|
|
|
|
}
|
|
|
|
|
2010-10-16 10:12:16 -04:00
|
|
|
#ifdef HAVE_IPV6
|
|
|
|
|
2010-10-15 22:53:39 +03:00
|
|
|
static bool
|
2011-04-10 11:42:00 -04:00
|
|
|
ipv6eq(struct sockaddr_in6 * a, struct sockaddr_in6 * b)
|
2010-10-15 22:53:39 +03:00
|
|
|
{
|
2011-04-10 11:42:00 -04:00
|
|
|
int i;
|
2010-10-15 22:53:39 +03:00
|
|
|
|
|
|
|
for (i = 0; i < 16; i++)
|
|
|
|
if (a->sin6_addr.s6_addr[i] != b->sin6_addr.s6_addr[i])
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2011-04-10 11:42:00 -04:00
|
|
|
#endif /* HAVE_IPV6 */
|
2010-10-16 10:12:16 -04:00
|
|
|
|
2010-10-24 15:54:00 +03:00
|
|
|
/*
|
|
|
|
* Check whether host name matches pattern.
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
hostname_match(const char *pattern, const char *actual_hostname)
|
|
|
|
{
|
|
|
|
if (pattern[0] == '.') /* suffix match */
|
|
|
|
{
|
2011-04-10 11:42:00 -04:00
|
|
|
size_t plen = strlen(pattern);
|
|
|
|
size_t hlen = strlen(actual_hostname);
|
2010-10-24 15:54:00 +03:00
|
|
|
|
|
|
|
if (hlen < plen)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return (pg_strcasecmp(pattern, actual_hostname + (hlen - plen)) == 0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return (pg_strcasecmp(pattern, actual_hostname) == 0);
|
|
|
|
}
|
|
|
|
|
2010-10-15 22:53:39 +03:00
|
|
|
/*
|
|
|
|
* Check to see if a connecting IP matches a given host name.
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
check_hostname(hbaPort *port, const char *hostname)
|
|
|
|
{
|
2011-04-10 11:42:00 -04:00
|
|
|
struct addrinfo *gai_result,
|
|
|
|
*gai;
|
2010-10-15 22:53:39 +03:00
|
|
|
int ret;
|
|
|
|
bool found;
|
|
|
|
|
|
|
|
/* Lookup remote host name if not already done */
|
|
|
|
if (!port->remote_hostname)
|
|
|
|
{
|
|
|
|
char remote_hostname[NI_MAXHOST];
|
|
|
|
|
|
|
|
if (pg_getnameinfo_all(&port->raddr.addr, port->raddr.salen,
|
|
|
|
remote_hostname, sizeof(remote_hostname),
|
|
|
|
NULL, 0,
|
2011-08-09 18:28:35 +03:00
|
|
|
0) != 0)
|
2010-10-15 22:53:39 +03:00
|
|
|
return false;
|
|
|
|
|
|
|
|
port->remote_hostname = pstrdup(remote_hostname);
|
|
|
|
}
|
|
|
|
|
2010-10-24 15:54:00 +03:00
|
|
|
if (!hostname_match(hostname, port->remote_hostname))
|
2010-10-15 22:53:39 +03:00
|
|
|
return false;
|
|
|
|
|
|
|
|
/* Lookup IP from host name and check against original IP */
|
|
|
|
|
|
|
|
if (port->remote_hostname_resolv == +1)
|
|
|
|
return true;
|
|
|
|
if (port->remote_hostname_resolv == -1)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
ret = getaddrinfo(port->remote_hostname, NULL, NULL, &gai_result);
|
|
|
|
if (ret != 0)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errmsg("could not translate host name \"%s\" to address: %s",
|
|
|
|
port->remote_hostname, gai_strerror(ret))));
|
|
|
|
|
|
|
|
found = false;
|
|
|
|
for (gai = gai_result; gai; gai = gai->ai_next)
|
|
|
|
{
|
|
|
|
if (gai->ai_addr->sa_family == port->raddr.addr.ss_family)
|
|
|
|
{
|
|
|
|
if (gai->ai_addr->sa_family == AF_INET)
|
|
|
|
{
|
|
|
|
if (ipv4eq((struct sockaddr_in *) gai->ai_addr,
|
2011-04-10 11:42:00 -04:00
|
|
|
(struct sockaddr_in *) & port->raddr.addr))
|
2010-10-15 22:53:39 +03:00
|
|
|
{
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2010-10-16 10:12:16 -04:00
|
|
|
#ifdef HAVE_IPV6
|
2010-10-15 22:53:39 +03:00
|
|
|
else if (gai->ai_addr->sa_family == AF_INET6)
|
|
|
|
{
|
|
|
|
if (ipv6eq((struct sockaddr_in6 *) gai->ai_addr,
|
2011-04-10 11:42:00 -04:00
|
|
|
(struct sockaddr_in6 *) & port->raddr.addr))
|
2010-10-15 22:53:39 +03:00
|
|
|
{
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2010-10-16 10:12:16 -04:00
|
|
|
#endif
|
2010-10-15 22:53:39 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (gai_result)
|
|
|
|
freeaddrinfo(gai_result);
|
|
|
|
|
|
|
|
if (!found)
|
|
|
|
elog(DEBUG2, "pg_hba.conf host name \"%s\" rejected because address resolution did not return a match with IP address of client",
|
|
|
|
hostname);
|
|
|
|
|
|
|
|
port->remote_hostname_resolv = found ? +1 : -1;
|
|
|
|
|
|
|
|
return found;
|
|
|
|
}
|
|
|
|
|
2009-10-01 01:58:58 +00:00
|
|
|
/*
|
|
|
|
* Check to see if a connecting IP matches the given address and netmask.
|
|
|
|
*/
|
|
|
|
static bool
|
2010-02-26 02:01:40 +00:00
|
|
|
check_ip(SockAddr *raddr, struct sockaddr * addr, struct sockaddr * mask)
|
2009-10-01 01:58:58 +00:00
|
|
|
{
|
|
|
|
if (raddr->addr.ss_family == addr->sa_family)
|
|
|
|
{
|
|
|
|
/* Same address family */
|
|
|
|
if (!pg_range_sockaddr(&raddr->addr,
|
2010-02-26 02:01:40 +00:00
|
|
|
(struct sockaddr_storage *) addr,
|
|
|
|
(struct sockaddr_storage *) mask))
|
2009-10-01 01:58:58 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
#ifdef HAVE_IPV6
|
|
|
|
else if (addr->sa_family == AF_INET &&
|
|
|
|
raddr->addr.ss_family == AF_INET6)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* If we're connected on IPv6 but the file specifies an IPv4 address
|
2010-02-26 02:01:40 +00:00
|
|
|
* to match against, promote the latter to an IPv6 address before
|
|
|
|
* trying to match the client's address.
|
2009-10-01 01:58:58 +00:00
|
|
|
*/
|
|
|
|
struct sockaddr_storage addrcopy,
|
|
|
|
maskcopy;
|
|
|
|
|
|
|
|
memcpy(&addrcopy, &addr, sizeof(addrcopy));
|
|
|
|
memcpy(&maskcopy, &mask, sizeof(maskcopy));
|
|
|
|
pg_promote_v4_to_v6_addr(&addrcopy);
|
|
|
|
pg_promote_v4_to_v6_mask(&maskcopy);
|
|
|
|
|
|
|
|
if (!pg_range_sockaddr(&raddr->addr, &addrcopy, &maskcopy))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
#endif /* HAVE_IPV6 */
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Wrong address family, no IPV6 */
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* pg_foreach_ifaddr callback: does client addr match this machine interface?
|
|
|
|
*/
|
|
|
|
static void
|
2010-02-26 02:01:40 +00:00
|
|
|
check_network_callback(struct sockaddr * addr, struct sockaddr * netmask,
|
2009-10-01 01:58:58 +00:00
|
|
|
void *cb_data)
|
|
|
|
{
|
|
|
|
check_network_data *cn = (check_network_data *) cb_data;
|
|
|
|
struct sockaddr_storage mask;
|
|
|
|
|
|
|
|
/* Already found a match? */
|
|
|
|
if (cn->result)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (cn->method == ipCmpSameHost)
|
|
|
|
{
|
|
|
|
/* Make an all-ones netmask of appropriate length for family */
|
|
|
|
pg_sockaddr_cidr_mask(&mask, NULL, addr->sa_family);
|
2010-02-26 02:01:40 +00:00
|
|
|
cn->result = check_ip(cn->raddr, addr, (struct sockaddr *) & mask);
|
2009-10-01 01:58:58 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Use the netmask of the interface itself */
|
|
|
|
cn->result = check_ip(cn->raddr, addr, netmask);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Use pg_foreach_ifaddr to check a samehost or samenet match
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
check_same_host_or_net(SockAddr *raddr, IPCompareMethod method)
|
|
|
|
{
|
|
|
|
check_network_data cn;
|
|
|
|
|
|
|
|
cn.method = method;
|
|
|
|
cn.raddr = raddr;
|
|
|
|
cn.result = false;
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
if (pg_foreach_ifaddr(check_network_callback, &cn) < 0)
|
|
|
|
{
|
|
|
|
elog(LOG, "error enumerating network interfaces: %m");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return cn.result;
|
|
|
|
}
|
|
|
|
|
2001-07-30 14:50:24 +00:00
|
|
|
|
2008-10-23 13:31:10 +00:00
|
|
|
/*
|
|
|
|
* Macros used to check and report on invalid configuration options.
|
|
|
|
* INVALID_AUTH_OPTION = reports when an option is specified for a method where it's
|
2009-06-11 14:49:15 +00:00
|
|
|
* not supported.
|
2008-10-23 13:31:10 +00:00
|
|
|
* REQUIRE_AUTH_OPTION = same as INVALID_AUTH_OPTION, except it also checks if the
|
2009-06-11 14:49:15 +00:00
|
|
|
* method is actually the one specified. Used as a shortcut when
|
|
|
|
* the option is only valid for one authentication method.
|
2008-10-23 13:31:10 +00:00
|
|
|
* MANDATORY_AUTH_ARG = check if a required option is set for an authentication method,
|
2009-06-11 14:49:15 +00:00
|
|
|
* reporting error if it's not.
|
2008-10-23 13:31:10 +00:00
|
|
|
*/
|
|
|
|
#define INVALID_AUTH_OPTION(optname, validmethods) do {\
|
|
|
|
ereport(LOG, \
|
|
|
|
(errcode(ERRCODE_CONFIG_FILE_ERROR), \
|
2009-04-15 21:42:50 +00:00
|
|
|
/* translator: the second %s is a list of auth methods */ \
|
|
|
|
errmsg("authentication option \"%s\" is only valid for authentication methods %s", \
|
|
|
|
optname, _(validmethods)), \
|
2008-10-23 13:31:10 +00:00
|
|
|
errcontext("line %d of configuration file \"%s\"", \
|
|
|
|
line_num, HbaFileName))); \
|
2008-10-24 12:48:31 +00:00
|
|
|
return false; \
|
2008-10-23 13:31:10 +00:00
|
|
|
} while (0);
|
|
|
|
|
|
|
|
#define REQUIRE_AUTH_OPTION(methodval, optname, validmethods) do {\
|
2011-06-20 17:20:14 -04:00
|
|
|
if (hbaline->auth_method != methodval) \
|
2009-01-02 11:34:03 +00:00
|
|
|
INVALID_AUTH_OPTION(optname, validmethods); \
|
2008-10-23 13:31:10 +00:00
|
|
|
} while (0);
|
|
|
|
|
|
|
|
#define MANDATORY_AUTH_ARG(argvar, argname, authname) do {\
|
|
|
|
if (argvar == NULL) {\
|
|
|
|
ereport(LOG, \
|
|
|
|
(errcode(ERRCODE_CONFIG_FILE_ERROR), \
|
2009-03-25 14:12:02 +00:00
|
|
|
errmsg("authentication method \"%s\" requires argument \"%s\" to be set", \
|
2008-10-23 13:31:10 +00:00
|
|
|
authname, argname), \
|
|
|
|
errcontext("line %d of configuration file \"%s\"", \
|
|
|
|
line_num, HbaFileName))); \
|
2008-10-24 12:48:31 +00:00
|
|
|
return false; \
|
2008-10-23 13:31:10 +00:00
|
|
|
} \
|
|
|
|
} while (0);
|
|
|
|
|
2011-06-20 17:20:14 -04:00
|
|
|
/*
|
|
|
|
* IDENT_FIELD_ABSENT:
|
|
|
|
* Throw an error and exit the function if the given ident field ListCell is
|
|
|
|
* not populated.
|
|
|
|
*
|
|
|
|
* IDENT_MULTI_VALUE:
|
|
|
|
* Throw an error and exit the function if the given ident token List has more
|
|
|
|
* than one element.
|
|
|
|
*/
|
|
|
|
#define IDENT_FIELD_ABSENT(field) do {\
|
|
|
|
if (!field) { \
|
|
|
|
ereport(LOG, \
|
|
|
|
(errcode(ERRCODE_CONFIG_FILE_ERROR), \
|
|
|
|
errmsg("missing entry in file \"%s\" at end of line %d", \
|
|
|
|
IdentFileName, line_number))); \
|
|
|
|
*error_p = true; \
|
|
|
|
return; \
|
|
|
|
} \
|
|
|
|
} while (0);
|
|
|
|
|
|
|
|
#define IDENT_MULTI_VALUE(tokens) do {\
|
|
|
|
if (tokens->length > 1) { \
|
|
|
|
ereport(LOG, \
|
|
|
|
(errcode(ERRCODE_CONFIG_FILE_ERROR), \
|
|
|
|
errmsg("multiple values in ident field"), \
|
|
|
|
errcontext("line %d of configuration file \"%s\"", \
|
|
|
|
line_number, IdentFileName))); \
|
|
|
|
*error_p = true; \
|
|
|
|
return; \
|
|
|
|
} \
|
|
|
|
} while (0);
|
|
|
|
|
2008-10-23 13:31:10 +00:00
|
|
|
|
2001-07-30 14:50:24 +00:00
|
|
|
/*
|
2011-06-20 17:20:14 -04:00
|
|
|
* Parse one tokenised line from the hba config file and store the result in a
|
|
|
|
* HbaLine structure, or NULL if parsing fails.
|
|
|
|
*
|
|
|
|
* The tokenised line is a List of fields, each field being a List of
|
|
|
|
* HbaTokens.
|
|
|
|
*
|
|
|
|
* Note: this function leaks memory when an error occurs. Caller is expected
|
|
|
|
* to have set a memory context that will be reset if this function returns
|
|
|
|
* NULL.
|
2001-07-30 14:50:24 +00:00
|
|
|
*/
|
2011-06-20 17:20:14 -04:00
|
|
|
static HbaLine *
|
|
|
|
parse_hba_line(List *line, int line_num)
|
1997-09-07 05:04:48 +00:00
|
|
|
{
|
2011-06-20 17:20:14 -04:00
|
|
|
char *str;
|
2003-09-05 23:07:21 +00:00
|
|
|
struct addrinfo *gai_result;
|
2003-08-04 00:43:34 +00:00
|
|
|
struct addrinfo hints;
|
2003-06-12 07:36:51 +00:00
|
|
|
int ret;
|
2003-09-05 23:07:21 +00:00
|
|
|
char *cidr_slash;
|
2008-09-15 12:32:57 +00:00
|
|
|
char *unsupauth;
|
2011-06-20 17:20:14 -04:00
|
|
|
ListCell *field;
|
|
|
|
List *tokens;
|
|
|
|
ListCell *tokencell;
|
|
|
|
HbaToken *token;
|
|
|
|
HbaLine *parsedline;
|
2008-09-15 12:32:57 +00:00
|
|
|
|
2011-06-20 17:20:14 -04:00
|
|
|
parsedline = palloc0(sizeof(HbaLine));
|
2008-09-15 12:32:57 +00:00
|
|
|
parsedline->linenumber = line_num;
|
|
|
|
|
1998-01-26 01:42:53 +00:00
|
|
|
/* Check the record type. */
|
2011-06-20 17:20:14 -04:00
|
|
|
field = list_head(line);
|
|
|
|
tokens = lfirst(field);
|
|
|
|
if (tokens->length > 1)
|
|
|
|
{
|
|
|
|
ereport(LOG,
|
|
|
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
|
|
|
errmsg("multiple values specified for connection type"),
|
|
|
|
errhint("Specify exactly one connection type per line."),
|
|
|
|
errcontext("line %d of configuration file \"%s\"",
|
|
|
|
line_num, HbaFileName)));
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
token = linitial(tokens);
|
|
|
|
if (strcmp(token->string, "local") == 0)
|
1998-01-26 01:42:53 +00:00
|
|
|
{
|
2011-05-30 20:11:13 +02:00
|
|
|
#ifdef HAVE_UNIX_SOCKETS
|
2008-09-15 12:32:57 +00:00
|
|
|
parsedline->conntype = ctLocal;
|
2011-05-30 20:11:13 +02:00
|
|
|
#else
|
|
|
|
ereport(LOG,
|
|
|
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
|
|
|
errmsg("local connections are not supported by this build"),
|
|
|
|
errcontext("line %d of configuration file \"%s\"",
|
|
|
|
line_num, HbaFileName)));
|
2011-06-20 17:20:14 -04:00
|
|
|
return NULL;
|
2011-05-30 20:11:13 +02:00
|
|
|
#endif
|
1998-01-26 01:42:53 +00:00
|
|
|
}
|
2011-06-20 17:20:14 -04:00
|
|
|
else if (strcmp(token->string, "host") == 0 ||
|
|
|
|
strcmp(token->string, "hostssl") == 0 ||
|
|
|
|
strcmp(token->string, "hostnossl") == 0)
|
1997-09-07 05:04:48 +00:00
|
|
|
{
|
2002-12-06 04:37:05 +00:00
|
|
|
|
2011-06-20 17:20:14 -04:00
|
|
|
if (token->string[4] == 's') /* "hostssl" */
|
2000-04-12 17:17:23 +00:00
|
|
|
{
|
2011-04-26 15:40:11 -04:00
|
|
|
/* SSL support must be actually active, else complain */
|
2001-08-02 14:27:40 +00:00
|
|
|
#ifdef USE_SSL
|
2011-04-26 15:40:11 -04:00
|
|
|
if (EnableSSL)
|
|
|
|
parsedline->conntype = ctHostSSL;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ereport(LOG,
|
|
|
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
|
|
|
errmsg("hostssl requires SSL to be turned on"),
|
|
|
|
errhint("Set ssl = on in postgresql.conf."),
|
|
|
|
errcontext("line %d of configuration file \"%s\"",
|
|
|
|
line_num, HbaFileName)));
|
2011-06-20 17:20:14 -04:00
|
|
|
return NULL;
|
2011-04-26 15:40:11 -04:00
|
|
|
}
|
1999-09-27 03:13:16 +00:00
|
|
|
#else
|
2008-10-27 20:04:45 +00:00
|
|
|
ereport(LOG,
|
|
|
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
2011-04-26 15:56:28 -04:00
|
|
|
errmsg("hostssl is not supported by this build"),
|
2010-07-06 19:19:02 +00:00
|
|
|
errhint("Compile with --with-openssl to use SSL connections."),
|
2008-10-27 20:04:45 +00:00
|
|
|
errcontext("line %d of configuration file \"%s\"",
|
2009-06-11 14:49:15 +00:00
|
|
|
line_num, HbaFileName)));
|
2011-06-20 17:20:14 -04:00
|
|
|
return NULL;
|
1999-09-27 03:13:16 +00:00
|
|
|
#endif
|
2001-08-02 14:27:40 +00: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 13:50:02 +00:00
|
|
|
#ifdef USE_SSL
|
2011-06-20 17:20:14 -04:00
|
|
|
else if (token->string[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 13:50:02 +00:00
|
|
|
{
|
2008-09-15 12:32:57 +00:00
|
|
|
parsedline->conntype = ctHostNoSSL;
|
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 13:50:02 +00:00
|
|
|
}
|
|
|
|
#endif
|
2009-06-11 14:49:15 +00:00
|
|
|
else
|
2008-09-15 12:32:57 +00:00
|
|
|
{
|
|
|
|
/* "host", or "hostnossl" and SSL support not built in */
|
|
|
|
parsedline->conntype = ctHost;
|
|
|
|
}
|
2009-06-11 14:49:15 +00:00
|
|
|
} /* record type */
|
2008-09-15 12:32:57 +00:00
|
|
|
else
|
2008-10-27 20:04:45 +00:00
|
|
|
{
|
|
|
|
ereport(LOG,
|
|
|
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
|
|
|
errmsg("invalid connection type \"%s\"",
|
2011-06-20 17:20:14 -04:00
|
|
|
token->string),
|
2008-10-27 20:04:45 +00:00
|
|
|
errcontext("line %d of configuration file \"%s\"",
|
2009-06-11 14:49:15 +00:00
|
|
|
line_num, HbaFileName)));
|
2011-06-20 17:20:14 -04:00
|
|
|
return NULL;
|
2008-10-27 20:04:45 +00:00
|
|
|
}
|
1998-01-26 01:42:53 +00:00
|
|
|
|
2011-06-20 17:20:14 -04:00
|
|
|
/* Get the databases. */
|
|
|
|
field = lnext(field);
|
|
|
|
if (!field)
|
2008-10-27 20:04:45 +00:00
|
|
|
{
|
|
|
|
ereport(LOG,
|
|
|
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
|
|
|
errmsg("end-of-line before database specification"),
|
|
|
|
errcontext("line %d of configuration file \"%s\"",
|
2009-06-11 14:49:15 +00:00
|
|
|
line_num, HbaFileName)));
|
2011-06-20 17:20:14 -04:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
parsedline->databases = NIL;
|
|
|
|
tokens = lfirst(field);
|
|
|
|
foreach(tokencell, tokens)
|
|
|
|
{
|
|
|
|
parsedline->databases = lappend(parsedline->databases,
|
|
|
|
copy_hba_token(lfirst(tokencell)));
|
2008-10-27 20:04:45 +00:00
|
|
|
}
|
1998-01-26 01:42:53 +00:00
|
|
|
|
2011-06-20 17:20:14 -04:00
|
|
|
/* Get the roles. */
|
|
|
|
field = lnext(field);
|
|
|
|
if (!field)
|
2008-10-27 20:04:45 +00:00
|
|
|
{
|
|
|
|
ereport(LOG,
|
|
|
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
|
|
|
errmsg("end-of-line before role specification"),
|
|
|
|
errcontext("line %d of configuration file \"%s\"",
|
2009-06-11 14:49:15 +00:00
|
|
|
line_num, HbaFileName)));
|
2011-06-20 17:20:14 -04:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
parsedline->roles = NIL;
|
|
|
|
tokens = lfirst(field);
|
|
|
|
foreach(tokencell, tokens)
|
|
|
|
{
|
|
|
|
parsedline->roles = lappend(parsedline->roles,
|
|
|
|
copy_hba_token(lfirst(tokencell)));
|
2008-10-27 20:04:45 +00:00
|
|
|
}
|
2002-04-04 04:25:54 +00:00
|
|
|
|
2008-09-15 12:32:57 +00:00
|
|
|
if (parsedline->conntype != ctLocal)
|
|
|
|
{
|
2003-06-12 07:36:51 +00:00
|
|
|
/* Read the IP address field. (with or without CIDR netmask) */
|
2011-06-20 17:20:14 -04:00
|
|
|
field = lnext(field);
|
|
|
|
if (!field)
|
2008-10-27 20:04:45 +00:00
|
|
|
{
|
|
|
|
ereport(LOG,
|
|
|
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
2009-06-21 20:15:32 +00:00
|
|
|
errmsg("end-of-line before IP address specification"),
|
2008-10-27 20:04:45 +00:00
|
|
|
errcontext("line %d of configuration file \"%s\"",
|
2009-06-11 14:49:15 +00:00
|
|
|
line_num, HbaFileName)));
|
2011-06-20 17:20:14 -04:00
|
|
|
return NULL;
|
2008-10-27 20:04:45 +00:00
|
|
|
}
|
2011-06-20 17:20:14 -04:00
|
|
|
tokens = lfirst(field);
|
|
|
|
if (tokens->length > 1)
|
|
|
|
{
|
|
|
|
ereport(LOG,
|
|
|
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
|
|
|
errmsg("multiple values specified for host address"),
|
|
|
|
errhint("Specify one address range per line."),
|
|
|
|
errcontext("line %d of configuration file \"%s\"",
|
|
|
|
line_num, HbaFileName)));
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
token = linitial(tokens);
|
2009-10-01 01:58:58 +00:00
|
|
|
|
2011-06-20 17:20:14 -04:00
|
|
|
if (token_is_keyword(token, "all"))
|
2010-10-18 22:14:47 +03:00
|
|
|
{
|
|
|
|
parsedline->ip_cmp_method = ipCmpAll;
|
|
|
|
}
|
2011-06-20 17:20:14 -04:00
|
|
|
else if (token_is_keyword(token, "samehost"))
|
2003-06-12 07:36:51 +00:00
|
|
|
{
|
2009-10-01 01:58:58 +00:00
|
|
|
/* Any IP on this host is allowed to connect */
|
|
|
|
parsedline->ip_cmp_method = ipCmpSameHost;
|
2003-06-12 07:36:51 +00:00
|
|
|
}
|
2011-06-20 17:20:14 -04:00
|
|
|
else if (token_is_keyword(token, "samenet"))
|
2003-06-12 07:36:51 +00:00
|
|
|
{
|
2009-10-01 01:58:58 +00:00
|
|
|
/* Any IP on the host's subnets is allowed to connect */
|
|
|
|
parsedline->ip_cmp_method = ipCmpSameNet;
|
2003-06-12 07:36:51 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2009-10-01 01:58:58 +00:00
|
|
|
/* IP and netmask are specified */
|
|
|
|
parsedline->ip_cmp_method = ipCmpMask;
|
|
|
|
|
|
|
|
/* need a modifiable copy of token */
|
2011-06-20 17:20:14 -04:00
|
|
|
str = pstrdup(token->string);
|
2009-10-01 01:58:58 +00:00
|
|
|
|
|
|
|
/* Check if it has a CIDR suffix and if so isolate it */
|
2011-06-20 17:20:14 -04:00
|
|
|
cidr_slash = strchr(str, '/');
|
2009-10-01 01:58:58 +00:00
|
|
|
if (cidr_slash)
|
|
|
|
*cidr_slash = '\0';
|
|
|
|
|
|
|
|
/* Get the IP address either way */
|
|
|
|
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;
|
2003-06-12 07:36:51 +00:00
|
|
|
|
2011-06-20 17:20:14 -04:00
|
|
|
ret = pg_getaddrinfo_all(str, NULL, &hints, &gai_result);
|
2010-10-15 22:53:39 +03:00
|
|
|
if (ret == 0 && gai_result)
|
|
|
|
memcpy(&parsedline->addr, gai_result->ai_addr,
|
|
|
|
gai_result->ai_addrlen);
|
|
|
|
else if (ret == EAI_NONAME)
|
2011-06-20 17:20:14 -04:00
|
|
|
parsedline->hostname = str;
|
2010-10-15 22:53:39 +03:00
|
|
|
else
|
2003-09-05 23:07:21 +00:00
|
|
|
{
|
2004-05-19 22:06:16 +00:00
|
|
|
ereport(LOG,
|
|
|
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
2009-10-01 01:58:58 +00:00
|
|
|
errmsg("invalid IP address \"%s\": %s",
|
2011-06-20 17:20:14 -04:00
|
|
|
str, gai_strerror(ret)),
|
2008-09-15 20:55:04 +00:00
|
|
|
errcontext("line %d of configuration file \"%s\"",
|
2009-06-11 14:49:15 +00:00
|
|
|
line_num, HbaFileName)));
|
2003-09-05 23:07:21 +00:00
|
|
|
if (gai_result)
|
2005-10-17 16:24:20 +00:00
|
|
|
pg_freeaddrinfo_all(hints.ai_family, gai_result);
|
2011-06-20 17:20:14 -04:00
|
|
|
return NULL;
|
2003-09-05 23:07:21 +00:00
|
|
|
}
|
2003-07-22 19:00:12 +00:00
|
|
|
|
2005-10-17 16:24:20 +00:00
|
|
|
pg_freeaddrinfo_all(hints.ai_family, gai_result);
|
2003-06-12 07:36:51 +00:00
|
|
|
|
2009-10-01 01:58:58 +00:00
|
|
|
/* Get the netmask */
|
|
|
|
if (cidr_slash)
|
2004-05-19 22:06:16 +00:00
|
|
|
{
|
2010-10-15 22:53:39 +03:00
|
|
|
if (parsedline->hostname)
|
|
|
|
{
|
|
|
|
ereport(LOG,
|
|
|
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
|
|
|
errmsg("specifying both host name and CIDR mask is invalid: \"%s\"",
|
2011-06-20 17:20:14 -04:00
|
|
|
token->string),
|
|
|
|
errcontext("line %d of configuration file \"%s\"",
|
|
|
|
line_num, HbaFileName)));
|
|
|
|
return NULL;
|
2010-10-15 22:53:39 +03:00
|
|
|
}
|
|
|
|
|
2009-10-01 01:58:58 +00:00
|
|
|
if (pg_sockaddr_cidr_mask(&parsedline->mask, cidr_slash + 1,
|
|
|
|
parsedline->addr.ss_family) < 0)
|
|
|
|
{
|
|
|
|
ereport(LOG,
|
|
|
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
|
|
|
errmsg("invalid CIDR mask in address \"%s\"",
|
2011-06-20 17:20:14 -04:00
|
|
|
token->string),
|
2010-02-26 02:01:40 +00:00
|
|
|
errcontext("line %d of configuration file \"%s\"",
|
|
|
|
line_num, HbaFileName)));
|
2011-06-20 17:20:14 -04:00
|
|
|
return NULL;
|
2009-10-01 01:58:58 +00:00
|
|
|
}
|
2011-06-20 17:20:14 -04:00
|
|
|
pfree(str);
|
2009-10-01 01:58:58 +00:00
|
|
|
}
|
2010-10-15 22:53:39 +03:00
|
|
|
else if (!parsedline->hostname)
|
2009-10-01 01:58:58 +00:00
|
|
|
{
|
|
|
|
/* Read the mask field. */
|
2011-06-20 17:20:14 -04:00
|
|
|
pfree(str);
|
|
|
|
field = lnext(field);
|
|
|
|
if (!field)
|
2009-10-01 01:58:58 +00:00
|
|
|
{
|
|
|
|
ereport(LOG,
|
|
|
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
2010-02-26 02:01:40 +00:00
|
|
|
errmsg("end-of-line before netmask specification"),
|
2011-06-20 17:20:14 -04:00
|
|
|
errhint("Specify an address range in CIDR notation, or provide a separate netmask."),
|
2010-02-26 02:01:40 +00:00
|
|
|
errcontext("line %d of configuration file \"%s\"",
|
|
|
|
line_num, HbaFileName)));
|
2011-06-20 17:20:14 -04:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
tokens = lfirst(field);
|
|
|
|
if (tokens->length > 1)
|
|
|
|
{
|
|
|
|
ereport(LOG,
|
|
|
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
|
|
|
errmsg("multiple values specified for netmask"),
|
|
|
|
errcontext("line %d of configuration file \"%s\"",
|
|
|
|
line_num, HbaFileName)));
|
|
|
|
return NULL;
|
2009-10-01 01:58:58 +00:00
|
|
|
}
|
2011-06-20 17:20:14 -04:00
|
|
|
token = linitial(tokens);
|
2009-10-01 01:58:58 +00:00
|
|
|
|
2011-06-20 17:20:14 -04:00
|
|
|
ret = pg_getaddrinfo_all(token->string, NULL,
|
|
|
|
&hints, &gai_result);
|
2009-10-01 01:58:58 +00:00
|
|
|
if (ret || !gai_result)
|
|
|
|
{
|
|
|
|
ereport(LOG,
|
|
|
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
|
|
|
errmsg("invalid IP mask \"%s\": %s",
|
2011-06-20 17:20:14 -04:00
|
|
|
token->string, gai_strerror(ret)),
|
2010-02-26 02:01:40 +00:00
|
|
|
errcontext("line %d of configuration file \"%s\"",
|
|
|
|
line_num, HbaFileName)));
|
2009-10-01 01:58:58 +00:00
|
|
|
if (gai_result)
|
|
|
|
pg_freeaddrinfo_all(hints.ai_family, gai_result);
|
2011-06-20 17:20:14 -04:00
|
|
|
return NULL;
|
2009-10-01 01:58:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(&parsedline->mask, gai_result->ai_addr,
|
|
|
|
gai_result->ai_addrlen);
|
|
|
|
pg_freeaddrinfo_all(hints.ai_family, gai_result);
|
|
|
|
|
|
|
|
if (parsedline->addr.ss_family != parsedline->mask.ss_family)
|
|
|
|
{
|
|
|
|
ereport(LOG,
|
|
|
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
2010-05-26 16:43:13 +00:00
|
|
|
errmsg("IP address and mask do not match"),
|
2010-07-06 19:19:02 +00:00
|
|
|
errcontext("line %d of configuration file \"%s\"",
|
|
|
|
line_num, HbaFileName)));
|
2011-06-20 17:20:14 -04:00
|
|
|
return NULL;
|
2009-10-01 01:58:58 +00:00
|
|
|
}
|
2004-05-19 22:06:16 +00:00
|
|
|
}
|
2003-06-12 07:36:51 +00:00
|
|
|
}
|
2009-06-11 14:49:15 +00:00
|
|
|
} /* != ctLocal */
|
2008-09-15 12:32:57 +00:00
|
|
|
|
|
|
|
/* Get the authentication method */
|
2011-06-20 17:20:14 -04:00
|
|
|
field = lnext(field);
|
|
|
|
if (!field)
|
2008-10-27 20:04:45 +00:00
|
|
|
{
|
|
|
|
ereport(LOG,
|
|
|
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
|
|
|
errmsg("end-of-line before authentication method"),
|
|
|
|
errcontext("line %d of configuration file \"%s\"",
|
2009-06-11 14:49:15 +00:00
|
|
|
line_num, HbaFileName)));
|
2011-06-20 17:20:14 -04:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
tokens = lfirst(field);
|
|
|
|
if (tokens->length > 1)
|
|
|
|
{
|
|
|
|
ereport(LOG,
|
|
|
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
|
|
|
errmsg("multiple values specified for authentication type"),
|
|
|
|
errhint("Specify exactly one authentication type per line."),
|
|
|
|
errcontext("line %d of configuration file \"%s\"",
|
|
|
|
line_num, HbaFileName)));
|
|
|
|
return NULL;
|
2008-10-27 20:04:45 +00:00
|
|
|
}
|
2011-06-20 17:20:14 -04:00
|
|
|
token = linitial(tokens);
|
1998-01-26 01:42:53 +00:00
|
|
|
|
2008-09-15 12:32:57 +00:00
|
|
|
unsupauth = NULL;
|
2011-06-20 17:20:14 -04:00
|
|
|
if (strcmp(token->string, "trust") == 0)
|
2008-09-15 12:32:57 +00:00
|
|
|
parsedline->auth_method = uaTrust;
|
2011-06-20 17:20:14 -04:00
|
|
|
else if (strcmp(token->string, "ident") == 0)
|
2008-09-15 12:32:57 +00:00
|
|
|
parsedline->auth_method = uaIdent;
|
2011-06-20 17:20:14 -04:00
|
|
|
else if (strcmp(token->string, "peer") == 0)
|
2011-03-19 18:44:35 +01:00
|
|
|
parsedline->auth_method = uaPeer;
|
2011-06-20 17:20:14 -04:00
|
|
|
else if (strcmp(token->string, "password") == 0)
|
2008-09-15 12:32:57 +00:00
|
|
|
parsedline->auth_method = uaPassword;
|
2011-06-20 17:20:14 -04:00
|
|
|
else if (strcmp(token->string, "krb5") == 0)
|
2008-09-15 12:32:57 +00:00
|
|
|
#ifdef KRB5
|
|
|
|
parsedline->auth_method = uaKrb5;
|
|
|
|
#else
|
|
|
|
unsupauth = "krb5";
|
|
|
|
#endif
|
2011-06-20 17:20:14 -04:00
|
|
|
else if (strcmp(token->string, "gss") == 0)
|
2008-09-15 12:32:57 +00:00
|
|
|
#ifdef ENABLE_GSS
|
|
|
|
parsedline->auth_method = uaGSS;
|
|
|
|
#else
|
|
|
|
unsupauth = "gss";
|
|
|
|
#endif
|
2011-06-20 17:20:14 -04:00
|
|
|
else if (strcmp(token->string, "sspi") == 0)
|
2008-09-15 12:32:57 +00:00
|
|
|
#ifdef ENABLE_SSPI
|
|
|
|
parsedline->auth_method = uaSSPI;
|
|
|
|
#else
|
|
|
|
unsupauth = "sspi";
|
|
|
|
#endif
|
2011-06-20 17:20:14 -04:00
|
|
|
else if (strcmp(token->string, "reject") == 0)
|
2008-09-15 12:32:57 +00:00
|
|
|
parsedline->auth_method = uaReject;
|
2011-06-20 17:20:14 -04:00
|
|
|
else if (strcmp(token->string, "md5") == 0)
|
2008-11-20 20:45:30 +00:00
|
|
|
{
|
|
|
|
if (Db_user_namespace)
|
|
|
|
{
|
|
|
|
ereport(LOG,
|
|
|
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
2010-05-26 16:43:13 +00:00
|
|
|
errmsg("MD5 authentication is not supported when \"db_user_namespace\" is enabled"),
|
|
|
|
errcontext("line %d of configuration file \"%s\"",
|
|
|
|
line_num, HbaFileName)));
|
2011-06-20 17:20:14 -04:00
|
|
|
return NULL;
|
2008-11-20 20:45:30 +00:00
|
|
|
}
|
2008-09-15 12:32:57 +00:00
|
|
|
parsedline->auth_method = uaMD5;
|
2008-11-20 20:45:30 +00:00
|
|
|
}
|
2011-06-20 17:20:14 -04:00
|
|
|
else if (strcmp(token->string, "pam") == 0)
|
2008-09-15 12:32:57 +00:00
|
|
|
#ifdef USE_PAM
|
|
|
|
parsedline->auth_method = uaPAM;
|
|
|
|
#else
|
|
|
|
unsupauth = "pam";
|
|
|
|
#endif
|
2011-06-20 17:20:14 -04:00
|
|
|
else if (strcmp(token->string, "ldap") == 0)
|
2008-09-15 12:32:57 +00:00
|
|
|
#ifdef USE_LDAP
|
|
|
|
parsedline->auth_method = uaLDAP;
|
|
|
|
#else
|
|
|
|
unsupauth = "ldap";
|
2008-11-20 11:48:26 +00:00
|
|
|
#endif
|
2011-06-20 17:20:14 -04:00
|
|
|
else if (strcmp(token->string, "cert") == 0)
|
2008-11-20 11:48:26 +00:00
|
|
|
#ifdef USE_SSL
|
|
|
|
parsedline->auth_method = uaCert;
|
|
|
|
#else
|
|
|
|
unsupauth = "cert";
|
2008-09-15 12:32:57 +00:00
|
|
|
#endif
|
2011-06-20 17:20:14 -04:00
|
|
|
else if (strcmp(token->string, "radius") == 0)
|
2010-01-27 12:12:00 +00:00
|
|
|
parsedline->auth_method = uaRADIUS;
|
2008-09-15 12:32:57 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
ereport(LOG,
|
|
|
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
|
|
|
errmsg("invalid authentication method \"%s\"",
|
2011-06-20 17:20:14 -04:00
|
|
|
token->string),
|
2008-09-15 20:55:04 +00:00
|
|
|
errcontext("line %d of configuration file \"%s\"",
|
2009-06-11 14:49:15 +00:00
|
|
|
line_num, HbaFileName)));
|
2011-06-20 17:20:14 -04:00
|
|
|
return NULL;
|
2008-09-15 12:32:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (unsupauth)
|
|
|
|
{
|
|
|
|
ereport(LOG,
|
|
|
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
2011-04-26 15:56:28 -04:00
|
|
|
errmsg("invalid authentication method \"%s\": not supported by this build",
|
2011-06-20 17:20:14 -04:00
|
|
|
token->string),
|
2008-09-15 20:55:04 +00:00
|
|
|
errcontext("line %d of configuration file \"%s\"",
|
2009-06-11 14:49:15 +00:00
|
|
|
line_num, HbaFileName)));
|
2011-06-20 17:20:14 -04:00
|
|
|
return NULL;
|
2008-09-15 12:32:57 +00:00
|
|
|
}
|
|
|
|
|
2011-03-19 18:44:35 +01:00
|
|
|
/*
|
|
|
|
* XXX: When using ident on local connections, change it to peer, for
|
|
|
|
* backwards compatibility.
|
|
|
|
*/
|
|
|
|
if (parsedline->conntype == ctLocal &&
|
|
|
|
parsedline->auth_method == uaIdent)
|
|
|
|
parsedline->auth_method = uaPeer;
|
|
|
|
|
2008-09-15 12:32:57 +00:00
|
|
|
/* Invalid authentication combinations */
|
|
|
|
if (parsedline->conntype == ctLocal &&
|
|
|
|
parsedline->auth_method == uaKrb5)
|
|
|
|
{
|
|
|
|
ereport(LOG,
|
|
|
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
2009-06-11 14:49:15 +00:00
|
|
|
errmsg("krb5 authentication is not supported on local sockets"),
|
2008-09-15 20:55:04 +00:00
|
|
|
errcontext("line %d of configuration file \"%s\"",
|
2009-06-11 14:49:15 +00:00
|
|
|
line_num, HbaFileName)));
|
2011-06-20 17:20:14 -04:00
|
|
|
return NULL;
|
2008-09-15 12:32:57 +00:00
|
|
|
}
|
|
|
|
|
2010-03-08 09:57:26 +00:00
|
|
|
if (parsedline->conntype == ctLocal &&
|
|
|
|
parsedline->auth_method == uaGSS)
|
|
|
|
{
|
|
|
|
ereport(LOG,
|
|
|
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
2010-07-06 19:19:02 +00:00
|
|
|
errmsg("gssapi authentication is not supported on local sockets"),
|
2010-03-08 09:57:26 +00:00
|
|
|
errcontext("line %d of configuration file \"%s\"",
|
|
|
|
line_num, HbaFileName)));
|
2011-06-20 17:20:14 -04:00
|
|
|
return NULL;
|
2010-03-08 09:57:26 +00:00
|
|
|
}
|
2010-07-06 19:19:02 +00:00
|
|
|
|
2011-03-19 18:44:35 +01:00
|
|
|
if (parsedline->conntype != ctLocal &&
|
|
|
|
parsedline->auth_method == uaPeer)
|
|
|
|
{
|
|
|
|
ereport(LOG,
|
|
|
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
|
|
|
errmsg("peer authentication is only supported on local sockets"),
|
|
|
|
errcontext("line %d of configuration file \"%s\"",
|
|
|
|
line_num, HbaFileName)));
|
2011-06-20 17:20:14 -04:00
|
|
|
return NULL;
|
2011-03-19 18:44:35 +01:00
|
|
|
}
|
|
|
|
|
2010-03-08 09:57:26 +00:00
|
|
|
/*
|
2010-07-06 19:19:02 +00:00
|
|
|
* SSPI authentication can never be enabled on ctLocal connections,
|
|
|
|
* because it's only supported on Windows, where ctLocal isn't supported.
|
2010-03-08 09:57:26 +00:00
|
|
|
*/
|
2012-01-27 21:39:38 +02:00
|
|
|
|
|
|
|
|
2008-11-20 11:48:26 +00:00
|
|
|
if (parsedline->conntype != ctHostSSL &&
|
|
|
|
parsedline->auth_method == uaCert)
|
|
|
|
{
|
|
|
|
ereport(LOG,
|
|
|
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
|
|
|
errmsg("cert authentication is only supported on hostssl connections"),
|
|
|
|
errcontext("line %d of configuration file \"%s\"",
|
|
|
|
line_num, HbaFileName)));
|
2011-06-20 17:20:14 -04:00
|
|
|
return NULL;
|
2008-11-20 11:48:26 +00:00
|
|
|
}
|
|
|
|
|
2008-10-23 13:31:10 +00:00
|
|
|
/* Parse remaining arguments */
|
2011-06-20 17:20:14 -04:00
|
|
|
while ((field = lnext(field)) != NULL)
|
2008-09-15 12:32:57 +00:00
|
|
|
{
|
2011-06-20 17:20:14 -04:00
|
|
|
tokens = lfirst(field);
|
|
|
|
foreach(tokencell, tokens)
|
2003-09-05 20:31:36 +00:00
|
|
|
{
|
2011-06-20 17:20:14 -04:00
|
|
|
char *val;
|
|
|
|
token = lfirst(tokencell);
|
|
|
|
|
|
|
|
str = pstrdup(token->string);
|
|
|
|
val = strchr(str, '=');
|
|
|
|
if (val == NULL)
|
2008-11-20 09:29:36 +00:00
|
|
|
{
|
|
|
|
/*
|
2011-06-20 17:20:14 -04:00
|
|
|
* Got something that's not a name=value pair.
|
2008-11-20 09:29:36 +00:00
|
|
|
*/
|
2008-10-23 13:31:10 +00:00
|
|
|
ereport(LOG,
|
|
|
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
2011-06-20 17:20:14 -04:00
|
|
|
errmsg("authentication option not in name=value format: %s", token->string),
|
2008-10-23 13:31:10 +00:00
|
|
|
errcontext("line %d of configuration file \"%s\"",
|
|
|
|
line_num, HbaFileName)));
|
2011-06-20 17:20:14 -04:00
|
|
|
return NULL;
|
2003-09-05 20:31:36 +00:00
|
|
|
}
|
2011-06-20 17:20:14 -04:00
|
|
|
|
|
|
|
*val++ = '\0'; /* str now holds "name", val holds "value" */
|
|
|
|
if (!parse_hba_auth_opt(str, val, parsedline, line_num))
|
|
|
|
/* parse_hba_auth_opt already logged the error message */
|
|
|
|
return NULL;
|
|
|
|
pfree(str);
|
2003-09-05 20:31:36 +00:00
|
|
|
}
|
1997-09-07 05:04:48 +00:00
|
|
|
}
|
2008-10-23 13:31:10 +00:00
|
|
|
|
|
|
|
/*
|
2009-06-11 14:49:15 +00:00
|
|
|
* Check if the selected authentication method has any mandatory arguments
|
|
|
|
* that are not set.
|
2008-10-23 13:31:10 +00:00
|
|
|
*/
|
|
|
|
if (parsedline->auth_method == uaLDAP)
|
|
|
|
{
|
|
|
|
MANDATORY_AUTH_ARG(parsedline->ldapserver, "ldapserver", "ldap");
|
2009-12-12 21:35:21 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* LDAP can operate in two modes: either with a direct bind, using
|
2010-02-26 02:01:40 +00:00
|
|
|
* ldapprefix and ldapsuffix, or using a search+bind, using
|
|
|
|
* ldapbasedn, ldapbinddn, ldapbindpasswd and ldapsearchattribute.
|
2009-12-12 21:35:21 +00:00
|
|
|
* Disallow mixing these parameters.
|
|
|
|
*/
|
|
|
|
if (parsedline->ldapprefix || parsedline->ldapsuffix)
|
|
|
|
{
|
|
|
|
if (parsedline->ldapbasedn ||
|
|
|
|
parsedline->ldapbinddn ||
|
|
|
|
parsedline->ldapbindpasswd ||
|
|
|
|
parsedline->ldapsearchattribute)
|
|
|
|
{
|
|
|
|
ereport(LOG,
|
|
|
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
2010-03-21 00:17:59 +00:00
|
|
|
errmsg("cannot use ldapbasedn, ldapbinddn, ldapbindpasswd, or ldapsearchattribute together with ldapprefix"),
|
2009-12-12 21:35:21 +00:00
|
|
|
errcontext("line %d of configuration file \"%s\"",
|
|
|
|
line_num, HbaFileName)));
|
2011-06-20 17:20:14 -04:00
|
|
|
return NULL;
|
2009-12-12 21:35:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (!parsedline->ldapbasedn)
|
|
|
|
{
|
|
|
|
ereport(LOG,
|
|
|
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
2010-03-21 00:17:59 +00:00
|
|
|
errmsg("authentication method \"ldap\" requires argument \"ldapbasedn\", \"ldapprefix\", or \"ldapsuffix\" to be set"),
|
2009-12-12 21:35:21 +00:00
|
|
|
errcontext("line %d of configuration file \"%s\"",
|
|
|
|
line_num, HbaFileName)));
|
2011-06-20 17:20:14 -04:00
|
|
|
return NULL;
|
2009-12-12 21:35:21 +00:00
|
|
|
}
|
2008-10-23 13:31:10 +00:00
|
|
|
}
|
2008-11-20 11:48:26 +00:00
|
|
|
|
2010-01-27 12:12:00 +00:00
|
|
|
if (parsedline->auth_method == uaRADIUS)
|
|
|
|
{
|
|
|
|
MANDATORY_AUTH_ARG(parsedline->radiusserver, "radiusserver", "radius");
|
|
|
|
MANDATORY_AUTH_ARG(parsedline->radiussecret, "radiussecret", "radius");
|
|
|
|
}
|
|
|
|
|
2008-11-20 11:48:26 +00:00
|
|
|
/*
|
|
|
|
* Enforce any parameters implied by other settings.
|
|
|
|
*/
|
|
|
|
if (parsedline->auth_method == uaCert)
|
|
|
|
{
|
|
|
|
parsedline->clientcert = true;
|
|
|
|
}
|
2009-06-11 14:49:15 +00:00
|
|
|
|
2011-06-20 17:20:14 -04:00
|
|
|
return parsedline;
|
1996-10-12 07:47:12 +00:00
|
|
|
}
|
|
|
|
|
2001-07-30 14:50:24 +00:00
|
|
|
/*
|
2011-06-20 17:20:14 -04:00
|
|
|
* Parse one name-value pair as an authentication option into the given
|
|
|
|
* HbaLine. Return true if we successfully parse the option, false if we
|
|
|
|
* encounter an error.
|
2001-07-30 14:50:24 +00:00
|
|
|
*/
|
|
|
|
static bool
|
2011-06-20 17:20:14 -04:00
|
|
|
parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num)
|
|
|
|
{
|
|
|
|
if (strcmp(name, "map") == 0)
|
|
|
|
{
|
|
|
|
if (hbaline->auth_method != uaIdent &&
|
|
|
|
hbaline->auth_method != uaPeer &&
|
|
|
|
hbaline->auth_method != uaKrb5 &&
|
|
|
|
hbaline->auth_method != uaGSS &&
|
|
|
|
hbaline->auth_method != uaSSPI &&
|
|
|
|
hbaline->auth_method != uaCert)
|
|
|
|
INVALID_AUTH_OPTION("map", gettext_noop("ident, peer, krb5, gssapi, sspi and cert"));
|
|
|
|
hbaline->usermap = pstrdup(val);
|
|
|
|
}
|
|
|
|
else if (strcmp(name, "clientcert") == 0)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Since we require ctHostSSL, this really can never happen
|
|
|
|
* on non-SSL-enabled builds, so don't bother checking for
|
|
|
|
* USE_SSL.
|
|
|
|
*/
|
|
|
|
if (hbaline->conntype != ctHostSSL)
|
|
|
|
{
|
|
|
|
ereport(LOG,
|
|
|
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
|
|
|
errmsg("clientcert can only be configured for \"hostssl\" rows"),
|
|
|
|
errcontext("line %d of configuration file \"%s\"",
|
|
|
|
line_num, HbaFileName)));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (strcmp(val, "1") == 0)
|
|
|
|
{
|
|
|
|
if (!secure_loaded_verify_locations())
|
|
|
|
{
|
|
|
|
ereport(LOG,
|
|
|
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
|
|
|
errmsg("client certificates can only be checked if a root certificate store is available"),
|
|
|
|
errhint("Make sure the root.crt file is present and readable."),
|
|
|
|
errcontext("line %d of configuration file \"%s\"",
|
|
|
|
line_num, HbaFileName)));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
hbaline->clientcert = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (hbaline->auth_method == uaCert)
|
|
|
|
{
|
|
|
|
ereport(LOG,
|
|
|
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
|
|
|
errmsg("clientcert can not be set to 0 when using \"cert\" authentication"),
|
|
|
|
errcontext("line %d of configuration file \"%s\"",
|
|
|
|
line_num, HbaFileName)));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
hbaline->clientcert = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (strcmp(name, "pamservice") == 0)
|
|
|
|
{
|
|
|
|
REQUIRE_AUTH_OPTION(uaPAM, "pamservice", "pam");
|
|
|
|
hbaline->pamservice = pstrdup(val);
|
|
|
|
}
|
|
|
|
else if (strcmp(name, "ldaptls") == 0)
|
|
|
|
{
|
|
|
|
REQUIRE_AUTH_OPTION(uaLDAP, "ldaptls", "ldap");
|
|
|
|
if (strcmp(val, "1") == 0)
|
|
|
|
hbaline->ldaptls = true;
|
|
|
|
else
|
|
|
|
hbaline->ldaptls = false;
|
|
|
|
}
|
|
|
|
else if (strcmp(name, "ldapserver") == 0)
|
|
|
|
{
|
|
|
|
REQUIRE_AUTH_OPTION(uaLDAP, "ldapserver", "ldap");
|
|
|
|
hbaline->ldapserver = pstrdup(val);
|
|
|
|
}
|
|
|
|
else if (strcmp(name, "ldapport") == 0)
|
|
|
|
{
|
|
|
|
REQUIRE_AUTH_OPTION(uaLDAP, "ldapport", "ldap");
|
|
|
|
hbaline->ldapport = atoi(val);
|
|
|
|
if (hbaline->ldapport == 0)
|
|
|
|
{
|
|
|
|
ereport(LOG,
|
|
|
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
|
|
|
errmsg("invalid LDAP port number: \"%s\"", val),
|
|
|
|
errcontext("line %d of configuration file \"%s\"",
|
|
|
|
line_num, HbaFileName)));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (strcmp(name, "ldapbinddn") == 0)
|
|
|
|
{
|
|
|
|
REQUIRE_AUTH_OPTION(uaLDAP, "ldapbinddn", "ldap");
|
|
|
|
hbaline->ldapbinddn = pstrdup(val);
|
|
|
|
}
|
|
|
|
else if (strcmp(name, "ldapbindpasswd") == 0)
|
|
|
|
{
|
|
|
|
REQUIRE_AUTH_OPTION(uaLDAP, "ldapbindpasswd", "ldap");
|
|
|
|
hbaline->ldapbindpasswd = pstrdup(val);
|
|
|
|
}
|
|
|
|
else if (strcmp(name, "ldapsearchattribute") == 0)
|
|
|
|
{
|
|
|
|
REQUIRE_AUTH_OPTION(uaLDAP, "ldapsearchattribute", "ldap");
|
|
|
|
hbaline->ldapsearchattribute = pstrdup(val);
|
|
|
|
}
|
|
|
|
else if (strcmp(name, "ldapbasedn") == 0)
|
|
|
|
{
|
|
|
|
REQUIRE_AUTH_OPTION(uaLDAP, "ldapbasedn", "ldap");
|
|
|
|
hbaline->ldapbasedn = pstrdup(val);
|
|
|
|
}
|
|
|
|
else if (strcmp(name, "ldapprefix") == 0)
|
|
|
|
{
|
|
|
|
REQUIRE_AUTH_OPTION(uaLDAP, "ldapprefix", "ldap");
|
|
|
|
hbaline->ldapprefix = pstrdup(val);
|
|
|
|
}
|
|
|
|
else if (strcmp(name, "ldapsuffix") == 0)
|
|
|
|
{
|
|
|
|
REQUIRE_AUTH_OPTION(uaLDAP, "ldapsuffix", "ldap");
|
|
|
|
hbaline->ldapsuffix = pstrdup(val);
|
|
|
|
}
|
|
|
|
else if (strcmp(name, "krb_server_hostname") == 0)
|
|
|
|
{
|
|
|
|
REQUIRE_AUTH_OPTION(uaKrb5, "krb_server_hostname", "krb5");
|
|
|
|
hbaline->krb_server_hostname = pstrdup(val);
|
|
|
|
}
|
|
|
|
else if (strcmp(name, "krb_realm") == 0)
|
|
|
|
{
|
|
|
|
if (hbaline->auth_method != uaKrb5 &&
|
|
|
|
hbaline->auth_method != uaGSS &&
|
|
|
|
hbaline->auth_method != uaSSPI)
|
|
|
|
INVALID_AUTH_OPTION("krb_realm", gettext_noop("krb5, gssapi and sspi"));
|
|
|
|
hbaline->krb_realm = pstrdup(val);
|
|
|
|
}
|
|
|
|
else if (strcmp(name, "include_realm") == 0)
|
|
|
|
{
|
|
|
|
if (hbaline->auth_method != uaKrb5 &&
|
|
|
|
hbaline->auth_method != uaGSS &&
|
|
|
|
hbaline->auth_method != uaSSPI)
|
|
|
|
INVALID_AUTH_OPTION("include_realm", gettext_noop("krb5, gssapi and sspi"));
|
|
|
|
if (strcmp(val, "1") == 0)
|
|
|
|
hbaline->include_realm = true;
|
|
|
|
else
|
|
|
|
hbaline->include_realm = false;
|
|
|
|
}
|
|
|
|
else if (strcmp(name, "radiusserver") == 0)
|
|
|
|
{
|
|
|
|
struct addrinfo *gai_result;
|
|
|
|
struct addrinfo hints;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
REQUIRE_AUTH_OPTION(uaRADIUS, "radiusserver", "radius");
|
|
|
|
|
|
|
|
MemSet(&hints, 0, sizeof(hints));
|
|
|
|
hints.ai_socktype = SOCK_DGRAM;
|
|
|
|
hints.ai_family = AF_UNSPEC;
|
|
|
|
|
|
|
|
ret = pg_getaddrinfo_all(val, NULL, &hints, &gai_result);
|
|
|
|
if (ret || !gai_result)
|
|
|
|
{
|
|
|
|
ereport(LOG,
|
|
|
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
|
|
|
errmsg("could not translate RADIUS server name \"%s\" to address: %s",
|
|
|
|
val, gai_strerror(ret)),
|
|
|
|
errcontext("line %d of configuration file \"%s\"",
|
|
|
|
line_num, HbaFileName)));
|
|
|
|
if (gai_result)
|
|
|
|
pg_freeaddrinfo_all(hints.ai_family, gai_result);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
pg_freeaddrinfo_all(hints.ai_family, gai_result);
|
|
|
|
hbaline->radiusserver = pstrdup(val);
|
|
|
|
}
|
|
|
|
else if (strcmp(name, "radiusport") == 0)
|
|
|
|
{
|
|
|
|
REQUIRE_AUTH_OPTION(uaRADIUS, "radiusport", "radius");
|
|
|
|
hbaline->radiusport = atoi(val);
|
|
|
|
if (hbaline->radiusport == 0)
|
|
|
|
{
|
|
|
|
ereport(LOG,
|
|
|
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
|
|
|
errmsg("invalid RADIUS port number: \"%s\"", val),
|
|
|
|
errcontext("line %d of configuration file \"%s\"",
|
|
|
|
line_num, HbaFileName)));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (strcmp(name, "radiussecret") == 0)
|
|
|
|
{
|
|
|
|
REQUIRE_AUTH_OPTION(uaRADIUS, "radiussecret", "radius");
|
|
|
|
hbaline->radiussecret = pstrdup(val);
|
|
|
|
}
|
|
|
|
else if (strcmp(name, "radiusidentifier") == 0)
|
|
|
|
{
|
|
|
|
REQUIRE_AUTH_OPTION(uaRADIUS, "radiusidentifier", "radius");
|
|
|
|
hbaline->radiusidentifier = pstrdup(val);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ereport(LOG,
|
|
|
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
|
|
|
errmsg("unrecognized authentication option name: \"%s\"",
|
|
|
|
name),
|
|
|
|
errcontext("line %d of configuration file \"%s\"",
|
|
|
|
line_num, HbaFileName)));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Scan the pre-parsed hba file, looking for a match to the port's connection
|
|
|
|
* request.
|
|
|
|
*/
|
|
|
|
static void
|
2001-07-30 14:50:24 +00:00
|
|
|
check_hba(hbaPort *port)
|
1997-09-07 05:04:48 +00:00
|
|
|
{
|
2009-08-29 19:26:52 +00:00
|
|
|
Oid roleid;
|
2004-05-26 04:41:50 +00:00
|
|
|
ListCell *line;
|
2009-06-11 14:49:15 +00:00
|
|
|
HbaLine *hba;
|
1999-05-25 16:15:34 +00:00
|
|
|
|
2009-08-29 19:26:52 +00:00
|
|
|
/* Get the target role's OID. Note we do not error out for bad role. */
|
2010-08-05 14:45:09 +00:00
|
|
|
roleid = get_role_oid(port->user_name, true);
|
2009-08-29 19:26:52 +00:00
|
|
|
|
2008-09-15 12:32:57 +00:00
|
|
|
foreach(line, parsed_hba_lines)
|
2001-07-30 14:50:24 +00:00
|
|
|
{
|
2008-09-15 12:32:57 +00:00
|
|
|
hba = (HbaLine *) lfirst(line);
|
1998-01-26 01:42:53 +00:00
|
|
|
|
2008-09-15 12:32:57 +00:00
|
|
|
/* Check connection type */
|
|
|
|
if (hba->conntype == ctLocal)
|
|
|
|
{
|
|
|
|
if (!IS_AF_UNIX(port->raddr.addr.ss_family))
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (IS_AF_UNIX(port->raddr.addr.ss_family))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Check SSL state */
|
|
|
|
#ifdef USE_SSL
|
|
|
|
if (port->ssl)
|
|
|
|
{
|
|
|
|
/* Connection is SSL, match both "host" and "hostssl" */
|
|
|
|
if (hba->conntype == ctHostNoSSL)
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Connection is not SSL, match both "host" and "hostnossl" */
|
|
|
|
if (hba->conntype == ctHostSSL)
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
/* No SSL support, so reject "hostssl" lines */
|
|
|
|
if (hba->conntype == ctHostSSL)
|
|
|
|
continue;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Check IP address */
|
2009-10-01 01:58:58 +00:00
|
|
|
switch (hba->ip_cmp_method)
|
2008-09-15 12:32:57 +00:00
|
|
|
{
|
2009-10-01 01:58:58 +00:00
|
|
|
case ipCmpMask:
|
2010-10-15 22:53:39 +03:00
|
|
|
if (hba->hostname)
|
|
|
|
{
|
|
|
|
if (!check_hostname(port,
|
|
|
|
hba->hostname))
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (!check_ip(&port->raddr,
|
|
|
|
(struct sockaddr *) & hba->addr,
|
|
|
|
(struct sockaddr *) & hba->mask))
|
|
|
|
continue;
|
|
|
|
}
|
2009-10-01 01:58:58 +00:00
|
|
|
break;
|
2010-10-18 22:14:47 +03:00
|
|
|
case ipCmpAll:
|
|
|
|
break;
|
2009-10-01 01:58:58 +00:00
|
|
|
case ipCmpSameHost:
|
|
|
|
case ipCmpSameNet:
|
|
|
|
if (!check_same_host_or_net(&port->raddr,
|
|
|
|
hba->ip_cmp_method))
|
|
|
|
continue;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* shouldn't get here, but deem it no-match if so */
|
2008-09-15 12:32:57 +00:00
|
|
|
continue;
|
|
|
|
}
|
2009-06-11 14:49:15 +00:00
|
|
|
} /* != ctLocal */
|
2008-09-15 12:32:57 +00:00
|
|
|
|
|
|
|
/* Check database and role */
|
2009-08-29 19:26:52 +00:00
|
|
|
if (!check_db(port->database_name, port->user_name, roleid,
|
2011-06-20 17:20:14 -04:00
|
|
|
hba->databases))
|
2008-09-15 12:32:57 +00:00
|
|
|
continue;
|
|
|
|
|
2011-06-20 17:20:14 -04:00
|
|
|
if (!check_role(port->user_name, roleid, hba->roles))
|
2008-09-15 12:32:57 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Found a record that matched! */
|
|
|
|
port->hba = hba;
|
2011-06-20 17:20:14 -04:00
|
|
|
return;
|
1998-01-27 03:25:14 +00:00
|
|
|
}
|
2008-09-15 12:32:57 +00:00
|
|
|
|
2010-04-19 19:02:18 +00:00
|
|
|
/* If no matching entry was found, then implicitly reject. */
|
2008-09-15 12:32:57 +00:00
|
|
|
hba = palloc0(sizeof(HbaLine));
|
2010-04-19 19:02:18 +00:00
|
|
|
hba->auth_method = uaImplicitReject;
|
2008-09-15 12:32:57 +00:00
|
|
|
port->hba = hba;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read the config file and create a List of HbaLine records for the contents.
|
|
|
|
*
|
2011-10-18 20:09:18 -04:00
|
|
|
* The configuration is read into a temporary list, and if any parse error
|
|
|
|
* occurs the old list is kept in place and false is returned. Only if the
|
|
|
|
* whole file parses OK is the list replaced, and the function returns true.
|
|
|
|
*
|
|
|
|
* On a false result, caller will take care of reporting a FATAL error in case
|
|
|
|
* this is the initial startup. If it happens on reload, we just keep running
|
|
|
|
* with the old data.
|
2008-09-15 12:32:57 +00:00
|
|
|
*/
|
|
|
|
bool
|
2001-07-31 22:55:45 +00:00
|
|
|
load_hba(void)
|
2001-07-30 14:50:24 +00:00
|
|
|
{
|
2004-10-09 23:13:22 +00:00
|
|
|
FILE *file;
|
2009-06-11 14:49:15 +00:00
|
|
|
List *hba_lines = NIL;
|
|
|
|
List *hba_line_nums = NIL;
|
|
|
|
ListCell *line,
|
|
|
|
*line_num;
|
|
|
|
List *new_parsed_lines = NIL;
|
|
|
|
bool ok = true;
|
2011-06-20 17:20:14 -04:00
|
|
|
MemoryContext linecxt;
|
|
|
|
MemoryContext oldcxt;
|
|
|
|
MemoryContext hbacxt;
|
2001-10-25 05:50:21 +00:00
|
|
|
|
2004-10-09 23:13:22 +00:00
|
|
|
file = AllocateFile(HbaFileName, "r");
|
2002-04-04 04:25:54 +00:00
|
|
|
if (file == NULL)
|
2009-03-04 08:43:15 +00:00
|
|
|
{
|
2009-03-04 18:43:38 +00:00
|
|
|
ereport(LOG,
|
2003-07-22 19:00:12 +00:00
|
|
|
(errcode_for_file_access(),
|
2003-09-25 06:58:07 +00:00
|
|
|
errmsg("could not open configuration file \"%s\": %m",
|
2004-10-09 23:13:22 +00:00
|
|
|
HbaFileName)));
|
2009-03-04 08:43:15 +00:00
|
|
|
return false;
|
|
|
|
}
|
2002-12-14 18:49:37 +00:00
|
|
|
|
2011-06-20 17:20:14 -04:00
|
|
|
linecxt = tokenize_file(HbaFileName, file, &hba_lines, &hba_line_nums);
|
2002-12-14 18:49:37 +00:00
|
|
|
FreeFile(file);
|
2008-09-15 12:32:57 +00:00
|
|
|
|
|
|
|
/* Now parse all the lines */
|
2011-06-20 17:20:14 -04:00
|
|
|
hbacxt = AllocSetContextCreate(TopMemoryContext,
|
|
|
|
"hba parser context",
|
|
|
|
ALLOCSET_DEFAULT_MINSIZE,
|
|
|
|
ALLOCSET_DEFAULT_MINSIZE,
|
|
|
|
ALLOCSET_DEFAULT_MAXSIZE);
|
|
|
|
oldcxt = MemoryContextSwitchTo(hbacxt);
|
2008-09-15 12:32:57 +00:00
|
|
|
forboth(line, hba_lines, line_num, hba_line_nums)
|
|
|
|
{
|
2009-06-11 14:49:15 +00:00
|
|
|
HbaLine *newline;
|
2008-09-15 12:32:57 +00:00
|
|
|
|
2011-06-20 17:20:14 -04:00
|
|
|
if ((newline = parse_hba_line(lfirst(line), lfirst_int(line_num))) == NULL)
|
2008-09-15 12:32:57 +00:00
|
|
|
{
|
2011-06-20 17:20:14 -04:00
|
|
|
/*
|
|
|
|
* Parse error in the file, so indicate there's a problem. NB: a
|
|
|
|
* problem in a line will free the memory for all previous lines as
|
|
|
|
* well!
|
|
|
|
*/
|
|
|
|
MemoryContextReset(hbacxt);
|
|
|
|
new_parsed_lines = NIL;
|
2009-10-03 20:04:39 +00:00
|
|
|
ok = false;
|
2009-03-07 21:28:00 +00:00
|
|
|
|
|
|
|
/*
|
2009-06-11 14:49:15 +00:00
|
|
|
* Keep parsing the rest of the file so we can report errors on
|
|
|
|
* more than the first row. Error has already been reported in the
|
|
|
|
* parsing function, so no need to log it here.
|
2009-03-07 21:28:00 +00:00
|
|
|
*/
|
|
|
|
continue;
|
2008-09-15 12:32:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
new_parsed_lines = lappend(new_parsed_lines, newline);
|
|
|
|
}
|
|
|
|
|
2011-10-18 20:09:18 -04:00
|
|
|
/*
|
|
|
|
* A valid HBA file must have at least one entry; else there's no way
|
|
|
|
* to connect to the postmaster. But only complain about this if we
|
|
|
|
* didn't already have parsing errors.
|
|
|
|
*/
|
|
|
|
if (ok && new_parsed_lines == NIL)
|
|
|
|
{
|
|
|
|
ereport(LOG,
|
|
|
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
|
|
|
errmsg("configuration file \"%s\" contains no entries",
|
|
|
|
HbaFileName)));
|
|
|
|
ok = false;
|
|
|
|
}
|
|
|
|
|
2011-06-20 17:20:14 -04:00
|
|
|
/* Free tokenizer memory */
|
|
|
|
MemoryContextDelete(linecxt);
|
|
|
|
MemoryContextSwitchTo(oldcxt);
|
2009-10-03 20:04:39 +00:00
|
|
|
|
2009-03-07 21:28:00 +00:00
|
|
|
if (!ok)
|
|
|
|
{
|
2011-10-18 20:09:18 -04:00
|
|
|
/* File contained one or more errors, so bail out */
|
2011-06-20 17:20:14 -04:00
|
|
|
MemoryContextDelete(hbacxt);
|
2009-03-07 21:28:00 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2008-09-15 12:32:57 +00:00
|
|
|
/* Loaded new file successfully, replace the one we use */
|
2011-06-20 17:20:14 -04:00
|
|
|
if (parsed_hba_context != NULL)
|
|
|
|
MemoryContextDelete(parsed_hba_context);
|
|
|
|
parsed_hba_context = hbacxt;
|
2008-09-15 12:32:57 +00:00
|
|
|
parsed_hba_lines = new_parsed_lines;
|
|
|
|
|
|
|
|
return true;
|
1996-10-12 07:47:12 +00:00
|
|
|
}
|
|
|
|
|
2001-07-30 14:50:24 +00:00
|
|
|
/*
|
2001-07-31 22:55:45 +00:00
|
|
|
* Process one line from the ident config file.
|
|
|
|
*
|
2005-06-28 05:09:14 +00:00
|
|
|
* Take the line and compare it to the needed map, pg_role and ident_user.
|
2001-07-31 22:55:45 +00:00
|
|
|
* *found_p and *error_p are set according to our results.
|
2001-07-30 14:50:24 +00:00
|
|
|
*/
|
1996-10-12 07:47:12 +00:00
|
|
|
static void
|
2004-05-26 04:41:50 +00:00
|
|
|
parse_ident_usermap(List *line, int line_number, const char *usermap_name,
|
2005-06-28 05:09:14 +00:00
|
|
|
const char *pg_role, const char *ident_user,
|
2008-10-23 13:31:10 +00:00
|
|
|
bool case_insensitive, bool *found_p, bool *error_p)
|
2001-07-30 14:50:24 +00:00
|
|
|
{
|
2011-06-20 17:20:14 -04:00
|
|
|
ListCell *field;
|
|
|
|
List *tokens;
|
|
|
|
HbaToken *token;
|
2001-10-25 05:50:21 +00:00
|
|
|
char *file_map;
|
2005-06-28 05:09:14 +00:00
|
|
|
char *file_pgrole;
|
2001-10-25 05:50:21 +00:00
|
|
|
char *file_ident_user;
|
2001-07-30 14:50:24 +00:00
|
|
|
|
|
|
|
*found_p = false;
|
2001-07-31 22:55:45 +00:00
|
|
|
*error_p = false;
|
2001-07-30 14:50:24 +00:00
|
|
|
|
|
|
|
Assert(line != NIL);
|
2011-06-20 17:20:14 -04:00
|
|
|
field = list_head(line);
|
2001-07-31 22:55:45 +00:00
|
|
|
|
|
|
|
/* Get the map token (must exist) */
|
2011-06-20 17:20:14 -04:00
|
|
|
tokens = lfirst(field);
|
|
|
|
IDENT_MULTI_VALUE(tokens);
|
|
|
|
token = linitial(tokens);
|
|
|
|
file_map = token->string;
|
2001-07-30 14:50:24 +00:00
|
|
|
|
2005-06-21 01:20:09 +00:00
|
|
|
/* Get the ident user token */
|
2011-06-20 17:20:14 -04:00
|
|
|
field = lnext(field);
|
|
|
|
IDENT_FIELD_ABSENT(field);
|
|
|
|
tokens = lfirst(field);
|
|
|
|
IDENT_MULTI_VALUE(tokens);
|
|
|
|
token = linitial(tokens);
|
|
|
|
file_ident_user = token->string;
|
2001-07-30 14:50:24 +00:00
|
|
|
|
2005-06-28 05:09:14 +00:00
|
|
|
/* Get the PG rolename token */
|
2011-06-20 17:20:14 -04:00
|
|
|
field = lnext(field);
|
|
|
|
IDENT_FIELD_ABSENT(field);
|
|
|
|
tokens = lfirst(field);
|
|
|
|
IDENT_MULTI_VALUE(tokens);
|
|
|
|
token = linitial(tokens);
|
|
|
|
file_pgrole = token->string;
|
2001-07-31 22:55:45 +00:00
|
|
|
|
2008-11-28 14:26:58 +00:00
|
|
|
if (strcmp(file_map, usermap_name) != 0)
|
|
|
|
/* Line does not match the map name we're looking for, so just abort */
|
|
|
|
return;
|
|
|
|
|
2001-07-31 22:55:45 +00:00
|
|
|
/* Match? */
|
2008-11-28 14:26:58 +00:00
|
|
|
if (file_ident_user[0] == '/')
|
2008-10-23 13:31:10 +00:00
|
|
|
{
|
2008-11-28 14:26:58 +00:00
|
|
|
/*
|
2009-06-11 14:49:15 +00:00
|
|
|
* When system username starts with a slash, treat it as a regular
|
|
|
|
* expression. In this case, we process the system username as a
|
|
|
|
* regular expression that returns exactly one match. This is replaced
|
|
|
|
* for \1 in the database username string, if present.
|
2008-11-28 14:26:58 +00:00
|
|
|
*/
|
|
|
|
int r;
|
|
|
|
regex_t re;
|
|
|
|
regmatch_t matches[2];
|
|
|
|
pg_wchar *wstr;
|
|
|
|
int wlen;
|
|
|
|
char *ofs;
|
|
|
|
char *regexp_pgrole;
|
|
|
|
|
2009-06-11 14:49:15 +00:00
|
|
|
wstr = palloc((strlen(file_ident_user + 1) + 1) * sizeof(pg_wchar));
|
|
|
|
wlen = pg_mb2wchar_with_len(file_ident_user + 1, wstr, strlen(file_ident_user + 1));
|
2008-11-28 14:26:58 +00:00
|
|
|
|
|
|
|
/*
|
2009-06-11 14:49:15 +00:00
|
|
|
* XXX: Major room for optimization: regexps could be compiled when
|
|
|
|
* the file is loaded and then re-used in every connection.
|
2008-11-28 14:26:58 +00:00
|
|
|
*/
|
2011-04-10 18:02:17 -04:00
|
|
|
r = pg_regcomp(&re, wstr, wlen, REG_ADVANCED, C_COLLATION_OID);
|
2008-11-28 14:26:58 +00:00
|
|
|
if (r)
|
|
|
|
{
|
2009-06-11 14:49:15 +00:00
|
|
|
char errstr[100];
|
2008-11-28 14:26:58 +00:00
|
|
|
|
|
|
|
pg_regerror(r, &re, errstr, sizeof(errstr));
|
2009-06-24 13:39:42 +00:00
|
|
|
ereport(LOG,
|
2008-11-28 14:26:58 +00:00
|
|
|
(errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
|
2010-05-26 16:43:13 +00:00
|
|
|
errmsg("invalid regular expression \"%s\": %s",
|
|
|
|
file_ident_user + 1, errstr)));
|
2008-11-28 14:26:58 +00:00
|
|
|
|
|
|
|
pfree(wstr);
|
|
|
|
*error_p = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
pfree(wstr);
|
|
|
|
|
|
|
|
wstr = palloc((strlen(ident_user) + 1) * sizeof(pg_wchar));
|
|
|
|
wlen = pg_mb2wchar_with_len(ident_user, wstr, strlen(ident_user));
|
|
|
|
|
2009-06-11 14:49:15 +00:00
|
|
|
r = pg_regexec(&re, wstr, wlen, 0, NULL, 2, matches, 0);
|
2008-11-28 14:26:58 +00:00
|
|
|
if (r)
|
|
|
|
{
|
2009-06-11 14:49:15 +00:00
|
|
|
char errstr[100];
|
2008-11-28 14:26:58 +00:00
|
|
|
|
|
|
|
if (r != REG_NOMATCH)
|
|
|
|
{
|
|
|
|
/* REG_NOMATCH is not an error, everything else is */
|
|
|
|
pg_regerror(r, &re, errstr, sizeof(errstr));
|
2009-06-24 13:39:42 +00:00
|
|
|
ereport(LOG,
|
2008-11-28 14:26:58 +00:00
|
|
|
(errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
|
2010-07-06 19:19:02 +00:00
|
|
|
errmsg("regular expression match for \"%s\" failed: %s",
|
|
|
|
file_ident_user + 1, errstr)));
|
2008-11-28 14:26:58 +00:00
|
|
|
*error_p = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
pfree(wstr);
|
|
|
|
pg_regfree(&re);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
pfree(wstr);
|
|
|
|
|
|
|
|
if ((ofs = strstr(file_pgrole, "\\1")) != NULL)
|
|
|
|
{
|
|
|
|
/* substitution of the first argument requested */
|
|
|
|
if (matches[1].rm_so < 0)
|
2009-06-24 13:39:42 +00:00
|
|
|
{
|
|
|
|
ereport(LOG,
|
2008-11-28 14:26:58 +00:00
|
|
|
(errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
|
2009-03-25 14:12:02 +00:00
|
|
|
errmsg("regular expression \"%s\" has no subexpressions as requested by backreference in \"%s\"",
|
2009-06-11 14:49:15 +00:00
|
|
|
file_ident_user + 1, file_pgrole)));
|
2009-06-24 13:39:42 +00:00
|
|
|
pg_regfree(&re);
|
|
|
|
*error_p = true;
|
|
|
|
return;
|
|
|
|
}
|
2009-06-11 14:49:15 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* length: original length minus length of \1 plus length of match
|
|
|
|
* plus null terminator
|
|
|
|
*/
|
|
|
|
regexp_pgrole = palloc0(strlen(file_pgrole) - 2 + (matches[1].rm_eo - matches[1].rm_so) + 1);
|
|
|
|
strncpy(regexp_pgrole, file_pgrole, (ofs - file_pgrole));
|
|
|
|
memcpy(regexp_pgrole + strlen(regexp_pgrole),
|
|
|
|
ident_user + matches[1].rm_so,
|
|
|
|
matches[1].rm_eo - matches[1].rm_so);
|
|
|
|
strcat(regexp_pgrole, ofs + 2);
|
2008-11-28 14:26:58 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* no substitution, so copy the match */
|
|
|
|
regexp_pgrole = pstrdup(file_pgrole);
|
|
|
|
}
|
|
|
|
|
|
|
|
pg_regfree(&re);
|
|
|
|
|
2009-06-11 14:49:15 +00:00
|
|
|
/*
|
|
|
|
* now check if the username actually matched what the user is trying
|
|
|
|
* to connect as
|
|
|
|
*/
|
2008-11-28 14:26:58 +00:00
|
|
|
if (case_insensitive)
|
|
|
|
{
|
|
|
|
if (pg_strcasecmp(regexp_pgrole, pg_role) == 0)
|
|
|
|
*found_p = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (strcmp(regexp_pgrole, pg_role) == 0)
|
|
|
|
*found_p = true;
|
|
|
|
}
|
|
|
|
pfree(regexp_pgrole);
|
|
|
|
|
|
|
|
return;
|
2008-10-23 13:31:10 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2008-11-28 14:26:58 +00:00
|
|
|
/* Not regular expression, so make complete match */
|
|
|
|
if (case_insensitive)
|
|
|
|
{
|
|
|
|
if (pg_strcasecmp(file_pgrole, pg_role) == 0 &&
|
|
|
|
pg_strcasecmp(file_ident_user, ident_user) == 0)
|
|
|
|
*found_p = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (strcmp(file_pgrole, pg_role) == 0 &&
|
|
|
|
strcmp(file_ident_user, ident_user) == 0)
|
|
|
|
*found_p = true;
|
|
|
|
}
|
2008-10-23 13:31:10 +00:00
|
|
|
}
|
2001-07-30 14:50:24 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2001-10-25 05:50:21 +00:00
|
|
|
* Scan the (pre-parsed) ident usermap file line by line, looking for a match
|
2001-07-31 22:55:45 +00:00
|
|
|
*
|
2010-03-24 17:05:45 +00:00
|
|
|
* See if the user with ident username "auth_user" is allowed to act
|
|
|
|
* as Postgres user "pg_role" according to usermap "usermap_name".
|
2001-07-31 22:55:45 +00:00
|
|
|
*
|
2009-06-11 14:49:15 +00:00
|
|
|
* Special case: Usermap NULL, equivalent to what was previously called
|
2010-03-24 17:05:45 +00:00
|
|
|
* "sameuser" or "samerole", means don't look in the usermap file.
|
|
|
|
* That's an implied map wherein "pg_role" must be identical to
|
|
|
|
* "auth_user" in order to be authorized.
|
2001-07-31 22:55:45 +00:00
|
|
|
*
|
2008-10-23 13:31:10 +00:00
|
|
|
* Iff authorized, return STATUS_OK, otherwise return STATUS_ERROR.
|
2001-07-30 14:50:24 +00:00
|
|
|
*/
|
2008-10-23 13:31:10 +00:00
|
|
|
int
|
|
|
|
check_usermap(const char *usermap_name,
|
2009-06-11 14:49:15 +00:00
|
|
|
const char *pg_role,
|
|
|
|
const char *auth_user,
|
|
|
|
bool case_insensitive)
|
2001-07-30 14:50:24 +00:00
|
|
|
{
|
2001-10-25 05:50:21 +00:00
|
|
|
bool found_entry = false,
|
|
|
|
error = false;
|
2001-07-30 14:50:24 +00:00
|
|
|
|
2003-04-17 22:26:02 +00:00
|
|
|
if (usermap_name == NULL || usermap_name[0] == '\0')
|
2001-07-30 14:50:24 +00:00
|
|
|
{
|
2008-10-23 13:31:10 +00:00
|
|
|
if (case_insensitive)
|
|
|
|
{
|
|
|
|
if (pg_strcasecmp(pg_role, auth_user) == 0)
|
|
|
|
return STATUS_OK;
|
|
|
|
}
|
2009-06-11 14:49:15 +00:00
|
|
|
else
|
|
|
|
{
|
2008-10-23 13:31:10 +00:00
|
|
|
if (strcmp(pg_role, auth_user) == 0)
|
|
|
|
return STATUS_OK;
|
|
|
|
}
|
|
|
|
ereport(LOG,
|
2010-03-21 00:17:59 +00:00
|
|
|
(errmsg("provided user name (%s) and authenticated user name (%s) do not match",
|
2010-03-24 17:05:45 +00:00
|
|
|
pg_role, auth_user)));
|
2008-10-23 13:31:10 +00:00
|
|
|
return STATUS_ERROR;
|
2001-07-30 14:50:24 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2004-08-29 05:07:03 +00:00
|
|
|
ListCell *line_cell,
|
|
|
|
*num_cell;
|
2004-05-26 04:41:50 +00:00
|
|
|
|
|
|
|
forboth(line_cell, ident_lines, num_cell, ident_line_nums)
|
2001-07-30 14:50:24 +00:00
|
|
|
{
|
2004-05-26 04:41:50 +00:00
|
|
|
parse_ident_usermap(lfirst(line_cell), lfirst_int(num_cell),
|
2009-06-11 14:49:15 +00:00
|
|
|
usermap_name, pg_role, auth_user, case_insensitive,
|
2004-05-26 04:41:50 +00:00
|
|
|
&found_entry, &error);
|
2001-07-30 14:50:24 +00:00
|
|
|
if (found_entry || error)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2008-10-23 13:31:10 +00:00
|
|
|
if (!found_entry && !error)
|
|
|
|
{
|
|
|
|
ereport(LOG,
|
2010-05-26 16:43:13 +00:00
|
|
|
(errmsg("no match in usermap \"%s\" for user \"%s\" authenticated as \"%s\"",
|
|
|
|
usermap_name, pg_role, auth_user)));
|
2008-10-23 13:31:10 +00:00
|
|
|
}
|
2009-06-11 14:49:15 +00:00
|
|
|
return found_entry ? STATUS_OK : STATUS_ERROR;
|
2001-07-30 14:50:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2011-06-20 17:20:14 -04:00
|
|
|
* Read the ident config file and populate ident_lines and ident_line_nums.
|
|
|
|
*
|
|
|
|
* Like parsed_hba_lines, ident_lines is a triple-nested List of lines, fields
|
|
|
|
* and tokens.
|
2001-07-30 14:50:24 +00:00
|
|
|
*/
|
2002-04-04 04:25:54 +00:00
|
|
|
void
|
2001-07-31 22:55:45 +00:00
|
|
|
load_ident(void)
|
1997-09-07 05:04:48 +00:00
|
|
|
{
|
2004-10-09 23:13:22 +00:00
|
|
|
FILE *file;
|
2004-08-29 05:07:03 +00:00
|
|
|
|
2011-06-20 17:20:14 -04:00
|
|
|
if (ident_context != NULL)
|
|
|
|
{
|
|
|
|
MemoryContextDelete(ident_context);
|
|
|
|
ident_context = NULL;
|
|
|
|
}
|
2001-07-30 14:50:24 +00:00
|
|
|
|
2004-10-09 23:13:22 +00:00
|
|
|
file = AllocateFile(IdentFileName, "r");
|
2001-07-30 14:50:24 +00:00
|
|
|
if (file == NULL)
|
|
|
|
{
|
2004-10-09 23:13:22 +00:00
|
|
|
/* not fatal ... we just won't do any special ident maps */
|
2003-07-22 19:00:12 +00:00
|
|
|
ereport(LOG,
|
|
|
|
(errcode_for_file_access(),
|
2010-05-26 16:43:13 +00:00
|
|
|
errmsg("could not open usermap file \"%s\": %m",
|
2004-10-09 23:13:22 +00:00
|
|
|
IdentFileName)));
|
2001-07-30 14:50:24 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2011-06-20 17:20:14 -04:00
|
|
|
ident_context = tokenize_file(IdentFileName, file, &ident_lines,
|
|
|
|
&ident_line_nums);
|
2001-07-30 14:50:24 +00:00
|
|
|
FreeFile(file);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1996-10-12 07:47:12 +00:00
|
|
|
|
2001-07-30 14:50:24 +00:00
|
|
|
/*
|
2001-10-25 05:50:21 +00: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-07-31 22:55:45 +00:00
|
|
|
*
|
2011-06-20 17:20:14 -04:00
|
|
|
* If the file does not contain any entry matching the request, we return
|
|
|
|
* method = uaImplicitReject.
|
2001-07-30 14:50:24 +00:00
|
|
|
*/
|
2011-06-20 17:20:14 -04:00
|
|
|
void
|
2001-07-30 14:50:24 +00:00
|
|
|
hba_getauthmethod(hbaPort *port)
|
1997-09-07 05:04:48 +00:00
|
|
|
{
|
2011-06-20 17:20:14 -04:00
|
|
|
check_hba(port);
|
2001-07-30 14:50:24 +00:00
|
|
|
}
|