pg_hba.conf improvements - Mailing list pgsql-patches

From Bruce Momjian
Subject pg_hba.conf improvements
Date
Msg-id 200204030437.g334b2n05993@candle.pha.pa.us
Whole thread Raw
List pgsql-patches
This patch implements a variety of authorization improvements:

    A new pg_hba.conf column, USER
    Allow specifiction of lists of users separated by commas
    Allow group names specified by +
    Allow include files containing lists of users specified by @
    Allow lists of databases, and database files
    Allow samegroup in database column to match group name matching dbname
    Removal of secondary password files
    Remove pg_passwd utility
    Lots of code cleanup in user.c and hba.c
    New data/global/pg_pwd format
    New data/global/pg_group file

--
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 853-3000
  +  If your life is a hard drive,     |  830 Blythe Avenue
  +  Christ can be your backup.        |  Drexel Hill, Pennsylvania 19026
Index: doc/src/sgml/client-auth.sgml
===================================================================
RCS file: /cvsroot/pgsql/doc/src/sgml/client-auth.sgml,v
retrieving revision 1.33
diff -c -r1.33 client-auth.sgml
*** doc/src/sgml/client-auth.sgml    22 Mar 2002 19:20:06 -0000    1.33
--- doc/src/sgml/client-auth.sgml    3 Apr 2002 03:48:41 -0000
***************
*** 10,23 ****
   </indexterm>

   <para>
!   When a client application connects to the database server, it specifies which
!   <productname>PostgreSQL</productname> user name it wants to connect as,
!   much the same way one logs into a Unix computer as a particular user.
!   Within the SQL environment the active
!   database user name determines access privileges to database
!   objects -- see <xref linkend="user-manag"> for more information
!   about that. It is therefore obviously essential to restrict which
!   database user name(s) a given client can connect as.
   </para>

   <para>
--- 10,22 ----
   </indexterm>

   <para>
!   When a client application connects to the database server, it
!   specifies which <productname>PostgreSQL</productname> user name it
!   wants to connect as, much the same way one logs into a Unix computer
!   as a particular user. Within the SQL environment the active database
!   user name determines access privileges to database objects -- see
!   <xref linkend="user-manag"> for more information. Therefore, it is
!   essential to restrict which database users can connect.
   </para>

   <para>
***************
*** 30,49 ****

   <para>
    <productname>PostgreSQL</productname> offers a number of different
!   client authentication methods.  The method to be used can be selected
!   on the basis of (client) host and database; some authentication methods
!   allow you to restrict by user name as well.
   </para>

   <para>
!   <productname>PostgreSQL</productname> database user names are logically
    separate from user names of the operating system in which the server
!   runs.  If all the users of a particular server also have accounts on
    the server's machine, it makes sense to assign database user names
!   that match their operating system user names.  However, a server that accepts remote
!   connections may have many users who have no local account, and in such
!   cases there need be no connection between database user names and OS
!   user names.
   </para>

   <sect1 id="pg-hba-conf">
--- 29,47 ----

   <para>
    <productname>PostgreSQL</productname> offers a number of different
!   client authentication methods. The method to be used can be selected
!   on the basis of (client) host, database, and user.
   </para>

   <para>
!   <productname>PostgreSQL</productname> user names are logically
    separate from user names of the operating system in which the server
!   runs. If all the users of a particular server also have accounts on
    the server's machine, it makes sense to assign database user names
!   that match their operating system user names. However, a server that
!   accepts remote connections may have many users who have no local
!   account, and in such cases there need be no connection between
!   database user names and OS user names.
   </para>

   <sect1 id="pg-hba-conf">
***************
*** 56,94 ****
    <para>
     Client authentication is controlled by the file
     <filename>pg_hba.conf</filename> in the data directory, e.g.,
!    <filename>/usr/local/pgsql/data/pg_hba.conf</filename>. (<acronym>HBA</> stands
!    for host-based authentication.) A default <filename>pg_hba.conf</filename>
!    file is installed when the
!    data area is initialized by <command>initdb</command>.
    </para>

    <para>
