*** a/src/backend/libpq/auth.c
--- b/src/backend/libpq/auth.c
***************
*** 315,329 **** ClientAuthentication(Port *port)
  
  	/*
  	 * Get the authentication method to use for this frontend/database
! 	 * combination.  Note: a failure return indicates a problem with the hba
! 	 * config file, not with the request.  hba.c should have dropped an error
! 	 * message into the postmaster logfile if it failed.
  	 */
! 	if (hba_getauthmethod(port) != STATUS_OK)
! 		ereport(FATAL,
! 				(errcode(ERRCODE_CONFIG_FILE_ERROR),
! 				 errmsg("missing or erroneous pg_hba.conf file"),
! 				 errhint("See server log for details.")));
  
  	/*
  	 * Enable immediate response to SIGTERM/SIGINT/timeout interrupts. (We
--- 315,325 ----
  
  	/*
  	 * Get the authentication method to use for this frontend/database
! 	 * combination.  Note: we do not parse the file at this point; this has
! 	 * already been done elsewhere.  hba.c dropped an error message
! 	 * into the server logfile if parsing the hba config file failed.
  	 */
! 	hba_getauthmethod(port);
  
  	/*
  	 * Enable immediate response to SIGTERM/SIGINT/timeout interrupts. (We
*** a/src/backend/libpq/hba.c
--- b/src/backend/libpq/hba.c
***************
*** 35,50 ****
  #include "utils/acl.h"
  #include "utils/guc.h"
  #include "utils/lsyscache.h"
  
  
  #define atooid(x)  ((Oid) strtoul((x), NULL, 10))
  #define atoxid(x)  ((TransactionId) strtoul((x), NULL, 10))
  
- /* This is used to separate values in multi-valued column strings */
- #define MULTI_VALUE_SEP "\001"
- 
  #define MAX_TOKEN	256
  
  /* callback data for check_network_callback */
  typedef struct check_network_data
  {
--- 35,51 ----
  #include "utils/acl.h"
  #include "utils/guc.h"
  #include "utils/lsyscache.h"
+ #include "utils/memutils.h"
  
  
  #define atooid(x)  ((Oid) strtoul((x), NULL, 10))
  #define atoxid(x)  ((TransactionId) strtoul((x), NULL, 10))
  
  #define MAX_TOKEN	256
  
+ #define token_is_keyword(t, k)	(!t->quoted && strcmp(t->string, k) == 0)
+ #define token_matches(t, k)  (strcmp(t->string, k) == 0)
+ 
  /* callback data for check_network_callback */
  typedef struct check_network_data
  {
***************
*** 53,78 **** typedef struct check_network_data
  	bool		result;			/* set to true if match */
  } check_network_data;
  
! /* pre-parsed content of HBA config file: list of HbaLine structs */
  static List *parsed_hba_lines = NIL;
  
  /*
   * These variables hold the pre-parsed contents of the ident usermap
!  * configuration file.	ident_lines is a list of sublists, one sublist for
!  * each (non-empty, non-comment) line of the file.	The sublist items are
!  * palloc'd strings, one string per token on the line.  Note there will always
!  * be at least one token, since blank lines are not entered in the data
!  * structure.  ident_line_nums is an integer list containing the actual line
!  * number for each line represented in ident_lines.
   */
  static List *ident_lines = NIL;
  static List *ident_line_nums = NIL;
  
  
! static void tokenize_file(const char *filename, FILE *file,
  			  List **lines, List **line_nums);
! static char *tokenize_inc_file(const char *outer_filename,
! 				  const char *inc_filename);
  
  /*
   * isblank() exists in the ISO C99 spec, but it's not very portable yet,
--- 54,98 ----
  	bool		result;			/* set to true if match */
  } check_network_data;
  
! 
! /*
!  * A single string token lexed from the HBA config file, together with whether
!  * the token had been quoted.
!  */
! typedef struct HbaToken
! {
! 	char   *string;
! 	bool	quoted;
! } HbaToken;
! 
! /*
!  * pre-parsed content of HBA config file: list of HbaLine structs.
!  * parsed_hba_context is the memory context where it lives.
!  */
  static List *parsed_hba_lines = NIL;
+ static MemoryContext parsed_hba_context = NULL;
  
  /*
   * These variables hold the pre-parsed contents of the ident usermap
!  * configuration file.	ident_lines is a triple-nested list of lines, fields
!  * and tokens, as returned by tokenize_file.  There will be one line in
!  * ident_lines for each (non-empty, non-comment) line of the file.  Note there
!  * will always be at least one field, since blank lines are not entered in the
!  * data structure.  ident_line_nums is an integer list containing the actual
!  * line number for each line represented in ident_lines.  ident_context is
!  * the memory context holding all this.
   */
  static List *ident_lines = NIL;
  static List *ident_line_nums = NIL;
+ static MemoryContext ident_context = NULL;
  
  
! static MemoryContext tokenize_file(const char *filename, FILE *file,
  			  List **lines, List **line_nums);
! static List *tokenize_inc_file(List *tokens,
! 				  const char *outer_filename, const char *inc_filename);
! static bool parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline,
! 				   int line_num);
  
  /*
   * isblank() exists in the ISO C99 spec, but it's not very portable yet,
***************
*** 104,113 **** pg_isblank(const char c)
   * whichever comes first. If no more tokens on line, position the file to the
   * beginning of the next line or EOF, whichever comes first.
   *
!  * Handle comments. Treat unquoted keywords that might be role, database, or
!  * host names specially, by appending a newline to them.  Also, when
!  * a token is terminated by a comma, the comma is included in the returned
!  * token.
   */
  static bool
  next_token(FILE *fp, char *buf, int bufsz, bool *initial_quote)
--- 124,131 ----
   * whichever comes first. If no more tokens on line, position the file to the
   * beginning of the next line or EOF, whichever comes first.
   *
!  * Handle comments. Also, when a token is terminated by a comma, the comma is
!  * included in the returned token.
   */
  static bool
  next_token(FILE *fp, char *buf, int bufsz, bool *initial_quote)
***************
*** 198,245 **** next_token(FILE *fp, char *buf, int bufsz, bool *initial_quote)
  
  	*buf = '\0';
  
- 	if (!saw_quote &&
- 		(strcmp(start_buf, "all") == 0 ||
- 		 strcmp(start_buf, "samehost") == 0 ||
- 		 strcmp(start_buf, "samenet") == 0 ||
- 		 strcmp(start_buf, "sameuser") == 0 ||
- 		 strcmp(start_buf, "samegroup") == 0 ||
- 		 strcmp(start_buf, "samerole") == 0 ||
- 		 strcmp(start_buf, "replication") == 0))
- 	{
- 		/* append newline to a magical keyword */
- 		*buf++ = '\n';
- 		*buf = '\0';
- 	}
- 
  	return (saw_quote || buf > start_buf);
  }
  
  /*
!  *	 Tokenize file and handle file inclusion and comma lists. We have
!  *	 to  break	apart  the	commas	to	expand	any  file names then
!  *	 reconstruct with commas.
   *
!  * The result is a palloc'd string, or NULL if we have reached EOL.
   */
