Magnus Hagander wrote: > This patch changes the options field of pg_hba.conf to take name/value > pairs instead of a fixed string. This makes it a lot nicer to deal with > auth methods that need more than one parameter, such as LDAP. > > While at it, it also adds map support to kerberos, gssapi and sspi and > not just ident - basically all methods where the username comes from an > outside source (lmk if I missed one). > > Also in passing, changes the methods in auth.c to deal with "unsupported > auth method on this platform" errors the same way for all authentication > methods. > > I intend to build on this patch to support setting some > Kerberos/GSSAPI/SSPI parameters on a per-connection base, but wanted to > get the basics in first. > > Obviously, documentation still pending. I'm working on that in parallel. > > > So, comments? Both in general, and specifically on if we need to do > backwards compatible parsing of LDAP options (doing it of all the other > options would be trivial, but LDAP would be harder)
Updated version of this patch, now with doc changes. //Magnus
*** a/doc/src/sgml/client-auth.sgml --- b/doc/src/sgml/client-auth.sgml *************** *** 96,108 **** <para> A record can have one of the seven formats <synopsis> ! local <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>auth-method</replaceable> <optional><replaceable>auth-option</replaceable></optional> ! host <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>CIDR-address</replaceable> <replaceable>auth-method</replaceable> <optional><replaceable>auth-option</replaceable></optional> ! hostssl <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>CIDR-address</replaceable> <replaceable>auth-method</replaceable> <optional><replaceable>auth-option</replaceable></optional> ! hostnossl <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>CIDR-address</replaceable> <replaceable>auth-method</replaceable> <optional><replaceable>auth-option</replaceable></optional> ! host <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>IP-address</replaceable> <replaceable>IP-mask</replaceable> <replaceable>auth-method</replaceable> <optional><replaceable>auth-option</replaceable></optional> ! hostssl <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>IP-address</replaceable> <replaceable>IP-mask</replaceable> <replaceable>auth-method</replaceable> <optional><replaceable>auth-option</replaceable></optional> ! hostnossl <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>IP-address</replaceable> <replaceable>IP-mask</replaceable> <replaceable>auth-method</replaceable> <optional><replaceable>auth-option</replaceable></optional> </synopsis> The meaning of the fields is as follows: --- 96,108 ---- <para> A record can have one of the seven formats <synopsis> ! local <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>auth-method</replaceable> <optional><replaceable>auth-options</replaceable></optional> ! host <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>CIDR-address</replaceable> <replaceable>auth-method</replaceable> <optional><replaceable>auth-options</replaceable></optional> ! hostssl <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>CIDR-address</replaceable> <replaceable>auth-method</replaceable> <optional><replaceable>auth-options</replaceable></optional> ! hostnossl <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>CIDR-address</replaceable> <replaceable>auth-method</replaceable> <optional><replaceable>auth-options</replaceable></optional> ! host <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>IP-address</replaceable> <replaceable>IP-mask</replaceable> <replaceable>auth-method</replaceable> <optional><replaceable>auth-options</replaceable></optional> ! hostssl <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>IP-address</replaceable> <replaceable>IP-mask</replaceable> <replaceable>auth-method</replaceable> <optional><replaceable>auth-options</replaceable></optional> ! hostnossl <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>IP-address</replaceable> <replaceable>IP-mask</replaceable> <replaceable>auth-method</replaceable> <optional><replaceable>auth-options</replaceable></optional> </synopsis> The meaning of the fields is as follows: *************** *** 422,432 **** hostnossl <replaceable>database</replaceable> <replaceable>user</replaceable> </varlistentry> <varlistentry> ! <term><replaceable>auth-option</replaceable></term> <listitem> <para> ! The meaning of this optional field depends on the chosen ! authentication method. Details appear below. </para> </listitem> </varlistentry> --- 422,434 ---- </varlistentry> <varlistentry> ! <term><replaceable>auth-options</replaceable></term> <listitem> <para> ! This field contains zero or more name-value pairs with ! extra options passed to this authentication method. Details ! about which options are available for which authentication ! method appear below. </para> </listitem> </varlistentry> *************** *** 534,540 **** host all all 0.0.0.0/0 krb5 # "omicron" that says "bryanh" is allowed to connect as "guest1". # # TYPE DATABASE USER CIDR-ADDRESS METHOD ! host all all 192.168.0.0/16 ident omicron # If these are the only three lines for local connections, they will # allow local users to connect only to their own databases (databases --- 536,542 ---- # "omicron" that says "bryanh" is allowed to connect as "guest1". # # TYPE DATABASE USER CIDR-ADDRESS METHOD ! host all all 192.168.0.0/16 ident map=omicron # If these are the only three lines for local connections, they will # allow local users to connect only to their own databases (databases *************** *** 557,562 **** local db1,db2,@demodbs all md5 --- 559,650 ---- </example> </sect1> + <sect1 id="auth-username-maps"> + <title>Username maps</title> + + <indexterm zone="auth-username-maps"> + <primary>Username maps</primary> + </indexterm> + + <para> + When using an external authentication system like Ident or GSSAPI, + the name of the operating system user that initiated the connection may + not be the same as the database user he is requesting to connect as. + In this case, a user name map can be applied to map the operating system + username to a database user, using the <filename>pg_ident.conf</filename> + file. In order to use username mapping, specify + <literal>map</literal>=<replaceable>map-name</replaceable> + in the options field in <filename>pg_hba.conf</filename>. This option is + supported for all authentication methods that receive external usernames. + Since the <filename>pg_ident.conf</filename> file can contain multiple maps, + the name of the map to be used is specified in the + <replaceable>map-name</replaceable> parameter in <filename>pg_hba.conf</filename> + to indicate which map to use for each individual connection. + </para> + + <para> + Ident maps are defined in the ident map file, which by default is named + <filename>pg_ident.conf</><indexterm><primary>pg_ident.conf</primary></indexterm> + and is stored in the + cluster's data directory. (It is possible to place the map file + elsewhere, however; see the <xref linkend="guc-ident-file"> + configuration parameter.) + The ident map file contains lines of the general form: + <synopsis> + <replaceable>map-name</> <replaceable>system-username</> <replaceable>database-username</> + </synopsis> + Comments and whitespace are handled in the same way as in + <filename>pg_hba.conf</>. The + <replaceable>map-name</> is an arbitrary name that will be used to + refer to this mapping in <filename>pg_hba.conf</filename>. The other + two fields specify which operating system user is allowed to connect + as which database user. The same <replaceable>map-name</> can be + used repeatedly to specify more user-mappings within a single map. + There is no restriction regarding how many database users a given + operating system user can correspond to, nor vice versa. + </para> + + <para> + The <filename>pg_ident.conf</filename> file is read on start-up and + when the main server process receives a + <systemitem>SIGHUP</systemitem><indexterm><primary>SIGHUP</primary></indexterm> + signal. If you edit the file on an + active system, you will need to signal the server + (using <literal>pg_ctl reload</> or <literal>kill -HUP</>) to make it + re-read the file. + </para> + + <para> + A <filename>pg_ident.conf</filename> file that could be used in + conjunction with the <filename>pg_hba.conf</> file in <xref + linkend="example-pg-hba.conf"> is shown in <xref + linkend="example-pg-ident.conf">. In this example setup, anyone + logged in to a machine on the 192.168 network that does not have the + Unix user name <literal>bryanh</>, <literal>ann</>, or + <literal>robert</> would not be granted access. Unix user + <literal>robert</> would only be allowed access when he tries to + connect as <productname>PostgreSQL</> user <literal>bob</>, not + as <literal>robert</> or anyone else. <literal>ann</> would + only be allowed to connect as <literal>ann</>. User + <literal>bryanh</> would be allowed to connect as either + <literal>bryanh</> himself or as <literal>guest1</>. + </para> + + <example id="example-pg-ident.conf"> + <title>An example <filename>pg_ident.conf</> file</title> + <programlisting> + # MAPNAME IDENT-USERNAME PG-USERNAME + + omicron bryanh bryanh + omicron ann ann + # bob has user name robert on these machines + omicron robert bob + # bryanh can also connect as guest1 + omicron bryanh guest1 + </programlisting> + </example> + </sect1> + <sect1 id="auth-methods"> <title>Authentication methods</title> <para> *************** *** 685,691 **** local db1,db2,@demodbs all md5 GSSAPI support has to be enabled when <productname>PostgreSQL</> is built; see <xref linkend="installation"> for more information. </para> ! </sect2> <sect2 id="sspi-auth"> --- 773,793 ---- GSSAPI support has to be enabled when <productname>PostgreSQL</> is built; see <xref linkend="installation"> for more information. </para> ! ! <para> ! The following configuration options are supported for <productname>GSSAPI</productname>: ! <variablelist> ! <varlistentry> ! <term>map</term> ! <listitem> ! <para> ! Allows for mapping between system and database usernames. See ! <xref linkend="auth-username-maps"> for details. ! </para> ! </listitem> ! </varlistentry> ! </variablelist> ! </para> </sect2> <sect2 id="sspi-auth"> *************** *** 713,718 **** local db1,db2,@demodbs all md5 --- 815,834 ---- for details. </para> + <para> + The following configuration options are supported for <productname>SSPI</productname>: + <variablelist> + <varlistentry> + <term>map</term> + <listitem> + <para> + Allows for mapping between system and database usernames. See + <xref linkend="auth-username-maps"> for details. + </para> + </listitem> + </varlistentry> + </variablelist> + </para> </sect2> <sect2 id="kerberos-auth"> *************** *** 846,851 **** local db1,db2,@demodbs all md5 --- 962,982 ---- depending on the connection type. </para> + <para> + The following configuration options are supported for <productname>GSSAPI</productname>: + <variablelist> + <varlistentry> + <term>map</term> + <listitem> + <para> + Allows for mapping between system and database usernames. See + <xref linkend="auth-username-maps"> for details. + </para> + </listitem> + </varlistentry> + </variablelist> + </para> + <sect3> <title>Ident Authentication over TCP/IP</title> *************** *** 918,1000 **** local db1,db2,@demodbs all md5 </para> </sect3> - <sect3 id="auth-ident-maps"> - <title>Ident Maps</title> - - <para> - When using ident-based authentication, after having determined the - name of the operating system user that initiated the connection, - <productname>PostgreSQL</productname> checks whether that user is - allowed to connect as the database user he is requesting to connect - as. This is controlled by the ident map argument that follows the - <literal>ident</> key word in the <filename>pg_hba.conf</filename> - file. If an ident map is not specified, the database user will be - checked with the same name as the operating system user. Other maps - must be created manually. - </para> - - <para> - Ident maps are defined in the ident map file, which by default is named - <filename>pg_ident.conf</><indexterm><primary>pg_ident.conf</primary></indexterm> - and is stored in the - cluster's data directory. (It is possible to place the map file - elsewhere, however; see the <xref linkend="guc-ident-file"> - configuration parameter.) - The ident map file contains lines of the general form: - <synopsis> - <replaceable>map-name</> <replaceable>ident-username</> <replaceable>database-username</> - </synopsis> - Comments and whitespace are handled in the same way as in - <filename>pg_hba.conf</>. The - <replaceable>map-name</> is an arbitrary name that will be used to - refer to this mapping in <filename>pg_hba.conf</filename>. The other - two fields specify which operating system user is allowed to connect - as which database user. The same <replaceable>map-name</> can be - used repeatedly to specify more user-mappings within a single map. - There is no restriction regarding how many database users a given - operating system user can correspond to, nor vice versa. - </para> - - <para> - The <filename>pg_ident.conf</filename> file is read on start-up and - when the main server process receives a - <systemitem>SIGHUP</systemitem><indexterm><primary>SIGHUP</primary></indexterm> - signal. If you edit the file on an - active system, you will need to signal the server - (using <literal>pg_ctl reload</> or <literal>kill -HUP</>) to make it - re-read the file. - </para> - - <para> - A <filename>pg_ident.conf</filename> file that could be used in - conjunction with the <filename>pg_hba.conf</> file in <xref - linkend="example-pg-hba.conf"> is shown in <xref - linkend="example-pg-ident.conf">. In this example setup, anyone - logged in to a machine on the 192.168 network that does not have the - Unix user name <literal>bryanh</>, <literal>ann</>, or - <literal>robert</> would not be granted access. Unix user - <literal>robert</> would only be allowed access when he tries to - connect as <productname>PostgreSQL</> user <literal>bob</>, not - as <literal>robert</> or anyone else. <literal>ann</> would - only be allowed to connect as <literal>ann</>. User - <literal>bryanh</> would be allowed to connect as either - <literal>bryanh</> himself or as <literal>guest1</>. - </para> - - <example id="example-pg-ident.conf"> - <title>An example <filename>pg_ident.conf</> file</title> - <programlisting> - # MAPNAME IDENT-USERNAME PG-USERNAME - - omicron bryanh bryanh - omicron ann ann - # bob has user name robert on these machines - omicron robert bob - # bryanh can also connect as guest1 - omicron bryanh guest1 - </programlisting> - </example> - </sect3> </sect2> <sect2 id="auth-ldap"> --- 1049,1054 ---- *************** *** 1007,1055 **** omicron bryanh guest1 <para> This authentication method operates similarly to <literal>password</literal> except that it uses LDAP ! as the authentication method. LDAP is used only to validate the user name/password pairs. Therefore the user must already exist in the database before LDAP can be used for ! authentication. The server and parameters used are specified ! after the <literal>ldap</> key word in the file ! <filename>pg_hba.conf</filename>. The format of this parameter is: ! <synopsis> ! ldap[<replaceable>s</>]://<replaceable>servername</>[:<replaceable>port</>]/<replaceable>base dn</replaceable>[;<replaceable>prefix</>[;<replaceable>suffix</>]] ! </synopsis> ! Commas are used to specify multiple items in an <literal>ldap</> ! component. However, because unquoted commas are treated as item ! separators in <filename>pg_hba.conf</filename>, it is wise to ! double-quote the <literal>ldap</> URL to preserve any commas present, ! e.g.: ! <synopsis> ! "ldap://ldap.example.net/dc=example,dc=net;EXAMPLE\" ! </synopsis> ! ! </para> ! <para> ! If <literal>ldaps</> is specified instead of <literal>ldap</>, ! TLS encryption will be enabled for the connection. Note that this ! will encrypt only the connection between the PostgreSQL server ! and the LDAP server. The connection between the client and the ! PostgreSQL server is not affected by this setting. To make use of ! TLS encryption, you might need to configure the LDAP library prior ! to configuring PostgreSQL. Note that encrypted LDAP is available only ! if the platform's LDAP library supports it. ! </para> ! <para> ! If no port is specified, the default port as configured in the ! LDAP library will be used. </para> <para> ! The server will bind to the distinguished name specified as ! <replaceable>base dn</> using the user name supplied by the client. ! If <replaceable>prefix</> and <replaceable>suffix</> is ! specified, it will be prepended and appended to the user name before the bind. Typically, the prefix parameter is used to specify <replaceable>cn=</>, or <replaceable>DOMAIN\</> in an Active ! Directory environment. </para> ! </sect2> <sect2 id="auth-pam"> --- 1061,1144 ---- <para> This authentication method operates similarly to <literal>password</literal> except that it uses LDAP ! as the password verification method. LDAP is used only to validate the user name/password pairs. Therefore the user must already exist in the database before LDAP can be used for ! authentication. </para> + <para> ! The server will bind to the distinguished name constructed as ! <replaceable>prefix</> <replaceable>username</> <replaceable>suffix</>. before the bind. Typically, the prefix parameter is used to specify <replaceable>cn=</>, or <replaceable>DOMAIN\</> in an Active ! Directory environment, and suffix is used to specify the remaining part ! of the DN in a non-Active Directory environment. </para> ! ! <para> ! The following configuration options are supported for LDAP: ! <variablelist> ! <varlistentry> ! <term>ldapserver</term> ! <listitem> ! <para> ! Name or IP of LDAP server to connect to. ! </para> ! </listitem> ! </varlistentry> ! <varlistentry> ! <term>ldapprefix</term> ! <listitem> ! <para> ! String to prepend to the username when building the base DN to ! bind as. ! </para> ! </listitem> ! </varlistentry> ! <varlistentry> ! <term>ldapsuffix</term> ! <listitem> ! <para> ! String to append to the username when building the base DN to ! bind as. ! </para> ! </listitem> ! </varlistentry> ! <varlistentry> ! <term>ldapport</term> ! <listitem> ! <para> ! Port number on LDAP server to connect to. If no port is specified, ! the default port in the LDAP library will be used. ! </para> ! </listitem> ! </varlistentry> ! <varlistentry> ! <term>ldaptls</term> ! <listitem> ! <para> ! Set to 1 to make the connection between PostgreSQL and the ! LDAP server use TLS encryption. Note that this only encrypts ! the traffic to the LDAP server - the connection to the client ! may still be unencrypted unless TLS is used there as well. ! </para> ! </listitem> ! </varlistentry> ! </variablelist> ! </para> ! ! <note> ! <para> ! Since LDAP often uses commas and spaces to separate the different ! parts of a DN, it is advised to always use double-quoted parameter ! values when configuring LDAP options, such as: ! </para> ! </note> ! <synopsis> ! ldapserver=ldap.example.net prefix="cn=" suffix="dc=example, dc=net" ! </synopsis> ! </sect2> <sect2 id="auth-pam"> *************** *** 1063,1071 **** ldap[<replaceable>s</>]://<replaceable>servername</>[:<replaceable>port</>]/<rep This authentication method operates similarly to <literal>password</literal> except that it uses PAM (Pluggable Authentication Modules) as the authentication mechanism. The ! default PAM service name is <literal>postgresql</literal>. You can ! optionally supply your own service name after the <literal>pam</> ! key word in the file <filename>pg_hba.conf</filename>. PAM is used only to validate user name/password pairs. Therefore the user must already exist in the database before PAM can be used for authentication. For more information about --- 1152,1158 ---- This authentication method operates similarly to <literal>password</literal> except that it uses PAM (Pluggable Authentication Modules) as the authentication mechanism. The ! default PAM service name is <literal>postgresql</literal>. PAM is used only to validate user name/password pairs. Therefore the user must already exist in the database before PAM can be used for authentication. For more information about *************** *** 1075,1080 **** ldap[<replaceable>s</>]://<replaceable>servername</>[:<replaceable>port</>]/<rep --- 1162,1181 ---- <systemitem class="osname">Solaris</> PAM Page</ulink>. </para> + <para> + The following configuration options are supported for PAM: + <variablelist> + <varlistentry> + <term>pamservice</term> + <listitem> + <para> + PAM service name. + </para> + </listitem> + </varlistentry> + </variablelist> + </para> + <note> <para> If PAM is set up to read <filename>/etc/shadow</>, authentication *** a/src/backend/libpq/auth.c --- b/src/backend/libpq/auth.c *************** *** 126,134 **** char *pg_krb_realm = NULL; * MIT Kerberos authentication system - protocol version 5 *---------------------------------------------------------------- */ - static int pg_krb5_recvauth(Port *port); - #ifdef KRB5 #include <krb5.h> /* Some old versions of Kerberos do not include <com_err.h> in <krb5.h> */ --- 126,133 ---- * MIT Kerberos authentication system - protocol version 5 *---------------------------------------------------------------- */ #ifdef KRB5 + static int pg_krb5_recvauth(Port *port); #include <krb5.h> /* Some old versions of Kerberos do not include <com_err.h> in <krb5.h> */ *************** *** 150,163 **** static krb5_principal pg_krb5_server; * GSSAPI Authentication *---------------------------------------------------------------- */ - static int pg_GSS_recvauth(Port *port); - #ifdef ENABLE_GSS #if defined(HAVE_GSSAPI_H) #include <gssapi.h> #else #include <gssapi/gssapi.h> #endif #endif /* ENABLE_GSS */ --- 149,162 ---- * GSSAPI Authentication *---------------------------------------------------------------- */ #ifdef ENABLE_GSS #if defined(HAVE_GSSAPI_H) #include <gssapi.h> #else #include <gssapi/gssapi.h> #endif + + static int pg_GSS_recvauth(Port *port); #endif /* ENABLE_GSS */ *************** *** 165,176 **** static int pg_GSS_recvauth(Port *port); * SSPI Authentication *---------------------------------------------------------------- */ - static int pg_SSPI_recvauth(Port *port); - #ifdef ENABLE_SSPI typedef SECURITY_STATUS (WINAPI * QUERY_SECURITY_CONTEXT_TOKEN_FN) ( PCtxtHandle, void **); #endif --- 164,174 ---- * SSPI Authentication *---------------------------------------------------------------- */ #ifdef ENABLE_SSPI typedef SECURITY_STATUS (WINAPI * QUERY_SECURITY_CONTEXT_TOKEN_FN) ( PCtxtHandle, void **); + static int pg_SSPI_recvauth(Port *port); #endif *************** *** 236,251 **** auth_failed(Port *port, int status) case uaPassword: errstr = gettext_noop("password authentication failed for user \"%s\""); break; - #ifdef USE_PAM case uaPAM: errstr = gettext_noop("PAM authentication failed for user \"%s\""); break; - #endif /* USE_PAM */ - #ifdef USE_LDAP case uaLDAP: errstr = gettext_noop("LDAP authentication failed for user \"%s\""); break; - #endif /* USE_LDAP */ default: errstr = gettext_noop("authentication failed for user \"%s\": invalid authentication method"); break; --- 234,245 ---- *************** *** 316,333 **** ClientAuthentication(Port *port) --- 310,339 ---- } case uaKrb5: + #ifdef KRB5 sendAuthRequest(port, AUTH_REQ_KRB5); status = pg_krb5_recvauth(port); + #else + Assert(false); + #endif break; case uaGSS: + #ifdef ENABLE_GSS sendAuthRequest(port, AUTH_REQ_GSS); status = pg_GSS_recvauth(port); + #else + Assert(false); + #endif break; case uaSSPI: + #ifdef ENABLE_SSPI sendAuthRequest(port, AUTH_REQ_SSPI); status = pg_SSPI_recvauth(port); + #else + Assert(false); + #endif break; case uaIdent: *************** *** 377,394 **** ClientAuthentication(Port *port) status = recv_and_check_password_packet(port); break; - #ifdef USE_PAM case uaPAM: pam_port_cludge = port; status = CheckPAMAuth(port, port->user_name, ""); ! break; #endif /* USE_PAM */ - #ifdef USE_LDAP case uaLDAP: status = CheckLDAPAuth(port); ! break; #endif case uaTrust: status = STATUS_OK; --- 383,404 ---- status = recv_and_check_password_packet(port); break; case uaPAM: + #ifdef USE_PAM pam_port_cludge = port; status = CheckPAMAuth(port, port->user_name, ""); ! #else ! Assert(false); #endif /* USE_PAM */ + break; case uaLDAP: + #ifdef USE_LDAP status = CheckLDAPAuth(port); ! #else ! Assert(false); #endif + break; case uaTrust: status = STATUS_OK; *************** *** 713,731 **** pg_krb5_recvauth(Port *port) return STATUS_ERROR; } ! if (pg_krb_caseins_users) ! ret = pg_strncasecmp(port->user_name, kusername, SM_DATABASE_USER); ! else ! ret = strncmp(port->user_name, kusername, SM_DATABASE_USER); ! if (ret) ! { ! ereport(LOG, ! (errmsg("unexpected Kerberos user name received from client (received \"%s\", expected \"%s\")", ! port->user_name, kusername))); ! ret = STATUS_ERROR; ! } ! else ! ret = STATUS_OK; krb5_free_ticket(pg_krb5_context, ticket); krb5_auth_con_free(pg_krb5_context, auth_context); --- 723,730 ---- return STATUS_ERROR; } ! ret = check_usermap(port->hba->usermap, port->user_name, kusername, ! pg_krb_caseins_users); krb5_free_ticket(pg_krb5_context, ticket); krb5_auth_con_free(pg_krb5_context, auth_context); *************** *** 733,748 **** pg_krb5_recvauth(Port *port) return ret; } - #else - - static int - pg_krb5_recvauth(Port *port) - { - ereport(LOG, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("Kerberos 5 not implemented on this server"))); - return STATUS_ERROR; - } #endif /* KRB5 */ --- 732,737 ---- *************** *** 1020,1057 **** pg_GSS_recvauth(Port *port) return STATUS_ERROR; } ! if (pg_krb_caseins_users) ! ret = pg_strcasecmp(port->user_name, gbuf.value); ! else ! ret = strcmp(port->user_name, gbuf.value); ! ! if (ret) ! { ! /* GSS name and PGUSER are not equivalent */ ! elog(DEBUG2, ! "provided username (%s) and GSSAPI username (%s) don't match", ! port->user_name, (char *) gbuf.value); ! ! gss_release_buffer(&lmin_s, &gbuf); ! return STATUS_ERROR; ! } gss_release_buffer(&lmin_s, &gbuf); return STATUS_OK; } - - #else /* no ENABLE_GSS */ - - static int - pg_GSS_recvauth(Port *port) - { - ereport(LOG, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("GSSAPI not implemented on this server"))); - return STATUS_ERROR; - } - #endif /* ENABLE_GSS */ --- 1009,1021 ---- return STATUS_ERROR; } ! ret = check_usermap(port->hba->usermap, port->user_name, gbuf.value, ! pg_krb_caseins_users); gss_release_buffer(&lmin_s, &gbuf); return STATUS_OK; } #endif /* ENABLE_GSS */ *************** *** 1328,1357 **** pg_SSPI_recvauth(Port *port) * We have the username (without domain/realm) in accountname, compare to * the supplied value. In SSPI, always compare case insensitive. */ ! if (pg_strcasecmp(port->user_name, accountname)) ! { ! /* GSS name and PGUSER are not equivalent */ ! elog(DEBUG2, ! "provided username (%s) and SSPI username (%s) don't match", ! port->user_name, accountname); ! ! return STATUS_ERROR; ! } ! ! return STATUS_OK; } - - #else /* no ENABLE_SSPI */ - - static int - pg_SSPI_recvauth(Port *port) - { - ereport(LOG, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("SSPI not implemented on this server"))); - return STATUS_ERROR; - } - #endif /* ENABLE_SSPI */ --- 1292,1299 ---- * We have the username (without domain/realm) in accountname, compare to * the supplied value. In SSPI, always compare case insensitive. */ ! return check_usermap(port->hba->usermap, port->user_name, accountname, true); } #endif /* ENABLE_SSPI */ *************** *** 1795,1808 **** authident(hbaPort *port) return STATUS_ERROR; } ! ereport(DEBUG2, ! (errmsg("Ident protocol identifies remote user as \"%s\"", ! ident_user))); ! ! if (check_ident_usermap(port->hba->usermap, port->user_name, ident_user)) ! return STATUS_OK; ! else ! return STATUS_ERROR; } --- 1737,1743 ---- return STATUS_ERROR; } ! return check_usermap(port->hba->usermap, port->user_name, ident_user, false); } *************** *** 1913,1920 **** CheckPAMAuth(Port *port, char *user, char *password) * not allocated */ /* Optionally, one can set the service name in pg_hba.conf */ ! if (port->hba->auth_arg && port->hba->auth_arg[0] != '\0') ! retval = pam_start(port->hba->auth_arg, "pgsql@", &pam_passw_conv, &pamh); else retval = pam_start(PGSQL_PAM_SERVICE, "pgsql@", --- 1848,1855 ---- * not allocated */ /* Optionally, one can set the service name in pg_hba.conf */ ! if (port->hba->pamservice && port->hba->pamservice[0] != '\0') ! retval = pam_start(port->hba->pamservice, "pgsql@", &pam_passw_conv, &pamh); else retval = pam_start(PGSQL_PAM_SERVICE, "pgsql@", *************** *** 2000,2075 **** static int CheckLDAPAuth(Port *port) { char *passwd; - char server[128]; - char basedn[128]; - char prefix[128]; - char suffix[128]; LDAP *ldap; - bool ssl = false; int r; int ldapversion = LDAP_VERSION3; - int ldapport = LDAP_PORT; char fulluser[NAMEDATALEN + 256 + 1]; ! if (!port->hba->auth_arg || port->hba->auth_arg[0] == '\0') { ereport(LOG, ! (errmsg("LDAP configuration URL not specified"))); return STATUS_ERROR; } ! /* ! * Crack the LDAP url. We do a very trivial parse: ! * ! * ldap[s]://<server>[:<port>]/<basedn>[;prefix[;suffix]] ! * ! * This code originally used "%127s" for the suffix, but that doesn't ! * work for embedded whitespace. We know that tokens formed by ! * hba.c won't include newlines, so we can use a "not newline" scanset ! * instead. ! */ ! ! server[0] = '\0'; ! basedn[0] = '\0'; ! prefix[0] = '\0'; ! suffix[0] = '\0'; ! ! /* ldap, including port number */ ! r = sscanf(port->hba->auth_arg, ! "ldap://%127[^:]:%d/%127[^;];%127[^;];%127[^\n]", ! server, &ldapport, basedn, prefix, suffix); ! if (r < 3) ! { ! /* ldaps, including port number */ ! r = sscanf(port->hba->auth_arg, ! "ldaps://%127[^:]:%d/%127[^;];%127[^;];%127[^\n]", ! server, &ldapport, basedn, prefix, suffix); ! if (r >= 3) ! ssl = true; ! } ! if (r < 3) ! { ! /* ldap, no port number */ ! r = sscanf(port->hba->auth_arg, ! "ldap://%127[^/]/%127[^;];%127[^;];%127[^\n]", ! server, basedn, prefix, suffix); ! } ! if (r < 2) ! { ! /* ldaps, no port number */ ! r = sscanf(port->hba->auth_arg, ! "ldaps://%127[^/]/%127[^;];%127[^;];%127[^\n]", ! server, basedn, prefix, suffix); ! if (r >= 2) ! ssl = true; ! } ! if (r < 2) ! { ! ereport(LOG, ! (errmsg("invalid LDAP URL: \"%s\"", ! port->hba->auth_arg))); ! return STATUS_ERROR; ! } sendAuthRequest(port, AUTH_REQ_PASSWORD); --- 1935,1954 ---- CheckLDAPAuth(Port *port) { char *passwd; LDAP *ldap; int r; int ldapversion = LDAP_VERSION3; char fulluser[NAMEDATALEN + 256 + 1]; ! if (!port->hba->ldapserver|| port->hba->ldapserver[0] == '\0') { ereport(LOG, ! (errmsg("LDAP server not specified"))); return STATUS_ERROR; } ! if (port->hba->ldapport == 0) ! port->hba->ldapport = LDAP_PORT; sendAuthRequest(port, AUTH_REQ_PASSWORD); *************** *** 2077,2083 **** CheckLDAPAuth(Port *port) if (passwd == NULL) return STATUS_EOF; /* client wouldn't send password */ ! ldap = ldap_init(server, ldapport); if (!ldap) { #ifndef WIN32 --- 1956,1962 ---- if (passwd == NULL) return STATUS_EOF; /* client wouldn't send password */ ! ldap = ldap_init(port->hba->ldapserver, port->hba->ldapport); if (!ldap) { #ifndef WIN32 *************** *** 2100,2106 **** CheckLDAPAuth(Port *port) return STATUS_ERROR; } ! if (ssl) { #ifndef WIN32 if ((r = ldap_start_tls_s(ldap, NULL, NULL)) != LDAP_SUCCESS) --- 1979,1985 ---- return STATUS_ERROR; } ! if (port->hba->ldaptls) { #ifndef WIN32 if ((r = ldap_start_tls_s(ldap, NULL, NULL)) != LDAP_SUCCESS) *************** *** 2155,2161 **** CheckLDAPAuth(Port *port) } snprintf(fulluser, sizeof(fulluser), "%s%s%s", ! prefix, port->user_name, suffix); fulluser[sizeof(fulluser) - 1] = '\0'; r = ldap_simple_bind_s(ldap, fulluser, passwd); --- 2034,2042 ---- } snprintf(fulluser, sizeof(fulluser), "%s%s%s", ! port->hba->ldapprefix?port->hba->ldapprefix:"", ! port->user_name, ! port->hba->ldapsuffix?port->hba->ldapsuffix:""); fulluser[sizeof(fulluser) - 1] = '\0'; r = ldap_simple_bind_s(ldap, fulluser, passwd); *************** *** 2165,2171 **** CheckLDAPAuth(Port *port) { ereport(LOG, (errmsg("LDAP login failed for user \"%s\" on server \"%s\": error code %d", ! fulluser, server, r))); return STATUS_ERROR; } --- 2046,2052 ---- { ereport(LOG, (errmsg("LDAP login failed for user \"%s\" on server \"%s\": error code %d", ! fulluser, port->hba->ldapserver, r))); return STATUS_ERROR; } *** a/src/backend/libpq/hba.c --- b/src/backend/libpq/hba.c *************** *** 565,570 **** check_db(const char *dbname, const char *role, char *param_str) --- 565,606 ---- /* + * Macros used to check and report on invalid configuration options. + * INVALID_AUTH_OPTION = reports when an option is specified for a method where it's + * not supported. + * REQUIRE_AUTH_OPTION = same as INVALID_AUTH_OPTION, except it also checks if the + * method is actually the one specified. Used as a shortcut when + * the option is only valid for one authentication method. + * MANDATORY_AUTH_ARG = check if a required option is set for an authentication method, + * reporting error if it's not. + */ + #define INVALID_AUTH_OPTION(optname, validmethods) do {\ + ereport(LOG, \ + (errcode(ERRCODE_CONFIG_FILE_ERROR), \ + errmsg("authentication option '%s' is only valid for authentication methods '%s'", \ + optname, validmethods), \ + errcontext("line %d of configuration file \"%s\"", \ + line_num, HbaFileName))); \ + goto hba_other_error; \ + } while (0) + + #define REQUIRE_AUTH_OPTION(methodval, optname, validmethods) \ + if (parsedline->auth_method != methodval) \ + INVALID_AUTH_OPTION("ldaptls", "ldap") + + #define MANDATORY_AUTH_ARG(argvar, argname, authname) \ + if (argvar == NULL) {\ + ereport(LOG, \ + (errcode(ERRCODE_CONFIG_FILE_ERROR), \ + errmsg("authentication method '%s' requires argument '%s' to be set", \ + authname, argname), \ + errcontext("line %d of configuration file \"%s\"", \ + line_num, HbaFileName))); \ + goto hba_other_error; \ + } while (0); + + + /* * Parse one line in the hba config file and store the result in * a HbaLine structure. */ *************** *** 801,838 **** parse_hba_line(List *line, int line_num, HbaLine *parsedline) goto hba_other_error; } ! /* Get the authentication argument token, if any */ ! line_item = lnext(line_item); ! if (line_item) { token = lfirst(line_item); - parsedline->auth_arg= pstrdup(token); - } ! /* ! * Backwards compatible format of ident authentication - support "naked" ident map ! * name, as well as "sameuser"/"samerole" ! */ ! if (parsedline->auth_method == uaIdent) ! { ! if (parsedline->auth_arg && strlen(parsedline->auth_arg)) { ! if (strcmp(parsedline->auth_arg, "sameuser\n") == 0 || ! strcmp(parsedline->auth_arg, "samerole\n") == 0) { ! /* This is now the default */ ! pfree(parsedline->auth_arg); ! parsedline->auth_arg = NULL; ! parsedline->usermap = NULL; } else { ! /* Specific ident map specified */ ! parsedline->usermap = parsedline->auth_arg; ! parsedline->auth_arg = NULL; } } } return true; --- 837,938 ---- goto hba_other_error; } ! /* Parse remaining arguments */ ! while ((line_item = lnext(line_item)) != NULL) { + char *c; + token = lfirst(line_item); ! c = strchr(token, '='); ! if (c == NULL) { ! /* ! * Got something that's not a name=value pair. ! * ! * XXX: attempt to do some backwards compatible parsing here? ! */ ! ereport(LOG, ! (errcode(ERRCODE_CONFIG_FILE_ERROR), ! errmsg("authentication option not in name=value format: %s", token), ! errcontext("line %d of configuration file \"%s\"", ! line_num, HbaFileName))); ! goto hba_other_error; ! } ! else ! { ! *c++ = '\0'; /* token now holds "name", c holds "value" */ ! if (strcmp(token, "map") == 0) ! { ! if (parsedline->auth_method != uaIdent && ! parsedline->auth_method != uaKrb5 && ! parsedline->auth_method != uaGSS && ! parsedline->auth_method != uaSSPI) ! INVALID_AUTH_OPTION("map", "ident, krb5, gssapi and sspi"); ! parsedline->usermap = pstrdup(c); ! } ! else if (strcmp(token, "pamservice") == 0) ! { ! REQUIRE_AUTH_OPTION(uaPAM, "pamservice", "pam"); ! parsedline->pamservice = pstrdup(c); ! } ! else if (strcmp(token, "ldaptls") == 0) ! { ! REQUIRE_AUTH_OPTION(uaLDAP, "ldaptls", "ldap"); ! if (strcmp(c, "1") == 0) ! parsedline->ldaptls = true; ! else ! parsedline->ldaptls = false; ! } ! else if (strcmp(token, "ldapserver") == 0) ! { ! REQUIRE_AUTH_OPTION(uaLDAP, "ldapserver", "ldap"); ! parsedline->ldapserver = pstrdup(c); ! } ! else if (strcmp(token, "ldapport") == 0) ! { ! REQUIRE_AUTH_OPTION(uaLDAP, "ldapport", "ldap"); ! parsedline->ldapport = atoi(c); ! if (parsedline->ldapport == 0) ! { ! ereport(LOG, ! (errcode(ERRCODE_CONFIG_FILE_ERROR), ! errmsg("invalid ldap port '%s'", c), ! errcontext("line %d of configuration file \"%s\"", ! line_num, HbaFileName))); ! goto hba_other_error; ! } ! } ! else if (strcmp(token, "ldapprefix") == 0) { ! REQUIRE_AUTH_OPTION(uaLDAP, "ldapprefix", "ldap"); ! parsedline->ldapprefix = pstrdup(c); ! } ! else if (strcmp(token, "ldapsuffix") == 0) ! { ! REQUIRE_AUTH_OPTION(uaLDAP, "ldapsuffix", "ldap"); ! parsedline->ldapsuffix = pstrdup(c); } else { ! ereport(LOG, ! (errcode(ERRCODE_CONFIG_FILE_ERROR), ! errmsg("unknown authentication option name '%s'", token), ! errcontext("line %d of configuration file \"%s\"", ! line_num, HbaFileName))); ! goto hba_other_error; } } } + + /* + * Check if the selected authentication method has any mandatory arguments that + * are not set. + */ + if (parsedline->auth_method == uaLDAP) + { + MANDATORY_AUTH_ARG(parsedline->ldapserver, "ldapserver", "ldap"); + } return true; *************** *** 1018,1025 **** free_hba_record(HbaLine *record) pfree(record->database); if (record->role) pfree(record->role); ! if (record->auth_arg) ! pfree(record->auth_arg); } /* --- 1118,1131 ---- pfree(record->database); if (record->role) pfree(record->role); ! if (record->pamservice) ! pfree(record->pamservice); ! if (record->ldapserver) ! pfree(record->ldapserver); ! if (record->ldapprefix) ! pfree(record->ldapprefix); ! if (record->ldapsuffix) ! pfree(record->ldapsuffix); } /* *************** *** 1150,1156 **** read_pg_database_line(FILE *fp, char *dbname, Oid *dboid, static void parse_ident_usermap(List *line, int line_number, const char *usermap_name, const char *pg_role, const char *ident_user, ! bool *found_p, bool *error_p) { ListCell *line_item; char *token; --- 1256,1262 ---- static void parse_ident_usermap(List *line, int line_number, const char *usermap_name, const char *pg_role, const char *ident_user, ! bool case_insensitive, bool *found_p, bool *error_p) { ListCell *line_item; char *token; *************** *** 1183,1192 **** parse_ident_usermap(List *line, int line_number, const char *usermap_name, file_pgrole = token; /* Match? */ ! if (strcmp(file_map, usermap_name) == 0 && ! strcmp(file_pgrole, pg_role) == 0 && ! strcmp(file_ident_user, ident_user) == 0) ! *found_p = true; return; --- 1289,1308 ---- file_pgrole = token; /* Match? */ ! if (case_insensitive) ! { ! if (strcmp(file_map, usermap_name) == 0 && ! pg_strcasecmp(file_pgrole, pg_role) == 0 && ! pg_strcasecmp(file_ident_user, ident_user) == 0) ! *found_p = true; ! } ! else ! { ! if (strcmp(file_map, usermap_name) == 0 && ! strcmp(file_pgrole, pg_role) == 0 && ! strcmp(file_ident_user, ident_user) == 0) ! *found_p = true; ! } return; *************** *** 1210,1231 **** ident_syntax: * file. That's an implied map where "pgrole" must be identical to * "ident_user" in order to be authorized. * ! * Iff authorized, return true. */ ! bool ! check_ident_usermap(const char *usermap_name, const char *pg_role, ! const char *ident_user) { bool found_entry = false, error = false; if (usermap_name == NULL || usermap_name[0] == '\0') { ! if (strcmp(pg_role, ident_user) == 0) ! found_entry = true; ! else ! found_entry = false; } else { --- 1326,1357 ---- * file. That's an implied map where "pgrole" must be identical to * "ident_user" in order to be authorized. * ! * Iff authorized, return STATUS_OK, otherwise return STATUS_ERROR. */ ! int ! check_usermap(const char *usermap_name, const char *pg_role, ! const char *auth_user, ! bool case_insensitive) { bool found_entry = false, error = false; if (usermap_name == NULL || usermap_name[0] == '\0') { ! if (case_insensitive) ! { ! if (pg_strcasecmp(pg_role, auth_user) == 0) ! return STATUS_OK; ! } ! else { ! if (strcmp(pg_role, auth_user) == 0) ! return STATUS_OK; ! } ! ereport(LOG, ! (errmsg("provided username (%s) and authenticated username (%s) don't match", ! auth_user, pg_role))); ! return STATUS_ERROR; } else { *************** *** 1235,1247 **** check_ident_usermap(const char *usermap_name, forboth(line_cell, ident_lines, num_cell, ident_line_nums) { parse_ident_usermap(lfirst(line_cell), lfirst_int(num_cell), ! usermap_name, pg_role, ident_user, &found_entry, &error); if (found_entry || error) break; } } ! return found_entry; } --- 1361,1380 ---- forboth(line_cell, ident_lines, num_cell, ident_line_nums) { parse_ident_usermap(lfirst(line_cell), lfirst_int(num_cell), ! usermap_name, pg_role, auth_user, case_insensitive, &found_entry, &error); if (found_entry || error) break; } } ! if (!found_entry && !error) ! { ! ereport(LOG, ! (errmsg("no match in usermap for user '%s' authenticated as '%s'", ! pg_role, auth_user), ! errcontext("usermap '%s'", usermap_name))); ! } ! return found_entry?STATUS_OK:STATUS_ERROR; } *** a/src/backend/libpq/pg_hba.conf.sample --- b/src/backend/libpq/pg_hba.conf.sample *************** *** 9,18 **** # are authenticated, which PostgreSQL user names they can use, which # databases they can access. Records take one of these forms: # ! # local DATABASE USER METHOD [OPTION] ! # host DATABASE USER CIDR-ADDRESS METHOD [OPTION] ! # hostssl DATABASE USER CIDR-ADDRESS METHOD [OPTION] ! # hostnossl DATABASE USER CIDR-ADDRESS METHOD [OPTION] # # (The uppercase items must be replaced by actual values.) # --- 9,18 ---- # are authenticated, which PostgreSQL user names they can use, which # databases they can access. Records take one of these forms: # ! # local DATABASE USER METHOD [OPTIONS] ! # host DATABASE USER CIDR-ADDRESS METHOD [OPTIONS] ! # hostssl DATABASE USER CIDR-ADDRESS METHOD [OPTIONS] ! # hostnossl DATABASE USER CIDR-ADDRESS METHOD [OPTIONS] # # (The uppercase items must be replaced by actual values.) # *************** *** 38,44 **** # "krb5", "ident", "pam" or "ldap". Note that "password" sends passwords # in clear text; "md5" is preferred since it sends encrypted passwords. # ! # OPTION is the ident map or the name of the PAM service, depending on METHOD. # # Database and user names containing spaces, commas, quotes and other special # characters must be quoted. Quoting one of the keywords "all", "sameuser" or --- 38,47 ---- # "krb5", "ident", "pam" or "ldap". Note that "password" sends passwords # in clear text; "md5" is preferred since it sends encrypted passwords. # ! # OPTIONS are a set of options for the authentication in the format ! # NAME=VALUE. The available options depend on the different authentication ! # methods - refer to the "Client Authentication" section in the documentation ! # for a list of which options are available for which authentication methods. # # Database and user names containing spaces, commas, quotes and other special # characters must be quoted. Quoting one of the keywords "all", "sameuser" or *** a/src/backend/libpq/pg_ident.conf.sample --- b/src/backend/libpq/pg_ident.conf.sample *************** *** 5,22 **** # Authentication" for a complete description. A short synopsis # follows. # ! # This file controls PostgreSQL ident-based authentication. It maps ! # ident user names (typically Unix user names) to their corresponding # PostgreSQL user names. Records are of the form: # ! # MAPNAME IDENT-USERNAME PG-USERNAME # # (The uppercase quantities must be replaced by actual values.) # # MAPNAME is the (otherwise freely chosen) map name that was used in ! # pg_hba.conf. IDENT-USERNAME is the detected user name of the # client. PG-USERNAME is the requested PostgreSQL user name. The ! # existence of a record specifies that IDENT-USERNAME may connect as # PG-USERNAME. Multiple maps may be specified in this file and used # by pg_hba.conf. # --- 5,22 ---- # Authentication" for a complete description. A short synopsis # follows. # ! # This file controls PostgreSQL username mapping. It maps ! # external user names to their corresponding # PostgreSQL user names. Records are of the form: # ! # MAPNAME SYSTEM-USERNAME PG-USERNAME # # (The uppercase quantities must be replaced by actual values.) # # MAPNAME is the (otherwise freely chosen) map name that was used in ! # pg_hba.conf. SYSTEM-USERNAME is the detected user name of the # client. PG-USERNAME is the requested PostgreSQL user name. The ! # existence of a record specifies that SYSTEM-USERNAME may connect as # PG-USERNAME. Multiple maps may be specified in this file and used # by pg_hba.conf. # *************** *** 28,35 **** # Put your actual configuration here # ---------------------------------- # ! # No map names are defined in the default configuration. If all ident # user names and PostgreSQL user names are the same, you don't need # this file. ! # MAPNAME IDENT-USERNAME PG-USERNAME --- 28,35 ---- # Put your actual configuration here # ---------------------------------- # ! # No map names are defined in the default configuration. If all system # user names and PostgreSQL user names are the same, you don't need # this file. ! # MAPNAME SYSTEM-USERNAME PG-USERNAME *** a/src/include/libpq/hba.h --- b/src/include/libpq/hba.h *************** *** 25,37 **** typedef enum UserAuth uaCrypt, uaMD5, uaGSS, ! uaSSPI ! #ifdef USE_PAM ! ,uaPAM ! #endif /* USE_PAM */ ! #ifdef USE_LDAP ! ,uaLDAP ! #endif } UserAuth; typedef enum ConnType --- 25,33 ---- uaCrypt, uaMD5, uaGSS, ! uaSSPI, ! uaPAM, ! uaLDAP } UserAuth; typedef enum ConnType *************** *** 51,58 **** typedef struct struct sockaddr_storage addr; struct sockaddr_storage mask; UserAuth auth_method; char *usermap; ! char *auth_arg; } HbaLine; typedef struct Port hbaPort; --- 47,60 ---- struct sockaddr_storage addr; struct sockaddr_storage mask; UserAuth auth_method; + char *usermap; ! char *pamservice; ! bool ldaptls; ! char *ldapserver; ! int ldapport; ! char *ldapprefix; ! char *ldapsuffix; } HbaLine; typedef struct Port hbaPort; *************** *** 64,71 **** extern void load_role(void); extern int hba_getauthmethod(hbaPort *port); extern bool read_pg_database_line(FILE *fp, char *dbname, Oid *dboid, Oid *dbtablespace, TransactionId *dbfrozenxid); ! extern bool check_ident_usermap(const char *usermap_name, ! const char *pg_role, const char *ident_user); extern bool pg_isblank(const char c); #endif /* HBA_H */ --- 66,74 ---- extern int hba_getauthmethod(hbaPort *port); extern bool read_pg_database_line(FILE *fp, char *dbname, Oid *dboid, Oid *dbtablespace, TransactionId *dbfrozenxid); ! extern int check_usermap(const char *usermap_name, ! const char *pg_role, const char *auth_user, ! bool case_sensitive); extern bool pg_isblank(const char c); #endif /* HBA_H */
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers