OSDN Git Service

Authentication improvements:
authorBruce Momjian <bruce@momjian.us>
Thu, 4 Apr 2002 04:25:54 +0000 (04:25 +0000)
committerBruce Momjian <bruce@momjian.us>
Thu, 4 Apr 2002 04:25:54 +0000 (04:25 +0000)
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

25 files changed:
doc/src/sgml/client-auth.sgml
doc/src/sgml/ref/allfiles.sgml
doc/src/sgml/ref/pg_passwd.sgml [deleted file]
doc/src/sgml/reference.sgml
src/backend/commands/user.c
src/backend/libpq/Makefile
src/backend/libpq/auth.c
src/backend/libpq/crypt.c
src/backend/libpq/hba.c
src/backend/libpq/password.c [deleted file]
src/backend/libpq/pg_hba.conf.sample
src/backend/postmaster/postmaster.c
src/backend/utils/adt/quote.c
src/backend/utils/init/miscinit.c
src/bin/Makefile
src/bin/initdb/initdb.sh
src/bin/pg_passwd/Makefile [deleted file]
src/bin/pg_passwd/pg_passwd.c [deleted file]
src/include/catalog/pg_proc.h
src/include/commands/user.h
src/include/libpq/crypt.h
src/include/libpq/hba.h
src/include/miscadmin.h
src/test/regress/expected/opr_sanity.out
src/test/regress/sql/opr_sanity.sql

index 76542f9..4afd179 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/client-auth.sgml,v 1.33 2002/03/22 19:20:06 petere Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/client-auth.sgml,v 1.34 2002/04/04 04:25:44 momjian Exp $
 -->
 
 <chapter id="client-authentication">
@@ -10,14 +10,13 @@ $Header: /cvsroot/pgsql/doc/src/sgml/client-auth.sgml,v 1.33 2002/03/22 19:20:06
  </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.
+  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,20 +29,19 @@ $Header: /cvsroot/pgsql/doc/src/sgml/client-auth.sgml,v 1.33 2002/03/22 19:20:06
 
  <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.
+  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> database user names are logically
+  <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
+  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.
+  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,39 +54,39 @@ $Header: /cvsroot/pgsql/doc/src/sgml/client-auth.sgml,v 1.33 2002/03/22 19:20:06
   <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>.
+   <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.
+   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 or names,
+   (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 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
+   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, the following records are not considered. If no record
-   matches, the access will be denied.
+   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>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> ]
+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,7 +95,7 @@ hostssl <replaceable>database</replaceable> <replaceable>IP-address</replaceable
      <term><literal>local</literal></term>
      <listitem>
       <para>
-       This record pertains to connection attempts over Unix domain
+       This record applies to connection attempts using Unix domain
        sockets.
       </para>
      </listitem>
@@ -107,10 +105,11 @@ hostssl <replaceable>database</replaceable> <replaceable>IP-address</replaceable
      <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.
+       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,13 +118,13 @@ hostssl <replaceable>database</replaceable> <replaceable>IP-address</replaceable
      <term><literal>hostssl</literal></term>
      <listitem>
       <para>
-       This record pertains to connection attempts with SSL over
+       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 match only SSL connections.)
+       <literal>hostssl</literal> records requires SSL connections.)
       </para>
      </listitem>
     </varlistentry>
@@ -134,12 +133,35 @@ hostssl <replaceable>database</replaceable> <replaceable>IP-address</replaceable
      <term><replaceable>database</replaceable></term>
      <listitem>
       <para>
-       Specifies the database that this record applies to. The value
+       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.  Otherwise,
-       this is the name of a specific <productname>PostgreSQL</productname>
-       database.
+       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,10 +171,9 @@ hostssl <replaceable>database</replaceable> <replaceable>IP-address</replaceable
      <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
+       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,10 +190,9 @@ hostssl <replaceable>database</replaceable> <replaceable>IP-address</replaceable
      <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">.
+       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,66 +210,41 @@ hostssl <replaceable>database</replaceable> <replaceable>IP-address</replaceable
         <term><literal>reject</></term>
         <listitem>
          <para>
-          The connection is rejected unconditionally. This is mostly
-          useful to <quote>filter out</> certain hosts from a group.
+          The connection is rejected unconditionally. This is useful for
+          <quote>filtering out</> certain hosts from a group.
          </para>
         </listitem>
        </varlistentry>
 
        <varlistentry>
-        <term><literal>password</></term>
+        <term><literal>md5</></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.
+          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>md5</></term>
+        <term><literal>crypt</></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.
+          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>crypt</></term>
+        <term><literal>password</></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.
+          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,34 +273,36 @@ hostssl <replaceable>database</replaceable> <replaceable>IP-address</replaceable
         <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>
+          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 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.
+         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>
-          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.
+          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,17 +312,16 @@ hostssl <replaceable>database</replaceable> <replaceable>IP-address</replaceable
         <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>.
+          <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,42 +332,33 @@ hostssl <replaceable>database</replaceable> <replaceable>IP-address</replaceable
      </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.
+   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
+   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.
+   (using <literal>pg_ctl reload</> or <literal>kill -HUP</>) to make it
+   re-read the file.
   </para>
 
   <para>
@@ -382,27 +369,27 @@ hostssl <replaceable>database</replaceable> <replaceable>IP-address</replaceable
    <example id="example-pg-hba.conf">
     <title>An example <filename>pg_hba.conf</filename> file</title>
 <programlisting>
-# TYPE       DATABASE    IP_ADDRESS    MASK               AUTHTYPE  MAP
+# TYPE       DATABASE    USER       IP_ADDRESS    MASK               AUTHTYPE
 
 # Allow any user on the local system to connect to any
-# database under any username, but only via an IP connection:
+# database under any user name, but only via an IP connection:
 
-host         all         127.0.0.1     255.255.255.255    trust     
+host         all         all        127.0.0.1     255.255.255.255    trust     
 
 # The same, over Unix-socket connections:
 
-local        all                                          trust
+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 username that ident on that
-# host identifies him as (typically his Unix username):
+# connect to database "template1" as the same user name that ident on that
+# host identifies him as (typically his Unix user name):
 
-host         template1   192.168.93.0  255.255.255.0      ident     sameuser
+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 in pg_shadow is correctly supplied:
+# if the user's password is correctly supplied:
 
-host         template1   192.168.12.10 255.255.255.255    md5
+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,8 +397,8 @@ host         template1   192.168.12.10 255.255.255.255    md5
 # 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
+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,7 +406,7 @@ host         all        0.0.0.0        0.0.0.0            krb5
 # 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
+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,8 +416,8 @@ host         all        192.168.0.0    255.255.0.0        ident     omicron
 # 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
+local        sameuser   all                                            md5
+local        all        @admins                                        md5
 </programlisting>
    </example>
   </para>
@@ -490,86 +477,49 @@ local        all                                          md5  admins
    <title>Password authentication</title>
 
    <indexterm>
-    <primary>password</primary>
+    <primary>MD5</>
    </indexterm>
    <indexterm>
-    <primary>MD5</>
+    <primary>crypt</>
+   </indexterm>
+   <indexterm>
+    <primary>password</primary>
    </indexterm>
 
    <para>
     Password-based authentication methods include <literal>md5</>,
-    <literal>crypt</>, and <literal>password</>.  These methods operate
+    <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).
+    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.
+    <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.
+    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,10 +538,10 @@ local        all                                          md5  admins
     <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
-    good starting point for exploration. Several sources for
+    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,34 +556,33 @@ local        all                                          md5  admins
    <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.
+    <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.
+    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
+    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,18 +598,20 @@ local        all                                          md5  admins
 
    <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
+    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. 
+    <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.
+    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,55 +658,54 @@ local        all                                          md5  admins
    </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.
