#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;
 
 
 /*
- *     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
                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;
 #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, '/');
                {
                        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)
                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
                        {
                                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),
                                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),
 
        /* Come here if suitable message already logged */
 hba_other_error:
-       *error_p = true;
+       return false;
 }
 
 
 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.
+        */
 }
 
 
        }
 }
 
+/*
+ * 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... */
 
        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;
 }
 
 /*
  *     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.
  *
                                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;