! static char *
! next_token_expand(const char *filename, FILE *file)
  {
  	char		buf[MAX_TOKEN];
- 	char	   *comma_str = pstrdup("");
- 	bool		got_something = false;
  	bool		trailing_comma;
  	bool		initial_quote;
! 	char	   *incbuf;
! 	int			needed;
  
  	do
  	{
  		if (!next_token(file, buf, sizeof(buf), &initial_quote))
  			break;
  
- 		got_something = true;
- 
  		if (strlen(buf) > 0 && buf[strlen(buf) - 1] == ',')
  		{
  			trailing_comma = true;
--- 216,243 ----
  
  	*buf = '\0';
  
  	return (saw_quote || buf > start_buf);
  }
  
  /*
!  * Tokenize one HBA field from a file, handling file inclusion and comma lists.
   *
!  * The result is a List of HbaToken structs for each individual token,
!  * or NIL if we reached EOL.
   */
! static List *
! next_field_expand(const char *filename, FILE *file)
  {
  	char		buf[MAX_TOKEN];
  	bool		trailing_comma;
  	bool		initial_quote;
! 	List	   *tokens = NIL;
  
  	do
  	{
  		if (!next_token(file, buf, sizeof(buf), &initial_quote))
  			break;
  
  		if (strlen(buf) > 0 && buf[strlen(buf) - 1] == ',')
  		{
  			trailing_comma = true;
***************
*** 250,335 **** next_token_expand(const char *filename, FILE *file)
  
  		/* Is this referencing a file? */
  		if (!initial_quote && buf[0] == '@' && buf[1] != '\0')
! 			incbuf = tokenize_inc_file(filename, buf + 1);
  		else
! 			incbuf = pstrdup(buf);
! 
! 		needed = strlen(comma_str) + strlen(incbuf) + 1;
! 		if (trailing_comma)
! 			needed++;
! 		comma_str = repalloc(comma_str, needed);
! 		strcat(comma_str, incbuf);
! 		if (trailing_comma)
! 			strcat(comma_str, MULTI_VALUE_SEP);
! 		pfree(incbuf);
! 	} while (trailing_comma);
  
! 	if (!got_something)
! 	{
! 		pfree(comma_str);
! 		return NULL;
! 	}
  
! 	return comma_str;
  }
  
- 
  /*
!  * Free memory used by lines/tokens (i.e., structure built by tokenize_file)
   */
! static void
! free_lines(List **lines, List **line_nums)
  {
! 	/*
! 	 * Either both must be non-NULL, or both must be NULL
! 	 */
! 	Assert((*lines != NIL && *line_nums != NIL) ||
! 		   (*lines == NIL && *line_nums == NIL));
! 
! 	if (*lines)
! 	{
! 		/*
! 		 * "lines" is a list of lists; each of those sublists consists of
! 		 * palloc'ed tokens, so we want to free each pointed-to token in a
! 		 * sublist, followed by the sublist itself, and finally the whole
! 		 * list.
! 		 */
! 		ListCell   *line;
  
! 		foreach(line, *lines)
! 		{
! 			List	   *ln = lfirst(line);
! 			ListCell   *token;
! 
! 			foreach(token, ln)
! 				pfree(lfirst(token));
! 			/* free the sublist structure itself */
! 			list_free(ln);
! 		}
! 		/* free the list structure itself */
! 		list_free(*lines);
! 		/* clear the static variable */
! 		*lines = NIL;
! 	}
! 
! 	if (*line_nums)
! 	{
! 		list_free(*line_nums);
! 		*line_nums = NIL;
! 	}
  }
  
  
! static char *
! tokenize_inc_file(const char *outer_filename,
  				  const char *inc_filename)
  {
  	char	   *inc_fullname;
  	FILE	   *inc_file;
  	List	   *inc_lines;
  	List	   *inc_line_nums;
! 	ListCell   *line;
! 	char	   *comma_str;
  
  	if (is_absolute_path(inc_filename))
  	{
--- 248,293 ----
  
  		/* Is this referencing a file? */
  		if (!initial_quote && buf[0] == '@' && buf[1] != '\0')
! 			tokens = tokenize_inc_file(tokens, filename, buf + 1);
  		else
! 		{
! 			HbaToken *token = palloc0(sizeof(HbaToken));
  
! 			token->string = pstrdup(buf);
! 			token->quoted = initial_quote;
! 			tokens = lappend(tokens, token);
! 		}
! 	} while (trailing_comma);
  
! 	return tokens;
  }
  
  /*
!  * Copy a HbaToken struct into freshly palloc'd memory.
   */
! static HbaToken *
! copy_hba_token(HbaToken *in)
  {
! 	HbaToken *out = palloc0(sizeof(HbaToken));
  
! 	/* XXX --- reduce palloc overhead here? */
! 	out->string = pstrdup(in->string);
! 	out->quoted = in->quoted;
! 	return out;
  }
  
  
! static List *
! tokenize_inc_file(List *tokens,
! 				  const char *outer_filename,
  				  const char *inc_filename)
  {
  	char	   *inc_fullname;
  	FILE	   *inc_file;
  	List	   *inc_lines;
  	List	   *inc_line_nums;
! 	ListCell   *inc_line;
! 	MemoryContext linecxt;
  
  	if (is_absolute_path(inc_filename))
  	{
***************
*** 355,451 **** tokenize_inc_file(const char *outer_filename,
  				 errmsg("could not open secondary authentication file \"@%s\" as \"%s\": %m",
  						inc_filename, inc_fullname)));
  		pfree(inc_fullname);
! 
! 		/* return single space, it matches nothing */
! 		return pstrdup(" ");
  	}
  
  	/* There is possible recursion here if the file contains @ */
! 	tokenize_file(inc_fullname, inc_file, &inc_lines, &inc_line_nums);
  
  	FreeFile(inc_file);
  	pfree(inc_fullname);
  
! 	/* Create comma-separated string from List */
! 	comma_str = pstrdup("");
! 	foreach(line, inc_lines)
  	{
! 		List	   *token_list = (List *) lfirst(line);
! 		ListCell   *token;
  
! 		foreach(token, token_list)
  		{
! 			int			oldlen = strlen(comma_str);
! 			int			needed;
! 
! 			needed = oldlen + strlen(lfirst(token)) + 1;
! 			if (oldlen > 0)
! 				needed++;
! 			comma_str = repalloc(comma_str, needed);
! 			if (oldlen > 0)
! 				strcat(comma_str, MULTI_VALUE_SEP);
! 			strcat(comma_str, lfirst(token));
! 		}
! 	}
  
! 	free_lines(&inc_lines, &inc_line_nums);
  
! 	/* if file is empty, return single space rather than empty string */
! 	if (strlen(comma_str) == 0)
! 	{
! 		pfree(comma_str);
! 		return pstrdup(" ");
  	}
  
! 	return comma_str;
  }
  
  
  /*
!  * Tokenize the given file, storing the resulting data into two lists:
!  * a list of sublists, each sublist containing the tokens in a line of
!  * the file, and a list of line numbers.
   *
   * filename must be the absolute path to the target file.
   */
! static void
  tokenize_file(const char *filename, FILE *file,
  			  List **lines, List **line_nums)
  {
  	List	   *current_line = NIL;
  	int			line_number = 1;
! 	char	   *buf;
  
  	*lines = *line_nums = NIL;
  
  	while (!feof(file) && !ferror(file))
  	{
! 		buf = next_token_expand(filename, file);
  
! 		/* add token to list, unless we are at EOL or comment start */
! 		if (buf)
  		{
  			if (current_line == NIL)
  			{
  				/* make a new line List, record its line number */
! 				current_line = lappend(current_line, buf);
  				*lines = lappend(*lines, current_line);
  				*line_nums = lappend_int(*line_nums, line_number);
  			}
  			else
  			{
! 				/* append token to current line's list */
! 				current_line = lappend(current_line, buf);
  			}
  		}
  		else
  		{
  			/* we are at real or logical EOL, so force a new line List */
  			current_line = NIL;
- 			/* Advance line number whenever we reach EOL */
  			line_number++;
  		}
  	}
  }
  
  
--- 313,413 ----
  				 errmsg("could not open secondary authentication file \"@%s\" as \"%s\": %m",
  						inc_filename, inc_fullname)));
  		pfree(inc_fullname);
! 		return tokens;
  	}
  
  	/* There is possible recursion here if the file contains @ */
! 	linecxt = tokenize_file(inc_fullname, inc_file, &inc_lines, &inc_line_nums);
  
  	FreeFile(inc_file);
  	pfree(inc_fullname);
  
! 	foreach(inc_line, inc_lines)
  	{
! 		List	   *inc_fields = lfirst(inc_line);
! 		ListCell   *inc_field;
  
! 		foreach(inc_field, inc_fields)
  		{
! 			List	   *inc_tokens = lfirst(inc_field);
! 			ListCell   *inc_token;
  
! 			foreach(inc_token, inc_tokens)
! 			{
! 				HbaToken   *token = lfirst(inc_token);
  
! 				tokens = lappend(tokens, copy_hba_token(token));
! 			}
! 		}
  	}
  
! 	MemoryContextDelete(linecxt);
! 	return tokens;
  }
  
  
  /*
!  * Tokenize the given file, storing the resulting data into two Lists: a
!  * List of lines, and a List of line numbers.
!  *
!  * The list of lines is a triple-nested List structure.  Each line is a List of
!  * fields, and each field is a List of HbaTokens.
   *
   * filename must be the absolute path to the target file.
+  *
+  * Return value is a memory context which contains all memory allocated by
+  * this function.
   */
! static MemoryContext
  tokenize_file(const char *filename, FILE *file,
  			  List **lines, List **line_nums)
  {
  	List	   *current_line = NIL;
+ 	List	   *current_field = NIL;
  	int			line_number = 1;
! 	MemoryContext	linecxt;
! 	MemoryContext	oldcxt;
! 
! 	linecxt = AllocSetContextCreate(TopMemoryContext,
! 									"tokenize file cxt",
! 									ALLOCSET_DEFAULT_MINSIZE,
! 									ALLOCSET_DEFAULT_INITSIZE,
! 									ALLOCSET_DEFAULT_MAXSIZE);
! 	oldcxt = MemoryContextSwitchTo(linecxt);
  
  	*lines = *line_nums = NIL;
  
  	while (!feof(file) && !ferror(file))
  	{
! 		current_field = next_field_expand(filename, file);
  
! 		/* add tokens to list, unless we are at EOL or comment start */
! 		if (list_length(current_field) > 0)
  		{
  			if (current_line == NIL)
  			{
  				/* make a new line List, record its line number */
! 				current_line = lappend(current_line, current_field);
  				*lines = lappend(*lines, current_line);
  				*line_nums = lappend_int(*line_nums, line_number);
  			}
  			else
  			{
! 				/* append tokens to current line's list */
! 				current_line = lappend(current_line, current_field);
  			}
  		}
  		else
  		{
  			/* we are at real or logical EOL, so force a new line List */
  			current_line = NIL;
  			line_number++;
  		}
  	}
+ 
+ 	MemoryContextSwitchTo(oldcxt);
+ 
+ 	return linecxt;
  }
  
  
***************
*** 473,546 **** is_member(Oid userid, const char *role)
  }
  
  /*
!  * Check comma-separated list for a match to role, allowing group names.
!  *
!  * NB: param_str is destructively modified!  In current usage, this is
!  * okay only because this code is run after forking off from the postmaster,
!  * and so it doesn't matter that we clobber the stored hba info.
   */
  static bool
! check_role(const char *role, Oid roleid, char *param_str)
  {
! 	char	   *tok;
  
! 	for (tok = strtok(param_str, MULTI_VALUE_SEP);
! 		 tok != NULL;
! 		 tok = strtok(NULL, MULTI_VALUE_SEP))
  	{
! 		if (tok[0] == '+')
  		{
! 			if (is_member(roleid, tok + 1))
  				return true;
  		}
! 		else if (strcmp(tok, role) == 0 ||
! 				 (strcmp(tok, "replication\n") == 0 &&
! 				  strcmp(role, "replication") == 0) ||
! 				 strcmp(tok, "all\n") == 0)
  			return true;
  	}
- 
  	return false;
  }
  
  /*
!  * Check to see if db/role combination matches param string.
!  *
!  * NB: param_str is destructively modified!  In current usage, this is
!  * okay only because this code is run after forking off from the postmaster,
!  * and so it doesn't matter that we clobber the stored hba info.
   */
  static bool
! check_db(const char *dbname, const char *role, Oid roleid, char *param_str)
  {
! 	char	   *tok;
  
! 	for (tok = strtok(param_str, MULTI_VALUE_SEP);
! 		 tok != NULL;
! 		 tok = strtok(NULL, MULTI_VALUE_SEP))
  	{
  		if (am_walsender)
  		{
  			/* walsender connections can only match replication keyword */
! 			if (strcmp(tok, "replication\n") == 0)
  				return true;
  		}
! 		else if (strcmp(tok, "all\n") == 0)
  			return true;
! 		else if (strcmp(tok, "sameuser\n") == 0)
  		{
  			if (strcmp(dbname, role) == 0)
  				return true;
  		}
! 		else if (strcmp(tok, "samegroup\n") == 0 ||
! 				 strcmp(tok, "samerole\n") == 0)
  		{
  			if (is_member(roleid, dbname))
  				return true;
  		}
! 		else if (strcmp(tok, "replication\n") == 0)
  			continue;			/* never match this if not walsender */
! 		else if (strcmp(tok, dbname) == 0)
  			return true;
  	}
  	return false;
--- 435,497 ----
  }
  
  /*
!  * Check HbaToken list for a match to role, allowing group names.
   */
  static bool
! check_role(const char *role, Oid roleid, List *tokens)
  {
! 	ListCell	   *cell;
! 	HbaToken	   *tok;
  
! 	foreach(cell, tokens)
  	{
! 		tok = lfirst(cell);
! 		if (!tok->quoted && tok->string[0] == '+')
  		{
! 			if (is_member(roleid, tok->string + 1))
  				return true;
  		}
! 		else if (token_matches(tok, role) ||
! 				 token_is_keyword(tok, "all"))
  			return true;
  	}
  	return false;
  }
  
  /*
!  * Check to see if db/role combination matches HbaToken list.
   */
  static bool