+    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.
+    <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:
+    <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.
+    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
+   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.
+   (using <literal>pg_ctl reload</> or <literal>kill -HUP</>) to make it
+   re-read the file.
   </para>
 
    <para>
@@ -763,13 +713,14 @@ local        all                                          md5  admins
     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
+    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,7 +731,7 @@ local        all                                          md5  admins
 
 omicron        bryanh       bryanh
 omicron        ann          ann
-# bob has username robert on these machines
+# bob has user name robert on these machines
 omicron        robert       bob
 # bryanh can also connect as guest1
 omicron        bryanh       guest1
@@ -799,30 +750,30 @@ omicron        bryanh       guest1
 
    <para>
 <ProgramListing>
-No pg_hba.conf entry for host 123.123.123.123, user joeblow, database testdb
+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>
+    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'
+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.
+    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
+FATAL 1:  user "andym" does not exist
 </ProgramListing>
     The indicated user name was not found.
    </para>
@@ -837,9 +788,9 @@ FATAL 1:  Database "testdb" does not exist in the system catalog.
    </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.
+    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 460f150..61d979a 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/ref/allfiles.sgml,v 1.36 2002/03/19 02:18:12 momjian Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/allfiles.sgml,v 1.37 2002/04/04 04:25:45 momjian Exp $
 PostgreSQL documentation
 Complete list of usable sgml source files in this directory.
 -->
@@ -125,7 +125,6 @@ Complete list of usable sgml source files in this directory.
 <!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">
diff --git a/doc/src/sgml/ref/pg_passwd.sgml b/doc/src/sgml/ref/pg_passwd.sgml
deleted file mode 100644 (file)
index 13125b0..0000000
+++ /dev/null
@@ -1,123 +0,0 @@
-<!--
-$Header: /cvsroot/pgsql/doc/src/sgml/ref/Attic/pg_passwd.sgml,v 1.10 2001/12/08 03:24:38 thomas Exp $
-PostgreSQL documentation
--->
-
-<refentry id="APP-PG-PASSWD">
- <docinfo>
-  <date>2000-11-18</date>
- </docinfo>
-
- <refmeta>
-  <refentrytitle id="APP-PG-PASSWD-TITLE"><application>pg_passwd</application></refentrytitle>
-  <manvolnum>1</manvolnum>
-  <refmiscinfo>Application</refmiscinfo>
- </refmeta>
-
- <refnamediv>
-  <refname>pg_passwd</refname>
-  <refpurpose>change a secondary <productname>PostgreSQL</> password file</refpurpose>
- </refnamediv>
-
- <refsynopsisdiv>
-  <cmdsynopsis>
-   <command>pg_passwd</command>
-   <arg choice="plain"><replaceable>filename</replaceable></arg>
-  </cmdsynopsis>
- </refsynopsisdiv>
-
- <refsect1 id="app-pg-passwd-description">
-  <title>Description</title>
-  <para>
-   <application>pg_passwd</application> is a tool for manipulating flat
-   text password files. These files can control client authentication of
-   the <productname>PostgreSQL</productname> server. More information
-   about setting up this authentication mechanism can be found in the
-   <citetitle>Administrator's Guide</citetitle>.
-  </para>
-
-  <para>
-   The format of a text password file is one entry per line; the fields
-   of each entry are separated by colons. The first field is the user
-   name, the second field is the encrypted password. Other fields are
-   ignored (to allow password files to be shared between applications
-   that use similar formats). <application>pg_passwd</application>
-   enables users to interactively add entries to such a file, to alter
-   passwords of existing entries, and to encrypt such passwords.
-  </para>
-
-  <para>
-   Supply the name of the password file as argument to the
-   <application>pg_passwd</application> command. To be used by
-   PostgreSQL, the file needs to be located in the server's data
-   directory, and the base name of the file needs to be specified in the
-   <filename>pg_hba.conf</filename> access control file.
-
-<screen>
-<prompt>$</prompt> <userinput>pg_passwd /usr/local/pgsql/data/passwords</userinput>
-<computeroutput>File "/usr/local/pgsql/data/passwords" does not exist.  Create? (y/n):</computeroutput> <userinput>y</userinput>
-<prompt>Username:</prompt> <userinput>guest</userinput>
-<prompt>Password:</prompt>
-<prompt>Re-enter password:</prompt>
-</screen>
-
-   where the <literal>Password:</literal> and <literal>Re-enter
-   password:</literal> prompts require the same password input which
-   is not displayed on the terminal.  Note that the password is limited
-   to eight useful characters by restrictions of the standard crypt(3)
-   library routine.
-  </para>
-
-  <para>
-   The original password file is renamed to
-   <filename>passwords.bk</filename>.
-  </para>
-
-  <para>
-   To make use of this password file, put a line like the following in
-   <filename>pg_hba.conf</filename>:
-
-<programlisting>
-host  mydb     133.65.96.250   255.255.255.255 password passwords
-</programlisting>
-
-   which would allow access to database mydb from host 133.65.96.250 using
-   the passwords listed in the <filename>passwords</filename> file (and
-   only to the users listed in that file).
-  </para>
-
-  <note>
-   <para>
-    It is also useful to have entries in a password file with empty
-    password fields. (This is different from an empty password.) Such
-    entries allow you to restrict users who can access the system. These
-    entries cannot be managed by <application>pg_passwd</application>,
-    but you can edit password files manually.
-   </para>
-  </note>
- </refsect1>
-
- <refsect1 id="app-pg-passwd-seealso">
-  <title>See also</title>
-  <para>
-   <citetitle>PostgreSQL Administrator's Guide</citetitle>
-  </para>
- </refsect1>
-</refentry>
-
-<!-- Keep this comment at the end of the file
-Local variables:
-mode: sgml
-sgml-omittag:nil
-sgml-shorttag:t
-sgml-minimize-attributes:nil
-sgml-always-quote-attributes:t
-sgml-indent-step:1
-sgml-indent-data:t
-sgml-parent-document:nil
-sgml-default-dtd-file:"../reference.ced"
-sgml-exposed-tags:nil
-sgml-local-catalogs:"/usr/lib/sgml/catalog"
-sgml-local-ecat-files:nil
-End:
--->
index b6b6325..41d419d 100644 (file)
@@ -1,5 +1,5 @@
 <!-- reference.sgml
-$Header: /cvsroot/pgsql/doc/src/sgml/reference.sgml,v 1.24 2002/03/19 02:18:11 momjian Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/reference.sgml,v 1.25 2002/04/04 04:25:44 momjian Exp $
 
 PostgreSQL Reference Manual
 -->
@@ -191,7 +191,6 @@ Disable this chapter until we have more functions documented.
    &initlocation;
    &ipcclean;
    &pgCtl;
-   &pgPasswd;
    &postgres;
    &postmaster;
 
index 98ee430..9ebd6fa 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Header: /cvsroot/pgsql/src/backend/commands/user.c,v 1.94 2002/03/26 19:15:48 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/user.c,v 1.95 2002/04/04 04:25:45 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -15,6 +15,7 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
+#include <errno.h>
 #include <unistd.h>
 
 #include "access/heapam.h"
@@ -27,6 +28,7 @@
 #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,8 +41,205 @@ extern bool Password_encryption;
 
 static void CheckPgUserAclNotNull(void);
 
-/*---------------------------------------------------------------------
- * write_password_file / update_pg_pwd
+
+
+/*
+ *     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,10 +250,9 @@ static void CheckPgUserAclNotNull(void);
  * 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)
+write_user_file(Relation urel)
 {
        char       *filename,
                           *tempname;
@@ -63,14 +261,14 @@ write_password_file(Relation rel)
        mode_t          oumask;
        HeapScanDesc scan;
        HeapTuple       tuple;
-       TupleDesc       dsc = RelationGetDescr(rel);
+       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 = crypt_getpwdfilename();
+       filename = user_getfilename();
        bufsize = strlen(filename) + 12;
        tempname = (char *) palloc(bufsize);
 
@@ -82,26 +280,22 @@ write_password_file(Relation rel)
                elog(ERROR, "write_password_file: unable to write %s: %m", tempname);
 
        /* read table */
