Parse pg_hba.conf in postmaster, instead of once in each backend for

each connection. This makes it possible to catch errors in the pg_hba
file when it's being reloaded, instead of silently reloading a broken
file and failing only when a user tries to connect.

This patch also makes the "sameuser" argument to ident authentication
optional.
This commit is contained in:
Magnus Hagander 2008-09-15 12:32:57 +00:00
parent b850cf61bd
commit 9872381090
8 changed files with 394 additions and 261 deletions

View File

@ -1,4 +1,4 @@
<!-- $PostgreSQL: pgsql/doc/src/sgml/client-auth.sgml,v 1.106 2008/01/05 13:17:00 petere Exp $ -->
<!-- $PostgreSQL: pgsql/doc/src/sgml/client-auth.sgml,v 1.107 2008/09/15 12:32:56 mha Exp $ -->
<chapter id="client-authentication">
<title>Client Authentication</title>
@ -509,7 +509,7 @@ host all all 127.0.0.1 255.255.255.255 trust
# the connection (typically the Unix user name).
#
# TYPE DATABASE USER CIDR-ADDRESS METHOD
host postgres all 192.168.93.0/24 ident sameuser
host postgres all 192.168.93.0/24 ident
# Allow a user from host 192.168.12.10 to connect to database
# "postgres" if the user's password is correctly supplied.
@ -839,8 +839,8 @@ local db1,db2,@demodbs all md5
<para>
The ident authentication method works by obtaining the client's
operating system user name, then determining the allowed database
user names using a map file that lists the permitted
operating system user name, then optionally determining the allowed
database user names using a map file that lists the permitted
corresponding pairs of names. The determination of the client's
user name is the security-critical point, and it works differently
depending on the connection type.
@ -928,15 +928,13 @@ local db1,db2,@demodbs all md5
allowed to connect as the database user he is requesting to connect
as. This is controlled by the ident map argument that follows the
<literal>ident</> key word in the <filename>pg_hba.conf</filename>
file. There is a predefined ident map <literal>sameuser</literal>,
which allows any operating system user to connect as the database
user of the same name (if the latter exists). Other maps must be
created manually.
file. If an ident map is not specified, the database user will be
checked with the same name as the operating system user. Other maps
must be created manually.
</para>
<para>
Ident maps other than <literal>sameuser</literal> are defined in the
ident map file, which by default is named
Ident maps are defined in the ident map file, which by default is named
<filename>pg_ident.conf</><indexterm><primary>pg_ident.conf</primary></indexterm>
and is stored in the
cluster's data directory. (It is possible to place the map file

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/libpq/auth.c,v 1.167 2008/08/01 11:41:12 mha Exp $
* $PostgreSQL: pgsql/src/backend/libpq/auth.c,v 1.168 2008/09/15 12:32:56 mha Exp $
*
*-------------------------------------------------------------------------
*/
@ -211,7 +211,7 @@ auth_failed(Port *port, int status)
if (status == STATUS_EOF)
proc_exit(0);
switch (port->auth_method)
switch (port->hba->auth_method)
{
case uaReject:
errstr = gettext_noop("authentication failed for user \"%s\": host rejected");
@ -279,7 +279,7 @@ ClientAuthentication(Port *port)
errmsg("missing or erroneous pg_hba.conf file"),
errhint("See server log for details.")));
switch (port->auth_method)
switch (port->hba->auth_method)
{
case uaReject:
@ -1761,7 +1761,7 @@ ident_unix(int sock, char *ident_user)
/*
* Determine the username of the initiator of the connection described
* by "port". Then look in the usermap file under the usermap
* port->auth_arg and see if that user is equivalent to Postgres user
* port->hba->usermap and see if that user is equivalent to Postgres user
* port->user.
*
* Return STATUS_OK if yes, STATUS_ERROR if no match (or couldn't get info).
@ -1799,7 +1799,7 @@ authident(hbaPort *port)
(errmsg("Ident protocol identifies remote user as \"%s\"",
ident_user)));
if (check_ident_usermap(port->auth_arg, port->user_name, ident_user))
if (check_ident_usermap(port->hba->usermap, port->user_name, ident_user))
return STATUS_OK;
else
return STATUS_ERROR;
@ -1913,8 +1913,8 @@ CheckPAMAuth(Port *port, char *user, char *password)
* not allocated */
/* Optionally, one can set the service name in pg_hba.conf */
if (port->auth_arg && port->auth_arg[0] != '\0')
retval = pam_start(port->auth_arg, "pgsql@",
if (port->hba->auth_arg && port->hba->auth_arg[0] != '\0')
retval = pam_start(port->hba->auth_arg, "pgsql@",
&pam_passw_conv, &pamh);
else
retval = pam_start(PGSQL_PAM_SERVICE, "pgsql@",
@ -2011,7 +2011,7 @@ CheckLDAPAuth(Port *port)
int ldapport = LDAP_PORT;
char fulluser[NAMEDATALEN + 256 + 1];
if (!port->auth_arg || port->auth_arg[0] == '\0')
if (!port->hba->auth_arg || port->hba->auth_arg[0] == '\0')
{
ereport(LOG,
(errmsg("LDAP configuration URL not specified")));
@ -2035,13 +2035,13 @@ CheckLDAPAuth(Port *port)
suffix[0] = '\0';
/* ldap, including port number */
r = sscanf(port->auth_arg,
r = sscanf(port->hba->auth_arg,
"ldap://%127[^:]:%d/%127[^;];%127[^;];%127[^\n]",
server, &ldapport, basedn, prefix, suffix);
if (r < 3)
{
/* ldaps, including port number */
r = sscanf(port->auth_arg,
r = sscanf(port->hba->auth_arg,
"ldaps://%127[^:]:%d/%127[^;];%127[^;];%127[^\n]",
server, &ldapport, basedn, prefix, suffix);
if (r >= 3)
@ -2050,14 +2050,14 @@ CheckLDAPAuth(Port *port)
if (r < 3)
{
/* ldap, no port number */
r = sscanf(port->auth_arg,
r = sscanf(port->hba->auth_arg,
"ldap://%127[^/]/%127[^;];%127[^;];%127[^\n]",
server, basedn, prefix, suffix);
}
if (r < 2)
{
/* ldaps, no port number */
r = sscanf(port->auth_arg,
r = sscanf(port->hba->auth_arg,
"ldaps://%127[^/]/%127[^;];%127[^;];%127[^\n]",
server, basedn, prefix, suffix);
if (r >= 2)
@ -2067,7 +2067,7 @@ CheckLDAPAuth(Port *port)
{
ereport(LOG,
(errmsg("invalid LDAP URL: \"%s\"",
port->auth_arg)));
port->hba->auth_arg)));
return STATUS_ERROR;
}

View File

@ -9,7 +9,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/backend/libpq/crypt.c,v 1.74 2008/01/01 19:45:49 momjian Exp $
* $PostgreSQL: pgsql/src/backend/libpq/crypt.c,v 1.75 2008/09/15 12:32:56 mha Exp $
*
*-------------------------------------------------------------------------
*/
@ -54,7 +54,7 @@ md5_crypt_verify(const Port *port, const char *role, char *client_pass)
return STATUS_ERROR;
/* We can't do crypt with MD5 passwords */
if (isMD5(shadow_pass) && port->auth_method == uaCrypt)
if (isMD5(shadow_pass) && port->hba->auth_method == uaCrypt)
{
ereport(LOG,
(errmsg("cannot use authentication method \"crypt\" because password is MD5-encrypted")));
@ -65,7 +65,7 @@ md5_crypt_verify(const Port *port, const char *role, char *client_pass)
* Compare with the encrypted or plain password depending on the
* authentication method being used for this connection.
*/
switch (port->auth_method)
switch (port->hba->auth_method)
{
case uaMD5:
crypt_pwd = palloc(MD5_PASSWD_LEN + 1);
@ -155,7 +155,7 @@ md5_crypt_verify(const Port *port, const char *role, char *client_pass)
}
}
if (port->auth_method == uaMD5)
if (port->hba->auth_method == uaMD5)
pfree(crypt_pwd);
if (crypt_client_pass != client_pass)
pfree(crypt_client_pass);

View File

@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.166 2008/08/01 09:09:49 mha Exp $
* $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.167 2008/09/15 12:32:56 mha Exp $
*
*-------------------------------------------------------------------------
*/
@ -41,8 +41,11 @@
#define MAX_TOKEN 256
/* pre-parsed content of HBA config file */
static List *parsed_hba_lines = NIL;
/*
* These variables hold the pre-parsed contents of the hba and ident
* These variables hold the pre-parsed contents of the ident
* configuration files, as well as the flat auth file.
* Each is a list of sublists, one sublist for
* each (non-empty, non-comment) line of the file. Each sublist's
@ -52,10 +55,6 @@
* one token, since blank lines are not entered in the data structure.
*/
/* pre-parsed content of HBA config file and corresponding line #s */
static List *hba_lines = NIL;
static List *hba_line_nums = NIL;
/* pre-parsed content of ident usermap file and corresponding line #s */
static List *ident_lines = NIL;
static List *ident_line_nums = NIL;
@ -566,131 +565,29 @@ check_db(const char *dbname, const char *role, char *param_str)
/*
* Scan the rest of a host record (after the mask field)
* and return the interpretation of it as *userauth_p, *auth_arg_p, and
* *error_p. *line_item points to the next token of the line, and is
* advanced over successfully-read tokens.
* Parse one line in the hba config file and store the result in
* a HbaLine structure.
*/
static void
parse_hba_auth(ListCell **line_item, UserAuth *userauth_p,
char **auth_arg_p, bool *error_p)
static bool
parse_hba_line(List *line, int line_num, HbaLine *parsedline)
{
char *token;
*auth_arg_p = NULL;
if (!*line_item)
{
*error_p = true;
return;
}
token = lfirst(*line_item);
if (strcmp(token, "trust") == 0)
*userauth_p = uaTrust;
else if (strcmp(token, "ident") == 0)
*userauth_p = uaIdent;
else if (strcmp(token, "password") == 0)
*userauth_p = uaPassword;
else if (strcmp(token, "krb5") == 0)
*userauth_p = uaKrb5;
else if (strcmp(token, "gss") == 0)
*userauth_p = uaGSS;
else if (strcmp(token, "sspi") == 0)
*userauth_p = uaSSPI;
else if (strcmp(token, "reject") == 0)
*userauth_p = uaReject;
else if (strcmp(token, "md5") == 0)
*userauth_p = uaMD5;
else if (strcmp(token, "crypt") == 0)
*userauth_p = uaCrypt;
#ifdef USE_PAM
else if (strcmp(token, "pam") == 0)
*userauth_p = uaPAM;
#endif
#ifdef USE_LDAP
else if (strcmp(token, "ldap") == 0)
*userauth_p = uaLDAP;
#endif
else
{
*error_p = true;
return;
}
*line_item = lnext(*line_item);
/* Get the authentication argument token, if any */
if (*line_item)
{
token = lfirst(*line_item);
*auth_arg_p = pstrdup(token);
*line_item = lnext(*line_item);
/* If there is more on the line, it is an error */
if (*line_item)
*error_p = true;
}
}
/*
* Process one line from the hba config file.
*
* See if it applies to a connection from a host with IP address port->raddr
* to a database named port->database. If so, return *found_p true
* and fill in the auth arguments into the appropriate port fields.
* If not, leave *found_p as it was. If the record has a syntax error,
* return *error_p true, after issuing a message to the log. If no error,
* leave *error_p as it was.
*/
static void
parse_hba(List *line, int line_num, hbaPort *port,
bool *found_p, bool *error_p)
{
char *token;
char *db;
char *role;
struct addrinfo *gai_result;
struct addrinfo hints;
int ret;
struct sockaddr_storage addr;
struct sockaddr_storage mask;
char *cidr_slash;
char *unsupauth;
ListCell *line_item;
line_item = list_head(line);
parsedline->linenumber = line_num;
/* Check the record type. */
token = lfirst(line_item);
if (strcmp(token, "local") == 0)
{
/* Get the database. */
line_item = lnext(line_item);
if (!line_item)
goto hba_syntax;
db = lfirst(line_item);
/* Get the role. */
line_item = lnext(line_item);
if (!line_item)
goto hba_syntax;
role = lfirst(line_item);
line_item = lnext(line_item);
if (!line_item)
goto hba_syntax;
/* Read the rest of the line. */
parse_hba_auth(&line_item, &port->auth_method,
&port->auth_arg, error_p);
if (*error_p)
goto hba_syntax;
/* Disallow auth methods that always need TCP/IP sockets to work */
if (port->auth_method == uaKrb5)
goto hba_syntax;
/* Does not match if connection isn't AF_UNIX */
if (!IS_AF_UNIX(port->raddr.addr.ss_family))
return;
parsedline->conntype = ctLocal;
}
else if (strcmp(token, "host") == 0
|| strcmp(token, "hostssl") == 0
@ -700,14 +597,7 @@ parse_hba(List *line, int line_num, hbaPort *port,
if (token[4] == 's') /* "hostssl" */
{
#ifdef USE_SSL
/* Record does not match if we are not on an SSL connection */
if (!port->ssl)
return;
/* Placeholder to require specific SSL level, perhaps? */
/* Or a client certificate */
/* Since we were on SSL, proceed as with normal 'host' mode */
parsedline->conntype = ctHostSSL;
#else
/* We don't accept this keyword at all if no SSL support */
goto hba_syntax;
@ -716,29 +606,37 @@ parse_hba(List *line, int line_num, hbaPort *port,
#ifdef USE_SSL
else if (token[4] == 'n') /* "hostnossl" */
{
/* Record does not match if we are on an SSL connection */
if (port->ssl)
return;
parsedline->conntype = ctHostNoSSL;
}
#endif
else
{
/* "host", or "hostnossl" and SSL support not built in */
parsedline->conntype = ctHost;
}
} /* record type */
else
goto hba_syntax;
/* Get the database. */
line_item = lnext(line_item);
if (!line_item)
goto hba_syntax;
db = lfirst(line_item);
/* Get the database. */
line_item = lnext(line_item);
if (!line_item)
goto hba_syntax;
parsedline->database = pstrdup(lfirst(line_item));
/* Get the role. */
line_item = lnext(line_item);
if (!line_item)
goto hba_syntax;
role = lfirst(line_item);
/* Get the role. */
line_item = lnext(line_item);
if (!line_item)
goto hba_syntax;
parsedline->role = pstrdup(lfirst(line_item));
if (parsedline->conntype != ctLocal)
{
/* Read the IP address field. (with or without CIDR netmask) */
line_item = lnext(line_item);
if (!line_item)
goto hba_syntax;
token = lfirst(line_item);
token = pstrdup(lfirst(line_item));
/* Check if it has a CIDR suffix and if so isolate it */
cidr_slash = strchr(token, '/');
@ -760,9 +658,10 @@ parse_hba(List *line, int line_num, hbaPort *port,
{
ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("invalid IP address \"%s\" in file \"%s\" line %d: %s",
token, HbaFileName, line_num,
gai_strerror(ret))));
errmsg("invalid IP address \"%s\": %s",
token, gai_strerror(ret)),
errdetail("In file \"%s\", line %d",
HbaFileName, line_num)));
if (cidr_slash)
*cidr_slash = '/';
if (gai_result)
@ -773,14 +672,14 @@ parse_hba(List *line, int line_num, hbaPort *port,
if (cidr_slash)
*cidr_slash = '/';
memcpy(&addr, gai_result->ai_addr, gai_result->ai_addrlen);
memcpy(&parsedline->addr, gai_result->ai_addr, gai_result->ai_addrlen);
pg_freeaddrinfo_all(hints.ai_family, gai_result);
/* Get the netmask */
if (cidr_slash)
{
if (pg_sockaddr_cidr_mask(&mask, cidr_slash + 1,
addr.ss_family) < 0)
if (pg_sockaddr_cidr_mask(&parsedline->mask, cidr_slash + 1,
parsedline->addr.ss_family) < 0)
goto hba_syntax;
}
else
@ -796,18 +695,19 @@ parse_hba(List *line, int line_num, hbaPort *port,
{
ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("invalid IP mask \"%s\" in file \"%s\" line %d: %s",
token, HbaFileName, line_num,
gai_strerror(ret))));
errmsg("invalid IP mask \"%s\": %s",
token, gai_strerror(ret)),
errdetail("In file \"%s\", line %d",
HbaFileName, line_num)));
if (gai_result)
pg_freeaddrinfo_all(hints.ai_family, gai_result);
goto hba_other_error;
}
memcpy(&mask, gai_result->ai_addr, gai_result->ai_addrlen);
memcpy(&parsedline->mask, gai_result->ai_addr, gai_result->ai_addrlen);
pg_freeaddrinfo_all(hints.ai_family, gai_result);
if (addr.ss_family != mask.ss_family)
if (parsedline->addr.ss_family != parsedline->mask.ss_family)
{
ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
@ -816,62 +716,133 @@ parse_hba(List *line, int line_num, hbaPort *port,
goto hba_other_error;
}
}
} /* != ctLocal */
if (addr.ss_family != port->raddr.addr.ss_family)
/* Get the authentication method */
line_item = lnext(line_item);
if (!line_item)
goto hba_syntax;
token = lfirst(line_item);
unsupauth = NULL;
if (strcmp(token, "trust") == 0)
parsedline->auth_method = uaTrust;
else if (strcmp(token, "ident") == 0)
parsedline->auth_method = uaIdent;
else if (strcmp(token, "password") == 0)
parsedline->auth_method = uaPassword;
else if (strcmp(token, "krb5") == 0)
#ifdef KRB5
parsedline->auth_method = uaKrb5;
#else
unsupauth = "krb5";
#endif
else if (strcmp(token, "gss") == 0)
#ifdef ENABLE_GSS
parsedline->auth_method = uaGSS;
#else
unsupauth = "gss";
#endif
else if (strcmp(token, "sspi") == 0)
#ifdef ENABLE_SSPI
parsedline->auth_method = uaSSPI;
#else
unsupauth = "sspi";
#endif
else if (strcmp(token, "reject") == 0)
parsedline->auth_method = uaReject;
else if (strcmp(token, "md5") == 0)
parsedline->auth_method = uaMD5;
else if (strcmp(token, "crypt") == 0)
parsedline->auth_method = uaCrypt;
else if (strcmp(token, "pam") == 0)
#ifdef USE_PAM
parsedline->auth_method = uaPAM;
#else
unsupauth = "pam";
#endif
else if (strcmp(token, "ldap") == 0)
#ifdef USE_LDAP
parsedline->auth_method = uaLDAP;
#else
unsupauth = "ldap";
#endif
else
{
ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("invalid authentication method \"%s\"",
token),
errdetail("In file \"%s\" line %d",
HbaFileName, line_num)));
goto hba_other_error;
}
if (unsupauth)
{
ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("invalid authentication method \"%s\": not supported on this platform",
token),
errdetail("In file \"%s\" line %d",
HbaFileName, line_num)));
goto hba_other_error;
}
/* Invalid authentication combinations */
if (parsedline->conntype == ctLocal &&
parsedline->auth_method == uaKrb5)
{
ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("krb5 authentication is not supported on local sockets"),
errdetail("In file \"%s\" line %d",
HbaFileName, line_num)));
goto hba_other_error;
}
/* Get the authentication argument token, if any */
line_item = lnext(line_item);
if (line_item)
{
token = lfirst(line_item);
parsedline->auth_arg= pstrdup(token);
}
/*
* Backwards compatible format of ident authentication - support "naked" ident map
* name, as well as "sameuser"/"samerole"
*/
if (parsedline->auth_method == uaIdent)
{
if (parsedline->auth_arg && strlen(parsedline->auth_arg))
{
/*
* Wrong address family. We allow only one case: if the file has
* IPv4 and the port is IPv6, promote the file address to IPv6 and
* try to match that way.
*/
#ifdef HAVE_IPV6
if (addr.ss_family == AF_INET &&
port->raddr.addr.ss_family == AF_INET6)
if (strcmp(parsedline->auth_arg, "sameuser\n") == 0 ||
strcmp(parsedline->auth_arg, "samerole\n") == 0)
{
pg_promote_v4_to_v6_addr(&addr);
pg_promote_v4_to_v6_mask(&mask);
/* This is now the default */
pfree(parsedline->auth_arg);
parsedline->auth_arg = NULL;
parsedline->usermap = NULL;
}
else
#endif /* HAVE_IPV6 */
{
/* Line doesn't match client port, so ignore it. */
return;
/* Specific ident map specified */
parsedline->usermap = parsedline->auth_arg;
parsedline->auth_arg = NULL;
}
}
/* Ignore line if client port is not in the matching addr range. */
if (!pg_range_sockaddr(&port->raddr.addr, &addr, &mask))
return;
/* Read the rest of the line. */
line_item = lnext(line_item);
if (!line_item)
goto hba_syntax;
parse_hba_auth(&line_item, &port->auth_method,
&port->auth_arg, error_p);
if (*error_p)
goto hba_syntax;
}
else
goto hba_syntax;
/* Does the entry match database and role? */
if (!check_db(port->database_name, port->user_name, db))
return;
if (!check_role(port->user_name, role))
return;
/* Success */
*found_p = true;
return;
return true;
hba_syntax:
if (line_item)
ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("invalid entry in file \"%s\" at line %d, token \"%s\"",
HbaFileName, line_num,
(char *) lfirst(line_item))));
errmsg("invalid entry in file \"%s\" at line %d, token \"%s\"",
HbaFileName, line_num,
(char *) lfirst(line_item))));
else
ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
@ -880,7 +851,7 @@ hba_syntax:
/* Come here if suitable message already logged */
hba_other_error:
*error_p = true;
return false;
}
@ -891,28 +862,96 @@ hba_other_error:
static bool
check_hba(hbaPort *port)
{
bool found_entry = false;
bool error = false;
ListCell *line;
ListCell *line_num;
HbaLine *hba;
forboth(line, hba_lines, line_num, hba_line_nums)
foreach(line, parsed_hba_lines)
{
parse_hba(lfirst(line), lfirst_int(line_num),
port, &found_entry, &error);
if (found_entry || error)
break;
}
hba = (HbaLine *) lfirst(line);
if (!error)
{
/* If no matching entry was found, synthesize 'reject' entry. */
if (!found_entry)
port->auth_method = uaReject;
/* 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 */
if (port->raddr.addr.ss_family == hba->addr.ss_family)
{
if (!pg_range_sockaddr(&port->raddr.addr, &hba->addr, &hba->mask))
continue;
}
#ifdef HAVE_IPV6
else if (hba->addr.ss_family == AF_INET &&
port->raddr.addr.ss_family == AF_INET6)
{
/*
* Wrong address family. We allow only one case: if the file has
* IPv4 and the port is IPv6, promote the file address to IPv6 and
* try to match that way.
*/
struct sockaddr_storage addrcopy, maskcopy;
memcpy(&addrcopy, &hba->addr, sizeof(addrcopy));
memcpy(&maskcopy, &hba->mask, sizeof(maskcopy));
pg_promote_v4_to_v6_addr(&addrcopy);
pg_promote_v4_to_v6_mask(&maskcopy);
if (!pg_range_sockaddr(&port->raddr.addr, &addrcopy, &maskcopy))
continue;
}
#endif /* HAVE_IPV6 */
else
/* Wrong address family, no IPV6 */
continue;
} /* != ctLocal */
/* Check database and role */
if (!check_db(port->database_name, port->user_name, hba->database))
continue;
if (!check_role(port->user_name, hba->role))
continue;
/* Found a record that matched! */
port->hba = hba;
return true;
}
else
return false;
/* If no matching entry was found, synthesize 'reject' entry. */
hba = palloc0(sizeof(HbaLine));
hba->auth_method = uaReject;
port->hba = hba;
return true;
/* XXX:
* Return false only happens if we have a parsing error, which we can
* no longer have (parsing now in postmaster). Consider changing API.
*/
}
@ -967,17 +1006,52 @@ load_role(void)
}
}
/*
* Free the contents of a hba record
*/
static void
free_hba_record(HbaLine *record)
{
if (record->database)
pfree(record->database);
if (record->role)
pfree(record->role);
if (record->auth_arg)
pfree(record->auth_arg);
}
/*
* Read the config file and create a List of Lists of tokens in the file.
* Free all records on the parsed HBA list
*/
void
static void
clean_hba_list(List *lines)
{
ListCell *line;
foreach(line, lines)
{
HbaLine *parsed = (HbaLine *)lfirst(line);
if (parsed)
free_hba_record(parsed);
}
list_free(lines);
}
/*
* Read the config file and create a List of HbaLine records for the contents.
*
* 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.
*/
bool
load_hba(void)
{
FILE *file;
if (hba_lines || hba_line_nums)
free_lines(&hba_lines, &hba_line_nums);
List *hba_lines = NIL;
List *hba_line_nums = NIL;
ListCell *line, *line_num;
List *new_parsed_lines = NIL;
file = AllocateFile(HbaFileName, "r");
/* Failure is fatal since with no HBA entries we can do nothing... */
@ -989,6 +1063,35 @@ load_hba(void)
tokenize_file(HbaFileName, file, &hba_lines, &hba_line_nums);
FreeFile(file);
/* Now parse all the lines */
forboth(line, hba_lines, line_num, hba_line_nums)
{
HbaLine *newline;
newline = palloc0(sizeof(HbaLine));
if (!parse_hba_line(lfirst(line), lfirst_int(line_num), newline))
{
/* Parse error in the file, so bail out */
free_hba_record(newline);
pfree(newline);
clean_hba_list(new_parsed_lines);
/* Error has already been reported in the parsing function */
return false;
}
new_parsed_lines = lappend(new_parsed_lines, newline);
}
/* Loaded new file successfully, replace the one we use */
clean_hba_list(parsed_hba_lines);
parsed_hba_lines = new_parsed_lines;
/* Free the temporary lists */
free_lines(&hba_lines, &hba_line_nums);
return true;
}
/*
@ -1100,7 +1203,8 @@ ident_syntax:
* See if the user with ident username "ident_user" is allowed to act
* as Postgres user "pgrole" according to usermap "usermap_name".
*
* Special case: For usermap "samerole", don't look in the usermap
* Special case: Usermap NULL, equivalent to what was previously called
* "sameuser" or "samerole", don't look in the usermap
* file. That's an implied map where "pgrole" must be identical to
* "ident_user" in order to be authorized.
*
@ -1115,14 +1219,6 @@ check_ident_usermap(const char *usermap_name,
error = false;
if (usermap_name == NULL || usermap_name[0] == '\0')
{
ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("cannot use Ident authentication without usermap field")));
found_entry = false;
}
else if (strcmp(usermap_name, "sameuser\n") == 0 ||
strcmp(usermap_name, "samerole\n") == 0)
{
if (strcmp(pg_role, ident_user) == 0)
found_entry = true;

View File

@ -30,7 +30,6 @@
#
# No map names are defined in the default configuration. If all ident
# user names and PostgreSQL user names are the same, you don't need
# this file. Instead, use the special map name "sameuser" in
# pg_hba.conf.
# this file.
# MAPNAME IDENT-USERNAME PG-USERNAME

View File

@ -37,7 +37,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.562 2008/08/25 15:11:01 mha Exp $
* $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.563 2008/09/15 12:32:57 mha Exp $
*
* NOTES
*
@ -888,7 +888,15 @@ PostmasterMain(int argc, char *argv[])
/*
* Load configuration files for client authentication.
*/
load_hba();
if (!load_hba())
{
/*
* It makes no sense continue if we fail to load the HBA file, since
* there is no way to connect to the database in this case.
*/
ereport(FATAL,
(errmsg("could not load pg_hba.conf")));
}
load_ident();
/*
@ -1927,7 +1935,10 @@ SIGHUP_handler(SIGNAL_ARGS)
signal_child(PgStatPID, SIGHUP);
/* Reload authentication config files too */
load_hba();
if (!load_hba())
ereport(WARNING,
(errmsg("pg_hba.conf not reloaded")));
load_ident();
#ifdef EXEC_BACKEND
@ -3081,7 +3092,15 @@ BackendInitialize(Port *port)
ALLOCSET_DEFAULT_MAXSIZE);
MemoryContextSwitchTo(PostmasterContext);
load_hba();
if (!load_hba())
{
/*
* It makes no sense continue if we fail to load the HBA file, since
* there is no way to connect to the database in this case.
*/
ereport(FATAL,
(errmsg("could not load pg_hba.conf")));
}
load_ident();
load_role();
#endif