! check_db(const char *dbname, const char *role, Oid roleid, List *tokens)
  {
! 	ListCell	   *cell;
! 	HbaToken	   *tok;
  
! 	foreach(cell, tokens)
  	{
+ 		tok = lfirst(cell);
  		if (am_walsender)
  		{
  			/* walsender connections can only match replication keyword */
! 			if (token_is_keyword(tok, "replication"))
  				return true;
  		}
! 		else if (token_is_keyword(tok, "all"))
  			return true;
! 		else if (token_is_keyword(tok, "sameuser"))
  		{
  			if (strcmp(dbname, role) == 0)
  				return true;
  		}
! 		else if (token_is_keyword(tok, "samegroup") ||
! 				 token_is_keyword(tok, "samerole"))
  		{
  			if (is_member(roleid, dbname))
  				return true;
  		}
! 		else if (token_is_keyword(tok, "replication"))
  			continue;			/* never match this if not walsender */
! 		else if (token_matches(tok, dbname))
  			return true;
  	}
  	return false;
***************
*** 784,790 **** check_same_host_or_net(SockAddr *raddr, IPCompareMethod method)
  } while (0);
  
  #define REQUIRE_AUTH_OPTION(methodval, optname, validmethods) do {\
! 	if (parsedline->auth_method != methodval) \
  		INVALID_AUTH_OPTION(optname, validmethods); \
  } while (0);
  
--- 735,741 ----
  } while (0);
  
  #define REQUIRE_AUTH_OPTION(methodval, optname, validmethods) do {\
! 	if (hbaline->auth_method != methodval) \
  		INVALID_AUTH_OPTION(optname, validmethods); \
  } while (0);
  
***************
*** 800,828 **** check_same_host_or_net(SockAddr *raddr, IPCompareMethod method)
  	} \
  } while (0);
  
  
  /*
!  * Parse one line in the hba config file and store the result in
!  * a HbaLine structure.
   */
! static bool
! parse_hba_line(List *line, int line_num, HbaLine *parsedline)
  {
! 	char	   *token;
  	struct addrinfo *gai_result;
  	struct addrinfo hints;
  	int			ret;
  	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)
  	{
  #ifdef HAVE_UNIX_SOCKETS
  		parsedline->conntype = ctLocal;
--- 751,833 ----
  	} \
  } while (0);
  
+ /*
+  * IDENT_FIELD_ABSENT:
+  * Throw an error and exit the function if the given ident field ListCell is
+  * not populated.
+  *
+  * IDENT_MULTI_VALUE:
+  * Throw an error and exit the function if the given ident token List has more
+  * than one element.
+  */
+ #define IDENT_FIELD_ABSENT(field) do {\
+ 	if (!field) { \
+ 		ereport(LOG, \
+ 				(errcode(ERRCODE_CONFIG_FILE_ERROR), \
+ 				 errmsg("missing entry in file \"%s\" at end of line %d", \
+ 						IdentFileName, line_number))); \
+ 		*error_p = true; \
+ 		return; \
+ 	} \
+ } while (0);
+ 
+ #define IDENT_MULTI_VALUE(tokens) do {\
+ 	if (tokens->length > 1) { \
+ 		ereport(LOG, \
+ 				(errcode(ERRCODE_CONFIG_FILE_ERROR), \
+ 				 errmsg("multiple values in ident field"), \
+ 				 errcontext("line %d of configuration file \"%s\"", \
+ 						line_number, IdentFileName))); \
+ 		*error_p = true; \
+ 		return; \
+ 	} \
+ } while (0);
+ 
  
  /*
!  * Parse one tokenised line from the hba config file and store the result in a
!  * HbaLine structure, or NULL if parsing fails.
!  *
!  * The tokenised line is a List of fields, each field being a List of
!  * HbaTokens.
!  *
!  * Note: this function leaks memory when an error occurs.  Caller is expected
!  * to have set a memory context that will be reset if this function returns
!  * NULL.
   */
! static HbaLine *
! parse_hba_line(List *line, int line_num)
  {
! 	char	   *str;
  	struct addrinfo *gai_result;
  	struct addrinfo hints;
  	int			ret;
  	char	   *cidr_slash;
  	char	   *unsupauth;
! 	ListCell   *field;
! 	List	   *tokens;
! 	ListCell   *tokencell;
! 	HbaToken   *token;
! 	HbaLine	   *parsedline;
  
+ 	parsedline = palloc0(sizeof(HbaLine));
  	parsedline->linenumber = line_num;
  
  	/* Check the record type. */
! 	field = list_head(line);
! 	tokens = lfirst(field);
! 	if (tokens->length > 1)
! 	{
! 		ereport(LOG,
! 				(errcode(ERRCODE_CONFIG_FILE_ERROR),
! 				 errmsg("multiple values specified for connection type"),
! 				 errhint("Specify exactly one connection type per line."),
! 				 errcontext("line %d of configuration file \"%s\"",
! 							line_num, HbaFileName)));
! 		return NULL;
! 	}
! 	token = linitial(tokens);
! 	if (strcmp(token->string, "local") == 0)
  	{
  #ifdef HAVE_UNIX_SOCKETS
  		parsedline->conntype = ctLocal;
***************
*** 832,846 **** parse_hba_line(List *line, int line_num, HbaLine *parsedline)
  				 errmsg("local connections are not supported by this build"),
  				 errcontext("line %d of configuration file \"%s\"",
  							line_num, HbaFileName)));
! 		return false;
  #endif
  	}
! 	else if (strcmp(token, "host") == 0
! 			 || strcmp(token, "hostssl") == 0
! 			 || strcmp(token, "hostnossl") == 0)
  	{
  
! 		if (token[4] == 's')	/* "hostssl" */
  		{
  			/* SSL support must be actually active, else complain */
  #ifdef USE_SSL
--- 837,851 ----
  				 errmsg("local connections are not supported by this build"),
  				 errcontext("line %d of configuration file \"%s\"",
  							line_num, HbaFileName)));
! 		return NULL;
  #endif
  	}
! 	else if (strcmp(token->string, "host") == 0 ||
! 			 strcmp(token->string, "hostssl") == 0 ||
! 			 strcmp(token->string, "hostnossl") == 0)
  	{
  
! 		if (token->string[4] == 's')	/* "hostssl" */
  		{
  			/* SSL support must be actually active, else complain */
  #ifdef USE_SSL
***************
*** 854,860 **** parse_hba_line(List *line, int line_num, HbaLine *parsedline)
  						 errhint("Set ssl = on in postgresql.conf."),
  						 errcontext("line %d of configuration file \"%s\"",
  									line_num, HbaFileName)));
! 				return false;
  			}
  #else
  			ereport(LOG,
--- 859,865 ----
  						 errhint("Set ssl = on in postgresql.conf."),
  						 errcontext("line %d of configuration file \"%s\"",
  									line_num, HbaFileName)));
! 				return NULL;
  			}
  #else
  			ereport(LOG,
***************
*** 863,873 **** parse_hba_line(List *line, int line_num, HbaLine *parsedline)
  			  errhint("Compile with --with-openssl to use SSL connections."),
  					 errcontext("line %d of configuration file \"%s\"",
  								line_num, HbaFileName)));
! 			return false;
  #endif
  		}
  #ifdef USE_SSL
! 		else if (token[4] == 'n')		/* "hostnossl" */
  		{
  			parsedline->conntype = ctHostNoSSL;
  		}
--- 868,878 ----
  			  errhint("Compile with --with-openssl to use SSL connections."),
  					 errcontext("line %d of configuration file \"%s\"",
  								line_num, HbaFileName)));
! 			return NULL;
  #endif
  		}
  #ifdef USE_SSL
! 		else if (token->string[4] == 'n')		/* "hostnossl" */
  		{
  			parsedline->conntype = ctHostNoSSL;
  		}
***************
*** 883,945 **** parse_hba_line(List *line, int line_num, HbaLine *parsedline)
  		ereport(LOG,
  				(errcode(ERRCODE_CONFIG_FILE_ERROR),
  				 errmsg("invalid connection type \"%s\"",
! 						token),
  				 errcontext("line %d of configuration file \"%s\"",
  							line_num, HbaFileName)));
! 		return false;
  	}
  
! 	/* Get the database. */
! 	line_item = lnext(line_item);
! 	if (!line_item)
  	{
  		ereport(LOG,
  				(errcode(ERRCODE_CONFIG_FILE_ERROR),
  				 errmsg("end-of-line before database specification"),
  				 errcontext("line %d of configuration file \"%s\"",
  							line_num, HbaFileName)));
! 		return false;
  	}
- 	parsedline->database = pstrdup(lfirst(line_item));
  
! 	/* Get the role. */
! 	line_item = lnext(line_item);
! 	if (!line_item)
  	{
  		ereport(LOG,
  				(errcode(ERRCODE_CONFIG_FILE_ERROR),
  				 errmsg("end-of-line before role specification"),
  				 errcontext("line %d of configuration file \"%s\"",
  							line_num, HbaFileName)));
! 		return false;
  	}
- 	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)
  		{
  			ereport(LOG,
  					(errcode(ERRCODE_CONFIG_FILE_ERROR),
  					 errmsg("end-of-line before IP address specification"),
  					 errcontext("line %d of configuration file \"%s\"",
  								line_num, HbaFileName)));
! 			return false;
  		}
! 		token = lfirst(line_item);
  
! 		if (strcmp(token, "all\n") == 0)
  		{
  			parsedline->ip_cmp_method = ipCmpAll;
  		}
! 		else if (strcmp(token, "samehost\n") == 0)
  		{
  			/* Any IP on this host is allowed to connect */
  			parsedline->ip_cmp_method = ipCmpSameHost;
  		}
! 		else if (strcmp(token, "samenet\n") == 0)
  		{
  			/* Any IP on the host's subnets is allowed to connect */
  			parsedline->ip_cmp_method = ipCmpSameNet;
--- 888,973 ----
  		ereport(LOG,
  				(errcode(ERRCODE_CONFIG_FILE_ERROR),
  				 errmsg("invalid connection type \"%s\"",
! 						token->string),
  				 errcontext("line %d of configuration file \"%s\"",
  							line_num, HbaFileName)));
! 		return NULL;
  	}
  
! 	/* Get the databases. */
! 	field = lnext(field);
! 	if (!field)
  	{
  		ereport(LOG,
  				(errcode(ERRCODE_CONFIG_FILE_ERROR),
  				 errmsg("end-of-line before database specification"),
  				 errcontext("line %d of configuration file \"%s\"",
  							line_num, HbaFileName)));
! 		return NULL;
! 	}
! 	parsedline->databases = NIL;
! 	tokens = lfirst(field);
! 	foreach(tokencell, tokens)
! 	{
! 		parsedline->databases = lappend(parsedline->databases,
! 										copy_hba_token(lfirst(tokencell)));
  	}
  