-       scan = heap_beginscan(rel, false, SnapshotSelf, 0, NULL);
+       scan = heap_beginscan(urel, 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;
+               Datum           datum;
+               bool            isnull;
+               char       *usename,
+                                  *passwd,
+                                  *valuntil;
                int                     i;
 
-               datum_n = heap_getattr(tuple, Anum_pg_shadow_usename, dsc, &null_n);
-               if (null_n)
+               datum = heap_getattr(tuple, Anum_pg_shadow_usename, dsc, &isnull);
+               if (isnull)
                        continue;                       /* ignore NULL usernames */
-               str_n = DatumGetCString(DirectFunctionCall1(nameout, datum_n));
+               usename = (char *) DatumGetName(datum);
 
-               datum_p = heap_getattr(tuple, Anum_pg_shadow_passwd, dsc, &null_p);
+               datum = heap_getattr(tuple, Anum_pg_shadow_passwd, dsc, &isnull);
 
                /*
                 * It can be argued that people having a null password shouldn't
@@ -110,57 +304,40 @@ write_password_file(Relation rel)
                 * 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);
+               if (isnull)
                        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");
+               passwd = DatumGetCString(DirectFunctionCall1(textout, datum));
+
+               datum = heap_getattr(tuple, Anum_pg_shadow_valuntil, dsc, &isnull);
+               if (isnull)
+                       valuntil = pstrdup("");
                else
-                       str_v = DatumGetCString(DirectFunctionCall1(nabstimeout, datum_v));
+                       valuntil = DatumGetCString(DirectFunctionCall1(nabstimeout, datum));
 
                /*
                 * 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);
+               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.
                 */
-               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);
+               fputs_quote(usename, fp);
+               fputs(" ", fp);
+               fputs_quote(passwd, fp);
+               fputs(" ", fp);
+               fputs_quote(valuntil, fp);
+
+               pfree(passwd);
+               pfree(valuntil);
        }
        heap_endscan(scan);
 
@@ -178,29 +355,33 @@ write_password_file(Relation rel)
 
        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)
+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        rel = heap_openr(ShadowRelationName, ExclusiveLock);
+       Relation        urel = heap_openr(ShadowRelationName, ExclusiveLock);
+       Relation        grel = heap_openr(GroupRelationName, ExclusiveLock);
 
-       write_password_file(rel);
+       write_user_file(urel);
+       write_group_file(urel, grel);
        /* OK to release lock, since we did not modify the relation */
-       heap_close(rel, ExclusiveLock);
+       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,14 +627,14 @@ CreateUser(CreateUserStmt *stmt)
        }
 
        /*
-        * Write the updated pg_shadow data to the flat password file.
+        * Now we can clean up; but keep lock until commit.
         */
-       write_password_file(pg_shadow_rel);
+       heap_close(pg_shadow_rel, NoLock);
 
        /*
-        * Now we can clean up; but keep lock until commit.
+        * Write the updated pg_shadow and pg_group data to the flat file.
         */
-       heap_close(pg_shadow_rel, NoLock);
+       update_pg_pwd_and_pg_group(NULL);
 }
 
 
@@ -680,14 +861,14 @@ AlterUser(AlterUserStmt *stmt)
        heap_freetuple(new_tuple);
 
        /*
-        * Write the updated pg_shadow data to the flat password file.
+        * Now we can clean up.
         */
-       write_password_file(pg_shadow_rel);
+       heap_close(pg_shadow_rel, NoLock);
 
        /*
-        * Now we can clean up.
+        * Write the updated pg_shadow and pg_group data to the flat file.
         */
-       heap_close(pg_shadow_rel, NoLock);
+       update_pg_pwd_and_pg_group(NULL);
 }
 
 
@@ -733,7 +914,7 @@ AlterUserSet(AlterUserSetStmt *stmt)
        {
                Datum datum;
                bool isnull;
-               ArrayType *a;
+               ArrayType *array;
 
                repl_null[Anum_pg_shadow_useconfig-1] = ' ';
 
@@ -741,17 +922,17 @@ AlterUserSet(AlterUserSetStmt *stmt)
                                                                Anum_pg_shadow_useconfig, &isnull);
 
                if (valuestr)
-                       a = GUCArrayAdd(isnull
+                       array = GUCArrayAdd(isnull
                                                        ? NULL
                                                        : (ArrayType *) pg_detoast_datum((struct varlena *)datum),
                                                        stmt->variable, valuestr);
                else
-                       a = GUCArrayDelete(isnull
+                       array = GUCArrayDelete(isnull
                                                           ? NULL
                                                           : (ArrayType *) pg_detoast_datum((struct varlena *)datum),
                                                           stmt->variable);
 
-               repl_val[Anum_pg_shadow_useconfig-1] = PointerGetDatum(a);
+               repl_val[Anum_pg_shadow_useconfig-1] = PointerGetDatum(array);
        }
 
        newtuple = heap_modifytuple(oldtuple, rel, repl_val, repl_null, repl_repl);
@@ -846,7 +1027,7 @@ DropUser(DropUserStmt *stmt)
                        datum = heap_getattr(tmp_tuple, Anum_pg_database_datname,
                                                                 pg_dsc, &null);
                        Assert(!null);
-                       dbname = DatumGetCString(DirectFunctionCall1(nameout, datum));
+                       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,14 +1082,14 @@ DropUser(DropUserStmt *stmt)
        }
 
        /*
-        * Write the updated pg_shadow data to the flat password file.
+        * Now we can clean up.
         */
-       write_password_file(pg_shadow_rel);
+       heap_close(pg_shadow_rel, NoLock);
 
        /*
-        * Now we can clean up.
+        * Write the updated pg_shadow and pg_group data to the flat file.
         */
-       heap_close(pg_shadow_rel, NoLock);
+       update_pg_pwd_and_pg_group(NULL);
 }
 
 
@@ -1111,6 +1292,11 @@ CreateGroup(CreateGroupStmt *stmt)
        }
 
        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,7 +1552,15 @@ AlterGroup(AlterGroupStmt *stmt, const char *tag)
 
        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,4 +1613,9 @@ DropGroup(DropGroupStmt *stmt)
                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 f5c2339..d0f05e8 100644 (file)
@@ -4,7 +4,7 @@
 #    Makefile for libpq subsystem (backend half of libpq interface)
 #
 # IDENTIFICATION
-#    $Header: /cvsroot/pgsql/src/backend/libpq/Makefile,v 1.29 2002/03/04 01:46:02 tgl Exp $
+#    $Header: /cvsroot/pgsql/src/backend/libpq/Makefile,v 1.30 2002/04/04 04:25:46 momjian Exp $
 #
 #-------------------------------------------------------------------------
 
@@ -14,9 +14,7 @@ include $(top_builddir)/src/Makefile.global
 
 # 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
+OBJS = be-fsstubs.o auth.o crypt.o hba.o md5.o pqcomm.o pqformat.o pqsignal.o
 
 
 all: SUBSYS.o
index 637e2a6..81a4949 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.79 2002/03/05 07:57:45 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.80 2002/04/04 04:25:47 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -34,7 +34,6 @@
 #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);
@@ -381,7 +380,7 @@ recv_and_check_passwordv0(Port *port)
                saved = port->auth_method;
                port->auth_method = uaPassword;
 
-               status = checkPassword(port, user, password);
+               status = md5_crypt_verify(port, user, password);
 
                port->auth_method = saved;
 
