[TODO] Process pg_hba.conf keywords as case-insensitive

Started by Viswanatham kirankumarover 11 years ago20 messages
#1Viswanatham kirankumar
viswanatham.kirankumar@huawei.com
1 attachment(s)

Attached patch is implementing following TODO item
Process pg_hba.conf keywords as case-insensitive

* More robust pg_hba.conf parsing/error logging<http://archives.postgresql.org/pgsql-hackers/2009-09/msg00432.php&gt;

Thanks & Regards,
Viswanatham Kiran Kumar

Attachments:

pg_hba.conf_keywords_as_case-insensitive.patchapplication/octet-stream; name=pg_hba.conf_keywords_as_case-insensitive.patchDownload
*** a/src/backend/libpq/hba.c
--- b/src/backend/libpq/hba.c
***************
*** 61,67 **** typedef struct check_network_data
  } check_network_data;
  
  
! #define token_is_keyword(t, k)	(!t->quoted && strcmp(t->string, k) == 0)
  #define token_matches(t, k)  (strcmp(t->string, k) == 0)
  
  /*
--- 61,67 ----
  } check_network_data;
  
  
! #define token_is_keyword(t, k)	(!t->quoted && pg_strcasecmp(t->string, k) == 0)
  #define token_matches(t, k)  (strcmp(t->string, k) == 0)
  
  /*
***************
*** 881,887 **** parse_hba_line(List *line, int line_num, char *raw_line)
  		return NULL;
  	}
  	token = linitial(tokens);
! 	if (strcmp(token->string, "local") == 0)
  	{
  #ifdef HAVE_UNIX_SOCKETS
  		parsedline->conntype = ctLocal;
--- 881,887 ----
  		return NULL;
  	}
  	token = linitial(tokens);
! 	if (pg_strcasecmp(token->string, "local") == 0)
  	{
  #ifdef HAVE_UNIX_SOCKETS
  		parsedline->conntype = ctLocal;
***************
*** 894,905 **** parse_hba_line(List *line, int line_num, char *raw_line)
  		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
--- 894,905 ----
  		return NULL;
  #endif
  	}
! 	else if (pg_strcasecmp(token->string, "host") == 0 ||
! 			 pg_strcasecmp(token->string, "hostssl") == 0 ||
! 			 pg_strcasecmp(token->string, "hostnossl") == 0)
  	{
  
! 		if ((token->string[4] == 's') || (token->string[4] == 'S'))	/* "hostssl" */
  		{
  			/* SSL support must be actually active, else complain */
  #ifdef USE_SSL
***************
*** 926,932 **** parse_hba_line(List *line, int line_num, char *raw_line)
  #endif
  		}
  #ifdef USE_SSL
! 		else if (token->string[4] == 'n')		/* "hostnossl" */
  		{
  			parsedline->conntype = ctHostNoSSL;
  		}
--- 926,932 ----
  #endif
  		}
  #ifdef USE_SSL
! 		else if ((token->string[4] == 'n') || (token->string[4] == 'N')) /* "hostnossl" */
  		{
  			parsedline->conntype = ctHostNoSSL;
  		}
***************
*** 1181,1209 **** parse_hba_line(List *line, int line_num, char *raw_line)
  	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, "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)
  		{
--- 1181,1209 ----
  	token = linitial(tokens);
  
  	unsupauth = NULL;
! 	if (pg_strcasecmp(token->string, "trust") == 0)
  		parsedline->auth_method = uaTrust;
! 	else if (pg_strcasecmp(token->string, "ident") == 0)
  		parsedline->auth_method = uaIdent;
! 	else if (pg_strcasecmp(token->string, "peer") == 0)
  		parsedline->auth_method = uaPeer;
! 	else if (pg_strcasecmp(token->string, "password") == 0)
  		parsedline->auth_method = uaPassword;
! 	else if (pg_strcasecmp(token->string, "gss") == 0)
  #ifdef ENABLE_GSS
  		parsedline->auth_method = uaGSS;
  #else
  		unsupauth = "gss";
  #endif
! 	else if (pg_strcasecmp(token->string, "sspi") == 0)
  #ifdef ENABLE_SSPI
  		parsedline->auth_method = uaSSPI;
  #else
  		unsupauth = "sspi";
  #endif
! 	else if (pg_strcasecmp(token->string, "reject") == 0)
  		parsedline->auth_method = uaReject;
! 	else if (pg_strcasecmp(token->string, "md5") == 0)
  	{
  		if (Db_user_namespace)
  		{
***************
*** 1216,1240 **** parse_hba_line(List *line, int line_num, char *raw_line)
  		}
  		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
  	{
--- 1216,1240 ----
  		}
  		parsedline->auth_method = uaMD5;
  	}
! 	else if (pg_strcasecmp(token->string, "pam") == 0)
  #ifdef USE_PAM
  		parsedline->auth_method = uaPAM;
  #else
  		unsupauth = "pam";
  #endif
! 	else if (pg_strcasecmp(token->string, "ldap") == 0)
  #ifdef USE_LDAP
  		parsedline->auth_method = uaLDAP;
  #else
  		unsupauth = "ldap";
  #endif
! 	else if (pg_strcasecmp(token->string, "cert") == 0)
  #ifdef USE_SSL
  		parsedline->auth_method = uaCert;
  #else
  		unsupauth = "cert";
  #endif
! 	else if (pg_strcasecmp(token->string, "radius") == 0)
  		parsedline->auth_method = uaRADIUS;
  	else
  	{
***************
*** 1408,1414 **** parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num)
  	hbaline->ldapscope = LDAP_SCOPE_SUBTREE;
  #endif
  
! 	if (strcmp(name, "map") == 0)
  	{
  		if (hbaline->auth_method != uaIdent &&
  			hbaline->auth_method != uaPeer &&
--- 1408,1414 ----
  	hbaline->ldapscope = LDAP_SCOPE_SUBTREE;
  #endif
  
! 	if (pg_strcasecmp(name, "map") == 0)
  	{
  		if (hbaline->auth_method != uaIdent &&
  			hbaline->auth_method != uaPeer &&
***************
*** 1418,1424 **** parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num)
  			INVALID_AUTH_OPTION("map", gettext_noop("ident, peer, gssapi, sspi, and cert"));
  		hbaline->usermap = pstrdup(val);
  	}
! 	else if (strcmp(name, "clientcert") == 0)
  	{
  		/*
  		 * Since we require ctHostSSL, this really can never happen on
--- 1418,1424 ----
  			INVALID_AUTH_OPTION("map", gettext_noop("ident, peer, gssapi, sspi, and cert"));
  		hbaline->usermap = pstrdup(val);
  	}
! 	else if (pg_strcasecmp(name, "clientcert") == 0)
  	{
  		/*
  		 * Since we require ctHostSSL, this really can never happen on
***************
*** 1461,1472 **** parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num)
  			hbaline->clientcert = false;
  		}
  	}
! 	else if (strcmp(name, "pamservice") == 0)
  	{
  		REQUIRE_AUTH_OPTION(uaPAM, "pamservice", "pam");
  		hbaline->pamservice = pstrdup(val);
  	}
! 	else if (strcmp(name, "ldapurl") == 0)
  	{
  #ifdef LDAP_API_FEATURE_X_OPENLDAP
  		LDAPURLDesc *urldata;
--- 1461,1472 ----
  			hbaline->clientcert = false;
  		}
  	}
! 	else if (pg_strcasecmp(name, "pamservice") == 0)
  	{
  		REQUIRE_AUTH_OPTION(uaPAM, "pamservice", "pam");
  		hbaline->pamservice = pstrdup(val);
  	}
! 	else if (pg_strcasecmp(name, "ldapurl") == 0)
  	{
  #ifdef LDAP_API_FEATURE_X_OPENLDAP
  		LDAPURLDesc *urldata;
***************
*** 1515,1521 **** parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num)
  				 errmsg("LDAP URLs not supported on this platform")));
  #endif   /* not OpenLDAP */
  	}
! 	else if (strcmp(name, "ldaptls") == 0)
  	{
  		REQUIRE_AUTH_OPTION(uaLDAP, "ldaptls", "ldap");
  		if (strcmp(val, "1") == 0)
--- 1515,1521 ----
  				 errmsg("LDAP URLs not supported on this platform")));
  #endif   /* not OpenLDAP */
  	}
! 	else if (pg_strcasecmp(name, "ldaptls") == 0)
  	{
  		REQUIRE_AUTH_OPTION(uaLDAP, "ldaptls", "ldap");
  		if (strcmp(val, "1") == 0)
***************
*** 1523,1534 **** parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num)
  		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);
--- 1523,1534 ----
  		else
  			hbaline->ldaptls = false;
  	}
! 	else if (pg_strcasecmp(name, "ldapserver") == 0)
  	{
  		REQUIRE_AUTH_OPTION(uaLDAP, "ldapserver", "ldap");
  		hbaline->ldapserver = pstrdup(val);
  	}
! 	else if (pg_strcasecmp(name, "ldapport") == 0)
  	{
  		REQUIRE_AUTH_OPTION(uaLDAP, "ldapport", "ldap");
  		hbaline->ldapport = atoi(val);
***************
*** 1542,1585 **** parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num)
  			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_realm") == 0)
  	{
  		if (hbaline->auth_method != uaGSS &&
  			hbaline->auth_method != uaSSPI)
  			INVALID_AUTH_OPTION("krb_realm", gettext_noop("gssapi and sspi"));
  		hbaline->krb_realm = pstrdup(val);
  	}
! 	else if (strcmp(name, "include_realm") == 0)
  	{
  		if (hbaline->auth_method != uaGSS &&
  			hbaline->auth_method != uaSSPI)
--- 1542,1585 ----
  			return false;
  		}
  	}
! 	else if (pg_strcasecmp(name, "ldapbinddn") == 0)
  	{
  		REQUIRE_AUTH_OPTION(uaLDAP, "ldapbinddn", "ldap");
  		hbaline->ldapbinddn = pstrdup(val);
  	}
! 	else if (pg_strcasecmp(name, "ldapbindpasswd") == 0)
  	{
  		REQUIRE_AUTH_OPTION(uaLDAP, "ldapbindpasswd", "ldap");
  		hbaline->ldapbindpasswd = pstrdup(val);
  	}
! 	else if (pg_strcasecmp(name, "ldapsearchattribute") == 0)
  	{
  		REQUIRE_AUTH_OPTION(uaLDAP, "ldapsearchattribute", "ldap");
  		hbaline->ldapsearchattribute = pstrdup(val);
  	}
! 	else if (pg_strcasecmp(name, "ldapbasedn") == 0)
  	{
  		REQUIRE_AUTH_OPTION(uaLDAP, "ldapbasedn", "ldap");
  		hbaline->ldapbasedn = pstrdup(val);
  	}
! 	else if (pg_strcasecmp(name, "ldapprefix") == 0)
  	{
  		REQUIRE_AUTH_OPTION(uaLDAP, "ldapprefix", "ldap");
  		hbaline->ldapprefix = pstrdup(val);
  	}
! 	else if (pg_strcasecmp(name, "ldapsuffix") == 0)
  	{
  		REQUIRE_AUTH_OPTION(uaLDAP, "ldapsuffix", "ldap");
  		hbaline->ldapsuffix = pstrdup(val);
  	}
! 	else if (pg_strcasecmp(name, "krb_realm") == 0)
  	{
  		if (hbaline->auth_method != uaGSS &&
  			hbaline->auth_method != uaSSPI)
  			INVALID_AUTH_OPTION("krb_realm", gettext_noop("gssapi and sspi"));
  		hbaline->krb_realm = pstrdup(val);
  	}
! 	else if (pg_strcasecmp(name, "include_realm") == 0)
  	{
  		if (hbaline->auth_method != uaGSS &&
  			hbaline->auth_method != uaSSPI)
***************
*** 1589,1595 **** parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num)
  		else
  			hbaline->include_realm = false;
  	}
! 	else if (strcmp(name, "radiusserver") == 0)
  	{
  		struct addrinfo *gai_result;
  		struct addrinfo hints;
--- 1589,1595 ----
  		else
  			hbaline->include_realm = false;
  	}