!    The general format of the <filename>pg_hba.conf</filename> file is
!    of a set of records, one per line. Blank lines and lines beginning
!    with a hash character (<quote>#</quote>) are ignored. A record is
!    made up of a number of fields which are separated by spaces and/or
!    tabs.  Records cannot be continued across lines.
    </para>

    <para>
     Each record specifies a connection type, a client IP address range
!    (if relevant for the connection type), a database name or names,
     and the authentication method to be used for connections matching
!    these parameters.
!    The first record that matches the type, client address, and requested
!    database name of a connection attempt is used to do the
!    authentication step.  There is no <quote>fall-through</> or
     <quote>backup</>: if one record is chosen and the authentication
!    fails, the following records are not considered. If no record
!    matches, the access will be denied.
    </para>

    <para>
     A record may have one of the three formats
     <synopsis>
! local   <replaceable>database</replaceable> <replaceable>authentication-method</replaceable> [
<replaceable>authentication-option</replaceable>] 
! host    <replaceable>database</replaceable> <replaceable>IP-address</replaceable> <replaceable>IP-mask</replaceable>
<replaceable>authentication-method</replaceable>[ <replaceable>authentication-option</replaceable> ] 
! hostssl <replaceable>database</replaceable> <replaceable>IP-address</replaceable> <replaceable>IP-mask</replaceable>
<replaceable>authentication-method</replaceable>[ <replaceable>authentication-option</replaceable> ] 
      </synopsis>
     The meaning of the fields is as follows:

--- 54,92 ----
    <para>
     Client authentication is controlled by the file
     <filename>pg_hba.conf</filename> in the data directory, e.g.,
!    <filename>/usr/local/pgsql/data/pg_hba.conf</filename>.
!    (<acronym>HBA</> stands for host-based authentication.) A default
!    <filename>pg_hba.conf</filename> file is installed when the data area
!    is initialized by <command>initdb</command>.
    </para>

    <para>
!    The general format of the <filename>pg_hba.conf</filename> file is of
!    a set of records, one per line. Blank lines are ignored, as is any
!    text after the <quote>#</quote> comment character. A record is made
!    up of a number of fields which are separated by spaces and/or tabs.
!    Fields can contain white space if the field value is quoted. Records
!    cannot be continued across lines.
    </para>

    <para>
     Each record specifies a connection type, a client IP address range
!    (if relevant for the connection type), a database name, a user name,
     and the authentication method to be used for connections matching
!    these parameters. The first record with a matching connection type,
!    client address, requested database, and user name is used to perform
!    authentication. There is no <quote>fall-through</> or
     <quote>backup</>: if one record is chosen and the authentication
!    fails, subsequent records are not considered. If no record matches,
!    access is denied.
    </para>

    <para>
     A record may have one of the three formats
     <synopsis>
! local   <replaceable>database</replaceable> <replaceable>user</replaceable>
<replaceable>authentication-method</replaceable>[ <replaceable>authentication-option</replaceable> ] 
! host    <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>IP-address</replaceable>
<replaceable>IP-mask</replaceable><replaceable>authentication-method</replaceable> 
! hostssl <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>IP-address</replaceable>
<replaceable>IP-mask</replaceable><replaceable>authentication-method</replaceable> 
      </synopsis>
     The meaning of the fields is as follows:

***************
*** 97,103 ****
       <term><literal>local</literal></term>
       <listitem>
        <para>
!        This record pertains to connection attempts over Unix domain
         sockets.
        </para>
       </listitem>
--- 95,101 ----
       <term><literal>local</literal></term>
       <listitem>
        <para>
!        This record applies to connection attempts using Unix domain
         sockets.
        </para>
       </listitem>
***************
*** 107,116 ****
       <term><literal>host</literal></term>
       <listitem>
        <para>
!        This record pertains to connection attempts over TCP/IP
!        networks. Note that TCP/IP connections are completely disabled
!        unless the server is started with the <option>-i</option> switch or
!        the equivalent configuration parameter is set.
        </para>
       </listitem>
      </varlistentry>
--- 105,115 ----
       <term><literal>host</literal></term>
       <listitem>
        <para>
!        This record applied to connection attempts using TCP/IP networks.
!        Note that TCP/IP connections are disabled unless the server is
!        started with the <option>-i</option> option or the
!        <literal>tcpip_socket</> <filename>postgresql.conf</>
!        configuration parameter is enabled.
        </para>
       </listitem>
      </varlistentry>
***************
*** 119,131 ****
       <term><literal>hostssl</literal></term>
       <listitem>
        <para>
!        This record pertains to connection attempts with SSL over
         TCP/IP. To make use of this option the server must be
         built with SSL support enabled. Furthermore, SSL must be
         enabled with the <option>-l</> option or equivalent configuration
         setting when the server is started.  (Note: <literal>host</literal>
         records will match either SSL or non-SSL connection attempts, but
!        <literal>hostssl</literal> records match only SSL connections.)
        </para>
       </listitem>
      </varlistentry>
--- 118,130 ----
       <term><literal>hostssl</literal></term>
       <listitem>
        <para>
!        This record applies to connection attempts using SSL over
         TCP/IP. To make use of this option the server must be
         built with SSL support enabled. Furthermore, SSL must be
         enabled with the <option>-l</> option or equivalent configuration
         setting when the server is started.  (Note: <literal>host</literal>
         records will match either SSL or non-SSL connection attempts, but
!        <literal>hostssl</literal> records requires SSL connections.)
        </para>
       </listitem>
      </varlistentry>
***************
*** 134,145 ****
       <term><replaceable>database</replaceable></term>
       <listitem>
        <para>
!        Specifies the database that this record applies to. The value
         <literal>all</literal> specifies that it applies to all
         databases, while the value <literal>sameuser</> identifies the
!        database with the same name as the connecting user.  Otherwise,
!        this is the name of a specific <productname>PostgreSQL</productname>
!        database.
        </para>
       </listitem>
      </varlistentry>
--- 133,167 ----
       <term><replaceable>database</replaceable></term>
       <listitem>
        <para>
!        Specifies the database for this record. The value
         <literal>all</literal> specifies that it applies to all
         databases, while the value <literal>sameuser</> identifies the
!        database with the same name as the connecting user. The value
!        <literal>samegroup</> identifies a group with the same name as
!        the database name. Only members of this group can connect to the
!        database. Otherwise, this is the name of a specific
!        <productname>PostgreSQL</productname> database. Multiple database
!        names can be supplied by separating them with commas. A file
!        containing database names can be specified by preceding the file
!        name with <literal>@</>. The file must be in the same directory
!        as <filename>pg_hba.conf</>.
!       </para>
!      </listitem>
!     </varlistentry>
!
!     <varlistentry>
!      <term><replaceable>user</replaceable></term>
!      <listitem>
!       <para>
!        Specifies the user for this record. The value
!        <literal>all</literal> specifies that it applies to all users.
!        Otherwise, this is the name of a specific
!        <productname>PostgreSQL</productname> user. Multiple user names
!        can be supplied by separating them with commas. Group names can
!        be specified by preceding the group name with <literal>+</>. A
!        file containing user names can be specified by preceding the file
!        name with <literal>@</>. The file must be in the same directory
!        as <filename>pg_hba.conf</>.
        </para>
       </listitem>
      </varlistentry>
***************
*** 149,158 ****
       <term><replaceable>IP mask</replaceable></term>
       <listitem>
        <para>
!        These two fields specify to which client machines a
!        <literal>host</literal> or <literal>hostssl</literal>
!        record applies, based on their IP
!        address. (Of course IP addresses can be spoofed but this
         consideration is beyond the scope of
         <productname>PostgreSQL</productname>.) The precise logic is that
         <blockquote>
--- 171,179 ----
       <term><replaceable>IP mask</replaceable></term>
       <listitem>
        <para>
!        These two fields specify the client machine IP addresses
!        (<literal>host</literal> or <literal>hostssl</literal>) for this
!        record. (Of course IP addresses can be spoofed but this
         consideration is beyond the scope of
         <productname>PostgreSQL</productname>.) The precise logic is that
         <blockquote>
***************
*** 169,178 ****
       <term><replaceable>authentication method</replaceable></term>
       <listitem>
        <para>
!        Specifies the method that users must use to authenticate themselves
!        when connecting under the control of this authentication record.
!        The possible choices are summarized here,
!        details are in <xref linkend="auth-methods">.

         <variablelist>
          <varlistentry>
--- 190,198 ----
       <term><replaceable>authentication method</replaceable></term>
       <listitem>
        <para>
!        Specifies the authentication method to use when connecting via
!        this record. The possible choices are summarized here; details
!        are in <xref linkend="auth-methods">.

         <variablelist>
          <varlistentry>
***************
*** 190,255 ****
          <term><literal>reject</></term>
          <listitem>
           <para>
!           The connection is rejected unconditionally. This is mostly
!           useful to <quote>filter out</> certain hosts from a group.
           </para>
          </listitem>
         </varlistentry>

         <varlistentry>
!         <term><literal>password</></term>
          <listitem>
           <para>
!           The client is required to supply a password which is required to
!       match the database password that was set up for the user.
!          </para>
!
!          <para>
!           An optional file name may be specified after the
!           <literal>password</literal> keyword. This file is expected to
!           contain a list of users who may connect using this record,
!           and optionally alternative passwords for them.
!          </para>
!
!          <para>
!           The password is sent over the wire in clear text. For better
!           protection, use the <literal>md5</literal> or
!           <literal>crypt</literal> methods.
           </para>
          </listitem>
         </varlistentry>

         <varlistentry>
!         <term><literal>md5</></term>
          <listitem>
           <para>
!           Like the <literal>password</literal> method, but the password
!           is sent over the wire encrypted using a simple
!           challenge-response protocol. This protects against incidental
!           wire-sniffing.  This is now the recommended choice for
!       password-based authentication.
!          </para>
!
!          <para>
!       The name of a file may follow the
!           <literal>md5</literal> keyword.  It contains a list of users
!           who may connect using this record.
           </para>
          </listitem>
         </varlistentry>

         <varlistentry>
!         <term><literal>crypt</></term>
          <listitem>
           <para>
!           Like the <literal>md5</literal> method but uses older crypt
!           encryption, which is needed for pre-7.2
!       clients. <literal>md5</literal> is
!           preferred for 7.2 and later clients. The <literal>crypt</>
!           method is not compatible with encrypting passwords in
!           <filename>pg_shadow</>, and may fail if client and server
!           machines have different implementations of the crypt() library
!           routine.
           </para>
          </listitem>
         </varlistentry>
--- 210,250 ----
          <term><literal>reject</></term>
          <listitem>
           <para>
!           The connection is rejected unconditionally. This is useful for
!           <quote>filtering out</> certain hosts from a group.
           </para>
          </listitem>
         </varlistentry>

         <varlistentry>
!         <term><literal>md5</></term>
          <listitem>
           <para>
!           Requires the client to supply an MD5 encrypted password for
!           authentication. This is the only method that allows encrypted
!           passwords to be stored in pg_shadow.
           </para>
          </listitem>
         </varlistentry>

         <varlistentry>
!         <term><literal>crypt</></term>
          <listitem>
           <para>
!           Like <literal>md5</literal> method but uses older crypt
!           encryption, which is needed for pre-7.2 clients.
!           <literal>md5</literal> is preferred for 7.2 and later clients.
           </para>
          </listitem>
         </varlistentry>

         <varlistentry>
!         <term><literal>password</></term>
          <listitem>
           <para>
!           Same as "md5", but the password is sent in cleartext over the
!           network. This should not be used on untrusted networks.
!          </para>
           </para>
          </listitem>
         </varlistentry>
***************
*** 278,311 ****
          <term><literal>ident</></term>
          <listitem>
       <para>
!       The identity of the user as determined on login to the
!       operating system is used by <productname>PostgreSQL</productname>
!       to determine whether the user
!           is allowed to connect as the requested database user.
!       For TCP/IP connections the user's identity is determined by
!       contacting the <firstterm>ident</firstterm> server on the client
!       host.  (Note that this is only as reliable as the remote ident
!       server; ident authentication should never be used for remote hosts
!       whose administrators are not trustworthy.)
!       On operating systems
!       supporting <symbol>SO_PEERCRED</> requests for Unix domain sockets,
!       ident authentication is possible for local connections;
!       the system is then asked for the connecting user's identity.
       </para>
           <para>
!       On systems without <symbol>SO_PEERCRED</> requests, ident authentication
!       is only available for TCP/IP connections.  As a workaround,
!       it is possible to
!       specify the <systemitem class="systemname">localhost</> address
!           <systemitem class="systemname">127.0.0.1</> and make connections
!       to this address.
!      </para>
!          <para>
!           The <replaceable>authentication option</replaceable> following
!           the <literal>ident</> keyword specifies the name of an
!           <firstterm>ident map</firstterm> that specifies which operating
!           system users equate with which database users. See below for
!           details.
           </para>
          </listitem>
         </varlistentry>
--- 273,308 ----
          <term><literal>ident</></term>
          <listitem>
       <para>
!           For TCP/IP connections, authentication is done by contacting
!           the <firstterm>ident</firstterm> server on the client host.
!           This is only as secure as the client machine. You must specify
!           the map name after the 'ident' keyword. It determines how to
!           map remote user names to PostgreSQL user names. If you use
!           "sameuser", the user names are assumed to be identical. If
!           not, the map name is looked up in the $PGDATA/pg_ident.conf
!           file. The connection is accepted if that file contains an
!           entry for this map name with the ident-supplied user name and
!           the requested PostgreSQL user name.
!          </para>
!          <para>
!           On machines that support unix-domain socket credentials
!           (currently Linux, FreeBSD, NetBSD, and BSD/OS), ident allows
!           reliable authentication of 'local' connections without ident
!           running on the local machine.
!          </para>
!          <para>
!       On systems without <symbol>SO_PEERCRED</> requests, ident
!       authentication is only available for TCP/IP connections. As a
!       work around, it is possible to specify the <systemitem
!       class="systemname">localhost</> address <systemitem
!       class="systemname">127.0.0.1</> and make connections to this
!       address.
       </para>
           <para>
!           Following the <literal>ident</> keyword, an <firstterm>ident
!           map</firstterm> name should be supplied which specifies which
!           operating system users equate with which database users. See
!           below for details.
           </para>
          </listitem>
         </varlistentry>
***************
*** 315,331 ****
          <listitem>
           <para>
            This authentication type operates similarly to
!           <firstterm>password</firstterm>, with the main difference that
!           it will use PAM (Pluggable Authentication Modules) as the
!           authentication mechanism. The <replaceable>authentication
!           option</replaceable> following the <literal>pam</> keyword
!           specifies the service name that will be passed to PAM. The
!           default service name is <literal>postgresql</literal>.
!           For more information about PAM, please read the <ulink
!           url="http://www.kernel.org/pub/linux/libs/pam/"><productname>Linux-PAM</productname>
!           Page</ulink> and/or the <ulink
!           url="http://www.sun.com/software/solaris/pam/"><systemitem class="osname">Solaris</> PAM
!           Page</ulink>.
           </para>
          </listitem>
         </varlistentry>
--- 312,327 ----
          <listitem>
           <para>
            This authentication type operates similarly to
!           <firstterm>password</firstterm> 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 you
!           own service name after the <literal>pam</> keyword in the
!           file. For more information about PAM, please read the <ulink
!           url="http://www.kernel.org/pub/linux/libs/pam/"><productname>L
!           inux-PAM</productname> Page</ulink> and the <ulink
!           url="http://www.sun.com/software/solaris/pam/"><systemitem
!           class="osname">Solaris</> PAM Page</ulink>.
           </para>
          </listitem>
         </varlistentry>
***************
*** 336,377 ****
       </listitem>
      </varlistentry>

-     <varlistentry>
-      <term><replaceable>authentication option</replaceable></term>
-      <listitem>
-       <para>
-        This field is interpreted differently depending on the
-        authentication method, as described above.
-       </para>
-      </listitem>
-     </varlistentry>
     </variablelist>
    </para>

    <para>
     Since the <filename>pg_hba.conf</filename> records are examined
     sequentially for each connection attempt, the order of the records is
!    very significant.  Typically, earlier records will have tight
!    connection match parameters and weaker authentication methods,
!    while later records will have looser match parameters and stronger
!    authentication methods.  For example, one might wish to use
!    <literal>trust</> authentication for local TCP connections but
!    require a password for remote TCP connections.  In this case a
!    record specifying <literal>trust</> authentication for connections
!    from 127.0.0.1 would appear before a record specifying password
!    authentication for a wider range of allowed client IP addresses.
    </para>

    <para>
      <indexterm>
       <primary>SIGHUP</primary>
      </indexterm>
!    The <filename>pg_hba.conf</filename> file is read on start-up
!    and when the <application>postmaster</> receives a
     <systemitem>SIGHUP</systemitem> signal. If you edit the file on an
     active system, you will need to signal the <application>postmaster</>
!    (using <literal>pg_ctl reload</> or <literal>kill -HUP</>)
!    to make it re-read the file.
    </para>

    <para>
--- 332,364 ----
       </listitem>
      </varlistentry>

     </variablelist>
    </para>

    <para>
     Since the <filename>pg_hba.conf</filename> records are examined
     sequentially for each connection attempt, the order of the records is
!    significant. Typically, earlier records will have tight connection
!    match parameters and weaker authentication methods, while later
!    records will have looser match parameters and stronger authentication
!    methods. For example, one might wish to use <literal>trust</>
!    authentication for local TCP connections but require a password for
!    remote TCP connections. In this case a record specifying
!    <literal>trust</> authentication for connections from 127.0.0.1 would
!    appear before a record specifying password authentication for a wider
!    range of allowed client IP addresses.
    </para>

    <para>
      <indexterm>
       <primary>SIGHUP</primary>
      </indexterm>
!    The <filename>pg_hba.conf</filename> file is read on start-up and when
!    the <application>postmaster</> receives a
     <systemitem>SIGHUP</systemitem> signal. If you edit the file on an
     active system, you will need to signal the <application>postmaster</>
!    (using <literal>pg_ctl reload</> or <literal>kill -HUP</>) to make it
!    re-read the file.
    </para>

    <para>
***************
*** 382,408 ****
     <example id="example-pg-hba.conf">
      <title>An example <filename>pg_hba.conf</filename> file</title>
  <programlisting>
! # TYPE       DATABASE    IP_ADDRESS    MASK               AUTHTYPE  MAP

  # Allow any user on the local system to connect to any
! # database under any username, but only via an IP connection:

! host         all         127.0.0.1     255.255.255.255    trust

  # The same, over Unix-socket connections:

! local        all                                          trust

  # Allow any user from any host with IP address 192.168.93.x to
! # connect to database "template1" as the same username that ident on that
! # host identifies him as (typically his Unix username):

! host         template1   192.168.93.0  255.255.255.0      ident     sameuser

  # Allow a user from host 192.168.12.10 to connect to database "template1"
! # if the user's password in pg_shadow is correctly supplied:

! host         template1   192.168.12.10 255.255.255.255    md5

  # In the absence of preceding "host" lines, these two lines will reject
  # all connection attempts from 192.168.54.1 (since that entry will be
--- 369,395 ----
     <example id="example-pg-hba.conf">
      <title>An example <filename>pg_hba.conf</filename> file</title>
  <programlisting>
! # TYPE       DATABASE    USER       IP_ADDRESS    MASK               AUTHTYPE

  # Allow any user on the local system to connect to any
! # database under any user name, but only via an IP connection:

! host         all         all        127.0.0.1     255.255.255.255    trust

  # The same, over Unix-socket connections:

! local        all         all                                         trust

  # Allow any user from any host with IP address 192.168.93.x to
! # connect to database "template1" as the same user name that ident on that
! # host identifies him as (typically his Unix user name):

! host         template1   all        192.168.93.0  255.255.255.0      ident sameuser

  # Allow a user from host 192.168.12.10 to connect to database "template1"
! # if the user's password is correctly supplied:

! host         template1   all        192.168.12.10 255.255.255.255    md5

  # In the absence of preceding "host" lines, these two lines will reject
  # all connection attempts from 192.168.54.1 (since that entry will be
***************
*** 410,417 ****
  # else on the Internet. The zero mask means that no bits of the host IP
  # address are considered, so it matches any host:

! host         all        192.168.54.1   255.255.255.255    reject
! host         all        0.0.0.0        0.0.0.0            krb5

  # Allow users from 192.168.x.x hosts to connect to any database, if they
  # pass the ident check.  If, for example, ident says the user is "bryanh"
--- 397,404 ----
  # else on the Internet. The zero mask means that no bits of the host IP
  # address are considered, so it matches any host:

! host         all        all         192.168.54.1   255.255.255.255    reject
! host         all        all         0.0.0.0        0.0.0.0            krb5

  # Allow users from 192.168.x.x hosts to connect to any database, if they
  # pass the ident check.  If, for example, ident says the user is "bryanh"
***************
*** 419,425 ****
  # is allowed if there is an entry in pg_ident.conf for map "omicron" that
  # says "bryanh" is allowed to connect as "guest1":

! host         all        192.168.0.0    255.255.0.0        ident     omicron

  # If these are the only two lines for local connections, they will allow
  # local users to connect only to their own databases (database named the
--- 406,412 ----
  # is allowed if there is an entry in pg_ident.conf for map "omicron" that
  # says "bryanh" is allowed to connect as "guest1":

! host         all        all         192.168.0.0    255.255.0.0        ident omicron

  # If these are the only two lines for local connections, they will allow
  # local users to connect only to their own databases (database named the
***************
*** 429,436 ****
  # cases.  (If you prefer to use ident authorization, an ident map can
  # serve a parallel purpose to the password list file used here.)

! local        sameuser                                     md5
! local        all                                          md5  admins
  </programlisting>
     </example>
    </para>
--- 416,423 ----
  # cases.  (If you prefer to use ident authorization, an ident map can
  # serve a parallel purpose to the password list file used here.)

! local        sameuser   all                                            md5
! local        all        @admins                                        md5
  </programlisting>
     </example>
    </para>
***************
*** 490,575 ****
     <title>Password authentication</title>

     <indexterm>
!     <primary>password</primary>
     </indexterm>
     <indexterm>
!     <primary>MD5</>
     </indexterm>

     <para>
      Password-based authentication methods include <literal>md5</>,
!     <literal>crypt</>, and <literal>password</>.  These methods operate
      similarly except for the way that the password is sent across the
!     connection.  If you are at all concerned about password <quote>sniffing</>
!     attacks then <literal>md5</> is preferred, with <literal>crypt</> a
!     second choice if you must support obsolete clients.  Plain
!     <literal>password</> should especially be avoided for connections over
!     the open Internet (unless you use SSL, SSH, or other communications
!     security wrappers around the connection).
     </para>

     <para>
!     <productname>PostgreSQL</productname> database passwords are separate from
!     operating system user passwords. Ordinarily, the password for each
!     database user is stored in the pg_shadow system catalog table.
!     Passwords can be managed with the query language commands
!     <command>CREATE USER</command> and <command>ALTER USER</command>,
!     e.g., <userinput>CREATE USER foo WITH PASSWORD
!     'secret';</userinput>. By default, that is, if no password has
!     been set up, the stored password is <literal>NULL</literal>
!     and password authentication will always fail for that user.
     </para>

     <para>
      To restrict the set of users that are allowed to connect to certain
!     databases, list the set of users in a separate file (one user name
!     per line) in the same directory that <filename>pg_hba.conf</> is in,
!     and mention the (base) name of the file after the
!     <literal>password</>, <literal>md5</>, or <literal>crypt</> keyword,
!     respectively, in <filename>pg_hba.conf</>. If you do not use this
!     feature, then any user that is known to the database system can
!     connect to any database (so long as he supplies the correct password,
!     of course).
!    </para>
!
!    <para>
!     These files can also be used to apply a different set of passwords
!     to a particular database or set thereof. In that case, the files
!     have a format similar to the standard Unix password file
!     <filename>/etc/passwd</filename>, that is,
! <synopsis>
! <replaceable>username</replaceable>:<replaceable>password</replaceable>
! </synopsis>
!     Any extra colon-separated fields following the password are
!     ignored. The password is expected to be encrypted using the
!     system's <function>crypt()</function> function. The utility
!     program <application>pg_passwd</application> that is installed
!     with <productname>PostgreSQL</productname> can be used to manage
!     these password files.
!    </para>
!
!    <para>
!     Lines with and without passwords can be mixed in secondary
!     password files. Lines without password indicate use of the main
!     password in <literal>pg_shadow</> that is managed by
!     <command>CREATE USER</> and <command>ALTER USER</>. Lines with
!     passwords will cause that password to be used. A password entry of
!     <quote>+</quote> also means using the pg_shadow password.
!    </para>
!
!    <para>
!     Alternative passwords cannot be used when using the <literal>md5</>
!     or <literal>crypt</> methods. The file will be read as
!     usual, but the password field will simply be ignored and the
!     <literal>pg_shadow</> password will always be used.
!    </para>
!
!    <para>
!     Note that using alternative passwords like this means that one can
!     no longer use <command>ALTER USER</command> to change one's
!     password. It will appear to work but the password one is
!     changing is not the password that the system will end up
!     using.
     </para>

    </sect2>
--- 477,525 ----
     <title>Password authentication</title>

     <indexterm>
!     <primary>MD5</>
     </indexterm>
     <indexterm>
!     <primary>crypt</>
!    </indexterm>
!    <indexterm>
!     <primary>password</primary>
     </indexterm>

     <para>
      Password-based authentication methods include <literal>md5</>,
!     <literal>crypt</>, and <literal>password</>. These methods operate
      similarly except for the way that the password is sent across the
!     connection. If you are at all concerned about password
!     <quote>sniffing</> attacks then <literal>md5</> is preferred, with
!     <literal>crypt</> a second choice if you must support pre-7.2
!     clients. Plain <literal>password</> should especially be avoided for
!     connections over the open Internet (unless you use SSL, SSH, or
!     other communications security wrappers around the connection).
     </para>

     <para>
!     <productname>PostgreSQL</productname> database passwords are
!     separate from operating system user passwords. Ordinarily, the
!     password for each database user is stored in the pg_shadow system
!     catalog table. Passwords can be managed with the query language
!     commands <command>CREATE USER</command> and <command>ALTER
!     USER</command>, e.g., <userinput>CREATE USER foo WITH PASSWORD
!     'secret';</userinput>. By default, that is, if no password has been
!     set up, the stored password is <literal>NULL</literal> and password
!     authentication will always fail for that user.
     </para>

     <para>
      To restrict the set of users that are allowed to connect to certain
!     databases, list the users separated by commas, or in a separate
!     file. The file should contain user names separated by commas or one
!     user name per line, and be in the same directory as
!     <filename>pg_hba.conf</>. Mention the (base) name of the file
!     preceded with <literal>@</>in the <literal>USER</> column. The
!     <literal>DATABASE</> column can similarly accept a list of values or
!     a file name. You can also specify group names by preceding the group
!     name with <literal>+</>.
     </para>

    </sect2>
***************
*** 588,597 ****
      <productname>Kerberos</productname> system is far beyond the scope
      of this document; in all generality it can be quite complex (yet
      powerful). The <ulink
!     url="http://www.nrl.navy.mil/CCS/people/kenh/kerberos-faq.html">Kerberos
!     <acronym>FAQ</></ulink> or <ulink
!     url="ftp://athena-dist.mit.edu">MIT Project Athena</ulink> can be
!     a good starting point for exploration. Several sources for
      <productname>Kerberos</> distributions exist.
     </para>

--- 538,547 ----
      <productname>Kerberos</productname> system is far beyond the scope
      of this document; in all generality it can be quite complex (yet
      powerful). The <ulink
!     url="http://www.nrl.navy.mil/CCS/people/kenh/kerberos-faq.html">Kerb
!     eros <acronym>FAQ</></ulink> or <ulink
!     url="ftp://athena-dist.mit.edu">MIT Project Athena</ulink> can be a
!     good starting point for exploration. Several sources for
      <productname>Kerberos</> distributions exist.
     </para>

***************
*** 606,639 ****
     <para>
      <productname>PostgreSQL</> operates like a normal Kerberos service.
      The name of the service principal is
!     <replaceable>servicename/hostname@realm</>, where
!     <replaceable>servicename</> is <literal>postgres</literal>
!     (unless a different service name was selected at configure time
!     with <literal>./configure --with-krb-srvnam=whatever</>).
!     <replaceable>hostname</> is the fully qualified domain name of the server
!     machine.  The service principal's realm is the preferred realm of the
!     server machine.
     </para>

     <para>
!     Client principals must have their <productname>PostgreSQL</> user name as
!     their first component, for example
!     <replaceable>pgusername/otherstuff@realm</>.
!     At present the realm of the client is not checked by
!     <productname>PostgreSQL</>; so
!     if you have cross-realm authentication enabled, then any principal
!     in any realm that can communicate with yours will be accepted.
     </para>

     <para>
!     Make sure that your server key file is readable (and
!     preferably only readable) by the
!     <productname>PostgreSQL</productname> server account (see
!     <xref linkend="postgres-user">). The location of the key file
!     is specified with the <varname>krb_server_keyfile</> run time
!     configuration parameter. (See also <xref linkend="runtime-config">.)
!     The default is <filename>/etc/srvtab</> if you are using Kerberos 4
!     and <filename>FILE:/usr/local/pgsql/etc/krb5.keytab</> (or whichever
      directory was specified as <varname>sysconfdir</> at build time)
      with Kerberos 5.
     </para>
--- 556,588 ----
     <para>
      <productname>PostgreSQL</> operates like a normal Kerberos service.
      The name of the service principal is
!     <replaceable>servicename/hostname@realm</>, where
!     <replaceable>servicename</> is <literal>postgres</literal> (unless a
!     different service name was selected at configure time with
!     <literal>./configure --with-krb-srvnam=whatever</>).
!     <replaceable>hostname</> is the fully qualified domain name of the
!     server machine. The service principal's realm is the preferred realm
!     of the server machine.
     </para>

     <para>
!     Client principals must have their <productname>PostgreSQL</> user
!     name as their first component, for example
!     <replaceable>pgusername/otherstuff@realm</>. At present the realm of
!     the client is not checked by <productname>PostgreSQL</>; so if you
!     have cross-realm authentication enabled, then any principal in any
!     realm that can communicate with yours will be accepted.
     </para>

     <para>
!     Make sure that your server key file is readable (and preferably only
!     readable) by the <productname>PostgreSQL</productname> server
!     account (see <xref linkend="postgres-user">). The location of the
!     key file is specified with the <varname>krb_server_keyfile</> run
!     time configuration parameter. (See also <xref
!     linkend="runtime-config">.) The default is <filename>/etc/srvtab</>
!     if you are using Kerberos 4 and
!     <filename>FILE:/usr/local/pgsql/etc/krb5.keytab</> (or whichever
      directory was specified as <varname>sysconfdir</> at build time)
      with Kerberos 5.
     </para>
***************
*** 649,666 ****

     <para>
      When connecting to the database make sure you have a ticket for a
!     principal matching the requested database user name.
!     An example: For database user name <literal>fred</>, both principal
      <literal>fred@EXAMPLE.COM</> and
!     <literal>fred/users.example.com@EXAMPLE.COM</> can be
!     used to authenticate to the database server.
     </para>

     <para>
!     If you use <application>mod_auth_krb</application> and <application>mod_perl</application> on your
<productname>Apache</productname>web server, 
!     you can use <literal>AuthType KerberosV5SaveCredentials</literal> with a <application>mod_perl</application>
!     script. This gives secure database access over the web, no extra
!     passwords required.
     </para>

    </sect2>
--- 598,617 ----

     <para>
      When connecting to the database make sure you have a ticket for a
!     principal matching the requested database user name. An example: For
!     database user name <literal>fred</>, both principal
      <literal>fred@EXAMPLE.COM</> and
!     <literal>fred/users.example.com@EXAMPLE.COM</> can be used to
!     authenticate to the database server.
     </para>

     <para>
!     If you use <application>mod_auth_krb</application> and
!     <application>mod_perl</application> on your
!     <productname>Apache</productname> web server, you can use
!     <literal>AuthType KerberosV5SaveCredentials</literal> with a
!     <application>mod_perl</application> script. This gives secure
!     database access over the web, no extra passwords required.
     </para>

    </sect2>
***************
*** 707,761 ****
     </para>

     <para>
!     On systems supporting <symbol>SO_PEERCRED</symbol> requests for Unix-domain sockets,
!     ident authentication can also be applied to local connections.  In this
!     case, no security risk is added by using ident authentication; indeed
!     it is a preferable choice for local connections on such a system.
     </para>

     <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</> keyword in the
!     <filename>pg_hba.conf</filename> file. There is a predefined ident map
!     <literal>sameuser</literal>, which allows any operating system
!     user to connect as the database user of the same name (if the
!     latter exists). Other maps must be created manually.
     </para>

     <para>
!     <indexterm><primary>pg_ident.conf</primary></indexterm>
!     Ident maps other than <literal>sameuser</literal> are defined
!     in the file <filename>pg_ident.conf</filename>
!     in the data directory, which contains lines of the general form:
  <synopsis>
  <replaceable>map-name</> <replaceable>ident-username</> <replaceable>database-username</>
  </synopsis>
!     Comments and whitespace are handled in the usual way.
!     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 may correspond to and vice
!     versa.
     </para>

    <para>
      <indexterm>
       <primary>SIGHUP</primary>
      </indexterm>
!    The <filename>pg_ident.conf</filename> file is read on start-up
!    and when the <application>postmaster</> receives a
     <systemitem>SIGHUP</systemitem> signal. If you edit the file on an
     active system, you will need to signal the <application>postmaster</>
!    (using <literal>pg_ctl reload</> or <literal>kill -HUP</>)
!    to make it re-read the file.
    </para>

     <para>
--- 658,711 ----
     </para>

     <para>
!     On systems supporting <symbol>SO_PEERCRED</symbol> requests for
!     Unix-domain sockets, ident authentication can also be applied to
!     local connections. In this case, no security risk is added by using
!     ident authentication; indeed it is a preferable choice for local
!     connections on such systems.
     </para>

     <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</> keyword in the <filename>pg_hba.conf</filename>
!     file. There is a predefined ident map <literal>sameuser</literal>,
!     which allows any operating system user to connect as the database
!     user of the same name (if the latter exists). Other maps must be
!     created manually.
     </para>

     <para>
!     <indexterm><primary>pg_ident.conf</primary></indexterm> Ident maps
!     other than <literal>sameuser</literal> are defined in the file
!     <filename>pg_ident.conf</filename> in the data directory, which
!     contains lines of the general form:
  <synopsis>
  <replaceable>map-name</> <replaceable>ident-username</> <replaceable>database-username</>
  </synopsis>
!     Comments and whitespace are handled in the usual way. 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 may correspond to and vice versa.
     </para>

    <para>
      <indexterm>
       <primary>SIGHUP</primary>
      </indexterm>
!    The <filename>pg_ident.conf</filename> file is read on start-up and
!    when the <application>postmaster</> receives a
     <systemitem>SIGHUP</systemitem> signal. If you edit the file on an
     active system, you will need to signal the <application>postmaster</>
!    (using <literal>pg_ctl reload</> or <literal>kill -HUP</>) to make it
!    re-read the file.
    </para>

     <para>
***************
*** 763,775 ****
      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 <systemitem>bryanh</>, <systemitem>ann</>, or <systemitem>robert</> would not be granted
access.
!     Unix user <systemitem>robert</> would only be allowed access when he tries to
!     connect as <productname>PostgreSQL</> user <systemitem>bob</>,
!       not as <systemitem>robert</>
!     or anyone else. <systemitem>ann</> would only be allowed to connect as
!     <systemitem>ann</>. User <systemitem>bryanh</> would be allowed to connect as either
      <systemitem>bryanh</> himself or as <systemitem>guest1</>.
     </para>

--- 713,726 ----
      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 <systemitem>bryanh</>, <systemitem>ann</>, or
!     <systemitem>robert</> would not be granted access. Unix user
!     <systemitem>robert</> would only be allowed access when he tries to
!     connect as <productname>PostgreSQL</> user <systemitem>bob</>, not
!     as <systemitem>robert</> or anyone else. <systemitem>ann</> would
!     only be allowed to connect as <systemitem>ann</>. User
!     <systemitem>bryanh</> would be allowed to connect as either
      <systemitem>bryanh</> himself or as <systemitem>guest1</>.
     </para>

***************
*** 780,786 ****

  omicron        bryanh       bryanh
  omicron        ann          ann
! # bob has username robert on these machines
  omicron        robert       bob
  # bryanh can also connect as guest1
  omicron        bryanh       guest1
--- 731,737 ----

  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
***************
*** 799,828 ****

     <para>
  <ProgramListing>
! No pg_hba.conf entry for host 123.123.123.123, user joeblow, database testdb
  </ProgramListing>
!     This is what you are most likely to get if you succeed in
!     contacting the server, but it does not want to talk to you. As the
!     message suggests, the server refused the connection request
!     because it found no authorizing entry in its <filename>pg_hba.conf</filename>
      configuration file.
     </para>

     <para>
  <ProgramListing>
! Password authentication failed for user 'joeblow'
  </ProgramListing>
!     Messages like this indicate that you contacted the server, and
!     it is willing to talk to you, but not until you pass the
!     authorization method specified in the
!     <filename>pg_hba.conf</filename> file. Check the password you are
!     providing, or check your Kerberos or ident software if the
!     complaint mentions one of those authentication types.
     </para>

     <para>
  <ProgramListing>
! FATAL 1:  user "joeblow" does not exist
  </ProgramListing>
      The indicated user name was not found.
     </para>
--- 750,779 ----

     <para>
  <ProgramListing>
! No pg_hba.conf entry for host 123.123.123.123, user andym, database testdb
  </ProgramListing>
!     This is what you are most likely to get if you succeed in contacting
!     the server, but it does not want to talk to you. As the message
!     suggests, the server refused the connection request because it found
!     no authorizing entry in its <filename>pg_hba.conf</filename>
      configuration file.
     </para>

     <para>
  <ProgramListing>
! Password authentication failed for user 'andym'
  </ProgramListing>
!     Messages like this indicate that you contacted the server, and it is
!     willing to talk to you, but not until you pass the authorization
!     method specified in the <filename>pg_hba.conf</filename> file. Check
!     the password you are providing, or check your Kerberos or ident
!     software if the complaint mentions one of those authentication
!     types.
     </para>

     <para>
  <ProgramListing>
! FATAL 1:  user "andym" does not exist
  </ProgramListing>
      The indicated user name was not found.
     </para>
***************
*** 837,845 ****
     </para>

     <para>
!     Note that the server log may contain more information
!     about an authentication failure than is reported to the client.
!     If you are confused about the reason for a failure, check the log.
     </para>
    </sect1>

--- 788,796 ----
     </para>

     <para>
!     Note that the server log may contain more information about an
!     authentication failure than is reported to the client. If you are
!     confused about the reason for a failure, check the log.
     </para>
    </sect1>

Index: doc/src/sgml/reference.sgml
===================================================================
RCS file: /cvsroot/pgsql/doc/src/sgml/reference.sgml,v
retrieving revision 1.24
diff -c -r1.24 reference.sgml
*** doc/src/sgml/reference.sgml    19 Mar 2002 02:18:11 -0000    1.24
--- doc/src/sgml/reference.sgml    3 Apr 2002 03:48:41 -0000
***************
*** 191,197 ****
     &initlocation;
     &ipcclean;
     &pgCtl;
-    &pgPasswd;
     &postgres;
     &postmaster;

--- 191,196 ----
Index: doc/src/sgml/ref/allfiles.sgml
===================================================================
RCS file: /cvsroot/pgsql/doc/src/sgml/ref/allfiles.sgml,v
retrieving revision 1.36
diff -c -r1.36 allfiles.sgml
*** doc/src/sgml/ref/allfiles.sgml    19 Mar 2002 02:18:12 -0000    1.36
--- doc/src/sgml/ref/allfiles.sgml    3 Apr 2002 03:48:41 -0000
***************
*** 125,131 ****
  <!entity pgCtl              system "pg_ctl-ref.sgml">
  <!entity pgDump             system "pg_dump.sgml">
  <!entity pgDumpall          system "pg_dumpall.sgml">
- <!entity pgPasswd           system "pg_passwd.sgml">
  <!entity pgRestore          system "pg_restore.sgml">
  <!entity pgTclSh            system "pgtclsh.sgml">
  <!entity pgTkSh             system "pgtksh.sgml">
--- 125,130 ----
Index: src/backend/commands/user.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/commands/user.c,v
retrieving revision 1.94
diff -c -r1.94 user.c
*** src/backend/commands/user.c    26 Mar 2002 19:15:48 -0000    1.94
--- src/backend/commands/user.c    3 Apr 2002 03:48:43 -0000
***************
*** 15,20 ****
--- 15,21 ----
  #include <sys/types.h>
  #include <sys/stat.h>
  #include <fcntl.h>
+ #include <errno.h>
  #include <unistd.h>

  #include "access/heapam.h"
***************
*** 27,32 ****
--- 28,34 ----
  #include "libpq/crypt.h"
  #include "miscadmin.h"
  #include "storage/pmsignal.h"
+ #include "utils/acl.h"
  #include "utils/array.h"
  #include "utils/builtins.h"
  #include "utils/fmgroids.h"
***************
*** 39,46 ****

  static void CheckPgUserAclNotNull(void);

! /*---------------------------------------------------------------------
!  * write_password_file / update_pg_pwd
   *
   * copy the modified contents of pg_shadow to a file used by the postmaster
   * for user authentication.  The file is stored as $PGDATA/global/pg_pwd.
--- 41,245 ----

  static void CheckPgUserAclNotNull(void);

!
!
! /*
!  *    fputs_quote
!  *
!  *    Outputs string in quotes, with double-quotes duplicated.
!  *    We could use quote_ident(), but that expects varlena.
!  */
! static void fputs_quote(char *str, FILE *fp)
! {
!     fputc('"', fp);
!     while (*str)
!     {
!         fputc(*str, fp);
!         if (*str == '"')
!             fputc('"', fp);
!         str++;
!     }
!     fputc('"', fp);
! }
!
!
!
! /*
!  * group_getfilename --- get full pathname of group file
!  *
!  * Note that result string is palloc'd, and should be freed by the caller.
!  */
! char *
! group_getfilename(void)
! {
!     int            bufsize;
!     char       *pfnam;
!
!     bufsize = strlen(DataDir) + strlen("/global/") +
!               strlen(USER_GROUP_FILE) + 1;
!     pfnam = (char *) palloc(bufsize);
!     snprintf(pfnam, bufsize, "%s/global/%s", DataDir, USER_GROUP_FILE);
!
!     return pfnam;
! }
!
!
!
! /*
!  * Get full pathname of password file.
!  * Note that result string is palloc'd, and should be freed by the caller.
!  */
! char *
! user_getfilename(void)
! {
!     int            bufsize;
!     char       *pfnam;
!
!     bufsize = strlen(DataDir) + strlen("/global/") +
!               strlen(PWD_FILE) + 1;
!     pfnam = (char *) palloc(bufsize);
!     snprintf(pfnam, bufsize, "%s/global/%s", DataDir, PWD_FILE);
!
!     return pfnam;
! }
!
!
!
! /*
!  * write_group_file for trigger update_pg_pwd_and_pg_group
!  */
! static void
! write_group_file(Relation urel, Relation grel)
! {
!     char       *filename,
!                *tempname;
!     int            bufsize;
!     FILE       *fp;
!     mode_t        oumask;
!     HeapScanDesc scan;
!     HeapTuple    tuple;
!     TupleDesc    dsc = RelationGetDescr(grel);
!
!     /*
!      * Create a temporary filename to be renamed later.  This prevents the
!      * backend from clobbering the pg_group file while the postmaster might
!      * be reading from it.
!      */
!     filename = group_getfilename();
!     bufsize = strlen(filename) + 12;
!     tempname = (char *) palloc(bufsize);
!
!     snprintf(tempname, bufsize, "%s.%d", filename, MyProcPid);
!     oumask = umask((mode_t) 077);
!     fp = AllocateFile(tempname, "w");
!     umask(oumask);
!     if (fp == NULL)
!         elog(ERROR, "write_group_file: unable to write %s: %m", tempname);
!
!     /* read table */
!     scan = heap_beginscan(grel, false, SnapshotSelf, 0, NULL);
!     while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
!     {
!         Datum        datum, grolist_datum;
!         bool        isnull;
!         char       *groname;
!         IdList       *grolist_p;
!         AclId       *aidp;
!         int            i, j,
!                     num;
!         char        *usename;
!         bool        first_user = true;
!
!         datum = heap_getattr(tuple, Anum_pg_group_groname, dsc, &isnull);
!         if (isnull)
!             continue;            /* ignore NULL groupnames */
!         groname = (char *) DatumGetName(datum);
!
!         grolist_datum = heap_getattr(tuple, Anum_pg_group_grolist, dsc, &isnull);
!         /* Ignore NULL group lists */
!         if (isnull)
!             continue;
!
!         grolist_p = DatumGetIdListP(grolist_datum);
!         /*
!          * Check for illegal characters in the group name.
!          */
!         i = strcspn(groname, "\n");
!         if (groname[i] != '\0')
!         {
!             elog(LOG, "Invalid group name '%s'", groname);
!             continue;
!         }
!
!         /* be sure the IdList is not toasted */
!         /* scan it */
!         num = IDLIST_NUM(grolist_p);
!         aidp = IDLIST_DAT(grolist_p);
!         for (i = 0; i < num; ++i)
!         {
!             tuple = SearchSysCache(SHADOWSYSID,
!                                    PointerGetDatum(aidp[i]),
!                                    0, 0, 0);
!             if (HeapTupleIsValid(tuple))
!             {
!                 usename = NameStr(((Form_pg_shadow) GETSTRUCT(tuple))->usename);
!
!                 /*
!                  * Check for illegal characters in the user name.
!                  */
!                 j = strcspn(usename, "\n");
!                 if (usename[j] != '\0')
!                 {
!                     elog(LOG, "Invalid user name '%s'", usename);
!                     continue;
!                 }
!
!                 /* File format is:
!                  *        "dbname"    "user1","user2","user3"
!                  * This matches pg_hba.conf.
!                  */
!                 if (first_user)
!                 {
!                     fputs_quote(groname, fp);
!                     fputs("\t", fp);
!                 }
!                 else
!                     fputs(" ", fp);
!
!                 first_user = false;
!                 fputs_quote(usename, fp);
!
!                 ReleaseSysCache(tuple);
!             }
!         }
!         if (!first_user)
!             fputs("\n", fp);
!         /* if IdList was toasted, free detoasted copy */
!         if ((Pointer) grolist_p != DatumGetPointer(grolist_datum))
!             pfree(grolist_p);
!     }
!     heap_endscan(scan);
!
!     fflush(fp);
!     if (ferror(fp))
!         elog(ERROR, "%s: %m", tempname);
!     FreeFile(fp);
!
!     /*
!      * Rename the temp file to its final name, deleting the old pg_pwd. We
!      * expect that rename(2) is an atomic action.
!      */
!     if (rename(tempname, filename))
!         elog(ERROR, "rename %s to %s: %m", tempname, filename);
!
!     pfree((void *) tempname);
!     pfree((void *) filename);
! }
!
!
!
! /*
!  * write_password_file for trigger update_pg_pwd_and_pg_group
   *
   * copy the modified contents of pg_shadow to a file used by the postmaster
   * for user authentication.  The file is stored as $PGDATA/global/pg_pwd.
***************
*** 51,60 ****
   * We raise an error to force transaction rollback if we detect an illegal
   * username or password --- illegal being defined as values that would
   * mess up the pg_pwd parser.
-  *---------------------------------------------------------------------
   */
  static void
! write_password_file(Relation rel)
  {
      char       *filename,
                 *tempname;
--- 250,258 ----
   * We raise an error to force transaction rollback if we detect an illegal
   * username or password --- illegal being defined as values that would
   * mess up the pg_pwd parser.
   */
  static void
! write_user_file(Relation urel)
  {
      char       *filename,
                 *tempname;
***************
*** 63,76 ****
      mode_t        oumask;
      HeapScanDesc scan;
      HeapTuple    tuple;
!     TupleDesc    dsc = RelationGetDescr(rel);

      /*
       * Create a temporary filename to be renamed later.  This prevents the
       * backend from clobbering the pg_pwd file while the postmaster might
       * be reading from it.
       */
!     filename = crypt_getpwdfilename();
      bufsize = strlen(filename) + 12;
      tempname = (char *) palloc(bufsize);

--- 261,274 ----
      mode_t        oumask;
      HeapScanDesc scan;
      HeapTuple    tuple;
!     TupleDesc    dsc = RelationGetDescr(urel);

      /*
       * Create a temporary filename to be renamed later.  This prevents the
       * backend from clobbering the pg_pwd file while the postmaster might
       * be reading from it.
       */
!     filename = user_getfilename();
      bufsize = strlen(filename) + 12;
      tempname = (char *) palloc(bufsize);

***************
*** 82,107 ****
          elog(ERROR, "write_password_file: unable to write %s: %m", tempname);

      /* read table */
!     scan = heap_beginscan(rel, false, SnapshotSelf, 0, NULL);
      while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
      {
!         Datum        datum_n,
!                     datum_p,
!                     datum_v;
!         bool        null_n,
!                     null_p,
!                     null_v;
!         char       *str_n,
!                    *str_p,
!                    *str_v;
          int            i;

!         datum_n = heap_getattr(tuple, Anum_pg_shadow_usename, dsc, &null_n);
!         if (null_n)
              continue;            /* ignore NULL usernames */
!         str_n = DatumGetCString(DirectFunctionCall1(nameout, datum_n));

!         datum_p = heap_getattr(tuple, Anum_pg_shadow_passwd, dsc, &null_p);

          /*
           * It can be argued that people having a null password shouldn't
--- 280,301 ----
          elog(ERROR, "write_password_file: unable to write %s: %m", tempname);

      /* read table */
!     scan = heap_beginscan(urel, false, SnapshotSelf, 0, NULL);
      while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
      {
!         Datum        datum;
!         bool        isnull;
!         char       *usename,
!                    *passwd,
!                    *valuntil;
          int            i;

!         datum = heap_getattr(tuple, Anum_pg_shadow_usename, dsc, &isnull);
!         if (isnull)
              continue;            /* ignore NULL usernames */
!         usename = (char *) DatumGetName(datum);

!         datum = heap_getattr(tuple, Anum_pg_shadow_passwd, dsc, &isnull);

          /*
           * It can be argued that people having a null password shouldn't
***************
*** 110,166 ****
           * assuming an empty password in that case is better, change this
           * logic to look something like the code for valuntil.
           */
!         if (null_p)
!         {
!             pfree(str_n);
              continue;
-         }
-         str_p = DatumGetCString(DirectFunctionCall1(textout, datum_p));

!         datum_v = heap_getattr(tuple, Anum_pg_shadow_valuntil, dsc, &null_v);
!         if (null_v)
!             str_v = pstrdup("\\N");
          else
!             str_v = DatumGetCString(DirectFunctionCall1(nabstimeout, datum_v));

          /*
           * Check for illegal characters in the username and password.
           */
!         i = strcspn(str_n, CRYPT_PWD_FILE_SEPSTR "\n");
!         if (str_n[i] != '\0')
!             elog(ERROR, "Invalid user name '%s'", str_n);
!         i = strcspn(str_p, CRYPT_PWD_FILE_SEPSTR "\n");
!         if (str_p[i] != '\0')
!             elog(ERROR, "Invalid user password '%s'", str_p);

          /*
           * The extra columns we emit here are not really necessary. To
           * remove them, the parser in backend/libpq/crypt.c would need to
           * be adjusted.
           */
!         fprintf(fp,
!                 "%s"
!                 CRYPT_PWD_FILE_SEPSTR
!                 "0"
!                 CRYPT_PWD_FILE_SEPSTR
!                 "x"
!                 CRYPT_PWD_FILE_SEPSTR
!                 "x"
!                 CRYPT_PWD_FILE_SEPSTR
!                 "x"
!                 CRYPT_PWD_FILE_SEPSTR
!                 "x"
!                 CRYPT_PWD_FILE_SEPSTR
!                 "%s"
!                 CRYPT_PWD_FILE_SEPSTR
!                 "%s\n",
!                 str_n,
!                 str_p,
!                 str_v);
!
!         pfree(str_n);
!         pfree(str_p);
!         pfree(str_v);
      }
      heap_endscan(scan);

--- 304,343 ----
           * assuming an empty password in that case is better, change this
           * logic to look something like the code for valuntil.
           */
!         if (isnull)
              continue;

!         passwd = DatumGetCString(DirectFunctionCall1(textout, datum));
!
!         datum = heap_getattr(tuple, Anum_pg_shadow_valuntil, dsc, &isnull);
!         if (isnull)
!             valuntil = pstrdup("");
          else
!             valuntil = DatumGetCString(DirectFunctionCall1(nabstimeout, datum));

          /*
           * Check for illegal characters in the username and password.
           */
!         i = strcspn(usename, "\n");
!         if (usename[i] != '\0')
!             elog(ERROR, "Invalid user name '%s'", usename);
!         i = strcspn(passwd, "\n");
!         if (passwd[i] != '\0')
!             elog(ERROR, "Invalid user password '%s'", passwd);

          /*
           * The extra columns we emit here are not really necessary. To
           * remove them, the parser in backend/libpq/crypt.c would need to
           * be adjusted.
           */
!         fputs_quote(usename, fp);
!         fputs(" ", fp);
!         fputs_quote(passwd, fp);
!         fputs(" ", fp);
!         fputs_quote(valuntil, fp);
!
!         pfree(passwd);
!         pfree(valuntil);
      }
      heap_endscan(scan);

***************
*** 178,206 ****

      pfree((void *) tempname);
      pfree((void *) filename);
-
-     /*
-      * Signal the postmaster to reload its password-file cache.
-      */
-     SendPostmasterSignal(PMSIGNAL_PASSWORD_CHANGE);
  }



  /* This is the wrapper for triggers. */
  Datum
! update_pg_pwd(PG_FUNCTION_ARGS)
  {
      /*
       * ExclusiveLock ensures no one modifies pg_shadow while we read it,
       * and that only one backend rewrites the flat file at a time.    It's
       * OK to allow normal reads of pg_shadow in parallel, however.
       */
!     Relation    rel = heap_openr(ShadowRelationName, ExclusiveLock);

!     write_password_file(rel);
      /* OK to release lock, since we did not modify the relation */
!     heap_close(rel, ExclusiveLock);
      return PointerGetDatum(NULL);
  }

--- 355,387 ----

      pfree((void *) tempname);
      pfree((void *) filename);
  }



  /* This is the wrapper for triggers. */
  Datum
! update_pg_pwd_and_pg_group(PG_FUNCTION_ARGS)
  {
      /*
       * ExclusiveLock ensures no one modifies pg_shadow while we read it,
       * and that only one backend rewrites the flat file at a time.    It's
       * OK to allow normal reads of pg_shadow in parallel, however.
       */
!     Relation    urel = heap_openr(ShadowRelationName, ExclusiveLock);
!     Relation    grel = heap_openr(GroupRelationName, ExclusiveLock);

!     write_user_file(urel);
!     write_group_file(urel, grel);
      /* OK to release lock, since we did not modify the relation */
!     heap_close(grel, ExclusiveLock);
!     heap_close(urel, ExclusiveLock);
!
!     /*
!      * Signal the postmaster to reload its password & group-file cache.
!      */
!     SendPostmasterSignal(PMSIGNAL_PASSWORD_CHANGE);
!
      return PointerGetDatum(NULL);
  }

***************
*** 446,459 ****
      }

      /*
!      * Write the updated pg_shadow data to the flat password file.
       */
