Parse pg_hba.conf in postmaster, instead of once in each backend for
authorMagnus Hagander <magnus@hagander.net>
Mon, 15 Sep 2008 12:32:57 +0000 (12:32 +0000)
committerMagnus Hagander <magnus@hagander.net>
Mon, 15 Sep 2008 12:32:57 +0000 (12:32 +0000)
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.

doc/src/sgml/client-auth.sgml
src/backend/libpq/auth.c
src/backend/libpq/crypt.c
src/backend/libpq/hba.c
src/backend/libpq/pg_ident.conf.sample
src/backend/postmaster/postmaster.c
src/include/libpq/hba.h
src/include/libpq/libpq-be.h

index e5f56e55d94c95e1fc5c512e5278c20fea224b51..828b5e2caec137c996aa94007819f3f018707a42 100644 (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
index 1c50b8e588273259a571be936fa6affdbf2160ad..f01be3b18f01f34019b4cf52c521280752341bb3 100644 (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;
    }
 
index 546806994d759a2a7c63b63b97f64957c3c92430..ab237ad3b116519edb2b9e8ebb9bb6a75613c274 100644 (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);
index c552a4b2542d814ef6bd4d234499a56e4098fed5..a511ee293f632b0999ef6fd70b244a44c662a3eb 100644 (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 $
  *
  *-------------------------------------------------------------------------
  */
 
 #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
  * 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.
  *
@@ -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;
index 4019f6fef134fdf32243fabdd3e7b75d42ef36fd..6f0de64910b9362919ef24024620c171caad4dd0 100644 (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
index 8bc95f1f2f747091eaf68e4a20aea8a6b0be6c21..72b9387767a6e412dbf3d0c43af6fe7dd552f27f 100644 (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
index 16150846622919342fc8c65d7e0e7a031697537f..3af7db9e626fed86498ef0b87f6747a7d1eb6c8c 100644 (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);
index 6927da2fd086a9b39ae3e40c58966fc23e926d70..4d5e0039c89dc4bffec1fd00b6d72cfc4966d74f 100644 (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 */