! 	else if (pg_strcasecmp(name, "radiusserver") == 0)
  	{
  		struct addrinfo *gai_result;
  		struct addrinfo hints;
***************
*** 1617,1623 **** parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num)
  		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);
--- 1617,1623 ----
  		pg_freeaddrinfo_all(hints.ai_family, gai_result);
  		hbaline->radiusserver = pstrdup(val);
  	}
! 	else if (pg_strcasecmp(name, "radiusport") == 0)
  	{
  		REQUIRE_AUTH_OPTION(uaRADIUS, "radiusport", "radius");
  		hbaline->radiusport = atoi(val);
***************
*** 1631,1642 **** parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num)
  			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);
--- 1631,1642 ----
  			return false;
  		}
  	}
! 	else if (pg_strcasecmp(name, "radiussecret") == 0)
  	{
  		REQUIRE_AUTH_OPTION(uaRADIUS, "radiussecret", "radius");
  		hbaline->radiussecret = pstrdup(val);
  	}
! 	else if (pg_strcasecmp(name, "radiusidentifier") == 0)
  	{
  		REQUIRE_AUTH_OPTION(uaRADIUS, "radiusidentifier", "radius");
  		hbaline->radiusidentifier = pstrdup(val);
#2Michael Paquier
michael.paquier@gmail.com
In reply to: Viswanatham kirankumar (#1)
Re: [TODO] Process pg_hba.conf keywords as case-insensitive

On Wed, Jul 16, 2014 at 6:23 PM, Viswanatham kirankumar
<viswanatham.kirankumar@huawei.com> wrote:

Attached patch is implementing following TODO item

Process pg_hba.conf keywords as case-insensitive

More robust pg_hba.conf parsing/error logging

You should consider adding this patch to the next commit fest:
https://commitfest.postgresql.org/action/commitfest_view?id=23
Regards,
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#3Christoph Berg
cb@df7cb.de
In reply to: Viswanatham kirankumar (#1)
Re: [TODO] Process pg_hba.conf keywords as case-insensitive

Re: Viswanatham kirankumar 2014-07-16 <EC867DEF52699D4189B584A14BAA7C2165440538@blreml504-mbx.china.huawei.com>

Attached patch is implementing following TODO item
Process pg_hba.conf keywords as case-insensitive

* More robust pg_hba.conf parsing/error logging<http://archives.postgresql.org/pgsql-hackers/2009-09/msg00432.php&gt;

Hmm. I see a case for accepting "ALL" (as in hosts.allow(5)), so +1 on
that, but I don't think the other keywords like "host" and "peer"
should be valid in upper case.

Possibly things like "MD5" and "GSSAPI" are naturally spelled in upper
case, but I have my doubts about the rest.

Christoph
--
cb@df7cb.de | http://www.df7cb.de/

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#4Tom Lane
tgl@sss.pgh.pa.us
In reply to: Christoph Berg (#3)
Re: [TODO] Process pg_hba.conf keywords as case-insensitive

Christoph Berg <cb@df7cb.de> writes:

Re: Viswanatham kirankumar 2014-07-16 <EC867DEF52699D4189B584A14BAA7C2165440538@blreml504-mbx.china.huawei.com>

Attached patch is implementing following TODO item
Process pg_hba.conf keywords as case-insensitive

Hmm. I see a case for accepting "ALL" (as in hosts.allow(5)), so +1 on
that, but I don't think the other keywords like "host" and "peer"
should be valid in upper case.

I think the argument was that SQL users are accustomed to thinking
that keywords are case-insensitive. It makes sense to me that we
should adopt that same convention in pg_hba.conf.

Re-reading the original thread, there was also concern about whether
we should try to make quoting/casefolding behave more like it does in SQL,
specifically for matching pg_hba.conf items to SQL identifiers (database
and role names). This patch doesn't seem to have addressed that part
of it, but I think we need to think those things through before we
just do a blind s/strcmp/pg_strcasecmp/g. Otherwise we might find that
we've added ambiguity that will give us trouble when we do try to fix
that.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#5Christoph Berg
cb@df7cb.de
In reply to: Tom Lane (#4)
Re: [TODO] Process pg_hba.conf keywords as case-insensitive

Re: Tom Lane 2014-07-16 <30956.1405532518@sss.pgh.pa.us>

Christoph Berg <cb@df7cb.de> writes:

Re: Viswanatham kirankumar 2014-07-16 <EC867DEF52699D4189B584A14BAA7C2165440538@blreml504-mbx.china.huawei.com>

Attached patch is implementing following TODO item
Process pg_hba.conf keywords as case-insensitive

Hmm. I see a case for accepting "ALL" (as in hosts.allow(5)), so +1 on
that, but I don't think the other keywords like "host" and "peer"
should be valid in upper case.

I think the argument was that SQL users are accustomed to thinking
that keywords are case-insensitive. It makes sense to me that we
should adopt that same convention in pg_hba.conf.

One place that's been bugging me where case-insensitivity would really
make sense is this:

# set work_mem = '1mb';
ERROR: 22023: invalid value for parameter "work_mem": "1mb"
HINT: Valid units for this parameter are "kB", "MB", and "GB".

Christoph
--
cb@df7cb.de | http://www.df7cb.de/

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#6Tom Lane
tgl@sss.pgh.pa.us
In reply to: Christoph Berg (#5)
Re: [TODO] Process pg_hba.conf keywords as case-insensitive

Christoph Berg <cb@df7cb.de> writes:

One place that's been bugging me where case-insensitivity would really
make sense is this:

# set work_mem = '1mb';
ERROR: 22023: invalid value for parameter "work_mem": "1mb"
HINT: Valid units for this parameter are "kB", "MB", and "GB".

Yeah ... there was some pedantry about how "kB" and "KB" mean different
things. IMO that's mere pedantry, but ...

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#7Gavin Flower
GavinFlower@archidevsys.co.nz
In reply to: Tom Lane (#6)
Re: [TODO] Process pg_hba.conf keywords as case-insensitive

On 18/07/14 04:08, Tom Lane wrote:

Christoph Berg <cb@df7cb.de> writes:

One place that's been bugging me where case-insensitivity would really
make sense is this:
# set work_mem = '1mb';
ERROR: 22023: invalid value for parameter "work_mem": "1mb"
HINT: Valid units for this parameter are "kB", "MB", and "GB".

Yeah ... there was some pedantry about how "kB" and "KB" mean different
things. IMO that's mere pedantry, but ...

regards, tom lane

But kb & kB do mean different things: kilobits vs kilobytes! :-)
(Network throughput seems to be always in bits per second - my broadband
download is quoted at 100Mb/s, whereas I get 12MB/s download at best.)

Cheers,
Gavin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#8Craig Ringer
craig@2ndquadrant.com
In reply to: Tom Lane (#4)
Re: [TODO] Process pg_hba.conf keywords as case-insensitive

On 07/17/2014 01:41 AM, Tom Lane wrote:

Christoph Berg <cb@df7cb.de> writes:

Re: Viswanatham kirankumar 2014-07-16 <EC867DEF52699D4189B584A14BAA7C2165440538@blreml504-mbx.china.huawei.com>

Attached patch is implementing following TODO item
Process pg_hba.conf keywords as case-insensitive

Hmm. I see a case for accepting "ALL" (as in hosts.allow(5)), so +1 on
that, but I don't think the other keywords like "host" and "peer"
should be valid in upper case.

I think the argument was that SQL users are accustomed to thinking
that keywords are case-insensitive. It makes sense to me that we
should adopt that same convention in pg_hba.conf.

Re-reading the original thread, there was also concern about whether
we should try to make quoting/casefolding behave more like it does in SQL,
specifically for matching pg_hba.conf items to SQL identifiers (database
and role names). This patch doesn't seem to have addressed that part
of it, but I think we need to think those things through before we
just do a blind s/strcmp/pg_strcasecmp/g. Otherwise we might find that
we've added ambiguity that will give us trouble when we do try to fix
that.

It's worth noting that pg_ident.conf uses SQL-like case-folding and
quoting, though I don't think it's documented.

We should certainly be using the same thing in pg_hba.conf IMO.

--
Craig Ringer http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#9Viswanatham kirankumar
viswanatham.kirankumar@huawei.com
In reply to: Tom Lane (#4)
1 attachment(s)
Re: [TODO] Process pg_hba.conf keywords as case-insensitive

On 16 July 2014 23:12, Tom Lane wrote

Christoph Berg <cb@df7cb.de> writes:
Re: Viswanatham kirankumar 2014-07-16
<EC867DEF52699D4189B584A14BAA7C2165440538@blreml504-mbx.china.huawei.com>

Attached patch is implementing following TODO item Process
pg_hba.conf keywords as case-insensitive

Hmm. I see a case for accepting "ALL" (as in hosts.allow(5)), so +1 on
that, but I don't think the other keywords like "host" and "peer"
should be valid in upper case.

I think the argument was that SQL users are accustomed to thinking that keywords are
case-insensitive. It makes sense to me that we should adopt that same convention in pg_hba.conf.

Re-reading the original thread, there was also concern about whether
we should try to make quoting/casefolding behave more like it does in SQL,
specifically for matching pg_hba.conf items to SQL identifiers (database and role names).
This patch doesn't seem to have addressed that part of it, but I think we need to think those
things through before we just do a blind s/strcmp/pg_strcasecmp/g. Otherwise we might
find that we've added ambiguity that will give us trouble when we do try to fix that.

I had updated as per you review comments

1) database and role names behave similar to SQL identifiers (case-sensitive / case-folding).

2) users and user-groups only requires special handling and behavior as follows
Normal user :
A. unquoted ( USER ) will be treated as user ( downcase ).
B. quoted ( "USeR" ) will be treated as USeR (case-sensitive).
C. quoted ( "+USER" ) will be treated as normal user +USER (i.e. will not be considered as user-group) and case-sensitive as string is quoted.
User Group :
A. unquoted ( +USERGROUP ) will be treated as +usergruop ( downcase ).
B. plus quoted ( +"UserGROUP" ) will be treated as +UserGROUP (case-sensitive).

3) Host name is not a SQL object so it will be treated as case-sensitive
except for all, samehost, samenet are considered as keywords.
For these user need to use quotes to differentiate between hostname and keywords.

4) All the fixed keywords mention in pg_hba.conf and Client Authentication section will be considered as keywords
Eg: host, local, hostssl etc..

Thanks & Regards,
VISWANATHAM KIRAN KUMAR
HUAWEI TECHNOLOGIES INDIA PVT. LTD.

Attachments:

pg_hba.conf_keywords_as_case-insensitive_v2.patchapplication/octet-stream; name=pg_hba.conf_keywords_as_case-insensitive_v2.patchDownload
*** a/src/backend/libpq/hba.c
--- b/src/backend/libpq/hba.c
***************
*** 60,68 **** typedef struct check_network_data
  	bool		result;			/* set to true if match */
  } check_network_data;
  
  
! #define token_is_keyword(t, k)	(!t->quoted && strcmp(t->string, k) == 0)
! #define token_matches(t, k)  (strcmp(t->string, k) == 0)
  
  /*
   * A single string token lexed from the HBA config file, together with whether
--- 60,77 ----
  	bool		result;			/* set to true if match */
  } check_network_data;
  
+ typedef enum token_type
+ {
+ 	QUOTED_STRING,				/* Quoted STRING */
+ 	UNQUOTED_STRING,			/* Unquoted string */
+ 	SPECIAL_PLUS_QUOTED_STRING	/* Is also unquoted string but after '+' it is
+ 								 * quoted eg: +"ROLENAME". used for special
+ 								 * handling incase of user-group/roles */
+ }	token_type;
  
! #define token_is_keyword(t, k)	(t->toktype != QUOTED_STRING && pg_strcasecmp(t->string, k) == 0)
! #define token_matches(t, k)		((t->toktype == QUOTED_STRING && strcmp(t->string, k) == 0) \
! 								   || (t->toktype != QUOTED_STRING && pg_strcasecmp(t->string, k) == 0))
  
  /*
   * A single string token lexed from the HBA config file, together with whether
***************
*** 71,77 **** typedef struct check_network_data
  typedef struct HbaToken
  {
  	char	   *string;
! 	bool		quoted;
  } HbaToken;
  
  /*
--- 80,86 ----
  typedef struct HbaToken
  {
  	char	   *string;
! 	token_type	toktype;
  } HbaToken;
  
  /*
***************
*** 123,128 **** pg_isblank(const char c)
--- 132,141 ----
   * the first character.  (We use that to prevent "@x" from being treated
   * as a file inclusion request.  Note that @"x" should be so treated;
   * we want to allow that to support embedded spaces in file paths.)
+  * we set *special_plus_quote to indicate whether there was quoting after "+"
+  * charecter. (We use this to prevent +"ROLENAME" from treating as unquoted
+  * string as first charecter is not '"', but which require special handling
+  * only incase of role/user-group name).
   * We set *terminating_comma to indicate whether the token is terminated by a
   * comma (which is not returned.)
   *
***************
*** 137,143 **** pg_isblank(const char c)
   */
  static bool
  next_token(char **lineptr, char *buf, int bufsz, bool *initial_quote,
! 		   bool *terminating_comma)
  {
  	int			c;
  	char	   *start_buf = buf;
--- 150,156 ----
   */
  static bool
  next_token(char **lineptr, char *buf, int bufsz, bool *initial_quote,
! 		   bool *special_plus_quote, bool *terminating_comma)
  {
  	int			c;
  	char	   *start_buf = buf;
***************
*** 151,156 **** next_token(char **lineptr, char *buf, int bufsz, bool *initial_quote,
--- 164,170 ----
  
  	*initial_quote = false;
  	*terminating_comma = false;
+ 	*special_plus_quote = false;
  
  	/* Move over initial whitespace and commas */
  	while ((c = (*(*lineptr)++)) != '\0' && (pg_isblank(c) || c == ','))
***************
*** 162,167 **** next_token(char **lineptr, char *buf, int bufsz, bool *initial_quote,
--- 176,186 ----
  		return false;
  	}
  
+ 	if ((c == '+') && *(*lineptr) == '"')
+ 	{
+ 		*special_plus_quote = true;
+ 	}
+ 
  	/*
  	 * Build a token in buf of next characters up to EOF, EOL, unquoted comma,
  	 * or unquoted whitespace.
***************
*** 232,238 **** next_token(char **lineptr, char *buf, int bufsz, bool *initial_quote,
  }
  
  static HbaToken *
! make_hba_token(char *token, bool quoted)
  {
  	HbaToken   *hbatoken;
  	int			toklen;
--- 251,257 ----
  }
  
  static HbaToken *
! make_hba_token(char *token, token_type toktype)
  {
  	HbaToken   *hbatoken;
  	int			toklen;
***************
*** 240,246 **** make_hba_token(char *token, bool quoted)
  	toklen = strlen(token);
  	hbatoken = (HbaToken *) palloc(sizeof(HbaToken) + toklen + 1);
  	hbatoken->string = (char *) hbatoken + sizeof(HbaToken);
! 	hbatoken->quoted = quoted;
  	memcpy(hbatoken->string, token, toklen + 1);
  
  	return hbatoken;
--- 259,265 ----
  	toklen = strlen(token);
  	hbatoken = (HbaToken *) palloc(sizeof(HbaToken) + toklen + 1);
  	hbatoken->string = (char *) hbatoken + sizeof(HbaToken);
! 	hbatoken->toktype = toktype;
  	memcpy(hbatoken->string, token, toklen + 1);
  
  	return hbatoken;
***************
*** 252,258 **** make_hba_token(char *token, bool quoted)
  static HbaToken *
  copy_hba_token(HbaToken *in)
  {
! 	HbaToken   *out = make_hba_token(in->string, in->quoted);
  
  	return out;
  }
--- 271,277 ----
  static HbaToken *
  copy_hba_token(HbaToken *in)
  {
! 	HbaToken   *out = make_hba_token(in->string, in->toktype);
  
  	return out;
  }
***************
*** 270,287 **** next_field_expand(const char *filename, char **lineptr)
  	char		buf[MAX_TOKEN];
  	bool		trailing_comma;
  	bool		initial_quote;
  	List	   *tokens = NIL;
  
  	do
  	{
! 		if (!next_token(lineptr, buf, sizeof(buf), &initial_quote, &trailing_comma))
  			break;
  
  		/* Is this referencing a file? */
  		if (!initial_quote && buf[0] == '@' && buf[1] != '\0')
  			tokens = tokenize_inc_file(tokens, filename, buf + 1);
  		else
! 			tokens = lappend(tokens, make_hba_token(buf, initial_quote));
  	} while (trailing_comma);
  
  	return tokens;
--- 289,309 ----
  	char		buf[MAX_TOKEN];
  	bool		trailing_comma;
  	bool		initial_quote;
+ 	bool		special_plus_quote;
  	List	   *tokens = NIL;
  
  	do
  	{
! 		if (!next_token(lineptr, buf, sizeof(buf), &initial_quote, &special_plus_quote, &trailing_comma))
  			break;
  
  		/* Is this referencing a file? */
  		if (!initial_quote && buf[0] == '@' && buf[1] != '\0')
  			tokens = tokenize_inc_file(tokens, filename, buf + 1);
+ 		else if (special_plus_quote)
+ 			tokens = lappend(tokens, make_hba_token(buf, SPECIAL_PLUS_QUOTED_STRING));
  		else
! 			tokens = lappend(tokens, make_hba_token(buf, initial_quote ? QUOTED_STRING : UNQUOTED_STRING));
  	} while (trailing_comma);
  
  	return tokens;
***************
*** 489,497 **** check_role(const char *role, Oid roleid, List *tokens)
  	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) ||
--- 511,538 ----
  	foreach(cell, tokens)
  	{
  		tok = lfirst(cell);
! 		if (tok->toktype != QUOTED_STRING && tok->string[0] == '+')
  		{
! 			char	   *usergreoup;
! 
! 			/*
! 			 * '+' follewed by QUOTES denote case sensitive rolename i.e.
! 			 * +"ROLENAME" matches only with "ROLENAME"
! 			 */
! 			if (tok->toktype == SPECIAL_PLUS_QUOTED_STRING)
! 			{
! 				usergreoup = pstrdup(tok->string + 1);
! 			}
! 			else
! 			{
! 				char	   *p;
! 
! 				usergreoup = pstrdup(tok->string + 1);
! 				for (p = usergreoup; *p; p++)
! 					*p = pg_tolower(*p);
! 			}
! 
! 			if (is_member(roleid, usergreoup))
  				return true;
  		}
  		else if (token_matches(tok, role) ||
***************
*** 881,887 **** parse_hba_line(List *line, int line_num, char *raw_line)
  		return NULL;
  	}
  	token = linitial(tokens);
! 	if (strcmp(token->string, "local") == 0)
  	{
  #ifdef HAVE_UNIX_SOCKETS
  		parsedline->conntype = ctLocal;
--- 922,928 ----
  		return NULL;
  	}
  	token = linitial(tokens);
! 	if (pg_strcasecmp(token->string, "local") == 0)
  	{
  #ifdef HAVE_UNIX_SOCKETS
  		parsedline->conntype = ctLocal;
***************
*** 894,905 **** parse_hba_line(List *line, int line_num, char *raw_line)
  		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
--- 935,946 ----
  		return NULL;
  #endif
  	}
! 	else if (pg_strcasecmp(token->string, "host") == 0 ||
! 			 pg_strcasecmp(token->string, "hostssl") == 0 ||
! 			 pg_strcasecmp(token->string, "hostnossl") == 0)
  	{
  
! 		if ((token->string[4] == 's') || (token->string[4] == 'S'))		/* "hostssl" */
  		{
  			/* SSL support must be actually active, else complain */
  #ifdef USE_SSL
***************
*** 926,932 **** parse_hba_line(List *line, int line_num, char *raw_line)
  #endif
  		}
  #ifdef USE_SSL
! 		else if (token->string[4] == 'n')		/* "hostnossl" */
  		{
  			parsedline->conntype = ctHostNoSSL;
  		}
--- 967,973 ----
  #endif
  		}
  #ifdef USE_SSL
! 		else if ((token->string[4] == 'n') || (token->string[4] == 'N'))		/* "hostnossl" */
  		{
  			parsedline->conntype = ctHostNoSSL;
  		}
***************
*** 1181,1209 **** parse_hba_line(List *line, int line_num, char *raw_line)
  	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, "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)
  		{
--- 1222,1250 ----
  	token = linitial(tokens);
  
  	unsupauth = NULL;
! 	if (pg_strcasecmp(token->string, "trust") == 0)
  		parsedline->auth_method = uaTrust;
! 	else if (pg_strcasecmp(token->string, "ident") == 0)
  		parsedline->auth_method = uaIdent;
! 	else if (pg_strcasecmp(token->string, "peer") == 0)
  		parsedline->auth_method = uaPeer;
! 	else if (pg_strcasecmp(token->string, "password") == 0)
  		parsedline->auth_method = uaPassword;
! 	else if (pg_strcasecmp(token->string, "gss") == 0)
  #ifdef ENABLE_GSS
  		parsedline->auth_method = uaGSS;
  #else
  		unsupauth = "gss";
  #endif
! 	else if (pg_strcasecmp(token->string, "sspi") == 0)
  #ifdef ENABLE_SSPI
  		parsedline->auth_method = uaSSPI;
  #else
  		unsupauth = "sspi";
  #endif
! 	else if (pg_strcasecmp(token->string, "reject") == 0)
  		parsedline->auth_method = uaReject;
! 	else if (pg_strcasecmp(token->string, "md5") == 0)
  	{
  		if (Db_user_namespace)
  		{
***************
*** 1216,1240 **** parse_hba_line(List *line, int line_num, char *raw_line)
  		}
  		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
  	{
--- 1257,1281 ----
  		}
  		parsedline->auth_method = uaMD5;
  	}
! 	else if (pg_strcasecmp(token->string, "pam") == 0)
  #ifdef USE_PAM
  		parsedline->auth_method = uaPAM;
  #else
  		unsupauth = "pam";
  #endif
! 	else if (pg_strcasecmp(token->string, "ldap") == 0)
  #ifdef USE_LDAP
  		parsedline->auth_method = uaLDAP;
  #else
  		unsupauth = "ldap";
  #endif
! 	else if (pg_strcasecmp(token->string, "cert") == 0)
  #ifdef USE_SSL
  		parsedline->auth_method = uaCert;
  #else
  		unsupauth = "cert";
  #endif
! 	else if (pg_strcasecmp(token->string, "radius") == 0)
  		parsedline->auth_method = uaRADIUS;
  	else
  	{
***************
*** 1408,1414 **** parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num)
  	hbaline->ldapscope = LDAP_SCOPE_SUBTREE;
  #endif
  
! 	if (strcmp(name, "map") == 0)
  	{
  		if (hbaline->auth_method != uaIdent &&
  			hbaline->auth_method != uaPeer &&
--- 1449,1455 ----
  	hbaline->ldapscope = LDAP_SCOPE_SUBTREE;
  #endif
  
! 	if (pg_strcasecmp(name, "map") == 0)
  	{
  		if (hbaline->auth_method != uaIdent &&
  			hbaline->auth_method != uaPeer &&
***************
*** 1418,1424 **** parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num)
  			INVALID_AUTH_OPTION("map", gettext_noop("ident, peer, gssapi, sspi, and cert"));
  		hbaline->usermap = pstrdup(val);
  	}
! 	else if (strcmp(name, "clientcert") == 0)
  	{
  		/*
  		 * Since we require ctHostSSL, this really can never happen on
--- 1459,1465 ----
  			INVALID_AUTH_OPTION("map", gettext_noop("ident, peer, gssapi, sspi, and cert"));
  		hbaline->usermap = pstrdup(val);
  	}
! 	else if (pg_strcasecmp(name, "clientcert") == 0)
  	{
  		/*
  		 * Since we require ctHostSSL, this really can never happen on
***************
*** 1461,1472 **** parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num)
  			hbaline->clientcert = false;
  		}
  	}
! 	else if (strcmp(name, "pamservice") == 0)
  	{
  		REQUIRE_AUTH_OPTION(uaPAM, "pamservice", "pam");
  		hbaline->pamservice = pstrdup(val);
  	}
! 	else if (strcmp(name, "ldapurl") == 0)
  	{
  #ifdef LDAP_API_FEATURE_X_OPENLDAP
  		LDAPURLDesc *urldata;
--- 1502,1513 ----
  			hbaline->clientcert = false;
  		}
  	}
! 	else if (pg_strcasecmp(name, "pamservice") == 0)
  	{
  		REQUIRE_AUTH_OPTION(uaPAM, "pamservice", "pam");
  		hbaline->pamservice = pstrdup(val);
  	}
! 	else if (pg_strcasecmp(name, "ldapurl") == 0)
  	{
  #ifdef LDAP_API_FEATURE_X_OPENLDAP
  		LDAPURLDesc *urldata;
***************
*** 1515,1521 **** parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num)
  				 errmsg("LDAP URLs not supported on this platform")));
  #endif   /* not OpenLDAP */
  	}
! 	else if (strcmp(name, "ldaptls") == 0)
  	{
  		REQUIRE_AUTH_OPTION(uaLDAP, "ldaptls", "ldap");
  		if (strcmp(val, "1") == 0)
--- 1556,1562 ----
  				 errmsg("LDAP URLs not supported on this platform")));
  #endif   /* not OpenLDAP */
  	}
! 	else if (pg_strcasecmp(name, "ldaptls") == 0)
  	{
  		REQUIRE_AUTH_OPTION(uaLDAP, "ldaptls", "ldap");
  		if (strcmp(val, "1") == 0)
***************
*** 1523,1534 **** parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num)
  		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);
--- 1564,1575 ----
  		else
  			hbaline->ldaptls = false;
  	}
! 	else if (pg_strcasecmp(name, "ldapserver") == 0)
  	{
  		REQUIRE_AUTH_OPTION(uaLDAP, "ldapserver", "ldap");
  		hbaline->ldapserver = pstrdup(val);
  	}
! 	else if (pg_strcasecmp(name, "ldapport") == 0)
  	{
  		REQUIRE_AUTH_OPTION(uaLDAP, "ldapport", "ldap");
  		hbaline->ldapport = atoi(val);
***************
*** 1542,1585 **** parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num)
  			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_realm") == 0)
  	{
  		if (hbaline->auth_method != uaGSS &&
  			hbaline->auth_method != uaSSPI)
  			INVALID_AUTH_OPTION("krb_realm", gettext_noop("gssapi and sspi"));
  		hbaline->krb_realm = pstrdup(val);
  	}
! 	else if (strcmp(name, "include_realm") == 0)
  	{
  		if (hbaline->auth_method != uaGSS &&
  			hbaline->auth_method != uaSSPI)
--- 1583,1626 ----
  			return false;
  		}
  	}
! 	else if (pg_strcasecmp(name, "ldapbinddn") == 0)
  	{
  		REQUIRE_AUTH_OPTION(uaLDAP, "ldapbinddn", "ldap");
  		hbaline->ldapbinddn = pstrdup(val);
  	}
! 	else if (pg_strcasecmp(name, "ldapbindpasswd") == 0)
  	{
  		REQUIRE_AUTH_OPTION(uaLDAP, "ldapbindpasswd", "ldap");
  		hbaline->ldapbindpasswd = pstrdup(val);
  	}
! 	else if (pg_strcasecmp(name, "ldapsearchattribute") == 0)
  	{
  		REQUIRE_AUTH_OPTION(uaLDAP, "ldapsearchattribute", "ldap");
  		hbaline->ldapsearchattribute = pstrdup(val);
  	}
! 	else if (pg_strcasecmp(name, "ldapbasedn") == 0)
  	{
  		REQUIRE_AUTH_OPTION(uaLDAP, "ldapbasedn", "ldap");
  		hbaline->ldapbasedn = pstrdup(val);
  	}
! 	else if (pg_strcasecmp(name, "ldapprefix") == 0)
  	{
  		REQUIRE_AUTH_OPTION(uaLDAP, "ldapprefix", "ldap");
  		hbaline->ldapprefix = pstrdup(val);
  	}
! 	else if (pg_strcasecmp(name, "ldapsuffix") == 0)
  	{
  		REQUIRE_AUTH_OPTION(uaLDAP, "ldapsuffix", "ldap");
  		hbaline->ldapsuffix = pstrdup(val);
  	}
! 	else if (pg_strcasecmp(name, "krb_realm") == 0)
  	{
  		if (hbaline->auth_method != uaGSS &&
  			hbaline->auth_method != uaSSPI)
  			INVALID_AUTH_OPTION("krb_realm", gettext_noop("gssapi and sspi"));
  		hbaline->krb_realm = pstrdup(val);
  	}
! 	else if (pg_strcasecmp(name, "include_realm") == 0)
  	{
  		if (hbaline->auth_method != uaGSS &&
  			hbaline->auth_method != uaSSPI)
***************
*** 1589,1595 **** parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num)
  		else
  			hbaline->include_realm = false;
  	}