@@ -663,7 +662,7 @@ pam_passwd_conv_proc(int num_msg, const struct pam_message ** msg, struct pam_re
 
                initStringInfo(&buf);
                pq_getstr(&buf);
-               
+
                /* Do not echo failed password to logs, for security. */
                elog(DEBUG5, "received PAM packet");
 
@@ -810,27 +809,14 @@ recv_and_check_password_packet(Port *port)
        /* Do not echo failed password to logs, for security. */
        elog(DEBUG5, "received password packet");
 
-       result = checkPassword(port, port->user, buf.data);
+       result = md5_crypt_verify(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);
-}
-
-
-/*
  * Server demux routine for incoming authentication information for protocol
  * version 0.
  */
index 6ab63dd..7c66530 100644 (file)
@@ -9,13 +9,12 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Header: /cvsroot/pgsql/src/backend/libpq/crypt.c,v 1.44 2002/03/04 01:46:03 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/libpq/crypt.c,v 1.45 2002/04/04 04:25:47 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 
-#include <errno.h>
 #include <unistd.h>
 #ifdef HAVE_CRYPT_H
 #include <crypt.h>
 #include "libpq/libpq.h"
 #include "miscadmin.h"
 #include "storage/fd.h"
+#include "nodes/pg_list.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)
 {
@@ -257,10 +35,14 @@ md5_crypt_verify(const Port *port, const char *user, const char *pgpass)
                           *valuntil,
                           *crypt_pwd;
        int                     retval = STATUS_ERROR;
+       List       **line;
 
-       if (!crypt_getloginfo(user, &passwd, &valuntil))
+       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')
        {
                if (passwd)
index b86cb81..fce63ab 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/libpq/hba.c,v 1.80 2002/03/04 01:46:03 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/libpq/hba.c,v 1.81 2002/04/04 04:25:47 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
 #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 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 */
 
+/* This is used to separate values in multi-valued column strings */
+#define MULTI_VALUE_SEP        "\001"
+
 /*
  * These variables hold the pre-parsed contents of the hba and ident
  * configuration files.  Each is a list of sublists, one sublist for
  */
 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);
 
 /*
  * Some standard C libraries, including GNU, have an isblank() function.
@@ -67,41 +79,76 @@ isblank(const char c)
 
 
 /*
- *     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.
+ *   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.
  */
-static void
+void
 next_token(FILE *fp, char *buf, const int bufsz)
 {
        int                     c;
-       char       *eb = buf + (bufsz - 1);
+       char       *end_buf = buf + (bufsz - 1);
+       bool            in_quote = false;
+       bool            was_quote = false;
 
-       /* Move over initial token-delimiting blanks */
-       while ((c = getc(fp)) != EOF && isblank(c))
+       /* 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, or
-                * blank.  If the token gets too long, we still parse it
-                * correctly, but the excess characters are not stored into *buf.
+                * Build a token in buf of next characters up to EOF, EOL, unquoted
+                * comma, or unquoted whitespace.
                 */
-               while (c != EOF && c != '\n' && !isblank(c))
+               while (c != EOF && c != '\n' &&
+                          (!isblank(c) || in_quote == true))
                {
-                       if (buf < eb)
+                       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).
+                * EOL, since we need to detect end-of-line at next call).
                 */
                if (c != EOF)
                        ungetc(c, fp);
@@ -109,17 +156,142 @@ next_token(FILE *fp, char *buf, const int bufsz)
        *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, MULTI_VALUE_SEP);
+               }
+       } while (trailing_comma);
+
+       return comma_str;
+}
+
 
+/*
+ * Free memory used by lines/tokens (i.e., structure built by tokenize_file)
+ */
 static void
-read_through_eol(FILE *file)
+free_lines(List **lines)
 {
-       int                     c;
+       if (*lines)
+       {
+               List       *line,
+                                  *token;
 
-       while ((c = getc(file)) != EOF && c != '\n')
-               ;
+               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, MULTI_VALUE_SEP);
+                       }
+                       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,19 +301,13 @@ tokenize_file(FILE *file)
        List       *lines = NIL;
        List       *next_line = NIL;
        int                     line_number = 1;
-       char            buf[MAX_TOKEN];
-       char       *comment_ptr;
+       char       *buf;
 
        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';
+               buf = next_token_expand(file);
 
-               /* add token to list, unless we are at eol or comment start */
+               /* add token to list, unless we are at EOL or comment start */
                if (buf[0] != '\0')
                {
                        if (next_line == NIL)
@@ -151,22 +317,15 @@ tokenize_file(FILE *file)
                                lines = lappend(lines, next_line);
                        }
                        /* append token to current line's list */
-                       next_line = lappend(next_line, pstrdup(buf));
+                       next_line = lappend(next_line, buf);
                }
                else
                {
-                       /* we are at real or logical eol, so force a new line List */
+                       /* 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 */
+               /* Advance line number whenever we reach EOL */
                if (next_line == NIL)
                        line_number++;
        }
@@ -176,31 +335,116 @@ tokenize_file(FILE *file)
 
 
 /*
- * Free memory used by lines/tokens (ie, structure built by tokenize_file)
+ * Compare two password-file lines on the basis of their user names.
+ *
+ * Used for qsort() sorting and bsearch() lookup.
  */
-static void
-free_lines(List **lines)
+static int
+user_group_cmp(const void *user, const void *list)
 {
-       if (*lines)
+                                               /* 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)
        {
-               List       *line,
-                                  *token;
+               foreach(l, lnext(lnext(*line)))
+                       if (strcmp(lfirst(l), user) == 0)
+                               return 1;
+       }
 
-               foreach(line, *lines)
+       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, MULTI_VALUE_SEP); tok != NULL; tok = strtok(NULL, MULTI_VALUE_SEP))
+       {
+               if (tok[0] == '+')
                {
-                       List       *ln = lfirst(line);
+                       if (check_group(tok+1, user))
+                               return 1;
+               }
+               else if (strcmp(tok, user) == 0 ||
+                       strcmp(tok, "all") == 0)
+                       return 1;
+       }
 
-                       /* 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);
+       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, MULTI_VALUE_SEP); tok != NULL; tok = strtok(NULL, MULTI_VALUE_SEP))
+       {
+               if (strcmp(tok, "all") == 0)
+                       return 1;
+               else if (strcmp(tok, "sameuser") == 0)
+               {
+                       if (strcmp(dbname, user) == 0)
+                               return 1;
                }
-               /* free the list structure itself */
-               freeList(*lines);
-               /* clear the static variable */
-               *lines = NIL;
+               else if (strcmp(tok, "samegroup") == 0)
+               {
+                       if (check_group(dbname, user))
+                               return 1;
+               }
+               else if (strcmp(tok, dbname) == 0)
+                       return 1;
        }
+       return 0;
 }
 
 
@@ -278,6 +522,7 @@ parse_hba(List *line, hbaPort *port, bool *found_p, bool *error_p)
        int                     line_number;
        char       *token;
        char       *db;
+       char       *user;
 
        Assert(line != NIL);
        line_number = lfirsti(line);
@@ -293,10 +538,17 @@ parse_hba(List *line, hbaPort *port, bool *found_p, bool *error_p)
                        goto hba_syntax;
                db = lfirst(line);
 
-               /* Read the rest of the 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,15 +560,7 @@ parse_hba(List *line, hbaPort *port, bool *found_p, bool *error_p)
                        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)
+               if (port->raddr.sa.sa_family != AF_UNIX)
                        return;
        }
        else if (strcmp(token, "host") == 0 || strcmp(token, "hostssl") == 0)
@@ -347,6 +591,12 @@ parse_hba(List *line, hbaPort *port, bool *found_p, bool *error_p)
                        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,21 +621,19 @@ parse_hba(List *line, hbaPort *port, bool *found_p, bool *error_p)
                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 ||
+               /* 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,6 +678,127 @@ check_hba(hbaPort *port)
 }
 
 
+
+/*
+ * 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,60 +806,35 @@ check_hba(hbaPort *port)
  * follow directions and just installed his old hba file in the new database
  * system.
  */