!     write_password_file(pg_shadow_rel);

      /*
!      * Now we can clean up; but keep lock until commit.
       */
!     heap_close(pg_shadow_rel, NoLock);
  }


--- 627,640 ----
      }

      /*
!      * Now we can clean up; but keep lock until commit.
       */
!     heap_close(pg_shadow_rel, NoLock);

      /*
!      * Write the updated pg_shadow and pg_group data to the flat file.
       */
!     update_pg_pwd_and_pg_group(NULL);
  }


***************
*** 680,693 ****
      heap_freetuple(new_tuple);

      /*
!      * Write the updated pg_shadow data to the flat password file.
       */
!     write_password_file(pg_shadow_rel);

      /*
!      * Now we can clean up.
       */
!     heap_close(pg_shadow_rel, NoLock);
  }


--- 861,874 ----
      heap_freetuple(new_tuple);

      /*
!      * Now we can clean up.
       */
!     heap_close(pg_shadow_rel, NoLock);

      /*
!      * Write the updated pg_shadow and pg_group data to the flat file.
       */
!     update_pg_pwd_and_pg_group(NULL);
  }


***************
*** 733,739 ****
      {
          Datum datum;
          bool isnull;
!         ArrayType *a;

          repl_null[Anum_pg_shadow_useconfig-1] = ' ';

--- 914,920 ----
      {
          Datum datum;
          bool isnull;
!         ArrayType *array;

          repl_null[Anum_pg_shadow_useconfig-1] = ' ';

***************
*** 741,757 ****
                                  Anum_pg_shadow_useconfig, &isnull);

          if (valuestr)
!             a = GUCArrayAdd(isnull
                              ? NULL
                              : (ArrayType *) pg_detoast_datum((struct varlena *)datum),
                              stmt->variable, valuestr);
          else
!             a = GUCArrayDelete(isnull
                                 ? NULL
                                 : (ArrayType *) pg_detoast_datum((struct varlena *)datum),
                                 stmt->variable);

!         repl_val[Anum_pg_shadow_useconfig-1] = PointerGetDatum(a);
      }

      newtuple = heap_modifytuple(oldtuple, rel, repl_val, repl_null, repl_repl);