! 	else if (strcmp(name, "radiusserver") == 0)
  	{
  		struct addrinfo *gai_result;
  		struct addrinfo hints;
--- 1630,1636 ----
  		else
  			hbaline->include_realm = false;
  	}
! 	else if (pg_strcasecmp(name, "radiusserver") == 0)
  	{
  		struct addrinfo *gai_result;
  		struct addrinfo hints;
***************
*** 1617,1623 **** parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num)
  		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);
--- 1658,1664 ----
  		pg_freeaddrinfo_all(hints.ai_family, gai_result);
  		hbaline->radiusserver = pstrdup(val);
  	}
! 	else if (pg_strcasecmp(name, "radiusport") == 0)
  	{
  		REQUIRE_AUTH_OPTION(uaRADIUS, "radiusport", "radius");
  		hbaline->radiusport = atoi(val);
***************
*** 1631,1642 **** parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num)
  			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);
--- 1672,1683 ----
  			return false;
  		}
  	}
! 	else if (pg_strcasecmp(name, "radiussecret") == 0)
  	{
  		REQUIRE_AUTH_OPTION(uaRADIUS, "radiussecret", "radius");
  		hbaline->radiussecret = pstrdup(val);
  	}
! 	else if (pg_strcasecmp(name, "radiusidentifier") == 0)
  	{
  		REQUIRE_AUTH_OPTION(uaRADIUS, "radiusidentifier", "radius");
  		hbaline->radiusidentifier = pstrdup(val);
In reply to: Viswanatham kirankumar (#9)
Re: [TODO] Process pg_hba.conf keywords as case-insensitive

Re: Viswanatham kirankumar 2014-07-23 <EC867DEF52699D4189B584A14BAA7C21654422EB@blreml504-mbx.china.huawei.com>

3) Host name is not a SQL object so it will be treated as case-sensitive
except for all, samehost, samenet are considered as keywords.
For these user need to use quotes to differentiate between hostname and keywords.

DNS is case-insensitive, though most of the time case-preserving
(nothing guarantees that it won't down-up-whatever-case the answer you
get).

(FTR, I'll retract my original complaint, the idea of using SQL-like
case folding is nice.)

Christoph
--
cb@df7cb.de | http://www.df7cb.de/

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#11Heikki Linnakangas
hlinnakangas@vmware.com
In reply to: Viswanatham kirankumar (#9)
Re: [TODO] Process pg_hba.conf keywords as case-insensitive

On 07/23/2014 09:14 AM, Viswanatham kirankumar wrote:

On 16 July 2014 23:12, Tom Lane wrote

Christoph Berg <cb@df7cb.de> writes:
Re: Viswanatham kirankumar 2014-07-16
<EC867DEF52699D4189B584A14BAA7C2165440538@blreml504-mbx.china.huawei.com>

Attached patch is implementing following TODO item Process
pg_hba.conf keywords as case-insensitive

Hmm. I see a case for accepting "ALL" (as in hosts.allow(5)), so +1 on
that, but I don't think the other keywords like "host" and "peer"
should be valid in upper case.

I think the argument was that SQL users are accustomed to thinking that keywords are
case-insensitive. It makes sense to me that we should adopt that same convention in pg_hba.conf.

Re-reading the original thread, there was also concern about whether
we should try to make quoting/casefolding behave more like it does in SQL,
specifically for matching pg_hba.conf items to SQL identifiers (database and role names).
This patch doesn't seem to have addressed that part of it, but I think we need to think those
things through before we just do a blind s/strcmp/pg_strcasecmp/g. Otherwise we might
find that we've added ambiguity that will give us trouble when we do try to fix that.

I had updated as per you review comments

1) database and role names behave similar to SQL identifiers (case-sensitive / case-folding).

2) users and user-groups only requires special handling and behavior as follows
Normal user :
A. unquoted ( USER ) will be treated as user ( downcase ).
B. quoted ( "USeR" ) will be treated as USeR (case-sensitive).
C. quoted ( "+USER" ) will be treated as normal user +USER (i.e. will not be considered as user-group) and case-sensitive as string is quoted.
User Group :
A. unquoted ( +USERGROUP ) will be treated as +usergruop ( downcase ).
B. plus quoted ( +"UserGROUP" ) will be treated as +UserGROUP (case-sensitive).

3) Host name is not a SQL object so it will be treated as case-sensitive
except for all, samehost, samenet are considered as keywords.
For these user need to use quotes to differentiate between hostname and keywords.

4) All the fixed keywords mention in pg_hba.conf and Client Authentication section will be considered as keywords
Eg: host, local, hostssl etc..

With this patch, database (and role?) names are compared
case-insensitively. For example:

local MixedDB all trust
local mixedDB all reject

psql -d "mixedDB"
psql (9.5devel)
Type "help" for help.

mixedDB=#

That connection should've matched that 2nd line, and be rejected.

PS. Please update the docs.

- Heikki

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

In reply to: Heikki Linnakangas (#11)
Re: [TODO] Process pg_hba.conf keywords as case-insensitive

Re: Heikki Linnakangas 2014-08-21 <53F5A2D6.2050208@vmware.com>

1) database and role names behave similar to SQL identifiers (case-sensitive / case-folding).

2) users and user-groups only requires special handling and behavior as follows
Normal user :
A. unquoted ( USER ) will be treated as user ( downcase ).
B. quoted ( "USeR" ) will be treated as USeR (case-sensitive).

With this patch, database (and role?) names are compared case-insensitively.
For example:

local MixedDB all trust
local mixedDB all reject

psql -d "mixedDB"
psql (9.5devel)
Type "help" for help.

mixedDB=#

That connection should've matched that 2nd line, and be rejected.

Actually it should have matched neither, as both lines will get folded
downcase:

local mixeddb all trust
local mixeddb all reject