-static void
+void
 load_hba(void)
 {
-       int                     fd,
-                               bufsize;
+       int                     bufsize;
        FILE       *file;                       /* The config file we have to read */
-       char       *old_conf_file;
+       char       *conf_file;  /* The name of the config 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);
+       /* 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);
 
-       if ((fd = open(old_conf_file, O_RDONLY | PG_BINARY, 0)) != -1)
+       file = AllocateFile(conf_file, "r");
+       if (file == NULL)
        {
-               /* 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);
+               /* 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
        {
-               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);
+               hba_lines = tokenize_file(file);
+               FreeFile(file);
        }
-       pfree(old_conf_file);
+       pfree(conf_file);
 }
 
 
@@ -606,7 +950,7 @@ check_ident_usermap(const char *usermap_name,
 /*
  * Read the ident config file and create a List of Lists of tokens in the file.
  */
-static void
+void
 load_ident(void)
 {
        FILE       *file;                       /* The map file we have to read */
@@ -622,7 +966,7 @@ load_ident(void)
        map_file = (char *) palloc(bufsize);
        snprintf(map_file, bufsize, "%s/%s", DataDir, USERMAP_FILE);
 
-       file = AllocateFile(map_file, PG_BINARY_R);
+       file = AllocateFile(map_file, "r");
        if (file == NULL)
        {
                /* The open of the map file failed.  */
@@ -640,8 +984,8 @@ load_ident(void)
 
 /*
  *     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,
+ *     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,7 +1052,7 @@ interpret_ident_response(char *ident_response,
                                                cursor++;               /* Go over colon */
                                                while (isblank(*cursor))
                                                        cursor++;       /* skip blanks */
-                                               /* Rest of line is username.  Copy it over. */
+                                               /* Rest of line is user name.  Copy it over. */
                                                i = 0;
                                                while (*cursor != '\r' && i < IDENT_USERNAME_MAX)
                                                        ident_user[i++] = *cursor++;
@@ -725,7 +1069,7 @@ interpret_ident_response(char *ident_response,
 /*
  *     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
+ *     "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,6 +1299,8 @@ ident_unix(int sock, char *ident_user)
 #endif
 }
 
+
+
 /*
  *     Determine the username of the initiator of the connection described
  *     by "port".      Then look in the usermap file under the usermap
@@ -1010,211 +1356,3 @@ hba_getauthmethod(hbaPort *port)
                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 */
diff --git a/src/backend/libpq/password.c b/src/backend/libpq/password.c
deleted file mode 100644 (file)
index f8490f8..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- * $Id: password.c,v 1.41 2002/03/04 01:46:03 tgl Exp $
- *
- */
-
-#include <errno.h>
-#include <unistd.h>
-
-#include "postgres.h"
-
-#ifdef HAVE_CRYPT_H
-#include <crypt.h>
-#endif
-
-#include "libpq/libpq.h"
-#include "libpq/password.h"
-#include "libpq/crypt.h"
-#include "miscadmin.h"
-#include "storage/fd.h"
-
-
-int
-verify_password(const Port *port, const char *user, const char *password)
-{
-       char       *pw_file_fullname;
-       FILE       *pw_file;
-
-       pw_file_fullname = (char *) palloc(strlen(DataDir) + strlen(port->auth_arg) + 2);
-       strcpy(pw_file_fullname, DataDir);
-       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",
-                        pw_file_fullname);
-
-               pfree(pw_file_fullname);
-
-               return STATUS_ERROR;
-       }
-
-       pfree(pw_file_fullname);
-
-       while (!feof(pw_file))
-       {
-               char            pw_file_line[255],
-                                  *p,
-                                  *test_user,
-                                  *test_pw;
-
-               if (fgets(pw_file_line, sizeof(pw_file_line), pw_file) == NULL)
-                       pw_file_line[0] = '\0';
-               /* kill the newline */
-               if (strlen(pw_file_line) > 0 &&
-                       pw_file_line[strlen(pw_file_line) - 1] == '\n')
-                       pw_file_line[strlen(pw_file_line) - 1] = '\0';
-
-               p = pw_file_line;
-
-               test_user = strtok(p, ":");
-               if (!test_user || test_user[0] == '\0')
-                       continue;
-               test_pw = strtok(NULL, ":");
-
-               if (strcmp(user, test_user) == 0)
-               {
-                       /* we're outta here one way or the other, so close file */
-                       FreeFile(pw_file);
-
-                       /*
-                        * If the password is empty or "+" then we use the regular
-                        * pg_shadow passwords. If we use crypt then we have to use
-                        * pg_shadow passwords no matter what.  This is because the
-                        * current code needs non-encrypted passwords to encrypt with
-                        * a random salt.
-                        */
-                       if (port->auth_method == uaMD5 ||
-                               port->auth_method == uaCrypt ||
-                               test_pw == NULL ||
-                               test_pw[0] == '\0' ||
-                               strcmp(test_pw, "+") == 0)
-                               return md5_crypt_verify(port, user, password);
-
-                       /* external password file is crypt-only */
-                       if (strcmp(crypt(password, test_pw), test_pw) == 0)
-                       {
-                               /* it matched. */
-                               return STATUS_OK;
-                       }
-
-                       elog(LOG, "verify_password: password mismatch for '%s'",
-                                user);
-
-                       return STATUS_ERROR;
-               }
-       }
-
-       FreeFile(pw_file);
-
-       elog(LOG, "verify_password: user '%s' not found in password file",
-                user);
-
-       return STATUS_ERROR;
-}
index 669588e..05e6959 100644 (file)
 # 
 # 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
+#   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 ADDRESS_MASK are standard dotted decimal IP address and
+# 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 and AUTH_ARGUMENT are described below.
+# AUTH_TYPE is described below.
 #
 # 
 # hostssl
 # 
 # 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
+# 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
 # connections. Without this record, UNIX-socket connections are disallowed
 # 
 # Format:
-#   local  DBNAME  AUTH_TYPE  [AUTH_ARGUMENT]
+#   local      DATABASE    USER      AUTH_TYPE
 # 
 # This format is identical to the "host" record type except there are no
-# IP_ADDRESS and ADDRESS_MASK fields.
+# IP_ADDRESS and MASK fields.
 #
 # 
 # 
 # has an AUTH_TYPE.
 #
 #   trust: 
-#              No authentication is done. Any valid username is accepted,
+#              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.
 # 
-#   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.
+#              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.  You can
-#              not store encrypted passwords in pg_shadow if you use this
-#              method.
+#              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. On machines that support unix-domain socket
-#              credentials (currently Linux, FreeBSD, NetBSD, and BSD/OS), this
-#              method also works for "local" connections.
+#              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.
 #
-#              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.
+#              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
 #              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.
+#              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
 # 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
+# TYPE       DATABASE    USER       IP_ADDRESS    MASK               AUTH_TYPE
+# local      all         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     
+# 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    IP_ADDRESS    MASK               AUTH_TYPE  AUTH_ARGUMENT
-# host       template1   192.168.93.0  255.255.255.0      ident      sameuser
+# 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 in pg_shadow is correctly supplied:
+# if the user's password is correctly supplied:
 # 
-# TYPE       DATABASE    IP_ADDRESS    MASK               AUTH_TYPE  AUTH_ARGUMENT
-# host       template1   192.168.12.10 255.255.255.255    md5
+# 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:
+# 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
+# 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    IP_ADDRESS    MASK               AUTH_TYPE  AUTH_ARGUMENT
-# host       all        192.168.0.0    255.255.0.0        ident      phoenix
+# 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
 #
-# 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.
 #
 #
 # 
 # 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
+# TYPE       DATABASE      USER      IP_ADDRESS    MASK               AUTH_TYPE
 
-local      all                                          trust
-host       all         127.0.0.1     255.255.255.255    trust
+local        all           all                                        trust
+host         all           all       127.0.0.1     255.255.255.255    trust
index a6d6097..0ce817b 100644 (file)
@@ -37,7 +37,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.271 2002/03/15 19:20:35 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.272 2002/04/04 04:25:48 momjian Exp $
  *
  * NOTES
  *
@@ -748,8 +748,10 @@ PostmasterMain(int argc, char *argv[])
        /*
         * Load cached files for client authentication.
         */
-       load_hba_and_ident();
-       load_password_cache();
+       load_hba();
+       load_ident();
+       load_user();
+       load_group();
 
        /*
         * We're ready to rock and roll...
@@ -1389,7 +1391,8 @@ SIGHUP_handler(SIGNAL_ARGS)
                elog(LOG, "Received SIGHUP, reloading configuration files");
                SignalChildren(SIGHUP);
                ProcessConfigFile(PGC_SIGHUP);
-               load_hba_and_ident();
+               load_hba();
+               load_ident();
        }
 
        PG_SETMASK(&UnBlockSig);
@@ -2288,9 +2291,10 @@ sigusr1_handler(SIGNAL_ARGS)
        if (CheckPostmasterSignal(PMSIGNAL_PASSWORD_CHANGE))
        {
                /*
-                * Password file has changed.
+                * Password or group file has changed.
                 */
-               load_password_cache();
+               load_user();
+               load_group();
        }
 
        if (CheckPostmasterSignal(PMSIGNAL_WAKEN_CHILDREN))
index 39a1ec4..537e97a 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/utils/adt/quote.c,v 1.6 2001/10/28 06:25:53 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/utils/adt/quote.c,v 1.7 2002/04/04 04:25:49 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -124,8 +124,6 @@ do_quote_ident(text *iptr)
        {
                if (*cp1 == '"')
                        *cp2++ = '"';
-               if (*cp1 == '\\')
-                       *cp2++ = '\\';
                *cp2++ = *cp1++;
        }
        *cp2++ = '"';
@@ -234,8 +232,6 @@ do_quote_ident(text *iptr)
 
                if (*cp1 == '"')
                        *cp2++ = '"';
-               if (*cp1 == '\\')
-                       *cp2++ = '\\';
                *cp2++ = *cp1++;
 
                len--;
index dceb8b9..9ef8b1c 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/utils/init/miscinit.c,v 1.85 2002/03/04 04:45:27 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/utils/init/miscinit.c,v 1.86 2002/04/04 04:25:49 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -236,85 +236,17 @@ pg_convert2(PG_FUNCTION_ARGS)
 
 #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       *filename;
        char       *map_file;
        char            buf[MAX_TOKEN];
        int                     i,
                                c;
        unsigned char FromChar,
                                ToChar;
-       char            ChTable[80];
+       char            ChTable[MAX_TOKEN];
 
        for (i = 0; i < 128; i++)
        {
@@ -325,39 +257,40 @@ SetCharSet(void)
        if (IsUnderPostmaster)
        {
                GetCharSetByHost(ChTable, MyProcPort->raddr.in.sin_addr.s_addr, DataDir);
-               p = ChTable;
+               filename = ChTable;
        }
        else
-               p = getenv("PG_RECODETABLE");
+               filename = getenv("PG_RECODETABLE");
 
-       if (p && *p != '\0')
+       if (filename && *filename != '\0')
        {
-               map_file = palloc(strlen(DataDir) + strlen(p) + 2);
-               sprintf(map_file, "%s/%s", DataDir, p);
-               file = AllocateFile(map_file, PG_BINARY_R);
+               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 ((c = getc(file)) != EOF)
+
+               while (!feof(file))
                {
-                       if (c == '#')
-                               read_through_eol(file);
-                       else
+                       next_token(file, buf, sizeof(buf));
+                       if (buf[0] != '\0')
                        {
-                               /* Read the FromChar */
-                               ungetc(c, file);
+                               FromChar = strtoul(buf, 0, 0);
+                               /* Read the ToChar */
                                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])
                                        {
-                                               ToChar = strtoul(buf, 0, 0);
-                                               RecodeForwTable[FromChar - 128] = ToChar;
-                                               RecodeBackTable[ToChar - 128] = FromChar;
-                                               read_through_eol(file);
+                                               next_token(file, buf, sizeof(buf));
+                                               elog(LOG, "SetCharSet: unknown tag %s in file %s"
+                                                       buf, filename);
                                        }
                                }
                        }
@@ -366,6 +299,7 @@ SetCharSet(void)
        }
 }
 