--- 922,938 ----
                                  Anum_pg_shadow_useconfig, &isnull);

          if (valuestr)
!             array = GUCArrayAdd(isnull
                              ? NULL
                              : (ArrayType *) pg_detoast_datum((struct varlena *)datum),
                              stmt->variable, valuestr);
          else
!             array = GUCArrayDelete(isnull
                                 ? NULL
                                 : (ArrayType *) pg_detoast_datum((struct varlena *)datum),
                                 stmt->variable);

!         repl_val[Anum_pg_shadow_useconfig-1] = PointerGetDatum(array);
      }

      newtuple = heap_modifytuple(oldtuple, rel, repl_val, repl_null, repl_repl);
***************
*** 846,852 ****
              datum = heap_getattr(tmp_tuple, Anum_pg_database_datname,
                                   pg_dsc, &null);
              Assert(!null);
!             dbname = DatumGetCString(DirectFunctionCall1(nameout, datum));
              elog(ERROR, "DROP USER: user \"%s\" owns database \"%s\", cannot be removed%s",
                   user, dbname,
                   (length(stmt->users) > 1) ? " (no users removed)" : "");
--- 1027,1033 ----
              datum = heap_getattr(tmp_tuple, Anum_pg_database_datname,
                                   pg_dsc, &null);
              Assert(!null);
!             dbname = (char *) DatumGetName(datum);
              elog(ERROR, "DROP USER: user \"%s\" owns database \"%s\", cannot be removed%s",
                   user, dbname,
                   (length(stmt->users) > 1) ? " (no users removed)" : "");
***************
*** 901,914 ****
      }

      /*
!      * Write the updated pg_shadow data to the flat password file.
       */
!     write_password_file(pg_shadow_rel);

      /*
!      * Now we can clean up.
       */
!     heap_close(pg_shadow_rel, NoLock);
  }


--- 1082,1095 ----
      }

      /*
!      * Now we can clean up.
       */
!     heap_close(pg_shadow_rel, NoLock);

      /*
!      * Write the updated pg_shadow and pg_group data to the flat file.
       */
!     update_pg_pwd_and_pg_group(NULL);
  }


***************
*** 1111,1116 ****
--- 1292,1302 ----
      }

      heap_close(pg_group_rel, NoLock);
+
+     /*
+      * Write the updated pg_shadow and pg_group data to the flat file.
+      */
+     update_pg_pwd_and_pg_group(NULL);
  }


***************
*** 1366,1372 ****
--- 1552,1566 ----

      ReleaseSysCache(group_tuple);

+     /*
+      * Write the updated pg_shadow and pg_group data to the flat files.
+      */
      heap_close(pg_group_rel, NoLock);
+
+     /*
+      * Write the updated pg_shadow and pg_group data to the flat file.
+      */
+     update_pg_pwd_and_pg_group(NULL);
  }


***************
*** 1419,1422 ****
--- 1613,1621 ----
          elog(ERROR, "DROP GROUP: group \"%s\" does not exist", stmt->name);

      heap_close(pg_group_rel, NoLock);
+
+     /*
+      * Write the updated pg_shadow and pg_group data to the flat file.
+      */
+     update_pg_pwd_and_pg_group(NULL);
  }
Index: src/backend/libpq/Makefile
===================================================================
RCS file: /cvsroot/pgsql/src/backend/libpq/Makefile,v
retrieving revision 1.29
diff -c -r1.29 Makefile
*** src/backend/libpq/Makefile    4 Mar 2002 01:46:02 -0000    1.29
--- src/backend/libpq/Makefile    3 Apr 2002 03:48:43 -0000
***************
*** 14,22 ****

  # be-fsstubs is here for historical reasons, probably belongs elsewhere

! OBJS = be-fsstubs.o \
!     auth.o crypt.o hba.o md5.o password.o \
!     pqcomm.o pqformat.o pqsignal.o


  all: SUBSYS.o
--- 14,20 ----

  # be-fsstubs is here for historical reasons, probably belongs elsewhere

! OBJS = be-fsstubs.o auth.o crypt.o hba.o md5.o pqcomm.o pqformat.o pqsignal.o


  all: SUBSYS.o
Index: src/backend/libpq/auth.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/libpq/auth.c,v
retrieving revision 1.79
diff -c -r1.79 auth.c
*** src/backend/libpq/auth.c    5 Mar 2002 07:57:45 -0000    1.79
--- src/backend/libpq/auth.c    3 Apr 2002 03:48:43 -0000
***************
*** 34,40 ****
  #include "miscadmin.h"

  static void sendAuthRequest(Port *port, AuthRequest areq);
- static int    checkPassword(Port *port, char *user, char *password);
  static int    old_be_recvauth(Port *port);
  static int    map_old_to_new(Port *port, UserAuth old, int status);
  static void auth_failed(Port *port, int status);
--- 34,39 ----
***************
*** 381,387 ****
          saved = port->auth_method;
          port->auth_method = uaPassword;

!         status = checkPassword(port, user, password);

          port->auth_method = saved;

--- 380,386 ----
          saved = port->auth_method;
          port->auth_method = uaPassword;

!         status = md5_crypt_verify(port, user, password);

          port->auth_method = saved;

***************
*** 663,669 ****

          initStringInfo(&buf);
          pq_getstr(&buf);
!
          /* Do not echo failed password to logs, for security. */
          elog(DEBUG5, "received PAM packet");

--- 662,668 ----

          initStringInfo(&buf);
          pq_getstr(&buf);
!
          /* Do not echo failed password to logs, for security. */
          elog(DEBUG5, "received PAM packet");

***************
*** 810,832 ****
      /* Do not echo failed password to logs, for security. */
      elog(DEBUG5, "received password packet");

!     result = checkPassword(port, port->user, buf.data);
      pfree(buf.data);
      return result;
- }
-
-
- /*
-  * Handle `password' and `crypt' records. If an auth argument was
-  * specified, use the respective file. Else use pg_shadow passwords.
-  */
- static int
- checkPassword(Port *port, char *user, char *password)
- {
-     if (port->auth_arg[0] != '\0')
-         return verify_password(port, user, password);
-
-     return md5_crypt_verify(port, user, password);
  }


--- 809,818 ----
      /* Do not echo failed password to logs, for security. */
      elog(DEBUG5, "received password packet");

!     result = md5_crypt_verify(port, port->user, buf.data);
!
      pfree(buf.data);
      return result;
  }


Index: src/backend/libpq/crypt.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/libpq/crypt.c,v
retrieving revision 1.44
diff -c -r1.44 crypt.c
*** src/backend/libpq/crypt.c    4 Mar 2002 01:46:03 -0000    1.44
--- src/backend/libpq/crypt.c    3 Apr 2002 03:48:43 -0000
***************
*** 15,21 ****
   */
  #include "postgres.h"

- #include <errno.h>
  #include <unistd.h>
  #ifdef HAVE_CRYPT_H
  #include <crypt.h>
--- 15,20 ----
***************
*** 25,255 ****
  #include "libpq/libpq.h"
  #include "miscadmin.h"
  #include "storage/fd.h"
  #include "utils/nabstime.h"