! 	/* Get the roles. */
! 	field = lnext(field);
! 	if (!field)
  	{
  		ereport(LOG,
  				(errcode(ERRCODE_CONFIG_FILE_ERROR),
  				 errmsg("end-of-line before role specification"),
  				 errcontext("line %d of configuration file \"%s\"",
  							line_num, HbaFileName)));
! 		return NULL;
! 	}
! 	parsedline->roles = NIL;
! 	tokens = lfirst(field);
! 	foreach(tokencell, tokens)
! 	{
! 		parsedline->roles = lappend(parsedline->roles,
! 									copy_hba_token(lfirst(tokencell)));
  	}
  
  	if (parsedline->conntype != ctLocal)
  	{
  		/* Read the IP address field. (with or without CIDR netmask) */
! 		field = lnext(field);
! 		if (!field)
  		{
  			ereport(LOG,
  					(errcode(ERRCODE_CONFIG_FILE_ERROR),
  					 errmsg("end-of-line before IP address specification"),
  					 errcontext("line %d of configuration file \"%s\"",
  								line_num, HbaFileName)));
! 			return NULL;
! 		}
! 		tokens = lfirst(field);
! 		if (tokens->length > 1)
! 		{
! 			ereport(LOG,
! 					(errcode(ERRCODE_CONFIG_FILE_ERROR),
! 					 errmsg("multiple values specified for host address"),
! 					 errhint("Specify one address range per line."),
! 					 errcontext("line %d of configuration file \"%s\"",
! 								line_num, HbaFileName)));
! 			return NULL;
  		}
! 		token = linitial(tokens);
  
! 		if (token_is_keyword(token, "all"))
  		{
  			parsedline->ip_cmp_method = ipCmpAll;
  		}
! 		else if (token_is_keyword(token, "samehost"))
  		{
  			/* Any IP on this host is allowed to connect */
  			parsedline->ip_cmp_method = ipCmpSameHost;
  		}
! 		else if (token_is_keyword(token, "samenet"))
  		{
  			/* Any IP on the host's subnets is allowed to connect */
  			parsedline->ip_cmp_method = ipCmpSameNet;
***************
*** 950,959 **** parse_hba_line(List *line, int line_num, HbaLine *parsedline)
  			parsedline->ip_cmp_method = ipCmpMask;
  
  			/* need a modifiable copy of token */
! 			token = pstrdup(token);
  
  			/* Check if it has a CIDR suffix and if so isolate it */
! 			cidr_slash = strchr(token, '/');
  			if (cidr_slash)
  				*cidr_slash = '\0';
  
--- 978,987 ----
  			parsedline->ip_cmp_method = ipCmpMask;
  
  			/* need a modifiable copy of token */
! 			str = pstrdup(token->string);
  
  			/* Check if it has a CIDR suffix and if so isolate it */
! 			cidr_slash = strchr(str, '/');
  			if (cidr_slash)
  				*cidr_slash = '\0';
  
***************
*** 967,990 **** parse_hba_line(List *line, int line_num, HbaLine *parsedline)
  			hints.ai_addr = NULL;
  			hints.ai_next = NULL;
  
! 			ret = pg_getaddrinfo_all(token, NULL, &hints, &gai_result);
  			if (ret == 0 && gai_result)
  				memcpy(&parsedline->addr, gai_result->ai_addr,
  					   gai_result->ai_addrlen);
  			else if (ret == EAI_NONAME)
! 				parsedline->hostname = token;
  			else
  			{
  				ereport(LOG,
  						(errcode(ERRCODE_CONFIG_FILE_ERROR),
  						 errmsg("invalid IP address \"%s\": %s",
! 								token, gai_strerror(ret)),
  						 errcontext("line %d of configuration file \"%s\"",
  									line_num, HbaFileName)));
  				if (gai_result)
  					pg_freeaddrinfo_all(hints.ai_family, gai_result);
! 				pfree(token);
! 				return false;
  			}
  
  			pg_freeaddrinfo_all(hints.ai_family, gai_result);
--- 995,1017 ----
  			hints.ai_addr = NULL;
  			hints.ai_next = NULL;
  
! 			ret = pg_getaddrinfo_all(str, NULL, &hints, &gai_result);
  			if (ret == 0 && gai_result)
  				memcpy(&parsedline->addr, gai_result->ai_addr,
  					   gai_result->ai_addrlen);
  			else if (ret == EAI_NONAME)
! 				parsedline->hostname = str;
  			else
  			{
  				ereport(LOG,
  						(errcode(ERRCODE_CONFIG_FILE_ERROR),
  						 errmsg("invalid IP address \"%s\": %s",
! 								str, gai_strerror(ret)),
  						 errcontext("line %d of configuration file \"%s\"",
  									line_num, HbaFileName)));
  				if (gai_result)
  					pg_freeaddrinfo_all(hints.ai_family, gai_result);
! 				return NULL;
  			}
  
  			pg_freeaddrinfo_all(hints.ai_family, gai_result);
***************
*** 994,1053 **** parse_hba_line(List *line, int line_num, HbaLine *parsedline)
  			{
  				if (parsedline->hostname)
  				{
- 					*cidr_slash = '/';	/* restore token for message */
  					ereport(LOG,
  							(errcode(ERRCODE_CONFIG_FILE_ERROR),
  							 errmsg("specifying both host name and CIDR mask is invalid: \"%s\"",
! 									token),
! 						   errcontext("line %d of configuration file \"%s\"",
! 									  line_num, HbaFileName)));
! 					pfree(token);
! 					return false;
  				}
  
  				if (pg_sockaddr_cidr_mask(&parsedline->mask, cidr_slash + 1,
  										  parsedline->addr.ss_family) < 0)
  				{
- 					*cidr_slash = '/';	/* restore token for message */
  					ereport(LOG,
  							(errcode(ERRCODE_CONFIG_FILE_ERROR),
  							 errmsg("invalid CIDR mask in address \"%s\"",
! 									token),
  						   errcontext("line %d of configuration file \"%s\"",
  									  line_num, HbaFileName)));
! 					pfree(token);
! 					return false;
  				}
! 				pfree(token);
  			}
  			else if (!parsedline->hostname)
  			{
  				/* Read the mask field. */
! 				pfree(token);
! 				line_item = lnext(line_item);
! 				if (!line_item)
  				{
  					ereport(LOG,
  							(errcode(ERRCODE_CONFIG_FILE_ERROR),
  						  errmsg("end-of-line before netmask specification"),
  						   errcontext("line %d of configuration file \"%s\"",
  									  line_num, HbaFileName)));
! 					return false;
  				}
! 				token = lfirst(line_item);
  
! 				ret = pg_getaddrinfo_all(token, NULL, &hints, &gai_result);
  				if (ret || !gai_result)
  				{
  					ereport(LOG,
  							(errcode(ERRCODE_CONFIG_FILE_ERROR),
  							 errmsg("invalid IP mask \"%s\": %s",
! 									token, gai_strerror(ret)),
  						   errcontext("line %d of configuration file \"%s\"",
  									  line_num, HbaFileName)));
  					if (gai_result)
  						pg_freeaddrinfo_all(hints.ai_family, gai_result);
! 					return false;
  				}
  
  				memcpy(&parsedline->mask, gai_result->ai_addr,
--- 1021,1088 ----
  			{
  				if (parsedline->hostname)
  				{
  					ereport(LOG,
  							(errcode(ERRCODE_CONFIG_FILE_ERROR),
  							 errmsg("specifying both host name and CIDR mask is invalid: \"%s\"",
! 									token->string),
! 							 errcontext("line %d of configuration file \"%s\"",
! 										line_num, HbaFileName)));
! 					return NULL;
  				}
  
  				if (pg_sockaddr_cidr_mask(&parsedline->mask, cidr_slash + 1,
  										  parsedline->addr.ss_family) < 0)
  				{
  					ereport(LOG,
  							(errcode(ERRCODE_CONFIG_FILE_ERROR),
  							 errmsg("invalid CIDR mask in address \"%s\"",
! 									token->string),
  						   errcontext("line %d of configuration file \"%s\"",
  									  line_num, HbaFileName)));
! 					return NULL;
  				}
! 				pfree(str);
  			}
  			else if (!parsedline->hostname)
  			{
  				/* Read the mask field. */
! 				pfree(str);
! 				field = lnext(field);
! 				if (!field)
  				{
  					ereport(LOG,
  							(errcode(ERRCODE_CONFIG_FILE_ERROR),
  						  errmsg("end-of-line before netmask specification"),
+ 							 errhint("Specify an address range in CIDR notation, or provide a separate netmask."),
  						   errcontext("line %d of configuration file \"%s\"",
  									  line_num, HbaFileName)));
! 					return NULL;
  				}
! 				tokens = lfirst(field);
! 				if (tokens->length > 1)
! 				{
! 					ereport(LOG,
! 							(errcode(ERRCODE_CONFIG_FILE_ERROR),
! 						     errmsg("multiple values specified for netmask"),
! 							 errcontext("line %d of configuration file \"%s\"",
! 										line_num, HbaFileName)));
! 					return NULL;
! 				}
! 				token = linitial(tokens);
  
! 				ret = pg_getaddrinfo_all(token->string, NULL,
! 										 &hints, &gai_result);
  				if (ret || !gai_result)
  				{
  					ereport(LOG,
  							(errcode(ERRCODE_CONFIG_FILE_ERROR),
  							 errmsg("invalid IP mask \"%s\": %s",
! 									token->string, gai_strerror(ret)),
  						   errcontext("line %d of configuration file \"%s\"",
  									  line_num, HbaFileName)));
  					if (gai_result)
  						pg_freeaddrinfo_all(hints.ai_family, gai_result);
! 					return NULL;
  				}
  
  				memcpy(&parsedline->mask, gai_result->ai_addr,
***************
*** 1061,1115 **** parse_hba_line(List *line, int line_num, HbaLine *parsedline)
  							 errmsg("IP address and mask do not match"),
  						   errcontext("line %d of configuration file \"%s\"",
  									  line_num, HbaFileName)));
! 					return false;
  				}
  			}
  		}
  	}							/* != ctLocal */
  
  	/* Get the authentication method */
! 	line_item = lnext(line_item);
! 	if (!line_item)
  	{
  		ereport(LOG,
  				(errcode(ERRCODE_CONFIG_FILE_ERROR),
  				 errmsg("end-of-line before authentication method"),
  				 errcontext("line %d of configuration file \"%s\"",
  							line_num, HbaFileName)));
! 		return false;
  	}
! 	token = lfirst(line_item);
  
  	unsupauth = NULL;
! 	if (strcmp(token, "trust") == 0)
  		parsedline->auth_method = uaTrust;
! 	else if (strcmp(token, "ident") == 0)
  		parsedline->auth_method = uaIdent;
! 	else if (strcmp(token, "peer") == 0)
  		parsedline->auth_method = uaPeer;
