This patch implements a variety of authorization improvements:
A new pg_hba.conf column, USER
Allow specifiction of lists of users separated by commas
Allow group names specified by +
Allow include files containing lists of users specified by @
Allow lists of databases, and database files
Allow samegroup in database column to match group name matching dbname
Removal of secondary password files
Remove pg_passwd utility
Lots of code cleanup in user.c and hba.c
New data/global/pg_pwd format
New data/global/pg_group file
--
Bruce Momjian | http://candle.pha.pa.us
pgman@candle.pha.pa.us | (610) 853-3000
+ If your life is a hard drive, | 830 Blythe Avenue
+ Christ can be your backup. | Drexel Hill, Pennsylvania 19026
Index: doc/src/sgml/client-auth.sgml
===================================================================
RCS file: /cvsroot/pgsql/doc/src/sgml/client-auth.sgml,v
retrieving revision 1.33
diff -c -r1.33 client-auth.sgml
*** doc/src/sgml/client-auth.sgml 22 Mar 2002 19:20:06 -0000 1.33
--- doc/src/sgml/client-auth.sgml 3 Apr 2002 03:48:41 -0000
***************
*** 10,23 ****
</indexterm>
<para>
! When a client application connects to the database server, it specifies which
! <productname>PostgreSQL</productname> user name it wants to connect as,
! much the same way one logs into a Unix computer as a particular user.
! Within the SQL environment the active
! database user name determines access privileges to database
! objects -- see <xref linkend="user-manag"> for more information
! about that. It is therefore obviously essential to restrict which
! database user name(s) a given client can connect as.
</para>
<para>
--- 10,22 ----
</indexterm>
<para>
! When a client application connects to the database server, it
! specifies which <productname>PostgreSQL</productname> user name it
! wants to connect as, much the same way one logs into a Unix computer
! as a particular user. Within the SQL environment the active database
! user name determines access privileges to database objects -- see
! <xref linkend="user-manag"> for more information. Therefore, it is
! essential to restrict which database users can connect.
</para>
<para>
***************
*** 30,49 ****
<para>
<productname>PostgreSQL</productname> offers a number of different
! client authentication methods. The method to be used can be selected
! on the basis of (client) host and database; some authentication methods
! allow you to restrict by user name as well.
</para>
<para>
! <productname>PostgreSQL</productname> database user names are logically
separate from user names of the operating system in which the server
! runs. If all the users of a particular server also have accounts on
the server's machine, it makes sense to assign database user names
! that match their operating system user names. However, a server that accepts remote
! connections may have many users who have no local account, and in such
! cases there need be no connection between database user names and OS
! user names.
</para>
<sect1 id="pg-hba-conf">
--- 29,47 ----
<para>
<productname>PostgreSQL</productname> offers a number of different
! client authentication methods. The method to be used can be selected
! on the basis of (client) host, database, and user.
</para>
<para>
! <productname>PostgreSQL</productname> user names are logically
separate from user names of the operating system in which the server
! runs. If all the users of a particular server also have accounts on
the server's machine, it makes sense to assign database user names
! that match their operating system user names. However, a server that
! accepts remote connections may have many users who have no local
! account, and in such cases there need be no connection between
! database user names and OS user names.
</para>
<sect1 id="pg-hba-conf">
***************
*** 56,94 ****
<para>
Client authentication is controlled by the file
<filename>pg_hba.conf</filename> in the data directory, e.g.,
! <filename>/usr/local/pgsql/data/pg_hba.conf</filename>. (<acronym>HBA</> stands
! for host-based authentication.) A default <filename>pg_hba.conf</filename>
! file is installed when the
! data area is initialized by <command>initdb</command>.
</para>
<para>
! The general format of the <filename>pg_hba.conf</filename> file is
! of a set of records, one per line. Blank lines and lines beginning
! with a hash character (<quote>#</quote>) are ignored. A record is
! made up of a number of fields which are separated by spaces and/or
! tabs. Records cannot be continued across lines.
</para>
<para>
Each record specifies a connection type, a client IP address range
! (if relevant for the connection type), a database name or names,
and the authentication method to be used for connections matching
! these parameters.
! The first record that matches the type, client address, and requested
! database name of a connection attempt is used to do the
! authentication step. There is no <quote>fall-through</> or
<quote>backup</>: if one record is chosen and the authentication
! fails, the following records are not considered. If no record
! matches, the access will be denied.
</para>
<para>
A record may have one of the three formats
<synopsis>
! local <replaceable>database</replaceable> <replaceable>authentication-method</replaceable> [
<replaceable>authentication-option</replaceable>]
! host <replaceable>database</replaceable> <replaceable>IP-address</replaceable> <replaceable>IP-mask</replaceable>
<replaceable>authentication-method</replaceable>[ <replaceable>authentication-option</replaceable> ]
! hostssl <replaceable>database</replaceable> <replaceable>IP-address</replaceable> <replaceable>IP-mask</replaceable>
<replaceable>authentication-method</replaceable>[ <replaceable>authentication-option</replaceable> ]
</synopsis>
The meaning of the fields is as follows:
--- 54,92 ----
<para>
Client authentication is controlled by the file
<filename>pg_hba.conf</filename> in the data directory, e.g.,
! <filename>/usr/local/pgsql/data/pg_hba.conf</filename>.
! (<acronym>HBA</> stands for host-based authentication.) A default
! <filename>pg_hba.conf</filename> file is installed when the data area
! is initialized by <command>initdb</command>.
</para>
<para>
! The general format of the <filename>pg_hba.conf</filename> file is of
! a set of records, one per line. Blank lines are ignored, as is any
! text after the <quote>#</quote> comment character. A record is made
! up of a number of fields which are separated by spaces and/or tabs.
! Fields can contain white space if the field value is quoted. Records
! cannot be continued across lines.
</para>
<para>
Each record specifies a connection type, a client IP address range
! (if relevant for the connection type), a database name, a user name,
and the authentication method to be used for connections matching
! these parameters. The first record with a matching connection type,
! client address, requested database, and user name is used to perform
! authentication. There is no <quote>fall-through</> or
<quote>backup</>: if one record is chosen and the authentication
! fails, subsequent records are not considered. If no record matches,
! access is denied.
</para>
<para>
A record may have one of the three formats
<synopsis>
! local <replaceable>database</replaceable> <replaceable>user</replaceable>
<replaceable>authentication-method</replaceable>[ <replaceable>authentication-option</replaceable> ]
! host <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>IP-address</replaceable>
<replaceable>IP-mask</replaceable><replaceable>authentication-method</replaceable>
! hostssl <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>IP-address</replaceable>
<replaceable>IP-mask</replaceable><replaceable>authentication-method</replaceable>
</synopsis>
The meaning of the fields is as follows:
***************
*** 97,103 ****
<term><literal>local</literal></term>
<listitem>
<para>
! This record pertains to connection attempts over Unix domain
sockets.
</para>
</listitem>
--- 95,101 ----
<term><literal>local</literal></term>
<listitem>
<para>
! This record applies to connection attempts using Unix domain
sockets.
</para>
</listitem>
***************
*** 107,116 ****
<term><literal>host</literal></term>
<listitem>
<para>
! This record pertains to connection attempts over TCP/IP
! networks. Note that TCP/IP connections are completely disabled
! unless the server is started with the <option>-i</option> switch or
! the equivalent configuration parameter is set.
</para>
</listitem>
</varlistentry>
--- 105,115 ----
<term><literal>host</literal></term>
<listitem>
<para>
! This record applied to connection attempts using TCP/IP networks.
! Note that TCP/IP connections are disabled unless the server is
! started with the <option>-i</option> option or the
! <literal>tcpip_socket</> <filename>postgresql.conf</>
! configuration parameter is enabled.
</para>
</listitem>
</varlistentry>
***************
*** 119,131 ****
<term><literal>hostssl</literal></term>
<listitem>
<para>
! This record pertains to connection attempts with SSL over
TCP/IP. To make use of this option the server must be
built with SSL support enabled. Furthermore, SSL must be
enabled with the <option>-l</> option or equivalent configuration
setting when the server is started. (Note: <literal>host</literal>
records will match either SSL or non-SSL connection attempts, but
! <literal>hostssl</literal> records match only SSL connections.)
</para>
</listitem>
</varlistentry>
--- 118,130 ----
<term><literal>hostssl</literal></term>
<listitem>
<para>
! This record applies to connection attempts using SSL over
TCP/IP. To make use of this option the server must be
built with SSL support enabled. Furthermore, SSL must be
enabled with the <option>-l</> option or equivalent configuration
setting when the server is started. (Note: <literal>host</literal>
records will match either SSL or non-SSL connection attempts, but
! <literal>hostssl</literal> records requires SSL connections.)
</para>
</listitem>
</varlistentry>
***************
*** 134,145 ****
<term><replaceable>database</replaceable></term>
<listitem>
<para>
! Specifies the database that this record applies to. The value
<literal>all</literal> specifies that it applies to all
databases, while the value <literal>sameuser</> identifies the
! database with the same name as the connecting user. Otherwise,
! this is the name of a specific <productname>PostgreSQL</productname>
! database.
</para>
</listitem>
</varlistentry>
--- 133,167 ----
<term><replaceable>database</replaceable></term>
<listitem>
<para>
! Specifies the database for this record. The value
<literal>all</literal> specifies that it applies to all
databases, while the value <literal>sameuser</> identifies the
! database with the same name as the connecting user. The value
! <literal>samegroup</> identifies a group with the same name as
! the database name. Only members of this group can connect to the
! database. Otherwise, this is the name of a specific
! <productname>PostgreSQL</productname> database. Multiple database
! names can be supplied by separating them with commas. A file
! containing database names can be specified by preceding the file
! name with <literal>@</>. The file must be in the same directory
! as <filename>pg_hba.conf</>.
! </para>
! </listitem>
! </varlistentry>
!
! <varlistentry>
! <term><replaceable>user</replaceable></term>
! <listitem>
! <para>
! Specifies the user for this record. The value
! <literal>all</literal> specifies that it applies to all users.
! Otherwise, this is the name of a specific
! <productname>PostgreSQL</productname> user. Multiple user names
! can be supplied by separating them with commas. Group names can
! be specified by preceding the group name with <literal>+</>. A
! file containing user names can be specified by preceding the file
! name with <literal>@</>. The file must be in the same directory
! as <filename>pg_hba.conf</>.
</para>
</listitem>
</varlistentry>
***************
*** 149,158 ****
<term><replaceable>IP mask</replaceable></term>
<listitem>
<para>
! These two fields specify to which client machines a
! <literal>host</literal> or <literal>hostssl</literal>
! record applies, based on their IP
! address. (Of course IP addresses can be spoofed but this
consideration is beyond the scope of
<productname>PostgreSQL</productname>.) The precise logic is that
<blockquote>
--- 171,179 ----
<term><replaceable>IP mask</replaceable></term>
<listitem>
<para>
! These two fields specify the client machine IP addresses
! (<literal>host</literal> or <literal>hostssl</literal>) for this
! record. (Of course IP addresses can be spoofed but this
consideration is beyond the scope of
<productname>PostgreSQL</productname>.) The precise logic is that
<blockquote>
***************
*** 169,178 ****
<term><replaceable>authentication method</replaceable></term>
<listitem>
<para>
! Specifies the method that users must use to authenticate themselves
! when connecting under the control of this authentication record.
! The possible choices are summarized here,
! details are in <xref linkend="auth-methods">.
<variablelist>
<varlistentry>
--- 190,198 ----
<term><replaceable>authentication method</replaceable></term>
<listitem>
<para>
! Specifies the authentication method to use when connecting via
! this record. The possible choices are summarized here; details
! are in <xref linkend="auth-methods">.
<variablelist>
<varlistentry>
***************
*** 190,255 ****
<term><literal>reject</></term>
<listitem>
<para>
! The connection is rejected unconditionally. This is mostly
! useful to <quote>filter out</> certain hosts from a group.
</para>
</listitem>
</varlistentry>
<varlistentry>
! <term><literal>password</></term>
<listitem>
<para>
! The client is required to supply a password which is required to
! match the database password that was set up for the user.
! </para>
!
! <para>
! An optional file name may be specified after the
! <literal>password</literal> keyword. This file is expected to
! contain a list of users who may connect using this record,
! and optionally alternative passwords for them.
! </para>
!
! <para>
! The password is sent over the wire in clear text. For better
! protection, use the <literal>md5</literal> or
! <literal>crypt</literal> methods.
</para>
</listitem>
</varlistentry>
<varlistentry>
! <term><literal>md5</></term>
<listitem>
<para>
! Like the <literal>password</literal> method, but the password
! is sent over the wire encrypted using a simple
! challenge-response protocol. This protects against incidental
! wire-sniffing. This is now the recommended choice for
! password-based authentication.
! </para>
!
! <para>
! The name of a file may follow the
! <literal>md5</literal> keyword. It contains a list of users
! who may connect using this record.
</para>
</listitem>
</varlistentry>
<varlistentry>
! <term><literal>crypt</></term>
<listitem>
<para>
! Like the <literal>md5</literal> method but uses older crypt
! encryption, which is needed for pre-7.2
! clients. <literal>md5</literal> is
! preferred for 7.2 and later clients. The <literal>crypt</>
! method is not compatible with encrypting passwords in
! <filename>pg_shadow</>, and may fail if client and server
! machines have different implementations of the crypt() library
! routine.
</para>
</listitem>
</varlistentry>
--- 210,250 ----
<term><literal>reject</></term>
<listitem>
<para>
! The connection is rejected unconditionally. This is useful for
! <quote>filtering out</> certain hosts from a group.
</para>
</listitem>
</varlistentry>
<varlistentry>
! <term><literal>md5</></term>
<listitem>
<para>
! Requires the client to supply an MD5 encrypted password for
! authentication. This is the only method that allows encrypted
! passwords to be stored in pg_shadow.
</para>
</listitem>
</varlistentry>
<varlistentry>
! <term><literal>crypt</></term>
<listitem>
<para>
! Like <literal>md5</literal> method but uses older crypt
! encryption, which is needed for pre-7.2 clients.
! <literal>md5</literal> is preferred for 7.2 and later clients.
</para>
</listitem>
</varlistentry>
<varlistentry>
! <term><literal>password</></term>
<listitem>
<para>
! Same as "md5", but the password is sent in cleartext over the
! network. This should not be used on untrusted networks.
! </para>
</para>
</listitem>
</varlistentry>
***************
*** 278,311 ****
<term><literal>ident</></term>
<listitem>
<para>
! The identity of the user as determined on login to the
! operating system is used by <productname>PostgreSQL</productname>
! to determine whether the user
! is allowed to connect as the requested database user.
! For TCP/IP connections the user's identity is determined by
! contacting the <firstterm>ident</firstterm> server on the client
! host. (Note that this is only as reliable as the remote ident
! server; ident authentication should never be used for remote hosts
! whose administrators are not trustworthy.)
! On operating systems
! supporting <symbol>SO_PEERCRED</> requests for Unix domain sockets,
! ident authentication is possible for local connections;
! the system is then asked for the connecting user's identity.
</para>
<para>
! On systems without <symbol>SO_PEERCRED</> requests, ident authentication
! is only available for TCP/IP connections. As a workaround,
! it is possible to
! specify the <systemitem class="systemname">localhost</> address
! <systemitem class="systemname">127.0.0.1</> and make connections
! to this address.
! </para>
! <para>
! The <replaceable>authentication option</replaceable> following
! the <literal>ident</> keyword specifies the name of an
! <firstterm>ident map</firstterm> that specifies which operating
! system users equate with which database users. See below for
! details.
</para>
</listitem>
</varlistentry>
--- 273,308 ----
<term><literal>ident</></term>
<listitem>
<para>
! For TCP/IP connections, authentication is done by contacting
! the <firstterm>ident</firstterm> server on the client host.
! This is only as secure as the client machine. You must specify
! the map name after the 'ident' keyword. It determines how to
! map remote user names to PostgreSQL user names. If you use
! "sameuser", the user names are assumed to be identical. If
! not, the map name is looked up in the $PGDATA/pg_ident.conf
! file. The connection is accepted if that file contains an
! entry for this map name with the ident-supplied user name and
! the requested PostgreSQL user name.
! </para>
! <para>
! On machines that support unix-domain socket credentials
! (currently Linux, FreeBSD, NetBSD, and BSD/OS), ident allows
! reliable authentication of 'local' connections without ident
! running on the local machine.
! </para>
! <para>
! On systems without <symbol>SO_PEERCRED</> requests, ident
! authentication is only available for TCP/IP connections. As a
! work around, it is possible to specify the <systemitem
! class="systemname">localhost</> address <systemitem
! class="systemname">127.0.0.1</> and make connections to this
! address.
</para>
<para>
! Following the <literal>ident</> keyword, an <firstterm>ident
! map</firstterm> name should be supplied which specifies which
! operating system users equate with which database users. See
! below for details.
</para>
</listitem>
</varlistentry>
***************
*** 315,331 ****
<listitem>
<para>
This authentication type operates similarly to
! <firstterm>password</firstterm>, with the main difference that
! it will use PAM (Pluggable Authentication Modules) as the
! authentication mechanism. The <replaceable>authentication
! option</replaceable> following the <literal>pam</> keyword
! specifies the service name that will be passed to PAM. The
! default service name is <literal>postgresql</literal>.
! For more information about PAM, please read the <ulink
! url="http://www.kernel.org/pub/linux/libs/pam/"><productname>Linux-PAM</productname>
! Page</ulink> and/or the <ulink
! url="http://www.sun.com/software/solaris/pam/"><systemitem class="osname">Solaris</> PAM
! Page</ulink>.
</para>
</listitem>
</varlistentry>
--- 312,327 ----
<listitem>
<para>
This authentication type operates similarly to
! <firstterm>password</firstterm> except that it uses PAM
! (Pluggable Authentication Modules) as the authentication
! mechanism. The default PAM service name is
! <literal>postgresql</literal>. You can optionally supply you
! own service name after the <literal>pam</> keyword in the
! file. For more information about PAM, please read the <ulink
! url="http://www.kernel.org/pub/linux/libs/pam/"><productname>L
! inux-PAM</productname> Page</ulink> and the <ulink
! url="http://www.sun.com/software/solaris/pam/"><systemitem
! class="osname">Solaris</> PAM Page</ulink>.
</para>
</listitem>
</varlistentry>
***************
*** 336,377 ****
</listitem>
</varlistentry>
- <varlistentry>
- <term><replaceable>authentication option</replaceable></term>
- <listitem>
- <para>
- This field is interpreted differently depending on the
- authentication method, as described above.
- </para>
- </listitem>
- </varlistentry>
</variablelist>
</para>
<para>
Since the <filename>pg_hba.conf</filename> records are examined
sequentially for each connection attempt, the order of the records is
! very significant. Typically, earlier records will have tight
! connection match parameters and weaker authentication methods,
! while later records will have looser match parameters and stronger
! authentication methods. For example, one might wish to use
! <literal>trust</> authentication for local TCP connections but
! require a password for remote TCP connections. In this case a
! record specifying <literal>trust</> authentication for connections
! from 127.0.0.1 would appear before a record specifying password
! authentication for a wider range of allowed client IP addresses.
</para>
<para>
<indexterm>
<primary>SIGHUP</primary>
</indexterm>
! The <filename>pg_hba.conf</filename> file is read on start-up
! and when the <application>postmaster</> receives a
<systemitem>SIGHUP</systemitem> signal. If you edit the file on an
active system, you will need to signal the <application>postmaster</>
! (using <literal>pg_ctl reload</> or <literal>kill -HUP</>)
! to make it re-read the file.
</para>
<para>
--- 332,364 ----
</listitem>
</varlistentry>
</variablelist>
</para>
<para>
Since the <filename>pg_hba.conf</filename> records are examined
sequentially for each connection attempt, the order of the records is
! significant. Typically, earlier records will have tight connection
! match parameters and weaker authentication methods, while later
! records will have looser match parameters and stronger authentication
! methods. For example, one might wish to use <literal>trust</>
! authentication for local TCP connections but require a password for
! remote TCP connections. In this case a record specifying
! <literal>trust</> authentication for connections from 127.0.0.1 would
! appear before a record specifying password authentication for a wider
! range of allowed client IP addresses.
</para>
<para>
<indexterm>
<primary>SIGHUP</primary>
</indexterm>
! The <filename>pg_hba.conf</filename> file is read on start-up and when
! the <application>postmaster</> receives a
<systemitem>SIGHUP</systemitem> signal. If you edit the file on an
active system, you will need to signal the <application>postmaster</>
! (using <literal>pg_ctl reload</> or <literal>kill -HUP</>) to make it
! re-read the file.
</para>
<para>
***************
*** 382,408 ****
<example id="example-pg-hba.conf">
<title>An example <filename>pg_hba.conf</filename> file</title>
<programlisting>
! # TYPE DATABASE IP_ADDRESS MASK AUTHTYPE MAP
# Allow any user on the local system to connect to any
! # database under any username, but only via an IP connection:
! host all 127.0.0.1 255.255.255.255 trust
# The same, over Unix-socket connections:
! local all trust
# Allow any user from any host with IP address 192.168.93.x to
! # connect to database "template1" as the same username that ident on that
! # host identifies him as (typically his Unix username):
! host template1 192.168.93.0 255.255.255.0 ident sameuser
# Allow a user from host 192.168.12.10 to connect to database "template1"
! # if the user's password in pg_shadow is correctly supplied:
! host template1 192.168.12.10 255.255.255.255 md5
# In the absence of preceding "host" lines, these two lines will reject
# all connection attempts from 192.168.54.1 (since that entry will be
--- 369,395 ----
<example id="example-pg-hba.conf">
<title>An example <filename>pg_hba.conf</filename> file</title>
<programlisting>
! # TYPE DATABASE USER IP_ADDRESS MASK AUTHTYPE
# Allow any user on the local system to connect to any
! # database under any user name, but only via an IP connection:
! host all all 127.0.0.1 255.255.255.255 trust
# The same, over Unix-socket connections:
! local all all trust
# Allow any user from any host with IP address 192.168.93.x to
! # connect to database "template1" as the same user name that ident on that
! # host identifies him as (typically his Unix user name):
! host template1 all 192.168.93.0 255.255.255.0 ident sameuser
# Allow a user from host 192.168.12.10 to connect to database "template1"
! # if the user's password is correctly supplied:
! host template1 all 192.168.12.10 255.255.255.255 md5
# In the absence of preceding "host" lines, these two lines will reject
# all connection attempts from 192.168.54.1 (since that entry will be
***************
*** 410,417 ****
# else on the Internet. The zero mask means that no bits of the host IP
# address are considered, so it matches any host:
! host all 192.168.54.1 255.255.255.255 reject
! host all 0.0.0.0 0.0.0.0 krb5
# Allow users from 192.168.x.x hosts to connect to any database, if they
# pass the ident check. If, for example, ident says the user is "bryanh"
--- 397,404 ----
# else on the Internet. The zero mask means that no bits of the host IP
# address are considered, so it matches any host:
! host all all 192.168.54.1 255.255.255.255 reject
! host all all 0.0.0.0 0.0.0.0 krb5
# Allow users from 192.168.x.x hosts to connect to any database, if they
# pass the ident check. If, for example, ident says the user is "bryanh"
***************
*** 419,425 ****
# is allowed if there is an entry in pg_ident.conf for map "omicron" that
# says "bryanh" is allowed to connect as "guest1":
! host all 192.168.0.0 255.255.0.0 ident omicron
# If these are the only two lines for local connections, they will allow
# local users to connect only to their own databases (database named the
--- 406,412 ----
# is allowed if there is an entry in pg_ident.conf for map "omicron" that
# says "bryanh" is allowed to connect as "guest1":
! host all all 192.168.0.0 255.255.0.0 ident omicron
# If these are the only two lines for local connections, they will allow
# local users to connect only to their own databases (database named the
***************
*** 429,436 ****
# cases. (If you prefer to use ident authorization, an ident map can
# serve a parallel purpose to the password list file used here.)
! local sameuser md5
! local all md5 admins
</programlisting>
</example>
</para>
--- 416,423 ----
# cases. (If you prefer to use ident authorization, an ident map can
# serve a parallel purpose to the password list file used here.)
! local sameuser all md5
! local all @admins md5
</programlisting>
</example>
</para>
***************
*** 490,575 ****
<title>Password authentication</title>
<indexterm>
! <primary>password</primary>
</indexterm>
<indexterm>
! <primary>MD5</>
</indexterm>
<para>
Password-based authentication methods include <literal>md5</>,
! <literal>crypt</>, and <literal>password</>. These methods operate
similarly except for the way that the password is sent across the
! connection. If you are at all concerned about password <quote>sniffing</>
! attacks then <literal>md5</> is preferred, with <literal>crypt</> a
! second choice if you must support obsolete clients. Plain
! <literal>password</> should especially be avoided for connections over
! the open Internet (unless you use SSL, SSH, or other communications
! security wrappers around the connection).
</para>
<para>
! <productname>PostgreSQL</productname> database passwords are separate from
! operating system user passwords. Ordinarily, the password for each
! database user is stored in the pg_shadow system catalog table.
! Passwords can be managed with the query language commands
! <command>CREATE USER</command> and <command>ALTER USER</command>,
! e.g., <userinput>CREATE USER foo WITH PASSWORD
! 'secret';</userinput>. By default, that is, if no password has
! been set up, the stored password is <literal>NULL</literal>
! and password authentication will always fail for that user.
</para>
<para>
To restrict the set of users that are allowed to connect to certain
! databases, list the set of users in a separate file (one user name
! per line) in the same directory that <filename>pg_hba.conf</> is in,
! and mention the (base) name of the file after the
! <literal>password</>, <literal>md5</>, or <literal>crypt</> keyword,
! respectively, in <filename>pg_hba.conf</>. If you do not use this
! feature, then any user that is known to the database system can
! connect to any database (so long as he supplies the correct password,
! of course).
! </para>
!
! <para>
! These files can also be used to apply a different set of passwords
! to a particular database or set thereof. In that case, the files
! have a format similar to the standard Unix password file
! <filename>/etc/passwd</filename>, that is,
! <synopsis>
! <replaceable>username</replaceable>:<replaceable>password</replaceable>
! </synopsis>
! Any extra colon-separated fields following the password are
! ignored. The password is expected to be encrypted using the
! system's <function>crypt()</function> function. The utility
! program <application>pg_passwd</application> that is installed
! with <productname>PostgreSQL</productname> can be used to manage
! these password files.
! </para>
!
! <para>
! Lines with and without passwords can be mixed in secondary
! password files. Lines without password indicate use of the main
! password in <literal>pg_shadow</> that is managed by
! <command>CREATE USER</> and <command>ALTER USER</>. Lines with
! passwords will cause that password to be used. A password entry of
! <quote>+</quote> also means using the pg_shadow password.
! </para>
!
! <para>
! Alternative passwords cannot be used when using the <literal>md5</>
! or <literal>crypt</> methods. The file will be read as
! usual, but the password field will simply be ignored and the
! <literal>pg_shadow</> password will always be used.
! </para>
!
! <para>
! Note that using alternative passwords like this means that one can
! no longer use <command>ALTER USER</command> to change one's
! password. It will appear to work but the password one is
! changing is not the password that the system will end up
! using.
</para>
</sect2>
--- 477,525 ----
<title>Password authentication</title>
<indexterm>
! <primary>MD5</>
</indexterm>
<indexterm>
! <primary>crypt</>
! </indexterm>
! <indexterm>
! <primary>password</primary>
</indexterm>
<para>
Password-based authentication methods include <literal>md5</>,
! <literal>crypt</>, and <literal>password</>. These methods operate
similarly except for the way that the password is sent across the
! connection. If you are at all concerned about password
! <quote>sniffing</> attacks then <literal>md5</> is preferred, with
! <literal>crypt</> a second choice if you must support pre-7.2
! clients. Plain <literal>password</> should especially be avoided for
! connections over the open Internet (unless you use SSL, SSH, or
! other communications security wrappers around the connection).
</para>
<para>
! <productname>PostgreSQL</productname> database passwords are
! separate from operating system user passwords. Ordinarily, the
! password for each database user is stored in the pg_shadow system
! catalog table. Passwords can be managed with the query language
! commands <command>CREATE USER</command> and <command>ALTER
! USER</command>, e.g., <userinput>CREATE USER foo WITH PASSWORD
! 'secret';</userinput>. By default, that is, if no password has been
! set up, the stored password is <literal>NULL</literal> and password
! authentication will always fail for that user.
</para>
<para>
To restrict the set of users that are allowed to connect to certain
! databases, list the users separated by commas, or in a separate
! file. The file should contain user names separated by commas or one
! user name per line, and be in the same directory as
! <filename>pg_hba.conf</>. Mention the (base) name of the file
! preceded with <literal>@</>in the <literal>USER</> column. The
! <literal>DATABASE</> column can similarly accept a list of values or
! a file name. You can also specify group names by preceding the group
! name with <literal>+</>.
</para>
</sect2>
***************
*** 588,597 ****
<productname>Kerberos</productname> system is far beyond the scope
of this document; in all generality it can be quite complex (yet
powerful). The <ulink
! url="http://www.nrl.navy.mil/CCS/people/kenh/kerberos-faq.html">Kerberos
! <acronym>FAQ</></ulink> or <ulink
! url="ftp://athena-dist.mit.edu">MIT Project Athena</ulink> can be
! a good starting point for exploration. Several sources for
<productname>Kerberos</> distributions exist.
</para>
--- 538,547 ----
<productname>Kerberos</productname> system is far beyond the scope
of this document; in all generality it can be quite complex (yet
powerful). The <ulink
! url="http://www.nrl.navy.mil/CCS/people/kenh/kerberos-faq.html">Kerb
! eros <acronym>FAQ</></ulink> or <ulink
! url="ftp://athena-dist.mit.edu">MIT Project Athena</ulink> can be a
! good starting point for exploration. Several sources for
<productname>Kerberos</> distributions exist.
</para>
***************
*** 606,639 ****
<para>
<productname>PostgreSQL</> operates like a normal Kerberos service.
The name of the service principal is
! <replaceable>servicename/hostname@realm</>, where
! <replaceable>servicename</> is <literal>postgres</literal>
! (unless a different service name was selected at configure time
! with <literal>./configure --with-krb-srvnam=whatever</>).
! <replaceable>hostname</> is the fully qualified domain name of the server
! machine. The service principal's realm is the preferred realm of the
! server machine.
</para>
<para>
! Client principals must have their <productname>PostgreSQL</> user name as
! their first component, for example
! <replaceable>pgusername/otherstuff@realm</>.
! At present the realm of the client is not checked by
! <productname>PostgreSQL</>; so
! if you have cross-realm authentication enabled, then any principal
! in any realm that can communicate with yours will be accepted.
</para>
<para>
! Make sure that your server key file is readable (and
! preferably only readable) by the
! <productname>PostgreSQL</productname> server account (see
! <xref linkend="postgres-user">). The location of the key file
! is specified with the <varname>krb_server_keyfile</> run time
! configuration parameter. (See also <xref linkend="runtime-config">.)
! The default is <filename>/etc/srvtab</> if you are using Kerberos 4
! and <filename>FILE:/usr/local/pgsql/etc/krb5.keytab</> (or whichever
directory was specified as <varname>sysconfdir</> at build time)
with Kerberos 5.
</para>
--- 556,588 ----
<para>
<productname>PostgreSQL</> operates like a normal Kerberos service.
The name of the service principal is
! <replaceable>servicename/hostname@realm</>, where
! <replaceable>servicename</> is <literal>postgres</literal> (unless a
! different service name was selected at configure time with
! <literal>./configure --with-krb-srvnam=whatever</>).
! <replaceable>hostname</> is the fully qualified domain name of the
! server machine. The service principal's realm is the preferred realm
! of the server machine.
</para>
<para>
! Client principals must have their <productname>PostgreSQL</> user
! name as their first component, for example
! <replaceable>pgusername/otherstuff@realm</>. At present the realm of
! the client is not checked by <productname>PostgreSQL</>; so if you
! have cross-realm authentication enabled, then any principal in any
! realm that can communicate with yours will be accepted.
</para>
<para>
! Make sure that your server key file is readable (and preferably only
! readable) by the <productname>PostgreSQL</productname> server
! account (see <xref linkend="postgres-user">). The location of the
! key file is specified with the <varname>krb_server_keyfile</> run
! time configuration parameter. (See also <xref
! linkend="runtime-config">.) The default is <filename>/etc/srvtab</>
! if you are using Kerberos 4 and
! <filename>FILE:/usr/local/pgsql/etc/krb5.keytab</> (or whichever
directory was specified as <varname>sysconfdir</> at build time)
with Kerberos 5.
</para>
***************
*** 649,666 ****
<para>
When connecting to the database make sure you have a ticket for a
! principal matching the requested database user name.
! An example: For database user name <literal>fred</>, both principal
<literal>fred@EXAMPLE.COM</> and
! <literal>fred/users.example.com@EXAMPLE.COM</> can be
! used to authenticate to the database server.
</para>
<para>
! If you use <application>mod_auth_krb</application> and <application>mod_perl</application> on your
<productname>Apache</productname>web server,
! you can use <literal>AuthType KerberosV5SaveCredentials</literal> with a <application>mod_perl</application>
! script. This gives secure database access over the web, no extra
! passwords required.
</para>
</sect2>
--- 598,617 ----
<para>
When connecting to the database make sure you have a ticket for a
! principal matching the requested database user name. An example: For
! database user name <literal>fred</>, both principal
<literal>fred@EXAMPLE.COM</> and
! <literal>fred/users.example.com@EXAMPLE.COM</> can be used to
! authenticate to the database server.
</para>
<para>
! If you use <application>mod_auth_krb</application> and
! <application>mod_perl</application> on your
! <productname>Apache</productname> web server, you can use
! <literal>AuthType KerberosV5SaveCredentials</literal> with a
! <application>mod_perl</application> script. This gives secure
! database access over the web, no extra passwords required.
</para>
</sect2>
***************
*** 707,761 ****
</para>
<para>
! On systems supporting <symbol>SO_PEERCRED</symbol> requests for Unix-domain sockets,
! ident authentication can also be applied to local connections. In this
! case, no security risk is added by using ident authentication; indeed
! it is a preferable choice for local connections on such a system.
</para>
<para>
When using ident-based authentication, after having determined the
name of the operating system user that initiated the connection,
! <productname>PostgreSQL</productname> checks whether that user is allowed
! to connect as the database user he is requesting to connect as.
! This is controlled by the ident map
! argument that follows the <literal>ident</> keyword in the
! <filename>pg_hba.conf</filename> file. There is a predefined ident map
! <literal>sameuser</literal>, which allows any operating system
! user to connect as the database user of the same name (if the
! latter exists). Other maps must be created manually.
</para>
<para>
! <indexterm><primary>pg_ident.conf</primary></indexterm>
! Ident maps other than <literal>sameuser</literal> are defined
! in the file <filename>pg_ident.conf</filename>
! in the data directory, which contains lines of the general form:
<synopsis>
<replaceable>map-name</> <replaceable>ident-username</> <replaceable>database-username</>
</synopsis>
! Comments and whitespace are handled in the usual way.
! The <replaceable>map-name</> is an arbitrary name that will be
! used to refer to this mapping in <filename>pg_hba.conf</filename>.
! The other two fields specify which operating system user is
! allowed to connect as which database user. The same
! <replaceable>map-name</> can be used repeatedly to specify more
! user-mappings within a single map. There is no restriction regarding
! how many
! database users a given operating system user may correspond to and vice
! versa.
</para>
<para>
<indexterm>
<primary>SIGHUP</primary>
</indexterm>
! The <filename>pg_ident.conf</filename> file is read on start-up
! and when the <application>postmaster</> receives a
<systemitem>SIGHUP</systemitem> signal. If you edit the file on an
active system, you will need to signal the <application>postmaster</>
! (using <literal>pg_ctl reload</> or <literal>kill -HUP</>)
! to make it re-read the file.
</para>
<para>
--- 658,711 ----
</para>
<para>
! On systems supporting <symbol>SO_PEERCRED</symbol> requests for
! Unix-domain sockets, ident authentication can also be applied to
! local connections. In this case, no security risk is added by using
! ident authentication; indeed it is a preferable choice for local
! connections on such systems.
</para>
<para>
When using ident-based authentication, after having determined the
name of the operating system user that initiated the connection,
! <productname>PostgreSQL</productname> checks whether that user is
! allowed to connect as the database user he is requesting to connect
! as. This is controlled by the ident map argument that follows the
! <literal>ident</> keyword in the <filename>pg_hba.conf</filename>
! file. There is a predefined ident map <literal>sameuser</literal>,
! which allows any operating system user to connect as the database
! user of the same name (if the latter exists). Other maps must be
! created manually.
</para>
<para>
! <indexterm><primary>pg_ident.conf</primary></indexterm> Ident maps
! other than <literal>sameuser</literal> are defined in the file
! <filename>pg_ident.conf</filename> in the data directory, which
! contains lines of the general form:
<synopsis>
<replaceable>map-name</> <replaceable>ident-username</> <replaceable>database-username</>
</synopsis>
! Comments and whitespace are handled in the usual way. The
! <replaceable>map-name</> is an arbitrary name that will be used to
! refer to this mapping in <filename>pg_hba.conf</filename>. The other
! two fields specify which operating system user is allowed to connect
! as which database user. The same <replaceable>map-name</> can be
! used repeatedly to specify more user-mappings within a single map.
! There is no restriction regarding how many database users a given
! operating system user may correspond to and vice versa.
</para>
<para>
<indexterm>
<primary>SIGHUP</primary>
</indexterm>
! The <filename>pg_ident.conf</filename> file is read on start-up and
! when the <application>postmaster</> receives a
<systemitem>SIGHUP</systemitem> signal. If you edit the file on an
active system, you will need to signal the <application>postmaster</>
! (using <literal>pg_ctl reload</> or <literal>kill -HUP</>) to make it
! re-read the file.
</para>
<para>
***************
*** 763,775 ****
conjunction with the <filename>pg_hba.conf</> file in <xref
linkend="example-pg-hba.conf"> is shown in <xref
linkend="example-pg-ident.conf">. In this example setup, anyone
! logged in to a machine on the 192.168 network that does not have
! the Unix user name <systemitem>bryanh</>, <systemitem>ann</>, or <systemitem>robert</> would not be granted
access.
! Unix user <systemitem>robert</> would only be allowed access when he tries to
! connect as <productname>PostgreSQL</> user <systemitem>bob</>,
! not as <systemitem>robert</>
! or anyone else. <systemitem>ann</> would only be allowed to connect as
! <systemitem>ann</>. User <systemitem>bryanh</> would be allowed to connect as either
<systemitem>bryanh</> himself or as <systemitem>guest1</>.
</para>
--- 713,726 ----
conjunction with the <filename>pg_hba.conf</> file in <xref
linkend="example-pg-hba.conf"> is shown in <xref
linkend="example-pg-ident.conf">. In this example setup, anyone
! logged in to a machine on the 192.168 network that does not have the
! Unix user name <systemitem>bryanh</>, <systemitem>ann</>, or
! <systemitem>robert</> would not be granted access. Unix user
! <systemitem>robert</> would only be allowed access when he tries to
! connect as <productname>PostgreSQL</> user <systemitem>bob</>, not
! as <systemitem>robert</> or anyone else. <systemitem>ann</> would
! only be allowed to connect as <systemitem>ann</>. User
! <systemitem>bryanh</> would be allowed to connect as either
<systemitem>bryanh</> himself or as <systemitem>guest1</>.
</para>
***************
*** 780,786 ****
omicron bryanh bryanh
omicron ann ann
! # bob has username robert on these machines
omicron robert bob
# bryanh can also connect as guest1
omicron bryanh guest1
--- 731,737 ----
omicron bryanh bryanh
omicron ann ann
! # bob has user name robert on these machines
omicron robert bob
# bryanh can also connect as guest1
omicron bryanh guest1
***************
*** 799,828 ****
<para>
<ProgramListing>
! No pg_hba.conf entry for host 123.123.123.123, user joeblow, database testdb
</ProgramListing>
! This is what you are most likely to get if you succeed in
! contacting the server, but it does not want to talk to you. As the
! message suggests, the server refused the connection request
! because it found no authorizing entry in its <filename>pg_hba.conf</filename>
configuration file.
</para>
<para>
<ProgramListing>
! Password authentication failed for user 'joeblow'
</ProgramListing>
! Messages like this indicate that you contacted the server, and
! it is willing to talk to you, but not until you pass the
! authorization method specified in the
! <filename>pg_hba.conf</filename> file. Check the password you are
! providing, or check your Kerberos or ident software if the
! complaint mentions one of those authentication types.
</para>
<para>
<ProgramListing>
! FATAL 1: user "joeblow" does not exist
</ProgramListing>
The indicated user name was not found.
</para>
--- 750,779 ----
<para>
<ProgramListing>
! No pg_hba.conf entry for host 123.123.123.123, user andym, database testdb
</ProgramListing>
! This is what you are most likely to get if you succeed in contacting
! the server, but it does not want to talk to you. As the message
! suggests, the server refused the connection request because it found
! no authorizing entry in its <filename>pg_hba.conf</filename>
configuration file.
</para>
<para>
<ProgramListing>
! Password authentication failed for user 'andym'
</ProgramListing>
! Messages like this indicate that you contacted the server, and it is
! willing to talk to you, but not until you pass the authorization
! method specified in the <filename>pg_hba.conf</filename> file. Check
! the password you are providing, or check your Kerberos or ident
! software if the complaint mentions one of those authentication
! types.
</para>
<para>
<ProgramListing>
! FATAL 1: user "andym" does not exist
</ProgramListing>
The indicated user name was not found.
</para>
***************
*** 837,845 ****
</para>
<para>
! Note that the server log may contain more information
! about an authentication failure than is reported to the client.
! If you are confused about the reason for a failure, check the log.
</para>
</sect1>
--- 788,796 ----
</para>
<para>
! Note that the server log may contain more information about an
! authentication failure than is reported to the client. If you are
! confused about the reason for a failure, check the log.
</para>
</sect1>
Index: doc/src/sgml/reference.sgml
===================================================================
RCS file: /cvsroot/pgsql/doc/src/sgml/reference.sgml,v
retrieving revision 1.24
diff -c -r1.24 reference.sgml
*** doc/src/sgml/reference.sgml 19 Mar 2002 02:18:11 -0000 1.24
--- doc/src/sgml/reference.sgml 3 Apr 2002 03:48:41 -0000
***************
*** 191,197 ****
&initlocation;
&ipcclean;
&pgCtl;
- &pgPasswd;
&postgres;
&postmaster;
--- 191,196 ----
Index: doc/src/sgml/ref/allfiles.sgml
===================================================================
RCS file: /cvsroot/pgsql/doc/src/sgml/ref/allfiles.sgml,v
retrieving revision 1.36
diff -c -r1.36 allfiles.sgml
*** doc/src/sgml/ref/allfiles.sgml 19 Mar 2002 02:18:12 -0000 1.36
--- doc/src/sgml/ref/allfiles.sgml 3 Apr 2002 03:48:41 -0000
***************
*** 125,131 ****
<!entity pgCtl system "pg_ctl-ref.sgml">
<!entity pgDump system "pg_dump.sgml">
<!entity pgDumpall system "pg_dumpall.sgml">
- <!entity pgPasswd system "pg_passwd.sgml">
<!entity pgRestore system "pg_restore.sgml">
<!entity pgTclSh system "pgtclsh.sgml">
<!entity pgTkSh system "pgtksh.sgml">
--- 125,130 ----
Index: src/backend/commands/user.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/commands/user.c,v
retrieving revision 1.94
diff -c -r1.94 user.c
*** src/backend/commands/user.c 26 Mar 2002 19:15:48 -0000 1.94
--- src/backend/commands/user.c 3 Apr 2002 03:48:43 -0000
***************
*** 15,20 ****
--- 15,21 ----
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
+ #include <errno.h>
#include <unistd.h>
#include "access/heapam.h"
***************
*** 27,32 ****
--- 28,34 ----
#include "libpq/crypt.h"
#include "miscadmin.h"
#include "storage/pmsignal.h"
+ #include "utils/acl.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
***************
*** 39,46 ****
static void CheckPgUserAclNotNull(void);
! /*---------------------------------------------------------------------
! * write_password_file / update_pg_pwd
*
* copy the modified contents of pg_shadow to a file used by the postmaster
* for user authentication. The file is stored as $PGDATA/global/pg_pwd.
--- 41,245 ----
static void CheckPgUserAclNotNull(void);
!
!
! /*
! * fputs_quote
! *
! * Outputs string in quotes, with double-quotes duplicated.
! * We could use quote_ident(), but that expects varlena.
! */
! static void fputs_quote(char *str, FILE *fp)
! {
! fputc('"', fp);
! while (*str)
! {
! fputc(*str, fp);
! if (*str == '"')
! fputc('"', fp);
! str++;
! }
! fputc('"', fp);
! }
!
!
!
! /*
! * group_getfilename --- get full pathname of group file
! *
! * Note that result string is palloc'd, and should be freed by the caller.
! */
! char *
! group_getfilename(void)
! {
! int bufsize;
! char *pfnam;
!
! bufsize = strlen(DataDir) + strlen("/global/") +
! strlen(USER_GROUP_FILE) + 1;
! pfnam = (char *) palloc(bufsize);
! snprintf(pfnam, bufsize, "%s/global/%s", DataDir, USER_GROUP_FILE);
!
! return pfnam;
! }
!
!
!
! /*
! * Get full pathname of password file.
! * Note that result string is palloc'd, and should be freed by the caller.
! */
! char *
! user_getfilename(void)
! {
! int bufsize;
! char *pfnam;
!
! bufsize = strlen(DataDir) + strlen("/global/") +
! strlen(PWD_FILE) + 1;
! pfnam = (char *) palloc(bufsize);
! snprintf(pfnam, bufsize, "%s/global/%s", DataDir, PWD_FILE);
!
! return pfnam;
! }
!
!
!
! /*
! * write_group_file for trigger update_pg_pwd_and_pg_group
! */
! static void
! write_group_file(Relation urel, Relation grel)
! {
! char *filename,
! *tempname;
! int bufsize;
! FILE *fp;
! mode_t oumask;
! HeapScanDesc scan;
! HeapTuple tuple;
! TupleDesc dsc = RelationGetDescr(grel);
!
! /*
! * Create a temporary filename to be renamed later. This prevents the
! * backend from clobbering the pg_group file while the postmaster might
! * be reading from it.
! */
! filename = group_getfilename();
! bufsize = strlen(filename) + 12;
! tempname = (char *) palloc(bufsize);
!
! snprintf(tempname, bufsize, "%s.%d", filename, MyProcPid);
! oumask = umask((mode_t) 077);
! fp = AllocateFile(tempname, "w");
! umask(oumask);
! if (fp == NULL)
! elog(ERROR, "write_group_file: unable to write %s: %m", tempname);
!
! /* read table */
! scan = heap_beginscan(grel, false, SnapshotSelf, 0, NULL);
! while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
! {
! Datum datum, grolist_datum;
! bool isnull;
! char *groname;
! IdList *grolist_p;
! AclId *aidp;
! int i, j,
! num;
! char *usename;
! bool first_user = true;
!
! datum = heap_getattr(tuple, Anum_pg_group_groname, dsc, &isnull);
! if (isnull)
! continue; /* ignore NULL groupnames */
! groname = (char *) DatumGetName(datum);
!
! grolist_datum = heap_getattr(tuple, Anum_pg_group_grolist, dsc, &isnull);
! /* Ignore NULL group lists */
! if (isnull)
! continue;
!
! grolist_p = DatumGetIdListP(grolist_datum);
! /*
! * Check for illegal characters in the group name.
! */
! i = strcspn(groname, "\n");
! if (groname[i] != '\0')
! {
! elog(LOG, "Invalid group name '%s'", groname);
! continue;
! }
!
! /* be sure the IdList is not toasted */
! /* scan it */
! num = IDLIST_NUM(grolist_p);
! aidp = IDLIST_DAT(grolist_p);
! for (i = 0; i < num; ++i)
! {
! tuple = SearchSysCache(SHADOWSYSID,
! PointerGetDatum(aidp[i]),
! 0, 0, 0);
! if (HeapTupleIsValid(tuple))
! {
! usename = NameStr(((Form_pg_shadow) GETSTRUCT(tuple))->usename);
!
! /*
! * Check for illegal characters in the user name.
! */
! j = strcspn(usename, "\n");
! if (usename[j] != '\0')
! {
! elog(LOG, "Invalid user name '%s'", usename);
! continue;
! }
!
! /* File format is:
! * "dbname" "user1","user2","user3"
! * This matches pg_hba.conf.
! */
! if (first_user)
! {
! fputs_quote(groname, fp);
! fputs("\t", fp);
! }
! else
! fputs(" ", fp);
!
! first_user = false;
! fputs_quote(usename, fp);
!
! ReleaseSysCache(tuple);
! }
! }
! if (!first_user)
! fputs("\n", fp);
! /* if IdList was toasted, free detoasted copy */
! if ((Pointer) grolist_p != DatumGetPointer(grolist_datum))
! pfree(grolist_p);
! }
! heap_endscan(scan);
!
! fflush(fp);
! if (ferror(fp))
! elog(ERROR, "%s: %m", tempname);
! FreeFile(fp);
!
! /*
! * Rename the temp file to its final name, deleting the old pg_pwd. We
! * expect that rename(2) is an atomic action.
! */
! if (rename(tempname, filename))
! elog(ERROR, "rename %s to %s: %m", tempname, filename);
!
! pfree((void *) tempname);
! pfree((void *) filename);
! }
!
!
!
! /*
! * write_password_file for trigger update_pg_pwd_and_pg_group
*
* copy the modified contents of pg_shadow to a file used by the postmaster
* for user authentication. The file is stored as $PGDATA/global/pg_pwd.
***************
*** 51,60 ****
* We raise an error to force transaction rollback if we detect an illegal
* username or password --- illegal being defined as values that would
* mess up the pg_pwd parser.
- *---------------------------------------------------------------------
*/
static void
! write_password_file(Relation rel)
{
char *filename,
*tempname;
--- 250,258 ----
* We raise an error to force transaction rollback if we detect an illegal
* username or password --- illegal being defined as values that would
* mess up the pg_pwd parser.
*/
static void
! write_user_file(Relation urel)
{
char *filename,
*tempname;
***************
*** 63,76 ****
mode_t oumask;
HeapScanDesc scan;
HeapTuple tuple;
! TupleDesc dsc = RelationGetDescr(rel);
/*
* Create a temporary filename to be renamed later. This prevents the
* backend from clobbering the pg_pwd file while the postmaster might
* be reading from it.
*/
! filename = crypt_getpwdfilename();
bufsize = strlen(filename) + 12;
tempname = (char *) palloc(bufsize);
--- 261,274 ----
mode_t oumask;
HeapScanDesc scan;
HeapTuple tuple;
! TupleDesc dsc = RelationGetDescr(urel);
/*
* Create a temporary filename to be renamed later. This prevents the
* backend from clobbering the pg_pwd file while the postmaster might
* be reading from it.
*/
! filename = user_getfilename();
bufsize = strlen(filename) + 12;
tempname = (char *) palloc(bufsize);
***************
*** 82,107 ****
elog(ERROR, "write_password_file: unable to write %s: %m", tempname);
/* read table */
! scan = heap_beginscan(rel, false, SnapshotSelf, 0, NULL);
while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
{
! Datum datum_n,
! datum_p,
! datum_v;
! bool null_n,
! null_p,
! null_v;
! char *str_n,
! *str_p,
! *str_v;
int i;
! datum_n = heap_getattr(tuple, Anum_pg_shadow_usename, dsc, &null_n);
! if (null_n)
continue; /* ignore NULL usernames */
! str_n = DatumGetCString(DirectFunctionCall1(nameout, datum_n));
! datum_p = heap_getattr(tuple, Anum_pg_shadow_passwd, dsc, &null_p);
/*
* It can be argued that people having a null password shouldn't
--- 280,301 ----
elog(ERROR, "write_password_file: unable to write %s: %m", tempname);
/* read table */
! scan = heap_beginscan(urel, false, SnapshotSelf, 0, NULL);
while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
{
! Datum datum;
! bool isnull;
! char *usename,
! *passwd,
! *valuntil;
int i;
! datum = heap_getattr(tuple, Anum_pg_shadow_usename, dsc, &isnull);
! if (isnull)
continue; /* ignore NULL usernames */
! usename = (char *) DatumGetName(datum);
! datum = heap_getattr(tuple, Anum_pg_shadow_passwd, dsc, &isnull);
/*
* It can be argued that people having a null password shouldn't
***************
*** 110,166 ****
* assuming an empty password in that case is better, change this
* logic to look something like the code for valuntil.
*/
! if (null_p)
! {
! pfree(str_n);
continue;
- }
- str_p = DatumGetCString(DirectFunctionCall1(textout, datum_p));
! datum_v = heap_getattr(tuple, Anum_pg_shadow_valuntil, dsc, &null_v);
! if (null_v)
! str_v = pstrdup("\\N");
else
! str_v = DatumGetCString(DirectFunctionCall1(nabstimeout, datum_v));
/*
* Check for illegal characters in the username and password.
*/
! i = strcspn(str_n, CRYPT_PWD_FILE_SEPSTR "\n");
! if (str_n[i] != '\0')
! elog(ERROR, "Invalid user name '%s'", str_n);
! i = strcspn(str_p, CRYPT_PWD_FILE_SEPSTR "\n");
! if (str_p[i] != '\0')
! elog(ERROR, "Invalid user password '%s'", str_p);
/*
* The extra columns we emit here are not really necessary. To
* remove them, the parser in backend/libpq/crypt.c would need to
* be adjusted.
*/
! fprintf(fp,
! "%s"
! CRYPT_PWD_FILE_SEPSTR
! "0"
! CRYPT_PWD_FILE_SEPSTR
! "x"
! CRYPT_PWD_FILE_SEPSTR
! "x"
! CRYPT_PWD_FILE_SEPSTR
! "x"
! CRYPT_PWD_FILE_SEPSTR
! "x"
! CRYPT_PWD_FILE_SEPSTR
! "%s"
! CRYPT_PWD_FILE_SEPSTR
! "%s\n",
! str_n,
! str_p,
! str_v);
!
! pfree(str_n);
! pfree(str_p);
! pfree(str_v);
}
heap_endscan(scan);
--- 304,343 ----
* assuming an empty password in that case is better, change this
* logic to look something like the code for valuntil.
*/
! if (isnull)
continue;
! passwd = DatumGetCString(DirectFunctionCall1(textout, datum));
!
! datum = heap_getattr(tuple, Anum_pg_shadow_valuntil, dsc, &isnull);
! if (isnull)
! valuntil = pstrdup("");
else
! valuntil = DatumGetCString(DirectFunctionCall1(nabstimeout, datum));
/*
* Check for illegal characters in the username and password.
*/
! i = strcspn(usename, "\n");
! if (usename[i] != '\0')
! elog(ERROR, "Invalid user name '%s'", usename);
! i = strcspn(passwd, "\n");
! if (passwd[i] != '\0')
! elog(ERROR, "Invalid user password '%s'", passwd);
/*
* The extra columns we emit here are not really necessary. To
* remove them, the parser in backend/libpq/crypt.c would need to
* be adjusted.
*/
! fputs_quote(usename, fp);
! fputs(" ", fp);
! fputs_quote(passwd, fp);
! fputs(" ", fp);
! fputs_quote(valuntil, fp);
!
! pfree(passwd);
! pfree(valuntil);
}
heap_endscan(scan);
***************
*** 178,206 ****
pfree((void *) tempname);
pfree((void *) filename);
-
- /*
- * Signal the postmaster to reload its password-file cache.
- */
- SendPostmasterSignal(PMSIGNAL_PASSWORD_CHANGE);
}
/* This is the wrapper for triggers. */
Datum
! update_pg_pwd(PG_FUNCTION_ARGS)
{
/*
* ExclusiveLock ensures no one modifies pg_shadow while we read it,
* and that only one backend rewrites the flat file at a time. It's
* OK to allow normal reads of pg_shadow in parallel, however.
*/
! Relation rel = heap_openr(ShadowRelationName, ExclusiveLock);
! write_password_file(rel);
/* OK to release lock, since we did not modify the relation */
! heap_close(rel, ExclusiveLock);
return PointerGetDatum(NULL);
}
--- 355,387 ----
pfree((void *) tempname);
pfree((void *) filename);
}
/* This is the wrapper for triggers. */
Datum
! update_pg_pwd_and_pg_group(PG_FUNCTION_ARGS)
{
/*
* ExclusiveLock ensures no one modifies pg_shadow while we read it,
* and that only one backend rewrites the flat file at a time. It's
* OK to allow normal reads of pg_shadow in parallel, however.
*/
! Relation urel = heap_openr(ShadowRelationName, ExclusiveLock);
! Relation grel = heap_openr(GroupRelationName, ExclusiveLock);
! write_user_file(urel);
! write_group_file(urel, grel);
/* OK to release lock, since we did not modify the relation */
! heap_close(grel, ExclusiveLock);
! heap_close(urel, ExclusiveLock);
!
! /*
! * Signal the postmaster to reload its password & group-file cache.
! */
! SendPostmasterSignal(PMSIGNAL_PASSWORD_CHANGE);
!
return PointerGetDatum(NULL);
}
***************
*** 446,459 ****
}
/*
! * Write the updated pg_shadow data to the flat password file.
*/
! write_password_file(pg_shadow_rel);
/*
! * Now we can clean up; but keep lock until commit.
*/
! heap_close(pg_shadow_rel, NoLock);
}
--- 627,640 ----
}
/*
! * Now we can clean up; but keep lock until commit.
*/
! heap_close(pg_shadow_rel, NoLock);
/*
! * Write the updated pg_shadow and pg_group data to the flat file.
*/
! update_pg_pwd_and_pg_group(NULL);
}
***************
*** 680,693 ****
heap_freetuple(new_tuple);
/*
! * Write the updated pg_shadow data to the flat password file.
*/
! write_password_file(pg_shadow_rel);
/*
! * Now we can clean up.
*/
! heap_close(pg_shadow_rel, NoLock);
}
--- 861,874 ----
heap_freetuple(new_tuple);
/*
! * Now we can clean up.
*/
! heap_close(pg_shadow_rel, NoLock);
/*
! * Write the updated pg_shadow and pg_group data to the flat file.
*/
! update_pg_pwd_and_pg_group(NULL);
}
***************
*** 733,739 ****
{
Datum datum;
bool isnull;
! ArrayType *a;
repl_null[Anum_pg_shadow_useconfig-1] = ' ';
--- 914,920 ----
{
Datum datum;
bool isnull;
! ArrayType *array;
repl_null[Anum_pg_shadow_useconfig-1] = ' ';
***************
*** 741,757 ****
Anum_pg_shadow_useconfig, &isnull);
if (valuestr)
! a = GUCArrayAdd(isnull
? NULL
: (ArrayType *) pg_detoast_datum((struct varlena *)datum),
stmt->variable, valuestr);
else
! a = GUCArrayDelete(isnull
? NULL
: (ArrayType *) pg_detoast_datum((struct varlena *)datum),
stmt->variable);
! repl_val[Anum_pg_shadow_useconfig-1] = PointerGetDatum(a);
}
newtuple = heap_modifytuple(oldtuple, rel, repl_val, repl_null, repl_repl);
--- 922,938 ----
Anum_pg_shadow_useconfig, &isnull);
if (valuestr)
! array = GUCArrayAdd(isnull
? NULL
: (ArrayType *) pg_detoast_datum((struct varlena *)datum),
stmt->variable, valuestr);
else
! array = GUCArrayDelete(isnull
? NULL
: (ArrayType *) pg_detoast_datum((struct varlena *)datum),
stmt->variable);
! repl_val[Anum_pg_shadow_useconfig-1] = PointerGetDatum(array);
}
newtuple = heap_modifytuple(oldtuple, rel, repl_val, repl_null, repl_repl);
***************
*** 846,852 ****
datum = heap_getattr(tmp_tuple, Anum_pg_database_datname,
pg_dsc, &null);
Assert(!null);
! dbname = DatumGetCString(DirectFunctionCall1(nameout, datum));
elog(ERROR, "DROP USER: user \"%s\" owns database \"%s\", cannot be removed%s",
user, dbname,
(length(stmt->users) > 1) ? " (no users removed)" : "");
--- 1027,1033 ----
datum = heap_getattr(tmp_tuple, Anum_pg_database_datname,
pg_dsc, &null);
Assert(!null);
! dbname = (char *) DatumGetName(datum);
elog(ERROR, "DROP USER: user \"%s\" owns database \"%s\", cannot be removed%s",
user, dbname,
(length(stmt->users) > 1) ? " (no users removed)" : "");
***************
*** 901,914 ****
}
/*
! * Write the updated pg_shadow data to the flat password file.
*/
! write_password_file(pg_shadow_rel);
/*
! * Now we can clean up.
*/
! heap_close(pg_shadow_rel, NoLock);
}
--- 1082,1095 ----
}
/*
! * Now we can clean up.
*/
! heap_close(pg_shadow_rel, NoLock);
/*
! * Write the updated pg_shadow and pg_group data to the flat file.
*/
! update_pg_pwd_and_pg_group(NULL);
}
***************
*** 1111,1116 ****
--- 1292,1302 ----
}
heap_close(pg_group_rel, NoLock);
+
+ /*
+ * Write the updated pg_shadow and pg_group data to the flat file.
+ */
+ update_pg_pwd_and_pg_group(NULL);
}
***************
*** 1366,1372 ****
--- 1552,1566 ----
ReleaseSysCache(group_tuple);
+ /*
+ * Write the updated pg_shadow and pg_group data to the flat files.
+ */
heap_close(pg_group_rel, NoLock);
+
+ /*
+ * Write the updated pg_shadow and pg_group data to the flat file.
+ */
+ update_pg_pwd_and_pg_group(NULL);
}
***************
*** 1419,1422 ****
--- 1613,1621 ----
elog(ERROR, "DROP GROUP: group \"%s\" does not exist", stmt->name);
heap_close(pg_group_rel, NoLock);
+
+ /*
+ * Write the updated pg_shadow and pg_group data to the flat file.
+ */
+ update_pg_pwd_and_pg_group(NULL);
}
Index: src/backend/libpq/Makefile
===================================================================
RCS file: /cvsroot/pgsql/src/backend/libpq/Makefile,v
retrieving revision 1.29
diff -c -r1.29 Makefile
*** src/backend/libpq/Makefile 4 Mar 2002 01:46:02 -0000 1.29
--- src/backend/libpq/Makefile 3 Apr 2002 03:48:43 -0000
***************
*** 14,22 ****
# be-fsstubs is here for historical reasons, probably belongs elsewhere
! OBJS = be-fsstubs.o \
! auth.o crypt.o hba.o md5.o password.o \
! pqcomm.o pqformat.o pqsignal.o
all: SUBSYS.o
--- 14,20 ----
# be-fsstubs is here for historical reasons, probably belongs elsewhere
! OBJS = be-fsstubs.o auth.o crypt.o hba.o md5.o pqcomm.o pqformat.o pqsignal.o
all: SUBSYS.o
Index: src/backend/libpq/auth.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/libpq/auth.c,v
retrieving revision 1.79
diff -c -r1.79 auth.c
*** src/backend/libpq/auth.c 5 Mar 2002 07:57:45 -0000 1.79
--- src/backend/libpq/auth.c 3 Apr 2002 03:48:43 -0000
***************
*** 34,40 ****
#include "miscadmin.h"
static void sendAuthRequest(Port *port, AuthRequest areq);
- static int checkPassword(Port *port, char *user, char *password);
static int old_be_recvauth(Port *port);
static int map_old_to_new(Port *port, UserAuth old, int status);
static void auth_failed(Port *port, int status);
--- 34,39 ----
***************
*** 381,387 ****
saved = port->auth_method;
port->auth_method = uaPassword;
! status = checkPassword(port, user, password);
port->auth_method = saved;
--- 380,386 ----
saved = port->auth_method;
port->auth_method = uaPassword;
! status = md5_crypt_verify(port, user, password);
port->auth_method = saved;
***************
*** 663,669 ****
initStringInfo(&buf);
pq_getstr(&buf);
!
/* Do not echo failed password to logs, for security. */
elog(DEBUG5, "received PAM packet");
--- 662,668 ----
initStringInfo(&buf);
pq_getstr(&buf);
!
/* Do not echo failed password to logs, for security. */
elog(DEBUG5, "received PAM packet");
***************
*** 810,832 ****
/* Do not echo failed password to logs, for security. */
elog(DEBUG5, "received password packet");
! result = checkPassword(port, port->user, buf.data);
pfree(buf.data);
return result;
- }
-
-
- /*
- * Handle `password' and `crypt' records. If an auth argument was
- * specified, use the respective file. Else use pg_shadow passwords.
- */
- static int
- checkPassword(Port *port, char *user, char *password)
- {
- if (port->auth_arg[0] != '\0')
- return verify_password(port, user, password);
-
- return md5_crypt_verify(port, user, password);
}
--- 809,818 ----
/* Do not echo failed password to logs, for security. */
elog(DEBUG5, "received password packet");
! result = md5_crypt_verify(port, port->user, buf.data);
!
pfree(buf.data);
return result;
}
Index: src/backend/libpq/crypt.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/libpq/crypt.c,v
retrieving revision 1.44
diff -c -r1.44 crypt.c
*** src/backend/libpq/crypt.c 4 Mar 2002 01:46:03 -0000 1.44
--- src/backend/libpq/crypt.c 3 Apr 2002 03:48:43 -0000
***************
*** 15,21 ****
*/
#include "postgres.h"
- #include <errno.h>
#include <unistd.h>
#ifdef HAVE_CRYPT_H
#include <crypt.h>
--- 15,20 ----
***************
*** 25,255 ****
#include "libpq/libpq.h"
#include "miscadmin.h"
#include "storage/fd.h"
#include "utils/nabstime.h"
- #define CRYPT_PWD_FILE "pg_pwd"
-
-
- static char **pwd_cache = NULL;
- static int pwd_cache_count = 0;
-
- /*
- * crypt_getpwdfilename --- get full pathname of password file
- *
- * Note that result string is palloc'd, and should be freed by the caller.
- */
- char *
- crypt_getpwdfilename(void)
- {
- int bufsize;
- char *pfnam;
-
- bufsize = strlen(DataDir) + 8 + strlen(CRYPT_PWD_FILE) + 1;
- pfnam = (char *) palloc(bufsize);
- snprintf(pfnam, bufsize, "%s/global/%s", DataDir, CRYPT_PWD_FILE);
-
- return pfnam;
- }
-
- /*
- * Open the password file if possible (return NULL if not)
- */
- static FILE *
- crypt_openpwdfile(void)
- {
- char *filename;
- FILE *pwdfile;
-
- filename = crypt_getpwdfilename();
- pwdfile = AllocateFile(filename, "r");
-
- if (pwdfile == NULL && errno != ENOENT)
- elog(LOG, "could not open %s: %m", filename);
-
- pfree(filename);
-
- return pwdfile;
- }
-
- /*
- * Compare two password-file lines on the basis of their usernames.
- *
- * Can also be used to compare just a username against a password-file
- * line (for bsearch).
- */
- static int
- compar_user(const void *user_a, const void *user_b)
- {
- char *login_a;
- char *login_b;
- int len_a,
- len_b,
- result;
-
- login_a = *((char **) user_a);
- login_b = *((char **) user_b);
-
- /*
- * We only really want to compare the user logins which are first and
- * are terminated by CRYPT_PWD_FILE_SEPSTR. (NB: this code
- * effectively assumes that CRYPT_PWD_FILE_SEPSTR is just one char.)
- */
- len_a = strcspn(login_a, CRYPT_PWD_FILE_SEPSTR);
- len_b = strcspn(login_b, CRYPT_PWD_FILE_SEPSTR);
-
- result = strncmp(login_a, login_b, Min(len_a, len_b));
-
- if (result == 0) /* one could be a prefix of the other */
- result = (len_a - len_b);
-
- return result;
- }
-
- /*
- * Load or reload the password-file cache
- */
- void
- load_password_cache(void)
- {
- FILE *pwd_file;
- char buffer[1024];
-
- /*
- * If for some reason we fail to open the password file, preserve the
- * old cache contents; this seems better than dropping the cache if,
- * say, we are temporarily out of filetable slots.
- */
- if (!(pwd_file = crypt_openpwdfile()))
- return;
-
- /* free any old data */
- if (pwd_cache)
- {
- while (--pwd_cache_count >= 0)
- pfree(pwd_cache[pwd_cache_count]);
- pfree(pwd_cache);
- pwd_cache = NULL;
- pwd_cache_count = 0;
- }
-
- /*
- * Read the file and store its lines in current memory context, which
- * we expect will be PostmasterContext. That context will live as
- * long as we need the cache to live, ie, until just after each
- * postmaster child has completed client authentication.
- */
- while (fgets(buffer, sizeof(buffer), pwd_file) != NULL)
- {
- int blen;
-
- /*
- * We must remove the return char at the end of the string, as
- * this will affect the correct parsing of the password entry.
- */
- if (buffer[(blen = strlen(buffer) - 1)] == '\n')
- buffer[blen] = '\0';
-
- if (pwd_cache == NULL)
- pwd_cache = (char **)
- palloc(sizeof(char *) * (pwd_cache_count + 1));
- else
- pwd_cache = (char **)
- repalloc((void *) pwd_cache,
- sizeof(char *) * (pwd_cache_count + 1));
- pwd_cache[pwd_cache_count++] = pstrdup(buffer);
- }
-
- FreeFile(pwd_file);
-
- /*
- * Now sort the entries in the cache for faster searching later.
- */
- qsort((void *) pwd_cache, pwd_cache_count, sizeof(char *), compar_user);
- }
-
- /*
- * Parse a line of the password file to extract password and valid-until date.
- */
- static bool
- crypt_parsepwdentry(char *buffer, char **pwd, char **valdate)
- {
- char *parse = buffer;
- int count,
- i;
-
- *pwd = NULL;
- *valdate = NULL;
-
- /*
- * skip to the password field
- */
- for (i = 0; i < 6; i++)
- {
- parse += strcspn(parse, CRYPT_PWD_FILE_SEPSTR);
- if (*parse == '\0')
- return false;
- parse++;
- }
-
- /*
- * store a copy of user password to return
- */
- count = strcspn(parse, CRYPT_PWD_FILE_SEPSTR);
- *pwd = (char *) palloc(count + 1);
- memcpy(*pwd, parse, count);
- (*pwd)[count] = '\0';
- parse += count;
- if (*parse == '\0')
- {
- pfree(*pwd);
- *pwd = NULL;
- return false;
- }
- parse++;
-
- /*
- * store a copy of the date login becomes invalid
- */
- count = strcspn(parse, CRYPT_PWD_FILE_SEPSTR);
- *valdate = (char *) palloc(count + 1);
- memcpy(*valdate, parse, count);
- (*valdate)[count] = '\0';
-
- return true;
- }
-
- /*
- * Lookup a username in the password-file cache,
- * return his password and valid-until date.
- */
- static bool
- crypt_getloginfo(const char *user, char **passwd, char **valuntil)
- {
- *passwd = NULL;
- *valuntil = NULL;
-
- if (pwd_cache)
- {
- char **pwd_entry;
-
- pwd_entry = (char **) bsearch((void *) &user,
- (void *) pwd_cache,
- pwd_cache_count,
- sizeof(char *),
- compar_user);
- if (pwd_entry)
- {
- if (crypt_parsepwdentry(*pwd_entry, passwd, valuntil))
- return true;
- }
- }
-
- return false;
- }
-
- /*-------------------------------------------------------------------------*/
-
int
md5_crypt_verify(const Port *port, const char *user, const char *pgpass)
{
--- 24,33 ----
#include "libpq/libpq.h"
#include "miscadmin.h"
#include "storage/fd.h"
+ #include "nodes/pg_list.h"
#include "utils/nabstime.h"
int
md5_crypt_verify(const Port *port, const char *user, const char *pgpass)
{
***************
*** 257,265 ****
*valuntil,
*crypt_pwd;
int retval = STATUS_ERROR;
! if (!crypt_getloginfo(user, &passwd, &valuntil))
return STATUS_ERROR;
if (passwd == NULL || *passwd == '\0')
{
--- 35,47 ----
*valuntil,
*crypt_pwd;
int retval = STATUS_ERROR;
+ List **line;
! if ((line = get_user_line(user)) == NULL)
return STATUS_ERROR;
+
+ passwd = lfirst(lnext(lnext(*line)));
+ valuntil = lfirst(lnext(lnext(lnext(*line))));
if (passwd == NULL || *passwd == '\0')
{
Index: src/backend/libpq/hba.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/libpq/hba.c,v
retrieving revision 1.80
diff -c -r1.80 hba.c
*** src/backend/libpq/hba.c 4 Mar 2002 01:46:03 -0000 1.80
--- src/backend/libpq/hba.c 3 Apr 2002 03:48:44 -0000
***************
*** 30,44 ****
#include <arpa/inet.h>
#include <unistd.h>
#include "libpq/libpq.h"
#include "miscadmin.h"
#include "nodes/pg_list.h"
#include "storage/fd.h"
- #define MAX_TOKEN 80
- /* Maximum size of one token in the configuration file */
-
#define IDENT_USERNAME_MAX 512
/* Max size of username ident server can return */
--- 30,43 ----
#include <arpa/inet.h>
#include <unistd.h>
+ #include "commands/user.h"
+ #include "libpq/crypt.h"
#include "libpq/libpq.h"
#include "miscadmin.h"
#include "nodes/pg_list.h"
#include "storage/fd.h"
#define IDENT_USERNAME_MAX 512
/* Max size of username ident server can return */
***************
*** 53,58 ****
--- 52,68 ----
*/
static List *hba_lines = NIL; /* pre-parsed contents of hba file */
static List *ident_lines = NIL; /* pre-parsed contents of ident file */
+ static List *group_lines = NIL; /* pre-parsed contents of group file */
+ static List *user_lines = NIL; /* pre-parsed contents of user password file */
+
+ /* sorted entries so we can do binary search lookups */
+ static List **user_sorted = NULL; /* sorted user list, for bsearch() */
+ static List **group_sorted = NULL; /* sorted group list, for bsearch() */
+ static int user_length;
+ static int group_length;
+
+ static List *tokenize_file(FILE *file);
+ static char *tokenize_inc_file(const char *inc_filename);
/*
***************
*** 67,107 ****
/*
! * Grab one token out of fp. Tokens are strings of non-blank
! * characters bounded by blank characters, beginning of line, and end
! * of line. Blank means space or tab. Return the token as *buf.
! * Leave file positioned to character immediately after the token or
! * EOF, whichever comes first. If no more tokens on line, return null
! * string as *buf and position file to beginning of next line or EOF,
! * whichever comes first.
*/
! static void
next_token(FILE *fp, char *buf, const int bufsz)
{
int c;
! char *eb = buf + (bufsz - 1);
! /* Move over initial token-delimiting blanks */
! while ((c = getc(fp)) != EOF && isblank(c))
;
if (c != EOF && c != '\n')
{
/*
! * build a token in buf of next characters up to EOF, eol, or
! * blank. If the token gets too long, we still parse it
! * correctly, but the excess characters are not stored into *buf.
*/
! while (c != EOF && c != '\n' && !isblank(c))
{
! if (buf < eb)
*buf++ = c;
c = getc(fp);
}
/*
* Put back the char right after the token (critical in case it is
! * eol, since we need to detect end-of-line at next call).
*/
if (c != EOF)
ungetc(c, fp);
--- 77,152 ----
/*
! * Grab one token out of fp. Tokens are strings of non-blank
! * characters bounded by blank characters, beginning of line, and
! * end of line. Blank means space or tab. Return the token as
! * *buf. Leave file positioned to character immediately after the
! * token or EOF, whichever comes first. If no more tokens on line,
! * return null string as *buf and position file to beginning of
! * next line or EOF, whichever comes first. Allow spaces in quoted
! * strings. Terminate on unquoted commas. Handle comments.
*/
! void
next_token(FILE *fp, char *buf, const int bufsz)
{
int c;
! char *end_buf = buf + (bufsz - 1);
! bool in_quote = false;
! bool was_quote = false;
! /* Move over initial whitespace and commas */
! while ((c = getc(fp)) != EOF && (isblank(c) || c == ','))
;
if (c != EOF && c != '\n')
{
/*
! * Build a token in buf of next characters up to EOF, EOL, unquoted
! * comma, or unquoted whitespace.
*/
! while (c != EOF && c != '\n' &&
! (!isblank(c) || in_quote == true))
{
! if (c == '"')
! in_quote = !in_quote;
!
! /* skip comments to EOL */
! if (c == '#' && !in_quote)
! {
! while ((c = getc(fp)) != EOF && c != '\n')
! ;
! continue;
! }
!
! if (buf >= end_buf)
! {
! elog(LOG, "Token too long in authentication file, skipping, %s", buf);
! /* Discard remainder of line */
! while ((c = getc(fp)) != EOF && c != '\n')
! ;
! buf[0] = '\0';
! break;
! }
!
! if (c != '"' || (c == '"' && was_quote))
*buf++ = c;
+
+ /* We pass back the comma so the caller knows there is more */
+ if ((isblank(c) || c == ',') && !in_quote)
+ break;
+
+ /* Literal double-quote is two double-quotes */
+ if (c == '"')
+ was_quote = !was_quote;
+ else
+ was_quote = false;
+
c = getc(fp);
}
/*
* Put back the char right after the token (critical in case it is
! * EOL, since we need to detect end-of-line at next call).
*/
if (c != EOF)
ungetc(c, fp);
***************
*** 109,125 ****
*buf = '\0';
}
static void
! read_through_eol(FILE *file)
{
! int c;
! while ((c = getc(file)) != EOF && c != '\n')
! ;
}
/*
* Read the given file and create a list of line sublists.
*/
--- 154,295 ----
*buf = '\0';
}
+ /*
+ * Tokenize file and handle file inclusion and comma lists. We have
+ * to break apart the commas to expand any file names then
+ * reconstruct with commas.
+ */
+ static char *
+ next_token_expand(FILE *file)
+ {
+ char buf[MAX_TOKEN];
+ char *comma_str = pstrdup("");
+ bool trailing_comma;
+ char *incbuf;
+
+ do
+ {
+ next_token(file, buf, sizeof(buf));
+ if (!*buf)
+ break;
+
+ if (buf[strlen(buf)-1] == ',')
+ {
+ trailing_comma = true;
+ buf[strlen(buf)-1] = '\0';
+ }
+ else
+ trailing_comma = false;
+
+ /* Is this referencing a file? */
+ if (buf[0] == '@')
+ incbuf = tokenize_inc_file(buf+1);
+ else
+ incbuf = pstrdup(buf);
+
+ comma_str = repalloc(comma_str,
+ strlen(comma_str) + strlen(incbuf) + 1);
+ strcat(comma_str, incbuf);
+ pfree(incbuf);
+
+ if (trailing_comma)
+ {
+ comma_str = repalloc(comma_str, strlen(comma_str) + 1 + 1);
+ strcat(comma_str, ",");
+ }
+ } while (trailing_comma);
+
+ return comma_str;
+ }
+
+ /*
+ * Free memory used by lines/tokens (i.e., structure built by tokenize_file)
+ */
static void
! free_lines(List **lines)
{
! if (*lines)
! {
! List *line,
! *token;
! foreach(line, *lines)
! {
! List *ln = lfirst(line);
!
! /* free the pstrdup'd tokens (don't try it on the line number) */
! foreach(token, lnext(ln))
! pfree(lfirst(token));
! /* free the sublist structure itself */
! freeList(ln);
! }
! /* free the list structure itself */
! freeList(*lines);
! /* clear the static variable */
! *lines = NIL;
! }
! }
!
!
! static char *
! tokenize_inc_file(const char *inc_filename)
! {
! char *inc_fullname;
! FILE *inc_file;
! List *inc_lines;
! List *line;
! char *comma_str = pstrdup("");
!
! inc_fullname = (char *) palloc(strlen(DataDir) + 1 +
! strlen(inc_filename) + 1);
! strcpy(inc_fullname, DataDir);
! strcat(inc_fullname, "/");
! strcat(inc_fullname, inc_filename);
!
! inc_file = AllocateFile(inc_fullname, "r");
! if (!inc_file)
! {
! elog(LOG, "tokenize_inc_file: Unable to open secondary authentication file \"@%s\" as \"%s\": %m",
! inc_filename, inc_fullname);
! pfree(inc_fullname);
!
! /* return empty string, it matches nothing */
! return pstrdup("");
! }
! pfree(inc_fullname);
!
! /* There is possible recursion here if the file contains @ */
! inc_lines = tokenize_file(inc_file);
! FreeFile(inc_file);
!
! /* Create comma-separate string from List */
! foreach(line, inc_lines)
! {
! List *ln = lfirst(line);
! List *token;
!
! /* First entry is line number */
! foreach(token, lnext(ln))
! {
! if (strlen(comma_str))
! {
! comma_str = repalloc(comma_str, strlen(comma_str) + 1);
! strcat(comma_str, ",");
! }
! comma_str = repalloc(comma_str,
! strlen(comma_str) + strlen(lfirst(token)) + 1);
! strcat(comma_str, lfirst(token));
! }
! }
!
! free_lines(&inc_lines);
!
! return comma_str;
}
+
/*
* Read the given file and create a list of line sublists.
*/
***************
*** 129,147 ****
List *lines = NIL;
List *next_line = NIL;
int line_number = 1;
! char buf[MAX_TOKEN];
! char *comment_ptr;
while (!feof(file))
{
! next_token(file, buf, sizeof(buf));
! /* trim off comment, even if inside a token */
! comment_ptr = strchr(buf, '#');
! if (comment_ptr != NULL)
! *comment_ptr = '\0';
!
! /* add token to list, unless we are at eol or comment start */
if (buf[0] != '\0')
{
if (next_line == NIL)
--- 299,311 ----
List *lines = NIL;
List *next_line = NIL;
int line_number = 1;
! char *buf;
while (!feof(file))
{
! buf = next_token_expand(file);
! /* add token to list, unless we are at EOL or comment start */
if (buf[0] != '\0')
{
if (next_line == NIL)
***************
*** 151,172 ****
lines = lappend(lines, next_line);
}
/* append token to current line's list */
! next_line = lappend(next_line, pstrdup(buf));
}
else
{
! /* we are at real or logical eol, so force a new line List */
! next_line = NIL;
! }
!
! if (comment_ptr != NULL)
! {
! /* Found a comment, so skip the rest of the line */
! read_through_eol(file);
next_line = NIL;
}
! /* Advance line number whenever we reach eol */
if (next_line == NIL)
line_number++;
}
--- 315,329 ----
lines = lappend(lines, next_line);
}
/* append token to current line's list */
! next_line = lappend(next_line, buf);
}
else
{
! /* we are at real or logical EOL, so force a new line List */
next_line = NIL;
}
! /* Advance line number whenever we reach EOL */
if (next_line == NIL)
line_number++;
}
***************
*** 176,206 ****
/*
! * Free memory used by lines/tokens (ie, structure built by tokenize_file)
*/
! static void
! free_lines(List **lines)
{
! if (*lines)
{
! List *line,
! *token;
! foreach(line, *lines)
{
! List *ln = lfirst(line);
! /* free the pstrdup'd tokens (don't try it on the line number) */
! foreach(token, lnext(ln))
! pfree(lfirst(token));
! /* free the sublist structure itself */
! freeList(ln);
}
! /* free the list structure itself */
! freeList(*lines);
! /* clear the static variable */
! *lines = NIL;
}
}
--- 333,448 ----
/*
! * Compare two password-file lines on the basis of their user names.
! *
! * Used for qsort() sorting and bsearch() lookup.
*/
! static int
! user_group_cmp(const void *user, const void *list)
{
! /* first node is line number */
! char *user1 = (char *)user;
! char *user2 = lfirst(lnext(*(List **)list));
!
! return strcmp(user1, user2);
! }
!
!
! /*
! * Lookup a group name in the pg_group file
! */
! static List **
! get_group_line(const char *group)
! {
! return (List **) bsearch((void *) group,
! (void *) group_sorted,
! group_length,
! sizeof(List *),
! user_group_cmp);
! }
!
!
! /*
! * Lookup a user name in the pg_shadow file
! */
! List **
! get_user_line(const char *user)
! {
! return (List **) bsearch((void *) user,
! (void *) user_sorted,
! user_length,
! sizeof(List *),
! user_group_cmp);
! }
!
!
! /*
! * Check group for a specific user.
! */
! static int
! check_group(char *group, char *user)
! {
! List **line, *l;
!
! if ((line = get_group_line(group)) != NULL)
{
! foreach(l, lnext(lnext(*line)))
! if (strcmp(lfirst(l), user) == 0)
! return 1;
! }
! return 0;
! }
!
! /*
! * Check comma user list for a specific user, handle group names.
! */
! static int
! check_user(char *user, char *param_str)
! {
! char *tok;
!
! for (tok = strtok(param_str, ","); tok != NULL; tok = strtok(NULL, ","))
! {
! if (tok[0] == '+')
{
! if (check_group(tok+1, user))
! return 1;
! }
! else if (strcmp(tok, user) == 0 ||
! strcmp(tok, "all") == 0)
! return 1;
! }
! return 0;
! }
!
! /*
! * Check to see if db/user combination matches param string.
! */
! static int
! check_db(char *dbname, char *user, char *param_str)
! {
! char *tok;
!
! for (tok = strtok(param_str, ","); tok != NULL; tok = strtok(NULL, ","))
! {
! if (strcmp(tok, "all") == 0)
! return 1;
! else if (strcmp(tok, "sameuser") == 0)
! {
! if (strcmp(dbname, user) == 0)
! return 1;
}
! else if (strcmp(tok, "samegroup") == 0)
! {
! if (check_group(dbname, user))
! return 1;
! }
! else if (strcmp(tok, dbname) == 0)
! return 1;
}
+ return 0;
}
***************
*** 278,283 ****
--- 520,526 ----
int line_number;
char *token;
char *db;
+ char *user;
Assert(line != NIL);
line_number = lfirsti(line);
***************
*** 293,302 ****
goto hba_syntax;
db = lfirst(line);
! /* Read the rest of the line. */
line = lnext(line);
if (!line)
goto hba_syntax;
parse_hba_auth(line, &port->auth_method, port->auth_arg, error_p);
if (*error_p)
goto hba_syntax;
--- 536,552 ----
goto hba_syntax;
db = lfirst(line);
! /* Get the user. */
! line = lnext(line);
! if (!line)
! goto hba_syntax;
! user = lfirst(line);
!
line = lnext(line);
if (!line)
goto hba_syntax;
+
+ /* Read the rest of the line. */
parse_hba_auth(line, &port->auth_method, port->auth_arg, error_p);
if (*error_p)
goto hba_syntax;
***************
*** 308,322 ****
port->auth_method == uaKrb5)
goto hba_syntax;
! /*
! * If this record doesn't match the parameters of the connection
! * attempt, ignore it.
! */
! if ((strcmp(db, port->database) != 0 &&
! strcmp(db, "all") != 0 &&
! (strcmp(db, "sameuser") != 0 ||
! strcmp(port->database, port->user) != 0)) ||
! port->raddr.sa.sa_family != AF_UNIX)
return;
}
else if (strcmp(token, "host") == 0 || strcmp(token, "hostssl") == 0)
--- 558,564 ----
port->auth_method == uaKrb5)
goto hba_syntax;
! if (port->raddr.sa.sa_family != AF_UNIX)
return;
}
else if (strcmp(token, "host") == 0 || strcmp(token, "hostssl") == 0)
***************
*** 347,352 ****
--- 589,600 ----
goto hba_syntax;
db = lfirst(line);
+ /* Get the user. */
+ line = lnext(line);
+ if (!line)
+ goto hba_syntax;
+ user = lfirst(line);
+
/* Read the IP address field. */
line = lnext(line);
if (!line)
***************
*** 371,391 ****
if (*error_p)
goto hba_syntax;
! /*
! * If this record doesn't match the parameters of the connection
! * attempt, ignore it.
! */
! if ((strcmp(db, port->database) != 0 &&
! strcmp(db, "all") != 0 &&
! (strcmp(db, "sameuser") != 0 ||
! strcmp(port->database, port->user) != 0)) ||
! port->raddr.sa.sa_family != AF_INET ||
((file_ip_addr.s_addr ^ port->raddr.in.sin_addr.s_addr) & mask.s_addr) != 0)
return;
}
else
goto hba_syntax;
/* Success */
*found_p = true;
return;
--- 619,637 ----
if (*error_p)
goto hba_syntax;
! /* Must meet network restrictions */
! if (port->raddr.sa.sa_family != AF_INET ||
((file_ip_addr.s_addr ^ port->raddr.in.sin_addr.s_addr) & mask.s_addr) != 0)
return;
}
else
goto hba_syntax;
+ if (!check_db(port->database, port->user, db))
+ return;
+ if (!check_user(port->user, user))
+ return;
+
/* Success */
*found_p = true;
return;
***************
*** 430,435 ****
--- 676,802 ----
}
+
+ /*
+ * Open the group file if possible (return NULL if not)
+ */
+ static FILE *
+ group_openfile(void)
+ {
+ char *filename;
+ FILE *groupfile;
+
+ filename = group_getfilename();
+ groupfile = AllocateFile(filename, "r");
+
+ if (groupfile == NULL && errno != ENOENT)
+ elog(LOG, "could not open %s: %m", filename);
+
+ pfree(filename);
+
+ return groupfile;
+ }
+
+
+
+ /*
+ * Open the password file if possible (return NULL if not)
+ */
+ static FILE *
+ user_openfile(void)
+ {
+ char *filename;
+ FILE *pwdfile;
+
+ filename = user_getfilename();
+ pwdfile = AllocateFile(filename, "r");
+
+ if (pwdfile == NULL && errno != ENOENT)
+ elog(LOG, "could not open %s: %m", filename);
+
+ pfree(filename);
+
+ return pwdfile;
+ }
+
+
+
+ /*
+ * Load group/user name mapping file
+ */
+ void
+ load_group()
+ {
+ FILE *group_file;
+ List *line;
+
+ if (group_lines)
+ free_lines(&group_lines);
+
+ group_file = group_openfile();
+ if (!group_file)
+ return;
+ group_lines = tokenize_file(group_file);
+ FreeFile(group_file);
+
+ /* create sorted lines for binary searching */
+ if (group_sorted)
+ pfree(group_sorted);
+ group_length = length(group_lines);
+ if (group_length)
+ {
+ int i = 0;
+
+ group_sorted = palloc(group_length * sizeof(List *));
+
+ foreach(line, group_lines)
+ group_sorted[i++] = lfirst(line);
+
+ qsort((void *) group_sorted, group_length, sizeof(List *), user_group_cmp);
+ }
+ else
+ group_sorted = NULL;
+ }
+
+
+ /*
+ * Load user/password mapping file
+ */
+ void
+ load_user()
+ {
+ FILE *user_file;
+ List *line;
+
+ if (user_lines)
+ free_lines(&user_lines);
+
+ user_file = user_openfile();
+ if (!user_file)
+ return;
+ user_lines = tokenize_file(user_file);
+ FreeFile(user_file);
+
+ /* create sorted lines for binary searching */
+ if (user_sorted)
+ pfree(user_sorted);
+ user_length = length(user_lines);
+ if (user_length)
+ {
+ int i = 0;
+
+ user_sorted = palloc(user_length * sizeof(List *));
+
+ foreach(line, user_lines)
+ user_sorted[i++] = lfirst(line);
+
+ qsort((void *) user_sorted, user_length, sizeof(List *), user_group_cmp);
+ }
+ else
+ user_sorted = NULL;
+ }
+
+
/*
* Read the config file and create a List of Lists of tokens in the file.
* If we find a file by the old name of the config file (pg_hba), we issue
***************
*** 437,496 ****
* follow directions and just installed his old hba file in the new database
* system.
*/
! static void
load_hba(void)
{
! int fd,
! bufsize;
FILE *file; /* The config file we have to read */
! char *old_conf_file;
if (hba_lines)
free_lines(&hba_lines);
! /*
! * The name of old config file that better not exist. Fail if config
! * file by old name exists. Put together the full pathname to the old
! * config file.
! */
! bufsize = (strlen(DataDir) + strlen(OLD_CONF_FILE) + 2) * sizeof(char);
! old_conf_file = (char *) palloc(bufsize);
! snprintf(old_conf_file, bufsize, "%s/%s", DataDir, OLD_CONF_FILE);
!
! if ((fd = open(old_conf_file, O_RDONLY | PG_BINARY, 0)) != -1)
! {
! /* Old config file exists. Tell this guy he needs to upgrade. */
! close(fd);
! elog(LOG, "A file exists by the name used for host-based authentication "
! "in prior releases of Postgres (%s). The name and format of "
! "the configuration file have changed, so this file should be "
! "converted.", old_conf_file);
}
else
{
! char *conf_file; /* The name of the config file we have to
! * read */
!
! /* put together the full pathname to the config file */
! bufsize = (strlen(DataDir) + strlen(CONF_FILE) + 2) * sizeof(char);
! conf_file = (char *) palloc(bufsize);
! snprintf(conf_file, bufsize, "%s/%s", DataDir, CONF_FILE);
!
! file = AllocateFile(conf_file, "r");
! if (file == NULL)
! {
! /* The open of the config file failed. */
! elog(LOG, "load_hba: Unable to open authentication config file \"%s\": %m",
! conf_file);
! }
! else
! {
! hba_lines = tokenize_file(file);
! FreeFile(file);
! }
! pfree(conf_file);
}
! pfree(old_conf_file);
}
--- 804,838 ----
* follow directions and just installed his old hba file in the new database
* system.
*/
! void
load_hba(void)
{
! int bufsize;
FILE *file; /* The config file we have to read */
! char *conf_file; /* The name of the config file */
if (hba_lines)
free_lines(&hba_lines);
! /* Put together the full pathname to the config file. */
! bufsize = (strlen(DataDir) + strlen(CONF_FILE) + 2) * sizeof(char);
! conf_file = (char *) palloc(bufsize);
! snprintf(conf_file, bufsize, "%s/%s", DataDir, CONF_FILE);
!
! file = AllocateFile(conf_file, "r");
! if (file == NULL)
! {
! /* The open of the config file failed. */
! elog(LOG, "load_hba: Unable to open authentication config file \"%s\": %m",
! conf_file);
! pfree(conf_file);
}
else
{
! hba_lines = tokenize_file(file);
! FreeFile(file);
}
! pfree(conf_file);
}
***************
*** 606,612 ****
/*
* Read the ident config file and create a List of Lists of tokens in the file.
*/
! static void
load_ident(void)
{
FILE *file; /* The map file we have to read */
--- 948,954 ----
/*
* Read the ident config file and create a List of Lists of tokens in the file.
*/
! void
load_ident(void)
{
FILE *file; /* The map file we have to read */
***************
*** 622,628 ****
map_file = (char *) palloc(bufsize);
snprintf(map_file, bufsize, "%s/%s", DataDir, USERMAP_FILE);
! file = AllocateFile(map_file, PG_BINARY_R);
if (file == NULL)
{
/* The open of the map file failed. */
--- 964,970 ----
map_file = (char *) palloc(bufsize);
snprintf(map_file, bufsize, "%s/%s", DataDir, USERMAP_FILE);
! file = AllocateFile(map_file, "r");
if (file == NULL)
{
/* The open of the map file failed. */
***************
*** 640,647 ****
/*
* Parse the string "*ident_response" as a response from a query to an Ident
! * server. If it's a normal response indicating a username, return true
! * and store the username at *ident_user. If it's anything else,
* return false.
*/
static bool
--- 982,989 ----
/*
* Parse the string "*ident_response" as a response from a query to an Ident
! * server. If it's a normal response indicating a user name, return true
! * and store the user name at *ident_user. If it's anything else,
* return false.
*/
static bool
***************
*** 708,714 ****
cursor++; /* Go over colon */
while (isblank(*cursor))
cursor++; /* skip blanks */
! /* Rest of line is username. Copy it over. */
i = 0;
while (*cursor != '\r' && i < IDENT_USERNAME_MAX)
ident_user[i++] = *cursor++;
--- 1050,1056 ----
cursor++; /* Go over colon */
while (isblank(*cursor))
cursor++; /* skip blanks */
! /* Rest of line is user name. Copy it over. */
i = 0;
while (*cursor != '\r' && i < IDENT_USERNAME_MAX)
ident_user[i++] = *cursor++;
***************
*** 725,731 ****
/*
* Talk to the ident server on host "remote_ip_addr" and find out who
* owns the tcp connection from his port "remote_port" to port
! * "local_port_addr" on host "local_ip_addr". Return the username the
* ident server gives as "*ident_user".
*
* IP addresses and port numbers are in network byte order.
--- 1067,1073 ----
/*
* Talk to the ident server on host "remote_ip_addr" and find out who
* owns the tcp connection from his port "remote_port" to port
! * "local_port_addr" on host "local_ip_addr". Return the user name the
* ident server gives as "*ident_user".
*
* IP addresses and port numbers are in network byte order.
***************
*** 955,960 ****
--- 1297,1304 ----
#endif
}
+
+
/*
* Determine the username of the initiator of the connection described
* by "port". Then look in the usermap file under the usermap
***************
*** 1010,1220 ****
return STATUS_ERROR;
}
- /*
- * Clear and reload tokenized file contents.
- */
- void
- load_hba_and_ident(void)
- {
- load_hba();
- load_ident();
- }
-
-
- /* Character set stuff. Not sure it really belongs in this file. */
-
- #ifdef CYR_RECODE
-
- #define CHARSET_FILE "charset.conf"
- #define MAX_CHARSETS 10
- #define KEY_HOST 1
- #define KEY_BASE 2
- #define KEY_TABLE 3
-
- struct CharsetItem
- {
- char Orig[MAX_TOKEN];
- char Dest[MAX_TOKEN];
- char Table[MAX_TOKEN];
- };
-
-
- static bool
- CharSetInRange(char *buf, int host)
- {
- int valid,
- i,
- FromAddr,
- ToAddr,
- tmp;
- struct in_addr file_ip_addr;
- char *p;
- unsigned int one = 0x80000000,
- NetMask = 0;
- unsigned char mask;
-
- p = strchr(buf, '/');
- if (p)
- {
- *p++ = '\0';
- valid = inet_aton(buf, &file_ip_addr);
- if (valid)
- {
- mask = strtoul(p, 0, 0);
- FromAddr = ntohl(file_ip_addr.s_addr);
- ToAddr = ntohl(file_ip_addr.s_addr);
- for (i = 0; i < mask; i++)
- {
- NetMask |= one;
- one >>= 1;
- }
- FromAddr &= NetMask;
- ToAddr = ToAddr | ~NetMask;
- tmp = ntohl(host);
- return ((unsigned) tmp >= (unsigned) FromAddr &&
- (unsigned) tmp <= (unsigned) ToAddr);
- }
- }
- else
- {
- p = strchr(buf, '-');
- if (p)
- {
- *p++ = '\0';
- valid = inet_aton(buf, &file_ip_addr);
- if (valid)
- {
- FromAddr = ntohl(file_ip_addr.s_addr);
- valid = inet_aton(p, &file_ip_addr);
- if (valid)
- {
- ToAddr = ntohl(file_ip_addr.s_addr);
- tmp = ntohl(host);
- return ((unsigned) tmp >= (unsigned) FromAddr &&
- (unsigned) tmp <= (unsigned) ToAddr);
- }
- }
- }
- else
- {
- valid = inet_aton(buf, &file_ip_addr);
- if (valid)
- {
- FromAddr = file_ip_addr.s_addr;
- return (unsigned) FromAddr == (unsigned) host;
- }
- }
- }
- return false;
- }
-
- void
- GetCharSetByHost(char *TableName, int host, const char *DataDir)
- {
- FILE *file;
- char buf[MAX_TOKEN],
- BaseCharset[MAX_TOKEN],
- OrigCharset[MAX_TOKEN],
- DestCharset[MAX_TOKEN],
- HostCharset[MAX_TOKEN],
- *map_file;
- int key,
- ChIndex = 0,
- c,
- i,
- bufsize;
- struct CharsetItem *ChArray[MAX_CHARSETS];
-
- *TableName = '\0';
- bufsize = (strlen(DataDir) + strlen(CHARSET_FILE) + 2) * sizeof(char);
- map_file = (char *) palloc(bufsize);
- snprintf(map_file, bufsize, "%s/%s", DataDir, CHARSET_FILE);
- file = AllocateFile(map_file, PG_BINARY_R);
- pfree(map_file);
- if (file == NULL)
- {
- /* XXX should we log a complaint? */
- return;
- }
- while ((c = getc(file)) != EOF)
- {
- if (c == '#')
- read_through_eol(file);
- else
- {
- /* Read the key */
- ungetc(c, file);
- next_token(file, buf, sizeof(buf));
- if (buf[0] != '\0')
- {
- key = 0;
- if (strcasecmp(buf, "HostCharset") == 0)
- key = KEY_HOST;
- if (strcasecmp(buf, "BaseCharset") == 0)
- key = KEY_BASE;
- if (strcasecmp(buf, "RecodeTable") == 0)
- key = KEY_TABLE;
- switch (key)
- {
- case KEY_HOST:
- /* Read the host */
- next_token(file, buf, sizeof(buf));
- if (buf[0] != '\0')
- {
- if (CharSetInRange(buf, host))
- {
- /* Read the charset */
- next_token(file, buf, sizeof(buf));
- if (buf[0] != '\0')
- strcpy(HostCharset, buf);
- }
- }
- break;
- case KEY_BASE:
- /* Read the base charset */
- next_token(file, buf, sizeof(buf));
- if (buf[0] != '\0')
- strcpy(BaseCharset, buf);
- break;
- case KEY_TABLE:
- /* Read the original charset */
- next_token(file, buf, sizeof(buf));
- if (buf[0] != '\0')
- {
- strcpy(OrigCharset, buf);
- /* Read the destination charset */
- next_token(file, buf, sizeof(buf));
- if (buf[0] != '\0')
- {
- strcpy(DestCharset, buf);
- /* Read the table filename */
- next_token(file, buf, sizeof(buf));
- if (buf[0] != '\0')
- {
- ChArray[ChIndex] =
- (struct CharsetItem *) palloc(sizeof(struct CharsetItem));
- strcpy(ChArray[ChIndex]->Orig, OrigCharset);
- strcpy(ChArray[ChIndex]->Dest, DestCharset);
- strcpy(ChArray[ChIndex]->Table, buf);
- ChIndex++;
- }
- }
- }
- break;
- }
- read_through_eol(file);
- }
- }
- }
- FreeFile(file);
-
- for (i = 0; i < ChIndex; i++)
- {
- if (strcasecmp(BaseCharset, ChArray[i]->Orig) == 0 &&
- strcasecmp(HostCharset, ChArray[i]->Dest) == 0)
- strncpy(TableName, ChArray[i]->Table, 79);
- pfree(ChArray[i]);
- }
- }
-
- #endif /* CYR_RECODE */
--- 1354,1356 ----
Index: src/backend/libpq/password.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/libpq/password.c,v
retrieving revision 1.41
diff -c -r1.41 password.c
*** src/backend/libpq/password.c 4 Mar 2002 01:46:03 -0000 1.41
--- src/backend/libpq/password.c 3 Apr 2002 03:48:44 -0000
***************
*** 33,39 ****
strcat(pw_file_fullname, "/");
strcat(pw_file_fullname, port->auth_arg);
! pw_file = AllocateFile(pw_file_fullname, PG_BINARY_R);
if (!pw_file)
{
elog(LOG, "verify_password: Unable to open password file \"%s\": %m",
--- 33,39 ----
strcat(pw_file_fullname, "/");
strcat(pw_file_fullname, port->auth_arg);
! pw_file = AllocateFile(pw_file_fullname, "r");
if (!pw_file)
{
elog(LOG, "verify_password: Unable to open password file \"%s\": %m",
Index: src/backend/libpq/pg_hba.conf.sample
===================================================================
RCS file: /cvsroot/pgsql/src/backend/libpq/pg_hba.conf.sample,v
retrieving revision 1.35
diff -c -r1.35 pg_hba.conf.sample
*** src/backend/libpq/pg_hba.conf.sample 8 Mar 2002 20:36:58 -0000 1.35
--- src/backend/libpq/pg_hba.conf.sample 3 Apr 2002 03:48:48 -0000
***************
*** 42,63 ****
#
# Format:
#
! # host DBNAME IP_ADDRESS ADDRESS_MASK AUTH_TYPE [AUTH_ARGUMENT]
#
! # DBNAME can be:
! # o a database name
! # o "all", which means the record matches all databases
! # o "sameuser", which means users can only access databases whose name
! # is the same as their username
#
! # IP_ADDRESS and ADDRESS_MASK are standard dotted decimal IP address and
# mask values. IP addresses can only be specified numerically, not as
# domain or host names.
#
# Do not prevent the superuser from accessing the template1 database.
# Various utility commands need access to template1.
#
! # AUTH_TYPE and AUTH_ARGUMENT are described below.
#
#
# hostssl
--- 42,77 ----
#
# Format:
#
! # host DATABASE USER IP_ADDRESS MASK AUTH_TYPE
#
! # DATABASE can be:
! # o a database name
! # o "sameuser", which means a user can only access a database with the
! # same name as their user name
! # o "samegroup", which means a user can only access databases when they
! # are members of a group with the same name as the database name
! # o "all", which matches all databases
! # o a list of database names, separated by commas
! # o a file name containing database names, starting with '@'
! #
! # USER can be:
! # o a user name
! # o "all", which matches all users
! # o a list of user names, separated by commas
! # o a group name, starting with '+'
! # o a file name containing user names, starting with '@'
#
! # Files read using '@' can contain comma-separated database/user names,
! # or one name per line. The files can also contain comments using '#'.
! #
! # IP_ADDRESS and MASK are standard dotted decimal IP address and
# mask values. IP addresses can only be specified numerically, not as
# domain or host names.
#
# Do not prevent the superuser from accessing the template1 database.
# Various utility commands need access to template1.
#
! # AUTH_TYPE is described below.
#
#
# hostssl
***************
*** 65,74 ****
#
# The format of this record is identical to "host".
#
! #
! #
! # It specifies hosts that required connection via secure SSL. "host"
! # records allow SSL connections too, but "hostssl" only allows SSL-secured
# connections.
#
# This keyword is only available if the server was compiled with SSL
--- 79,86 ----
#
# The format of this record is identical to "host".
#
! # It specifies hosts that require connection via secure SSL. "host"
! # allows SSL connections too, but "hostssl" requires SSL-secured
# connections.
#
# This keyword is only available if the server was compiled with SSL
***************
*** 82,91 ****
# connections. Without this record, UNIX-socket connections are disallowed
#
# Format:
! # local DBNAME AUTH_TYPE [AUTH_ARGUMENT]
#
# This format is identical to the "host" record type except there are no
! # IP_ADDRESS and ADDRESS_MASK fields.
#
#
#
--- 94,103 ----
# connections. Without this record, UNIX-socket connections are disallowed
#
# Format:
! # local DATABASE USER AUTH_TYPE
#
# This format is identical to the "host" record type except there are no
! # IP_ADDRESS and MASK fields.
#
#
#
***************
*** 96,152 ****
# has an AUTH_TYPE.
#
# trust:
! # No authentication is done. Any valid username is accepted,
# including the PostgreSQL superuser. This option should
# be used only for hosts where all users are trusted.
#
- # password:
- # Authentication is done by matching a password supplied
- # in clear by the host. If no AUTH_ARGUMENT is used, the
- # password is compared with the user's entry in the
- # pg_shadow table.
- #
- # If AUTH_ARGUMENT is specified, the username is looked up
- # in that file in the $PGDATA directory. If the username
- # is found but there is no password, the password is looked
- # up in pg_shadow. If a password exists in the file, it is
- # used instead. These secondary files allow fine-grained
- # control over who can access which databases and whether
- # a non-default password is required. The same file can be
- # used in multiple records for easier administration.
- # Password files can be maintained with the pg_passwd(1)
- # utility. Remember, these passwords override pg_shadow
- # passwords. Also, such passwords are passed over the network
- # in cleartext, meaning this should not be used on untrusted
- # networks.
- #
# md5:
! # Same as "password", except the password is encrypted over the
! # network. This method is preferable to "password" and "crypt"
! # except for pre-7.2 clients that don't support it. NOTE: md5 can
! # use usernames stored in secondary password files but ignores
! # passwords stored there. The pg_shadow password will always be
! # used.
#
# crypt:
! # Same as "md5", but uses crypt for pre-7.2 clients. You can
! # not store encrypted passwords in pg_shadow if you use this
! # method.
#
# ident:
# For TCP/IP connections, authentication is done by contacting the
# ident server on the client host. This is only as secure as the
! # client machine. On machines that support unix-domain socket
! # credentials (currently Linux, FreeBSD, NetBSD, and BSD/OS), this
! # method also works for "local" connections.
! #
! # AUTH_ARGUMENT is required. It determines how to map remote user
! # names to PostgreSQL user names. If you use "sameuser", the user
! # names are assumed to be the identical. If not, AUTH_ARGUMENT is
! # assumed to be a map name found in the $PGDATA/pg_ident.conf
! # file. The connection is accepted if that file contains an entry
! # for this map name with the ident-supplied username and the
! # requested PostgreSQL username.
#
# krb4:
# Kerberos V4 authentication is used. Allowed only for
--- 108,145 ----
# has an AUTH_TYPE.
#
# trust:
! # No authentication is done. Any valid user name is accepted,
# including the PostgreSQL superuser. This option should
# be used only for hosts where all users are trusted.
#
# md5:
! # Requires the client to supply an MD5 encrypted password for
! # authentication. This is the only method that allows encrypted
! # passwords to be stored in pg_shadow.
#
# crypt:
! # Same as "md5", but uses crypt for pre-7.2 clients.
#
+ # password:
+ # Same as "md5", but the password is sent in cleartext over
+ # the network. This should not be used on untrusted
+ # networks.
+ #
# ident:
# For TCP/IP connections, authentication is done by contacting the
# ident server on the client host. This is only as secure as the
! # client machine. You must specify the map name after the 'ident'
! # keyword. It determines how to map remote user names to
! # PostgreSQL user names. If you use "sameuser", the user names are
! # assumed to be identical. If not, the map name is looked up
! # in the $PGDATA/pg_ident.conf file. The connection is accepted if
! # that file contains an entry for this map name with the
! # ident-supplied username and the requested PostgreSQL username.
! #
! # On machines that support unix-domain socket credentials
! # (currently Linux, FreeBSD, NetBSD, and BSD/OS), ident allows
! # reliable authentication of 'local' connections without ident
! # running on the local machine.
#
# krb4:
# Kerberos V4 authentication is used. Allowed only for
***************
*** 157,166 ****
# TCP/IP connections, not for local UNIX-domain sockets.
#
# pam:
! # Authentication is passed off to PAM (PostgreSQL must be
! # configured --with-pam), using the default service name
! # "postgresql" - you can specify your own service name by
! # setting AUTH_ARGUMENT to the desired service name.
#
# reject:
# Reject the connection. This is used to reject certain hosts
--- 150,159 ----
# TCP/IP connections, not for local UNIX-domain sockets.
#
# pam:
! # Authentication is done by PAM using the default service name
! # "postgresql". You can specify your own service name by adding
! # the service name after the 'pam' keyword. To use this option,
! # PostgreSQL must be configured --with-pam.
#
# reject:
# Reject the connection. This is used to reject certain hosts
***************
*** 177,236 ****
# Allow any user on the local system to connect to any database under any
# username using Unix-domain sockets (the default for local connections):
#
! # TYPE DATABASE IP_ADDRESS MASK AUTH_TYPE AUTH_ARGUMENT
! # local all trust
#
# The same using local loopback TCP/IP connections:
#
! # TYPE DATABASE IP_ADDRESS MASK AUTH_TYPE AUTH_ARGUMENT
! # host all 127.0.0.1 255.255.255.255 trust
#
# Allow any user from any host with IP address 192.168.93.x to
# connect to database "template1" as the same username that ident reports
# for the connection (typically his Unix username):
#
! # TYPE DATABASE IP_ADDRESS MASK AUTH_TYPE AUTH_ARGUMENT
! # host template1 192.168.93.0 255.255.255.0 ident sameuser
#
# Allow a user from host 192.168.12.10 to connect to database "template1"
! # if the user's password in pg_shadow is correctly supplied:
#
! # TYPE DATABASE IP_ADDRESS MASK AUTH_TYPE AUTH_ARGUMENT
! # host template1 192.168.12.10 255.255.255.255 md5
#
# In the absence of preceding "host" lines, these two lines will reject
# all connection from 192.168.54.1 (since that entry will be matched
# first), but allow Kerberos V5 connections from anywhere else on the
# Internet. The zero mask means that no bits of the host IP address are
! # considered, so it matches any host:
#
#
! # TYPE DATABASE IP_ADDRESS MASK AUTH_TYPE AUTH_ARGUMENT
! # host all 192.168.54.1 255.255.255.255 reject
! # host all 0.0.0.0 0.0.0.0 krb5
#
# Allow users from 192.168.x.x hosts to connect to any database if they
# pass the ident check. For example, if ident says the user is "james" and
# he requests to connect as PostgreSQL user "guest", the connection is
# allowed if there is an entry in $PGDATA/pg_ident.conf with map name
# "phoenix" that says "james" is allowed to connect as "guest":
#
! # TYPE DATABASE IP_ADDRESS MASK AUTH_TYPE AUTH_ARGUMENT
! # host all 192.168.0.0 255.255.0.0 ident phoenix
#
- # If these are the only two lines for local connections, they will allow
- # local users to connect only to their own databases (databases with the
- # same name as their user name) except for administrators who may connect
- # to all databases. The file $PGDATA/admins lists the user names who are
- # permitted to connect to all databases. Passwords are required in all
- # cases. (If you prefer to use ident authorization, an ident map can
- # serve a parallel purpose to the password list file used here.)
- #
- # TYPE DATABASE IP_ADDRESS MASK AUTH_TYPE AUTH_ARGUMENT
- # local sameuser md5
- # local all md5 admins
#
- # See $PGDATA/pg_ident.conf for more information on Ident maps.
#
#
#
--- 170,239 ----
# Allow any user on the local system to connect to any database under any
# username using Unix-domain sockets (the default for local connections):
#
! # TYPE DATABASE USER IP_ADDRESS MASK AUTH_TYPE
! # local all all trust
#
# The same using local loopback TCP/IP connections:
#
! # TYPE DATABASE USER IP_ADDRESS MASK AUTH_TYPE
! # host all all 127.0.0.1 255.255.255.255 trust
#
# Allow any user from any host with IP address 192.168.93.x to
# connect to database "template1" as the same username that ident reports
# for the connection (typically his Unix username):
#
! # TYPE DATABASE USER IP_ADDRESS MASK AUTH_TYPE
! # host template1 all 192.168.93.0 255.255.255.0 ident sameuser
#
# Allow a user from host 192.168.12.10 to connect to database "template1"
! # if the user's password is correctly supplied:
#
! # TYPE DATABASE USER IP_ADDRESS MASK AUTH_TYPE
! # host template1 all 192.168.12.10 255.255.255.255 md5
#
# In the absence of preceding "host" lines, these two lines will reject
# all connection from 192.168.54.1 (since that entry will be matched
# first), but allow Kerberos V5 connections from anywhere else on the
# Internet. The zero mask means that no bits of the host IP address are
! # considered so it matches any host:
#
#
! # TYPE DATABASE USER IP_ADDRESS MASK AUTH_TYPE
! # host all all 192.168.54.1 255.255.255.255 reject
! # host all all 0.0.0.0 0.0.0.0 krb5
#
# Allow users from 192.168.x.x hosts to connect to any database if they
# pass the ident check. For example, if ident says the user is "james" and
# he requests to connect as PostgreSQL user "guest", the connection is
# allowed if there is an entry in $PGDATA/pg_ident.conf with map name
# "phoenix" that says "james" is allowed to connect as "guest":
+ # See $PGDATA/pg_ident.conf for more information on Ident maps.
#
! # TYPE DATABASE USER IP_ADDRESS MASK AUTH_TYPE
! # host all all 192.168.0.0 255.255.0.0 ident phoenix
! #
! # If these are the only three lines for local connections, they will
! # allow local users to connect only to their own databases (databases
! # with the same name as their user name) except for administrators and
! # members of group 'support' who may connect to all databases . The file
! # $PGDATA/admins contains a list of user names. Passwords are required in
! # all cases.
! #
! # TYPE DATABASE USER IP_ADDRESS MASK AUTH_TYPE
! # local sameuser all md5
! # local all @admins md5
! # local all +support md5
! #
! # The last two lines above can be combined into a single line:
! #
! # local all @admins,+support md5
! #
! # The database column can also use lists and file names, but not groups:
! #
! # local db1,db2,@demodbs all md5
! #
#
#
#
#
#
***************
*** 250,256 ****
# configuration is probably too liberal for you. Change it to use
# something other than "trust" authentication.
#
! # TYPE DATABASE IP_ADDRESS MASK AUTH_TYPE AUTH_ARGUMENT
! local all trust
! host all 127.0.0.1 255.255.255.255 trust
--- 253,259 ----
# configuration is probably too liberal for you. Change it to use
# something other than "trust" authentication.
#
! # TYPE DATABASE USER IP_ADDRESS MASK AUTH_TYPE
! local all all trust
! host all all 127.0.0.1 255.255.255.255 trust
Index: src/backend/postmaster/postmaster.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v
retrieving revision 1.271
diff -c -r1.271 postmaster.c
*** src/backend/postmaster/postmaster.c 15 Mar 2002 19:20:35 -0000 1.271
--- src/backend/postmaster/postmaster.c 3 Apr 2002 03:48:52 -0000
***************
*** 748,755 ****
/*
* Load cached files for client authentication.
*/
! load_hba_and_ident();
! load_password_cache();
/*
* We're ready to rock and roll...
--- 748,757 ----
/*
* Load cached files for client authentication.
*/
! load_hba();
! load_ident();
! load_user();
! load_group();
/*
* We're ready to rock and roll...
***************
*** 1389,1395 ****
elog(LOG, "Received SIGHUP, reloading configuration files");
SignalChildren(SIGHUP);
ProcessConfigFile(PGC_SIGHUP);
! load_hba_and_ident();
}
PG_SETMASK(&UnBlockSig);
--- 1391,1398 ----
elog(LOG, "Received SIGHUP, reloading configuration files");
SignalChildren(SIGHUP);
ProcessConfigFile(PGC_SIGHUP);
! load_hba();
! load_ident();
}
PG_SETMASK(&UnBlockSig);
***************
*** 2288,2296 ****
if (CheckPostmasterSignal(PMSIGNAL_PASSWORD_CHANGE))
{
/*
! * Password file has changed.
*/
! load_password_cache();
}
if (CheckPostmasterSignal(PMSIGNAL_WAKEN_CHILDREN))
--- 2291,2300 ----
if (CheckPostmasterSignal(PMSIGNAL_PASSWORD_CHANGE))
{
/*
! * Password or group file has changed.
*/
! load_user();
! load_group();
}
if (CheckPostmasterSignal(PMSIGNAL_WAKEN_CHILDREN))
Index: src/backend/utils/adt/quote.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/utils/adt/quote.c,v
retrieving revision 1.6
diff -c -r1.6 quote.c
*** src/backend/utils/adt/quote.c 28 Oct 2001 06:25:53 -0000 1.6
--- src/backend/utils/adt/quote.c 3 Apr 2002 03:48:52 -0000
***************
*** 124,131 ****
{
if (*cp1 == '"')
*cp2++ = '"';
- if (*cp1 == '\\')
- *cp2++ = '\\';
*cp2++ = *cp1++;
}
*cp2++ = '"';
--- 124,129 ----
***************
*** 234,241 ****
if (*cp1 == '"')
*cp2++ = '"';
- if (*cp1 == '\\')
- *cp2++ = '\\';
*cp2++ = *cp1++;
len--;
--- 232,237 ----
Index: src/backend/utils/init/miscinit.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/utils/init/miscinit.c,v
retrieving revision 1.85
diff -c -r1.85 miscinit.c
*** src/backend/utils/init/miscinit.c 4 Mar 2002 04:45:27 -0000 1.85
--- src/backend/utils/init/miscinit.c 3 Apr 2002 03:48:53 -0000
***************
*** 236,320 ****
#ifdef CYR_RECODE
- #define MAX_TOKEN 80
-
- /*
- * Some standard C libraries, including GNU, have an isblank() function.
- * Others, including Solaris, do not. So we have our own.
- */
- static bool
- isblank(const char c)
- {
- return c == ' ' || c == '\t';
- }
-
-
- /*
- * Grab one token out of fp. Tokens are strings of non-blank
- * characters bounded by blank characters, beginning of line, and end
- * of line. Blank means space or tab. Return the token as *buf.
- * Leave file positioned to character immediately after the token or
- * EOF, whichever comes first. If no more tokens on line, return null
- * string as *buf and position file to beginning of next line or EOF,
- * whichever comes first.
- */
- static void
- next_token(FILE *fp, char *buf, const int bufsz)
- {
- int c;
- char *eb = buf + (bufsz - 1);
-
- /* Move over initial token-delimiting blanks */
- while ((c = getc(fp)) != EOF && isblank(c))
- ;
-
- if (c != EOF && c != '\n')
- {
- /*
- * build a token in buf of next characters up to EOF, eol, or
- * blank. If the token gets too long, we still parse it
- * correctly, but the excess characters are not stored into *buf.
- */
- while (c != EOF && c != '\n' && !isblank(c))
- {
- if (buf < eb)
- *buf++ = c;
- c = getc(fp);
- }
-
- /*
- * Put back the char right after the token (critical in case it is
- * eol, since we need to detect end-of-line at next call).
- */
- if (c != EOF)
- ungetc(c, fp);
- }
- *buf = '\0';
- }
-
-
- static void
- read_through_eol(FILE *file)
- {
- int c;
-
- while ((c = getc(file)) != EOF && c != '\n')
- ;
- }
-
-
- void
SetCharSet(void)
{
FILE *file;
! char *p;
char *map_file;
char buf[MAX_TOKEN];
int i,
c;
unsigned char FromChar,
ToChar;
! char ChTable[80];
for (i = 0; i < 128; i++)
{
--- 236,252 ----
#ifdef CYR_RECODE
SetCharSet(void)
{
FILE *file;
! char *filename;
char *map_file;
char buf[MAX_TOKEN];
int i,
c;
unsigned char FromChar,
ToChar;
! char ChTable[MAX_TOKEN];
for (i = 0; i < 128; i++)
{
***************
*** 325,363 ****
if (IsUnderPostmaster)
{
GetCharSetByHost(ChTable, MyProcPort->raddr.in.sin_addr.s_addr, DataDir);
! p = ChTable;
}
else
! p = getenv("PG_RECODETABLE");
! if (p && *p != '\0')
{
! map_file = palloc(strlen(DataDir) + strlen(p) + 2);
! sprintf(map_file, "%s/%s", DataDir, p);
! file = AllocateFile(map_file, PG_BINARY_R);
pfree(map_file);
if (file == NULL)
return;
! while ((c = getc(file)) != EOF)
{
! if (c == '#')
! read_through_eol(file);
! else
{
! /* Read the FromChar */
! ungetc(c, file);
next_token(file, buf, sizeof(buf));
if (buf[0] != '\0')
{
! FromChar = strtoul(buf, 0, 0);
! /* Read the ToChar */
! next_token(file, buf, sizeof(buf));
! if (buf[0] != '\0')
{
! ToChar = strtoul(buf, 0, 0);
! RecodeForwTable[FromChar - 128] = ToChar;
! RecodeBackTable[ToChar - 128] = FromChar;
! read_through_eol(file);
}
}
}
--- 257,296 ----
if (IsUnderPostmaster)
{
GetCharSetByHost(ChTable, MyProcPort->raddr.in.sin_addr.s_addr, DataDir);
! filename = ChTable;
}
else
! filename = getenv("PG_RECODETABLE");
! if (filename && *filename != '\0')
{
! map_file = palloc(strlen(DataDir) + strlen(filename) + 2);
! sprintf(map_file, "%s/%s", DataDir, filename);
! file = AllocateFile(map_file, "r");
pfree(map_file);
if (file == NULL)
return;
!
! while (!feof(file))
{
! next_token(file, buf, sizeof(buf));
! if (buf[0] != '\0')
{
! FromChar = strtoul(buf, 0, 0);
! /* Read the ToChar */
next_token(file, buf, sizeof(buf));
if (buf[0] != '\0')
{
! ToChar = strtoul(buf, 0, 0);
! RecodeForwTable[FromChar - 128] = ToChar;
! RecodeBackTable[ToChar - 128] = FromChar;
!
! /* read to EOL */
! while (!feof(file) && buf[0])
{
! next_token(file, buf, sizeof(buf));
! elog(LOG, "SetCharSet: unknown tag %s in file %s"
! buf, filename);
}
}
}
***************
*** 366,371 ****
--- 299,305 ----
}
}
+
char *
convertstr(unsigned char *buff, int len, int dest)
{
***************
*** 384,390 ****
}
return ch;
}
! #endif
--- 318,523 ----
}
return ch;
}
!
! #define CHARSET_FILE "charset.conf"
! #define MAX_CHARSETS 10
! #define KEY_HOST 1
! #define KEY_BASE 2
! #define KEY_TABLE 3
!
! struct CharsetItem
! {
! char Orig[MAX_TOKEN];
! char Dest[MAX_TOKEN];
! char Table[MAX_TOKEN];
! };
!
!
! static bool
! CharSetInRange(char *buf, int host)
! {
! int valid,
! i,
! FromAddr,
! ToAddr,
! tmp;
! struct in_addr file_ip_addr;
! char *p;
! unsigned int one = 0x80000000,
! NetMask = 0;
! unsigned char mask;
!
! p = strchr(buf, '/');
! if (p)
! {
! *p++ = '\0';
! valid = inet_aton(buf, &file_ip_addr);
! if (valid)
! {
! mask = strtoul(p, 0, 0);
! FromAddr = ntohl(file_ip_addr.s_addr);
! ToAddr = ntohl(file_ip_addr.s_addr);
! for (i = 0; i < mask; i++)
! {
! NetMask |= one;
! one >>= 1;
! }
! FromAddr &= NetMask;
! ToAddr = ToAddr | ~NetMask;
! tmp = ntohl(host);
! return ((unsigned) tmp >= (unsigned) FromAddr &&
! (unsigned) tmp <= (unsigned) ToAddr);
! }
! }
! else
! {
! p = strchr(buf, '-');
! if (p)
! {
! *p++ = '\0';
! valid = inet_aton(buf, &file_ip_addr);
! if (valid)
! {
! FromAddr = ntohl(file_ip_addr.s_addr);
! valid = inet_aton(p, &file_ip_addr);
! if (valid)
! {
! ToAddr = ntohl(file_ip_addr.s_addr);
! tmp = ntohl(host);
! return ((unsigned) tmp >= (unsigned) FromAddr &&
! (unsigned) tmp <= (unsigned) ToAddr);
! }
! }
! }
! else
! {
! valid = inet_aton(buf, &file_ip_addr);
! if (valid)
! {
! FromAddr = file_ip_addr.s_addr;
! return (unsigned) FromAddr == (unsigned) host;
! }
! }
! }
! return false;
! }
!
!
! static void
! GetCharSetByHost(char *TableName, int host, const char *DataDir)
! {
! FILE *file;
! char buf[MAX_TOKEN],
! BaseCharset[MAX_TOKEN],
! OrigCharset[MAX_TOKEN],
! DestCharset[MAX_TOKEN],
! HostCharset[MAX_TOKEN],
! *map_file;
! int key,
! ChIndex = 0,
! c,
! i,
! bufsize;
! struct CharsetItem *ChArray[MAX_CHARSETS];
!
! *TableName = '\0';
! bufsize = (strlen(DataDir) + strlen(CHARSET_FILE) + 2) * sizeof(char);
! map_file = (char *) palloc(bufsize);
! snprintf(map_file, bufsize, "%s/%s", DataDir, CHARSET_FILE);
! file = AllocateFile(map_file, "r");
! pfree(map_file);
! if (file == NULL)
! {
! /* XXX should we log a complaint? */
! return;
! }
!
! while (!feof(file))
! {
! next_token(file, buf, sizeof(buf));
! if (buf[0] != '\0')
! {
! key = 0;
! if (strcasecmp(buf, "HostCharset") == 0)
! key = KEY_HOST;
! else if (strcasecmp(buf, "BaseCharset") == 0)
! key = KEY_BASE;
! else if (strcasecmp(buf, "RecodeTable") == 0)
! key = KEY_TABLE;
! else
! elog(LOG, "GetCharSetByHost: unknown tag %s in file %s"
! buf, CHARSET_FILE);
!
! switch (key)
! {
! case KEY_HOST:
! /* Read the host */
! next_token(file, buf, sizeof(buf));
! if (buf[0] != '\0')
! {
! if (CharSetInRange(buf, host))
! {
! /* Read the charset */
! next_token(file, buf, sizeof(buf));
! if (buf[0] != '\0')
! strcpy(HostCharset, buf);
! }
! }
! break;
! case KEY_BASE:
! /* Read the base charset */
! next_token(file, buf, sizeof(buf));
! if (buf[0] != '\0')
! strcpy(BaseCharset, buf);
! break;
! case KEY_TABLE:
! /* Read the original charset */
! next_token(file, buf, sizeof(buf));
! if (buf[0] != '\0')
! {
! strcpy(OrigCharset, buf);
! /* Read the destination charset */
! next_token(file, buf, sizeof(buf));
! if (buf[0] != '\0')
! {
! strcpy(DestCharset, buf);
! /* Read the table filename */
! next_token(file, buf, sizeof(buf));
! if (buf[0] != '\0')
! {
! ChArray[ChIndex] =
! (struct CharsetItem *) palloc(sizeof(struct CharsetItem));
! strcpy(ChArray[ChIndex]->Orig, OrigCharset);
! strcpy(ChArray[ChIndex]->Dest, DestCharset);
! strcpy(ChArray[ChIndex]->Table, buf);
! ChIndex++;
! }
! }
! }
! break;
! }
!
! /* read to EOL */
! while (!feof(file) && buf[0])
! {
! next_token(file, buf, sizeof(buf));
! elog(LOG, "GetCharSetByHost: unknown tag %s in file %s"
! buf, CHARSET_FILE);
! }
! }
! }
! FreeFile(file);
!
! for (i = 0; i < ChIndex; i++)
! {
! if (strcasecmp(BaseCharset, ChArray[i]->Orig) == 0 &&
! strcasecmp(HostCharset, ChArray[i]->Dest) == 0)
! strncpy(TableName, ChArray[i]->Table, 79);
! pfree(ChArray[i]);
! }
! }
!
! #endif /* CYR_RECODE */
Index: src/bin/Makefile
===================================================================
RCS file: /cvsroot/pgsql/src/bin/Makefile,v
retrieving revision 1.34
diff -c -r1.34 Makefile
*** src/bin/Makefile 18 Feb 2001 18:33:59 -0000 1.34
--- src/bin/Makefile 3 Apr 2002 03:48:53 -0000
***************
*** 14,20 ****
include $(top_builddir)/src/Makefile.global
DIRS := initdb initlocation ipcclean pg_ctl pg_dump pg_id \
! pg_passwd psql scripts pg_config
ifdef MULTIBYTE
DIRS += pg_encoding
--- 14,20 ----
include $(top_builddir)/src/Makefile.global
DIRS := initdb initlocation ipcclean pg_ctl pg_dump pg_id \
! psql scripts pg_config
ifdef MULTIBYTE
DIRS += pg_encoding
Index: src/bin/initdb/initdb.sh
===================================================================
RCS file: /cvsroot/pgsql/src/bin/initdb/initdb.sh,v
retrieving revision 1.145
diff -c -r1.145 initdb.sh
*** src/bin/initdb/initdb.sh 2 Mar 2002 21:39:34 -0000 1.145
--- src/bin/initdb/initdb.sh 3 Apr 2002 03:48:53 -0000
***************
*** 506,514 ****
"$PGPATH"/postgres $PGSQL_OPT template1 >/dev/null <<EOF
-- Create a trigger so that direct updates to pg_shadow will be written
! -- to the flat password file pg_pwd
CREATE TRIGGER pg_sync_pg_pwd AFTER INSERT OR UPDATE OR DELETE ON pg_shadow \
! FOR EACH ROW EXECUTE PROCEDURE update_pg_pwd();
-- needs to be done before alter user, because alter user checks that
-- pg_shadow is secure ...
REVOKE ALL on pg_shadow FROM public;
--- 506,516 ----
"$PGPATH"/postgres $PGSQL_OPT template1 >/dev/null <<EOF
-- Create a trigger so that direct updates to pg_shadow will be written
! -- to the flat password/group files pg_pwd and pg_group
CREATE TRIGGER pg_sync_pg_pwd AFTER INSERT OR UPDATE OR DELETE ON pg_shadow \
! FOR EACH ROW EXECUTE PROCEDURE update_pg_pwd_and_pg_group();
! CREATE TRIGGER pg_sync_pg_group AFTER INSERT OR UPDATE OR DELETE ON pg_group \
! FOR EACH ROW EXECUTE PROCEDURE update_pg_pwd_and_pg_group();
-- needs to be done before alter user, because alter user checks that
-- pg_shadow is secure ...
REVOKE ALL on pg_shadow FROM public;
***************
*** 544,549 ****
--- 546,556 ----
if [ ! -f "$PGDATA"/global/pg_pwd ]; then
echo
echo "The password file wasn't generated. Please report this problem." 1>&2
+ exit_nicely
+ fi
+ if [ ! -f "$PGDATA"/global/pg_group ]; then
+ echo
+ echo "The group file wasn't generated. Please report this problem." 1>&2
exit_nicely
fi
echo "ok"
Index: src/include/miscadmin.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/miscadmin.h,v
retrieving revision 1.101
diff -c -r1.101 miscadmin.h
*** src/include/miscadmin.h 4 Mar 2002 01:46:04 -0000 1.101
--- src/include/miscadmin.h 3 Apr 2002 03:48:54 -0000
***************
*** 219,225 ****
extern int CheckPathAccess(char *path, char *name, int open_mode);
#ifdef CYR_RECODE
- extern void GetCharSetByHost(char *TableName, int host, const char *DataDir);
extern void SetCharSet(void);
extern char *convertstr(unsigned char *buff, int len, int dest);
#endif
--- 219,224 ----
Index: src/include/catalog/pg_proc.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/catalog/pg_proc.h,v
retrieving revision 1.224
diff -c -r1.224 pg_proc.h
*** src/include/catalog/pg_proc.h 29 Mar 2002 19:06:19 -0000 1.224
--- src/include/catalog/pg_proc.h 3 Apr 2002 03:49:00 -0000
***************
*** 2101,2108 ****
DATA(insert OID = 1637 ( like_escape PGUID 12 f t t t 2 f 25 "25 25" 100 0 0 100 like_escape - _null_ ));
DESCR("convert match pattern to use backslash escapes");
! DATA(insert OID = 1689 ( update_pg_pwd PGUID 12 f t f t 0 f 0 "" 100 0 0 100 update_pg_pwd - _null_ ));
! DESCR("update pg_pwd file");
/* Oracle Compatibility Related Functions - By Edmund Mergl <E.Mergl@bawue.de> */
DATA(insert OID = 868 ( strpos PGUID 12 f t t t 2 f 23 "25 25" 100 0 0 100 textpos - _null_ ));
--- 2101,2108 ----
DATA(insert OID = 1637 ( like_escape PGUID 12 f t t t 2 f 25 "25 25" 100 0 0 100 like_escape - _null_ ));
DESCR("convert match pattern to use backslash escapes");
! DATA(insert OID = 1689 ( update_pg_pwd_and_pg_group PGUID 12 f t f t 0 f 0 "" 100 0 0 100
update_pg_pwd_and_pg_group- _null_ ));
! DESCR("update pg_pwd and pg_group files");
/* Oracle Compatibility Related Functions - By Edmund Mergl <E.Mergl@bawue.de> */
DATA(insert OID = 868 ( strpos PGUID 12 f t t t 2 f 23 "25 25" 100 0 0 100 textpos - _null_ ));
Index: src/include/commands/user.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/commands/user.h,v
retrieving revision 1.17
diff -c -r1.17 user.h
*** src/include/commands/user.h 1 Mar 2002 22:45:17 -0000 1.17
--- src/include/commands/user.h 3 Apr 2002 03:49:01 -0000
***************
*** 10,17 ****
--- 10,25 ----
#ifndef USER_H
#define USER_H
+ #include "fmgr.h"
#include "nodes/parsenodes.h"
+ #define PWD_FILE "pg_pwd"
+
+ #define USER_GROUP_FILE "pg_group"
+
+
+ extern char *group_getfilename(void);
+ extern char *user_getfilename(void);
extern void CreateUser(CreateUserStmt *stmt);
extern void AlterUser(AlterUserStmt *stmt);
extern void AlterUserSet(AlterUserSetStmt *stmt);
***************
*** 21,26 ****
extern void AlterGroup(AlterGroupStmt *stmt, const char *tag);
extern void DropGroup(DropGroupStmt *stmt);
! extern Datum update_pg_pwd(PG_FUNCTION_ARGS);
#endif /* USER_H */
--- 29,34 ----
extern void AlterGroup(AlterGroupStmt *stmt, const char *tag);
extern void DropGroup(DropGroupStmt *stmt);
! extern Datum update_pg_pwd_and_pg_group(PG_FUNCTION_ARGS);
#endif /* USER_H */
Index: src/include/libpq/crypt.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/libpq/crypt.h,v
retrieving revision 1.19
diff -c -r1.19 crypt.h
*** src/include/libpq/crypt.h 12 Nov 2001 01:52:46 -0000 1.19
--- src/include/libpq/crypt.h 3 Apr 2002 03:49:01 -0000
***************
*** 15,31 ****
#include "libpq/libpq-be.h"
- #define CRYPT_PWD_FILE_SEPSTR "\t"
-
/* Also defined in interfaces/odbc/md5.h */
#define MD5_PASSWD_LEN 35
#define isMD5(passwd) (strncmp((passwd),"md5",3) == 0 && \
strlen(passwd) == MD5_PASSWD_LEN)
-
- extern char *crypt_getpwdfilename(void);
- extern void load_password_cache(void);
extern int md5_crypt_verify(const Port *port, const char *user,
const char *pgpass);
--- 15,26 ----
Index: src/include/libpq/hba.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/libpq/hba.h,v
retrieving revision 1.31
diff -c -r1.31 hba.h
*** src/include/libpq/hba.h 5 Nov 2001 17:46:33 -0000 1.31
--- src/include/libpq/hba.h 3 Apr 2002 03:49:02 -0000
***************
*** 15,29 ****
#include <netinet/in.h>
#endif
#define CONF_FILE "pg_hba.conf"
/* Name of the config file */
#define USERMAP_FILE "pg_ident.conf"
/* Name of the usermap file */
- #define OLD_CONF_FILE "pg_hba"
- /* Name of the config file in prior releases of Postgres. */
-
#define IDENT_PORT 113
/* Standard TCP port number for Ident service. Assigned by IANA */
--- 15,28 ----
#include <netinet/in.h>
#endif
+ #include "nodes/pg_list.h"
+
#define CONF_FILE "pg_hba.conf"
/* Name of the config file */
#define USERMAP_FILE "pg_ident.conf"
/* Name of the usermap file */
#define IDENT_PORT 113
/* Standard TCP port number for Ident service. Assigned by IANA */
***************
*** 46,53 ****
typedef struct Port hbaPort;
extern int hba_getauthmethod(hbaPort *port);
extern int authident(hbaPort *port);
- extern void load_hba_and_ident(void);
#endif
--- 45,59 ----
typedef struct Port hbaPort;
+ #define MAX_TOKEN 256
+
+ extern void next_token(FILE *fp, char *buf, const int bufsz);
+ extern List **get_user_line(const char *user);
+ extern void load_hba(void);
+ extern void load_ident(void);
+ extern void load_user(void);
+ extern void load_group(void);
extern int hba_getauthmethod(hbaPort *port);
extern int authident(hbaPort *port);
#endif
Index: src/test/regress/expected/opr_sanity.out
===================================================================
RCS file: /cvsroot/pgsql/src/test/regress/expected/opr_sanity.out,v
retrieving revision 1.34
diff -c -r1.34 opr_sanity.out
*** src/test/regress/expected/opr_sanity.out 30 Sep 2001 17:37:32 -0000 1.34
--- src/test/regress/expected/opr_sanity.out 3 Apr 2002 03:49:03 -0000
***************
*** 30,36 ****
AND p1.proname !~ '^pl[^_]+_call_handler$'
AND p1.proname !~ '^RI_FKey_'
AND p1.proname !~ 'costestimate$'
! AND p1.proname != 'update_pg_pwd';
oid | proname
-----+---------
(0 rows)
--- 30,36 ----
AND p1.proname !~ '^pl[^_]+_call_handler$'
AND p1.proname !~ '^RI_FKey_'
AND p1.proname !~ 'costestimate$'
! AND p1.proname != 'update_pg_pwd_and_pg_group';
oid | proname
-----+---------
(0 rows)
Index: src/test/regress/sql/opr_sanity.sql
===================================================================
RCS file: /cvsroot/pgsql/src/test/regress/sql/opr_sanity.sql,v
retrieving revision 1.23
diff -c -r1.23 opr_sanity.sql
*** src/test/regress/sql/opr_sanity.sql 30 Sep 2001 17:37:32 -0000 1.23
--- src/test/regress/sql/opr_sanity.sql 3 Apr 2002 03:49:04 -0000
***************
*** 33,39 ****
AND p1.proname !~ '^pl[^_]+_call_handler$'
AND p1.proname !~ '^RI_FKey_'
AND p1.proname !~ 'costestimate$'
! AND p1.proname != 'update_pg_pwd';
-- Look for conflicting proc definitions (same names and input datatypes).
-- (This test should be dead code now that we have the unique index
--- 33,39 ----
AND p1.proname !~ '^pl[^_]+_call_handler$'
AND p1.proname !~ '^RI_FKey_'
AND p1.proname !~ 'costestimate$'
! AND p1.proname != 'update_pg_pwd_and_pg_group';
-- Look for conflicting proc definitions (same names and input datatypes).
-- (This test should be dead code now that we have the unique index