Christoph
--
cb@df7cb.de | http://www.df7cb.de/

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#13Kyotaro HORIGUCHI
horiguchi.kyotaro@lab.ntt.co.jp
In reply to: Christoph Berg (#12)
Re: [TODO] Process pg_hba.conf keywords as case-insensitive

Hello, I will be the reviewer of this patch.

You approach that coloring tokens seems right, but you have
broken the parse logic by adding your code.

Other than the mistakes others pointed, I found that

- non-SQL-ident like tokens are ignored by their token style,
quoted or not, so the following line works.

| "local" All aLL trust

I suppose this is not what you intended. This is because you have
igonred the attribute of a token when comparing it as
non-SQL-ident tokens.

- '+' at the head of the sequence '+"' is treated as the first
character of the *quoted* string. e.g. +"hoge" is tokenized as
"+hoge":special_quoted.

This is why you simply continued processing for '+"' without
discarding and skipping the '+', and not setting in_quote so the
following parser code works as it is not intended. You should
understand what the original code does and insert or modify
logics not braeking the assumptions.

With this patch, database (and role?) names are compared case-insensitively.
For example:

local MixedDB all trust
local mixedDB all reject

psql -d "mixedDB"
psql (9.5devel)
Type "help" for help.

mixedDB=#

That connection should've matched that 2nd line, and be rejected.

Actually it should have matched neither, as both lines will get folded
downcase:

local mixeddb all trust
local mixeddb all reject

regards,

--
Kyotaro Horiguchi
NTT Open Source Software Center

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#14Kyotaro HORIGUCHI
horiguchi.kyotaro@lab.ntt.co.jp
In reply to: Kyotaro HORIGUCHI (#13)
Re: [TODO] Process pg_hba.conf keywords as case-insensitive

Sorry for wrong suggestion. Setting in_quote is wrong there because it's
before the beginning quote. Although, advancing read pointer and replacing
c with the next value is still needed.

regards,
--
Kyotaro Horiguchi
NTT Open Source Software Center
2014/09/09 20:49 "Kyotaro HORIGUCHI" <horiguchi.kyotaro@lab.ntt.co.jp>:

Show quoted text

Hello, I will be the reviewer of this patch.

You approach that coloring tokens seems right, but you have
broken the parse logic by adding your code.

Other than the mistakes others pointed, I found that

- non-SQL-ident like tokens are ignored by their token style,
quoted or not, so the following line works.

| "local" All aLL trust

I suppose this is not what you intended. This is because you have
igonred the attribute of a token when comparing it as
non-SQL-ident tokens.

- '+' at the head of the sequence '+"' is treated as the first
character of the *quoted* string. e.g. +"hoge" is tokenized as
"+hoge":special_quoted.

This is why you simply continued processing for '+"' without
discarding and skipping the '+', and not setting in_quote so the
following parser code works as it is not intended. You should
understand what the original code does and insert or modify
logics not braeking the assumptions.

With this patch, database (and role?) names are compared

case-insensitively.

For example:

local MixedDB all trust
local mixedDB all reject

psql -d "mixedDB"
psql (9.5devel)
Type "help" for help.

mixedDB=#

That connection should've matched that 2nd line, and be rejected.

Actually it should have matched neither, as both lines will get folded
downcase:

local mixeddb all trust
local mixeddb all reject

regards,

--
Kyotaro Horiguchi
NTT Open Source Software Center

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#15Kyotaro HORIGUCHI
horiguchi.kyotaro@lab.ntt.co.jp
In reply to: Kyotaro HORIGUCHI (#14)
1 attachment(s)
Re: [TODO] Process pg_hba.conf keywords as case-insensitive

Hello, I had a closer look on this patch.

Finally I think that we need case-insensitive version of
get_role_id and() get_database_id() to acoomplish this patch'es
objective. (This runs full-scans on pg_database or pg_authid X()

And I'd like to propose to change token categorization from
notation-base to how-to-treat base. Concretely this patch
categorizes tokens using 'special quote is used' and 'quote from
the first' but it seems making logics clearer to categorize them
using 'case sensive or not' and 'it represents group name'.

The attached patch is a revised version of your original patch
regarding to the above point. (Sorry in advance that this is a
quick hack, especially the code related to file-inclusion is not
tested at all)

I have tested this only superficial level but it seems works as
expected.

Under the new specifications, next_token will work as following,

- USER : token: USER , case-insensitive
- "USeR" : token: USeR , case-SENSITIVE
- "+uSeR" : token: +uSeR , case-SENSITIVE
- "+UsE"R : token: +UsEr , case-insensitive
- U"S""e"R : token: US"eR , case-insensitive

- +USER : token: USER , case-insensitive, group_name
- +"uSeR" : token: uSeR , case_SENSITIVE, group_name
- +U"sE""r" : token: UsE"r , case-insensitive, group_name

- + : token: + , (useless?)
- @ : token: @ , (useless?)
- @ho"ge : token: ho"ge, file_inclusion (not confirmed)

There's a concern that Case-insensitive matching is accomplished
by full-scan on pg_database or pg_authid so it would be rather
slow than case-sensitive matching. This might not be acceptable
by the community.

And one known defect is that you will get a bit odd message if
you put an hba line having keywords quoted or prefixed with '+',
for example

+locAl "postgres" +sUs tRust

The server complains for the line above that

*| LOG: invalid connection type "locAl"
| CONTEXT: line 84 of configuration file "/home/horiguti/data/data_work/pg_hba.conf"

The prefixing '+' is omitted. To correct this, either deparsing
token into original string or storing original string into tokens
is needed, I think.

What do you think about the changes, Viswanatham or all ?

regards,

--
Kyotaro Horiguchi
NTT Open Source Software Center

Attachments:

pg_hba.conf_keywords_as_case-insensitive_v2.patchtext/x-patch; charset=us-asciiDownload
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index f480be8..db73dd9 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -1991,6 +1991,50 @@ get_database_oid(const char *dbname, bool missing_ok)
 	return oid;
 }
 
+/*
+ * get_database_oid - given a database name, look up the OID in
+ * case-insensitive manner.
+ *
+ * If missing_ok is false, throw an error if database name not found.  If
+ * true, just return InvalidOid.
+ */
+Oid
+get_database_oid_case_insensitive(const char *dbname, bool missing_ok)
+{
+	Relation	relation;
+	SysScanDesc scandesc;
+	HeapTuple	tuple;
+	Oid oid = InvalidOid;
+
+	/*
+	 * SysCache has no abirility to case insensitive match, so we have no
+	 * means except scanning whole the systable.
+	 */
+	relation = heap_open(DatabaseRelationId, AccessShareLock);
+
+	scandesc = systable_beginscan(relation, InvalidOid, false,
+								  NULL, 0, NULL);
+	while (HeapTupleIsValid(tuple = systable_getnext(scandesc)))
+	{
+		Form_pg_database dbForm = (Form_pg_database) GETSTRUCT(tuple);
+
+		if (pg_strcasecmp(dbname, dbForm->datname.data) == 0)
+		{
+			oid = HeapTupleGetOid(tuple);
+			break;
+		}
+	}
+	systable_endscan(scandesc);
+	heap_close(relation, AccessShareLock);
+
+	if (!OidIsValid(oid) && !missing_ok)
+ 		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_DATABASE),
+				 errmsg("database \"%s\" does not exist",
+						dbname)));
+
+	return oid;
+}
 
 /*
  * get_database_name - given a database OID, look up the name
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index 84da823..2d3a059 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -60,9 +60,20 @@ typedef struct check_network_data
 	bool		result;			/* set to true if match */
 } check_network_data;
 
-
-#define token_is_keyword(t, k)	(!t->quoted && strcmp(t->string, k) == 0)
-#define token_matches(t, k)  (strcmp(t->string, k) == 0)
+typedef enum TokenType
+{
+	NORMAL,
+	GROUP_NAME,			/* this token had leading '+' */
+	FILE_INCLUSION,		/* this token had leading '@' */
+} TokenType;
+
+#define token_is_keyword(tk, kw)	\
+	((tk)->type != NORMAL || (tk)->case_sensitive ? false : \
+	 (pg_strcasecmp((tk)->string, (kw)) == 0))
+#define token_matches(t, k)		   \
+	((t)->type != NORMAL ? false :								\
+	 ((t)->case_sensitive ? (strcmp((t)->string, (k)) == 0):	\
+	  (pg_strcasecmp((t)->string, (k)) == 0)))
 
 /*
  * A single string token lexed from the HBA config file, together with whether
@@ -71,7 +82,8 @@ typedef struct check_network_data
 typedef struct HbaToken
 {
 	char	   *string;
-	bool		quoted;
+	TokenType	type;
+	bool		case_sensitive;
 } HbaToken;
 
 /*
@@ -111,6 +123,7 @@ pg_isblank(const char c)
 }
 
 
+
 /*
  * Grab one token out of the string pointed to by lineptr.
  * Tokens are strings of non-blank
@@ -123,6 +136,10 @@ pg_isblank(const char c)
  * the first character.  (We use that to prevent "@x" from being treated
  * as a file inclusion request.  Note that @"x" should be so treated;
  * we want to allow that to support embedded spaces in file paths.)
+ * we set *special_plus_quote to indicate whether there was quoting after "+"
+ * charecter. (We use this to prevent +"ROLENAME" from treating as unquoted
+ * string as first charecter is not '"', but which require special handling
+ * only incase of role/user-group name).
  * We set *terminating_comma to indicate whether the token is terminated by a
  * comma (which is not returned.)
  *
@@ -136,12 +153,13 @@ pg_isblank(const char c)
  * Handle comments.
  */
 static bool