! 	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)
  	{
  		if (Db_user_namespace)
  		{
--- 1096,1161 ----
  							 errmsg("IP address and mask do not match"),
  						   errcontext("line %d of configuration file \"%s\"",
  									  line_num, HbaFileName)));
! 					return NULL;
  				}
  			}
  		}
  	}							/* != ctLocal */
  
  	/* Get the authentication method */
! 	field = lnext(field);
! 	if (!field)
  	{
  		ereport(LOG,
  				(errcode(ERRCODE_CONFIG_FILE_ERROR),
  				 errmsg("end-of-line before authentication method"),
  				 errcontext("line %d of configuration file \"%s\"",
  							line_num, HbaFileName)));
! 		return NULL;
  	}
! 	tokens = lfirst(field);
! 	if (tokens->length > 1)
! 	{
! 		ereport(LOG,
! 				(errcode(ERRCODE_CONFIG_FILE_ERROR),
! 				 errmsg("multiple values specified for authentication type"),
! 				 errhint("Specify exactly one authentication type per line."),
! 				 errcontext("line %d of configuration file \"%s\"",
! 							line_num, HbaFileName)));
! 		return NULL;
! 	}
! 	token = linitial(tokens);
  
  	unsupauth = NULL;
! 	if (strcmp(token->string, "trust") == 0)
  		parsedline->auth_method = uaTrust;
! 	else if (strcmp(token->string, "ident") == 0)
  		parsedline->auth_method = uaIdent;
! 	else if (strcmp(token->string, "peer") == 0)
  		parsedline->auth_method = uaPeer;
! 	else if (strcmp(token->string, "password") == 0)
  		parsedline->auth_method = uaPassword;
! 	else if (strcmp(token->string, "krb5") == 0)
  #ifdef KRB5
  		parsedline->auth_method = uaKrb5;
  #else
  		unsupauth = "krb5";
  #endif
! 	else if (strcmp(token->string, "gss") == 0)
  #ifdef ENABLE_GSS
  		parsedline->auth_method = uaGSS;
  #else
  		unsupauth = "gss";
  #endif
! 	else if (strcmp(token->string, "sspi") == 0)
  #ifdef ENABLE_SSPI
  		parsedline->auth_method = uaSSPI;
  #else
  		unsupauth = "sspi";
  #endif
! 	else if (strcmp(token->string, "reject") == 0)
  		parsedline->auth_method = uaReject;
! 	else if (strcmp(token->string, "md5") == 0)
  	{
  		if (Db_user_namespace)
  		{
***************
*** 1118,1156 **** parse_hba_line(List *line, int line_num, HbaLine *parsedline)
  					 errmsg("MD5 authentication is not supported when \"db_user_namespace\" is enabled"),
  					 errcontext("line %d of configuration file \"%s\"",
  								line_num, HbaFileName)));
! 			return false;
  		}
  		parsedline->auth_method = uaMD5;
  	}
! 	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 if (strcmp(token, "cert") == 0)
  #ifdef USE_SSL
  		parsedline->auth_method = uaCert;
  #else
  		unsupauth = "cert";
  #endif
! 	else if (strcmp(token, "radius") == 0)
  		parsedline->auth_method = uaRADIUS;
  	else
  	{
  		ereport(LOG,
  				(errcode(ERRCODE_CONFIG_FILE_ERROR),
  				 errmsg("invalid authentication method \"%s\"",
! 						token),
  				 errcontext("line %d of configuration file \"%s\"",
  							line_num, HbaFileName)));
! 		return false;
  	}
  
  	if (unsupauth)
--- 1164,1202 ----
  					 errmsg("MD5 authentication is not supported when \"db_user_namespace\" is enabled"),
  					 errcontext("line %d of configuration file \"%s\"",
  								line_num, HbaFileName)));
! 			return NULL;
  		}
  		parsedline->auth_method = uaMD5;
  	}
! 	else if (strcmp(token->string, "pam") == 0)
  #ifdef USE_PAM
  		parsedline->auth_method = uaPAM;
  #else
  		unsupauth = "pam";
  #endif
! 	else if (strcmp(token->string, "ldap") == 0)
  #ifdef USE_LDAP
  		parsedline->auth_method = uaLDAP;
  #else
  		unsupauth = "ldap";
  #endif
! 	else if (strcmp(token->string, "cert") == 0)
  #ifdef USE_SSL
  		parsedline->auth_method = uaCert;
  #else
  		unsupauth = "cert";
  #endif
! 	else if (strcmp(token->string, "radius") == 0)
  		parsedline->auth_method = uaRADIUS;
  	else
  	{
  		ereport(LOG,
  				(errcode(ERRCODE_CONFIG_FILE_ERROR),
  				 errmsg("invalid authentication method \"%s\"",
! 						token->string),
  				 errcontext("line %d of configuration file \"%s\"",
  							line_num, HbaFileName)));
! 		return NULL;
  	}
  
  	if (unsupauth)
***************
*** 1158,1167 **** parse_hba_line(List *line, int line_num, HbaLine *parsedline)
  		ereport(LOG,
  				(errcode(ERRCODE_CONFIG_FILE_ERROR),
  				 errmsg("invalid authentication method \"%s\": not supported by this build",
! 						token),
  				 errcontext("line %d of configuration file \"%s\"",
  							line_num, HbaFileName)));
! 		return false;
  	}
  
  	/*
--- 1204,1213 ----
  		ereport(LOG,
  				(errcode(ERRCODE_CONFIG_FILE_ERROR),
  				 errmsg("invalid authentication method \"%s\": not supported by this build",
! 						token->string),
  				 errcontext("line %d of configuration file \"%s\"",
  							line_num, HbaFileName)));
! 		return NULL;
  	}
  
  	/*
***************
*** 1181,1187 **** parse_hba_line(List *line, int line_num, HbaLine *parsedline)
  			 errmsg("krb5 authentication is not supported on local sockets"),
  				 errcontext("line %d of configuration file \"%s\"",
  							line_num, HbaFileName)));
! 		return false;
  	}
  
  	if (parsedline->conntype == ctLocal &&
--- 1227,1233 ----
  			 errmsg("krb5 authentication is not supported on local sockets"),
  				 errcontext("line %d of configuration file \"%s\"",
  							line_num, HbaFileName)));
! 		return NULL;
  	}
  
  	if (parsedline->conntype == ctLocal &&
***************
*** 1192,1198 **** parse_hba_line(List *line, int line_num, HbaLine *parsedline)
  		   errmsg("gssapi authentication is not supported on local sockets"),
  				 errcontext("line %d of configuration file \"%s\"",
  							line_num, HbaFileName)));
! 		return false;
  	}
  
  	if (parsedline->conntype != ctLocal &&
--- 1238,1244 ----
  		   errmsg("gssapi authentication is not supported on local sockets"),
  				 errcontext("line %d of configuration file \"%s\"",
  							line_num, HbaFileName)));
! 		return NULL;
  	}
  
  	if (parsedline->conntype != ctLocal &&
***************
*** 1203,1217 **** parse_hba_line(List *line, int line_num, HbaLine *parsedline)
  			errmsg("peer authentication is only supported on local sockets"),
  				 errcontext("line %d of configuration file \"%s\"",
  							line_num, HbaFileName)));
! 		return false;
  	}
  
  	/*
  	 * SSPI authentication can never be enabled on ctLocal connections,
  	 * because it's only supported on Windows, where ctLocal isn't supported.
  	 */
- 
- 
  	if (parsedline->conntype != ctHostSSL &&
  		parsedline->auth_method == uaCert)
  	{
--- 1249,1261 ----
  			errmsg("peer authentication is only supported on local sockets"),
  				 errcontext("line %d of configuration file \"%s\"",
  							line_num, HbaFileName)));
! 		return NULL;
  	}
  
  	/*
  	 * SSPI authentication can never be enabled on ctLocal connections,
  	 * because it's only supported on Windows, where ctLocal isn't supported.
  	 */
  	if (parsedline->conntype != ctHostSSL &&
  		parsedline->auth_method == uaCert)
  	{
***************
*** 1220,1450 **** parse_hba_line(List *line, int line_num, HbaLine *parsedline)
  				 errmsg("cert authentication is only supported on hostssl connections"),
  				 errcontext("line %d of configuration file \"%s\"",
  							line_num, HbaFileName)));
! 		return false;
  	}
  
  	/* Parse remaining arguments */