+
 char *
 convertstr(unsigned char *buff, int len, int dest)
 {
@@ -384,7 +318,206 @@ convertstr(unsigned char *buff, int len, int dest)
        }
        return ch;
 }
-#endif
+
+#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 a1d4b1e..7a293c3 100644 (file)
@@ -5,7 +5,7 @@
 # Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
 # Portions Copyright (c) 1994, Regents of the University of California
 #
-# $Header: /cvsroot/pgsql/src/bin/Makefile,v 1.34 2001/02/18 18:33:59 momjian Exp $
+# $Header: /cvsroot/pgsql/src/bin/Makefile,v 1.35 2002/04/04 04:25:50 momjian Exp $
 #
 #-------------------------------------------------------------------------
 
@@ -14,7 +14,7 @@ top_builddir = ../..
 include $(top_builddir)/src/Makefile.global
 
 DIRS := initdb initlocation ipcclean pg_ctl pg_dump pg_id \
-       pg_passwd psql scripts pg_config
+       psql scripts pg_config
 
 ifdef MULTIBYTE
 DIRS += pg_encoding
index e028ac1..30f7de1 100644 (file)
@@ -27,7 +27,7 @@
 # Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
 # Portions Copyright (c) 1994, Regents of the University of California
 #
-# $Header: /cvsroot/pgsql/src/bin/initdb/Attic/initdb.sh,v 1.146 2002/04/03 05:39:32 petere Exp $
+# $Header: /cvsroot/pgsql/src/bin/initdb/Attic/initdb.sh,v 1.147 2002/04/04 04:25:50 momjian Exp $
 #
 #-------------------------------------------------------------------------
 
@@ -603,9 +603,11 @@ $ECHO_N "initializing pg_shadow... "$ECHO_C
 
 "$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
+-- 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();
+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;
@@ -643,6 +645,11 @@ EOF
         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"
 fi
 