-next_token(char **lineptr, char *buf, int bufsz, bool *initial_quote,
-		   bool *terminating_comma)
+next_token(char **lineptr, char *buf, int bufsz,
+		   bool *case_sensitive, int *type, bool *terminating_comma)
 {
 	int			c;
 	char	   *start_buf = buf;
 	char	   *end_buf = buf + (bufsz - 2);
+	char 	   *p;
 	bool		in_quote = false;
 	bool		was_quote = false;
 	bool		saw_quote = false;
@@ -149,8 +167,9 @@ next_token(char **lineptr, char *buf, int bufsz, bool *initial_quote,
 	/* end_buf reserves two bytes to ensure we can append \n and \0 */
 	Assert(end_buf > start_buf);
 
-	*initial_quote = false;
 	*terminating_comma = false;
+	*case_sensitive = false;
+	*type = NORMAL;
 
 	/* Move over initial whitespace and commas */
 	while ((c = (*(*lineptr)++)) != '\0' && (pg_isblank(c) || c == ','))
@@ -162,6 +181,17 @@ next_token(char **lineptr, char *buf, int bufsz, bool *initial_quote,
 		return false;
 	}
 
+	if (c == '+' || c == '@')
+	{
+		*type = (c == '+' ? GROUP_NAME : FILE_INCLUSION);
+
+		/*
+		 * Skip capturing it, and we can read the following characters as
+		 * usual.
+		 */
+		c = *(*lineptr)++;
+	}
+
 	/*
 	 * Build a token in buf of next characters up to EOF, EOL, unquoted comma,
 	 * or unquoted whitespace.
@@ -201,8 +231,17 @@ next_token(char **lineptr, char *buf, int bufsz, bool *initial_quote,
 		}
 
 		if (c != '"' || was_quote)
+		{
 			*buf++ = c;
 
+			/*
+			 * Cancel case-sensitive state if trailing characters found for
+			 * the quoted region.
+			 */
+			if (*case_sensitive && !in_quote)
+				*case_sensitive = false;
+		}
+
 		/* Literal double-quote is two double-quotes */
 		if (in_quote && c == '"')
 			was_quote = !was_quote;
@@ -214,7 +253,7 @@ next_token(char **lineptr, char *buf, int bufsz, bool *initial_quote,
 			in_quote = !in_quote;
 			saw_quote = true;
 			if (buf == start_buf)
-				*initial_quote = true;
+				*case_sensitive = true;
 		}
 
 		c = *(*lineptr)++;
@@ -226,13 +265,19 @@ next_token(char **lineptr, char *buf, int bufsz, bool *initial_quote,
 	 */
 	(*lineptr)--;
 
+	if (buf == start_buf && *type != NORMAL)
+	{
+		*buf++ = (*type == GROUP_NAME ? '+' : '@');
+		*type = NORMAL;
+	}
+
 	*buf = '\0';
 
 	return (saw_quote || buf > start_buf);
 }
 
 static HbaToken *
-make_hba_token(char *token, bool quoted)
+make_hba_token(char *token, TokenType toktype, bool case_sensitive)
 {
 	HbaToken   *hbatoken;
 	int			toklen;
@@ -240,7 +285,8 @@ make_hba_token(char *token, bool quoted)
 	toklen = strlen(token);
 	hbatoken = (HbaToken *) palloc(sizeof(HbaToken) + toklen + 1);
 	hbatoken->string = (char *) hbatoken + sizeof(HbaToken);
-	hbatoken->quoted = quoted;
+	hbatoken->type = toktype;
+	hbatoken->case_sensitive = case_sensitive;
 	memcpy(hbatoken->string, token, toklen + 1);
 
 	return hbatoken;
@@ -252,7 +298,8 @@ make_hba_token(char *token, bool quoted)
 static HbaToken *
 copy_hba_token(HbaToken *in)
 {
-	HbaToken   *out = make_hba_token(in->string, in->quoted);
+	HbaToken   *out = make_hba_token(in->string,
+									 in->type, in->case_sensitive);
 
 	return out;
 }
@@ -269,19 +316,26 @@ next_field_expand(const char *filename, char **lineptr)
 {
 	char		buf[MAX_TOKEN];
 	bool		trailing_comma;
-	bool		initial_quote;
+	bool		case_sensitive;
+	int			type;
 	List	   *tokens = NIL;
 
 	do
 	{
-		if (!next_token(lineptr, buf, sizeof(buf), &initial_quote, &trailing_comma))
+		if (!next_token(lineptr, buf, sizeof(buf), 
+						&case_sensitive, &type,
+						&trailing_comma))
 			break;
 
 		/* Is this referencing a file? */
-		if (!initial_quote && buf[0] == '@' && buf[1] != '\0')
-			tokens = tokenize_inc_file(tokens, filename, buf + 1);
+		if (type == FILE_INCLUSION)
+			tokens = tokenize_inc_file(tokens, filename, buf);
+		else if (type == GROUP_NAME)
+			tokens = lappend(tokens,
+							 make_hba_token(buf, GROUP_NAME, case_sensitive));
 		else
-			tokens = lappend(tokens, make_hba_token(buf, initial_quote));
+			tokens = lappend(tokens,
+							 make_hba_token(buf, NORMAL, case_sensitive));
 	} while (trailing_comma);
 
 	return tokens;
@@ -457,14 +511,17 @@ tokenize_file(const char *filename, FILE *file,
  * We check to see if it is a member of the specified role name.
  */
 static bool
-is_member(Oid userid, const char *role)
+is_member(Oid userid, const char *role, bool case_sensitive)
 {
 	Oid			roleid;
 
 	if (!OidIsValid(userid))
 		return false;			/* if user not exist, say "no" */
 
-	roleid = get_role_oid(role, true);
+	if (case_sensitive)
+		roleid = get_role_oid(role, true);
+	else
+		roleid = get_role_oid_case_insensitive(role, true);
 
 	if (!OidIsValid(roleid))
 		return false;			/* if target role not exist, say "no" */
@@ -489,9 +546,9 @@ check_role(const char *role, Oid roleid, List *tokens)
 	foreach(cell, tokens)
 	{
 		tok = lfirst(cell);
-		if (!tok->quoted && tok->string[0] == '+')
+		if (tok->type == GROUP_NAME)
 		{
-			if (is_member(roleid, tok->string + 1))
+			if (is_member(roleid, tok->string, tok->case_sensitive))
 				return true;
 		}
 		else if (token_matches(tok, role) ||
@@ -529,7 +586,7 @@ check_db(const char *dbname, const char *role, Oid roleid, List *tokens)
 		else if (token_is_keyword(tok, "samegroup") ||
 				 token_is_keyword(tok, "samerole"))
 		{
-			if (is_member(roleid, dbname))
+			if (is_member(roleid, dbname, tok->case_sensitive))
 				return true;
 		}
 		else if (token_is_keyword(tok, "replication"))
@@ -881,7 +938,7 @@ parse_hba_line(List *line, int line_num, char *raw_line)
 		return NULL;
 	}
 	token = linitial(tokens);
-	if (strcmp(token->string, "local") == 0)
+	if (token_is_keyword(token, "local"))
 	{
 #ifdef HAVE_UNIX_SOCKETS
 		parsedline->conntype = ctLocal;
@@ -894,12 +951,12 @@ parse_hba_line(List *line, int line_num, char *raw_line)
 		return NULL;
 #endif
 	}
-	else if (strcmp(token->string, "host") == 0 ||
-			 strcmp(token->string, "hostssl") == 0 ||
-			 strcmp(token->string, "hostnossl") == 0)
+	else if (token_is_keyword(token, "host") ||
+			 token_is_keyword(token, "hostssl") ||
+			 token_is_keyword(token, "hostnossl"))
 	{
 
-		if (token->string[4] == 's')	/* "hostssl" */
+		if ((token->string[4] == 's') || (token->string[4] == 'S'))		/* "hostssl" */
 		{
 			/* SSL support must be actually active, else complain */
 #ifdef USE_SSL
@@ -926,7 +983,7 @@ parse_hba_line(List *line, int line_num, char *raw_line)
 #endif
 		}
 #ifdef USE_SSL
-		else if (token->string[4] == 'n')		/* "hostnossl" */
+		else if ((token->string[4] == 'n') || (token->string[4] == 'N'))		/* "hostnossl" */
 		{
 			parsedline->conntype = ctHostNoSSL;
 		}
@@ -1181,29 +1238,29 @@ parse_hba_line(List *line, int line_num, char *raw_line)
 	token = linitial(tokens);
 
 	unsupauth = NULL;
-	if (strcmp(token->string, "trust") == 0)
+	if (token_is_keyword(token, "trust"))
 		parsedline->auth_method = uaTrust;
-	else if (strcmp(token->string, "ident") == 0)
+	else if (token_is_keyword(token, "ident"))
 		parsedline->auth_method = uaIdent;
-	else if (strcmp(token->string, "peer") == 0)
+	else if (token_is_keyword(token, "peer"))
 		parsedline->auth_method = uaPeer;
-	else if (strcmp(token->string, "password") == 0)
+	else if (token_is_keyword(token, "password"))
 		parsedline->auth_method = uaPassword;
-	else if (strcmp(token->string, "gss") == 0)
+	else if (token_is_keyword(token, "gss"))
 #ifdef ENABLE_GSS
 		parsedline->auth_method = uaGSS;
 #else
 		unsupauth = "gss";
 #endif
-	else if (strcmp(token->string, "sspi") == 0)
+	else if (token_is_keyword(token, "sspi"))
 #ifdef ENABLE_SSPI
 		parsedline->auth_method = uaSSPI;
 #else
 		unsupauth = "sspi";
 #endif
-	else if (strcmp(token->string, "reject") == 0)
+	else if (token_is_keyword(token, "reject"))
 		parsedline->auth_method = uaReject;
-	else if (strcmp(token->string, "md5") == 0)
+	else if (token_is_keyword(token, "md5"))
 	{
 		if (Db_user_namespace)
 		{
@@ -1216,25 +1273,25 @@ parse_hba_line(List *line, int line_num, char *raw_line)
 		}
 		parsedline->auth_method = uaMD5;
 	}
-	else if (strcmp(token->string, "pam") == 0)
+	else if (token_is_keyword(token, "pam"))
 #ifdef USE_PAM
 		parsedline->auth_method = uaPAM;
 #else
 		unsupauth = "pam";
 #endif
-	else if (strcmp(token->string, "ldap") == 0)
+	else if (token_is_keyword(token, "ldap"))
 #ifdef USE_LDAP
 		parsedline->auth_method = uaLDAP;
 #else
 		unsupauth = "ldap";
 #endif
-	else if (strcmp(token->string, "cert") == 0)
+	else if (token_is_keyword(token, "cert"))
 #ifdef USE_SSL
 		parsedline->auth_method = uaCert;
 #else
 		unsupauth = "cert";
 #endif
-	else if (strcmp(token->string, "radius") == 0)
+	else if (token_is_keyword(token, "radius"))
 		parsedline->auth_method = uaRADIUS;
 	else
 	{
@@ -1408,7 +1465,7 @@ parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num)
 	hbaline->ldapscope = LDAP_SCOPE_SUBTREE;
 #endif
 
-	if (strcmp(name, "map") == 0)
+	if (pg_strcasecmp(name, "map") == 0)
 	{
 		if (hbaline->auth_method != uaIdent &&
 			hbaline->auth_method != uaPeer &&
@@ -1418,7 +1475,7 @@ parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num)
 			INVALID_AUTH_OPTION("map", gettext_noop("ident, peer, gssapi, sspi, and cert"));
 		hbaline->usermap = pstrdup(val);
 	}
-	else if (strcmp(name, "clientcert") == 0)
+	else if (pg_strcasecmp(name, "clientcert") == 0)
 	{
 		/*
 		 * Since we require ctHostSSL, this really can never happen on
@@ -1461,12 +1518,12 @@ parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num)
 			hbaline->clientcert = false;
 		}
 	}
-	else if (strcmp(name, "pamservice") == 0)
+	else if (pg_strcasecmp(name, "pamservice") == 0)
 	{
 		REQUIRE_AUTH_OPTION(uaPAM, "pamservice", "pam");
 		hbaline->pamservice = pstrdup(val);
 	}
-	else if (strcmp(name, "ldapurl") == 0)
+	else if (pg_strcasecmp(name, "ldapurl") == 0)
 	{
 #ifdef LDAP_API_FEATURE_X_OPENLDAP
 		LDAPURLDesc *urldata;
@@ -1484,7 +1541,7 @@ parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num)
 			return false;
 		}
 
-		if (strcmp(urldata->lud_scheme, "ldap") != 0)
+		if (pg_strcasecmp(urldata->lud_scheme, "ldap") != 0)
 		{
 			ereport(LOG,
 					(errcode(ERRCODE_CONFIG_FILE_ERROR),
@@ -1515,7 +1572,7 @@ parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num)
 				 errmsg("LDAP URLs not supported on this platform")));
 #endif   /* not OpenLDAP */
 	}
-	else if (strcmp(name, "ldaptls") == 0)
+	else if (pg_strcasecmp(name, "ldaptls") == 0)
 	{
 		REQUIRE_AUTH_OPTION(uaLDAP, "ldaptls", "ldap");
 		if (strcmp(val, "1") == 0)
@@ -1523,12 +1580,12 @@ parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num)
 		else
 			hbaline->ldaptls = false;
 	}
-	else if (strcmp(name, "ldapserver") == 0)
+	else if (pg_strcasecmp(name, "ldapserver") == 0)
 	{
 		REQUIRE_AUTH_OPTION(uaLDAP, "ldapserver", "ldap");
 		hbaline->ldapserver = pstrdup(val);
 	}
-	else if (strcmp(name, "ldapport") == 0)
+	else if (pg_strcasecmp(name, "ldapport") == 0)
 	{
 		REQUIRE_AUTH_OPTION(uaLDAP, "ldapport", "ldap");
 		hbaline->ldapport = atoi(val);
@@ -1542,44 +1599,44 @@ parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num)
 			return false;
 		}
 	}
-	else if (strcmp(name, "ldapbinddn") == 0)
+	else if (pg_strcasecmp(name, "ldapbinddn") == 0)
 	{
 		REQUIRE_AUTH_OPTION(uaLDAP, "ldapbinddn", "ldap");
 		hbaline->ldapbinddn = pstrdup(val);
 	}
-	else if (strcmp(name, "ldapbindpasswd") == 0)
+	else if (pg_strcasecmp(name, "ldapbindpasswd") == 0)
 	{
 		REQUIRE_AUTH_OPTION(uaLDAP, "ldapbindpasswd", "ldap");
 		hbaline->ldapbindpasswd = pstrdup(val);
 	}
-	else if (strcmp(name, "ldapsearchattribute") == 0)
+	else if (pg_strcasecmp(name, "ldapsearchattribute") == 0)
 	{
 		REQUIRE_AUTH_OPTION(uaLDAP, "ldapsearchattribute", "ldap");
 		hbaline->ldapsearchattribute = pstrdup(val);
 	}
-	else if (strcmp(name, "ldapbasedn") == 0)
+	else if (pg_strcasecmp(name, "ldapbasedn") == 0)
 	{
 		REQUIRE_AUTH_OPTION(uaLDAP, "ldapbasedn", "ldap");
 		hbaline->ldapbasedn = pstrdup(val);
 	}
-	else if (strcmp(name, "ldapprefix") == 0)
+	else if (pg_strcasecmp(name, "ldapprefix") == 0)
 	{
 		REQUIRE_AUTH_OPTION(uaLDAP, "ldapprefix", "ldap");
 		hbaline->ldapprefix = pstrdup(val);
 	}
-	else if (strcmp(name, "ldapsuffix") == 0)
+	else if (pg_strcasecmp(name, "ldapsuffix") == 0)
 	{
 		REQUIRE_AUTH_OPTION(uaLDAP, "ldapsuffix", "ldap");
 		hbaline->ldapsuffix = pstrdup(val);
 	}
-	else if (strcmp(name, "krb_realm") == 0)
+	else if (pg_strcasecmp(name, "krb_realm") == 0)
 	{
 		if (hbaline->auth_method != uaGSS &&
 			hbaline->auth_method != uaSSPI)
 			INVALID_AUTH_OPTION("krb_realm", gettext_noop("gssapi and sspi"));
 		hbaline->krb_realm = pstrdup(val);
 	}
-	else if (strcmp(name, "include_realm") == 0)
+	else if (pg_strcasecmp(name, "include_realm") == 0)
 	{
 		if (hbaline->auth_method != uaGSS &&
 			hbaline->auth_method != uaSSPI)
@@ -1589,7 +1646,7 @@ parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num)
 		else
 			hbaline->include_realm = false;
 	}
-	else if (strcmp(name, "radiusserver") == 0)
+	else if (pg_strcasecmp(name, "radiusserver") == 0)
 	{
 		struct addrinfo *gai_result;
 		struct addrinfo hints;
@@ -1617,7 +1674,7 @@ parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num)
 		pg_freeaddrinfo_all(hints.ai_family, gai_result);
 		hbaline->radiusserver = pstrdup(val);
 	}
-	else if (strcmp(name, "radiusport") == 0)
+	else if (pg_strcasecmp(name, "radiusport") == 0)
 	{
 		REQUIRE_AUTH_OPTION(uaRADIUS, "radiusport", "radius");
 		hbaline->radiusport = atoi(val);
@@ -1631,12 +1688,12 @@ parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num)
 			return false;
 		}
 	}