View File

@ -4,7 +4,7 @@
* Interface to hba.c
*
*
* $PostgreSQL: pgsql/src/include/libpq/hba.h,v 1.48 2008/08/01 09:09:48 mha Exp $
* $PostgreSQL: pgsql/src/include/libpq/hba.h,v 1.49 2008/09/15 12:32:57 mha Exp $
*
*-------------------------------------------------------------------------
*/
@ -12,6 +12,7 @@
#define HBA_H
#include "nodes/pg_list.h"
#include "libpq/pqcomm.h"
typedef enum UserAuth
@ -33,10 +34,31 @@ typedef enum UserAuth
#endif
} UserAuth;
typedef enum ConnType
{
ctLocal,
ctHost,
ctHostSSL,
ctHostNoSSL
} ConnType;
typedef struct
{
int linenumber;
ConnType conntype;
char *database;
char *role;
struct sockaddr_storage addr;
struct sockaddr_storage mask;
UserAuth auth_method;
char *usermap;
char *auth_arg;
} HbaLine;
typedef struct Port hbaPort;
extern List **get_role_line(const char *role);
extern void load_hba(void);
extern bool load_hba(void);
extern void load_ident(void);
extern void load_role(void);
extern int hba_getauthmethod(hbaPort *port);

View File

@ -11,7 +11,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/libpq/libpq-be.h,v 1.66 2008/04/26 22:47:40 tgl Exp $
* $PostgreSQL: pgsql/src/include/libpq/libpq-be.h,v 1.67 2008/09/15 12:32:57 mha Exp $
*
*-------------------------------------------------------------------------
*/
@ -121,8 +121,7 @@ typedef struct Port
/*
* Information that needs to be held during the authentication cycle.
*/
UserAuth auth_method;
char *auth_arg;
HbaLine *hba;
char md5Salt[4]; /* Password salt */
char cryptSalt[2]; /* Password salt */