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