diff options
25 files changed, 1412 insertions, 1890 deletions
diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml index 76542f98720..4afd179a4b1 100644 --- a/doc/src/sgml/client-auth.sgml +++ b/doc/src/sgml/client-auth.sgml @@ -1,5 +1,5 @@ <!-- -$Header: /cvsroot/pgsql/doc/src/sgml/client-auth.sgml,v 1.33 2002/03/22 19:20:06 petere Exp $ +$Header: /cvsroot/pgsql/doc/src/sgml/client-auth.sgml,v 1.34 2002/04/04 04:25:44 momjian Exp $ --> <chapter id="client-authentication"> @@ -10,14 +10,13 @@ $Header: /cvsroot/pgsql/doc/src/sgml/client-auth.sgml,v 1.33 2002/03/22 19:20:06 </indexterm> <para> - When a client application connects to the database server, it specifies which - <productname>PostgreSQL</productname> user name it wants to connect as, - much the same way one logs into a Unix computer as a particular user. - Within the SQL environment the active - database user name determines access privileges to database - objects -- see <xref linkend="user-manag"> for more information - about that. It is therefore obviously essential to restrict which - database user name(s) a given client can connect as. + When a client application connects to the database server, it + specifies which <productname>PostgreSQL</productname> user name it + wants to connect as, much the same way one logs into a Unix computer + as a particular user. Within the SQL environment the active database + user name determines access privileges to database objects -- see + <xref linkend="user-manag"> for more information. Therefore, it is + essential to restrict which database users can connect. </para> <para> @@ -30,20 +29,19 @@ $Header: /cvsroot/pgsql/doc/src/sgml/client-auth.sgml,v 1.33 2002/03/22 19:20:06 <para> <productname>PostgreSQL</productname> offers a number of different - client authentication methods. The method to be used can be selected - on the basis of (client) host and database; some authentication methods - allow you to restrict by user name as well. + client authentication methods. The method to be used can be selected + on the basis of (client) host, database, and user. </para> <para> - <productname>PostgreSQL</productname> database user names are logically + <productname>PostgreSQL</productname> user names are logically separate from user names of the operating system in which the server - runs. If all the users of a particular server also have accounts on + runs. If all the users of a particular server also have accounts on the server's machine, it makes sense to assign database user names - that match their operating system user names. However, a server that accepts remote - connections may have many users who have no local account, and in such - cases there need be no connection between database user names and OS - user names. + that match their operating system user names. However, a server that + accepts remote connections may have many users who have no local + account, and in such cases there need be no connection between + database user names and OS user names. </para> <sect1 id="pg-hba-conf"> @@ -56,39 +54,39 @@ $Header: /cvsroot/pgsql/doc/src/sgml/client-auth.sgml,v 1.33 2002/03/22 19:20:06 <para> Client authentication is controlled by the file <filename>pg_hba.conf</filename> in the data directory, e.g., - <filename>/usr/local/pgsql/data/pg_hba.conf</filename>. (<acronym>HBA</> stands - for host-based authentication.) A default <filename>pg_hba.conf</filename> - file is installed when the - data area is initialized by <command>initdb</command>. + <filename>/usr/local/pgsql/data/pg_hba.conf</filename>. + (<acronym>HBA</> stands for host-based authentication.) A default + <filename>pg_hba.conf</filename> file is installed when the data area + is initialized by <command>initdb</command>. </para> <para> - The general format of the <filename>pg_hba.conf</filename> file is - of a set of records, one per line. Blank lines and lines beginning - with a hash character (<quote>#</quote>) are ignored. A record is - made up of a number of fields which are separated by spaces and/or - tabs. Records cannot be continued across lines. + The general format of the <filename>pg_hba.conf</filename> file is of + a set of records, one per line. Blank lines are ignored, as is any + text after the <quote>#</quote> comment character. A record is made + up of a number of fields which are separated by spaces and/or tabs. + Fields can contain white space if the field value is quoted. Records + cannot be continued across lines. </para> <para> Each record specifies a connection type, a client IP address range - (if relevant for the connection type), a database name or names, + (if relevant for the connection type), a database name, a user name, and the authentication method to be used for connections matching - these parameters. - The first record that matches the type, client address, and requested - database name of a connection attempt is used to do the - authentication step. There is no <quote>fall-through</> or + these parameters. The first record with a matching connection type, + client address, requested database, and user name is used to perform + authentication. There is no <quote>fall-through</> or <quote>backup</>: if one record is chosen and the authentication - fails, the following records are not considered. If no record - matches, the access will be denied. + fails, subsequent records are not considered. If no record matches, + access is denied. </para> <para> A record may have one of the three formats <synopsis> -local <replaceable>database</replaceable> <replaceable>authentication-method</replaceable> [ <replaceable>authentication-option</replaceable> ] -host <replaceable>database</replaceable> <replaceable>IP-address</replaceable> <replaceable>IP-mask</replaceable> <replaceable>authentication-method</replaceable> [ <replaceable>authentication-option</replaceable> ] -hostssl <replaceable>database</replaceable> <replaceable>IP-address</replaceable> <replaceable>IP-mask</replaceable> <replaceable>authentication-method</replaceable> [ <replaceable>authentication-option</replaceable> ] +local <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>authentication-method</replaceable> [ <replaceable>authentication-option</replaceable> ] +host <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>IP-address</replaceable> <replaceable>IP-mask</replaceable> <replaceable>authentication-method</replaceable> +hostssl <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>IP-address</replaceable> <replaceable>IP-mask</replaceable> <replaceable>authentication-method</replaceable> </synopsis> The meaning of the fields is as follows: @@ -97,7 +95,7 @@ hostssl <replaceable>database</replaceable> <replaceable>IP-address</replaceable <term><literal>local</literal></term> <listitem> <para> - This record pertains to connection attempts over Unix domain + This record applies to connection attempts using Unix domain sockets. </para> </listitem> @@ -107,10 +105,11 @@ hostssl <replaceable>database</replaceable> <replaceable>IP-address</replaceable <term><literal>host</literal></term> <listitem> <para> - This record pertains to connection attempts over TCP/IP - networks. Note that TCP/IP connections are completely disabled - unless the server is started with the <option>-i</option> switch or - the equivalent configuration parameter is set. + This record applied to connection attempts using TCP/IP networks. + Note that TCP/IP connections are disabled unless the server is + started with the <option>-i</option> option or the + <literal>tcpip_socket</> <filename>postgresql.conf</> + configuration parameter is enabled. </para> </listitem> </varlistentry> @@ -119,13 +118,13 @@ hostssl <replaceable>database</replaceable> <replaceable>IP-address</replaceable <term><literal>hostssl</literal></term> <listitem> <para> - This record pertains to connection attempts with SSL over + This record applies to connection attempts using SSL over TCP/IP. To make use of this option the server must be built with SSL support enabled. Furthermore, SSL must be enabled with the <option>-l</> option or equivalent configuration setting when the server is started. (Note: <literal>host</literal> records will match either SSL or non-SSL connection attempts, but - <literal>hostssl</literal> records match only SSL connections.) + <literal>hostssl</literal> records requires SSL connections.) </para> </listitem> </varlistentry> @@ -134,12 +133,35 @@ hostssl <replaceable>database</replaceable> <replaceable>IP-address</replaceable <term><replaceable>database</replaceable></term> <listitem> <para> - Specifies the database that this record applies to. The value + Specifies the database for this record. The value <literal>all</literal> specifies that it applies to all databases, while the value <literal>sameuser</> identifies the - database with the same name as the connecting user. Otherwise, - this is the name of a specific <productname>PostgreSQL</productname> - database. + database with the same name as the connecting user. The value + <literal>samegroup</> identifies a group with the same name as + the database name. Only members of this group can connect to the + database. Otherwise, this is the name of a specific + <productname>PostgreSQL</productname> database. Multiple database + names can be supplied by separating them with commas. A file + containing database names can be specified by preceding the file + name with <literal>@</>. The file must be in the same directory + as <filename>pg_hba.conf</>. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><replaceable>user</replaceable></term> + <listitem> + <para> + Specifies the user for this record. The value + <literal>all</literal> specifies that it applies to all users. + Otherwise, this is the name of a specific + <productname>PostgreSQL</productname> user. Multiple user names + can be supplied by separating them with commas. Group names can + be specified by preceding the group name with <literal>+</>. A + file containing user names can be specified by preceding the file + name with <literal>@</>. The file must be in the same directory + as <filename>pg_hba.conf</>. </para> </listitem> </varlistentry> @@ -149,10 +171,9 @@ hostssl <replaceable>database</replaceable> <replaceable>IP-address</replaceable <term><replaceable>IP mask</replaceable></term> <listitem> <para> - These two fields specify to which client machines a - <literal>host</literal> or <literal>hostssl</literal> - record applies, based on their IP - address. (Of course IP addresses can be spoofed but this + These two fields specify the client machine IP addresses + (<literal>host</literal> or <literal>hostssl</literal>) for this + record. (Of course IP addresses can be spoofed but this consideration is beyond the scope of <productname>PostgreSQL</productname>.) The precise logic is that <blockquote> @@ -169,10 +190,9 @@ hostssl <replaceable>database</replaceable> <replaceable>IP-address</replaceable <term><replaceable>authentication method</replaceable></term> <listitem> <para> - Specifies the method that users must use to authenticate themselves - when connecting under the control of this authentication record. - The possible choices are summarized here, - details are in <xref linkend="auth-methods">. + Specifies the authentication method to use when connecting via + this record. The possible choices are summarized here; details + are in <xref linkend="auth-methods">. <variablelist> <varlistentry> @@ -190,66 +210,41 @@ hostssl <replaceable>database</replaceable> <replaceable>IP-address</replaceable <term><literal>reject</></term> <listitem> <para> - The connection is rejected unconditionally. This is mostly - useful to <quote>filter out</> certain hosts from a group. + The connection is rejected unconditionally. This is useful for + <quote>filtering out</> certain hosts from a group. </para> </listitem> </varlistentry> <varlistentry> - <term><literal>password</></term> + <term><literal>md5</></term> <listitem> <para> - The client is required to supply a password which is required to - match the database password that was set up for the user. - </para> - - <para> - An optional file name may be specified after the - <literal>password</literal> keyword. This file is expected to - contain a list of users who may connect using this record, - and optionally alternative passwords for them. - </para> - - <para> - The password is sent over the wire in clear text. For better - protection, use the <literal>md5</literal> or - <literal>crypt</literal> methods. + Requires the client to supply an MD5 encrypted password for + authentication. This is the only method that allows encrypted + passwords to be stored in pg_shadow. </para> </listitem> </varlistentry> <varlistentry> - <term><literal>md5</></term> + <term><literal>crypt</></term> <listitem> <para> - Like the <literal>password</literal> method, but the password - is sent over the wire encrypted using a simple - challenge-response protocol. This protects against incidental - wire-sniffing. This is now the recommended choice for - password-based authentication. - </para> - - <para> - The name of a file may follow the - <literal>md5</literal> keyword. It contains a list of users - who may connect using this record. + Like <literal>md5</literal> method but uses older crypt + encryption, which is needed for pre-7.2 clients. + <literal>md5</literal> is preferred for 7.2 and later clients. </para> </listitem> </varlistentry> <varlistentry> - <term><literal>crypt</></term> + <term><literal>password</></term> <listitem> <para> - Like the <literal>md5</literal> method but uses older crypt - encryption, which is needed for pre-7.2 - clients. <literal>md5</literal> is - preferred for 7.2 and later clients. The <literal>crypt</> - method is not compatible with encrypting passwords in - <filename>pg_shadow</>, and may fail if client and server - machines have different implementations of the crypt() library - routine. + Same as "md5", but the password is sent in cleartext over the + network. This should not be used on untrusted networks. + </para> </para> </listitem> </varlistentry> @@ -278,34 +273,36 @@ hostssl <replaceable>database</replaceable> <replaceable>IP-address</replaceable <term><literal>ident</></term> <listitem> <para> - The identity of the user as determined on login to the - operating system is used by <productname>PostgreSQL</productname> - to determine whether the user - is allowed to connect as the requested database user. - For TCP/IP connections the user's identity is determined by - contacting the <firstterm>ident</firstterm> server on the client - host. (Note that this is only as reliable as the remote ident - server; ident authentication should never be used for remote hosts - whose administrators are not trustworthy.) - On operating systems - supporting <symbol>SO_PEERCRED</> requests for Unix domain sockets, - ident authentication is possible for local connections; - the system is then asked for the connecting user's identity. - </para> + For TCP/IP connections, authentication is done by contacting + the <firstterm>ident</firstterm> server on the client host. + This is only as secure as the client machine. You must specify + the map name after the 'ident' keyword. It determines how to + map remote user names to PostgreSQL user names. If you use + "sameuser", the user names are assumed to be identical. If + not, the map name is looked up in the $PGDATA/pg_ident.conf + file. The connection is accepted if that file contains an + entry for this map name with the ident-supplied user name and + the requested PostgreSQL user name. + </para> + <para> + On machines that support unix-domain socket credentials + (currently Linux, FreeBSD, NetBSD, and BSD/OS), ident allows + reliable authentication of 'local' connections without ident + running on the local machine. + </para> <para> - On systems without <symbol>SO_PEERCRED</> requests, ident authentication - is only available for TCP/IP connections. As a workaround, - it is possible to - specify the <systemitem class="systemname">localhost</> address - <systemitem class="systemname">127.0.0.1</> and make connections - to this address. + On systems without <symbol>SO_PEERCRED</> requests, ident + authentication is only available for TCP/IP connections. As a + work around, it is possible to specify the <systemitem + class="systemname">localhost</> address <systemitem + class="systemname">127.0.0.1</> and make connections to this + address. </para> <para> - The <replaceable>authentication option</replaceable> following - the <literal>ident</> keyword specifies the name of an - <firstterm>ident map</firstterm> that specifies which operating - system users equate with which database users. See below for - details. + Following the <literal>ident</> keyword, an <firstterm>ident + map</firstterm> name should be supplied which specifies which + operating system users equate with which database users. See + below for details. </para> </listitem> </varlistentry> @@ -315,17 +312,16 @@ hostssl <replaceable>database</replaceable> <replaceable>IP-address</replaceable <listitem> <para> This authentication type operates similarly to - <firstterm>password</firstterm>, with the main difference that - it will use PAM (Pluggable Authentication Modules) as the - authentication mechanism. The <replaceable>authentication - option</replaceable> following the <literal>pam</> keyword - specifies the service name that will be passed to PAM. The - default service name is <literal>postgresql</literal>. - For more information about PAM, please read the <ulink - url="http://www.kernel.org/pub/linux/libs/pam/"><productname>Linux-PAM</productname> - Page</ulink> and/or the <ulink - url="http://www.sun.com/software/solaris/pam/"><systemitem class="osname">Solaris</> PAM - Page</ulink>. + <firstterm>password</firstterm> except that it uses PAM + (Pluggable Authentication Modules) as the authentication + mechanism. The default PAM service name is + <literal>postgresql</literal>. You can optionally supply you + own service name after the <literal>pam</> keyword in the + file. For more information about PAM, please read the <ulink + url="http://www.kernel.org/pub/linux/libs/pam/"><productname>L + inux-PAM</productname> Page</ulink> and the <ulink + url="http://www.sun.com/software/solaris/pam/"><systemitem + class="osname">Solaris</> PAM Page</ulink>. </para> </listitem> </varlistentry> @@ -336,42 +332,33 @@ hostssl <replaceable>database</replaceable> <replaceable>IP-address</replaceable </listitem> </varlistentry> - <varlistentry> - <term><replaceable>authentication option</replaceable></term> - <listitem> - <para> - This field is interpreted differently depending on the - authentication method, as described above. - </para> - </listitem> - </varlistentry> </variablelist> </para> <para> Since the <filename>pg_hba.conf</filename> records are examined sequentially for each connection attempt, the order of the records is - very significant. Typically, earlier records will have tight - connection match parameters and weaker authentication methods, - while later records will have looser match parameters and stronger - authentication methods. For example, one might wish to use - <literal>trust</> authentication for local TCP connections but - require a password for remote TCP connections. In this case a - record specifying <literal>trust</> authentication for connections - from 127.0.0.1 would appear before a record specifying password - authentication for a wider range of allowed client IP addresses. + significant. Typically, earlier records will have tight connection + match parameters and weaker authentication methods, while later + records will have looser match parameters and stronger authentication + methods. For example, one might wish to use <literal>trust</> + authentication for local TCP connections but require a password for + remote TCP connections. In this case a record specifying + <literal>trust</> authentication for connections from 127.0.0.1 would + appear before a record specifying password authentication for a wider + range of allowed client IP addresses. </para> <para> <indexterm> <primary>SIGHUP</primary> </indexterm> - The <filename>pg_hba.conf</filename> file is read on start-up - and when the <application>postmaster</> receives a + The <filename>pg_hba.conf</filename> file is read on start-up and when + the <application>postmaster</> receives a <systemitem>SIGHUP</systemitem> signal. If you edit the file on an active system, you will need to signal the <application>postmaster</> - (using <literal>pg_ctl reload</> or <literal>kill -HUP</>) - to make it re-read the file. + (using <literal>pg_ctl reload</> or <literal>kill -HUP</>) to make it + re-read the file. </para> <para> @@ -382,27 +369,27 @@ hostssl <replaceable>database</replaceable> <replaceable>IP-address</replaceable <example id="example-pg-hba.conf"> <title>An example <filename>pg_hba.conf</filename> file</title> <programlisting> -# TYPE DATABASE IP_ADDRESS MASK AUTHTYPE MAP +# TYPE DATABASE USER IP_ADDRESS MASK AUTHTYPE # Allow any user on the local system to connect to any -# database under any username, but only via an IP connection: +# database under any user name, but only via an IP connection: -host all 127.0.0.1 255.255.255.255 trust +host all all 127.0.0.1 255.255.255.255 trust # The same, over Unix-socket connections: -local all trust +local all all trust # Allow any user from any host with IP address 192.168.93.x to -# connect to database "template1" as the same username that ident on that -# host identifies him as (typically his Unix username): +# connect to database "template1" as the same user name that ident on that +# host identifies him as (typically his Unix user name): -host template1 192.168.93.0 255.255.255.0 ident sameuser +host template1 all 192.168.93.0 255.255.255.0 ident sameuser # Allow a user from host 192.168.12.10 to connect to database "template1" -# if the user's password in pg_shadow is correctly supplied: +# if the user's password is correctly supplied: -host template1 192.168.12.10 255.255.255.255 md5 +host template1 all 192.168.12.10 255.255.255.255 md5 # In the absence of preceding "host" lines, these two lines will reject # all connection attempts from 192.168.54.1 (since that entry will be @@ -410,8 +397,8 @@ host template1 192.168.12.10 255.255.255.255 md5 # else on the Internet. The zero mask means that no bits of the host IP # address are considered, so it matches any host: -host all 192.168.54.1 255.255.255.255 reject -host all 0.0.0.0 0.0.0.0 krb5 +host all all 192.168.54.1 255.255.255.255 reject +host all all 0.0.0.0 0.0.0.0 krb5 # Allow users from 192.168.x.x hosts to connect to any database, if they # pass the ident check. If, for example, ident says the user is "bryanh" @@ -419,7 +406,7 @@ host all 0.0.0.0 0.0.0.0 krb5 # is allowed if there is an entry in pg_ident.conf for map "omicron" that # says "bryanh" is allowed to connect as "guest1": -host all 192.168.0.0 255.255.0.0 ident omicron +host all all 192.168.0.0 255.255.0.0 ident omicron # If these are the only two lines for local connections, they will allow # local users to connect only to their own databases (database named the @@ -429,8 +416,8 @@ host all 192.168.0.0 255.255.0.0 ident omicron # cases. (If you prefer to use ident authorization, an ident map can # serve a parallel purpose to the password list file used here.) -local sameuser md5 -local all md5 admins +local sameuser all md5 +local all @admins md5 </programlisting> </example> </para> @@ -490,86 +477,49 @@ local all md5 admins <title>Password authentication</title> <indexterm> - <primary>password</primary> + <primary>MD5</> </indexterm> <indexterm> - <primary>MD5</> + <primary>crypt</> + </indexterm> + <indexterm> + <primary>password</primary> </indexterm> <para> Password-based authentication methods include <literal>md5</>, - <literal>crypt</>, and <literal>password</>. These methods operate + <literal>crypt</>, and <literal>password</>. These methods operate similarly except for the way that the password is sent across the - connection. If you are at all concerned about password <quote>sniffing</> - attacks then <literal>md5</> is preferred, with <literal>crypt</> a - second choice if you must support obsolete clients. Plain - <literal>password</> should especially be avoided for connections over - the open Internet (unless you use SSL, SSH, or other communications - security wrappers around the connection). + connection. If you are at all concerned about password + <quote>sniffing</> attacks then <literal>md5</> is preferred, with + <literal>crypt</> a second choice if you must support pre-7.2 + clients. Plain <literal>password</> should especially be avoided for + connections over the open Internet (unless you use SSL, SSH, or + other communications security wrappers around the connection). </para> <para> - <productname>PostgreSQL</productname> database passwords are separate from - operating system user passwords. Ordinarily, the password for each - database user is stored in the pg_shadow system catalog table. - Passwords can be managed with the query language commands - <command>CREATE USER</command> and <command>ALTER USER</command>, - e.g., <userinput>CREATE USER foo WITH PASSWORD - 'secret';</userinput>. By default, that is, if no password has - been set up, the stored password is <literal>NULL</literal> - and password authentication will always fail for that user. + <productname>PostgreSQL</productname> database passwords are + separate from operating system user passwords. Ordinarily, the + password for each database user is stored in the pg_shadow system + catalog table. Passwords can be managed with the query language + commands <command>CREATE USER</command> and <command>ALTER + USER</command>, e.g., <userinput>CREATE USER foo WITH PASSWORD + 'secret';</userinput>. By default, that is, if no password has been + set up, the stored password is <literal>NULL</literal> and password + authentication will always fail for that user. </para> <para> To restrict the set of users that are allowed to connect to certain - databases, list the set of users in a separate file (one user name - per line) in the same directory that <filename>pg_hba.conf</> is in, - and mention the (base) name of the file after the - <literal>password</>, <literal>md5</>, or <literal>crypt</> keyword, - respectively, in <filename>pg_hba.conf</>. If you do not use this - feature, then any user that is known to the database system can - connect to any database (so long as he supplies the correct password, - of course). - </para> - - <para> - These files can also be used to apply a different set of passwords - to a particular database or set thereof. In that case, the files - have a format similar to the standard Unix password file - <filename>/etc/passwd</filename>, that is, -<synopsis> -<replaceable>username</replaceable>:<replaceable>password</replaceable> -</synopsis> - Any extra colon-separated fields following the password are - ignored. The password is expected to be encrypted using the - system's <function>crypt()</function> function. The utility - program <application>pg_passwd</application> that is installed - with <productname>PostgreSQL</productname> can be used to manage - these password files. - </para> - - <para> - Lines with and without passwords can be mixed in secondary - password files. Lines without password indicate use of the main - password in <literal>pg_shadow</> that is managed by - <command>CREATE USER</> and <command>ALTER USER</>. Lines with - passwords will cause that password to be used. A password entry of - <quote>+</quote> also means using the pg_shadow password. - </para> - - <para> - Alternative passwords cannot be used when using the <literal>md5</> - or <literal>crypt</> methods. The file will be read as - usual, but the password field will simply be ignored and the - <literal>pg_shadow</> password will always be used. - </para> - - <para> - Note that using alternative passwords like this means that one can - no longer use <command>ALTER USER</command> to change one's - password. It will appear to work but the password one is - changing is not the password that the system will end up - using. + databases, list the users separated by commas, or in a separate + file. The file should contain user names separated by commas or one + user name per line, and be in the same directory as + <filename>pg_hba.conf</>. Mention the (base) name of the file + preceded with <literal>@</>in the <literal>USER</> column. The + <literal>DATABASE</> column can similarly accept a list of values or + a file name. You can also specify group names by preceding the group + name with <literal>+</>. </para> </sect2> @@ -588,10 +538,10 @@ local all md5 admins <productname>Kerberos</productname> system is far beyond the scope of this document; in all generality it can be quite complex (yet powerful). The <ulink - url="http://www.nrl.navy.mil/CCS/people/kenh/kerberos-faq.html">Kerberos - <acronym>FAQ</></ulink> or <ulink - url="ftp://athena-dist.mit.edu">MIT Project Athena</ulink> can be - a good starting point for exploration. Several sources for + url="http://www.nrl.navy.mil/CCS/people/kenh/kerberos-faq.html">Kerb + eros <acronym>FAQ</></ulink> or <ulink + url="ftp://athena-dist.mit.edu">MIT Project Athena</ulink> can be a + good starting point for exploration. Several sources for <productname>Kerberos</> distributions exist. </para> @@ -606,34 +556,33 @@ local all md5 admins <para> <productname>PostgreSQL</> operates like a normal Kerberos service. The name of the service principal is - <replaceable>servicename/hostname@realm</>, where - <replaceable>servicename</> is <literal>postgres</literal> - (unless a different service name was selected at configure time - with <literal>./configure --with-krb-srvnam=whatever</>). - <replaceable>hostname</> is the fully qualified domain name of the server - machine. The service principal's realm is the preferred realm of the - server machine. + <replaceable>servicename/hostname@realm</>, where + <replaceable>servicename</> is <literal>postgres</literal> (unless a + different service name was selected at configure time with + <literal>./configure --with-krb-srvnam=whatever</>). + <replaceable>hostname</> is the fully qualified domain name of the + server machine. The service principal's realm is the preferred realm + of the server machine. </para> <para> - Client principals must have their <productname>PostgreSQL</> user name as - their first component, for example - <replaceable>pgusername/otherstuff@realm</>. - At present the realm of the client is not checked by - <productname>PostgreSQL</>; so - if you have cross-realm authentication enabled, then any principal - in any realm that can communicate with yours will be accepted. + Client principals must have their <productname>PostgreSQL</> user + name as their first component, for example + <replaceable>pgusername/otherstuff@realm</>. At present the realm of + the client is not checked by <productname>PostgreSQL</>; so if you + have cross-realm authentication enabled, then any principal in any + realm that can communicate with yours will be accepted. </para> <para> - Make sure that your server key file is readable (and - preferably only readable) by the - <productname>PostgreSQL</productname> server account (see - <xref linkend="postgres-user">). The location of the key file - is specified with the <varname>krb_server_keyfile</> run time - configuration parameter. (See also <xref linkend="runtime-config">.) - The default is <filename>/etc/srvtab</> if you are using Kerberos 4 - and <filename>FILE:/usr/local/pgsql/etc/krb5.keytab</> (or whichever + Make sure that your server key file is readable (and preferably only + readable) by the <productname>PostgreSQL</productname> server + account (see <xref linkend="postgres-user">). The location of the + key file is specified with the <varname>krb_server_keyfile</> run + time configuration parameter. (See also <xref + linkend="runtime-config">.) The default is <filename>/etc/srvtab</> + if you are using Kerberos 4 and + <filename>FILE:/usr/local/pgsql/etc/krb5.keytab</> (or whichever directory was specified as <varname>sysconfdir</> at build time) with Kerberos 5. </para> @@ -649,18 +598,20 @@ local all md5 admins <para> When connecting to the database make sure you have a ticket for a - principal matching the requested database user name. - An example: For database user name <literal>fred</>, both principal + principal matching the requested database user name. An example: For + database user name <literal>fred</>, both principal <literal>fred@EXAMPLE.COM</> and - <literal>fred/users.example.com@EXAMPLE.COM</> can be - used to authenticate to the database server. + <literal>fred/users.example.com@EXAMPLE.COM</> can be used to + authenticate to the database server. </para> <para> - If you use <application>mod_auth_krb</application> and <application>mod_perl</application> on your <productname>Apache</productname> web server, - you can use <literal>AuthType KerberosV5SaveCredentials</literal> with a <application>mod_perl</application> - script. This gives secure database access over the web, no extra - passwords required. + If you use <application>mod_auth_krb</application> and + <application>mod_perl</application> on your + <productname>Apache</productname> web server, you can use + <literal>AuthType KerberosV5SaveCredentials</literal> with a + <application>mod_perl</application> script. This gives secure + database access over the web, no extra passwords required. </para> </sect2> @@ -707,55 +658,54 @@ local all md5 admins </para> <para> - On systems supporting <symbol>SO_PEERCRED</symbol> requests for Unix-domain sockets, - ident authentication can also be applied to local connections. In this - case, no security risk is added by using ident authentication; indeed - it is a preferable choice for local connections on such a system. + On systems supporting <symbol>SO_PEERCRED</symbol> requests for + Unix-domain sockets, ident authentication can also be applied to + local connections. In this case, no security risk is added by using + ident authentication; indeed it is a preferable choice for local + connections on such systems. </para> <para> When using ident-based authentication, after having determined the name of the operating system user that initiated the connection, - <productname>PostgreSQL</productname> checks whether that user is allowed - to connect as the database user he is requesting to connect as. - This is controlled by the ident map - argument that follows the <literal>ident</> keyword in the - <filename>pg_hba.conf</filename> file. There is a predefined ident map - <literal>sameuser</literal>, which allows any operating system - user to connect as the database user of the same name (if the - latter exists). Other maps must be created manually. + <productname>PostgreSQL</productname> checks whether that user is + allowed to connect as the database user he is requesting to connect + as. This is controlled by the ident map argument that follows the + <literal>ident</> keyword in the <filename>pg_hba.conf</filename> + file. There is a predefined ident map <literal>sameuser</literal>, + which allows any operating system user to connect as the database + user of the same name (if the latter exists). Other maps must be + created manually. </para> <para> - <indexterm><primary>pg_ident.conf</primary></indexterm> - Ident maps other than <literal>sameuser</literal> are defined - in the file <filename>pg_ident.conf</filename> - in the data directory, which contains lines of the general form: + <indexterm><primary>pg_ident.conf</primary></indexterm> Ident maps + other than <literal>sameuser</literal> are defined in the file + <filename>pg_ident.conf</filename> in the data directory, which + contains lines of the general form: <synopsis> <replaceable>map-name</> <replaceable>ident-username</> <replaceable>database-username</> </synopsis> - Comments and whitespace are handled in the usual way. - The <replaceable>map-name</> is an arbitrary name that will be - used to refer to this mapping in <filename>pg_hba.conf</filename>. - The other two fields specify which operating system user is - allowed to connect as which database user. The same - <replaceable>map-name</> can be used repeatedly to specify more - user-mappings within a single map. There is no restriction regarding - how many - database users a given operating system user may correspond to and vice - versa. + Comments and whitespace are handled in the usual way. The + <replaceable>map-name</> is an arbitrary name that will be used to + refer to this mapping in <filename>pg_hba.conf</filename>. The other + two fields specify which operating system user is allowed to connect + as which database user. The same <replaceable>map-name</> can be + used repeatedly to specify more user-mappings within a single map. + There is no restriction regarding how many database users a given + operating system user may correspond to and vice versa. </para> <para> <indexterm> <primary>SIGHUP</primary> </indexterm> - The <filename>pg_ident.conf</filename> file is read on start-up - and when the <application>postmaster</> receives a + The <filename>pg_ident.conf</filename> file is read on start-up and + when the <application>postmaster</> receives a <systemitem>SIGHUP</systemitem> signal. If you edit the file on an active system, you will need to signal the <application>postmaster</> - (using <literal>pg_ctl reload</> or <literal>kill -HUP</>) - to make it re-read the file. + (using <literal>pg_ctl reload</> or <literal>kill -HUP</>) to make it + re-read the file. </para> <para> @@ -763,13 +713,14 @@ local all md5 admins conjunction with the <filename>pg_hba.conf</> file in <xref linkend="example-pg-hba.conf"> is shown in <xref linkend="example-pg-ident.conf">. In this example setup, anyone - logged in to a machine on the 192.168 network that does not have - the Unix user name <systemitem>bryanh</>, <systemitem>ann</>, or <systemitem>robert</> would not be granted access. - Unix user <systemitem>robert</> would only be allowed access when he tries to - connect as <productname>PostgreSQL</> user <systemitem>bob</>, - not as <systemitem>robert</> - or anyone else. <systemitem>ann</> would only be allowed to connect as - <systemitem>ann</>. User <systemitem>bryanh</> would be allowed to connect as either + logged in to a machine on the 192.168 network that does not have the + Unix user name <systemitem>bryanh</>, <systemitem>ann</>, or + <systemitem>robert</> would not be granted access. Unix user + <systemitem>robert</> would only be allowed access when he tries to + connect as <productname>PostgreSQL</> user <systemitem>bob</>, not + as <systemitem>robert</> or anyone else. <systemitem>ann</> would + only be allowed to connect as <systemitem>ann</>. User + <systemitem>bryanh</> would be allowed to connect as either <systemitem>bryanh</> himself or as <systemitem>guest1</>. </para> @@ -780,7 +731,7 @@ local all md5 admins omicron bryanh bryanh omicron ann ann -# bob has username robert on these machines +# bob has user name robert on these machines omicron robert bob # bryanh can also connect as guest1 omicron bryanh guest1 @@ -799,30 +750,30 @@ omicron bryanh guest1 <para> <ProgramListing> -No pg_hba.conf entry for host 123.123.123.123, user joeblow, database testdb +No pg_hba.conf entry for host 123.123.123.123, user andym, database testdb </ProgramListing> - This is what you are most likely to get if you succeed in - contacting the server, but it does not want to talk to you. As the - message suggests, the server refused the connection request - because it found no authorizing entry in its <filename>pg_hba.conf</filename> + This is what you are most likely to get if you succeed in contacting + the server, but it does not want to talk to you. As the message + suggests, the server refused the connection request because it found + no authorizing entry in its <filename>pg_hba.conf</filename> configuration file. </para> <para> <ProgramListing> -Password authentication failed for user 'joeblow' +Password authentication failed for user 'andym' </ProgramListing> - Messages like this indicate that you contacted the server, and - it is willing to talk to you, but not until you pass the - authorization method specified in the - <filename>pg_hba.conf</filename> file. Check the password you are - providing, or check your Kerberos or ident software if the - complaint mentions one of those authentication types. + Messages like this indicate that you contacted the server, and it is + willing to talk to you, but not until you pass the authorization + method specified in the <filename>pg_hba.conf</filename> file. Check + the password you are providing, or check your Kerberos or ident + software if the complaint mentions one of those authentication + types. </para> <para> <ProgramListing> -FATAL 1: user "joeblow" does not exist +FATAL 1: user "andym" does not exist </ProgramListing> The indicated user name was not found. </para> @@ -837,9 +788,9 @@ FATAL 1: Database "testdb" does not exist in the system catalog. </para> <para> - Note that the server log may contain more information - about an authentication failure than is reported to the client. - If you are confused about the reason for a failure, check the log. + Note that the server log may contain more information about an + authentication failure than is reported to the client. If you are + confused about the reason for a failure, check the log. </para> </sect1> diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml index 460f150bc89..61d979a5643 100644 --- a/doc/src/sgml/ref/allfiles.sgml +++ b/doc/src/sgml/ref/allfiles.sgml @@ -1,5 +1,5 @@ <!-- -$Header: /cvsroot/pgsql/doc/src/sgml/ref/allfiles.sgml,v 1.36 2002/03/19 02:18:12 momjian Exp $ +$Header: /cvsroot/pgsql/doc/src/sgml/ref/allfiles.sgml,v 1.37 2002/04/04 04:25:45 momjian Exp $ PostgreSQL documentation Complete list of usable sgml source files in this directory. --> @@ -125,7 +125,6 @@ Complete list of usable sgml source files in this directory. <!entity pgCtl system "pg_ctl-ref.sgml"> <!entity pgDump system "pg_dump.sgml"> <!entity pgDumpall system "pg_dumpall.sgml"> -<!entity pgPasswd system "pg_passwd.sgml"> <!entity pgRestore system "pg_restore.sgml"> <!entity pgTclSh system "pgtclsh.sgml"> <!entity pgTkSh system "pgtksh.sgml"> diff --git a/doc/src/sgml/ref/pg_passwd.sgml b/doc/src/sgml/ref/pg_passwd.sgml deleted file mode 100644 index 13125b08e27..00000000000 --- a/doc/src/sgml/ref/pg_passwd.sgml +++ /dev/null @@ -1,123 +0,0 @@ -<!-- -$Header: /cvsroot/pgsql/doc/src/sgml/ref/Attic/pg_passwd.sgml,v 1.10 2001/12/08 03:24:38 thomas Exp $ -PostgreSQL documentation ---> - -<refentry id="APP-PG-PASSWD"> - <docinfo> - <date>2000-11-18</date> - </docinfo> - - <refmeta> - <refentrytitle id="APP-PG-PASSWD-TITLE"><application>pg_passwd</application></refentrytitle> - <manvolnum>1</manvolnum> - <refmiscinfo>Application</refmiscinfo> - </refmeta> - - <refnamediv> - <refname>pg_passwd</refname> - <refpurpose>change a secondary <productname>PostgreSQL</> password file</refpurpose> - </refnamediv> - - <refsynopsisdiv> - <cmdsynopsis> - <command>pg_passwd</command> - <arg choice="plain"><replaceable>filename</replaceable></arg> - </cmdsynopsis> - </refsynopsisdiv> - - <refsect1 id="app-pg-passwd-description"> - <title>Description</title> - <para> - <application>pg_passwd</application> is a tool for manipulating flat - text password files. These files can control client authentication of - the <productname>PostgreSQL</productname> server. More information - about setting up this authentication mechanism can be found in the - <citetitle>Administrator's Guide</citetitle>. - </para> - - <para> - The format of a text password file is one entry per line; the fields - of each entry are separated by colons. The first field is the user - name, the second field is the encrypted password. Other fields are - ignored (to allow password files to be shared between applications - that use similar formats). <application>pg_passwd</application> - enables users to interactively add entries to such a file, to alter - passwords of existing entries, and to encrypt such passwords. - </para> - - <para> - Supply the name of the password file as argument to the - <application>pg_passwd</application> command. To be used by - PostgreSQL, the file needs to be located in the server's data - directory, and the base name of the file needs to be specified in the - <filename>pg_hba.conf</filename> access control file. - -<screen> -<prompt>$</prompt> <userinput>pg_passwd /usr/local/pgsql/data/passwords</userinput> -<computeroutput>File "/usr/local/pgsql/data/passwords" does not exist. Create? (y/n):</computeroutput> <userinput>y</userinput> -<prompt>Username:</prompt> <userinput>guest</userinput> -<prompt>Password:</prompt> -<prompt>Re-enter password:</prompt> -</screen> - - where the <literal>Password:</literal> and <literal>Re-enter - password:</literal> prompts require the same password input which - is not displayed on the terminal. Note that the password is limited - to eight useful characters by restrictions of the standard crypt(3) - library routine. - </para> - - <para> - The original password file is renamed to - <filename>passwords.bk</filename>. - </para> - - <para> - To make use of this password file, put a line like the following in - <filename>pg_hba.conf</filename>: - -<programlisting> -host mydb 133.65.96.250 255.255.255.255 password passwords -</programlisting> - - which would allow access to database mydb from host 133.65.96.250 using - the passwords listed in the <filename>passwords</filename> file (and - only to the users listed in that file). - </para> - - <note> - <para> - It is also useful to have entries in a password file with empty - password fields. (This is different from an empty password.) Such - entries allow you to restrict users who can access the system. These - entries cannot be managed by <application>pg_passwd</application>, - but you can edit password files manually. - </para> - </note> - </refsect1> - - <refsect1 id="app-pg-passwd-seealso"> - <title>See also</title> - <para> - <citetitle>PostgreSQL Administrator's Guide</citetitle> - </para> - </refsect1> -</refentry> - -<!-- Keep this comment at the end of the file -Local variables: -mode: sgml -sgml-omittag:nil -sgml-shorttag:t -sgml-minimize-attributes:nil -sgml-always-quote-attributes:t -sgml-indent-step:1 -sgml-indent-data:t -sgml-parent-document:nil -sgml-default-dtd-file:"../reference.ced" -sgml-exposed-tags:nil -sgml-local-catalogs:"/usr/lib/sgml/catalog" -sgml-local-ecat-files:nil -End: ---> diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml index b6b63254462..41d419dd2ea 100644 --- a/doc/src/sgml/reference.sgml +++ b/doc/src/sgml/reference.sgml @@ -1,5 +1,5 @@ <!-- reference.sgml -$Header: /cvsroot/pgsql/doc/src/sgml/reference.sgml,v 1.24 2002/03/19 02:18:11 momjian Exp $ +$Header: /cvsroot/pgsql/doc/src/sgml/reference.sgml,v 1.25 2002/04/04 04:25:44 momjian Exp $ PostgreSQL Reference Manual --> @@ -191,7 +191,6 @@ Disable this chapter until we have more functions documented. &initlocation; &ipcclean; &pgCtl; - &pgPasswd; &postgres; &postmaster; diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c index 98ee4308975..9ebd6fa3a2d 100644 --- a/src/backend/commands/user.c +++ b/src/backend/commands/user.c @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Header: /cvsroot/pgsql/src/backend/commands/user.c,v 1.94 2002/03/26 19:15:48 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/user.c,v 1.95 2002/04/04 04:25:45 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -15,6 +15,7 @@ #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> +#include <errno.h> #include <unistd.h> #include "access/heapam.h" @@ -27,6 +28,7 @@ #include "libpq/crypt.h" #include "miscadmin.h" #include "storage/pmsignal.h" +#include "utils/acl.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/fmgroids.h" @@ -39,8 +41,205 @@ extern bool Password_encryption; static void CheckPgUserAclNotNull(void); -/*--------------------------------------------------------------------- - * write_password_file / update_pg_pwd + + +/* + * fputs_quote + * + * Outputs string in quotes, with double-quotes duplicated. + * We could use quote_ident(), but that expects varlena. + */ +static void fputs_quote(char *str, FILE *fp) +{ + fputc('"', fp); + while (*str) + { + fputc(*str, fp); + if (*str == '"') + fputc('"', fp); + str++; + } + fputc('"', fp); +} + + + +/* + * group_getfilename --- get full pathname of group file + * + * Note that result string is palloc'd, and should be freed by the caller. + */ +char * +group_getfilename(void) +{ + int bufsize; + char *pfnam; + + bufsize = strlen(DataDir) + strlen("/global/") + + strlen(USER_GROUP_FILE) + 1; + pfnam = (char *) palloc(bufsize); + snprintf(pfnam, bufsize, "%s/global/%s", DataDir, USER_GROUP_FILE); + + return pfnam; +} + + + +/* + * Get full pathname of password file. + * Note that result string is palloc'd, and should be freed by the caller. + */ +char * +user_getfilename(void) +{ + int bufsize; + char *pfnam; + + bufsize = strlen(DataDir) + strlen("/global/") + + strlen(PWD_FILE) + 1; + pfnam = (char *) palloc(bufsize); + snprintf(pfnam, bufsize, "%s/global/%s", DataDir, PWD_FILE); + + return pfnam; +} + + + +/* + * write_group_file for trigger update_pg_pwd_and_pg_group + */ +static void +write_group_file(Relation urel, Relation grel) +{ + char *filename, + *tempname; + int bufsize; + FILE *fp; + mode_t oumask; + HeapScanDesc scan; + HeapTuple tuple; + TupleDesc dsc = RelationGetDescr(grel); + + /* + * Create a temporary filename to be renamed later. This prevents the + * backend from clobbering the pg_group file while the postmaster might + * be reading from it. + */ + filename = group_getfilename(); + bufsize = strlen(filename) + 12; + tempname = (char *) palloc(bufsize); + + snprintf(tempname, bufsize, "%s.%d", filename, MyProcPid); + oumask = umask((mode_t) 077); + fp = AllocateFile(tempname, "w"); + umask(oumask); + if (fp == NULL) + elog(ERROR, "write_group_file: unable to write %s: %m", tempname); + + /* read table */ + scan = heap_beginscan(grel, false, SnapshotSelf, 0, NULL); + while (HeapTupleIsValid(tuple = heap_getnext(scan, 0))) + { + Datum datum, grolist_datum; + bool isnull; + char *groname; + IdList *grolist_p; + AclId *aidp; + int i, j, + num; + char *usename; + bool first_user = true; + + datum = heap_getattr(tuple, Anum_pg_group_groname, dsc, &isnull); + if (isnull) + continue; /* ignore NULL groupnames */ + groname = (char *) DatumGetName(datum); + + grolist_datum = heap_getattr(tuple, Anum_pg_group_grolist, dsc, &isnull); + /* Ignore NULL group lists */ + if (isnull) + continue; + + grolist_p = DatumGetIdListP(grolist_datum); + /* + * Check for illegal characters in the group name. + */ + i = strcspn(groname, "\n"); + if (groname[i] != '\0') + { + elog(LOG, "Invalid group name '%s'", groname); + continue; + } + + /* be sure the IdList is not toasted */ + /* scan it */ + num = IDLIST_NUM(grolist_p); + aidp = IDLIST_DAT(grolist_p); + for (i = 0; i < num; ++i) + { + tuple = SearchSysCache(SHADOWSYSID, + PointerGetDatum(aidp[i]), + 0, 0, 0); + if (HeapTupleIsValid(tuple)) + { + usename = NameStr(((Form_pg_shadow) GETSTRUCT(tuple))->usename); + + /* + * Check for illegal characters in the user name. + */ + j = strcspn(usename, "\n"); + if (usename[j] != '\0') + { + elog(LOG, "Invalid user name '%s'", usename); + continue; + } + + /* File format is: + * "dbname" "user1","user2","user3" + * This matches pg_hba.conf. + */ + if (first_user) + { + fputs_quote(groname, fp); + fputs("\t", fp); + } + else + fputs(" ", fp); + + first_user = false; + fputs_quote(usename, fp); + + ReleaseSysCache(tuple); + } + } + if (!first_user) + fputs("\n", fp); + /* if IdList was toasted, free detoasted copy */ + if ((Pointer) grolist_p != DatumGetPointer(grolist_datum)) + pfree(grolist_p); + } + heap_endscan(scan); + + fflush(fp); + if (ferror(fp)) + elog(ERROR, "%s: %m", tempname); + FreeFile(fp); + + /* + * Rename the temp file to its final name, deleting the old pg_pwd. We + * expect that rename(2) is an atomic action. + */ + if (rename(tempname, filename)) + elog(ERROR, "rename %s to %s: %m", tempname, filename); + + pfree((void *) tempname); + pfree((void *) filename); +} + + + +/* + * write_password_file for trigger update_pg_pwd_and_pg_group * * copy the modified contents of pg_shadow to a file used by the postmaster * for user authentication. The file is stored as $PGDATA/global/pg_pwd. @@ -51,10 +250,9 @@ static void CheckPgUserAclNotNull(void); * We raise an error to force transaction rollback if we detect an illegal * username or password --- illegal being defined as values that would * mess up the pg_pwd parser. - *--------------------------------------------------------------------- */ static void -write_password_file(Relation rel) +write_user_file(Relation urel) { char *filename, *tempname; @@ -63,14 +261,14 @@ write_password_file(Relation rel) mode_t oumask; HeapScanDesc scan; HeapTuple tuple; - TupleDesc dsc = RelationGetDescr(rel); + TupleDesc dsc = RelationGetDescr(urel); /* * Create a temporary filename to be renamed later. This prevents the * backend from clobbering the pg_pwd file while the postmaster might * be reading from it. */ - filename = crypt_getpwdfilename(); + filename = user_getfilename(); bufsize = strlen(filename) + 12; tempname = (char *) palloc(bufsize); @@ -82,26 +280,22 @@ write_password_file(Relation rel) elog(ERROR, "write_password_file: unable to write %s: %m", tempname); /* read table */ - scan = heap_beginscan(rel, false, SnapshotSelf, 0, NULL); + scan = heap_beginscan(urel, false, SnapshotSelf, 0, NULL); while (HeapTupleIsValid(tuple = heap_getnext(scan, 0))) { - Datum datum_n, - datum_p, - datum_v; - bool null_n, - null_p, - null_v; - char *str_n, - *str_p, - *str_v; + Datum datum; + bool isnull; + char *usename, + *passwd, + *valuntil; int i; - datum_n = heap_getattr(tuple, Anum_pg_shadow_usename, dsc, &null_n); - if (null_n) + datum = heap_getattr(tuple, Anum_pg_shadow_usename, dsc, &isnull); + if (isnull) continue; /* ignore NULL usernames */ - str_n = DatumGetCString(DirectFunctionCall1(nameout, datum_n)); + usename = (char *) DatumGetName(datum); - datum_p = heap_getattr(tuple, Anum_pg_shadow_passwd, dsc, &null_p); + datum = heap_getattr(tuple, Anum_pg_shadow_passwd, dsc, &isnull); /* * It can be argued that people having a null password shouldn't @@ -110,57 +304,40 @@ write_password_file(Relation rel) * assuming an empty password in that case is better, change this * logic to look something like the code for valuntil. */ - if (null_p) - { - pfree(str_n); + if (isnull) continue; - } - str_p = DatumGetCString(DirectFunctionCall1(textout, datum_p)); - datum_v = heap_getattr(tuple, Anum_pg_shadow_valuntil, dsc, &null_v); - if (null_v) - str_v = pstrdup("\\N"); + passwd = DatumGetCString(DirectFunctionCall1(textout, datum)); + + datum = heap_getattr(tuple, Anum_pg_shadow_valuntil, dsc, &isnull); + if (isnull) + valuntil = pstrdup(""); else - str_v = DatumGetCString(DirectFunctionCall1(nabstimeout, datum_v)); + valuntil = DatumGetCString(DirectFunctionCall1(nabstimeout, datum)); /* * Check for illegal characters in the username and password. */ - i = strcspn(str_n, CRYPT_PWD_FILE_SEPSTR "\n"); - if (str_n[i] != '\0') - elog(ERROR, "Invalid user name '%s'", str_n); - i = strcspn(str_p, CRYPT_PWD_FILE_SEPSTR "\n"); - if (str_p[i] != '\0') - elog(ERROR, "Invalid user password '%s'", str_p); + i = strcspn(usename, "\n"); + if (usename[i] != '\0') + elog(ERROR, "Invalid user name '%s'", usename); + i = strcspn(passwd, "\n"); + if (passwd[i] != '\0') + elog(ERROR, "Invalid user password '%s'", passwd); /* * The extra columns we emit here are not really necessary. To * remove them, the parser in backend/libpq/crypt.c would need to * be adjusted. */ - fprintf(fp, - "%s" - CRYPT_PWD_FILE_SEPSTR - "0" - CRYPT_PWD_FILE_SEPSTR - "x" - CRYPT_PWD_FILE_SEPSTR - "x" - CRYPT_PWD_FILE_SEPSTR - "x" - CRYPT_PWD_FILE_SEPSTR - "x" - CRYPT_PWD_FILE_SEPSTR - "%s" - CRYPT_PWD_FILE_SEPSTR - "%s\n", - str_n, - str_p, - str_v); - - pfree(str_n); - pfree(str_p); - pfree(str_v); + fputs_quote(usename, fp); + fputs(" ", fp); + fputs_quote(passwd, fp); + fputs(" ", fp); + fputs_quote(valuntil, fp); + + pfree(passwd); + pfree(valuntil); } heap_endscan(scan); @@ -178,29 +355,33 @@ write_password_file(Relation rel) pfree((void *) tempname); pfree((void *) filename); - - /* - * Signal the postmaster to reload its password-file cache. - */ - SendPostmasterSignal(PMSIGNAL_PASSWORD_CHANGE); } /* This is the wrapper for triggers. */ Datum -update_pg_pwd(PG_FUNCTION_ARGS) +update_pg_pwd_and_pg_group(PG_FUNCTION_ARGS) { /* * ExclusiveLock ensures no one modifies pg_shadow while we read it, * and that only one backend rewrites the flat file at a time. It's * OK to allow normal reads of pg_shadow in parallel, however. */ - Relation rel = heap_openr(ShadowRelationName, ExclusiveLock); + Relation urel = heap_openr(ShadowRelationName, ExclusiveLock); + Relation grel = heap_openr(GroupRelationName, ExclusiveLock); - write_password_file(rel); + write_user_file(urel); + write_group_file(urel, grel); /* OK to release lock, since we did not modify the relation */ - heap_close(rel, ExclusiveLock); + heap_close(grel, ExclusiveLock); + heap_close(urel, ExclusiveLock); + + /* + * Signal the postmaster to reload its password & group-file cache. + */ + SendPostmasterSignal(PMSIGNAL_PASSWORD_CHANGE); + return PointerGetDatum(NULL); } @@ -446,14 +627,14 @@ CreateUser(CreateUserStmt *stmt) } /* - * Write the updated pg_shadow data to the flat password file. + * Now we can clean up; but keep lock until commit. */ - write_password_file(pg_shadow_rel); + heap_close(pg_shadow_rel, NoLock); /* - * Now we can clean up; but keep lock until commit. + * Write the updated pg_shadow and pg_group data to the flat file. */ - heap_close(pg_shadow_rel, NoLock); + update_pg_pwd_and_pg_group(NULL); } @@ -680,14 +861,14 @@ AlterUser(AlterUserStmt *stmt) heap_freetuple(new_tuple); /* - * Write the updated pg_shadow data to the flat password file. + * Now we can clean up. */ - write_password_file(pg_shadow_rel); + heap_close(pg_shadow_rel, NoLock); /* - * Now we can clean up. + * Write the updated pg_shadow and pg_group data to the flat file. */ - heap_close(pg_shadow_rel, NoLock); + update_pg_pwd_and_pg_group(NULL); } @@ -733,7 +914,7 @@ AlterUserSet(AlterUserSetStmt *stmt) { Datum datum; bool isnull; - ArrayType *a; + ArrayType *array; repl_null[Anum_pg_shadow_useconfig-1] = ' '; @@ -741,17 +922,17 @@ AlterUserSet(AlterUserSetStmt *stmt) Anum_pg_shadow_useconfig, &isnull); if (valuestr) - a = GUCArrayAdd(isnull + array = GUCArrayAdd(isnull ? NULL : (ArrayType *) pg_detoast_datum((struct varlena *)datum), stmt->variable, valuestr); else - a = GUCArrayDelete(isnull + array = GUCArrayDelete(isnull ? NULL : (ArrayType *) pg_detoast_datum((struct varlena *)datum), stmt->variable); - repl_val[Anum_pg_shadow_useconfig-1] = PointerGetDatum(a); + repl_val[Anum_pg_shadow_useconfig-1] = PointerGetDatum(array); } newtuple = heap_modifytuple(oldtuple, rel, repl_val, repl_null, repl_repl); @@ -846,7 +1027,7 @@ DropUser(DropUserStmt *stmt) datum = heap_getattr(tmp_tuple, Anum_pg_database_datname, pg_dsc, &null); Assert(!null); - dbname = DatumGetCString(DirectFunctionCall1(nameout, datum)); + dbname = (char *) DatumGetName(datum); elog(ERROR, "DROP USER: user \"%s\" owns database \"%s\", cannot be removed%s", user, dbname, (length(stmt->users) > 1) ? " (no users removed)" : ""); @@ -901,14 +1082,14 @@ DropUser(DropUserStmt *stmt) } /* - * Write the updated pg_shadow data to the flat password file. + * Now we can clean up. */ - write_password_file(pg_shadow_rel); + heap_close(pg_shadow_rel, NoLock); /* - * Now we can clean up. + * Write the updated pg_shadow and pg_group data to the flat file. */ - heap_close(pg_shadow_rel, NoLock); + update_pg_pwd_and_pg_group(NULL); } @@ -1111,6 +1292,11 @@ CreateGroup(CreateGroupStmt *stmt) } heap_close(pg_group_rel, NoLock); + + /* + * Write the updated pg_shadow and pg_group data to the flat file. + */ + update_pg_pwd_and_pg_group(NULL); } @@ -1366,7 +1552,15 @@ AlterGroup(AlterGroupStmt *stmt, const char *tag) ReleaseSysCache(group_tuple); + /* + * Write the updated pg_shadow and pg_group data to the flat files. + */ heap_close(pg_group_rel, NoLock); + + /* + * Write the updated pg_shadow and pg_group data to the flat file. + */ + update_pg_pwd_and_pg_group(NULL); } @@ -1419,4 +1613,9 @@ DropGroup(DropGroupStmt *stmt) elog(ERROR, "DROP GROUP: group \"%s\" does not exist", stmt->name); heap_close(pg_group_rel, NoLock); + + /* + * Write the updated pg_shadow and pg_group data to the flat file. + */ + update_pg_pwd_and_pg_group(NULL); } diff --git a/src/backend/libpq/Makefile b/src/backend/libpq/Makefile index f5c2339f1c3..d0f05e802d9 100644 --- a/src/backend/libpq/Makefile +++ b/src/backend/libpq/Makefile @@ -4,7 +4,7 @@ # Makefile for libpq subsystem (backend half of libpq interface) # # IDENTIFICATION -# $Header: /cvsroot/pgsql/src/backend/libpq/Makefile,v 1.29 2002/03/04 01:46:02 tgl Exp $ +# $Header: /cvsroot/pgsql/src/backend/libpq/Makefile,v 1.30 2002/04/04 04:25:46 momjian Exp $ # #------------------------------------------------------------------------- @@ -14,9 +14,7 @@ include $(top_builddir)/src/Makefile.global # be-fsstubs is here for historical reasons, probably belongs elsewhere -OBJS = be-fsstubs.o \ - auth.o crypt.o hba.o md5.o password.o \ - pqcomm.o pqformat.o pqsignal.o +OBJS = be-fsstubs.o auth.o crypt.o hba.o md5.o pqcomm.o pqformat.o pqsignal.o all: SUBSYS.o diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c index 637e2a623eb..81a494905ea 100644 --- a/src/backend/libpq/auth.c +++ b/src/backend/libpq/auth.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.79 2002/03/05 07:57:45 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.80 2002/04/04 04:25:47 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -34,7 +34,6 @@ #include "miscadmin.h" static void sendAuthRequest(Port *port, AuthRequest areq); -static int checkPassword(Port *port, char *user, char *password); static int old_be_recvauth(Port *port); static int map_old_to_new(Port *port, UserAuth old, int status); static void auth_failed(Port *port, int status); @@ -381,7 +380,7 @@ recv_and_check_passwordv0(Port *port) saved = port->auth_method; port->auth_method = uaPassword; - status = checkPassword(port, user, password); + status = md5_crypt_verify(port, user, password); port->auth_method = saved; @@ -663,7 +662,7 @@ pam_passwd_conv_proc(int num_msg, const struct pam_message ** msg, struct pam_re initStringInfo(&buf); pq_getstr(&buf); - + /* Do not echo failed password to logs, for security. */ elog(DEBUG5, "received PAM packet"); @@ -810,27 +809,14 @@ recv_and_check_password_packet(Port *port) /* Do not echo failed password to logs, for security. */ elog(DEBUG5, "received password packet"); - result = checkPassword(port, port->user, buf.data); + result = md5_crypt_verify(port, port->user, buf.data); + pfree(buf.data); return result; } /* - * Handle `password' and `crypt' records. If an auth argument was - * specified, use the respective file. Else use pg_shadow passwords. - */ -static int -checkPassword(Port *port, char *user, char *password) -{ - if (port->auth_arg[0] != '\0') - return verify_password(port, user, password); - - return md5_crypt_verify(port, user, password); -} - - -/* * Server demux routine for incoming authentication information for protocol * version 0. */ diff --git a/src/backend/libpq/crypt.c b/src/backend/libpq/crypt.c index 6ab63ddffe0..7c665300c85 100644 --- a/src/backend/libpq/crypt.c +++ b/src/backend/libpq/crypt.c @@ -9,13 +9,12 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Header: /cvsroot/pgsql/src/backend/libpq/crypt.c,v 1.44 2002/03/04 01:46:03 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/libpq/crypt.c,v 1.45 2002/04/04 04:25:47 momjian Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" -#include <errno.h> #include <unistd.h> #ifdef HAVE_CRYPT_H #include <crypt.h> @@ -25,231 +24,10 @@ #include "libpq/libpq.h" #include "miscadmin.h" #include "storage/fd.h" +#include "nodes/pg_list.h" #include "utils/nabstime.h" -#define CRYPT_PWD_FILE "pg_pwd" - - -static char **pwd_cache = NULL; -static int pwd_cache_count = 0; - -/* - * crypt_getpwdfilename --- get full pathname of password file - * - * Note that result string is palloc'd, and should be freed by the caller. - */ -char * -crypt_getpwdfilename(void) -{ - int bufsize; - char *pfnam; - - bufsize = strlen(DataDir) + 8 + strlen(CRYPT_PWD_FILE) + 1; - pfnam = (char *) palloc(bufsize); - snprintf(pfnam, bufsize, "%s/global/%s", DataDir, CRYPT_PWD_FILE); - - return pfnam; -} - -/* - * Open the password file if possible (return NULL if not) - */ -static FILE * -crypt_openpwdfile(void) -{ - char *filename; - FILE *pwdfile; - - filename = crypt_getpwdfilename(); - pwdfile = AllocateFile(filename, "r"); - - if (pwdfile == NULL && errno != ENOENT) - elog(LOG, "could not open %s: %m", filename); - - pfree(filename); - - return pwdfile; -} - -/* - * Compare two password-file lines on the basis of their usernames. - * - * Can also be used to compare just a username against a password-file - * line (for bsearch). - */ -static int -compar_user(const void *user_a, const void *user_b) -{ - char *login_a; - char *login_b; - int len_a, - len_b, - result; - - login_a = *((char **) user_a); - login_b = *((char **) user_b); - - /* - * We only really want to compare the user logins which are first and - * are terminated by CRYPT_PWD_FILE_SEPSTR. (NB: this code - * effectively assumes that CRYPT_PWD_FILE_SEPSTR is just one char.) - */ - len_a = strcspn(login_a, CRYPT_PWD_FILE_SEPSTR); - len_b = strcspn(login_b, CRYPT_PWD_FILE_SEPSTR); - - result = strncmp(login_a, login_b, Min(len_a, len_b)); - - if (result == 0) /* one could be a prefix of the other */ - result = (len_a - len_b); - - return result; -} - -/* - * Load or reload the password-file cache - */ -void -load_password_cache(void) -{ - FILE *pwd_file; - char buffer[1024]; - - /* - * If for some reason we fail to open the password file, preserve the - * old cache contents; this seems better than dropping the cache if, - * say, we are temporarily out of filetable slots. - */ - if (!(pwd_file = crypt_openpwdfile())) - return; - - /* free any old data */ - if (pwd_cache) - { - while (--pwd_cache_count >= 0) - pfree(pwd_cache[pwd_cache_count]); - pfree(pwd_cache); - pwd_cache = NULL; - pwd_cache_count = 0; - } - - /* - * Read the file and store its lines in current memory context, which - * we expect will be PostmasterContext. That context will live as - * long as we need the cache to live, ie, until just after each - * postmaster child has completed client authentication. - */ - while (fgets(buffer, sizeof(buffer), pwd_file) != NULL) - { - int blen; - - /* - * We must remove the return char at the end of the string, as - * this will affect the correct parsing of the password entry. - */ - if (buffer[(blen = strlen(buffer) - 1)] == '\n') - buffer[blen] = '\0'; - - if (pwd_cache == NULL) - pwd_cache = (char **) - palloc(sizeof(char *) * (pwd_cache_count + 1)); - else - pwd_cache = (char **) - repalloc((void *) pwd_cache, - sizeof(char *) * (pwd_cache_count + 1)); - pwd_cache[pwd_cache_count++] = pstrdup(buffer); - } - - FreeFile(pwd_file); - - /* - * Now sort the entries in the cache for faster searching later. - */ - qsort((void *) pwd_cache, pwd_cache_count, sizeof(char *), compar_user); -} - -/* - * Parse a line of the password file to extract password and valid-until date. - */ -static bool -crypt_parsepwdentry(char *buffer, char **pwd, char **valdate) -{ - char *parse = buffer; - int count, - i; - - *pwd = NULL; - *valdate = NULL; - - /* - * skip to the password field - */ - for (i = 0; i < 6; i++) - { - parse += strcspn(parse, CRYPT_PWD_FILE_SEPSTR); - if (*parse == '\0') - return false; - parse++; - } - - /* - * store a copy of user password to return - */ - count = strcspn(parse, CRYPT_PWD_FILE_SEPSTR); - *pwd = (char *) palloc(count + 1); - memcpy(*pwd, parse, count); - (*pwd)[count] = '\0'; - parse += count; - if (*parse == '\0') - { - pfree(*pwd); - *pwd = NULL; - return false; - } - parse++; - - /* - * store a copy of the date login becomes invalid - */ - count = strcspn(parse, CRYPT_PWD_FILE_SEPSTR); - *valdate = (char *) palloc(count + 1); - memcpy(*valdate, parse, count); - (*valdate)[count] = '\0'; - - return true; -} - -/* - * Lookup a username in the password-file cache, - * return his password and valid-until date. - */ -static bool -crypt_getloginfo(const char *user, char **passwd, char **valuntil) -{ - *passwd = NULL; - *valuntil = NULL; - - if (pwd_cache) - { - char **pwd_entry; - - pwd_entry = (char **) bsearch((void *) &user, - (void *) pwd_cache, - pwd_cache_count, - sizeof(char *), - compar_user); - if (pwd_entry) - { - if (crypt_parsepwdentry(*pwd_entry, passwd, valuntil)) - return true; - } - } - - return false; -} - -/*-------------------------------------------------------------------------*/ - int md5_crypt_verify(const Port *port, const char *user, const char *pgpass) { @@ -257,10 +35,14 @@ md5_crypt_verify(const Port *port, const char *user, const char *pgpass) *valuntil, *crypt_pwd; int retval = STATUS_ERROR; + List **line; - if (!crypt_getloginfo(user, &passwd, &valuntil)) + if ((line = get_user_line(user)) == NULL) return STATUS_ERROR; + passwd = lfirst(lnext(lnext(*line))); + valuntil = lfirst(lnext(lnext(lnext(*line)))); + if (passwd == NULL || *passwd == '\0') { if (passwd) diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c index b86cb817302..fce63ab2436 100644 --- a/src/backend/libpq/hba.c +++ b/src/backend/libpq/hba.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/libpq/hba.c,v 1.80 2002/03/04 01:46:03 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/libpq/hba.c,v 1.81 2002/04/04 04:25:47 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -30,18 +30,20 @@ #include <arpa/inet.h> #include <unistd.h> +#include "commands/user.h" +#include "libpq/crypt.h" #include "libpq/libpq.h" #include "miscadmin.h" #include "nodes/pg_list.h" #include "storage/fd.h" -#define MAX_TOKEN 80 -/* Maximum size of one token in the configuration file */ - #define IDENT_USERNAME_MAX 512 /* Max size of username ident server can return */ +/* This is used to separate values in multi-valued column strings */ +#define MULTI_VALUE_SEP "\001" + /* * These variables hold the pre-parsed contents of the hba and ident * configuration files. Each is a list of sublists, one sublist for @@ -53,7 +55,17 @@ */ static List *hba_lines = NIL; /* pre-parsed contents of hba file */ static List *ident_lines = NIL; /* pre-parsed contents of ident file */ +static List *group_lines = NIL; /* pre-parsed contents of group file */ +static List *user_lines = NIL; /* pre-parsed contents of user password file */ + +/* sorted entries so we can do binary search lookups */ +static List **user_sorted = NULL; /* sorted user list, for bsearch() */ +static List **group_sorted = NULL; /* sorted group list, for bsearch() */ +static int user_length; +static int group_length; +static List *tokenize_file(FILE *file); +static char *tokenize_inc_file(const char *inc_filename); /* * Some standard C libraries, including GNU, have an isblank() function. @@ -67,41 +79,76 @@ isblank(const char c) /* - * Grab one token out of fp. Tokens are strings of non-blank - * characters bounded by blank characters, beginning of line, and end - * of line. Blank means space or tab. Return the token as *buf. - * Leave file positioned to character immediately after the token or - * EOF, whichever comes first. If no more tokens on line, return null - * string as *buf and position file to beginning of next line or EOF, - * whichever comes first. + * Grab one token out of fp. Tokens are strings of non-blank + * characters bounded by blank characters, beginning of line, and + * end of line. Blank means space or tab. Return the token as + * *buf. Leave file positioned to character immediately after the + * token or EOF, whichever comes first. If no more tokens on line, + * return null string as *buf and position file to beginning of + * next line or EOF, whichever comes first. Allow spaces in quoted + * strings. Terminate on unquoted commas. Handle comments. */ -static void +void next_token(FILE *fp, char *buf, const int bufsz) { int c; - char *eb = buf + (bufsz - 1); + char *end_buf = buf + (bufsz - 1); + bool in_quote = false; + bool was_quote = false; - /* Move over initial token-delimiting blanks */ - while ((c = getc(fp)) != EOF && isblank(c)) + /* Move over initial whitespace and commas */ + while ((c = getc(fp)) != EOF && (isblank(c) || c == ',')) ; if (c != EOF && c != '\n') { /* - * build a token in buf of next characters up to EOF, eol, or - * blank. If the token gets too long, we still parse it - * correctly, but the excess characters are not stored into *buf. + * Build a token in buf of next characters up to EOF, EOL, unquoted + * comma, or unquoted whitespace. */ - while (c != EOF && c != '\n' && !isblank(c)) + while (c != EOF && c != '\n' && + (!isblank(c) || in_quote == true)) { - if (buf < eb) + if (c == '"') + in_quote = !in_quote; + + /* skip comments to EOL */ + if (c == '#' && !in_quote) + { + while ((c = getc(fp)) != EOF && c != '\n') + ; + continue; + } + + if (buf >= end_buf) + { + elog(LOG, "Token too long in authentication file, skipping, %s", buf); + /* Discard remainder of line */ + while ((c = getc(fp)) != EOF && c != '\n') + ; + buf[0] = '\0'; + break; + } + + if (c != '"' || (c == '"' && was_quote)) *buf++ = c; + + /* We pass back the comma so the caller knows there is more */ + if ((isblank(c) || c == ',') && !in_quote) + break; + + /* Literal double-quote is two double-quotes */ + if (c == '"') + was_quote = !was_quote; + else + was_quote = false; + c = getc(fp); } /* * Put back the char right after the token (critical in case it is - * eol, since we need to detect end-of-line at next call). + * EOL, since we need to detect end-of-line at next call). */ if (c != EOF) ungetc(c, fp); @@ -109,17 +156,142 @@ next_token(FILE *fp, char *buf, const int bufsz) *buf = '\0'; } +/* + * Tokenize file and handle file inclusion and comma lists. We have + * to break apart the commas to expand any file names then + * reconstruct with commas. + */ +static char * +next_token_expand(FILE *file) +{ + char buf[MAX_TOKEN]; + char *comma_str = pstrdup(""); + bool trailing_comma; + char *incbuf; + + do + { + next_token(file, buf, sizeof(buf)); + if (!*buf) + break; + + if (buf[strlen(buf)-1] == ',') + { + trailing_comma = true; + buf[strlen(buf)-1] = '\0'; + } + else + trailing_comma = false; + + /* Is this referencing a file? */ + if (buf[0] == '@') + incbuf = tokenize_inc_file(buf+1); + else + incbuf = pstrdup(buf); + + comma_str = repalloc(comma_str, + strlen(comma_str) + strlen(incbuf) + 1); + strcat(comma_str, incbuf); + pfree(incbuf); + + if (trailing_comma) + { + comma_str = repalloc(comma_str, strlen(comma_str) + 1 + 1); + strcat(comma_str, MULTI_VALUE_SEP); + } + } while (trailing_comma); + + return comma_str; +} + +/* + * Free memory used by lines/tokens (i.e., structure built by tokenize_file) + */ static void -read_through_eol(FILE *file) +free_lines(List **lines) { - int c; + if (*lines) + { + List *line, + *token; - while ((c = getc(file)) != EOF && c != '\n') - ; + foreach(line, *lines) + { + List *ln = lfirst(line); + + /* free the pstrdup'd tokens (don't try it on the line number) */ + foreach(token, lnext(ln)) + pfree(lfirst(token)); + /* free the sublist structure itself */ + freeList(ln); + } + /* free the list structure itself */ + freeList(*lines); + /* clear the static variable */ + *lines = NIL; + } +} + + +static char * +tokenize_inc_file(const char *inc_filename) +{ + char *inc_fullname; + FILE *inc_file; + List *inc_lines; + List *line; + char *comma_str = pstrdup(""); + + inc_fullname = (char *) palloc(strlen(DataDir) + 1 + + strlen(inc_filename) + 1); + strcpy(inc_fullname, DataDir); + strcat(inc_fullname, "/"); + strcat(inc_fullname, inc_filename); + + inc_file = AllocateFile(inc_fullname, "r"); + if (!inc_file) + { + elog(LOG, "tokenize_inc_file: Unable to open secondary authentication file \"@%s\" as \"%s\": %m", + inc_filename, inc_fullname); + pfree(inc_fullname); + + /* return empty string, it matches nothing */ + return pstrdup(""); + } + pfree(inc_fullname); + + /* There is possible recursion here if the file contains @ */ + inc_lines = tokenize_file(inc_file); + FreeFile(inc_file); + + /* Create comma-separate string from List */ + foreach(line, inc_lines) + { + List *ln = lfirst(line); + List *token; + + /* First entry is line number */ + foreach(token, lnext(ln)) + { + if (strlen(comma_str)) + { + comma_str = repalloc(comma_str, strlen(comma_str) + 1); + strcat(comma_str, MULTI_VALUE_SEP); + } + comma_str = repalloc(comma_str, + strlen(comma_str) + strlen(lfirst(token)) + 1); + strcat(comma_str, lfirst(token)); + } + } + + free_lines(&inc_lines); + + return comma_str; } + /* * Read the given file and create a list of line sublists. */ @@ -129,19 +301,13 @@ tokenize_file(FILE *file) List *lines = NIL; List *next_line = NIL; int line_number = 1; - char buf[MAX_TOKEN]; - char *comment_ptr; + char *buf; while (!feof(file)) { - next_token(file, buf, sizeof(buf)); - - /* trim off comment, even if inside a token */ - comment_ptr = strchr(buf, '#'); - if (comment_ptr != NULL) - *comment_ptr = '\0'; + buf = next_token_expand(file); - /* add token to list, unless we are at eol or comment start */ + /* add token to list, unless we are at EOL or comment start */ if (buf[0] != '\0') { if (next_line == NIL) @@ -151,22 +317,15 @@ tokenize_file(FILE *file) lines = lappend(lines, next_line); } /* append token to current line's list */ - next_line = lappend(next_line, pstrdup(buf)); + next_line = lappend(next_line, buf); } else { - /* we are at real or logical eol, so force a new line List */ + /* we are at real or logical EOL, so force a new line List */ next_line = NIL; } - if (comment_ptr != NULL) - { - /* Found a comment, so skip the rest of the line */ - read_through_eol(file); - next_line = NIL; - } - - /* Advance line number whenever we reach eol */ + /* Advance line number whenever we reach EOL */ if (next_line == NIL) line_number++; } @@ -176,31 +335,116 @@ tokenize_file(FILE *file) /* - * Free memory used by lines/tokens (ie, structure built by tokenize_file) + * Compare two password-file lines on the basis of their user names. + * + * Used for qsort() sorting and bsearch() lookup. */ -static void -free_lines(List **lines) +static int +user_group_cmp(const void *user, const void *list) { - if (*lines) + /* first node is line number */ + char *user1 = (char *)user; + char *user2 = lfirst(lnext(*(List **)list)); + + return strcmp(user1, user2); +} + + +/* + * Lookup a group name in the pg_group file + */ +static List ** +get_group_line(const char *group) +{ + return (List **) bsearch((void *) group, + (void *) group_sorted, + group_length, + sizeof(List *), + user_group_cmp); +} + + +/* + * Lookup a user name in the pg_shadow file + */ +List ** +get_user_line(const char *user) +{ + return (List **) bsearch((void *) user, + (void *) user_sorted, + user_length, + sizeof(List *), + user_group_cmp); +} + + +/* + * Check group for a specific user. + */ +static int +check_group(char *group, char *user) +{ + List **line, *l; + + if ((line = get_group_line(group)) != NULL) { - List *line, - *token; + foreach(l, lnext(lnext(*line))) + if (strcmp(lfirst(l), user) == 0) + return 1; + } - foreach(line, *lines) + return 0; +} + +/* + * Check comma user list for a specific user, handle group names. + */ +static int +check_user(char *user, char *param_str) +{ + char *tok; + + for (tok = strtok(param_str, MULTI_VALUE_SEP); tok != NULL; tok = strtok(NULL, MULTI_VALUE_SEP)) + { + if (tok[0] == '+') { - List *ln = lfirst(line); + if (check_group(tok+1, user)) + return 1; + } + else if (strcmp(tok, user) == 0 || + strcmp(tok, "all") == 0) + return 1; + } - /* free the pstrdup'd tokens (don't try it on the line number) */ - foreach(token, lnext(ln)) - pfree(lfirst(token)); - /* free the sublist structure itself */ - freeList(ln); + return 0; +} + +/* + * Check to see if db/user combination matches param string. + */ +static int +check_db(char *dbname, char *user, char *param_str) +{ + char *tok; + + for (tok = strtok(param_str, MULTI_VALUE_SEP); tok != NULL; tok = strtok(NULL, MULTI_VALUE_SEP)) + { + if (strcmp(tok, "all") == 0) + return 1; + else if (strcmp(tok, "sameuser") == 0) + { + if (strcmp(dbname, user) == 0) + return 1; } - /* free the list structure itself */ - freeList(*lines); - /* clear the static variable */ - *lines = NIL; + else if (strcmp(tok, "samegroup") == 0) + { + if (check_group(dbname, user)) + return 1; + } + else if (strcmp(tok, dbname) == 0) + return 1; } + return 0; } @@ -278,6 +522,7 @@ parse_hba(List *line, hbaPort *port, bool *found_p, bool *error_p) int line_number; char *token; char *db; + char *user; Assert(line != NIL); line_number = lfirsti(line); @@ -293,10 +538,17 @@ parse_hba(List *line, hbaPort *port, bool *found_p, bool *error_p) goto hba_syntax; db = lfirst(line); - /* Read the rest of the line. */ + /* Get the user. */ + line = lnext(line); + if (!line) + goto hba_syntax; + user = lfirst(line); + line = lnext(line); if (!line) goto hba_syntax; + + /* Read the rest of the line. */ parse_hba_auth(line, &port->auth_method, port->auth_arg, error_p); if (*error_p) goto hba_syntax; @@ -308,15 +560,7 @@ parse_hba(List *line, hbaPort *port, bool *found_p, bool *error_p) port->auth_method == uaKrb5) goto hba_syntax; - /* - * If this record doesn't match the parameters of the connection - * attempt, ignore it. - */ - if ((strcmp(db, port->database) != 0 && - strcmp(db, "all") != 0 && - (strcmp(db, "sameuser") != 0 || - strcmp(port->database, port->user) != 0)) || - port->raddr.sa.sa_family != AF_UNIX) + if (port->raddr.sa.sa_family != AF_UNIX) return; } else if (strcmp(token, "host") == 0 || strcmp(token, "hostssl") == 0) @@ -347,6 +591,12 @@ parse_hba(List *line, hbaPort *port, bool *found_p, bool *error_p) goto hba_syntax; db = lfirst(line); + /* Get the user. */ + line = lnext(line); + if (!line) + goto hba_syntax; + user = lfirst(line); + /* Read the IP address field. */ line = lnext(line); if (!line) @@ -371,21 +621,19 @@ parse_hba(List *line, hbaPort *port, bool *found_p, bool *error_p) if (*error_p) goto hba_syntax; - /* - * If this record doesn't match the parameters of the connection - * attempt, ignore it. - */ - if ((strcmp(db, port->database) != 0 && - strcmp(db, "all") != 0 && - (strcmp(db, "sameuser") != 0 || - strcmp(port->database, port->user) != 0)) || - port->raddr.sa.sa_family != AF_INET || + /* Must meet network restrictions */ + if (port->raddr.sa.sa_family != AF_INET || ((file_ip_addr.s_addr ^ port->raddr.in.sin_addr.s_addr) & mask.s_addr) != 0) return; } else goto hba_syntax; + if (!check_db(port->database, port->user, db)) + return; + if (!check_user(port->user, user)) + return; + /* Success */ *found_p = true; return; @@ -430,6 +678,127 @@ check_hba(hbaPort *port) } + +/* + * Open the group file if possible (return NULL if not) + */ +static FILE * +group_openfile(void) +{ + char *filename; + FILE *groupfile; + + filename = group_getfilename(); + groupfile = AllocateFile(filename, "r"); + + if (groupfile == NULL && errno != ENOENT) + elog(LOG, "could not open %s: %m", filename); + + pfree(filename); + + return groupfile; +} + + + +/* + * Open the password file if possible (return NULL if not) + */ +static FILE * +user_openfile(void) +{ + char *filename; + FILE *pwdfile; + + filename = user_getfilename(); + pwdfile = AllocateFile(filename, "r"); + + if (pwdfile == NULL && errno != ENOENT) + elog(LOG, "could not open %s: %m", filename); + + pfree(filename); + + return pwdfile; +} + + + +/* + * Load group/user name mapping file + */ +void +load_group() +{ + FILE *group_file; + List *line; + + if (group_lines) + free_lines(&group_lines); + + group_file = group_openfile(); + if (!group_file) + return; + group_lines = tokenize_file(group_file); + FreeFile(group_file); + + /* create sorted lines for binary searching */ + if (group_sorted) + pfree(group_sorted); + group_length = length(group_lines); + if (group_length) + { + int i = 0; + + group_sorted = palloc(group_length * sizeof(List *)); + + foreach(line, group_lines) + group_sorted[i++] = lfirst(line); + + qsort((void *) group_sorted, group_length, sizeof(List *), user_group_cmp); + } + else + group_sorted = NULL; +} + + +/* + * Load user/password mapping file + */ +void +load_user() +{ + FILE *user_file; + List *line; + + if (user_lines) + free_lines(&user_lines); + + user_file = user_openfile(); + if (!user_file) + return; + user_lines = tokenize_file(user_file); + FreeFile(user_file); + + /* create sorted lines for binary searching */ + if (user_sorted) + pfree(user_sorted); + user_length = length(user_lines); + if (user_length) + { + int i = 0; + + user_sorted = palloc(user_length * sizeof(List *)); + + foreach(line, user_lines) + user_sorted[i++] = lfirst(line); + + qsort((void *) user_sorted, user_length, sizeof(List *), user_group_cmp); + } + else + user_sorted = NULL; +} + + /* * Read the config file and create a List of Lists of tokens in the file. * If we find a file by the old name of the config file (pg_hba), we issue @@ -437,60 +806,35 @@ check_hba(hbaPort *port) * follow directions and just installed his old hba file in the new database * system. */ -static void +void load_hba(void) { - int fd, - bufsize; + int bufsize; FILE *file; /* The config file we have to read */ - char *old_conf_file; + char *conf_file; /* The name of the config file */ if (hba_lines) free_lines(&hba_lines); - /* - * The name of old config file that better not exist. Fail if config - * file by old name exists. Put together the full pathname to the old - * config file. - */ - bufsize = (strlen(DataDir) + strlen(OLD_CONF_FILE) + 2) * sizeof(char); - old_conf_file = (char *) palloc(bufsize); - snprintf(old_conf_file, bufsize, "%s/%s", DataDir, OLD_CONF_FILE); + /* Put together the full pathname to the config file. */ + bufsize = (strlen(DataDir) + strlen(CONF_FILE) + 2) * sizeof(char); + conf_file = (char *) palloc(bufsize); + snprintf(conf_file, bufsize, "%s/%s", DataDir, CONF_FILE); - if ((fd = open(old_conf_file, O_RDONLY | PG_BINARY, 0)) != -1) + file = AllocateFile(conf_file, "r"); + if (file == NULL) { - /* Old config file exists. Tell this guy he needs to upgrade. */ - close(fd); - elog(LOG, "A file exists by the name used for host-based authentication " - "in prior releases of Postgres (%s). The name and format of " - "the configuration file have changed, so this file should be " - "converted.", old_conf_file); + /* The open of the config file failed. */ + elog(LOG, "load_hba: Unable to open authentication config file \"%s\": %m", + conf_file); + pfree(conf_file); } else { - char *conf_file; /* The name of the config file we have to - * read */ - - /* put together the full pathname to the config file */ - bufsize = (strlen(DataDir) + strlen(CONF_FILE) + 2) * sizeof(char); - conf_file = (char *) palloc(bufsize); - snprintf(conf_file, bufsize, "%s/%s", DataDir, CONF_FILE); - - file = AllocateFile(conf_file, "r"); - if (file == NULL) - { - /* The open of the config file failed. */ - elog(LOG, "load_hba: Unable to open authentication config file \"%s\": %m", - conf_file); - } - else - { - hba_lines = tokenize_file(file); - FreeFile(file); - } - pfree(conf_file); + hba_lines = tokenize_file(file); + FreeFile(file); } - pfree(old_conf_file); + pfree(conf_file); } @@ -606,7 +950,7 @@ check_ident_usermap(const char *usermap_name, /* * Read the ident config file and create a List of Lists of tokens in the file. */ -static void +void load_ident(void) { FILE *file; /* The map file we have to read */ @@ -622,7 +966,7 @@ load_ident(void) map_file = (char *) palloc(bufsize); snprintf(map_file, bufsize, "%s/%s", DataDir, USERMAP_FILE); - file = AllocateFile(map_file, PG_BINARY_R); + file = AllocateFile(map_file, "r"); if (file == NULL) { /* The open of the map file failed. */ @@ -640,8 +984,8 @@ load_ident(void) /* * Parse the string "*ident_response" as a response from a query to an Ident - * server. If it's a normal response indicating a username, return true - * and store the username at *ident_user. If it's anything else, + * server. If it's a normal response indicating a user name, return true + * and store the user name at *ident_user. If it's anything else, * return false. */ static bool @@ -708,7 +1052,7 @@ interpret_ident_response(char *ident_response, cursor++; /* Go over colon */ while (isblank(*cursor)) cursor++; /* skip blanks */ - /* Rest of line is username. Copy it over. */ + /* Rest of line is user name. Copy it over. */ i = 0; while (*cursor != '\r' && i < IDENT_USERNAME_MAX) ident_user[i++] = *cursor++; @@ -725,7 +1069,7 @@ interpret_ident_response(char *ident_response, /* * Talk to the ident server on host "remote_ip_addr" and find out who * owns the tcp connection from his port "remote_port" to port - * "local_port_addr" on host "local_ip_addr". Return the username the + * "local_port_addr" on host "local_ip_addr". Return the user name the * ident server gives as "*ident_user". * * IP addresses and port numbers are in network byte order. @@ -955,6 +1299,8 @@ ident_unix(int sock, char *ident_user) #endif } + + /* * Determine the username of the initiator of the connection described * by "port". Then look in the usermap file under the usermap @@ -1010,211 +1356,3 @@ hba_getauthmethod(hbaPort *port) return STATUS_ERROR; } -/* - * Clear and reload tokenized file contents. - */ -void -load_hba_and_ident(void) -{ - load_hba(); - load_ident(); -} - - -/* Character set stuff. Not sure it really belongs in this file. */ - -#ifdef CYR_RECODE - -#define CHARSET_FILE "charset.conf" -#define MAX_CHARSETS 10 -#define KEY_HOST 1 -#define KEY_BASE 2 -#define KEY_TABLE 3 - -struct CharsetItem -{ - char Orig[MAX_TOKEN]; - char Dest[MAX_TOKEN]; - char Table[MAX_TOKEN]; -}; - - -static bool -CharSetInRange(char *buf, int host) -{ - int valid, - i, - FromAddr, - ToAddr, - tmp; - struct in_addr file_ip_addr; - char *p; - unsigned int one = 0x80000000, - NetMask = 0; - unsigned char mask; - - p = strchr(buf, '/'); - if (p) - { - *p++ = '\0'; - valid = inet_aton(buf, &file_ip_addr); - if (valid) - { - mask = strtoul(p, 0, 0); - FromAddr = ntohl(file_ip_addr.s_addr); - ToAddr = ntohl(file_ip_addr.s_addr); - for (i = 0; i < mask; i++) - { - NetMask |= one; - one >>= 1; - } - FromAddr &= NetMask; - ToAddr = ToAddr | ~NetMask; - tmp = ntohl(host); - return ((unsigned) tmp >= (unsigned) FromAddr && - (unsigned) tmp <= (unsigned) ToAddr); - } - } - else - { - p = strchr(buf, '-'); - if (p) - { - *p++ = '\0'; - valid = inet_aton(buf, &file_ip_addr); - if (valid) - { - FromAddr = ntohl(file_ip_addr.s_addr); - valid = inet_aton(p, &file_ip_addr); - if (valid) - { - ToAddr = ntohl(file_ip_addr.s_addr); - tmp = ntohl(host); - return ((unsigned) tmp >= (unsigned) FromAddr && - (unsigned) tmp <= (unsigned) ToAddr); - } - } - } - else - { - valid = inet_aton(buf, &file_ip_addr); - if (valid) - { - FromAddr = file_ip_addr.s_addr; - return (unsigned) FromAddr == (unsigned) host; - } - } - } - return false; -} - -void -GetCharSetByHost(char *TableName, int host, const char *DataDir) -{ - FILE *file; - char buf[MAX_TOKEN], - BaseCharset[MAX_TOKEN], - OrigCharset[MAX_TOKEN], - DestCharset[MAX_TOKEN], - HostCharset[MAX_TOKEN], - *map_file; - int key, - ChIndex = 0, - c, - i, - bufsize; - struct CharsetItem *ChArray[MAX_CHARSETS]; - - *TableName = '\0'; - bufsize = (strlen(DataDir) + strlen(CHARSET_FILE) + 2) * sizeof(char); - map_file = (char *) palloc(bufsize); - snprintf(map_file, bufsize, "%s/%s", DataDir, CHARSET_FILE); - file = AllocateFile(map_file, PG_BINARY_R); - pfree(map_file); - if (file == NULL) - { - /* XXX should we log a complaint? */ - return; - } - while ((c = getc(file)) != EOF) - { - if (c == '#') - read_through_eol(file); - else - { - /* Read the key */ - ungetc(c, file); - next_token(file, buf, sizeof(buf)); - if (buf[0] != '\0') - { - key = 0; - if (strcasecmp(buf, "HostCharset") == 0) - key = KEY_HOST; - if (strcasecmp(buf, "BaseCharset") == 0) - key = KEY_BASE; - if (strcasecmp(buf, "RecodeTable") == 0) - key = KEY_TABLE; - switch (key) - { - case KEY_HOST: - /* Read the host */ - next_token(file, buf, sizeof(buf)); - if (buf[0] != '\0') - { - if (CharSetInRange(buf, host)) - { - /* Read the charset */ - next_token(file, buf, sizeof(buf)); - if (buf[0] != '\0') - strcpy(HostCharset, buf); - } - } - break; - case KEY_BASE: - /* Read the base charset */ - next_token(file, buf, sizeof(buf)); - if (buf[0] != '\0') - strcpy(BaseCharset, buf); - break; - case KEY_TABLE: - /* Read the original charset */ - next_token(file, buf, sizeof(buf)); - if (buf[0] != '\0') - { - strcpy(OrigCharset, buf); - /* Read the destination charset */ - next_token(file, buf, sizeof(buf)); - if (buf[0] != '\0') - { - strcpy(DestCharset, buf); - /* Read the table filename */ - next_token(file, buf, sizeof(buf)); - if (buf[0] != '\0') - { - ChArray[ChIndex] = - (struct CharsetItem *) palloc(sizeof(struct CharsetItem)); - strcpy(ChArray[ChIndex]->Orig, OrigCharset); - strcpy(ChArray[ChIndex]->Dest, DestCharset); - strcpy(ChArray[ChIndex]->Table, buf); - ChIndex++; - } - } - } - break; - } - read_through_eol(file); - } - } - } - FreeFile(file); - - for (i = 0; i < ChIndex; i++) - { - if (strcasecmp(BaseCharset, ChArray[i]->Orig) == 0 && - strcasecmp(HostCharset, ChArray[i]->Dest) == 0) - strncpy(TableName, ChArray[i]->Table, 79); - pfree(ChArray[i]); - } -} - -#endif /* CYR_RECODE */ diff --git a/src/backend/libpq/password.c b/src/backend/libpq/password.c deleted file mode 100644 index f8490f8bd57..00000000000 --- a/src/backend/libpq/password.c +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * $Id: password.c,v 1.41 2002/03/04 01:46:03 tgl Exp $ - * - */ - -#include <errno.h> -#include <unistd.h> - -#include "postgres.h" - -#ifdef HAVE_CRYPT_H -#include <crypt.h> -#endif - -#include "libpq/libpq.h" -#include "libpq/password.h" -#include "libpq/crypt.h" -#include "miscadmin.h" -#include "storage/fd.h" - - -int -verify_password(const Port *port, const char *user, const char *password) -{ - char *pw_file_fullname; - FILE *pw_file; - - pw_file_fullname = (char *) palloc(strlen(DataDir) + strlen(port->auth_arg) + 2); - strcpy(pw_file_fullname, DataDir); - strcat(pw_file_fullname, "/"); - strcat(pw_file_fullname, port->auth_arg); - - pw_file = AllocateFile(pw_file_fullname, PG_BINARY_R); - if (!pw_file) - { - elog(LOG, "verify_password: Unable to open password file \"%s\": %m", - pw_file_fullname); - - pfree(pw_file_fullname); - - return STATUS_ERROR; - } - - pfree(pw_file_fullname); - - while (!feof(pw_file)) - { - char pw_file_line[255], - *p, - *test_user, - *test_pw; - - if (fgets(pw_file_line, sizeof(pw_file_line), pw_file) == NULL) - pw_file_line[0] = '\0'; - /* kill the newline */ - if (strlen(pw_file_line) > 0 && - pw_file_line[strlen(pw_file_line) - 1] == '\n') - pw_file_line[strlen(pw_file_line) - 1] = '\0'; - - p = pw_file_line; - - test_user = strtok(p, ":"); - if (!test_user || test_user[0] == '\0') - continue; - test_pw = strtok(NULL, ":"); - - if (strcmp(user, test_user) == 0) - { - /* we're outta here one way or the other, so close file */ - FreeFile(pw_file); - - /* - * If the password is empty or "+" then we use the regular - * pg_shadow passwords. If we use crypt then we have to use - * pg_shadow passwords no matter what. This is because the - * current code needs non-encrypted passwords to encrypt with - * a random salt. - */ - if (port->auth_method == uaMD5 || - port->auth_method == uaCrypt || - test_pw == NULL || - test_pw[0] == '\0' || - strcmp(test_pw, "+") == 0) - return md5_crypt_verify(port, user, password); - - /* external password file is crypt-only */ - if (strcmp(crypt(password, test_pw), test_pw) == 0) - { - /* it matched. */ - return STATUS_OK; - } - - elog(LOG, "verify_password: password mismatch for '%s'", - user); - - return STATUS_ERROR; - } - } - - FreeFile(pw_file); - - elog(LOG, "verify_password: user '%s' not found in password file", - user); - - return STATUS_ERROR; -} diff --git a/src/backend/libpq/pg_hba.conf.sample b/src/backend/libpq/pg_hba.conf.sample index 669588e517c..05e6959b4de 100644 --- a/src/backend/libpq/pg_hba.conf.sample +++ b/src/backend/libpq/pg_hba.conf.sample @@ -42,22 +42,36 @@ # # Format: # -# host DBNAME IP_ADDRESS ADDRESS_MASK AUTH_TYPE [AUTH_ARGUMENT] -# -# DBNAME can be: -# o a database name -# o "all", which means the record matches all databases -# o "sameuser", which means users can only access databases whose name -# is the same as their username +# host DATABASE USER IP_ADDRESS MASK AUTH_TYPE +# +# DATABASE can be: +# o a database name +# o "sameuser", which means a user can only access a database with the +# same name as their user name +# o "samegroup", which means a user can only access databases when they +# are members of a group with the same name as the database name +# o "all", which matches all databases +# o a list of database names, separated by commas +# o a file name containing database names, starting with '@' +# +# USER can be: +# o a user name +# o "all", which matches all users +# o a list of user names, separated by commas +# o a group name, starting with '+' +# o a file name containing user names, starting with '@' +# +# Files read using '@' can contain comma-separated database/user names, +# or one name per line. The files can also contain comments using '#'. # -# IP_ADDRESS and ADDRESS_MASK are standard dotted decimal IP address and +# IP_ADDRESS and MASK are standard dotted decimal IP address and # mask values. IP addresses can only be specified numerically, not as # domain or host names. # # Do not prevent the superuser from accessing the template1 database. # Various utility commands need access to template1. # -# AUTH_TYPE and AUTH_ARGUMENT are described below. +# AUTH_TYPE is described below. # # # hostssl @@ -65,10 +79,8 @@ # # The format of this record is identical to "host". # -# -# -# It specifies hosts that required connection via secure SSL. "host" -# records allow SSL connections too, but "hostssl" only allows SSL-secured +# It specifies hosts that require connection via secure SSL. "host" +# allows SSL connections too, but "hostssl" requires SSL-secured # connections. # # This keyword is only available if the server was compiled with SSL @@ -82,10 +94,10 @@ # connections. Without this record, UNIX-socket connections are disallowed # # Format: -# local DBNAME AUTH_TYPE [AUTH_ARGUMENT] +# local DATABASE USER AUTH_TYPE # # This format is identical to the "host" record type except there are no -# IP_ADDRESS and ADDRESS_MASK fields. +# IP_ADDRESS and MASK fields. # # # @@ -96,57 +108,38 @@ # has an AUTH_TYPE. # # trust: -# No authentication is done. Any valid username is accepted, +# No authentication is done. Any valid user name is accepted, # including the PostgreSQL superuser. This option should # be used only for hosts where all users are trusted. # -# password: -# Authentication is done by matching a password supplied -# in clear by the host. If no AUTH_ARGUMENT is used, the -# password is compared with the user's entry in the -# pg_shadow table. -# -# If AUTH_ARGUMENT is specified, the username is looked up -# in that file in the $PGDATA directory. If the username -# is found but there is no password, the password is looked -# up in pg_shadow. If a password exists in the file, it is -# used instead. These secondary files allow fine-grained -# control over who can access which databases and whether -# a non-default password is required. The same file can be -# used in multiple records for easier administration. -# Password files can be maintained with the pg_passwd(1) -# utility. Remember, these passwords override pg_shadow -# passwords. Also, such passwords are passed over the network -# in cleartext, meaning this should not be used on untrusted -# networks. -# # md5: -# Same as "password", except the password is encrypted over the -# network. This method is preferable to "password" and "crypt" -# except for pre-7.2 clients that don't support it. NOTE: md5 can -# use usernames stored in secondary password files but ignores -# passwords stored there. The pg_shadow password will always be -# used. +# Requires the client to supply an MD5 encrypted password for +# authentication. This is the only method that allows encrypted +# passwords to be stored in pg_shadow. # # crypt: -# Same as "md5", but uses crypt for pre-7.2 clients. You can -# not store encrypted passwords in pg_shadow if you use this -# method. +# Same as "md5", but uses crypt for pre-7.2 clients. # +# password: +# Same as "md5", but the password is sent in cleartext over +# the network. This should not be used on untrusted +# networks. +# # ident: # For TCP/IP connections, authentication is done by contacting the # ident server on the client host. This is only as secure as the -# client machine. On machines that support unix-domain socket -# credentials (currently Linux, FreeBSD, NetBSD, and BSD/OS), this -# method also works for "local" connections. +# client machine. You must specify the map name after the 'ident' +# keyword. It determines how to map remote user names to +# PostgreSQL user names. If you use "sameuser", the user names are +# assumed to be identical. If not, the map name is looked up +# in the $PGDATA/pg_ident.conf file. The connection is accepted if +# that file contains an entry for this map name with the +# ident-supplied username and the requested PostgreSQL username. # -# AUTH_ARGUMENT is required. It determines how to map remote user -# names to PostgreSQL user names. If you use "sameuser", the user -# names are assumed to be the identical. If not, AUTH_ARGUMENT is -# assumed to be a map name found in the $PGDATA/pg_ident.conf -# file. The connection is accepted if that file contains an entry -# for this map name with the ident-supplied username and the -# requested PostgreSQL username. +# On machines that support unix-domain socket credentials +# (currently Linux, FreeBSD, NetBSD, and BSD/OS), ident allows +# reliable authentication of 'local' connections without ident +# running on the local machine. # # krb4: # Kerberos V4 authentication is used. Allowed only for @@ -157,10 +150,10 @@ # TCP/IP connections, not for local UNIX-domain sockets. # # pam: -# Authentication is passed off to PAM (PostgreSQL must be -# configured --with-pam), using the default service name -# "postgresql" - you can specify your own service name by -# setting AUTH_ARGUMENT to the desired service name. +# Authentication is done by PAM using the default service name +# "postgresql". You can specify your own service name by adding +# the service name after the 'pam' keyword. To use this option, +# PostgreSQL must be configured --with-pam. # # reject: # Reject the connection. This is used to reject certain hosts @@ -177,60 +170,70 @@ # Allow any user on the local system to connect to any database under any # username using Unix-domain sockets (the default for local connections): # -# TYPE DATABASE IP_ADDRESS MASK AUTH_TYPE AUTH_ARGUMENT -# local all trust +# TYPE DATABASE USER IP_ADDRESS MASK AUTH_TYPE +# local all all trust # # The same using local loopback TCP/IP connections: # -# TYPE DATABASE IP_ADDRESS MASK AUTH_TYPE AUTH_ARGUMENT -# host all 127.0.0.1 255.255.255.255 trust +# TYPE DATABASE USER IP_ADDRESS MASK AUTH_TYPE +# host all all 127.0.0.1 255.255.255.255 trust # # Allow any user from any host with IP address 192.168.93.x to # connect to database "template1" as the same username that ident reports # for the connection (typically his Unix username): # -# TYPE DATABASE IP_ADDRESS MASK AUTH_TYPE AUTH_ARGUMENT -# host template1 192.168.93.0 255.255.255.0 ident sameuser +# TYPE DATABASE USER IP_ADDRESS MASK AUTH_TYPE +# host template1 all 192.168.93.0 255.255.255.0 ident sameuser # # Allow a user from host 192.168.12.10 to connect to database "template1" -# if the user's password in pg_shadow is correctly supplied: +# if the user's password is correctly supplied: # -# TYPE DATABASE IP_ADDRESS MASK AUTH_TYPE AUTH_ARGUMENT -# host template1 192.168.12.10 255.255.255.255 md5 +# TYPE DATABASE USER IP_ADDRESS MASK AUTH_TYPE +# host template1 all 192.168.12.10 255.255.255.255 md5 # # In the absence of preceding "host" lines, these two lines will reject # all connection from 192.168.54.1 (since that entry will be matched # first), but allow Kerberos V5 connections from anywhere else on the # Internet. The zero mask means that no bits of the host IP address are -# considered, so it matches any host: +# considered so it matches any host: # # -# TYPE DATABASE IP_ADDRESS MASK AUTH_TYPE AUTH_ARGUMENT -# host all 192.168.54.1 255.255.255.255 reject -# host all 0.0.0.0 0.0.0.0 krb5 +# TYPE DATABASE USER IP_ADDRESS MASK AUTH_TYPE +# host all all 192.168.54.1 255.255.255.255 reject +# host all all 0.0.0.0 0.0.0.0 krb5 # # Allow users from 192.168.x.x hosts to connect to any database if they # pass the ident check. For example, if ident says the user is "james" and # he requests to connect as PostgreSQL user "guest", the connection is # allowed if there is an entry in $PGDATA/pg_ident.conf with map name # "phoenix" that says "james" is allowed to connect as "guest": +# See $PGDATA/pg_ident.conf for more information on Ident maps. # -# TYPE DATABASE IP_ADDRESS MASK AUTH_TYPE AUTH_ARGUMENT -# host all 192.168.0.0 255.255.0.0 ident phoenix +# TYPE DATABASE USER IP_ADDRESS MASK AUTH_TYPE +# host all all 192.168.0.0 255.255.0.0 ident phoenix +# +# If these are the only three lines for local connections, they will +# allow local users to connect only to their own databases (databases +# with the same name as their user name) except for administrators and +# members of group 'support' who may connect to all databases . The file +# $PGDATA/admins contains a list of user names. Passwords are required in +# all cases. +# +# TYPE DATABASE USER IP_ADDRESS MASK AUTH_TYPE +# local sameuser all md5 +# local all @admins md5 +# local all +support md5 +# +# The last two lines above can be combined into a single line: +# +# local all @admins,+support md5 +# +# The database column can also use lists and file names, but not groups: +# +# local db1,db2,@demodbs all md5 # -# If these are the only two lines for local connections, they will allow -# local users to connect only to their own databases (databases with the -# same name as their user name) except for administrators who may connect -# to all databases. The file $PGDATA/admins lists the user names who are -# permitted to connect to all databases. Passwords are required in all -# cases. (If you prefer to use ident authorization, an ident map can -# serve a parallel purpose to the password list file used here.) # -# TYPE DATABASE IP_ADDRESS MASK AUTH_TYPE AUTH_ARGUMENT -# local sameuser md5 -# local all md5 admins # -# See $PGDATA/pg_ident.conf for more information on Ident maps. # # # @@ -250,7 +253,7 @@ # configuration is probably too liberal for you. Change it to use # something other than "trust" authentication. # -# TYPE DATABASE IP_ADDRESS MASK AUTH_TYPE AUTH_ARGUMENT +# TYPE DATABASE USER IP_ADDRESS MASK AUTH_TYPE -local all trust -host all 127.0.0.1 255.255.255.255 trust +local all all trust +host all all 127.0.0.1 255.255.255.255 trust diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index a6d6097a54c..0ce817b5b39 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -37,7 +37,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.271 2002/03/15 19:20:35 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.272 2002/04/04 04:25:48 momjian Exp $ * * NOTES * @@ -748,8 +748,10 @@ PostmasterMain(int argc, char *argv[]) /* * Load cached files for client authentication. */ - load_hba_and_ident(); - load_password_cache(); + load_hba(); + load_ident(); + load_user(); + load_group(); /* * We're ready to rock and roll... @@ -1389,7 +1391,8 @@ SIGHUP_handler(SIGNAL_ARGS) elog(LOG, "Received SIGHUP, reloading configuration files"); SignalChildren(SIGHUP); ProcessConfigFile(PGC_SIGHUP); - load_hba_and_ident(); + load_hba(); + load_ident(); } PG_SETMASK(&UnBlockSig); @@ -2288,9 +2291,10 @@ sigusr1_handler(SIGNAL_ARGS) if (CheckPostmasterSignal(PMSIGNAL_PASSWORD_CHANGE)) { /* - * Password file has changed. + * Password or group file has changed. */ - load_password_cache(); + load_user(); + load_group(); } if (CheckPostmasterSignal(PMSIGNAL_WAKEN_CHILDREN)) diff --git a/src/backend/utils/adt/quote.c b/src/backend/utils/adt/quote.c index 39a1ec4df16..537e97a50ee 100644 --- a/src/backend/utils/adt/quote.c +++ b/src/backend/utils/adt/quote.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/quote.c,v 1.6 2001/10/28 06:25:53 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/quote.c,v 1.7 2002/04/04 04:25:49 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -124,8 +124,6 @@ do_quote_ident(text *iptr) { if (*cp1 == '"') *cp2++ = '"'; - if (*cp1 == '\\') - *cp2++ = '\\'; *cp2++ = *cp1++; } *cp2++ = '"'; @@ -234,8 +232,6 @@ do_quote_ident(text *iptr) if (*cp1 == '"') *cp2++ = '"'; - if (*cp1 == '\\') - *cp2++ = '\\'; *cp2++ = *cp1++; len--; diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index dceb8b9cd6f..9ef8b1c87c5 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/init/miscinit.c,v 1.85 2002/03/04 04:45:27 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/init/miscinit.c,v 1.86 2002/04/04 04:25:49 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -236,85 +236,17 @@ pg_convert2(PG_FUNCTION_ARGS) #ifdef CYR_RECODE -#define MAX_TOKEN 80 - -/* - * Some standard C libraries, including GNU, have an isblank() function. - * Others, including Solaris, do not. So we have our own. - */ -static bool -isblank(const char c) -{ - return c == ' ' || c == '\t'; -} - - -/* - * Grab one token out of fp. Tokens are strings of non-blank - * characters bounded by blank characters, beginning of line, and end - * of line. Blank means space or tab. Return the token as *buf. - * Leave file positioned to character immediately after the token or - * EOF, whichever comes first. If no more tokens on line, return null - * string as *buf and position file to beginning of next line or EOF, - * whichever comes first. - */ -static void -next_token(FILE *fp, char *buf, const int bufsz) -{ - int c; - char *eb = buf + (bufsz - 1); - - /* Move over initial token-delimiting blanks */ - while ((c = getc(fp)) != EOF && isblank(c)) - ; - - if (c != EOF && c != '\n') - { - /* - * build a token in buf of next characters up to EOF, eol, or - * blank. If the token gets too long, we still parse it - * correctly, but the excess characters are not stored into *buf. - */ - while (c != EOF && c != '\n' && !isblank(c)) - { - if (buf < eb) - *buf++ = c; - c = getc(fp); - } - - /* - * Put back the char right after the token (critical in case it is - * eol, since we need to detect end-of-line at next call). - */ - if (c != EOF) - ungetc(c, fp); - } - *buf = '\0'; -} - - -static void -read_through_eol(FILE *file) -{ - int c; - - while ((c = getc(file)) != EOF && c != '\n') - ; -} - - -void SetCharSet(void) { FILE *file; - char *p; + char *filename; char *map_file; char buf[MAX_TOKEN]; int i, c; unsigned char FromChar, ToChar; - char ChTable[80]; + char ChTable[MAX_TOKEN]; for (i = 0; i < 128; i++) { @@ -325,39 +257,40 @@ SetCharSet(void) if (IsUnderPostmaster) { GetCharSetByHost(ChTable, MyProcPort->raddr.in.sin_addr.s_addr, DataDir); - p = ChTable; + filename = ChTable; } else - p = getenv("PG_RECODETABLE"); + filename = getenv("PG_RECODETABLE"); - if (p && *p != '\0') + if (filename && *filename != '\0') { - map_file = palloc(strlen(DataDir) + strlen(p) + 2); - sprintf(map_file, "%s/%s", DataDir, p); - file = AllocateFile(map_file, PG_BINARY_R); + map_file = palloc(strlen(DataDir) + strlen(filename) + 2); + sprintf(map_file, "%s/%s", DataDir, filename); + file = AllocateFile(map_file, "r"); pfree(map_file); if (file == NULL) return; - while ((c = getc(file)) != EOF) + + while (!feof(file)) { - if (c == '#') - read_through_eol(file); - else + next_token(file, buf, sizeof(buf)); + if (buf[0] != '\0') { - /* Read the FromChar */ - ungetc(c, file); + FromChar = strtoul(buf, 0, 0); + /* Read the ToChar */ next_token(file, buf, sizeof(buf)); if (buf[0] != '\0') { - FromChar = strtoul(buf, 0, 0); - /* Read the ToChar */ - next_token(file, buf, sizeof(buf)); - if (buf[0] != '\0') + ToChar = strtoul(buf, 0, 0); + RecodeForwTable[FromChar - 128] = ToChar; + RecodeBackTable[ToChar - 128] = FromChar; + + /* read to EOL */ + while (!feof(file) && buf[0]) { - ToChar = strtoul(buf, 0, 0); - RecodeForwTable[FromChar - 128] = ToChar; - RecodeBackTable[ToChar - 128] = FromChar; - read_through_eol(file); + next_token(file, buf, sizeof(buf)); + elog(LOG, "SetCharSet: unknown tag %s in file %s" + buf, filename); } } } @@ -366,6 +299,7 @@ SetCharSet(void) } } + char * convertstr(unsigned char *buff, int len, int dest) { @@ -384,7 +318,206 @@ convertstr(unsigned char *buff, int len, int dest) } return ch; } -#endif + +#define CHARSET_FILE "charset.conf" +#define MAX_CHARSETS 10 +#define KEY_HOST 1 +#define KEY_BASE 2 +#define KEY_TABLE 3 + +struct CharsetItem +{ + char Orig[MAX_TOKEN]; + char Dest[MAX_TOKEN]; + char Table[MAX_TOKEN]; +}; + + +static bool +CharSetInRange(char *buf, int host) +{ + int valid, + i, + FromAddr, + ToAddr, + tmp; + struct in_addr file_ip_addr; + char *p; + unsigned int one = 0x80000000, + NetMask = 0; + unsigned char mask; + + p = strchr(buf, '/'); + if (p) + { + *p++ = '\0'; + valid = inet_aton(buf, &file_ip_addr); + if (valid) + { + mask = strtoul(p, 0, 0); + FromAddr = ntohl(file_ip_addr.s_addr); + ToAddr = ntohl(file_ip_addr.s_addr); + for (i = 0; i < mask; i++) + { + NetMask |= one; + one >>= 1; + } + FromAddr &= NetMask; + ToAddr = ToAddr | ~NetMask; + tmp = ntohl(host); + return ((unsigned) tmp >= (unsigned) FromAddr && + (unsigned) tmp <= (unsigned) ToAddr); + } + } + else + { + p = strchr(buf, '-'); + if (p) + { + *p++ = '\0'; + valid = inet_aton(buf, &file_ip_addr); + if (valid) + { + FromAddr = ntohl(file_ip_addr.s_addr); + valid = inet_aton(p, &file_ip_addr); + if (valid) + { + ToAddr = ntohl(file_ip_addr.s_addr); + tmp = ntohl(host); + return ((unsigned) tmp >= (unsigned) FromAddr && + (unsigned) tmp <= (unsigned) ToAddr); + } + } + } + else + { + valid = inet_aton(buf, &file_ip_addr); + if (valid) + { + FromAddr = file_ip_addr.s_addr; + return (unsigned) FromAddr == (unsigned) host; + } + } + } + return false; +} + + +static void +GetCharSetByHost(char *TableName, int host, const char *DataDir) +{ + FILE *file; + char buf[MAX_TOKEN], + BaseCharset[MAX_TOKEN], + OrigCharset[MAX_TOKEN], + DestCharset[MAX_TOKEN], + HostCharset[MAX_TOKEN], + *map_file; + int key, + ChIndex = 0, + c, + i, + bufsize; + struct CharsetItem *ChArray[MAX_CHARSETS]; + + *TableName = '\0'; + bufsize = (strlen(DataDir) + strlen(CHARSET_FILE) + 2) * sizeof(char); + map_file = (char *) palloc(bufsize); + snprintf(map_file, bufsize, "%s/%s", DataDir, CHARSET_FILE); + file = AllocateFile(map_file, "r"); + pfree(map_file); + if (file == NULL) + { + /* XXX should we log a complaint? */ + return; + } + + while (!feof(file)) + { + next_token(file, buf, sizeof(buf)); + if (buf[0] != '\0') + { + key = 0; + if (strcasecmp(buf, "HostCharset") == 0) + key = KEY_HOST; + else if (strcasecmp(buf, "BaseCharset") == 0) + key = KEY_BASE; + else if (strcasecmp(buf, "RecodeTable") == 0) + key = KEY_TABLE; + else + elog(LOG, "GetCharSetByHost: unknown tag %s in file %s" + buf, CHARSET_FILE); + + switch (key) + { + case KEY_HOST: + /* Read the host */ + next_token(file, buf, sizeof(buf)); + if (buf[0] != '\0') + { + if (CharSetInRange(buf, host)) + { + /* Read the charset */ + next_token(file, buf, sizeof(buf)); + if (buf[0] != '\0') + strcpy(HostCharset, buf); + } + } + break; + case KEY_BASE: + /* Read the base charset */ + next_token(file, buf, sizeof(buf)); + if (buf[0] != '\0') + strcpy(BaseCharset, buf); + break; + case KEY_TABLE: + /* Read the original charset */ + next_token(file, buf, sizeof(buf)); + if (buf[0] != '\0') + { + strcpy(OrigCharset, buf); + /* Read the destination charset */ + next_token(file, buf, sizeof(buf)); + if (buf[0] != '\0') + { + strcpy(DestCharset, buf); + /* Read the table filename */ + next_token(file, buf, sizeof(buf)); + if (buf[0] != '\0') + { + ChArray[ChIndex] = + (struct CharsetItem *) palloc(sizeof(struct CharsetItem)); + strcpy(ChArray[ChIndex]->Orig, OrigCharset); + strcpy(ChArray[ChIndex]->Dest, DestCharset); + strcpy(ChArray[ChIndex]->Table, buf); + ChIndex++; + } + } + } + break; + } + + /* read to EOL */ + while (!feof(file) && buf[0]) + { + next_token(file, buf, sizeof(buf)); + elog(LOG, "GetCharSetByHost: unknown tag %s in file %s" + buf, CHARSET_FILE); + } + } + } + FreeFile(file); + + for (i = 0; i < ChIndex; i++) + { + if (strcasecmp(BaseCharset, ChArray[i]->Orig) == 0 && + strcasecmp(HostCharset, ChArray[i]->Dest) == 0) + strncpy(TableName, ChArray[i]->Table, 79); + pfree(ChArray[i]); + } +} + +#endif /* CYR_RECODE */ diff --git a/src/bin/Makefile b/src/bin/Makefile index a1d4b1e8fc0..7a293c35a26 100644 --- a/src/bin/Makefile +++ b/src/bin/Makefile @@ -5,7 +5,7 @@ # Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group # Portions Copyright (c) 1994, Regents of the University of California # -# $Header: /cvsroot/pgsql/src/bin/Makefile,v 1.34 2001/02/18 18:33:59 momjian Exp $ +# $Header: /cvsroot/pgsql/src/bin/Makefile,v 1.35 2002/04/04 04:25:50 momjian Exp $ # #------------------------------------------------------------------------- @@ -14,7 +14,7 @@ top_builddir = ../.. include $(top_builddir)/src/Makefile.global DIRS := initdb initlocation ipcclean pg_ctl pg_dump pg_id \ - pg_passwd psql scripts pg_config + psql scripts pg_config ifdef MULTIBYTE DIRS += pg_encoding diff --git a/src/bin/initdb/initdb.sh b/src/bin/initdb/initdb.sh index e028ac1b10e..30f7de1f201 100644 --- a/src/bin/initdb/initdb.sh +++ b/src/bin/initdb/initdb.sh @@ -27,7 +27,7 @@ # Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group # Portions Copyright (c) 1994, Regents of the University of California # -# $Header: /cvsroot/pgsql/src/bin/initdb/Attic/initdb.sh,v 1.146 2002/04/03 05:39:32 petere Exp $ +# $Header: /cvsroot/pgsql/src/bin/initdb/Attic/initdb.sh,v 1.147 2002/04/04 04:25:50 momjian Exp $ # #------------------------------------------------------------------------- @@ -603,9 +603,11 @@ $ECHO_N "initializing pg_shadow... "$ECHO_C "$PGPATH"/postgres $PGSQL_OPT template1 >/dev/null <<EOF -- Create a trigger so that direct updates to pg_shadow will be written --- to the flat password file pg_pwd +-- to the flat password/group files pg_pwd and pg_group CREATE TRIGGER pg_sync_pg_pwd AFTER INSERT OR UPDATE OR DELETE ON pg_shadow \ -FOR EACH ROW EXECUTE PROCEDURE update_pg_pwd(); +FOR EACH ROW EXECUTE PROCEDURE update_pg_pwd_and_pg_group(); +CREATE TRIGGER pg_sync_pg_group AFTER INSERT OR UPDATE OR DELETE ON pg_group \ +FOR EACH ROW EXECUTE PROCEDURE update_pg_pwd_and_pg_group(); -- needs to be done before alter user, because alter user checks that -- pg_shadow is secure ... REVOKE ALL on pg_shadow FROM public; @@ -643,6 +645,11 @@ EOF echo "The password file wasn't generated. Please report this problem." 1>&2 exit_nicely fi + if [ ! -f "$PGDATA"/global/pg_group ]; then + echo + echo "The group file wasn't generated. Please report this problem." 1>&2 + exit_nicely + fi echo "ok" fi diff --git a/src/bin/pg_passwd/Makefile b/src/bin/pg_passwd/Makefile deleted file mode 100644 index f6f4acd0118..00000000000 --- a/src/bin/pg_passwd/Makefile +++ /dev/null @@ -1,37 +0,0 @@ -# $Header: /cvsroot/pgsql/src/bin/pg_passwd/Attic/Makefile,v 1.14 2001/05/12 19:49:47 petere Exp $ - -subdir = src/bin/pg_passwd -top_builddir = ../../.. -include $(top_builddir)/src/Makefile.global - -OBJS = pg_passwd.o -ifdef STRDUP -OBJS += $(top_builddir)/src/utils/strdup.o -endif - -all: pg_passwd - -pg_passwd: $(OBJS) - $(CC) $(CFLAGS) $(LDFLAGS) $^ $(LIBS) -o $@ - -$(top_builddir)/src/utils/strdup.o: - $(MAKE) -C $(top_builddir)/src/utils strdup.o - -install: all installdirs - $(INSTALL_PROGRAM) pg_passwd$(X) $(DESTDIR)$(bindir)/pg_passwd$(X) - -installdirs: - $(mkinstalldirs) $(DESTDIR)$(bindir) - -uninstall: - rm -f $(DESTDIR)$(bindir)/pg_passwd$(X) - -depend dep: - $(CC) -MM $(CFLAGS) *.c >depend - -clean distclean maintainer-clean: - rm -f pg_passwd$(X) pg_passwd.o - -ifeq (depend,$(wildcard depend)) -include depend -endif diff --git a/src/bin/pg_passwd/pg_passwd.c b/src/bin/pg_passwd/pg_passwd.c deleted file mode 100644 index 831c3823708..00000000000 --- a/src/bin/pg_passwd/pg_passwd.c +++ /dev/null @@ -1,412 +0,0 @@ -/* - * @(#) pg_passwd.c 1.8 09:13:16 97/07/02 Y. Ichikawa - */ -#include "postgres_fe.h" - -#include <unistd.h> -#include <errno.h> -#include <time.h> -#include <ctype.h> -#define issaltchar(c) (isalnum((unsigned char) (c)) || (c) == '.' || (c) == '/') - -#ifdef HAVE_TERMIOS_H -#include <termios.h> -#endif -#ifdef HAVE_CRYPT_H -#include <crypt.h> -#else -extern char *crypt(const char *, const char *); -#endif - -/* - * We assume that the output of crypt(3) is always 13 characters, - * and that at most 8 characters can usefully be sent to it. - * - * Postgres usernames are assumed to be less than NAMEDATALEN chars long. - */ -#define CLEAR_PASSWD_LEN 8 /* not including null */ -#define CRYPTED_PASSWD_LEN 13 /* not including null */ - -const char *progname; - -static void usage(void); -static void read_pwd_file(char *filename); -static void write_pwd_file(char *filename, char *bkname); -static void encrypt_pwd(char key[CLEAR_PASSWD_LEN + 1], - char salt[3], - char passwd[CRYPTED_PASSWD_LEN + 1]); -static void prompt_for_username(char *username); -static void prompt_for_password(char *prompt, char *password); - -static void -usage(void) -{ - printf("%s manipulates flat text password files for PostgreSQL.\n\n", progname); - printf("Usage:\n %s PASSWORD-FILE\n\n", progname); - printf("Report bugs to <pgsql-bugs@postgresql.org>.\n"); -} - -typedef struct -{ - char *uname; - char *pwd; - char *rest; -} pg_pwd; - -#define MAXPWDS 1024 - -pg_pwd pwds[MAXPWDS]; -int npwds = 0; - - -static void -read_pwd_file(char *filename) -{ - FILE *fp; - static char line[512]; - static char ans[128]; - int i; - -try_again: - fp = fopen(filename, PG_BINARY_R); - if (fp == NULL) - { - if (errno == ENOENT) - { - printf("File \"%s\" does not exist. Create? (y/n): ", filename); - fflush(stdout); - if (fgets(ans, sizeof(ans), stdin) == NULL) - exit(1); - switch (ans[0]) - { - case 'y': - case 'Y': - fp = fopen(filename, PG_BINARY_W); - if (fp == NULL) - { - perror(filename); - exit(1); - } - fclose(fp); - goto try_again; - default: - /* cannot continue */ - exit(1); - } - } - else - { - perror(filename); - exit(1); - } - } - - /* read all the entries */ - for (npwds = 0; - npwds < MAXPWDS && fgets(line, sizeof(line), fp) != NULL; - ++npwds) - { - int l; - char *p, - *q; - - l = strlen(line); - if (line[l - 1] == '\n') - line[l - 1] = '\0'; - else - { - fprintf(stderr, "%s:%d: line too long\n", - filename, npwds + 1); - exit(1); - } - - /* get user name */ - p = line; - if ((q = strchr(p, ':')) != NULL) - *q = '\0'; - - if (strlen(p) == 0) - { - fprintf(stderr, "%s:%d: null user name\n", - filename, npwds + 1); - exit(1); - } - pwds[npwds].uname = strdup(p); - - /* check for duplicate user name */ - for (i = 0; i < npwds; ++i) - { - if (strcmp(pwds[i].uname, pwds[npwds].uname) == 0) - { - fprintf(stderr, "Duplicate username %s in entry %d\n", - pwds[npwds].uname, npwds + 1); - exit(1); - } - } - - /* get password field */ - if (q) - { - p = q + 1; - q = strchr(p, ':'); - - if (q != NULL) - *(q++) = '\0'; - - if (strlen(p) != CRYPTED_PASSWD_LEN && strcmp(p, "+") != 0) - { - fprintf(stderr, "%s:%d: warning: invalid password length\n", - filename, npwds + 1); - } - pwds[npwds].pwd = strdup(p); - } - else - pwds[npwds].pwd = NULL; - - /* rest of the line is treated as is */ - if (q == NULL) - pwds[npwds].rest = NULL; - else - pwds[npwds].rest = strdup(q); - } - - fclose(fp); -} - -static void -write_pwd_file(char *filename, char *bkname) -{ - FILE *fp; - int i; - - /* make the backup file */ -link_again: - if (link(filename, bkname)) - { - if (errno == EEXIST) - { - unlink(bkname); - goto link_again; - } - perror(bkname); - exit(1); - } - if (unlink(filename)) - { - perror(filename); - exit(1); - } - - /* open file */ - if ((fp = fopen(filename, PG_BINARY_W)) == NULL) - { - perror(filename); - exit(1); - } - - /* write file */ - for (i = 0; i < npwds; ++i) - { - fprintf(fp, "%s", pwds[i].uname); - if (pwds[i].pwd) - fprintf(fp, ":%s", pwds[i].pwd); - if (pwds[i].rest) - fprintf(fp, ":%s", pwds[i].rest); - fprintf(fp, "\n"); - } - - fclose(fp); -} - -static void -encrypt_pwd(char key[CLEAR_PASSWD_LEN + 1], - char salt[3], - char passwd[CRYPTED_PASSWD_LEN + 1]) -{ - int n; - - /* select a salt, if not already given */ - if (salt[0] == '\0') - { - srand(time(NULL)); - do - { - n = rand() % 256; - } while (!issaltchar(n)); - salt[0] = n; - do - { - n = rand() % 256; - } while (!issaltchar(n)); - salt[1] = n; - salt[2] = '\0'; - } - - /* get encrypted password */ - strcpy(passwd, crypt(key, salt)); - -#ifdef PG_PASSWD_DEBUG - /* show it */ - fprintf(stderr, "key = %s, salt = %s, password = %s\n", - key, salt, passwd); -#endif -} - -static void -prompt_for_username(char *username) -{ - int length; - - printf("Username: "); - fflush(stdout); - if (fgets(username, NAMEDATALEN, stdin) == NULL) - username[0] = '\0'; - - length = strlen(username); - if (length > 0 && username[length - 1] != '\n') - { - /* eat rest of the line */ - char buf[128]; - int buflen; - - do - { - if (fgets(buf, sizeof(buf), stdin) == NULL) - break; - buflen = strlen(buf); - } while (buflen > 0 && buf[buflen - 1] != '\n'); - } - if (length > 0 && username[length - 1] == '\n') - username[length - 1] = '\0'; -} - -static void -prompt_for_password(char *prompt, char *password) -{ - int length; - -#ifdef HAVE_TERMIOS_H - struct termios t_orig, - t; -#endif - -#ifdef HAVE_TERMIOS_H - tcgetattr(0, &t); - t_orig = t; - t.c_lflag &= ~ECHO; - tcsetattr(0, TCSADRAIN, &t); -#endif - - printf(prompt); - fflush(stdout); - - if (fgets(password, CLEAR_PASSWD_LEN + 1, stdin) == NULL) - password[0] = '\0'; - -#ifdef HAVE_TERMIOS_H - tcsetattr(0, TCSADRAIN, &t_orig); -#endif - - length = strlen(password); - if (length > 0 && password[length - 1] != '\n') - { - /* eat rest of the line */ - char buf[128]; - int buflen; - - do - { - if (fgets(buf, sizeof(buf), stdin) == NULL) - break; - buflen = strlen(buf); - } while (buflen > 0 && buf[buflen - 1] != '\n'); - } - if (length > 0 && password[length - 1] == '\n') - password[length - 1] = '\0'; - printf("\n"); -} - - -int -main(int argc, char *argv[]) -{ - char *filename; - char bkname[MAXPGPATH]; - char username[NAMEDATALEN]; - char salt[3]; - char key[CLEAR_PASSWD_LEN + 1], - key2[CLEAR_PASSWD_LEN + 1]; - char e_passwd[CRYPTED_PASSWD_LEN + 1]; - int i; - - progname = argv[0]; - - if (argc != 2) - { - fprintf(stderr, "%s: too %s arguments\nTry '%s --help' for more information.\n", - progname, argc > 2 ? "many" : "few", progname); - exit(1); - } - - if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) - { - usage(); - exit(0); - } - if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) - { - puts("pg_passwd (PostgreSQL) " PG_VERSION); - exit(0); - } - if (argv[1][0] == '-') - { - fprintf(stderr, "%s: invalid option: %s\nTry '%s --help' for more information.\n", - progname, argv[1], progname); - exit(1); - } - - filename = argv[1]; - - /* open file */ - read_pwd_file(filename); - - /* ask for the user name and the password */ - prompt_for_username(username); - prompt_for_password("New password: ", key); - prompt_for_password("Re-enter new password: ", key2); - if (strcmp(key, key2) != 0) - { - fprintf(stderr, "Password mismatch\n"); - exit(1); - } - salt[0] = '\0'; - encrypt_pwd(key, salt, e_passwd); - - /* check password entry */ - for (i = 0; i < npwds; ++i) - { - if (strcmp(pwds[i].uname, username) == 0) - { /* found */ - pwds[i].pwd = strdup(e_passwd); - break; - } - } - if (i == npwds) - { /* did not exist */ - if (npwds == MAXPWDS) - { - fprintf(stderr, "Cannot handle so many entries\n"); - exit(1); - } - pwds[npwds].uname = strdup(username); - pwds[npwds].pwd = strdup(e_passwd); - pwds[npwds].rest = NULL; - ++npwds; - } - - /* write back the file */ - sprintf(bkname, "%s.bk", filename); - write_pwd_file(filename, bkname); - - return 0; -} diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index ac32bd69aa0..cd939a9c10a 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pg_proc.h,v 1.224 2002/03/29 19:06:19 tgl Exp $ + * $Id: pg_proc.h,v 1.225 2002/04/04 04:25:52 momjian Exp $ * * NOTES * The script catalog/genbki.sh reads this file and generates .bki @@ -2101,8 +2101,8 @@ DESCR("does not match LIKE expression, case-insensitive"); DATA(insert OID = 1637 ( like_escape PGUID 12 f t t t 2 f 25 "25 25" 100 0 0 100 like_escape - _null_ )); DESCR("convert match pattern to use backslash escapes"); -DATA(insert OID = 1689 ( update_pg_pwd PGUID 12 f t f t 0 f 0 "" 100 0 0 100 update_pg_pwd - _null_ )); -DESCR("update pg_pwd file"); +DATA(insert OID = 1689 ( update_pg_pwd_and_pg_group PGUID 12 f t f t 0 f 0 "" 100 0 0 100 update_pg_pwd_and_pg_group - _null_ )); +DESCR("update pg_pwd and pg_group files"); /* Oracle Compatibility Related Functions - By Edmund Mergl <E.Mergl@bawue.de> */ DATA(insert OID = 868 ( strpos PGUID 12 f t t t 2 f 23 "25 25" 100 0 0 100 textpos - _null_ )); diff --git a/src/include/commands/user.h b/src/include/commands/user.h index 351c2d6ef6d..046e022ae8f 100644 --- a/src/include/commands/user.h +++ b/src/include/commands/user.h @@ -3,15 +3,23 @@ * user.h * * - * $Id: user.h,v 1.17 2002/03/01 22:45:17 petere Exp $ + * $Id: user.h,v 1.18 2002/04/04 04:25:53 momjian Exp $ * *------------------------------------------------------------------------- */ #ifndef USER_H #define USER_H +#include "fmgr.h" #include "nodes/parsenodes.h" +#define PWD_FILE "pg_pwd" + +#define USER_GROUP_FILE "pg_group" + + +extern char *group_getfilename(void); +extern char *user_getfilename(void); extern void CreateUser(CreateUserStmt *stmt); extern void AlterUser(AlterUserStmt *stmt); extern void AlterUserSet(AlterUserSetStmt *stmt); @@ -21,6 +29,6 @@ extern void CreateGroup(CreateGroupStmt *stmt); extern void AlterGroup(AlterGroupStmt *stmt, const char *tag); extern void DropGroup(DropGroupStmt *stmt); -extern Datum update_pg_pwd(PG_FUNCTION_ARGS); +extern Datum update_pg_pwd_and_pg_group(PG_FUNCTION_ARGS); #endif /* USER_H */ diff --git a/src/include/libpq/crypt.h b/src/include/libpq/crypt.h index 458be1fd2cf..a521a0e2cee 100644 --- a/src/include/libpq/crypt.h +++ b/src/include/libpq/crypt.h @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: crypt.h,v 1.19 2001/11/12 01:52:46 momjian Exp $ + * $Id: crypt.h,v 1.20 2002/04/04 04:25:53 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -15,8 +15,6 @@ #include "libpq/libpq-be.h" -#define CRYPT_PWD_FILE_SEPSTR "\t" - /* Also defined in interfaces/odbc/md5.h */ #define MD5_PASSWD_LEN 35 @@ -24,9 +22,6 @@ strlen(passwd) == MD5_PASSWD_LEN) -extern char *crypt_getpwdfilename(void); -extern void load_password_cache(void); - extern int md5_crypt_verify(const Port *port, const char *user, const char *pgpass); extern bool md5_hash(const void *buff, size_t len, char *hexsum); diff --git a/src/include/libpq/hba.h b/src/include/libpq/hba.h index 0d5ddbaf702..b9daf985f5c 100644 --- a/src/include/libpq/hba.h +++ b/src/include/libpq/hba.h @@ -4,7 +4,7 @@ * Interface to hba.c * * - * $Id: hba.h,v 1.31 2001/11/05 17:46:33 momjian Exp $ + * $Id: hba.h,v 1.32 2002/04/04 04:25:54 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -15,15 +15,14 @@ #include <netinet/in.h> #endif +#include "nodes/pg_list.h" + #define CONF_FILE "pg_hba.conf" /* Name of the config file */ #define USERMAP_FILE "pg_ident.conf" /* Name of the usermap file */ -#define OLD_CONF_FILE "pg_hba" - /* Name of the config file in prior releases of Postgres. */ - #define IDENT_PORT 113 /* Standard TCP port number for Ident service. Assigned by IANA */ @@ -46,8 +45,15 @@ typedef enum UserAuth typedef struct Port hbaPort; +#define MAX_TOKEN 256 + +extern void next_token(FILE *fp, char *buf, const int bufsz); +extern List **get_user_line(const char *user); +extern void load_hba(void); +extern void load_ident(void); +extern void load_user(void); +extern void load_group(void); extern int hba_getauthmethod(hbaPort *port); extern int authident(hbaPort *port); -extern void load_hba_and_ident(void); #endif diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h index 4fb64473924..99f7fae88bf 100644 --- a/src/include/miscadmin.h +++ b/src/include/miscadmin.h @@ -12,7 +12,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: miscadmin.h,v 1.101 2002/03/04 01:46:04 tgl Exp $ + * $Id: miscadmin.h,v 1.102 2002/04/04 04:25:51 momjian Exp $ * * NOTES * some of the information in this file should be moved to @@ -219,7 +219,6 @@ extern int FindExec(char *full_path, const char *argv0, extern int CheckPathAccess(char *path, char *name, int open_mode); #ifdef CYR_RECODE -extern void GetCharSetByHost(char *TableName, int host, const char *DataDir); extern void SetCharSet(void); extern char *convertstr(unsigned char *buff, int len, int dest); #endif diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out index be08da82371..39130f2a5e3 100644 --- a/src/test/regress/expected/opr_sanity.out +++ b/src/test/regress/expected/opr_sanity.out @@ -30,7 +30,7 @@ WHERE (p1.prolang = 0 OR p1.prorettype = 0 OR AND p1.proname !~ '^pl[^_]+_call_handler$' AND p1.proname !~ '^RI_FKey_' AND p1.proname !~ 'costestimate$' - AND p1.proname != 'update_pg_pwd'; + AND p1.proname != 'update_pg_pwd_and_pg_group'; oid | proname -----+--------- (0 rows) diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql index 7a5991a74a1..6a95c4cd1be 100644 --- a/src/test/regress/sql/opr_sanity.sql +++ b/src/test/regress/sql/opr_sanity.sql @@ -33,7 +33,7 @@ WHERE (p1.prolang = 0 OR p1.prorettype = 0 OR AND p1.proname !~ '^pl[^_]+_call_handler$' AND p1.proname !~ '^RI_FKey_' AND p1.proname !~ 'costestimate$' - AND p1.proname != 'update_pg_pwd'; + AND p1.proname != 'update_pg_pwd_and_pg_group'; -- Look for conflicting proc definitions (same names and input datatypes). -- (This test should be dead code now that we have the unique index |