diff options
-rw-r--r-- | doc/src/sgml/client-auth.sgml | 18 | ||||
-rw-r--r-- | src/backend/libpq/auth.c | 26 | ||||
-rw-r--r-- | src/backend/libpq/crypt.c | 8 | ||||
-rw-r--r-- | src/backend/libpq/hba.c | 542 | ||||
-rw-r--r-- | src/backend/libpq/pg_ident.conf.sample | 3 | ||||
-rw-r--r-- | src/backend/postmaster/postmaster.c | 27 | ||||
-rw-r--r-- | src/include/libpq/hba.h | 26 | ||||
-rw-r--r-- | src/include/libpq/libpq-be.h | 5 |
8 files changed, 394 insertions, 261 deletions
diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml index e5f56e55d94..828b5e2caec 100644 --- a/doc/src/sgml/client-auth.sgml +++ b/doc/src/sgml/client-auth.sgml @@ -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 diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c index 1c50b8e5882..f01be3b18f0 100644 --- a/src/backend/libpq/auth.c +++ b/src/backend/libpq/auth.c @@ -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; } diff --git a/src/backend/libpq/crypt.c b/src/backend/libpq/crypt.c index 546806994d7..ab237ad3b11 100644 --- a/src/backend/libpq/crypt.c +++ b/src/backend/libpq/crypt.c @@ -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); diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c index c552a4b2542..a511ee293f6 100644 --- a/src/backend/libpq/hba.c +++ b/src/backend/libpq/hba.c @@ -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. - */ -static void -parse_hba_auth(ListCell **line_item, UserAuth *userauth_p, - char **auth_arg_p, bool *error_p) -{ - 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. + * Parse one line in the hba config file and store the result in + * a HbaLine structure. */ -static void -parse_hba(List *line, int line_num, hbaPort *port, - bool *found_p, bool *error_p) +static bool +parse_hba_line(List *line, int line_num, HbaLine *parsedline) { 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 */ + + /* Get the authentication method */ + line_item = lnext(line_item); + if (!line_item) + goto hba_syntax; + token = lfirst(line_item); - if (addr.ss_family != port->raddr.addr.ss_family) + 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. * @@ -1116,14 +1220,6 @@ check_ident_usermap(const char *usermap_name, 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; else diff --git a/src/backend/libpq/pg_ident.conf.sample b/src/backend/libpq/pg_ident.conf.sample index 4019f6fef13..6f0de64910b 100644 --- a/src/backend/libpq/pg_ident.conf.sample +++ b/src/backend/libpq/pg_ident.conf.sample @@ -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 diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index 8bc95f1f2f7..72b9387767a 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -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 diff --git a/src/include/libpq/hba.h b/src/include/libpq/hba.h index 16150846622..3af7db9e626 100644 --- a/src/include/libpq/hba.h +++ b/src/include/libpq/hba.h @@ -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); diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h index 6927da2fd08..4d5e0039c89 100644 --- a/src/include/libpq/libpq-be.h +++ b/src/include/libpq/libpq-be.h @@ -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 */ |