-	else if (strcmp(name, "radiussecret") == 0)
+	else if (pg_strcasecmp(name, "radiussecret") == 0)
 	{
 		REQUIRE_AUTH_OPTION(uaRADIUS, "radiussecret", "radius");
 		hbaline->radiussecret = pstrdup(val);
 	}
-	else if (strcmp(name, "radiusidentifier") == 0)
+	else if (pg_strcasecmp(name, "radiusidentifier") == 0)
 	{
 		REQUIRE_AUTH_OPTION(uaRADIUS, "radiusidentifier", "radius");
 		hbaline->radiusidentifier = pstrdup(val);
@@ -1666,6 +1723,7 @@ check_hba(hbaPort *port)
 	HbaLine    *hba;
 
 	/* Get the target role's OID.  Note we do not error out for bad role. */
+
 	roleid = get_role_oid(port->user_name, true);
 
 	foreach(line, parsed_hba_lines)
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index 38cd5b8..9adad8c 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -5123,6 +5123,50 @@ get_role_oid(const char *rolname, bool missing_ok)
 }
 
 /*
+ * get_role_oid_case_insensitive - Given a role name, look up the role's OID
+ * in case-insensitive manner.
+ *
+ * If missing_ok is false, throw an error if tablespace name not found.  If
+ * true, just return InvalidOid.
+ */
+Oid
+get_role_oid_case_insensitive(const char *rolname, bool missing_ok)
+{
+	Relation	relation;
+	SysScanDesc scandesc;
+	HeapTuple	tuple;
+	Oid oid = InvalidOid;
+
+	/*
+	 * SysCache has no abirility to case insensitive match, so we have no
+	 * means except scanning whole the systable.
+	 */
+	relation = heap_open(AuthIdRelationId, AccessShareLock);
+
+	scandesc = systable_beginscan(relation, InvalidOid, false,
+								  NULL, 0, NULL);
+	while (HeapTupleIsValid(tuple = systable_getnext(scandesc)))
+	{
+		Form_pg_authid authForm = (Form_pg_authid) GETSTRUCT(tuple);
+
+		if (pg_strcasecmp(rolname, authForm->rolname.data) == 0)
+		{
+			oid = HeapTupleGetOid(tuple);
+			break;
+		}
+	}
+	systable_endscan(scandesc);
+	heap_close(relation, NoLock);
+
+	if (!OidIsValid(oid) && !missing_ok)
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_OBJECT),
+				 errmsg("role \"%s\" does not exist", rolname)));
+
+	return oid;
+}
+
+/*
  * get_role_oid_or_public - As above, but return ACL_ID_PUBLIC if the
  *		role name is "public".
  */
diff --git a/src/include/commands/dbcommands.h b/src/include/commands/dbcommands.h
index c2380dc..9ec3dae 100644
--- a/src/include/commands/dbcommands.h
+++ b/src/include/commands/dbcommands.h
@@ -60,6 +60,7 @@ extern Oid	AlterDatabaseSet(AlterDatabaseSetStmt *stmt);
 extern Oid	AlterDatabaseOwner(const char *dbname, Oid newOwnerId);
 
 extern Oid	get_database_oid(const char *dbname, bool missingok);
+extern Oid	get_database_oid_case_insensitive(const char *dbname, bool missingok);
 extern char *get_database_name(Oid dbid);
 
 extern void dbase_redo(XLogRecPtr lsn, XLogRecord *rptr);
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index 9430baa..819b33c 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -228,6 +228,7 @@ extern bool is_member_of_role_nosuper(Oid member, Oid role);
 extern bool is_admin_of_role(Oid member, Oid role);
 extern void check_is_member_of_role(Oid member, Oid role);
 extern Oid	get_role_oid(const char *rolname, bool missing_ok);