- #define CRYPT_PWD_FILE    "pg_pwd"
-
-
- static char **pwd_cache = NULL;
- static int    pwd_cache_count = 0;
-
- /*
-  * crypt_getpwdfilename --- get full pathname of password file
-  *
-  * Note that result string is palloc'd, and should be freed by the caller.
-  */
- char *
- crypt_getpwdfilename(void)
- {
-     int            bufsize;
-     char       *pfnam;
-
-     bufsize = strlen(DataDir) + 8 + strlen(CRYPT_PWD_FILE) + 1;
-     pfnam = (char *) palloc(bufsize);
-     snprintf(pfnam, bufsize, "%s/global/%s", DataDir, CRYPT_PWD_FILE);
-
-     return pfnam;
- }
-
- /*
-  * Open the password file if possible (return NULL if not)
-  */
- static FILE *
- crypt_openpwdfile(void)
- {
-     char       *filename;
-     FILE       *pwdfile;
-
-     filename = crypt_getpwdfilename();
-     pwdfile = AllocateFile(filename, "r");
-
-     if (pwdfile == NULL && errno != ENOENT)
-         elog(LOG, "could not open %s: %m", filename);
-
-     pfree(filename);
-
-     return pwdfile;
- }
-
- /*
-  * Compare two password-file lines on the basis of their usernames.
-  *
-  * Can also be used to compare just a username against a password-file
-  * line (for bsearch).
-  */
- static int
- compar_user(const void *user_a, const void *user_b)
- {
-     char       *login_a;
-     char       *login_b;
-     int            len_a,
-                 len_b,
-                 result;
-
-     login_a = *((char **) user_a);
-     login_b = *((char **) user_b);
-
-     /*
-      * We only really want to compare the user logins which are first and
-      * are terminated by CRYPT_PWD_FILE_SEPSTR.  (NB: this code
-      * effectively assumes that CRYPT_PWD_FILE_SEPSTR is just one char.)
-      */
-     len_a = strcspn(login_a, CRYPT_PWD_FILE_SEPSTR);
-     len_b = strcspn(login_b, CRYPT_PWD_FILE_SEPSTR);
-
-     result = strncmp(login_a, login_b, Min(len_a, len_b));
-
-     if (result == 0)            /* one could be a prefix of the other */
-         result = (len_a - len_b);
-
-     return result;
- }
-
- /*
-  * Load or reload the password-file cache
-  */
- void
- load_password_cache(void)
- {
-     FILE       *pwd_file;
-     char        buffer[1024];
-
-     /*
-      * If for some reason we fail to open the password file, preserve the
-      * old cache contents; this seems better than dropping the cache if,
-      * say, we are temporarily out of filetable slots.
-      */
-     if (!(pwd_file = crypt_openpwdfile()))
-         return;
-
-     /* free any old data */
-     if (pwd_cache)
-     {
-         while (--pwd_cache_count >= 0)
-             pfree(pwd_cache[pwd_cache_count]);
-         pfree(pwd_cache);
-         pwd_cache = NULL;
-         pwd_cache_count = 0;
-     }
-
-     /*
-      * Read the file and store its lines in current memory context, which
-      * we expect will be PostmasterContext.  That context will live as
-      * long as we need the cache to live, ie, until just after each
-      * postmaster child has completed client authentication.
-      */
-     while (fgets(buffer, sizeof(buffer), pwd_file) != NULL)
-     {
-         int            blen;
-
-         /*
-          * We must remove the return char at the end of the string, as
-          * this will affect the correct parsing of the password entry.
-          */
-         if (buffer[(blen = strlen(buffer) - 1)] == '\n')
-             buffer[blen] = '\0';
-
-         if (pwd_cache == NULL)
-             pwd_cache = (char **)
-                 palloc(sizeof(char *) * (pwd_cache_count + 1));
-         else
-             pwd_cache = (char **)
-                 repalloc((void *) pwd_cache,
-                          sizeof(char *) * (pwd_cache_count + 1));
-         pwd_cache[pwd_cache_count++] = pstrdup(buffer);
-     }
-
-     FreeFile(pwd_file);
-
-     /*
-      * Now sort the entries in the cache for faster searching later.
-      */
-     qsort((void *) pwd_cache, pwd_cache_count, sizeof(char *), compar_user);
- }
-
- /*
-  * Parse a line of the password file to extract password and valid-until date.
-  */
- static bool
- crypt_parsepwdentry(char *buffer, char **pwd, char **valdate)
- {
-     char       *parse = buffer;
-     int            count,
-                 i;
-
-     *pwd = NULL;
-     *valdate = NULL;
-
-     /*
-      * skip to the password field
-      */
-     for (i = 0; i < 6; i++)
-     {
-         parse += strcspn(parse, CRYPT_PWD_FILE_SEPSTR);
-         if (*parse == '\0')
-             return false;
-         parse++;
-     }
-
-     /*
-      * store a copy of user password to return
-      */
-     count = strcspn(parse, CRYPT_PWD_FILE_SEPSTR);
-     *pwd = (char *) palloc(count + 1);
-     memcpy(*pwd, parse, count);
-     (*pwd)[count] = '\0';
-     parse += count;
-     if (*parse == '\0')
-     {
-         pfree(*pwd);
-         *pwd = NULL;
-         return false;
-     }
-     parse++;
-
-     /*
-      * store a copy of the date login becomes invalid
-      */
-     count = strcspn(parse, CRYPT_PWD_FILE_SEPSTR);
-     *valdate = (char *) palloc(count + 1);
-     memcpy(*valdate, parse, count);
-     (*valdate)[count] = '\0';
-
-     return true;
- }
-
- /*
-  * Lookup a username in the password-file cache,
-  * return his password and valid-until date.
-  */
- static bool
- crypt_getloginfo(const char *user, char **passwd, char **valuntil)
- {
-     *passwd = NULL;
-     *valuntil = NULL;
-
-     if (pwd_cache)
-     {
-         char      **pwd_entry;
-
-         pwd_entry = (char **) bsearch((void *) &user,
-                                       (void *) pwd_cache,
-                                       pwd_cache_count,
-                                       sizeof(char *),
-                                       compar_user);
-         if (pwd_entry)
-         {
-             if (crypt_parsepwdentry(*pwd_entry, passwd, valuntil))
-                 return true;
-         }
-     }
-
-     return false;
- }
-
- /*-------------------------------------------------------------------------*/
-
  int
  md5_crypt_verify(const Port *port, const char *user, const char *pgpass)
  {
--- 24,33 ----
  #include "libpq/libpq.h"
  #include "miscadmin.h"
  #include "storage/fd.h"
+ #include "nodes/pg_list.h"
  #include "utils/nabstime.h"


  int
  md5_crypt_verify(const Port *port, const char *user, const char *pgpass)
  {
***************
*** 257,265 ****
                 *valuntil,
                 *crypt_pwd;
      int            retval = STATUS_ERROR;

!     if (!crypt_getloginfo(user, &passwd, &valuntil))
          return STATUS_ERROR;

      if (passwd == NULL || *passwd == '\0')
      {
--- 35,47 ----
                 *valuntil,
                 *crypt_pwd;
      int            retval = STATUS_ERROR;
+     List       **line;

!     if ((line = get_user_line(user)) == NULL)
          return STATUS_ERROR;
+
+     passwd = lfirst(lnext(lnext(*line)));
+     valuntil = lfirst(lnext(lnext(lnext(*line))));

      if (passwd == NULL || *passwd == '\0')
      {
Index: src/backend/libpq/hba.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/libpq/hba.c,v
retrieving revision 1.80
diff -c -r1.80 hba.c
*** src/backend/libpq/hba.c    4 Mar 2002 01:46:03 -0000    1.80
--- src/backend/libpq/hba.c    3 Apr 2002 03:48:44 -0000
***************
*** 30,44 ****
  #include <arpa/inet.h>
  #include <unistd.h>

  #include "libpq/libpq.h"
  #include "miscadmin.h"
  #include "nodes/pg_list.h"
  #include "storage/fd.h"


- #define MAX_TOKEN 80
- /* Maximum size of one token in the configuration file    */
-
  #define IDENT_USERNAME_MAX 512
  /* Max size of username ident server can return */

--- 30,43 ----
  #include <arpa/inet.h>
  #include <unistd.h>

+ #include "commands/user.h"
+ #include "libpq/crypt.h"
  #include "libpq/libpq.h"
  #include "miscadmin.h"
  #include "nodes/pg_list.h"
  #include "storage/fd.h"


  #define IDENT_USERNAME_MAX 512
  /* Max size of username ident server can return */

***************
*** 53,58 ****
--- 52,68 ----
   */
  static List *hba_lines = NIL;    /* pre-parsed contents of hba file */
  static List *ident_lines = NIL; /* pre-parsed contents of ident file */
+ static List *group_lines = NIL;    /* pre-parsed contents of group file */
+ static List *user_lines = NIL;    /* pre-parsed contents of user password file */
+
+ /* sorted entries so we can do binary search lookups */
+ static List **user_sorted = NULL;    /* sorted user list, for bsearch() */
+ static List **group_sorted = NULL;    /* sorted group list, for bsearch() */
+ static int  user_length;
+ static int  group_length;
+
+ static List *tokenize_file(FILE *file);
+ static char *tokenize_inc_file(const char *inc_filename);


  /*
***************
*** 67,107 ****


  /*
!  *    Grab one token out of fp.  Tokens are strings of non-blank
!  *    characters bounded by blank characters, beginning of line, and end
!  *    of line.    Blank means space or tab.  Return the token as *buf.
!  *    Leave file positioned to character immediately after the token or
!  *    EOF, whichever comes first.  If no more tokens on line, return null
!  *    string as *buf and position file to beginning of next line or EOF,
!  *    whichever comes first.
   */
! static void
  next_token(FILE *fp, char *buf, const int bufsz)
  {
      int            c;
!     char       *eb = buf + (bufsz - 1);

!     /* Move over initial token-delimiting blanks */
!     while ((c = getc(fp)) != EOF && isblank(c))
          ;

      if (c != EOF && c != '\n')
      {
          /*
!          * build a token in buf of next characters up to EOF, eol, or
!          * blank.  If the token gets too long, we still parse it
!          * correctly, but the excess characters are not stored into *buf.
           */
!         while (c != EOF && c != '\n' && !isblank(c))
          {
!             if (buf < eb)
                  *buf++ = c;
              c = getc(fp);
          }

          /*
           * Put back the char right after the token (critical in case it is
!          * eol, since we need to detect end-of-line at next call).
           */
          if (c != EOF)
              ungetc(c, fp);
--- 77,152 ----


  /*
!  *   Grab one token out of fp. Tokens are strings of non-blank
!  *   characters bounded by blank characters, beginning of line, and
!  *   end of line. Blank means space or tab. Return the token as
!  *   *buf. Leave file positioned to character immediately after the
!  *   token or EOF, whichever comes first. If no more tokens on line,
!  *   return null string as *buf and position file to beginning of
!  *   next line or EOF, whichever comes first. Allow spaces in quoted
!  *   strings. Terminate on unquoted commas. Handle comments.
   */
! void
  next_token(FILE *fp, char *buf, const int bufsz)
  {
      int            c;
!     char       *end_buf = buf + (bufsz - 1);
!     bool        in_quote = false;
!     bool        was_quote = false;

!     /* Move over initial whitespace and commas */
!     while ((c = getc(fp)) != EOF && (isblank(c) || c == ','))
          ;

      if (c != EOF && c != '\n')
      {
          /*
!          * Build a token in buf of next characters up to EOF, EOL, unquoted
!          * comma, or unquoted whitespace.
           */
!         while (c != EOF && c != '\n' &&
!                (!isblank(c) || in_quote == true))
          {
!             if (c == '"')
!                 in_quote = !in_quote;
!
!             /* skip comments to EOL */
!             if (c == '#' && !in_quote)
!             {
!                 while ((c = getc(fp)) != EOF && c != '\n')
!                     ;
!                 continue;
!             }
!
!             if (buf >= end_buf)
!             {
!                 elog(LOG, "Token too long in authentication file, skipping, %s", buf);
!                 /* Discard remainder of line */
!                 while ((c = getc(fp)) != EOF && c != '\n')
!                     ;
!                 buf[0] = '\0';
!                 break;
!             }
!
!             if (c != '"' || (c == '"' && was_quote))
                  *buf++ = c;
+
+             /* We pass back the comma so the caller knows there is more */
+             if ((isblank(c) || c == ',') && !in_quote)
+                 break;
+
+             /* Literal double-quote is two double-quotes */
+             if (c == '"')
+                 was_quote = !was_quote;
+             else
+                 was_quote = false;
+
              c = getc(fp);
          }

          /*
           * Put back the char right after the token (critical in case it is
!          * EOL, since we need to detect end-of-line at next call).
           */
          if (c != EOF)
              ungetc(c, fp);
***************
*** 109,125 ****
      *buf = '\0';
  }


  static void
! read_through_eol(FILE *file)
  {
!     int            c;

!     while ((c = getc(file)) != EOF && c != '\n')
!         ;
  }


  /*
   *    Read the given file and create a list of line sublists.
   */
--- 154,295 ----
      *buf = '\0';
  }

+ /*
+  *   Tokenize file and handle file inclusion and comma lists. We have
+  *   to  break  apart  the  commas  to  expand  any  file names then
+  *   reconstruct with commas.
+  */
+ static char *
+ next_token_expand(FILE *file)
+ {
+     char        buf[MAX_TOKEN];
+     char       *comma_str = pstrdup("");
+     bool        trailing_comma;
+     char       *incbuf;
+
+     do
+     {
+         next_token(file, buf, sizeof(buf));
+         if (!*buf)
+             break;
+
+         if (buf[strlen(buf)-1] == ',')
+         {
+             trailing_comma = true;
+             buf[strlen(buf)-1] = '\0';
+         }
+         else
+             trailing_comma = false;
+
+         /* Is this referencing a file? */
+         if (buf[0] == '@')
+             incbuf = tokenize_inc_file(buf+1);
+         else
+             incbuf = pstrdup(buf);
+
+         comma_str = repalloc(comma_str,
+                              strlen(comma_str) + strlen(incbuf) + 1);
+         strcat(comma_str, incbuf);
+         pfree(incbuf);
+
+         if (trailing_comma)
+         {
+             comma_str = repalloc(comma_str, strlen(comma_str) + 1 + 1);
+             strcat(comma_str, ",");
+         }
+     } while (trailing_comma);
+
+     return comma_str;
+ }
+

+ /*
+  * Free memory used by lines/tokens (i.e., structure built by tokenize_file)
+  */
  static void
! free_lines(List **lines)
  {
!     if (*lines)
!     {
!         List       *line,
!                    *token;

!         foreach(line, *lines)
!         {
!             List       *ln = lfirst(line);
!
!             /* free the pstrdup'd tokens (don't try it on the line number) */
!             foreach(token, lnext(ln))
!                 pfree(lfirst(token));
!             /* free the sublist structure itself */
!             freeList(ln);
!         }
!         /* free the list structure itself */
!         freeList(*lines);
!         /* clear the static variable */
!         *lines = NIL;
!     }
! }
!
!
! static char *
! tokenize_inc_file(const char *inc_filename)
! {
!     char       *inc_fullname;
!     FILE       *inc_file;
!     List        *inc_lines;
!     List       *line;
!     char       *comma_str = pstrdup("");
!
!     inc_fullname = (char *) palloc(strlen(DataDir) + 1 +
!                                    strlen(inc_filename) + 1);
!     strcpy(inc_fullname, DataDir);
!     strcat(inc_fullname, "/");
!     strcat(inc_fullname, inc_filename);
!
!     inc_file = AllocateFile(inc_fullname, "r");
!     if (!inc_file)
!     {
!         elog(LOG, "tokenize_inc_file: Unable to open secondary authentication file \"@%s\" as \"%s\": %m",
!              inc_filename, inc_fullname);
!         pfree(inc_fullname);
!
!         /* return empty string, it matches nothing */
!         return pstrdup("");
!     }
!     pfree(inc_fullname);
!
!     /* There is possible recursion here if the file contains @ */
!     inc_lines = tokenize_file(inc_file);
!     FreeFile(inc_file);
!
!     /* Create comma-separate string from List */
!     foreach(line, inc_lines)
!     {
!         List       *ln = lfirst(line);
!         List       *token;
!
!         /* First entry is line number */
!         foreach(token, lnext(ln))
!         {
!             if (strlen(comma_str))
!             {
!                 comma_str = repalloc(comma_str, strlen(comma_str) + 1);
!                 strcat(comma_str, ",");
!             }
!             comma_str = repalloc(comma_str,
!                         strlen(comma_str) + strlen(lfirst(token)) + 1);
!             strcat(comma_str, lfirst(token));
!         }
!     }
!
!     free_lines(&inc_lines);
!
!     return comma_str;
  }


+
  /*
   *    Read the given file and create a list of line sublists.
   */
***************
*** 129,147 ****
      List       *lines = NIL;
      List       *next_line = NIL;
      int            line_number = 1;
!     char        buf[MAX_TOKEN];
!     char       *comment_ptr;

      while (!feof(file))
      {
!         next_token(file, buf, sizeof(buf));

!         /* trim off comment, even if inside a token */
!         comment_ptr = strchr(buf, '#');
!         if (comment_ptr != NULL)
!             *comment_ptr = '\0';
!
!         /* add token to list, unless we are at eol or comment start */
          if (buf[0] != '\0')
          {
              if (next_line == NIL)
--- 299,311 ----
      List       *lines = NIL;
      List       *next_line = NIL;
      int            line_number = 1;
!     char       *buf;

      while (!feof(file))
      {
!         buf = next_token_expand(file);

!         /* add token to list, unless we are at EOL or comment start */
          if (buf[0] != '\0')
          {
              if (next_line == NIL)
***************
*** 151,172 ****
                  lines = lappend(lines, next_line);
              }
              /* append token to current line's list */
!             next_line = lappend(next_line, pstrdup(buf));
          }
          else
          {
!             /* we are at real or logical eol, so force a new line List */
!             next_line = NIL;
!         }
!
!         if (comment_ptr != NULL)
!         {
!             /* Found a comment, so skip the rest of the line */
!             read_through_eol(file);
              next_line = NIL;
          }

!         /* Advance line number whenever we reach eol */
          if (next_line == NIL)
              line_number++;
      }
--- 315,329 ----
                  lines = lappend(lines, next_line);
              }
              /* append token to current line's list */
!             next_line = lappend(next_line, buf);
          }
          else
          {
!             /* we are at real or logical EOL, so force a new line List */
              next_line = NIL;
          }

!         /* Advance line number whenever we reach EOL */
          if (next_line == NIL)
              line_number++;
      }
***************
*** 176,206 ****


  /*
!  * Free memory used by lines/tokens (ie, structure built by tokenize_file)
   */
! static void
! free_lines(List **lines)
  {
!     if (*lines)
      {
!         List       *line,
!                    *token;

!         foreach(line, *lines)
          {
!             List       *ln = lfirst(line);

!             /* free the pstrdup'd tokens (don't try it on the line number) */
!             foreach(token, lnext(ln))
!                 pfree(lfirst(token));
!             /* free the sublist structure itself */
!             freeList(ln);
          }
!         /* free the list structure itself */
!         freeList(*lines);
!         /* clear the static variable */
!         *lines = NIL;
      }
  }


--- 333,448 ----


  /*
!  * Compare two password-file lines on the basis of their user names.
!  *
!  * Used for qsort() sorting and bsearch() lookup.
   */
! static int
! user_group_cmp(const void *user, const void *list)
  {
!                         /* first node is line number */
!     char        *user1 = (char *)user;
!     char       *user2 = lfirst(lnext(*(List **)list));
!
!     return strcmp(user1, user2);
! }
!
!
! /*
!  * Lookup a group name in the pg_group file
!  */
! static List **
! get_group_line(const char *group)
! {
!     return (List **) bsearch((void *) group,
!                             (void *) group_sorted,
!                             group_length,
!                             sizeof(List *),
!                             user_group_cmp);
! }
!
!
! /*
!  * Lookup a user name in the pg_shadow file
!  */
! List **
! get_user_line(const char *user)
! {
!     return (List **) bsearch((void *) user,
!                             (void *) user_sorted,
!                             user_length,
!                             sizeof(List *),
!                             user_group_cmp);
! }
!
!
! /*
!  * Check group for a specific user.
!  */
! static int
! check_group(char *group, char *user)
! {
!     List       **line, *l;
!
!     if ((line = get_group_line(group)) != NULL)
      {
!         foreach(l, lnext(lnext(*line)))
!             if (strcmp(lfirst(l), user) == 0)
!                 return 1;
!     }

!     return 0;
! }
!
! /*
!  * Check comma user list for a specific user, handle group names.
!  */
! static int
! check_user(char *user, char *param_str)
! {
!     char *tok;
!
!     for (tok = strtok(param_str, ","); tok != NULL; tok = strtok(NULL, ","))
!     {
!         if (tok[0] == '+')
          {
!             if (check_group(tok+1, user))
!                 return 1;
!         }
!         else if (strcmp(tok, user) == 0 ||
!             strcmp(tok, "all") == 0)
!             return 1;
!     }

!     return 0;
! }
!
! /*
!  * Check to see if db/user combination matches param string.
!  */
! static int
! check_db(char *dbname, char *user, char *param_str)
! {
!     char *tok;
!
!     for (tok = strtok(param_str, ","); tok != NULL; tok = strtok(NULL, ","))
!     {
!         if (strcmp(tok, "all") == 0)
!             return 1;
!         else if (strcmp(tok, "sameuser") == 0)
!         {
!             if (strcmp(dbname, user) == 0)
!                 return 1;
          }
!         else if (strcmp(tok, "samegroup") == 0)
!         {
!             if (check_group(dbname, user))
!                 return 1;
!         }
!         else if (strcmp(tok, dbname) == 0)
!             return 1;
      }
+     return 0;
  }


***************
*** 278,283 ****
--- 520,526 ----
      int            line_number;
      char       *token;
      char       *db;
+     char       *user;

      Assert(line != NIL);
      line_number = lfirsti(line);
***************
*** 293,302 ****
              goto hba_syntax;
          db = lfirst(line);

!         /* Read the rest of the line. */
          line = lnext(line);
          if (!line)
              goto hba_syntax;
          parse_hba_auth(line, &port->auth_method, port->auth_arg, error_p);
          if (*error_p)
              goto hba_syntax;
--- 536,552 ----
              goto hba_syntax;
          db = lfirst(line);

!         /* Get the user. */
!         line = lnext(line);
!         if (!line)
!             goto hba_syntax;
!         user = lfirst(line);
!
          line = lnext(line);
          if (!line)
              goto hba_syntax;
+
+         /* Read the rest of the line. */
          parse_hba_auth(line, &port->auth_method, port->auth_arg, error_p);
          if (*error_p)
              goto hba_syntax;
***************
*** 308,322 ****
              port->auth_method == uaKrb5)
              goto hba_syntax;

!         /*
!          * If this record doesn't match the parameters of the connection
!          * attempt, ignore it.
!          */
!         if ((strcmp(db, port->database) != 0 &&
!              strcmp(db, "all") != 0 &&
!              (strcmp(db, "sameuser") != 0 ||
!               strcmp(port->database, port->user) != 0)) ||
!             port->raddr.sa.sa_family != AF_UNIX)
              return;
      }
      else if (strcmp(token, "host") == 0 || strcmp(token, "hostssl") == 0)
--- 558,564 ----
              port->auth_method == uaKrb5)
              goto hba_syntax;

!         if (port->raddr.sa.sa_family != AF_UNIX)
              return;
      }
      else if (strcmp(token, "host") == 0 || strcmp(token, "hostssl") == 0)
***************
*** 347,352 ****
--- 589,600 ----
              goto hba_syntax;
          db = lfirst(line);

+         /* Get the user. */
+         line = lnext(line);
+         if (!line)
+             goto hba_syntax;
+         user = lfirst(line);
+
          /* Read the IP address field. */
          line = lnext(line);
          if (!line)
***************
*** 371,391 ****
          if (*error_p)
              goto hba_syntax;

!         /*
!          * If this record doesn't match the parameters of the connection
!          * attempt, ignore it.
!          */
!         if ((strcmp(db, port->database) != 0 &&
!              strcmp(db, "all") != 0 &&
!              (strcmp(db, "sameuser") != 0 ||
!               strcmp(port->database, port->user) != 0)) ||
!             port->raddr.sa.sa_family != AF_INET ||
              ((file_ip_addr.s_addr ^ port->raddr.in.sin_addr.s_addr) & mask.s_addr) != 0)
              return;
      }
      else
          goto hba_syntax;

      /* Success */
      *found_p = true;
      return;
--- 619,637 ----
          if (*error_p)
              goto hba_syntax;

!         /* Must meet network restrictions */
!         if (port->raddr.sa.sa_family != AF_INET ||
              ((file_ip_addr.s_addr ^ port->raddr.in.sin_addr.s_addr) & mask.s_addr) != 0)
              return;
      }
      else
          goto hba_syntax;

+     if (!check_db(port->database, port->user, db))
+         return;
+     if (!check_user(port->user, user))
+         return;
+
      /* Success */
      *found_p = true;
      return;
***************
*** 430,435 ****
--- 676,802 ----
  }