diff --git a/src/bin/pg_passwd/Makefile b/src/bin/pg_passwd/Makefile
deleted file mode 100644 (file)
index f6f4acd..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-# $Header: /cvsroot/pgsql/src/bin/pg_passwd/Attic/Makefile,v 1.14 2001/05/12 19:49:47 petere Exp $
-
-subdir = src/bin/pg_passwd
-top_builddir = ../../..
-include $(top_builddir)/src/Makefile.global
-
-OBJS = pg_passwd.o
-ifdef STRDUP
-OBJS += $(top_builddir)/src/utils/strdup.o
-endif
-
-all: pg_passwd
-
-pg_passwd: $(OBJS)
-       $(CC) $(CFLAGS) $(LDFLAGS) $^ $(LIBS) -o $@
-
-$(top_builddir)/src/utils/strdup.o:
-       $(MAKE) -C $(top_builddir)/src/utils strdup.o
-
-install: all installdirs
-       $(INSTALL_PROGRAM) pg_passwd$(X) $(DESTDIR)$(bindir)/pg_passwd$(X)
-
-installdirs:
-       $(mkinstalldirs) $(DESTDIR)$(bindir)
-
-uninstall:
-       rm -f $(DESTDIR)$(bindir)/pg_passwd$(X)
-
-depend dep:
-       $(CC) -MM $(CFLAGS) *.c >depend
-
-clean distclean maintainer-clean:
-       rm -f pg_passwd$(X) pg_passwd.o
-
-ifeq (depend,$(wildcard depend))
-include depend
-endif
diff --git a/src/bin/pg_passwd/pg_passwd.c b/src/bin/pg_passwd/pg_passwd.c
deleted file mode 100644 (file)
index 831c382..0000000
+++ /dev/null
@@ -1,412 +0,0 @@
-/*
- * @(#) pg_passwd.c 1.8 09:13:16 97/07/02              Y. Ichikawa
- */
-#include "postgres_fe.h"
-
-#include <unistd.h>
-#include <errno.h>
-#include <time.h>
-#include <ctype.h>
-#define issaltchar(c)  (isalnum((unsigned char) (c)) || (c) == '.' || (c) == '/')
-
-#ifdef HAVE_TERMIOS_H
-#include <termios.h>
-#endif
-#ifdef HAVE_CRYPT_H
-#include <crypt.h>
-#else
-extern char *crypt(const char *, const char *);
-#endif
-
-/*
- * We assume that the output of crypt(3) is always 13 characters,
- * and that at most 8 characters can usefully be sent to it.
- *
- * Postgres usernames are assumed to be less than NAMEDATALEN chars long.
- */
-#define CLEAR_PASSWD_LEN 8             /* not including null */
-#define CRYPTED_PASSWD_LEN 13  /* not including null */
-
-const char *progname;
-
-static void usage(void);
-static void read_pwd_file(char *filename);
-static void write_pwd_file(char *filename, char *bkname);
-static void encrypt_pwd(char key[CLEAR_PASSWD_LEN + 1],
-                       char salt[3],
-                       char passwd[CRYPTED_PASSWD_LEN + 1]);
-static void prompt_for_username(char *username);
-static void prompt_for_password(char *prompt, char *password);
-
-static void
-usage(void)
-{
-       printf("%s manipulates flat text password files for PostgreSQL.\n\n", progname);
-       printf("Usage:\n  %s PASSWORD-FILE\n\n", progname);
-       printf("Report bugs to <pgsql-bugs@postgresql.org>.\n");
-}
-
-typedef struct
-{
-       char       *uname;
-       char       *pwd;
-       char       *rest;
-} pg_pwd;
-
-#define MAXPWDS 1024
-
-pg_pwd         pwds[MAXPWDS];
-int                    npwds = 0;
-
-
-static void
-read_pwd_file(char *filename)
-{
-       FILE       *fp;
-       static char line[512];
-       static char ans[128];
-       int                     i;
-
-try_again:
-       fp = fopen(filename, PG_BINARY_R);
-       if (fp == NULL)
-       {
-               if (errno == ENOENT)
-               {
-                       printf("File \"%s\" does not exist.  Create? (y/n): ", filename);
-                       fflush(stdout);
-                       if (fgets(ans, sizeof(ans), stdin) == NULL)
-                               exit(1);
-                       switch (ans[0])
-                       {
-                               case 'y':
-                               case 'Y':
-                                       fp = fopen(filename, PG_BINARY_W);
-                                       if (fp == NULL)
-                                       {
-                                               perror(filename);
-                                               exit(1);
-                                       }
-                                       fclose(fp);
-                                       goto try_again;
-                               default:
-                                       /* cannot continue */
-                                       exit(1);
-                       }
-               }
-               else
-               {
-                       perror(filename);
-                       exit(1);
-               }
-       }
-
-       /* read all the entries */
-       for (npwds = 0;
-                npwds < MAXPWDS && fgets(line, sizeof(line), fp) != NULL;
-                ++npwds)
-       {
-               int                     l;
-               char       *p,
-                                  *q;
-
-               l = strlen(line);
-               if (line[l - 1] == '\n')
-                       line[l - 1] = '\0';
-               else
-               {
-                       fprintf(stderr, "%s:%d: line too long\n",
-                                       filename, npwds + 1);
-                       exit(1);
-               }
-
-               /* get user name */
-               p = line;
-               if ((q = strchr(p, ':')) != NULL)
-                       *q = '\0';
-
-               if (strlen(p) == 0)
-               {
-                       fprintf(stderr, "%s:%d: null user name\n",
-                                       filename, npwds + 1);
-                       exit(1);
-               }
-               pwds[npwds].uname = strdup(p);
-
-               /* check for duplicate user name */
-               for (i = 0; i < npwds; ++i)
-               {
-                       if (strcmp(pwds[i].uname, pwds[npwds].uname) == 0)
-                       {
-                               fprintf(stderr, "Duplicate username %s in entry %d\n",
-                                               pwds[npwds].uname, npwds + 1);
-                               exit(1);
-                       }
-               }
-
-               /* get password field */
-               if (q)
-               {
-                       p = q + 1;
-                       q = strchr(p, ':');
-
-                       if (q != NULL)
-                               *(q++) = '\0';
-
-                       if (strlen(p) != CRYPTED_PASSWD_LEN && strcmp(p, "+") != 0)
-                       {
-                               fprintf(stderr, "%s:%d: warning: invalid password length\n",
-                                               filename, npwds + 1);
-                       }
-                       pwds[npwds].pwd = strdup(p);
-               }
-               else
-                       pwds[npwds].pwd = NULL;
-
-               /* rest of the line is treated as is */
-               if (q == NULL)
-                       pwds[npwds].rest = NULL;
-               else
-                       pwds[npwds].rest = strdup(q);
-       }
-
-       fclose(fp);
-}
-
-static void
-write_pwd_file(char *filename, char *bkname)
-{
-       FILE       *fp;
-       int                     i;
-
-       /* make the backup file */
-link_again:
-       if (link(filename, bkname))
-       {
-               if (errno == EEXIST)
-               {
-                       unlink(bkname);
-                       goto link_again;
-               }
-               perror(bkname);
-               exit(1);
-       }
-       if (unlink(filename))
-       {
-               perror(filename);
-               exit(1);
-       }
-
-       /* open file */
-       if ((fp = fopen(filename, PG_BINARY_W)) == NULL)
-       {
-               perror(filename);
-               exit(1);
-       }
-
-       /* write file */
-       for (i = 0; i < npwds; ++i)
-       {
-               fprintf(fp, "%s", pwds[i].uname);
-               if (pwds[i].pwd)
-                       fprintf(fp, ":%s", pwds[i].pwd);
-               if (pwds[i].rest)
-                       fprintf(fp, ":%s", pwds[i].rest);
-               fprintf(fp, "\n");
-       }
-
-       fclose(fp);
-}
-
-static void
-encrypt_pwd(char key[CLEAR_PASSWD_LEN + 1],
-                       char salt[3],
-                       char passwd[CRYPTED_PASSWD_LEN + 1])
-{
-       int                     n;
-
-       /* select a salt, if not already given */
-       if (salt[0] == '\0')
-       {
-               srand(time(NULL));
-               do
-               {
-                       n = rand() % 256;
-               } while (!issaltchar(n));
-               salt[0] = n;
-               do
-               {
-                       n = rand() % 256;
-               } while (!issaltchar(n));
-               salt[1] = n;
-               salt[2] = '\0';
-       }
-
-       /* get encrypted password */
-       strcpy(passwd, crypt(key, salt));
-
-#ifdef PG_PASSWD_DEBUG
-       /* show it */
-       fprintf(stderr, "key = %s, salt = %s, password = %s\n",
-                       key, salt, passwd);
-#endif
-}
-
-static void
-prompt_for_username(char *username)
-{
-       int                     length;
-
-       printf("Username: ");
-       fflush(stdout);
-       if (fgets(username, NAMEDATALEN, stdin) == NULL)
-               username[0] = '\0';
-
-       length = strlen(username);
-       if (length > 0 && username[length - 1] != '\n')
-       {
-               /* eat rest of the line */
-               char            buf[128];
-               int                     buflen;
-
-               do
-               {
-                       if (fgets(buf, sizeof(buf), stdin) == NULL)
-                               break;
-                       buflen = strlen(buf);
-               } while (buflen > 0 && buf[buflen - 1] != '\n');
-       }
-       if (length > 0 && username[length - 1] == '\n')
-               username[length - 1] = '\0';
-}
-
-static void
-prompt_for_password(char *prompt, char *password)
-{
-       int                     length;
-
-#ifdef HAVE_TERMIOS_H
-       struct termios t_orig,
-                               t;
-#endif
-
-#ifdef HAVE_TERMIOS_H
-       tcgetattr(0, &t);
-       t_orig = t;
-       t.c_lflag &= ~ECHO;
-       tcsetattr(0, TCSADRAIN, &t);
-#endif
-
-       printf(prompt);
-       fflush(stdout);
-
-       if (fgets(password, CLEAR_PASSWD_LEN + 1, stdin) == NULL)
-               password[0] = '\0';
-
-#ifdef HAVE_TERMIOS_H
-       tcsetattr(0, TCSADRAIN, &t_orig);
-#endif
-
-       length = strlen(password);
-       if (length > 0 && password[length - 1] != '\n')
-       {
-               /* eat rest of the line */
-               char            buf[128];
-               int                     buflen;
-
-               do
-               {
-                       if (fgets(buf, sizeof(buf), stdin) == NULL)
-                               break;
-                       buflen = strlen(buf);
-               } while (buflen > 0 && buf[buflen - 1] != '\n');
-       }
-       if (length > 0 && password[length - 1] == '\n')
-               password[length - 1] = '\0';
-       printf("\n");
-}
-
-
-int
-main(int argc, char *argv[])
-{
-       char       *filename;
-       char            bkname[MAXPGPATH];
-       char            username[NAMEDATALEN];
-       char            salt[3];
-       char            key[CLEAR_PASSWD_LEN + 1],
-                               key2[CLEAR_PASSWD_LEN + 1];
-       char            e_passwd[CRYPTED_PASSWD_LEN + 1];
-       int                     i;
-
-       progname = argv[0];
-
-       if (argc != 2)
-       {
-               fprintf(stderr, "%s: too %s arguments\nTry '%s --help' for more information.\n",
-                               progname, argc > 2 ? "many" : "few", progname);
-               exit(1);
-       }
-
-       if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
-       {
-               usage();
-               exit(0);
-       }
-       if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
-       {
-               puts("pg_passwd (PostgreSQL) " PG_VERSION);
-               exit(0);
-       }
-       if (argv[1][0] == '-')
-       {
-               fprintf(stderr, "%s: invalid option: %s\nTry '%s --help' for more information.\n",
-                               progname, argv[1], progname);
-               exit(1);
-       }
-
-       filename = argv[1];
-
-       /* open file */
-       read_pwd_file(filename);
-
-       /* ask for the user name and the password */
-       prompt_for_username(username);
-       prompt_for_password("New password: ", key);
-       prompt_for_password("Re-enter new password: ", key2);
-       if (strcmp(key, key2) != 0)
-       {
-               fprintf(stderr, "Password mismatch\n");
-               exit(1);
-       }
-       salt[0] = '\0';
-       encrypt_pwd(key, salt, e_passwd);
-
-       /* check password entry */
-       for (i = 0; i < npwds; ++i)
-       {
-               if (strcmp(pwds[i].uname, username) == 0)
-               {                                               /* found */
-                       pwds[i].pwd = strdup(e_passwd);
-                       break;
-               }
-       }
-       if (i == npwds)
-       {                                                       /* did not exist */
-               if (npwds == MAXPWDS)
-               {
-                       fprintf(stderr, "Cannot handle so many entries\n");
-                       exit(1);
-               }
-               pwds[npwds].uname = strdup(username);
-               pwds[npwds].pwd = strdup(e_passwd);
-               pwds[npwds].rest = NULL;
-               ++npwds;
-       }
-
-       /* write back the file */
-       sprintf(bkname, "%s.bk", filename);
-       write_pwd_file(filename, bkname);
-
-       return 0;
-}
index ac32bd6..cd939a9 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_proc.h,v 1.224 2002/03/29 19:06:19 tgl Exp $
+ * $Id: pg_proc.h,v 1.225 2002/04/04 04:25:52 momjian Exp $
  *
  * NOTES
  *       The script catalog/genbki.sh reads this file and generates .bki