! 	while ((line_item = lnext(line_item)) != NULL)
  	{
! 		char	   *c;
! 
! 		token = lfirst(line_item);
! 
! 		c = strchr(token, '=');
! 		if (c == NULL)
! 		{
! 			/*
! 			 * Got something that's not a name=value pair.
! 			 */
! 			ereport(LOG,
! 					(errcode(ERRCODE_CONFIG_FILE_ERROR),
! 					 errmsg("authentication option not in name=value format: %s", token),
! 					 errcontext("line %d of configuration file \"%s\"",
! 								line_num, HbaFileName)));
! 			return false;
! 		}
! 		else
  		{
! 			*c++ = '\0';		/* token now holds "name", c holds "value" */
! 			if (strcmp(token, "map") == 0)
! 			{
! 				if (parsedline->auth_method != uaIdent &&
! 					parsedline->auth_method != uaPeer &&
! 					parsedline->auth_method != uaKrb5 &&
! 					parsedline->auth_method != uaGSS &&
! 					parsedline->auth_method != uaSSPI &&
! 					parsedline->auth_method != uaCert)
! 					INVALID_AUTH_OPTION("map", gettext_noop("ident, peer, krb5, gssapi, sspi and cert"));
! 				parsedline->usermap = pstrdup(c);
! 			}
! 			else if (strcmp(token, "clientcert") == 0)
  			{
  				/*
! 				 * Since we require ctHostSSL, this really can never happen on
! 				 * non-SSL-enabled builds, so don't bother checking for
! 				 * USE_SSL.
  				 */
- 				if (parsedline->conntype != ctHostSSL)
- 				{
- 					ereport(LOG,
- 							(errcode(ERRCODE_CONFIG_FILE_ERROR),
- 							 errmsg("clientcert can only be configured for \"hostssl\" rows"),
- 						   errcontext("line %d of configuration file \"%s\"",
- 									  line_num, HbaFileName)));
- 					return false;
- 				}
- 				if (strcmp(c, "1") == 0)
- 				{
- 					if (!secure_loaded_verify_locations())
- 					{
- 						ereport(LOG,
- 								(errcode(ERRCODE_CONFIG_FILE_ERROR),
- 								 errmsg("client certificates can only be checked if a root certificate store is available"),
- 								 errhint("Make sure the root.crt file is present and readable."),
- 						   errcontext("line %d of configuration file \"%s\"",
- 									  line_num, HbaFileName)));
- 						return false;
- 					}
- 					parsedline->clientcert = true;
- 				}
- 				else
- 				{
- 					if (parsedline->auth_method == uaCert)
- 					{
- 						ereport(LOG,
- 								(errcode(ERRCODE_CONFIG_FILE_ERROR),
- 								 errmsg("clientcert can not be set to 0 when using \"cert\" authentication"),
- 						   errcontext("line %d of configuration file \"%s\"",
- 									  line_num, HbaFileName)));
- 						return false;
- 					}
- 					parsedline->clientcert = false;
- 				}
- 			}
- 			else if (strcmp(token, "pamservice") == 0)
- 			{
- 				REQUIRE_AUTH_OPTION(uaPAM, "pamservice", "pam");
- 				parsedline->pamservice = pstrdup(c);
- 			}
- 			else if (strcmp(token, "ldaptls") == 0)
- 			{
- 				REQUIRE_AUTH_OPTION(uaLDAP, "ldaptls", "ldap");
- 				if (strcmp(c, "1") == 0)
- 					parsedline->ldaptls = true;
- 				else
- 					parsedline->ldaptls = false;
- 			}
- 			else if (strcmp(token, "ldapserver") == 0)
- 			{
- 				REQUIRE_AUTH_OPTION(uaLDAP, "ldapserver", "ldap");
- 				parsedline->ldapserver = pstrdup(c);
- 			}
- 			else if (strcmp(token, "ldapport") == 0)
- 			{
- 				REQUIRE_AUTH_OPTION(uaLDAP, "ldapport", "ldap");
- 				parsedline->ldapport = atoi(c);
- 				if (parsedline->ldapport == 0)
- 				{
- 					ereport(LOG,
- 							(errcode(ERRCODE_CONFIG_FILE_ERROR),
- 							 errmsg("invalid LDAP port number: \"%s\"", c),
- 						   errcontext("line %d of configuration file \"%s\"",
- 									  line_num, HbaFileName)));
- 					return false;
- 				}
- 			}
- 			else if (strcmp(token, "ldapbinddn") == 0)
- 			{
- 				REQUIRE_AUTH_OPTION(uaLDAP, "ldapbinddn", "ldap");
- 				parsedline->ldapbinddn = pstrdup(c);
- 			}
- 			else if (strcmp(token, "ldapbindpasswd") == 0)
- 			{
- 				REQUIRE_AUTH_OPTION(uaLDAP, "ldapbindpasswd", "ldap");
- 				parsedline->ldapbindpasswd = pstrdup(c);
- 			}
- 			else if (strcmp(token, "ldapsearchattribute") == 0)
- 			{
- 				REQUIRE_AUTH_OPTION(uaLDAP, "ldapsearchattribute", "ldap");
- 				parsedline->ldapsearchattribute = pstrdup(c);
- 			}
- 			else if (strcmp(token, "ldapbasedn") == 0)
- 			{
- 				REQUIRE_AUTH_OPTION(uaLDAP, "ldapbasedn", "ldap");
- 				parsedline->ldapbasedn = pstrdup(c);
- 			}
- 			else if (strcmp(token, "ldapprefix") == 0)
- 			{
- 				REQUIRE_AUTH_OPTION(uaLDAP, "ldapprefix", "ldap");
- 				parsedline->ldapprefix = pstrdup(c);
- 			}
- 			else if (strcmp(token, "ldapsuffix") == 0)
- 			{
- 				REQUIRE_AUTH_OPTION(uaLDAP, "ldapsuffix", "ldap");
- 				parsedline->ldapsuffix = pstrdup(c);
- 			}
- 			else if (strcmp(token, "krb_server_hostname") == 0)
- 			{
- 				REQUIRE_AUTH_OPTION(uaKrb5, "krb_server_hostname", "krb5");
- 				parsedline->krb_server_hostname = pstrdup(c);
- 			}
- 			else if (strcmp(token, "krb_realm") == 0)
- 			{
- 				if (parsedline->auth_method != uaKrb5 &&
- 					parsedline->auth_method != uaGSS &&
- 					parsedline->auth_method != uaSSPI)
- 					INVALID_AUTH_OPTION("krb_realm", gettext_noop("krb5, gssapi and sspi"));
- 				parsedline->krb_realm = pstrdup(c);
- 			}
- 			else if (strcmp(token, "include_realm") == 0)
- 			{
- 				if (parsedline->auth_method != uaKrb5 &&
- 					parsedline->auth_method != uaGSS &&
- 					parsedline->auth_method != uaSSPI)
- 					INVALID_AUTH_OPTION("include_realm", gettext_noop("krb5, gssapi and sspi"));
- 				if (strcmp(c, "1") == 0)
- 					parsedline->include_realm = true;
- 				else
- 					parsedline->include_realm = false;
- 			}
- 			else if (strcmp(token, "radiusserver") == 0)
- 			{
- 				REQUIRE_AUTH_OPTION(uaRADIUS, "radiusserver", "radius");
- 
- 				MemSet(&hints, 0, sizeof(hints));
- 				hints.ai_socktype = SOCK_DGRAM;
- 				hints.ai_family = AF_UNSPEC;
- 
- 				ret = pg_getaddrinfo_all(c, NULL, &hints, &gai_result);
- 				if (ret || !gai_result)
- 				{
- 					ereport(LOG,
- 							(errcode(ERRCODE_CONFIG_FILE_ERROR),
- 							 errmsg("could not translate RADIUS server name \"%s\" to address: %s",
- 									c, gai_strerror(ret)),
- 						   errcontext("line %d of configuration file \"%s\"",
- 									  line_num, HbaFileName)));
- 					if (gai_result)
- 						pg_freeaddrinfo_all(hints.ai_family, gai_result);
- 					return false;
- 				}
- 				pg_freeaddrinfo_all(hints.ai_family, gai_result);
- 				parsedline->radiusserver = pstrdup(c);
- 			}
- 			else if (strcmp(token, "radiusport") == 0)
- 			{
- 				REQUIRE_AUTH_OPTION(uaRADIUS, "radiusport", "radius");
- 				parsedline->radiusport = atoi(c);
- 				if (parsedline->radiusport == 0)
- 				{
- 					ereport(LOG,
- 							(errcode(ERRCODE_CONFIG_FILE_ERROR),
- 							 errmsg("invalid RADIUS port number: \"%s\"", c),
- 						   errcontext("line %d of configuration file \"%s\"",
- 									  line_num, HbaFileName)));
- 					return false;
- 				}
- 			}
- 			else if (strcmp(token, "radiussecret") == 0)
- 			{
- 				REQUIRE_AUTH_OPTION(uaRADIUS, "radiussecret", "radius");
- 				parsedline->radiussecret = pstrdup(c);
- 			}
- 			else if (strcmp(token, "radiusidentifier") == 0)
- 			{
- 				REQUIRE_AUTH_OPTION(uaRADIUS, "radiusidentifier", "radius");
- 				parsedline->radiusidentifier = pstrdup(c);
- 			}
- 			else
- 			{
  				ereport(LOG,
  						(errcode(ERRCODE_CONFIG_FILE_ERROR),
! 					errmsg("unrecognized authentication option name: \"%s\"",
! 						   token),
  						 errcontext("line %d of configuration file \"%s\"",
  									line_num, HbaFileName)));
! 				return false;
  			}
  		}
  	}
  
--- 1264,1301 ----
  				 errmsg("cert authentication is only supported on hostssl connections"),
  				 errcontext("line %d of configuration file \"%s\"",
  							line_num, HbaFileName)));
! 		return NULL;
  	}
  
  	/* Parse remaining arguments */
! 	while ((field = lnext(field)) != NULL)
  	{
! 		tokens = lfirst(field);
! 		foreach(tokencell, tokens)
  		{
! 			char	   *val;
! 			token = lfirst(tokencell);
! 
! 			str = pstrdup(token->string);
! 			val = strchr(str, '=');
! 			if (val == NULL)
  			{
  				/*
! 				 * Got something that's not a name=value pair.
  				 */
  				ereport(LOG,
  						(errcode(ERRCODE_CONFIG_FILE_ERROR),
! 						 errmsg("authentication option not in name=value format: %s", token->string),
  						 errcontext("line %d of configuration file \"%s\"",
  									line_num, HbaFileName)));
! 				return NULL;
  			}
+ 
+ 			*val++ = '\0';	/* str now holds "name", val holds "value" */
+ 			if (!parse_hba_auth_opt(str, val, parsedline, line_num))
+ 				/* parse_hba_auth_opt already logged the error message */
+ 				return NULL;
+ 			pfree(str);
  		}
  	}
  
***************
*** 1474,1480 **** parse_hba_line(List *line, int line_num, HbaLine *parsedline)
  						 errmsg("cannot use ldapbasedn, ldapbinddn, ldapbindpasswd, or ldapsearchattribute together with ldapprefix"),
  						 errcontext("line %d of configuration file \"%s\"",
  									line_num, HbaFileName)));
! 				return false;
  			}
  		}
  		else if (!parsedline->ldapbasedn)
--- 1325,1331 ----
  						 errmsg("cannot use ldapbasedn, ldapbinddn, ldapbindpasswd, or ldapsearchattribute together with ldapprefix"),
  						 errcontext("line %d of configuration file \"%s\"",
  									line_num, HbaFileName)));
! 				return NULL;
  			}
  		}
  		else if (!parsedline->ldapbasedn)
***************
*** 1484,1490 **** parse_hba_line(List *line, int line_num, HbaLine *parsedline)
  					 errmsg("authentication method \"ldap\" requires argument \"ldapbasedn\", \"ldapprefix\", or \"ldapsuffix\" to be set"),
  					 errcontext("line %d of configuration file \"%s\"",
  								line_num, HbaFileName)));
! 			return false;
  		}
  	}
  
--- 1335,1341 ----
  					 errmsg("authentication method \"ldap\" requires argument \"ldapbasedn\", \"ldapprefix\", or \"ldapsuffix\" to be set"),
  					 errcontext("line %d of configuration file \"%s\"",
  								line_num, HbaFileName)));
! 			return NULL;
  		}
  	}
  
***************
*** 1502,1516 **** parse_hba_line(List *line, int line_num, HbaLine *parsedline)
  		parsedline->clientcert = true;
  	}
  
! 	return true;
  }
  
- 
  /*
!  *	Scan the (pre-parsed) hba file line by line, looking for a match
!  *	to the port's connection request.
   */
  static bool
  check_hba(hbaPort *port)
  {
  	Oid			roleid;
--- 1353,1580 ----
  		parsedline->clientcert = true;
  	}
  
! 	return parsedline;
  }
  
  /*
!  * Parse one name-value pair as an authentication option into the given
!  * HbaLine.  Return true if we successfully parse the option, false if we
!  * encounter an error.
   */
  static bool
+ parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num)
+ {
+ 	if (strcmp(name, "map") == 0)
+ 	{
+ 		if (hbaline->auth_method != uaIdent &&
+ 			hbaline->auth_method != uaPeer &&
+ 			hbaline->auth_method != uaKrb5 &&
+ 			hbaline->auth_method != uaGSS &&
+ 			hbaline->auth_method != uaSSPI &&
+ 			hbaline->auth_method != uaCert)
+ 			INVALID_AUTH_OPTION("map", gettext_noop("ident, peer, krb5, gssapi, sspi and cert"));
+ 		hbaline->usermap = pstrdup(val);
+ 	}
+ 	else if (strcmp(name, "clientcert") == 0)
+ 	{
+ 		/*
+ 		 * Since we require ctHostSSL, this really can never happen
+ 		 * on non-SSL-enabled builds, so don't bother checking for
+ 		 * USE_SSL.
+ 		 */
+ 		if (hbaline->conntype != ctHostSSL)
+ 		{
+ 			ereport(LOG,
+ 					(errcode(ERRCODE_CONFIG_FILE_ERROR),
+ 					 errmsg("clientcert can only be configured for \"hostssl\" rows"),
+ 				   errcontext("line %d of configuration file \"%s\"",
+ 							  line_num, HbaFileName)));
+ 			return false;
+ 		}
+ 		if (strcmp(val, "1") == 0)
+ 		{
+ 			if (!secure_loaded_verify_locations())
+ 			{
+ 				ereport(LOG,
+ 						(errcode(ERRCODE_CONFIG_FILE_ERROR),
+ 						 errmsg("client certificates can only be checked if a root certificate store is available"),
+ 						 errhint("Make sure the root.crt file is present and readable."),
+ 				   errcontext("line %d of configuration file \"%s\"",
+ 							  line_num, HbaFileName)));
+ 				return false;
+ 			}
+ 			hbaline->clientcert = true;
+ 		}
+ 		else
+ 		{
+ 			if (hbaline->auth_method == uaCert)
+ 			{
+ 				ereport(LOG,
+ 						(errcode(ERRCODE_CONFIG_FILE_ERROR),
+ 						 errmsg("clientcert can not be set to 0 when using \"cert\" authentication"),
+ 				   errcontext("line %d of configuration file \"%s\"",
+ 							  line_num, HbaFileName)));
+ 				return false;
+ 			}
+ 			hbaline->clientcert = false;
+ 		}
+ 	}
+ 	else if (strcmp(name, "pamservice") == 0)
+ 	{
+ 		REQUIRE_AUTH_OPTION(uaPAM, "pamservice", "pam");
+ 		hbaline->pamservice = pstrdup(val);
+ 	}
+ 	else if (strcmp(name, "ldaptls") == 0)
+ 	{
+ 		REQUIRE_AUTH_OPTION(uaLDAP, "ldaptls", "ldap");
+ 		if (strcmp(val, "1") == 0)
+ 			hbaline->ldaptls = true;
+ 		else
+ 			hbaline->ldaptls = false;
+ 	}
+ 	else if (strcmp(name, "ldapserver") == 0)
+ 	{
+ 		REQUIRE_AUTH_OPTION(uaLDAP, "ldapserver", "ldap");
+ 		hbaline->ldapserver = pstrdup(val);
+ 	}
+ 	else if (strcmp(name, "ldapport") == 0)
+ 	{
+ 		REQUIRE_AUTH_OPTION(uaLDAP, "ldapport", "ldap");
+ 		hbaline->ldapport = atoi(val);
+ 		if (hbaline->ldapport == 0)
+ 		{
+ 			ereport(LOG,
+ 					(errcode(ERRCODE_CONFIG_FILE_ERROR),
+ 					 errmsg("invalid LDAP port number: \"%s\"", val),
+ 				   errcontext("line %d of configuration file \"%s\"",
+ 							  line_num, HbaFileName)));
+ 			return false;
+ 		}
+ 	}
+ 	else if (strcmp(name, "ldapbinddn") == 0)
+ 	{
+ 		REQUIRE_AUTH_OPTION(uaLDAP, "ldapbinddn", "ldap");
+ 		hbaline->ldapbinddn = pstrdup(val);
+ 	}
+ 	else if (strcmp(name, "ldapbindpasswd") == 0)
+ 	{
+ 		REQUIRE_AUTH_OPTION(uaLDAP, "ldapbindpasswd", "ldap");
+ 		hbaline->ldapbindpasswd = pstrdup(val);
+ 	}
+ 	else if (strcmp(name, "ldapsearchattribute") == 0)
+ 	{
+ 		REQUIRE_AUTH_OPTION(uaLDAP, "ldapsearchattribute", "ldap");
+ 		hbaline->ldapsearchattribute = pstrdup(val);
+ 	}
+ 	else if (strcmp(name, "ldapbasedn") == 0)
+ 	{
+ 		REQUIRE_AUTH_OPTION(uaLDAP, "ldapbasedn", "ldap");
+ 		hbaline->ldapbasedn = pstrdup(val);
+ 	}
+ 	else if (strcmp(name, "ldapprefix") == 0)
+ 	{
+ 		REQUIRE_AUTH_OPTION(uaLDAP, "ldapprefix", "ldap");
+ 		hbaline->ldapprefix = pstrdup(val);
+ 	}
+ 	else if (strcmp(name, "ldapsuffix") == 0)
+ 	{
+ 		REQUIRE_AUTH_OPTION(uaLDAP, "ldapsuffix", "ldap");
+ 		hbaline->ldapsuffix = pstrdup(val);
+ 	}
+ 	else if (strcmp(name, "krb_server_hostname") == 0)
+ 	{
+ 		REQUIRE_AUTH_OPTION(uaKrb5, "krb_server_hostname", "krb5");
+ 		hbaline->krb_server_hostname = pstrdup(val);
+ 	}
+ 	else if (strcmp(name, "krb_realm") == 0)
+ 	{
+ 		if (hbaline->auth_method != uaKrb5 &&
+ 			hbaline->auth_method != uaGSS &&
+ 			hbaline->auth_method != uaSSPI)
+ 			INVALID_AUTH_OPTION("krb_realm", gettext_noop("krb5, gssapi and sspi"));
+ 		hbaline->krb_realm = pstrdup(val);
+ 	}
+ 	else if (strcmp(name, "include_realm") == 0)
+ 	{
+ 		if (hbaline->auth_method != uaKrb5 &&
+ 			hbaline->auth_method != uaGSS &&
+ 			hbaline->auth_method != uaSSPI)
+ 			INVALID_AUTH_OPTION("include_realm", gettext_noop("krb5, gssapi and sspi"));
+ 		if (strcmp(val, "1") == 0)
+ 			hbaline->include_realm = true;
+ 		else
+ 			hbaline->include_realm = false;
+ 	}
+ 	else if (strcmp(name, "radiusserver") == 0)
+ 	{
+ 		struct addrinfo *gai_result;
+ 		struct addrinfo hints;
+ 		int ret;
+ 
+ 		REQUIRE_AUTH_OPTION(uaRADIUS, "radiusserver", "radius");
+ 
+ 		MemSet(&hints, 0, sizeof(hints));
+ 		hints.ai_socktype = SOCK_DGRAM;
+ 		hints.ai_family = AF_UNSPEC;
+ 
+ 		ret = pg_getaddrinfo_all(val, NULL, &hints, &gai_result);
+ 		if (ret || !gai_result)
+ 		{
+ 			ereport(LOG,
+ 					(errcode(ERRCODE_CONFIG_FILE_ERROR),
+ 					 errmsg("could not translate RADIUS server name \"%s\" to address: %s",
+ 							val, gai_strerror(ret)),
+ 				   errcontext("line %d of configuration file \"%s\"",
+ 							  line_num, HbaFileName)));
+ 			if (gai_result)
+ 				pg_freeaddrinfo_all(hints.ai_family, gai_result);
+ 			return false;
+ 		}
+ 		pg_freeaddrinfo_all(hints.ai_family, gai_result);
+ 		hbaline->radiusserver = pstrdup(val);
+ 	}
+ 	else if (strcmp(name, "radiusport") == 0)
+ 	{
+ 		REQUIRE_AUTH_OPTION(uaRADIUS, "radiusport", "radius");
+ 		hbaline->radiusport = atoi(val);
+ 		if (hbaline->radiusport == 0)
+ 		{
+ 			ereport(LOG,
+ 					(errcode(ERRCODE_CONFIG_FILE_ERROR),
+ 					 errmsg("invalid RADIUS port number: \"%s\"", val),
+ 				   errcontext("line %d of configuration file \"%s\"",
+ 							  line_num, HbaFileName)));
+ 			return false;
+ 		}
+ 	}
+ 	else if (strcmp(name, "radiussecret") == 0)
+ 	{
+ 		REQUIRE_AUTH_OPTION(uaRADIUS, "radiussecret", "radius");
+ 		hbaline->radiussecret = pstrdup(val);
+ 	}
+ 	else if (strcmp(name, "radiusidentifier") == 0)
+ 	{
+ 		REQUIRE_AUTH_OPTION(uaRADIUS, "radiusidentifier", "radius");
+ 		hbaline->radiusidentifier = pstrdup(val);
+ 	}
+ 	else
+ 	{
+ 		ereport(LOG,
+ 				(errcode(ERRCODE_CONFIG_FILE_ERROR),
+ 			errmsg("unrecognized authentication option name: \"%s\"",
+ 				   name),
+ 				 errcontext("line %d of configuration file \"%s\"",
+ 							line_num, HbaFileName)));
+ 		return false;
+ 	}
+ 	return true;
+ }
+ 
+ /*
+  *	Scan the pre-parsed hba file, looking for a match to the port's connection
+  *	request.
+  */
+ static void
  check_hba(hbaPort *port)
  {
  	Oid			roleid;
***************
*** 1589,1660 **** check_hba(hbaPort *port)
  
  		/* Check database and role */
  		if (!check_db(port->database_name, port->user_name, roleid,
! 					  hba->database))
  			continue;
  
! 		if (!check_role(port->user_name, roleid, hba->role))
  			continue;
  
  		/* Found a record that matched! */
  		port->hba = hba;
! 		return true;
  	}
  
  	/* If no matching entry was found, then implicitly reject. */
  	hba = palloc0(sizeof(HbaLine));
  	hba->auth_method = uaImplicitReject;
  	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 an HbaLine structure