+
+ /*
+  * Open the group file if possible (return NULL if not)
+  */
+ static FILE *
+ group_openfile(void)
+ {
+     char       *filename;
+     FILE       *groupfile;
+
+     filename = group_getfilename();
+     groupfile = AllocateFile(filename, "r");
+
+     if (groupfile == NULL && errno != ENOENT)
+         elog(LOG, "could not open %s: %m", filename);
+
+     pfree(filename);
+
+     return groupfile;
+ }
+
+
+
+ /*
+  * Open the password file if possible (return NULL if not)
+  */
+ static FILE *
+ user_openfile(void)
+ {
+     char       *filename;
+     FILE       *pwdfile;
+
+     filename = user_getfilename();
+     pwdfile = AllocateFile(filename, "r");
+
+     if (pwdfile == NULL && errno != ENOENT)
+         elog(LOG, "could not open %s: %m", filename);
+
+     pfree(filename);
+
+     return pwdfile;
+ }
+
+
+
+ /*
+  *     Load group/user name mapping file
+  */
+ void
+ load_group()
+ {
+     FILE       *group_file;
+     List        *line;
+
+     if (group_lines)
+         free_lines(&group_lines);
+
+     group_file = group_openfile();
+     if (!group_file)
+         return;
+     group_lines = tokenize_file(group_file);
+     FreeFile(group_file);
+
+     /* create sorted lines for binary searching */
+     if (group_sorted)
+         pfree(group_sorted);
+     group_length = length(group_lines);
+     if (group_length)
+     {
+         int i = 0;
+
+         group_sorted = palloc(group_length * sizeof(List *));
+
+         foreach(line, group_lines)
+             group_sorted[i++] = lfirst(line);
+
+         qsort((void *) group_sorted, group_length, sizeof(List *), user_group_cmp);
+     }
+     else
+         group_sorted = NULL;
+ }
+
+
+ /*
+  *     Load user/password mapping file
+  */
+ void
+ load_user()
+ {
+     FILE       *user_file;
+     List       *line;
+
+     if (user_lines)
+         free_lines(&user_lines);
+
+     user_file = user_openfile();
+     if (!user_file)
+         return;
+     user_lines = tokenize_file(user_file);
+     FreeFile(user_file);
+
+     /* create sorted lines for binary searching */
+     if (user_sorted)
+         pfree(user_sorted);
+     user_length = length(user_lines);
+     if (user_length)
+     {
+         int i = 0;
+
+         user_sorted = palloc(user_length * sizeof(List *));
+
+         foreach(line, user_lines)
+             user_sorted[i++] = lfirst(line);
+
+         qsort((void *) user_sorted, user_length, sizeof(List *), user_group_cmp);
+     }
+     else
+         user_sorted = NULL;
+ }
+
+
  /*
   * Read the config file and create a List of Lists of tokens in the file.
   * If we find a file by the old name of the config file (pg_hba), we issue
***************
*** 437,496 ****
   * follow directions and just installed his old hba file in the new database
   * system.
   */
! static void
  load_hba(void)
  {
!     int            fd,
!                 bufsize;
      FILE       *file;            /* The config file we have to read */
!     char       *old_conf_file;

      if (hba_lines)
          free_lines(&hba_lines);

!     /*
!      * The name of old config file that better not exist. Fail if config
!      * file by old name exists. Put together the full pathname to the old
!      * config file.
!      */
!     bufsize = (strlen(DataDir) + strlen(OLD_CONF_FILE) + 2) * sizeof(char);
!     old_conf_file = (char *) palloc(bufsize);
!     snprintf(old_conf_file, bufsize, "%s/%s", DataDir, OLD_CONF_FILE);
!
!     if ((fd = open(old_conf_file, O_RDONLY | PG_BINARY, 0)) != -1)
!     {
!         /* Old config file exists.    Tell this guy he needs to upgrade. */
!         close(fd);
!         elog(LOG, "A file exists by the name used for host-based authentication "
!              "in prior releases of Postgres (%s).  The name and format of "
!              "the configuration file have changed, so this file should be "
!              "converted.", old_conf_file);
      }
      else
      {
!         char       *conf_file;    /* The name of the config file we have to
!                                  * read */
!
!         /* put together the full pathname to the config file */
!         bufsize = (strlen(DataDir) + strlen(CONF_FILE) + 2) * sizeof(char);
!         conf_file = (char *) palloc(bufsize);
!         snprintf(conf_file, bufsize, "%s/%s", DataDir, CONF_FILE);
!
!         file = AllocateFile(conf_file, "r");
!         if (file == NULL)
!         {
!             /* The open of the config file failed.    */
!             elog(LOG, "load_hba: Unable to open authentication config file \"%s\": %m",
!                  conf_file);
!         }
!         else
!         {
!             hba_lines = tokenize_file(file);
!             FreeFile(file);
!         }
!         pfree(conf_file);
      }
!     pfree(old_conf_file);
  }


--- 804,838 ----
   * follow directions and just installed his old hba file in the new database
   * system.
   */
! void
  load_hba(void)
  {
!     int            bufsize;
      FILE       *file;            /* The config file we have to read */
!     char       *conf_file;    /* The name of the config file */

      if (hba_lines)
          free_lines(&hba_lines);

!     /* Put together the full pathname to the config file. */
!     bufsize = (strlen(DataDir) + strlen(CONF_FILE) + 2) * sizeof(char);
!     conf_file = (char *) palloc(bufsize);
!     snprintf(conf_file, bufsize, "%s/%s", DataDir, CONF_FILE);
!
!     file = AllocateFile(conf_file, "r");
!     if (file == NULL)
!     {
!         /* The open of the config file failed.    */
!         elog(LOG, "load_hba: Unable to open authentication config file \"%s\": %m",
!              conf_file);
!         pfree(conf_file);
      }
      else
      {
!         hba_lines = tokenize_file(file);
!         FreeFile(file);
      }
!     pfree(conf_file);
  }


***************
*** 606,612 ****
  /*
   * Read the ident config file and create a List of Lists of tokens in the file.
   */