+extern Oid	get_role_oid_case_insensitive(const char *rolname, bool missing_ok);
 
 extern void select_best_grantor(Oid roleId, AclMode privileges,
 					const Acl *acl, Oid ownerId,
#16Kyotaro HORIGUCHI
horiguchi.kyotaro@lab.ntt.co.jp
In reply to: Kyotaro HORIGUCHI (#15)
Re: [TODO] Process pg_hba.conf keywords as case-insensitive

Hmm...

case-insensitive mathing could get multiple matches, which should be an
error but I've forgot to do so.

regards,

2014/09/10 17:54 "Kyotaro HORIGUCHI" <horiguchi.kyotaro@lab.ntt.co.jp>:

And one known defect is that you will get a bit odd message if
you put an hba line having keywords quoted or prefixed with '+',
for example

+locAl "postgres" +sUs tRust

The server complains for the line above that

*| LOG: invalid connection type "locAl"
| CONTEXT: line 84 of configuration file

"/home/horiguti/data/data_work/pg_hba.conf"

The prefixing '+' is omitted. To correct this, either deparsing
token into original string or storing original string into tokens
is needed, I think.

What do you think about the changes, Viswanatham or all ?

--
Kyotaro Horiguchi
NTT Open Source Software Center

#17Florian Pflug
fgp@phlo.org
In reply to: Kyotaro HORIGUCHI (#15)
Re: [TODO] Process pg_hba.conf keywords as case-insensitive

On Sep10, 2014, at 10:54 , Kyotaro HORIGUCHI <horiguchi.kyotaro@lab.ntt.co.jp> wrote:

Under the new specifications, next_token will work as following,

- USER : token: USER , case-insensitive
- "USeR" : token: USeR , case-SENSITIVE
- "+uSeR" : token: +uSeR , case-SENSITIVE
- "+UsE"R : token: +UsEr , case-insensitive
- U"S""e"R : token: US"eR , case-insensitive

- +USER : token: USER , case-insensitive, group_name
- +"uSeR" : token: uSeR , case_SENSITIVE, group_name
- +U"sE""r" : token: UsE"r , case-insensitive, group_name

- + : token: + , (useless?)
- @ : token: @ , (useless?)
- @ho"ge : token: ho"ge, file_inclusion (not confirmed)

There's a concern that Case-insensitive matching is accomplished
by full-scan on pg_database or pg_authid so it would be rather
slow than case-sensitive matching. This might not be acceptable
by the community.

That does indeed sound bad. Couldn't we handle this the same
way we handle SQL identifiers, i.e. simply downcase unquoted
identifiers, and then compare case-sensitively?

So foo, Foo and FOO would all match the user called <foo>,
but "Foo" would match the user called <Foo>, and "FOO" the
user called <FOO>.

An unquoted "+" would cause whatever follows it to be interpreted
as a group name, whereas a quoted "+" would simply become part of
the user name (or group name, if there's an additional unquoted
"+" before it).

So +foo would refer to the group <foo>, +"FOO" to the group <FOO>,
and +"+A" to the group <+A>.

I haven't checked if such an approach would be sufficiently
backwards-compatible, though.

best regards,
Florian Pflug

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#18Robert Haas
robertmhaas@gmail.com
In reply to: Kyotaro HORIGUCHI (#15)
Re: [TODO] Process pg_hba.conf keywords as case-insensitive

On Wed, Sep 10, 2014 at 4:54 AM, Kyotaro HORIGUCHI
<horiguchi.kyotaro@lab.ntt.co.jp> wrote:

Finally I think that we need case-insensitive version of
get_role_id and() get_database_id() to acoomplish this patch'es
objective. (This runs full-scans on pg_database or pg_authid X()

Any such thing is certainly grounds for rejecting the patch outright.
It may be that pg_hba.conf should follow the same case-folding rules
we use elsewhere, but it should not invent novel semantics, especially
ones that make connecting to the database a far more expensive
operation than it is today.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#19Kyotaro HORIGUCHI
horiguchi.kyotaro@lab.ntt.co.jp
In reply to: Robert Haas (#18)
Re: [TODO] Process pg_hba.conf keywords as case-insensitive

Hi,

At Thu, 11 Sep 2014 08:10:54 -0400, Robert Haas <robertmhaas@gmail.com> wrote in <CA+TgmoZ9xiNc_cA23-p1dMiHMV0zHcKeF6_rV6V3S+OxRLACNg@mail.gmail.com>

On Wed, Sep 10, 2014 at 4:54 AM, Kyotaro HORIGUCHI
<horiguchi.kyotaro@lab.ntt.co.jp> wrote:

Finally I think that we need case-insensitive version of
get_role_id and() get_database_id() to acoomplish this patch'es
objective. (This runs full-scans on pg_database or pg_authid X()

Any such thing is certainly grounds for rejecting the patch outright.
It may be that pg_hba.conf should follow the same case-folding rules
we use elsewhere, but it should not invent novel semantics, especially
ones that make connecting to the database a far more expensive
operation than it is today.

No wonder. I wondered why such things are needed for this
'case-insensitive matcing'. I've misunderstood the meaning of
'case-insensitive'. There's no need to scanning catalogues for
the 'case-insensitive' matching. Thank you for suggestion.

- Non-quoted names are matched with the names in the catalog
after lowercased.

- Quoted names are matched as is.

This is archieved by simply downcase the identifier if not
case-insensitive notation, and remove case-insensitive version
catalog stuff.

I'll show you more reasonable version sooner.

--
Kyotaro Horiguchi
NTT Open Source Software Center

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#20Kyotaro HORIGUCHI
horiguchi.kyotaro@lab.ntt.co.jp
In reply to: Kyotaro HORIGUCHI (#15)
2 attachment(s)
Re: [TODO] Process pg_hba.conf keywords as case-insensitive

Hi, This is revised patch including document.

I confused three identifiers to be compared, names in the
catalog, those in pg_hba lines and those given from the client
under connecting. This patch concerns the comparison between
pg_hba and client names.

Finally all the additional pg_strcasecmp() or whole catalog
scanning are eliminated. This version works as following.

Tokenize every hba tokens and categorize having two attributes,

One is whether the case is preserved or not. Case of a word is
preserved in the returned token if the word is enclosed with
double quotes.

Another is token type, Leading bare '+' indicates the token is
a group name, and '@' indicates file inclusion. The string in
returned token is stripped of the special characters.

A double quoted region which does not begin at the beginning
of the word was handled in its own way from before this
change. I don't know it is right or not. (ho"r""i"guti stored
as hor"iguti by the orignal next_token() and it is not
changed)

Matching names are performed as following,

Tokens corrensponding to keywords should be 'normal' ones (not
a group name or file inclusion) and should not be
case-preserved ones, which were enclosed by double quotes. The
tokens are lowercased so token_is_keyword() macro compares
them by strcmp().

Database name and user name should be 'normal' tokens and the
cases of the names are preserved or not according to the
notaion in hba line so token_matches() compares them with the
name given from client by strcmp().

The patch size is far reduced from the previous version.

At Wed, 10 Sep 2014 11:32:22 +0200, Florian Pflug <fgp@phlo.org> wrote in <7D70EE06-1E80-44D6-9428-5F60AD796D26@phlo.org>

So foo, Foo and FOO would all match the user called <foo>,
but "Foo" would match the user called <Foo>, and "FOO" the
user called <FOO>.

This patch does so.

An unquoted "+" would cause whatever follows it to be interpreted
as a group name, whereas a quoted "+" would simply become part of
the user name (or group name, if there's an additional unquoted
"+" before it).
So +foo would refer to the group <foo>, +"FOO" to the group <FOO>,
and +"+A" to the group <+A>.

I think this behaves so.

I haven't checked if such an approach would be sufficiently
backwards-compatible, though.

One obveous breaking which affects the existing sane pg_hba.conf
is that db and user names not surrounded by double quotes became
to match the lowercased names, not the original name containing
uppercase characters. But this is just what this patch intended.

I think all behaviors for other cases appear in existing
pg_hba.conf are unchanged including the behaviors for string
consists of single character '+' or '@'.

# '+' is treated as a group name '' and '@' is treated as a
# user/db name '@' but they seems meanless..

Any suggestions?

regards,

--
Kyotaro Horiguchi
NTT Open Source Software Center

Attachments:

0002-Make-pg_hba.conf-case-insensitive.patchtext/x-patch; charset=us-asciiDownload
>From b02cea3ead352a198f341c0f2a9f6ab93f439077 Mon Sep 17 00:00:00 2001
From: Kyotaro Horiguchi <horiguchi.kyotaro@lab.ntt.co.jp>
Date: Thu, 18 Sep 2014 17:06:21 +0900
Subject: [PATCH 2/2] Make pg_hba.conf case insensitive.

---
 src/backend/libpq/hba.c |  101 ++++++++++++++++++++++++++++++++++++++--------
 1 files changed, 83 insertions(+), 18 deletions(-)

diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index 84da823..e4b1635 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -60,9 +60,18 @@ typedef struct check_network_data
 	bool		result;			/* set to true if match */
 } check_network_data;
 
+typedef enum TokenType
+{
+	NORMAL,
+	GROUP_NAME,			/* this token had leading '+' */
+	FILE_INCLUSION,		/* this token had leading '@' */
+} TokenType;
 
-#define token_is_keyword(t, k)	(!t->quoted && strcmp(t->string, k) == 0)
-#define token_matches(t, k)  (strcmp(t->string, k) == 0)
+#define token_is_keyword(tk, kw)	\
+	((tk)->type == NORMAL && !(tk)->case_preserved &&	\
+	 (strcmp((tk)->string, (kw)) == 0))
+#define token_matches(t, k)	\
+	((t)->type == NORMAL && (strcmp((t)->string, (k)) == 0))
 
 /*
  * A single string token lexed from the HBA config file, together with whether
@@ -71,7 +80,8 @@ typedef struct check_network_data
 typedef struct HbaToken
 {
 	char	   *string;
-	bool		quoted;
+	TokenType	type;
+	bool		case_preserved;
 } HbaToken;
 
 /*
@@ -123,8 +133,14 @@ pg_isblank(const char c)
  * the first character.  (We use that to prevent "@x" from being treated
  * as a file inclusion request.  Note that @"x" should be so treated;
  * we want to allow that to support embedded spaces in file paths.)
+ * type is one of NORMAL, GROUP_NAME, FILE_INCLUSION. GROUP_NAME is set if the
+ * token is prefix by '+', FILE_INCLUSION if prefixed by '@', NORMAL
+ * otherwise.
  * We set *terminating_comma to indicate whether the token is terminated by a
  * comma (which is not returned.)
+ * case_preserved is set if the token's case of every character is preserved
+ * when the whole word is enclosed by double quotes. Elsewise returned token
+ * string is lowercased.
  *
  * If successful: store null-terminated token at *buf and return TRUE.
  * If no more tokens on line: set *buf = '\0' and return FALSE.
@@ -136,8 +152,8 @@ pg_isblank(const char c)
  * Handle comments.
  */
 static bool
-next_token(char **lineptr, char *buf, int bufsz, bool *initial_quote,
-		   bool *terminating_comma)
+next_token(char **lineptr, char *buf, int bufsz,
+		   bool *case_preserved, int *type, bool *terminating_comma)
 {
 	int			c;
 	char	   *start_buf = buf;
@@ -149,8 +165,9 @@ next_token(char **lineptr, char *buf, int bufsz, bool *initial_quote,
 	/* end_buf reserves two bytes to ensure we can append \n and \0 */
 	Assert(end_buf > start_buf);
 
-	*initial_quote = false;
 	*terminating_comma = false;
+	*case_preserved = false;
+	*type = NORMAL;
 
 	/* Move over initial whitespace and commas */
 	while ((c = (*(*lineptr)++)) != '\0' && (pg_isblank(c) || c == ','))
@@ -162,6 +179,17 @@ next_token(char **lineptr, char *buf, int bufsz, bool *initial_quote,
 		return false;
 	}
 
+	if (c == '+' || c == '@')
+	{
+		*type = (c == '+' ? GROUP_NAME : FILE_INCLUSION);
+
+		/*
+		 * Skip capturing it, and we can read the following characters as
+		 * usual.
+		 */
+		c = *(*lineptr)++;
+	}
+
 	/*
 	 * Build a token in buf of next characters up to EOF, EOL, unquoted comma,
 	 * or unquoted whitespace.
@@ -201,8 +229,17 @@ next_token(char **lineptr, char *buf, int bufsz, bool *initial_quote,
 		}
 
 		if (c != '"' || was_quote)
+		{
 			*buf++ = c;
 
+			/*
+			 * Cancel case-sensitive state if trailing characters found for
+			 * the quoted region.
+			 */
+			if (*case_preserved && !in_quote)
+				*case_preserved = false;
+		}
+
 		/* Literal double-quote is two double-quotes */
 		if (in_quote && c == '"')
 			was_quote = !was_quote;
@@ -214,7 +251,7 @@ next_token(char **lineptr, char *buf, int bufsz, bool *initial_quote,
 			in_quote = !in_quote;
 			saw_quote = true;
 			if (buf == start_buf)
-				*initial_quote = true;
+				*case_preserved = true;
 		}
 
 		c = *(*lineptr)++;
@@ -226,13 +263,32 @@ next_token(char **lineptr, char *buf, int bufsz, bool *initial_quote,
 	 */
 	(*lineptr)--;
 
+	/*
+	 * only '@' alone is treated as the character itself for backward
+	 * compatibility.
+	 */
+	if (buf == start_buf && *type == FILE_INCLUSION)
+	{
+		*buf++ = '@';
+		*type = NORMAL;
+	}
+
 	*buf = '\0';
 
-	return (saw_quote || buf > start_buf);
+	/* Down case the names if non-case-preserve notation */
+	if (!*case_preserved)
+	{
+		char *p;
+		for (p = start_buf ; p < buf ; p++) 
+			*p = tolower(*p);
+	}
+
+	/* Allowing empty group name for backward comptibility */
+	return (saw_quote || buf > start_buf || *type == GROUP_NAME);
 }
 
 static HbaToken *
-make_hba_token(char *token, bool quoted)
+make_hba_token(char *token, TokenType toktype, bool case_preserved)
 {
 	HbaToken   *hbatoken;
 	int			toklen;
@@ -240,7 +296,8 @@ make_hba_token(char *token, bool quoted)
 	toklen = strlen(token);
 	hbatoken = (HbaToken *) palloc(sizeof(HbaToken) + toklen + 1);
 	hbatoken->string = (char *) hbatoken + sizeof(HbaToken);
-	hbatoken->quoted = quoted;
+	hbatoken->type = toktype;
+	hbatoken->case_preserved = case_preserved;
 	memcpy(hbatoken->string, token, toklen + 1);
 
 	return hbatoken;
@@ -252,7 +309,8 @@ make_hba_token(char *token, bool quoted)
 static HbaToken *
 copy_hba_token(HbaToken *in)
 {
-	HbaToken   *out = make_hba_token(in->string, in->quoted);
+	HbaToken   *out = make_hba_token(in->string,
+									 in->type, in->case_preserved);
 
 	return out;
 }
@@ -269,19 +327,26 @@ next_field_expand(const char *filename, char **lineptr)
 {
 	char		buf[MAX_TOKEN];
 	bool		trailing_comma;
-	bool		initial_quote;
+	bool		case_preserved;
+	int			type;
 	List	   *tokens = NIL;
 
 	do
 	{
-		if (!next_token(lineptr, buf, sizeof(buf), &initial_quote, &trailing_comma))
+		if (!next_token(lineptr, buf, sizeof(buf), 
+						&case_preserved, &type,
+						&trailing_comma))
 			break;
 
 		/* Is this referencing a file? */
-		if (!initial_quote && buf[0] == '@' && buf[1] != '\0')
-			tokens = tokenize_inc_file(tokens, filename, buf + 1);
+		if (type == FILE_INCLUSION)
+			tokens = tokenize_inc_file(tokens, filename, buf);
+		else if (type == GROUP_NAME)
+			tokens = lappend(tokens,
+							 make_hba_token(buf, GROUP_NAME, case_preserved));
 		else
-			tokens = lappend(tokens, make_hba_token(buf, initial_quote));
+			tokens = lappend(tokens,
+							 make_hba_token(buf, NORMAL, case_preserved));
 	} while (trailing_comma);
 
 	return tokens;
@@ -489,9 +554,9 @@ check_role(const char *role, Oid roleid, List *tokens)
 	foreach(cell, tokens)
 	{
 		tok = lfirst(cell);
-		if (!tok->quoted && tok->string[0] == '+')
+		if (tok->type == GROUP_NAME)
 		{
-			if (is_member(roleid, tok->string + 1))
+			if (is_member(roleid, tok->string))
 				return true;
 		}
 		else if (token_matches(tok, role) ||
-- 
1.7.1

0001-Document-for-make-pg_hba.conf-case-insensitive.patchtext/x-patch; charset=us-asciiDownload
>From 3eafe5ef922f3bdf9ea34d8ae408c3b1d0345768 Mon Sep 17 00:00:00 2001
From: Kyotaro Horiguchi <horiguchi.kyotaro@lab.ntt.co.jp>
Date: Thu, 18 Sep 2014 17:07:41 +0900
Subject: [PATCH 1/2] Document for make pg_hba.conf case insensitive

---
 doc/src/sgml/client-auth.sgml |   10 +++++++++-
 1 files changed, 9 insertions(+), 1 deletions(-)

diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml
index 7704f73..5fd0108 100644
--- a/doc/src/sgml/client-auth.sgml
+++ b/doc/src/sgml/client-auth.sgml
@@ -196,8 +196,13 @@ hostnossl  <replaceable>database</replaceable>  <replaceable>user</replaceable>
        Otherwise, this is the name of
        a specific <productname>PostgreSQL</productname> database.
        Multiple database names can be supplied by separating them with
-       commas.  A separate file containing database names can be specified by
+       commas.
+       Quoted identifers excepet unicode escaping are
+       allowed. (See <xref linkend="sql-syntax-identifiers"> for more
+       information).
+       A separate file containing database names can be specified by
        preceding the file name with <literal>@</>.
+       the file name with <literal>@</>.
       </para>
      </listitem>
     </varlistentry>
@@ -219,6 +224,9 @@ hostnossl  <replaceable>database</replaceable>  <replaceable>user</replaceable>
        of the role, directly or indirectly, and not just by virtue of
        being a superuser.
        Multiple user names can be supplied by separating them with commas.
+       Quoted identifers excepet unicode escaping are
+       allowed. (See <xref linkend="sql-syntax-identifiers"> for more
+       information).
        A separate file containing user names can be specified by preceding the
        file name with <literal>@</>.
       </para>
-- 
1.7.1