-  */
- static void
- free_hba_record(HbaLine *record)
- {
- 	if (record->database)
- 		pfree(record->database);
- 	if (record->role)
- 		pfree(record->role);
- 	if (record->usermap)
- 		pfree(record->usermap);
- 	if (record->pamservice)
- 		pfree(record->pamservice);
- 	if (record->ldapserver)
- 		pfree(record->ldapserver);
- 	if (record->ldapprefix)
- 		pfree(record->ldapprefix);
- 	if (record->ldapsuffix)
- 		pfree(record->ldapsuffix);
- 	if (record->krb_server_hostname)
- 		pfree(record->krb_server_hostname);
- 	if (record->krb_realm)
- 		pfree(record->krb_realm);
- 	pfree(record);
- }
- 
- /*
-  * Free all records on the parsed HBA list
-  */
- 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);
  }
  
  /*
--- 1653,1673 ----
  
  		/* Check database and role */
  		if (!check_db(port->database_name, port->user_name, roleid,
! 					  hba->databases))
  			continue;
  
! 		if (!check_role(port->user_name, roleid, hba->roles))
  			continue;
  
  		/* Found a record that matched! */
  		port->hba = hba;
! 		return;
  	}
  
  	/* If no matching entry was found, then implicitly reject. */
  	hba = palloc0(sizeof(HbaLine));
  	hba->auth_method = uaImplicitReject;
  	port->hba = hba;
  }
  
  /*
***************
*** 1674,1679 **** load_hba(void)
--- 1687,1695 ----
  			   *line_num;
  	List	   *new_parsed_lines = NIL;
  	bool		ok = true;
+ 	MemoryContext	linecxt;
+ 	MemoryContext	oldcxt;
+ 	MemoryContext	hbacxt;
  
  	file = AllocateFile(HbaFileName, "r");
  	if (file == NULL)
***************
*** 1691,1710 **** load_hba(void)
  		return false;
  	}
  
! 	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 indicate there's a problem */
! 			free_hba_record(newline);
  			ok = false;
  
  			/*
--- 1707,1735 ----
  		return false;
  	}
  
! 	linecxt = tokenize_file(HbaFileName, file, &hba_lines, &hba_line_nums);
  	FreeFile(file);
  
  	/* Now parse all the lines */
+ 	hbacxt = AllocSetContextCreate(TopMemoryContext,
+ 								   "hba parser context",
+ 								   ALLOCSET_DEFAULT_MINSIZE,
+ 								   ALLOCSET_DEFAULT_MINSIZE,
+ 								   ALLOCSET_DEFAULT_MAXSIZE);
+ 	oldcxt = MemoryContextSwitchTo(hbacxt);
  	forboth(line, hba_lines, line_num, hba_line_nums)
  	{
  		HbaLine    *newline;
  
! 		if ((newline = parse_hba_line(lfirst(line), lfirst_int(line_num))) == NULL)
  		{
! 			/*
! 			 * Parse error in the file, so indicate there's a problem.  NB: a
! 			 * problem in a line will free the memory for all previous lines as
! 			 * well!
! 			 */
! 			MemoryContextReset(hbacxt);
! 			new_parsed_lines = NIL;
  			ok = false;
  
  			/*
***************
*** 1718,1735 **** load_hba(void)
  		new_parsed_lines = lappend(new_parsed_lines, newline);
  	}
  
! 	/* Free the temporary lists */
! 	free_lines(&hba_lines, &hba_line_nums);
  
  	if (!ok)
  	{
  		/* Parsing failed at one or more rows, so bail out */
! 		clean_hba_list(new_parsed_lines);
  		return false;
  	}
  
  	/* Loaded new file successfully, replace the one we use */
! 	clean_hba_list(parsed_hba_lines);
  	parsed_hba_lines = new_parsed_lines;
  
  	return true;
--- 1743,1763 ----
  		new_parsed_lines = lappend(new_parsed_lines, newline);
  	}
  
! 	/* Free tokenizer memory */
! 	MemoryContextDelete(linecxt);
! 	MemoryContextSwitchTo(oldcxt);
  
  	if (!ok)
  	{
  		/* Parsing failed at one or more rows, so bail out */
! 		MemoryContextDelete(hbacxt);
  		return false;
  	}
  
  	/* Loaded new file successfully, replace the one we use */
! 	if (parsed_hba_context != NULL)
! 		MemoryContextDelete(parsed_hba_context);
! 	parsed_hba_context = hbacxt;
  	parsed_hba_lines = new_parsed_lines;
  
  	return true;
***************
*** 1746,1753 **** parse_ident_usermap(List *line, int line_number, const char *usermap_name,
  					const char *pg_role, const char *ident_user,
  					bool case_insensitive, bool *found_p, bool *error_p)
  {
! 	ListCell   *line_item;
! 	char	   *token;
  	char	   *file_map;
  	char	   *file_pgrole;
  	char	   *file_ident_user;
--- 1774,1782 ----
  					const char *pg_role, const char *ident_user,
  					bool case_insensitive, bool *found_p, bool *error_p)
  {
! 	ListCell   *field;
! 	List	   *tokens;
! 	HbaToken   *token;
  	char	   *file_map;
  	char	   *file_pgrole;
  	char	   *file_ident_user;
***************
*** 1756,1780 **** parse_ident_usermap(List *line, int line_number, const char *usermap_name,
  	*error_p = false;
  
  	Assert(line != NIL);
! 	line_item = list_head(line);
  
  	/* Get the map token (must exist) */
! 	token = lfirst(line_item);
! 	file_map = token;
  
  	/* Get the ident user token */
! 	line_item = lnext(line_item);
! 	if (!line_item)
! 		goto ident_syntax;
! 	token = lfirst(line_item);
! 	file_ident_user = token;
  
  	/* Get the PG rolename token */
! 	line_item = lnext(line_item);
! 	if (!line_item)
! 		goto ident_syntax;
! 	token = lfirst(line_item);
! 	file_pgrole = token;
  
  	if (strcmp(file_map, usermap_name) != 0)
  		/* Line does not match the map name we're looking for, so just abort */
--- 1785,1813 ----
  	*error_p = false;
  
  	Assert(line != NIL);
! 	field = list_head(line);
  
  	/* Get the map token (must exist) */
! 	tokens = lfirst(field);
! 	IDENT_MULTI_VALUE(tokens);
! 	token = linitial(tokens);
! 	file_map = token->string;
  
  	/* Get the ident user token */
! 	field = lnext(field);
! 	IDENT_FIELD_ABSENT(field);
! 	tokens = lfirst(field);
! 	IDENT_MULTI_VALUE(tokens);
! 	token = linitial(tokens);
! 	file_ident_user = token->string;
  
  	/* Get the PG rolename token */
! 	field = lnext(field);
! 	IDENT_FIELD_ABSENT(field);
! 	tokens = lfirst(field);
! 	IDENT_MULTI_VALUE(tokens);
! 	token = linitial(tokens);
! 	file_pgrole = token->string;
  
  	if (strcmp(file_map, usermap_name) != 0)
  		/* Line does not match the map name we're looking for, so just abort */
***************
*** 1913,1927 **** parse_ident_usermap(List *line, int line_number, const char *usermap_name,
  				*found_p = true;
  		}
  	}
- 
  	return;
- 
- ident_syntax:
- 	ereport(LOG,
- 			(errcode(ERRCODE_CONFIG_FILE_ERROR),
- 			 errmsg("missing entry in file \"%s\" at end of line %d",
- 					IdentFileName, line_number)));
- 	*error_p = true;
  }
  
  
--- 1946,1952 ----
***************
*** 1989,2003 **** check_usermap(const char *usermap_name,
  
  
  /*
!  * Read the ident config file and create a List of Lists of tokens in the file.
   */
  void
  load_ident(void)
  {
  	FILE	   *file;
  
! 	if (ident_lines || ident_line_nums)
! 		free_lines(&ident_lines, &ident_line_nums);
  
  	file = AllocateFile(IdentFileName, "r");
  	if (file == NULL)
--- 2014,2034 ----
  
  
  /*
!  * Read the ident config file and populate ident_lines and ident_line_nums.
!  *
!  * Like parsed_hba_lines, ident_lines is a triple-nested List of lines, fields
!  * and tokens.
   */
  void
  load_ident(void)
  {
  	FILE	   *file;
  
! 	if (ident_context != NULL)
! 	{
! 		MemoryContextDelete(ident_context);
! 		ident_context = NULL;
! 	}
  
  	file = AllocateFile(IdentFileName, "r");
  	if (file == NULL)
***************
*** 2010,2016 **** load_ident(void)
  	}
  	else
  	{
! 		tokenize_file(IdentFileName, file, &ident_lines, &ident_line_nums);
  		FreeFile(file);
  	}
  }
--- 2041,2048 ----
  	}
  	else
  	{
! 		ident_context = tokenize_file(IdentFileName, file, &ident_lines,
! 									  &ident_line_nums);
  		FreeFile(file);
  	}
  }
***************
*** 2022,2036 **** load_ident(void)
   *	"database" from frontend "raddr", user "user".	Return the method and
   *	an optional argument (stored in fields of *port), and STATUS_OK.
   *
!  *	Note that STATUS_ERROR indicates a problem with the hba config file.
!  *	If the file is OK but does not contain any entry matching the request,
!  *	we return STATUS_OK and method = uaImplicitReject.
   */
! int
  hba_getauthmethod(hbaPort *port)
  {
! 	if (check_hba(port))
! 		return STATUS_OK;
! 	else
! 		return STATUS_ERROR;
  }
--- 2054,2064 ----
   *	"database" from frontend "raddr", user "user".	Return the method and
   *	an optional argument (stored in fields of *port), and STATUS_OK.
   *
!  *	If the file does not contain any entry matching the request, we return
!  *	method = uaImplicitReject.
   */
! void
  hba_getauthmethod(hbaPort *port)
  {
! 	check_hba(port);
  }
*** a/src/include/libpq/hba.h
--- b/src/include/libpq/hba.h
***************
*** 49,60 **** typedef enum ConnType
  	ctHostNoSSL
  } ConnType;
  
! typedef struct
  {
  	int			linenumber;
  	ConnType	conntype;
! 	char	   *database;
! 	char	   *role;
  	struct sockaddr_storage addr;
  	struct sockaddr_storage mask;
  	IPCompareMethod ip_cmp_method;
--- 49,60 ----
  	ctHostNoSSL
  } ConnType;
  
! typedef struct HbaLine
  {
  	int			linenumber;
  	ConnType	conntype;
! 	List	   *databases;
! 	List	   *roles;
  	struct sockaddr_storage addr;
  	struct sockaddr_storage mask;
  	IPCompareMethod ip_cmp_method;
***************
*** 87,93 **** typedef struct Port hbaPort;
  
  extern bool load_hba(void);
  extern void load_ident(void);
! extern int	hba_getauthmethod(hbaPort *port);
  extern int check_usermap(const char *usermap_name,
  			  const char *pg_role, const char *auth_user,
  			  bool case_sensitive);
--- 87,93 ----
  
  extern bool load_hba(void);
  extern void load_ident(void);
! extern void hba_getauthmethod(hbaPort *port);
  extern int check_usermap(const char *usermap_name,
  			  const char *pg_role, const char *auth_user,
  			  bool case_sensitive);