! static void
  load_ident(void)
  {
      FILE       *file;            /* The map file we have to read */
--- 948,954 ----
  /*
   * Read the ident config file and create a List of Lists of tokens in the file.
   */
! void
  load_ident(void)
  {
      FILE       *file;            /* The map file we have to read */
***************
*** 622,628 ****
      map_file = (char *) palloc(bufsize);
      snprintf(map_file, bufsize, "%s/%s", DataDir, USERMAP_FILE);

!     file = AllocateFile(map_file, PG_BINARY_R);
      if (file == NULL)
      {
          /* The open of the map file failed.  */
--- 964,970 ----
      map_file = (char *) palloc(bufsize);
      snprintf(map_file, bufsize, "%s/%s", DataDir, USERMAP_FILE);

!     file = AllocateFile(map_file, "r");
      if (file == NULL)
      {
          /* The open of the map file failed.  */
***************
*** 640,647 ****

  /*
   *    Parse the string "*ident_response" as a response from a query to an Ident
!  *    server.  If it's a normal response indicating a username, return true
!  *    and store the username at *ident_user.    If it's anything else,
   *    return false.
   */
  static bool
--- 982,989 ----

  /*
   *    Parse the string "*ident_response" as a response from a query to an Ident
!  *    server.  If it's a normal response indicating a user name, return true
!  *    and store the user name at *ident_user.    If it's anything else,
   *    return false.
   */
  static bool
***************
*** 708,714 ****
                          cursor++;        /* Go over colon */
                          while (isblank(*cursor))
                              cursor++;    /* skip blanks */
!                         /* Rest of line is username.  Copy it over. */
                          i = 0;
                          while (*cursor != '\r' && i < IDENT_USERNAME_MAX)
                              ident_user[i++] = *cursor++;
--- 1050,1056 ----
                          cursor++;        /* Go over colon */
                          while (isblank(*cursor))
                              cursor++;    /* skip blanks */
!                         /* Rest of line is user name.  Copy it over. */
                          i = 0;
                          while (*cursor != '\r' && i < IDENT_USERNAME_MAX)
                              ident_user[i++] = *cursor++;
***************
*** 725,731 ****
  /*
   *    Talk to the ident server on host "remote_ip_addr" and find out who
   *    owns the tcp connection from his port "remote_port" to port
!  *    "local_port_addr" on host "local_ip_addr".    Return the username the
   *    ident server gives as "*ident_user".
   *
   *    IP addresses and port numbers are in network byte order.
--- 1067,1073 ----
  /*
   *    Talk to the ident server on host "remote_ip_addr" and find out who
   *    owns the tcp connection from his port "remote_port" to port
!  *    "local_port_addr" on host "local_ip_addr".    Return the user name the
   *    ident server gives as "*ident_user".
   *
   *    IP addresses and port numbers are in network byte order.
***************
*** 955,960 ****
--- 1297,1304 ----
  #endif
  }

+
+
  /*
   *    Determine the username of the initiator of the connection described
   *    by "port".    Then look in the usermap file under the usermap
***************
*** 1010,1220 ****
          return STATUS_ERROR;
  }

- /*
-  * Clear and reload tokenized file contents.
-  */
- void
- load_hba_and_ident(void)
- {
-     load_hba();
-     load_ident();
- }
-
-
- /* Character set stuff.  Not sure it really belongs in this file. */
-
- #ifdef CYR_RECODE
-
- #define CHARSET_FILE "charset.conf"
- #define MAX_CHARSETS   10
- #define KEY_HOST       1
- #define KEY_BASE       2
- #define KEY_TABLE       3
-
- struct CharsetItem
- {
-     char        Orig[MAX_TOKEN];
-     char        Dest[MAX_TOKEN];
-     char        Table[MAX_TOKEN];
- };
-
-
- static bool
- CharSetInRange(char *buf, int host)
- {
-     int            valid,
-                 i,
-                 FromAddr,
-                 ToAddr,
-                 tmp;
-     struct in_addr file_ip_addr;
-     char       *p;
-     unsigned int one = 0x80000000,
-                 NetMask = 0;
-     unsigned char mask;
-
-     p = strchr(buf, '/');
-     if (p)
-     {
-         *p++ = '\0';
-         valid = inet_aton(buf, &file_ip_addr);
-         if (valid)
-         {
-             mask = strtoul(p, 0, 0);
-             FromAddr = ntohl(file_ip_addr.s_addr);
-             ToAddr = ntohl(file_ip_addr.s_addr);
-             for (i = 0; i < mask; i++)
-             {
-                 NetMask |= one;
-                 one >>= 1;
-             }
-             FromAddr &= NetMask;
-             ToAddr = ToAddr | ~NetMask;
-             tmp = ntohl(host);
-             return ((unsigned) tmp >= (unsigned) FromAddr &&
-                     (unsigned) tmp <= (unsigned) ToAddr);
-         }
-     }
-     else
-     {
-         p = strchr(buf, '-');
-         if (p)
-         {
-             *p++ = '\0';
-             valid = inet_aton(buf, &file_ip_addr);
-             if (valid)
-             {
-                 FromAddr = ntohl(file_ip_addr.s_addr);
-                 valid = inet_aton(p, &file_ip_addr);
-                 if (valid)
-                 {
-                     ToAddr = ntohl(file_ip_addr.s_addr);
-                     tmp = ntohl(host);
-                     return ((unsigned) tmp >= (unsigned) FromAddr &&
-                             (unsigned) tmp <= (unsigned) ToAddr);
-                 }
-             }
-         }
-         else
-         {
-             valid = inet_aton(buf, &file_ip_addr);
-             if (valid)
-             {
-                 FromAddr = file_ip_addr.s_addr;
-                 return (unsigned) FromAddr == (unsigned) host;
-             }
-         }
-     }
-     return false;
- }
-
- void
- GetCharSetByHost(char *TableName, int host, const char *DataDir)
- {
-     FILE       *file;
-     char        buf[MAX_TOKEN],
-                 BaseCharset[MAX_TOKEN],
-                 OrigCharset[MAX_TOKEN],
-                 DestCharset[MAX_TOKEN],
-                 HostCharset[MAX_TOKEN],
-                *map_file;
-     int            key,
-                 ChIndex = 0,
-                 c,
-                 i,
-                 bufsize;
-     struct CharsetItem *ChArray[MAX_CHARSETS];
-
-     *TableName = '\0';
-     bufsize = (strlen(DataDir) + strlen(CHARSET_FILE) + 2) * sizeof(char);
-     map_file = (char *) palloc(bufsize);
-     snprintf(map_file, bufsize, "%s/%s", DataDir, CHARSET_FILE);
-     file = AllocateFile(map_file, PG_BINARY_R);
-     pfree(map_file);
-     if (file == NULL)
-     {
-         /* XXX should we log a complaint? */
-         return;
-     }
-     while ((c = getc(file)) != EOF)
-     {
-         if (c == '#')
-             read_through_eol(file);
-         else
-         {
-             /* Read the key */
-             ungetc(c, file);
-             next_token(file, buf, sizeof(buf));
-             if (buf[0] != '\0')
-             {
-                 key = 0;
-                 if (strcasecmp(buf, "HostCharset") == 0)
-                     key = KEY_HOST;
-                 if (strcasecmp(buf, "BaseCharset") == 0)
-                     key = KEY_BASE;
-                 if (strcasecmp(buf, "RecodeTable") == 0)
-                     key = KEY_TABLE;
-                 switch (key)
-                 {
-                     case KEY_HOST:
-                         /* Read the host */
-                         next_token(file, buf, sizeof(buf));
-                         if (buf[0] != '\0')
-                         {
-                             if (CharSetInRange(buf, host))
-                             {
-                                 /* Read the charset */
-                                 next_token(file, buf, sizeof(buf));
-                                 if (buf[0] != '\0')
-                                     strcpy(HostCharset, buf);
-                             }
-                         }
-                         break;
-                     case KEY_BASE:
-                         /* Read the base charset */
-                         next_token(file, buf, sizeof(buf));
-                         if (buf[0] != '\0')
-                             strcpy(BaseCharset, buf);
-                         break;
-                     case KEY_TABLE:
-                         /* Read the original charset */
-                         next_token(file, buf, sizeof(buf));
-                         if (buf[0] != '\0')
-                         {
-                             strcpy(OrigCharset, buf);
-                             /* Read the destination charset */
-                             next_token(file, buf, sizeof(buf));
-                             if (buf[0] != '\0')
-                             {
-                                 strcpy(DestCharset, buf);
-                                 /* Read the table filename */
-                                 next_token(file, buf, sizeof(buf));
-                                 if (buf[0] != '\0')
-                                 {
-                                     ChArray[ChIndex] =
-                                         (struct CharsetItem *) palloc(sizeof(struct CharsetItem));
-                                     strcpy(ChArray[ChIndex]->Orig, OrigCharset);
-                                     strcpy(ChArray[ChIndex]->Dest, DestCharset);
-                                     strcpy(ChArray[ChIndex]->Table, buf);
-                                     ChIndex++;
-                                 }
-                             }
-                         }
-                         break;
-                 }
-                 read_through_eol(file);
-             }
-         }
-     }
-     FreeFile(file);
-
-     for (i = 0; i < ChIndex; i++)
-     {
-         if (strcasecmp(BaseCharset, ChArray[i]->Orig) == 0 &&
-             strcasecmp(HostCharset, ChArray[i]->Dest) == 0)
-             strncpy(TableName, ChArray[i]->Table, 79);
-         pfree(ChArray[i]);
-     }
- }
-
- #endif   /* CYR_RECODE */
--- 1354,1356 ----
Index: src/backend/libpq/password.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/libpq/password.c,v
retrieving revision 1.41
diff -c -r1.41 password.c
*** src/backend/libpq/password.c    4 Mar 2002 01:46:03 -0000    1.41
--- src/backend/libpq/password.c    3 Apr 2002 03:48:44 -0000
***************
*** 33,39 ****
      strcat(pw_file_fullname, "/");
      strcat(pw_file_fullname, port->auth_arg);

!     pw_file = AllocateFile(pw_file_fullname, PG_BINARY_R);
      if (!pw_file)
      {
          elog(LOG, "verify_password: Unable to open password file \"%s\": %m",
--- 33,39 ----
      strcat(pw_file_fullname, "/");
      strcat(pw_file_fullname, port->auth_arg);

!     pw_file = AllocateFile(pw_file_fullname, "r");
      if (!pw_file)
      {
          elog(LOG, "verify_password: Unable to open password file \"%s\": %m",
Index: src/backend/libpq/pg_hba.conf.sample
===================================================================
RCS file: /cvsroot/pgsql/src/backend/libpq/pg_hba.conf.sample,v
retrieving revision 1.35
diff -c -r1.35 pg_hba.conf.sample
*** src/backend/libpq/pg_hba.conf.sample    8 Mar 2002 20:36:58 -0000    1.35
--- src/backend/libpq/pg_hba.conf.sample    3 Apr 2002 03:48:48 -0000
***************
*** 42,63 ****
  #
  # Format:
  #
! #   host  DBNAME  IP_ADDRESS  ADDRESS_MASK  AUTH_TYPE  [AUTH_ARGUMENT]
  #
! # DBNAME can be:
! #     o a database name
! #     o "all", which means the record matches all databases
! #   o "sameuser", which means users can only access databases whose name
! #     is the same as their username
  #
! # IP_ADDRESS and ADDRESS_MASK are standard dotted decimal IP address and
  # mask values. IP addresses can only be specified numerically, not as
  # domain or host names.
  #
  # Do not prevent the superuser from accessing the template1 database.
  # Various utility commands need access to template1.
  #
! # AUTH_TYPE and AUTH_ARGUMENT are described below.
  #
  #
  # hostssl
--- 42,77 ----
  #
  # Format:
  #
! #   host       DATABASE    USER      IP_ADDRESS    MASK               AUTH_TYPE
  #
! # DATABASE can be:
! #    o a database name
! #    o "sameuser", which means a user can only access a database with the
! #      same name as their user name
! #    o "samegroup", which means a user can only access databases when they
! #      are members of a group with the same name as the database name
! #    o "all", which matches all databases
! #    o a list of database names, separated by commas
! #    o a file name containing database names, starting with '@'
! #
! # USER can be:
! #     o a user name
! #     o "all", which matches all users
! #    o a list of user names, separated by commas
! #    o a group name, starting with '+'
! #    o a file name containing user names, starting with '@'
  #
! # Files read using '@' can contain comma-separated database/user names,
! # or one name per line.  The files can also contain comments using '#'.
! #
! # IP_ADDRESS and MASK are standard dotted decimal IP address and
  # mask values. IP addresses can only be specified numerically, not as
  # domain or host names.
  #
  # Do not prevent the superuser from accessing the template1 database.
  # Various utility commands need access to template1.
  #
! # AUTH_TYPE is described below.
  #
  #
  # hostssl
***************
*** 65,74 ****
  #
  # The format of this record is identical to "host".
  #
! #
! #
! # It specifies hosts that required connection via secure SSL. "host"
! # records allow SSL connections too, but "hostssl" only allows SSL-secured
  # connections.
  #
  # This keyword is only available if the server was compiled with SSL
--- 79,86 ----
  #
  # The format of this record is identical to "host".
  #
! # It specifies hosts that require connection via secure SSL. "host"
! # allows SSL connections too, but "hostssl" requires SSL-secured
  # connections.
  #
  # This keyword is only available if the server was compiled with SSL
***************
*** 82,91 ****
  # connections. Without this record, UNIX-socket connections are disallowed
  #
  # Format:
! #   local  DBNAME  AUTH_TYPE  [AUTH_ARGUMENT]
  #
  # This format is identical to the "host" record type except there are no
! # IP_ADDRESS and ADDRESS_MASK fields.
  #
  #
  #
--- 94,103 ----
  # connections. Without this record, UNIX-socket connections are disallowed
  #
  # Format:
! #   local      DATABASE    USER      AUTH_TYPE
  #
  # This format is identical to the "host" record type except there are no
! # IP_ADDRESS and MASK fields.
  #
  #
  #
***************
*** 96,152 ****
  # has an AUTH_TYPE.
  #
  #   trust:
! #        No authentication is done. Any valid username is accepted,
  #         including the PostgreSQL superuser. This option should
  #         be used only for hosts where all users are trusted.
  #
- #   password:
- #        Authentication is done by matching a password supplied
- #        in clear by the host. If no AUTH_ARGUMENT is used, the
- #        password is compared with the user's entry in the
- #        pg_shadow table.
- #
- #         If AUTH_ARGUMENT is specified, the username is looked up
- #         in that file in the $PGDATA directory. If the username
- #         is found but there is no password, the password is looked
- #         up in pg_shadow. If a password exists in the file, it is
- #         used instead. These secondary files allow fine-grained
- #         control over who can access which databases and whether
- #         a non-default password is required. The same file can be
- #         used in multiple records for easier administration.
- #         Password files can be maintained with the pg_passwd(1)
- #         utility. Remember, these passwords override pg_shadow
- #         passwords.  Also, such passwords are passed over the network
- #        in cleartext, meaning this should not be used on untrusted
- #        networks.
- #
  #   md5:
! #          Same as "password", except the password is encrypted over the
! #          network. This method is preferable to "password" and "crypt"
! #          except for pre-7.2 clients that don't support it. NOTE: md5 can
! #          use usernames stored in secondary password files but ignores
! #          passwords stored there. The pg_shadow password will always be
! #          used.
  #
  #   crypt:
! #          Same as "md5", but uses crypt for pre-7.2 clients.  You can
! #        not store encrypted passwords in pg_shadow if you use this
! #        method.
  #
  #   ident:
  #        For TCP/IP connections, authentication is done by contacting the
  #        ident server on the client host. This is only as secure as the
! #        client machine. On machines that support unix-domain socket
! #        credentials (currently Linux, FreeBSD, NetBSD, and BSD/OS), this
! #        method also works for "local" connections.
! #
! #        AUTH_ARGUMENT is required. It determines how to map remote user
! #        names to PostgreSQL user names. If you use "sameuser", the user
! #        names are assumed to be the identical. If not, AUTH_ARGUMENT is
! #        assumed to be a map name found in the $PGDATA/pg_ident.conf
! #        file. The connection is accepted if that file contains an entry
! #        for this map name with the ident-supplied username and the
! #        requested PostgreSQL username.
  #
  #   krb4:
  #        Kerberos V4 authentication is used.  Allowed only for
--- 108,145 ----
  # has an AUTH_TYPE.
  #
  #   trust:
! #        No authentication is done. Any valid user name is accepted,
  #         including the PostgreSQL superuser. This option should
  #         be used only for hosts where all users are trusted.
  #
  #   md5:
! #          Requires the client to supply an MD5 encrypted password for
! #        authentication.  This is the only method that allows encrypted
! #        passwords to be stored in pg_shadow.
  #
  #   crypt:
! #          Same as "md5", but uses crypt for pre-7.2 clients.
  #
+ #   password:
+ #        Same as "md5", but the password is sent in cleartext over
+ #        the network.  This should not be used on untrusted
+ #        networks.
+ #
  #   ident:
  #        For TCP/IP connections, authentication is done by contacting the
  #        ident server on the client host. This is only as secure as the
! #        client machine. You must specify the map name after the 'ident'
! #        keyword. It determines how to map remote user names to
! #        PostgreSQL user names. If you use "sameuser", the user names are
! #        assumed to be identical. If not, the map name is looked up
! #        in the $PGDATA/pg_ident.conf file. The connection is accepted if
! #        that file contains an entry for this map name with the
! #        ident-supplied username and the requested PostgreSQL username.
! #
! #        On machines that support unix-domain socket credentials
! #        (currently Linux, FreeBSD, NetBSD, and BSD/OS), ident allows
! #        reliable authentication of 'local' connections without ident
! #        running on the local machine.
  #
  #   krb4:
  #        Kerberos V4 authentication is used.  Allowed only for
***************
*** 157,166 ****
  #        TCP/IP connections, not for local UNIX-domain sockets.
  #
  #   pam:
! #        Authentication is passed off to PAM (PostgreSQL must be
! #        configured --with-pam), using the default service name
! #        "postgresql" - you can specify your own service name by
! #        setting AUTH_ARGUMENT to the desired service name.
  #
  #   reject:
  #         Reject the connection. This is used to reject certain hosts
--- 150,159 ----
  #        TCP/IP connections, not for local UNIX-domain sockets.
  #
  #   pam:
! #        Authentication is done by PAM using the default service name
! #        "postgresql". You can specify your own service name by adding
! #        the service name after the 'pam' keyword. To use this option,
! #        PostgreSQL must be configured --with-pam.
  #
  #   reject:
  #         Reject the connection. This is used to reject certain hosts
***************
*** 177,236 ****
  # Allow any user on the local system to connect to any database under any
  # username using Unix-domain sockets (the default for local connections):
  #
! # TYPE       DATABASE    IP_ADDRESS    MASK               AUTH_TYPE  AUTH_ARGUMENT
! # local      all                                          trust
  #
  # The same using local loopback TCP/IP connections:
  #
! # TYPE       DATABASE    IP_ADDRESS    MASK               AUTH_TYPE  AUTH_ARGUMENT
! # host       all         127.0.0.1     255.255.255.255    trust
  #
  # Allow any user from any host with IP address 192.168.93.x to
  # connect to database "template1" as the same username that ident reports
  # for the connection (typically his Unix username):
  #
! # TYPE       DATABASE    IP_ADDRESS    MASK               AUTH_TYPE  AUTH_ARGUMENT
! # host       template1   192.168.93.0  255.255.255.0      ident      sameuser
  #
  # Allow a user from host 192.168.12.10 to connect to database "template1"
! # if the user's password in pg_shadow is correctly supplied:
  #
! # TYPE       DATABASE    IP_ADDRESS    MASK               AUTH_TYPE  AUTH_ARGUMENT
! # host       template1   192.168.12.10 255.255.255.255    md5
  #
  # In the absence of preceding "host" lines, these two lines will reject
  # all connection from 192.168.54.1 (since that entry will be matched
  # first), but allow Kerberos V5 connections from anywhere else on the
  # Internet. The zero mask means that no bits of the host IP address are
! # considered, so it matches any host:
  #
  #
! # TYPE       DATABASE    IP_ADDRESS    MASK               AUTH_TYPE  AUTH_ARGUMENT
! # host       all        192.168.54.1   255.255.255.255    reject
! # host       all        0.0.0.0        0.0.0.0            krb5
  #
  # Allow users from 192.168.x.x hosts to connect to any database if they
  # pass the ident check. For example, if ident says the user is "james" and
  # he requests to connect as PostgreSQL user "guest", the connection is
  # allowed if there is an entry in $PGDATA/pg_ident.conf with map name
  # "phoenix" that says "james" is allowed to connect as "guest":
  #
! # TYPE       DATABASE    IP_ADDRESS    MASK               AUTH_TYPE  AUTH_ARGUMENT
! # host       all        192.168.0.0    255.255.0.0        ident      phoenix
  #
- # If these are the only two lines for local connections, they will allow
- # local users to connect only to their own databases (databases with the
- # same name as their user name) except for administrators who may connect
- # to all databases. The file $PGDATA/admins lists the user names who are
- # permitted to connect to all databases. Passwords are required in all
- # cases. (If you prefer to use ident authorization, an ident map can
- # serve a parallel purpose to the password list file used here.)
- #
- # TYPE       DATABASE    IP_ADDRESS    MASK               AUTH_TYPE  AUTH_ARGUMENT
- # local      sameuser                                     md5
- # local      all                                          md5  admins
  #
- # See $PGDATA/pg_ident.conf for more information on Ident maps.
  #
  #
  #
--- 170,239 ----
  # Allow any user on the local system to connect to any database under any
  # username using Unix-domain sockets (the default for local connections):
  #
! # TYPE       DATABASE    USER       IP_ADDRESS    MASK               AUTH_TYPE
! # local      all         all                                         trust
  #
  # The same using local loopback TCP/IP connections:
  #
! # TYPE      DATABASE     USER    IP_ADDRESS    MASK               AUTH_TYPE
! # host      all          all     127.0.0.1     255.255.255.255    trust
  #
  # Allow any user from any host with IP address 192.168.93.x to
  # connect to database "template1" as the same username that ident reports
  # for the connection (typically his Unix username):
  #
! # TYPE       DATABASE    USER    IP_ADDRESS    MASK               AUTH_TYPE
! # host       template1   all     192.168.93.0  255.255.255.0      ident sameuser
  #
  # Allow a user from host 192.168.12.10 to connect to database "template1"
! # if the user's password is correctly supplied:
  #
! # TYPE       DATABASE    USER     IP_ADDRESS    MASK               AUTH_TYPE
! # host       template1   all      192.168.12.10 255.255.255.255    md5
  #
  # In the absence of preceding "host" lines, these two lines will reject
  # all connection from 192.168.54.1 (since that entry will be matched
  # first), but allow Kerberos V5 connections from anywhere else on the
  # Internet. The zero mask means that no bits of the host IP address are
! # considered so it matches any host:
  #
  #
! # TYPE       DATABASE    USER     IP_ADDRESS    MASK               AUTH_TYPE
! # host       all         all      192.168.54.1  255.255.255.255    reject
! # host       all         all      0.0.0.0       0.0.0.0            krb5
  #
  # Allow users from 192.168.x.x hosts to connect to any database if they
  # pass the ident check. For example, if ident says the user is "james" and
  # he requests to connect as PostgreSQL user "guest", the connection is
  # allowed if there is an entry in $PGDATA/pg_ident.conf with map name
  # "phoenix" that says "james" is allowed to connect as "guest":
+ # See $PGDATA/pg_ident.conf for more information on Ident maps.
  #
! # TYPE       DATABASE    USER     IP_ADDRESS    MASK               AUTH_TYPE
! # host       all         all      192.168.0.0    255.255.0.0       ident phoenix
! #
! # If these are the only three lines for local connections, they will
! # allow local users to connect only to their own databases (databases
! # with the same name as their user name) except for administrators and
! # members of group 'support' who may connect to all databases . The file
! # $PGDATA/admins contains a list of user names. Passwords are required in
! # all cases.
! #
! # TYPE       DATABASE    USER      IP_ADDRESS    MASK               AUTH_TYPE
! # local      sameuser    all                                        md5
! # local      all         @admins                                    md5
! # local      all         +support                                   md5
! #
! # The last two lines above can be combined into a single line:
! #
! # local      all         @admins,+support                           md5
! #
! # The database column can also use lists and file names, but not groups:
! #
! # local      db1,db2,@demodbs  all                                  md5
! #
  #
  #
  #
  #
  #
***************
*** 250,256 ****
  # configuration is probably too liberal for you. Change it to use
  # something other than "trust" authentication.
  #
! # TYPE     DATABASE    IP_ADDRESS    MASK               AUTH_TYPE  AUTH_ARGUMENT

! local      all                                          trust
! host       all         127.0.0.1     255.255.255.255    trust
--- 253,259 ----
  # configuration is probably too liberal for you. Change it to use
  # something other than "trust" authentication.
  #
! # TYPE       DATABASE      USER      IP_ADDRESS    MASK               AUTH_TYPE

! local        all           all                                        trust
! host         all           all       127.0.0.1     255.255.255.255    trust
Index: src/backend/postmaster/postmaster.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v
retrieving revision 1.271
diff -c -r1.271 postmaster.c
*** src/backend/postmaster/postmaster.c    15 Mar 2002 19:20:35 -0000    1.271
--- src/backend/postmaster/postmaster.c    3 Apr 2002 03:48:52 -0000
***************
*** 748,755 ****
      /*
       * Load cached files for client authentication.
       */
!     load_hba_and_ident();
!     load_password_cache();

      /*
       * We're ready to rock and roll...
--- 748,757 ----
      /*
       * Load cached files for client authentication.
       */
!     load_hba();
!     load_ident();
!     load_user();
!     load_group();

      /*
       * We're ready to rock and roll...
***************
*** 1389,1395 ****
          elog(LOG, "Received SIGHUP, reloading configuration files");
          SignalChildren(SIGHUP);
          ProcessConfigFile(PGC_SIGHUP);
!         load_hba_and_ident();
      }

      PG_SETMASK(&UnBlockSig);
--- 1391,1398 ----
          elog(LOG, "Received SIGHUP, reloading configuration files");
          SignalChildren(SIGHUP);
          ProcessConfigFile(PGC_SIGHUP);
!         load_hba();
!         load_ident();
      }

      PG_SETMASK(&UnBlockSig);
***************
*** 2288,2296 ****
      if (CheckPostmasterSignal(PMSIGNAL_PASSWORD_CHANGE))
      {
          /*
!          * Password file has changed.
           */
!         load_password_cache();
      }

      if (CheckPostmasterSignal(PMSIGNAL_WAKEN_CHILDREN))
--- 2291,2300 ----
      if (CheckPostmasterSignal(PMSIGNAL_PASSWORD_CHANGE))
      {
          /*
!          * Password or group file has changed.
           */
!         load_user();
!         load_group();
      }

      if (CheckPostmasterSignal(PMSIGNAL_WAKEN_CHILDREN))
Index: src/backend/utils/adt/quote.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/utils/adt/quote.c,v
retrieving revision 1.6
diff -c -r1.6 quote.c
*** src/backend/utils/adt/quote.c    28 Oct 2001 06:25:53 -0000    1.6
--- src/backend/utils/adt/quote.c    3 Apr 2002 03:48:52 -0000
***************
*** 124,131 ****
      {
          if (*cp1 == '"')
              *cp2++ = '"';
-         if (*cp1 == '\\')
-             *cp2++ = '\\';
          *cp2++ = *cp1++;
      }
      *cp2++ = '"';
--- 124,129 ----
***************
*** 234,241 ****

          if (*cp1 == '"')
              *cp2++ = '"';
-         if (*cp1 == '\\')
-             *cp2++ = '\\';
          *cp2++ = *cp1++;

          len--;
--- 232,237 ----
Index: src/backend/utils/init/miscinit.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/utils/init/miscinit.c,v
retrieving revision 1.85
diff -c -r1.85 miscinit.c
*** src/backend/utils/init/miscinit.c    4 Mar 2002 04:45:27 -0000    1.85
--- src/backend/utils/init/miscinit.c    3 Apr 2002 03:48:53 -0000
***************
*** 236,320 ****

  #ifdef CYR_RECODE

- #define MAX_TOKEN    80
-
- /*
-  * Some standard C libraries, including GNU, have an isblank() function.
-  * Others, including Solaris, do not.  So we have our own.
-  */
- static bool
- isblank(const char c)
- {
-     return c == ' ' || c == '\t';
- }
-
-
- /*
-  *    Grab one token out of fp.  Tokens are strings of non-blank
-  *    characters bounded by blank characters, beginning of line, and end
-  *    of line.    Blank means space or tab.  Return the token as *buf.
-  *    Leave file positioned to character immediately after the token or
-  *    EOF, whichever comes first.  If no more tokens on line, return null
-  *    string as *buf and position file to beginning of next line or EOF,
-  *    whichever comes first.
-  */
- static void
- next_token(FILE *fp, char *buf, const int bufsz)
- {
-     int            c;
-     char       *eb = buf + (bufsz - 1);
-
-     /* Move over initial token-delimiting blanks */
-     while ((c = getc(fp)) != EOF && isblank(c))
-         ;
-
-     if (c != EOF && c != '\n')
-     {
-         /*
-          * build a token in buf of next characters up to EOF, eol, or
-          * blank.  If the token gets too long, we still parse it
-          * correctly, but the excess characters are not stored into *buf.
-          */
-         while (c != EOF && c != '\n' && !isblank(c))
-         {
-             if (buf < eb)
-                 *buf++ = c;
-             c = getc(fp);
-         }
-
-         /*
-          * Put back the char right after the token (critical in case it is
-          * eol, since we need to detect end-of-line at next call).
-          */
-         if (c != EOF)
-             ungetc(c, fp);
-     }
-     *buf = '\0';
- }
-
-
- static void
- read_through_eol(FILE *file)
- {
-     int            c;
-
-     while ((c = getc(file)) != EOF && c != '\n')
-         ;
- }
-
-
- void
  SetCharSet(void)
  {
      FILE       *file;
!     char       *p;
      char       *map_file;
      char        buf[MAX_TOKEN];
      int            i,
                  c;
      unsigned char FromChar,
                  ToChar;
!     char        ChTable[80];

      for (i = 0; i < 128; i++)
      {
--- 236,252 ----

  #ifdef CYR_RECODE

  SetCharSet(void)
  {
      FILE       *file;
!     char       *filename;
      char       *map_file;
      char        buf[MAX_TOKEN];
      int            i,
                  c;
      unsigned char FromChar,
                  ToChar;
!     char        ChTable[MAX_TOKEN];

      for (i = 0; i < 128; i++)
      {
***************
*** 325,363 ****
      if (IsUnderPostmaster)
      {
          GetCharSetByHost(ChTable, MyProcPort->raddr.in.sin_addr.s_addr, DataDir);
!         p = ChTable;
      }
      else
!         p = getenv("PG_RECODETABLE");

!     if (p && *p != '\0')
      {
!         map_file = palloc(strlen(DataDir) + strlen(p) + 2);
!         sprintf(map_file, "%s/%s", DataDir, p);
!         file = AllocateFile(map_file, PG_BINARY_R);
          pfree(map_file);
          if (file == NULL)
              return;
!         while ((c = getc(file)) != EOF)
          {
!             if (c == '#')
!                 read_through_eol(file);
!             else
              {
!                 /* Read the FromChar */
!                 ungetc(c, file);
                  next_token(file, buf, sizeof(buf));
                  if (buf[0] != '\0')
                  {
!                     FromChar = strtoul(buf, 0, 0);
!                     /* Read the ToChar */
!                     next_token(file, buf, sizeof(buf));
!                     if (buf[0] != '\0')
                      {
!                         ToChar = strtoul(buf, 0, 0);
!                         RecodeForwTable[FromChar - 128] = ToChar;
!                         RecodeBackTable[ToChar - 128] = FromChar;
!                         read_through_eol(file);
                      }
                  }
              }
--- 257,296 ----
      if (IsUnderPostmaster)
      {
          GetCharSetByHost(ChTable, MyProcPort->raddr.in.sin_addr.s_addr, DataDir);
!         filename = ChTable;
      }
      else
!         filename = getenv("PG_RECODETABLE");

!     if (filename && *filename != '\0')
      {
!         map_file = palloc(strlen(DataDir) + strlen(filename) + 2);
!         sprintf(map_file, "%s/%s", DataDir, filename);
!         file = AllocateFile(map_file, "r");
          pfree(map_file);
          if (file == NULL)
              return;
!
!         while (!feof(file))
          {
!             next_token(file, buf, sizeof(buf));
!             if (buf[0] != '\0')
              {
!                 FromChar = strtoul(buf, 0, 0);
!                 /* Read the ToChar */
                  next_token(file, buf, sizeof(buf));
                  if (buf[0] != '\0')
                  {
!                     ToChar = strtoul(buf, 0, 0);
!                     RecodeForwTable[FromChar - 128] = ToChar;
!                     RecodeBackTable[ToChar - 128] = FromChar;
!
!                     /* read to EOL */
!                     while (!feof(file) && buf[0])
                      {
!                         next_token(file, buf, sizeof(buf));
!                         elog(LOG, "SetCharSet: unknown tag %s in file %s"
!                             buf, filename);
                      }
                  }
              }
***************
*** 366,371 ****
--- 299,305 ----
      }
  }

+
  char *
  convertstr(unsigned char *buff, int len, int dest)
  {
***************
*** 384,390 ****
      }
      return ch;
  }
! #endif



--- 318,523 ----
      }
      return ch;
  }
!
! #define CHARSET_FILE "charset.conf"
! #define MAX_CHARSETS   10
! #define KEY_HOST       1
! #define KEY_BASE       2
! #define KEY_TABLE       3
!
! struct CharsetItem
! {
!     char        Orig[MAX_TOKEN];
!     char        Dest[MAX_TOKEN];
!     char        Table[MAX_TOKEN];
! };
!
!
! static bool
! CharSetInRange(char *buf, int host)
! {
!     int            valid,
!                 i,
!                 FromAddr,
!                 ToAddr,
!                 tmp;
!     struct in_addr file_ip_addr;
!     char       *p;
!     unsigned int one = 0x80000000,
!                 NetMask = 0;
!     unsigned char mask;
!
!     p = strchr(buf, '/');
!     if (p)
!     {
!         *p++ = '\0';
!         valid = inet_aton(buf, &file_ip_addr);
!         if (valid)
!         {
!             mask = strtoul(p, 0, 0);
!             FromAddr = ntohl(file_ip_addr.s_addr);
!             ToAddr = ntohl(file_ip_addr.s_addr);
!             for (i = 0; i < mask; i++)
!             {
!                 NetMask |= one;
!                 one >>= 1;
!             }
!             FromAddr &= NetMask;
!             ToAddr = ToAddr | ~NetMask;
!             tmp = ntohl(host);
!             return ((unsigned) tmp >= (unsigned) FromAddr &&
!                     (unsigned) tmp <= (unsigned) ToAddr);
!         }
!     }
!     else
!     {
!         p = strchr(buf, '-');
!         if (p)
!         {
!             *p++ = '\0';
!             valid = inet_aton(buf, &file_ip_addr);
!             if (valid)
!             {
!                 FromAddr = ntohl(file_ip_addr.s_addr);
!                 valid = inet_aton(p, &file_ip_addr);
!                 if (valid)
!                 {
!                     ToAddr = ntohl(file_ip_addr.s_addr);
!                     tmp = ntohl(host);
!                     return ((unsigned) tmp >= (unsigned) FromAddr &&
!                             (unsigned) tmp <= (unsigned) ToAddr);
!                 }
!             }
!         }
!         else
!         {
!             valid = inet_aton(buf, &file_ip_addr);
!             if (valid)
!             {
!                 FromAddr = file_ip_addr.s_addr;
!                 return (unsigned) FromAddr == (unsigned) host;
!             }
!         }
!     }
!     return false;
! }
!
!
! static void
! GetCharSetByHost(char *TableName, int host, const char *DataDir)
! {
!     FILE       *file;
!     char        buf[MAX_TOKEN],
!                 BaseCharset[MAX_TOKEN],
!                 OrigCharset[MAX_TOKEN],
!                 DestCharset[MAX_TOKEN],
!                 HostCharset[MAX_TOKEN],
!                *map_file;
!     int            key,
!                 ChIndex = 0,
!                 c,
!                 i,
!                 bufsize;
!     struct CharsetItem *ChArray[MAX_CHARSETS];
!
!     *TableName = '\0';
!     bufsize = (strlen(DataDir) + strlen(CHARSET_FILE) + 2) * sizeof(char);
!     map_file = (char *) palloc(bufsize);
!     snprintf(map_file, bufsize, "%s/%s", DataDir, CHARSET_FILE);
!     file = AllocateFile(map_file, "r");
!     pfree(map_file);
!     if (file == NULL)
!     {
!         /* XXX should we log a complaint? */
!         return;
!     }
!
!     while (!feof(file))
!     {
!         next_token(file, buf, sizeof(buf));
!         if (buf[0] != '\0')
!         {
!             key = 0;
!             if (strcasecmp(buf, "HostCharset") == 0)
!                 key = KEY_HOST;
!             else if (strcasecmp(buf, "BaseCharset") == 0)
!                 key = KEY_BASE;
!             else if (strcasecmp(buf, "RecodeTable") == 0)
!                 key = KEY_TABLE;
!             else
!                 elog(LOG, "GetCharSetByHost: unknown tag %s in file %s"
!                     buf, CHARSET_FILE);
!
!             switch (key)
!             {
!                 case KEY_HOST:
!                     /* Read the host */
!                     next_token(file, buf, sizeof(buf));
!                     if (buf[0] != '\0')
!                     {
!                         if (CharSetInRange(buf, host))
!                         {
!                             /* Read the charset */
!                             next_token(file, buf, sizeof(buf));
!                             if (buf[0] != '\0')
!                                 strcpy(HostCharset, buf);
!                         }
!                     }
!                     break;
!                 case KEY_BASE:
!                     /* Read the base charset */
!                     next_token(file, buf, sizeof(buf));
!                     if (buf[0] != '\0')
!                         strcpy(BaseCharset, buf);
!                     break;
!                 case KEY_TABLE:
!                     /* Read the original charset */
!                     next_token(file, buf, sizeof(buf));
!                     if (buf[0] != '\0')
!                     {
!                         strcpy(OrigCharset, buf);
!                         /* Read the destination charset */
!                         next_token(file, buf, sizeof(buf));
!                         if (buf[0] != '\0')
!                         {
!                             strcpy(DestCharset, buf);
!                             /* Read the table filename */
!                             next_token(file, buf, sizeof(buf));
!                             if (buf[0] != '\0')
!                             {
!                                 ChArray[ChIndex] =
!                                     (struct CharsetItem *) palloc(sizeof(struct CharsetItem));
!                                 strcpy(ChArray[ChIndex]->Orig, OrigCharset);
!                                 strcpy(ChArray[ChIndex]->Dest, DestCharset);
!                                 strcpy(ChArray[ChIndex]->Table, buf);
!                                 ChIndex++;
!                             }
!                         }
!                     }
!                     break;
!             }
!
!             /* read to EOL */
!             while (!feof(file) && buf[0])
!             {
!                 next_token(file, buf, sizeof(buf));
!                 elog(LOG, "GetCharSetByHost: unknown tag %s in file %s"
!                     buf, CHARSET_FILE);
!             }
!         }
!     }
!     FreeFile(file);
!
!     for (i = 0; i < ChIndex; i++)
!     {
!         if (strcasecmp(BaseCharset, ChArray[i]->Orig) == 0 &&
!             strcasecmp(HostCharset, ChArray[i]->Dest) == 0)
!             strncpy(TableName, ChArray[i]->Table, 79);
!         pfree(ChArray[i]);
!     }
! }
!
! #endif   /* CYR_RECODE */



Index: src/bin/Makefile
===================================================================
RCS file: /cvsroot/pgsql/src/bin/Makefile,v
retrieving revision 1.34
diff -c -r1.34 Makefile
*** src/bin/Makefile    18 Feb 2001 18:33:59 -0000    1.34
--- src/bin/Makefile    3 Apr 2002 03:48:53 -0000
***************
*** 14,20 ****
  include $(top_builddir)/src/Makefile.global

  DIRS := initdb initlocation ipcclean pg_ctl pg_dump pg_id \
!     pg_passwd psql scripts pg_config

  ifdef MULTIBYTE
  DIRS += pg_encoding
--- 14,20 ----
  include $(top_builddir)/src/Makefile.global

  DIRS := initdb initlocation ipcclean pg_ctl pg_dump pg_id \
!     psql scripts pg_config

  ifdef MULTIBYTE
  DIRS += pg_encoding
Index: src/bin/initdb/initdb.sh
===================================================================
RCS file: /cvsroot/pgsql/src/bin/initdb/initdb.sh,v
retrieving revision 1.145
diff -c -r1.145 initdb.sh
*** src/bin/initdb/initdb.sh    2 Mar 2002 21:39:34 -0000    1.145
--- src/bin/initdb/initdb.sh    3 Apr 2002 03:48:53 -0000
***************
*** 506,514 ****

  "$PGPATH"/postgres $PGSQL_OPT template1 >/dev/null <<EOF
  -- Create a trigger so that direct updates to pg_shadow will be written
! -- to the flat password file pg_pwd
  CREATE TRIGGER pg_sync_pg_pwd AFTER INSERT OR UPDATE OR DELETE ON pg_shadow \
! FOR EACH ROW EXECUTE PROCEDURE update_pg_pwd();
  -- needs to be done before alter user, because alter user checks that
  -- pg_shadow is secure ...
  REVOKE ALL on pg_shadow FROM public;
--- 506,516 ----

  "$PGPATH"/postgres $PGSQL_OPT template1 >/dev/null <<EOF
  -- Create a trigger so that direct updates to pg_shadow will be written
! -- to the flat password/group files pg_pwd and pg_group
  CREATE TRIGGER pg_sync_pg_pwd AFTER INSERT OR UPDATE OR DELETE ON pg_shadow \
! FOR EACH ROW EXECUTE PROCEDURE update_pg_pwd_and_pg_group();
! CREATE TRIGGER pg_sync_pg_group AFTER INSERT OR UPDATE OR DELETE ON pg_group \
! FOR EACH ROW EXECUTE PROCEDURE update_pg_pwd_and_pg_group();
  -- needs to be done before alter user, because alter user checks that
  -- pg_shadow is secure ...
  REVOKE ALL on pg_shadow FROM public;
***************
*** 544,549 ****
--- 546,556 ----
      if [ ! -f "$PGDATA"/global/pg_pwd ]; then
          echo
          echo "The password file wasn't generated. Please report this problem." 1>&2
+         exit_nicely
+     fi
+     if [ ! -f "$PGDATA"/global/pg_group ]; then
+         echo
+         echo "The group file wasn't generated. Please report this problem." 1>&2
          exit_nicely
      fi
      echo "ok"
Index: src/include/miscadmin.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/miscadmin.h,v
retrieving revision 1.101
diff -c -r1.101 miscadmin.h
*** src/include/miscadmin.h    4 Mar 2002 01:46:04 -0000    1.101
--- src/include/miscadmin.h    3 Apr 2002 03:48:54 -0000
***************
*** 219,225 ****
  extern int    CheckPathAccess(char *path, char *name, int open_mode);

  #ifdef CYR_RECODE
- extern void GetCharSetByHost(char *TableName, int host, const char *DataDir);
  extern void SetCharSet(void);
  extern char *convertstr(unsigned char *buff, int len, int dest);
  #endif
--- 219,224 ----
Index: src/include/catalog/pg_proc.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/catalog/pg_proc.h,v
retrieving revision 1.224
diff -c -r1.224 pg_proc.h
*** src/include/catalog/pg_proc.h    29 Mar 2002 19:06:19 -0000    1.224
--- src/include/catalog/pg_proc.h    3 Apr 2002 03:49:00 -0000
***************
*** 2101,2108 ****
  DATA(insert OID = 1637 (  like_escape        PGUID 12 f t t t 2 f 25 "25 25" 100 0 0 100 like_escape - _null_ ));
  DESCR("convert match pattern to use backslash escapes");

! DATA(insert OID = 1689 (  update_pg_pwd          PGUID 12 f t f t 0 f 0  ""  100 0 0 100  update_pg_pwd - _null_ ));
! DESCR("update pg_pwd file");

  /* Oracle Compatibility Related Functions - By Edmund Mergl <E.Mergl@bawue.de> */
  DATA(insert OID =  868 (  strpos       PGUID 12 f t t t 2 f 23 "25 25" 100 0 0 100    textpos - _null_ ));
--- 2101,2108 ----
  DATA(insert OID = 1637 (  like_escape        PGUID 12 f t t t 2 f 25 "25 25" 100 0 0 100 like_escape - _null_ ));
  DESCR("convert match pattern to use backslash escapes");

! DATA(insert OID = 1689 (  update_pg_pwd_and_pg_group  PGUID 12 f t f t 0 f 0  ""  100 0 0 100
update_pg_pwd_and_pg_group- _null_ )); 
! DESCR("update pg_pwd and pg_group files");

  /* Oracle Compatibility Related Functions - By Edmund Mergl <E.Mergl@bawue.de> */
  DATA(insert OID =  868 (  strpos       PGUID 12 f t t t 2 f 23 "25 25" 100 0 0 100    textpos - _null_ ));
Index: src/include/commands/user.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/commands/user.h,v
retrieving revision 1.17
diff -c -r1.17 user.h
*** src/include/commands/user.h    1 Mar 2002 22:45:17 -0000    1.17
--- src/include/commands/user.h    3 Apr 2002 03:49:01 -0000
***************
*** 10,17 ****
--- 10,25 ----
  #ifndef USER_H
  #define USER_H

+ #include "fmgr.h"
  #include "nodes/parsenodes.h"

+ #define PWD_FILE    "pg_pwd"
+
+ #define USER_GROUP_FILE    "pg_group"
+
+
+ extern char *group_getfilename(void);
+ extern char *user_getfilename(void);
  extern void CreateUser(CreateUserStmt *stmt);
  extern void AlterUser(AlterUserStmt *stmt);
  extern void AlterUserSet(AlterUserSetStmt *stmt);
***************
*** 21,26 ****
  extern void AlterGroup(AlterGroupStmt *stmt, const char *tag);
  extern void DropGroup(DropGroupStmt *stmt);

! extern Datum update_pg_pwd(PG_FUNCTION_ARGS);

  #endif   /* USER_H */
--- 29,34 ----
  extern void AlterGroup(AlterGroupStmt *stmt, const char *tag);
  extern void DropGroup(DropGroupStmt *stmt);

! extern Datum update_pg_pwd_and_pg_group(PG_FUNCTION_ARGS);

  #endif   /* USER_H */
Index: src/include/libpq/crypt.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/libpq/crypt.h,v
retrieving revision 1.19
diff -c -r1.19 crypt.h
*** src/include/libpq/crypt.h    12 Nov 2001 01:52:46 -0000    1.19
--- src/include/libpq/crypt.h    3 Apr 2002 03:49:01 -0000
***************
*** 15,31 ****

  #include "libpq/libpq-be.h"

- #define CRYPT_PWD_FILE_SEPSTR    "\t"
-
  /* Also defined in interfaces/odbc/md5.h */
  #define MD5_PASSWD_LEN    35

  #define isMD5(passwd)    (strncmp((passwd),"md5",3) == 0 && \
                           strlen(passwd) == MD5_PASSWD_LEN)

-
- extern char *crypt_getpwdfilename(void);
- extern void load_password_cache(void);

  extern int md5_crypt_verify(const Port *port, const char *user,
                   const char *pgpass);
--- 15,26 ----
Index: src/include/libpq/hba.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/libpq/hba.h,v
retrieving revision 1.31
diff -c -r1.31 hba.h
*** src/include/libpq/hba.h    5 Nov 2001 17:46:33 -0000    1.31
--- src/include/libpq/hba.h    3 Apr 2002 03:49:02 -0000
***************
*** 15,29 ****
  #include <netinet/in.h>
  #endif

  #define CONF_FILE "pg_hba.conf"
   /* Name of the config file  */

  #define USERMAP_FILE "pg_ident.conf"
   /* Name of the usermap file */

- #define OLD_CONF_FILE "pg_hba"
-  /* Name of the config file in prior releases of Postgres. */
-
  #define IDENT_PORT 113
   /* Standard TCP port number for Ident service.  Assigned by IANA */

--- 15,28 ----
  #include <netinet/in.h>
  #endif

+ #include "nodes/pg_list.h"
+
  #define CONF_FILE "pg_hba.conf"
   /* Name of the config file  */

  #define USERMAP_FILE "pg_ident.conf"
   /* Name of the usermap file */

  #define IDENT_PORT 113
   /* Standard TCP port number for Ident service.  Assigned by IANA */

***************
*** 46,53 ****

  typedef struct Port hbaPort;

  extern int    hba_getauthmethod(hbaPort *port);
  extern int    authident(hbaPort *port);
- extern void load_hba_and_ident(void);

  #endif
--- 45,59 ----

  typedef struct Port hbaPort;

+ #define MAX_TOKEN    256
+
+ extern void next_token(FILE *fp, char *buf, const int bufsz);
+ extern List **get_user_line(const char *user);
+ extern void load_hba(void);
+ extern void load_ident(void);
+ extern void load_user(void);
+ extern void load_group(void);
  extern int    hba_getauthmethod(hbaPort *port);
  extern int    authident(hbaPort *port);

  #endif
Index: src/test/regress/expected/opr_sanity.out
===================================================================
RCS file: /cvsroot/pgsql/src/test/regress/expected/opr_sanity.out,v
retrieving revision 1.34
diff -c -r1.34 opr_sanity.out
*** src/test/regress/expected/opr_sanity.out    30 Sep 2001 17:37:32 -0000    1.34
--- src/test/regress/expected/opr_sanity.out    3 Apr 2002 03:49:03 -0000
***************
*** 30,36 ****
      AND p1.proname !~ '^pl[^_]+_call_handler$'
      AND p1.proname !~ '^RI_FKey_'
      AND p1.proname !~ 'costestimate$'
!     AND p1.proname != 'update_pg_pwd';
   oid | proname
  -----+---------
  (0 rows)
--- 30,36 ----
      AND p1.proname !~ '^pl[^_]+_call_handler$'
      AND p1.proname !~ '^RI_FKey_'
      AND p1.proname !~ 'costestimate$'
!     AND p1.proname != 'update_pg_pwd_and_pg_group';
   oid | proname
  -----+---------
  (0 rows)
Index: src/test/regress/sql/opr_sanity.sql
===================================================================
RCS file: /cvsroot/pgsql/src/test/regress/sql/opr_sanity.sql,v
retrieving revision 1.23
diff -c -r1.23 opr_sanity.sql
*** src/test/regress/sql/opr_sanity.sql    30 Sep 2001 17:37:32 -0000    1.23
--- src/test/regress/sql/opr_sanity.sql    3 Apr 2002 03:49:04 -0000
***************
*** 33,39 ****
      AND p1.proname !~ '^pl[^_]+_call_handler$'
      AND p1.proname !~ '^RI_FKey_'
      AND p1.proname !~ 'costestimate$'
!     AND p1.proname != 'update_pg_pwd';

  -- Look for conflicting proc definitions (same names and input datatypes).
  -- (This test should be dead code now that we have the unique index
--- 33,39 ----
      AND p1.proname !~ '^pl[^_]+_call_handler$'
      AND p1.proname !~ '^RI_FKey_'
      AND p1.proname !~ 'costestimate$'
!     AND p1.proname != 'update_pg_pwd_and_pg_group';

  -- Look for conflicting proc definitions (same names and input datatypes).
  -- (This test should be dead code now that we have the unique index

pgsql-patches by date:

Previous
From: "Nicolas Bazin"
Date:
Subject: Re: please apply patch - build on Unixware with GCC
Next
From: "Patrick Lam"
Date:
Subject: Help