@@ -2101,8 +2101,8 @@ DESCR("does not match LIKE expression, case-insensitive");
 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");
+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 351c2d6..046e022 100644 (file)
@@ -3,15 +3,23 @@
  * user.h
  *
  *
- * $Id: user.h,v 1.17 2002/03/01 22:45:17 petere Exp $
+ * $Id: user.h,v 1.18 2002/04/04 04:25:53 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
 #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,6 +29,6 @@ extern void CreateGroup(CreateGroupStmt *stmt);
 extern void AlterGroup(AlterGroupStmt *stmt, const char *tag);
 extern void DropGroup(DropGroupStmt *stmt);
 
-extern Datum update_pg_pwd(PG_FUNCTION_ARGS);
+extern Datum update_pg_pwd_and_pg_group(PG_FUNCTION_ARGS);
 
 #endif   /* USER_H */
index 458be1f..a521a0e 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: crypt.h,v 1.19 2001/11/12 01:52:46 momjian Exp $
+ * $Id: crypt.h,v 1.20 2002/04/04 04:25:53 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -15,8 +15,6 @@
 
 #include "libpq/libpq-be.h"
 
-#define CRYPT_PWD_FILE_SEPSTR  "\t"
-
 /* Also defined in interfaces/odbc/md5.h */
 #define MD5_PASSWD_LEN 35
 
@@ -24,9 +22,6 @@
                                                 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);
 extern bool md5_hash(const void *buff, size_t len, char *hexsum);
index 0d5ddba..b9daf98 100644 (file)
@@ -4,7 +4,7 @@
  *       Interface to hba.c
  *
  *
- * $Id: hba.h,v 1.31 2001/11/05 17:46:33 momjian Exp $
+ * $Id: hba.h,v 1.32 2002/04/04 04:25:54 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
 #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 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 */
 
@@ -46,8 +45,15 @@ typedef enum UserAuth
 
 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);
-extern void load_hba_and_ident(void);
 
 #endif
index 4fb6447..99f7fae 100644 (file)
@@ -12,7 +12,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: miscadmin.h,v 1.101 2002/03/04 01:46:04 tgl Exp $
+ * $Id: miscadmin.h,v 1.102 2002/04/04 04:25:51 momjian Exp $
  *
  * NOTES
  *       some of the information in this file should be moved to
@@ -219,7 +219,6 @@ extern int FindExec(char *full_path, const char *argv0,
 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
index be08da8..39130f2 100644 (file)
@@ -30,7 +30,7 @@ WHERE (p1.prolang = 0 OR p1.prorettype = 0 OR
        AND p1.proname !~ '^pl[^_]+_call_handler$'
        AND p1.proname !~ '^RI_FKey_'
        AND p1.proname !~ 'costestimate$'
-       AND p1.proname != 'update_pg_pwd';
+       AND p1.proname != 'update_pg_pwd_and_pg_group';
  oid | proname 
 -----+---------
 (0 rows)
index 7a5991a..6a95c4c 100644 (file)
@@ -33,7 +33,7 @@ WHERE (p1.prolang = 0 OR p1.prorettype = 0 OR
        AND p1.proname !~ '^pl[^_]+_call_handler$'
        AND p1.proname !~ '^RI_FKey_'
        AND p1.proname !~ 'costestimate$'
-       AND p1.proname != 'update_pg_pwd';
+       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