From 43a3543a4eb412a895df911eba9d8671ded45c54 Mon Sep 17 00:00:00 2001 From: Bruce Momjian Date: Thu, 4 Apr 2002 04:25:54 +0000 Subject: [PATCH] Authentication improvements: A new pg_hba.conf column, USER Allow specifiction of lists of users separated by commas Allow group names specified by + Allow include files containing lists of users specified by @ Allow lists of databases, and database files Allow samegroup in database column to match group name matching dbname Removal of secondary password files Remove pg_passwd utility Lots of code cleanup in user.c and hba.c New data/global/pg_pwd format New data/global/pg_group file --- doc/src/sgml/client-auth.sgml | 605 ++++++++--------- doc/src/sgml/ref/allfiles.sgml | 3 +- doc/src/sgml/ref/pg_passwd.sgml | 123 ---- doc/src/sgml/reference.sgml | 3 +- src/backend/commands/user.c | 415 +++++++++--- src/backend/libpq/Makefile | 6 +- src/backend/libpq/auth.c | 24 +- src/backend/libpq/crypt.c | 232 +------ src/backend/libpq/hba.c | 828 +++++++++++++---------- src/backend/libpq/password.c | 109 --- src/backend/libpq/pg_hba.conf.sample | 183 ++--- src/backend/postmaster/postmaster.c | 16 +- src/backend/utils/adt/quote.c | 6 +- src/backend/utils/init/miscinit.c | 317 ++++++--- src/bin/Makefile | 4 +- src/bin/initdb/initdb.sh | 13 +- src/bin/pg_passwd/Makefile | 37 - src/bin/pg_passwd/pg_passwd.c | 412 ----------- src/include/catalog/pg_proc.h | 6 +- src/include/commands/user.h | 12 +- src/include/libpq/crypt.h | 7 +- src/include/libpq/hba.h | 16 +- src/include/miscadmin.h | 3 +- src/test/regress/expected/opr_sanity.out | 2 +- src/test/regress/sql/opr_sanity.sql | 2 +- 25 files changed, 1453 insertions(+), 1931 deletions(-) delete mode 100644 doc/src/sgml/ref/pg_passwd.sgml delete mode 100644 src/backend/libpq/password.c delete mode 100644 src/bin/pg_passwd/Makefile delete mode 100644 src/bin/pg_passwd/pg_passwd.c diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml index 76542f98720..4afd179a4b1 100644 --- a/doc/src/sgml/client-auth.sgml +++ b/doc/src/sgml/client-auth.sgml @@ -1,5 +1,5 @@ @@ -10,14 +10,13 @@ $Header: /cvsroot/pgsql/doc/src/sgml/client-auth.sgml,v 1.33 2002/03/22 19:20:06 - 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. + 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,20 +29,19 @@ $Header: /cvsroot/pgsql/doc/src/sgml/client-auth.sgml,v 1.33 2002/03/22 19:20:06 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. + client authentication methods. The method to be used can be selected + on the basis of (client) host, database, and user. - PostgreSQL database user names are logically + 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 + runs. If all the users of a particular server also have accounts on the server's machine, it makes sense to assign database user names - that match their operating system user names. However, a server that accepts remote - connections may have many users who have no local account, and in such - cases there need be no connection between database user names and OS - user names. + that match their operating system user names. However, a server that + accepts remote connections may have many users who have no local + account, and in such cases there need be no connection between + database user names and OS user names. @@ -56,39 +54,39 @@ $Header: /cvsroot/pgsql/doc/src/sgml/client-auth.sgml,v 1.33 2002/03/22 19:20:06 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. + /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. + 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 or names, + (if relevant for the connection type), a database name, a user name, and the authentication method to be used for connections matching - these parameters. - The first record that matches the type, client address, and requested - database name of a connection attempt is used to do the - authentication step. There is no fall-through or + these parameters. The first record with a matching connection type, + client address, requested database, and user name is used to perform + authentication. There is no 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. + fails, subsequent records are not considered. If no record matches, + access is 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 ] +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,7 +95,7 @@ hostssl database IP-addresslocal - This record pertains to connection attempts over Unix domain + This record applies to connection attempts using Unix domain sockets. @@ -107,10 +105,11 @@ hostssl database IP-addresshost - 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. + 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,13 +118,13 @@ hostssl database IP-addresshostssl - This record pertains to connection attempts with SSL over + This record applies to connection attempts using SSL over TCP/IP. To make use of this option the server must be built with SSL support enabled. Furthermore, SSL must be enabled with the @@ -134,12 +133,35 @@ hostssl database IP-addressdatabase - Specifies the database that this record applies to. The value + 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. Otherwise, - this is the name of a specific PostgreSQL - database. + 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,10 +171,9 @@ hostssl database IP-addressIP 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 + 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,10 +190,9 @@ hostssl database IP-addressauthentication 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 . + Specifies the authentication method to use when connecting via + this record. The possible choices are summarized here; details + are in . @@ -190,31 +210,8 @@ hostssl database IP-addressreject - 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. + The connection is rejected unconditionally. This is useful for + filtering out certain hosts from a group. @@ -223,17 +220,9 @@ hostssl database IP-addressmd5 - 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. + 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. @@ -242,14 +231,20 @@ hostssl database IP-addresscrypt - 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. + 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,34 +273,36 @@ hostssl database IP-addressident - 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. + 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. - 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. + 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,17 +312,16 @@ hostssl database IP-address 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. + 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,42 +332,33 @@ hostssl database IP-address - - 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. + 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 + 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. + (using pg_ctl reload or kill -HUP) to make it + re-read the file. @@ -382,27 +369,27 @@ hostssl database IP-address An example <filename>pg_hba.conf</filename> file -# TYPE DATABASE IP_ADDRESS MASK AUTHTYPE MAP +# TYPE DATABASE USER IP_ADDRESS MASK AUTHTYPE # Allow any user on the local system to connect to any -# database under any username, but only via an IP connection: +# database under any user name, but only via an IP connection: -host all 127.0.0.1 255.255.255.255 trust +host all all 127.0.0.1 255.255.255.255 trust # The same, over Unix-socket connections: -local all trust +local all all trust # Allow any user from any host with IP address 192.168.93.x to -# connect to database "template1" as the same username that ident on that -# host identifies him as (typically his Unix username): +# connect to database "template1" as the same user name that ident on that +# host identifies him as (typically his Unix user name): -host template1 192.168.93.0 255.255.255.0 ident sameuser +host template1 all 192.168.93.0 255.255.255.0 ident sameuser # Allow a user from host 192.168.12.10 to connect to database "template1" -# if the user's password in pg_shadow is correctly supplied: +# if the user's password is correctly supplied: -host template1 192.168.12.10 255.255.255.255 md5 +host template1 all 192.168.12.10 255.255.255.255 md5 # In the absence of preceding "host" lines, these two lines will reject # all connection attempts from 192.168.54.1 (since that entry will be @@ -410,8 +397,8 @@ host template1 192.168.12.10 255.255.255.255 md5 # else on the Internet. The zero mask means that no bits of the host IP # address are considered, so it matches any host: -host all 192.168.54.1 255.255.255.255 reject -host all 0.0.0.0 0.0.0.0 krb5 +host all all 192.168.54.1 255.255.255.255 reject +host all all 0.0.0.0 0.0.0.0 krb5 # Allow users from 192.168.x.x hosts to connect to any database, if they # pass the ident check. If, for example, ident says the user is "bryanh" @@ -419,7 +406,7 @@ host all 0.0.0.0 0.0.0.0 krb5 # is allowed if there is an entry in pg_ident.conf for map "omicron" that # says "bryanh" is allowed to connect as "guest1": -host all 192.168.0.0 255.255.0.0 ident omicron +host all all 192.168.0.0 255.255.0.0 ident omicron # If these are the only two lines for local connections, they will allow # local users to connect only to their own databases (database named the @@ -429,8 +416,8 @@ host all 192.168.0.0 255.255.0.0 ident omicron # cases. (If you prefer to use ident authorization, an ident map can # serve a parallel purpose to the password list file used here.) -local sameuser md5 -local all md5 admins +local sameuser all md5 +local all @admins md5 @@ -490,86 +477,49 @@ local all md5 admins Password authentication - password + MD5 - MD5 + crypt + + + password Password-based authentication methods include md5, - crypt, and password. These methods operate + 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). + 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. + 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. + 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,10 +538,10 @@ local all md5 admins 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 + url="http://www.nrl.navy.mil/CCS/people/kenh/kerberos-faq.html">Kerb + eros FAQ or MIT Project Athena can be a + good starting point for exploration. Several sources for Kerberos distributions exist. @@ -606,34 +556,33 @@ local all md5 admins 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. + 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. + 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 + 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,18 +598,20 @@ local all md5 admins 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 + 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. + 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. + 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,55 +658,54 @@ local all md5 admins - 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. + 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. + 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: + 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. + 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 + 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. + (using pg_ctl reload or kill -HUP) to make it + re-read the file. @@ -763,13 +713,14 @@ local all md5 admins 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 + 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,7 +731,7 @@ local all md5 admins omicron bryanh bryanh omicron ann ann -# bob has username robert on these machines +# bob has user name robert on these machines omicron robert bob # bryanh can also connect as guest1 omicron bryanh guest1 @@ -799,30 +750,30 @@ omicron bryanh guest1 -No pg_hba.conf entry for host 123.123.123.123, user joeblow, database testdb +No pg_hba.conf entry for host 123.123.123.123, user andym, database testdb - 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 + 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' +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. + 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 +FATAL 1: user "andym" does not exist The indicated user name was not found. @@ -837,9 +788,9 @@ FATAL 1: Database "testdb" does not exist in the system catalog. - Note that the server log may contain more information - about an authentication failure than is reported to the client. - If you are confused about the reason for a failure, check the log. + Note that the server log may contain more information about an + authentication failure than is reported to the client. If you are + confused about the reason for a failure, check the log. diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml index 460f150bc89..61d979a5643 100644 --- a/doc/src/sgml/ref/allfiles.sgml +++ b/doc/src/sgml/ref/allfiles.sgml @@ -1,5 +1,5 @@ @@ -125,7 +125,6 @@ Complete list of usable sgml source files in this directory. - diff --git a/doc/src/sgml/ref/pg_passwd.sgml b/doc/src/sgml/ref/pg_passwd.sgml deleted file mode 100644 index 13125b08e27..00000000000 --- a/doc/src/sgml/ref/pg_passwd.sgml +++ /dev/null @@ -1,123 +0,0 @@ - - - - - 2000-11-18 - - - - pg_passwd - 1 - Application - - - - pg_passwd - change a secondary PostgreSQL password file - - - - - pg_passwd - filename - - - - - Description - - pg_passwd is a tool for manipulating flat - text password files. These files can control client authentication of - the PostgreSQL server. More information - about setting up this authentication mechanism can be found in the - Administrator's Guide. - - - - The format of a text password file is one entry per line; the fields - of each entry are separated by colons. The first field is the user - name, the second field is the encrypted password. Other fields are - ignored (to allow password files to be shared between applications - that use similar formats). pg_passwd - enables users to interactively add entries to such a file, to alter - passwords of existing entries, and to encrypt such passwords. - - - - Supply the name of the password file as argument to the - pg_passwd command. To be used by - PostgreSQL, the file needs to be located in the server's data - directory, and the base name of the file needs to be specified in the - pg_hba.conf access control file. - - -$ pg_passwd /usr/local/pgsql/data/passwords -File "/usr/local/pgsql/data/passwords" does not exist. Create? (y/n): y -Username: guest -Password: -Re-enter password: - - - where the Password: and Re-enter - password: prompts require the same password input which - is not displayed on the terminal. Note that the password is limited - to eight useful characters by restrictions of the standard crypt(3) - library routine. - - - - The original password file is renamed to - passwords.bk. - - - - To make use of this password file, put a line like the following in - pg_hba.conf: - - -host mydb 133.65.96.250 255.255.255.255 password passwords - - - which would allow access to database mydb from host 133.65.96.250 using - the passwords listed in the passwords file (and - only to the users listed in that file). - - - - - It is also useful to have entries in a password file with empty - password fields. (This is different from an empty password.) Such - entries allow you to restrict users who can access the system. These - entries cannot be managed by pg_passwd, - but you can edit password files manually. - - - - - - See also - - PostgreSQL Administrator's Guide - - - - - diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml index b6b63254462..41d419dd2ea 100644 --- a/doc/src/sgml/reference.sgml +++ b/doc/src/sgml/reference.sgml @@ -1,5 +1,5 @@ @@ -191,7 +191,6 @@ Disable this chapter until we have more functions documented. &initlocation; &ipcclean; &pgCtl; - &pgPasswd; &postgres; &postmaster; diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c index 98ee4308975..9ebd6fa3a2d 100644 --- a/src/backend/commands/user.c +++ b/src/backend/commands/user.c @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Header: /cvsroot/pgsql/src/backend/commands/user.c,v 1.94 2002/03/26 19:15:48 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/user.c,v 1.95 2002/04/04 04:25:45 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -15,6 +15,7 @@ #include #include #include +#include #include #include "access/heapam.h" @@ -27,6 +28,7 @@ #include "libpq/crypt.h" #include "miscadmin.h" #include "storage/pmsignal.h" +#include "utils/acl.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/fmgroids.h" @@ -39,22 +41,75 @@ extern bool Password_encryption; static void CheckPgUserAclNotNull(void); -/*--------------------------------------------------------------------- - * write_password_file / update_pg_pwd + + +/* + * fputs_quote * - * 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. + * 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 * - * This function set is both a trigger function for direct updates to pg_shadow - * as well as being called directly from create/alter/drop user. - * - * 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. - *--------------------------------------------------------------------- + * 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_password_file(Relation rel) +write_group_file(Relation urel, Relation grel) { char *filename, *tempname; @@ -63,14 +118,14 @@ write_password_file(Relation rel) mode_t oumask; HeapScanDesc scan; HeapTuple tuple; - TupleDesc dsc = RelationGetDescr(rel); + TupleDesc dsc = RelationGetDescr(grel); /* * Create a temporary filename to be renamed later. This prevents the - * backend from clobbering the pg_pwd file while the postmaster might + * backend from clobbering the pg_group file while the postmaster might * be reading from it. */ - filename = crypt_getpwdfilename(); + filename = group_getfilename(); bufsize = strlen(filename) + 12; tempname = (char *) palloc(bufsize); @@ -79,88 +134,89 @@ write_password_file(Relation rel) fp = AllocateFile(tempname, "w"); umask(oumask); if (fp == NULL) - elog(ERROR, "write_password_file: unable to write %s: %m", tempname); + elog(ERROR, "write_group_file: unable to write %s: %m", tempname); /* read table */ - scan = heap_beginscan(rel, false, SnapshotSelf, 0, NULL); + scan = heap_beginscan(grel, 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 datum, grolist_datum; + bool isnull; + char *groname; + IdList *grolist_p; + AclId *aidp; + int i, j, + num; + char *usename; + bool first_user = true; - 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 = heap_getattr(tuple, Anum_pg_group_groname, dsc, &isnull); + if (isnull) + continue; /* ignore NULL groupnames */ + groname = (char *) DatumGetName(datum); - datum_p = heap_getattr(tuple, Anum_pg_shadow_passwd, dsc, &null_p); + grolist_datum = heap_getattr(tuple, Anum_pg_group_grolist, dsc, &isnull); + /* Ignore NULL group lists */ + if (isnull) + continue; + grolist_p = DatumGetIdListP(grolist_datum); /* - * It can be argued that people having a null password shouldn't - * be allowed to connect under password authentication, because - * they need to have a password set up first. If you think - * assuming an empty password in that case is better, change this - * logic to look something like the code for valuntil. + * Check for illegal characters in the group name. */ - if (null_p) + i = strcspn(groname, "\n"); + if (groname[i] != '\0') { - pfree(str_n); + elog(LOG, "Invalid group name '%s'", groname); 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)); + /* 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 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); + /* + * Check for illegal characters in the user name. + */ + j = strcspn(usename, "\n"); + if (usename[j] != '\0') + { + elog(LOG, "Invalid user name '%s'", usename); + continue; + } - /* - * 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); + /* 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); - pfree(str_n); - pfree(str_p); - pfree(str_v); + 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); @@ -178,29 +234,154 @@ write_password_file(Relation rel) 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. + * + * This function set is both a trigger function for direct updates to pg_shadow + * as well as being called directly from create/alter/drop user. + * + * 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; + int bufsize; + FILE *fp; + mode_t oumask; + HeapScanDesc scan; + HeapTuple tuple; + TupleDesc dsc = RelationGetDescr(urel); /* - * Signal the postmaster to reload its password-file cache. + * 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. */ - SendPostmasterSignal(PMSIGNAL_PASSWORD_CHANGE); + filename = user_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_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 + * be allowed to connect under password authentication, because + * they need to have a password set up first. If you think + * 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); + + 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); } /* This is the wrapper for triggers. */ Datum -update_pg_pwd(PG_FUNCTION_ARGS) +update_pg_pwd_and_pg_group(PG_FUNCTION_ARGS) { /* * ExclusiveLock ensures no one modifies pg_shadow while we read it, * and that only one backend rewrites the flat file at a time. It's * OK to allow normal reads of pg_shadow in parallel, however. */ - Relation rel = heap_openr(ShadowRelationName, ExclusiveLock); + Relation urel = heap_openr(ShadowRelationName, ExclusiveLock); + Relation grel = heap_openr(GroupRelationName, ExclusiveLock); - write_password_file(rel); + write_user_file(urel); + write_group_file(urel, grel); /* OK to release lock, since we did not modify the relation */ - heap_close(rel, ExclusiveLock); + heap_close(grel, ExclusiveLock); + heap_close(urel, ExclusiveLock); + + /* + * Signal the postmaster to reload its password & group-file cache. + */ + SendPostmasterSignal(PMSIGNAL_PASSWORD_CHANGE); + return PointerGetDatum(NULL); } @@ -445,15 +626,15 @@ CreateUser(CreateUserStmt *stmt) AlterGroup(&ags, "CREATE USER"); } - /* - * 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); + + /* + * Write the updated pg_shadow and pg_group data to the flat file. + */ + update_pg_pwd_and_pg_group(NULL); } @@ -679,15 +860,15 @@ AlterUser(AlterUserStmt *stmt) ReleaseSysCache(tuple); 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); + + /* + * Write the updated pg_shadow and pg_group data to the flat file. + */ + update_pg_pwd_and_pg_group(NULL); } @@ -733,7 +914,7 @@ AlterUserSet(AlterUserSetStmt *stmt) { Datum datum; bool isnull; - ArrayType *a; + ArrayType *array; repl_null[Anum_pg_shadow_useconfig-1] = ' '; @@ -741,17 +922,17 @@ AlterUserSet(AlterUserSetStmt *stmt) Anum_pg_shadow_useconfig, &isnull); if (valuestr) - a = GUCArrayAdd(isnull + array = GUCArrayAdd(isnull ? NULL : (ArrayType *) pg_detoast_datum((struct varlena *)datum), stmt->variable, valuestr); else - a = GUCArrayDelete(isnull + array = GUCArrayDelete(isnull ? NULL : (ArrayType *) pg_detoast_datum((struct varlena *)datum), stmt->variable); - repl_val[Anum_pg_shadow_useconfig-1] = PointerGetDatum(a); + repl_val[Anum_pg_shadow_useconfig-1] = PointerGetDatum(array); } newtuple = heap_modifytuple(oldtuple, rel, repl_val, repl_null, repl_repl); @@ -846,7 +1027,7 @@ DropUser(DropUserStmt *stmt) datum = heap_getattr(tmp_tuple, Anum_pg_database_datname, pg_dsc, &null); Assert(!null); - dbname = DatumGetCString(DirectFunctionCall1(nameout, datum)); + dbname = (char *) DatumGetName(datum); elog(ERROR, "DROP USER: user \"%s\" owns database \"%s\", cannot be removed%s", user, dbname, (length(stmt->users) > 1) ? " (no users removed)" : ""); @@ -900,15 +1081,15 @@ DropUser(DropUserStmt *stmt) CommandCounterIncrement(); } - /* - * 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); + + /* + * Write the updated pg_shadow and pg_group data to the flat file. + */ + update_pg_pwd_and_pg_group(NULL); } @@ -1111,6 +1292,11 @@ CreateGroup(CreateGroupStmt *stmt) } heap_close(pg_group_rel, NoLock); + + /* + * Write the updated pg_shadow and pg_group data to the flat file. + */ + update_pg_pwd_and_pg_group(NULL); } @@ -1366,7 +1552,15 @@ AlterGroup(AlterGroupStmt *stmt, const char *tag) ReleaseSysCache(group_tuple); + /* + * Write the updated pg_shadow and pg_group data to the flat files. + */ heap_close(pg_group_rel, NoLock); + + /* + * Write the updated pg_shadow and pg_group data to the flat file. + */ + update_pg_pwd_and_pg_group(NULL); } @@ -1419,4 +1613,9 @@ DropGroup(DropGroupStmt *stmt) elog(ERROR, "DROP GROUP: group \"%s\" does not exist", stmt->name); heap_close(pg_group_rel, NoLock); + + /* + * Write the updated pg_shadow and pg_group data to the flat file. + */ + update_pg_pwd_and_pg_group(NULL); } diff --git a/src/backend/libpq/Makefile b/src/backend/libpq/Makefile index f5c2339f1c3..d0f05e802d9 100644 --- a/src/backend/libpq/Makefile +++ b/src/backend/libpq/Makefile @@ -4,7 +4,7 @@ # Makefile for libpq subsystem (backend half of libpq interface) # # IDENTIFICATION -# $Header: /cvsroot/pgsql/src/backend/libpq/Makefile,v 1.29 2002/03/04 01:46:02 tgl Exp $ +# $Header: /cvsroot/pgsql/src/backend/libpq/Makefile,v 1.30 2002/04/04 04:25:46 momjian Exp $ # #------------------------------------------------------------------------- @@ -14,9 +14,7 @@ include $(top_builddir)/src/Makefile.global # be-fsstubs is here for historical reasons, probably belongs elsewhere -OBJS = be-fsstubs.o \ - auth.o crypt.o hba.o md5.o password.o \ - pqcomm.o pqformat.o pqsignal.o +OBJS = be-fsstubs.o auth.o crypt.o hba.o md5.o pqcomm.o pqformat.o pqsignal.o all: SUBSYS.o diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c index 637e2a623eb..81a494905ea 100644 --- a/src/backend/libpq/auth.c +++ b/src/backend/libpq/auth.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.79 2002/03/05 07:57:45 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.80 2002/04/04 04:25:47 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -34,7 +34,6 @@ #include "miscadmin.h" static void sendAuthRequest(Port *port, AuthRequest areq); -static int checkPassword(Port *port, char *user, char *password); static int old_be_recvauth(Port *port); static int map_old_to_new(Port *port, UserAuth old, int status); static void auth_failed(Port *port, int status); @@ -381,7 +380,7 @@ recv_and_check_passwordv0(Port *port) saved = port->auth_method; port->auth_method = uaPassword; - status = checkPassword(port, user, password); + status = md5_crypt_verify(port, user, password); port->auth_method = saved; @@ -663,7 +662,7 @@ pam_passwd_conv_proc(int num_msg, const struct pam_message ** msg, struct pam_re initStringInfo(&buf); pq_getstr(&buf); - + /* Do not echo failed password to logs, for security. */ elog(DEBUG5, "received PAM packet"); @@ -810,26 +809,13 @@ recv_and_check_password_packet(Port *port) /* Do not echo failed password to logs, for security. */ elog(DEBUG5, "received password packet"); - result = checkPassword(port, port->user, buf.data); + result = md5_crypt_verify(port, port->user, buf.data); + pfree(buf.data); return result; } -/* - * Handle `password' and `crypt' records. If an auth argument was - * specified, use the respective file. Else use pg_shadow passwords. - */ -static int -checkPassword(Port *port, char *user, char *password) -{ - if (port->auth_arg[0] != '\0') - return verify_password(port, user, password); - - return md5_crypt_verify(port, user, password); -} - - /* * Server demux routine for incoming authentication information for protocol * version 0. diff --git a/src/backend/libpq/crypt.c b/src/backend/libpq/crypt.c index 6ab63ddffe0..7c665300c85 100644 --- a/src/backend/libpq/crypt.c +++ b/src/backend/libpq/crypt.c @@ -9,13 +9,12 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Header: /cvsroot/pgsql/src/backend/libpq/crypt.c,v 1.44 2002/03/04 01:46:03 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/libpq/crypt.c,v 1.45 2002/04/04 04:25:47 momjian Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" -#include #include #ifdef HAVE_CRYPT_H #include @@ -25,231 +24,10 @@ #include "libpq/libpq.h" #include "miscadmin.h" #include "storage/fd.h" +#include "nodes/pg_list.h" #include "utils/nabstime.h" -#define CRYPT_PWD_FILE "pg_pwd" - - -static char **pwd_cache = NULL; -static int pwd_cache_count = 0; - -/* - * crypt_getpwdfilename --- get full pathname of password file - * - * Note that result string is palloc'd, and should be freed by the caller. - */ -char * -crypt_getpwdfilename(void) -{ - int bufsize; - char *pfnam; - - bufsize = strlen(DataDir) + 8 + strlen(CRYPT_PWD_FILE) + 1; - pfnam = (char *) palloc(bufsize); - snprintf(pfnam, bufsize, "%s/global/%s", DataDir, CRYPT_PWD_FILE); - - return pfnam; -} - -/* - * Open the password file if possible (return NULL if not) - */ -static FILE * -crypt_openpwdfile(void) -{ - char *filename; - FILE *pwdfile; - - filename = crypt_getpwdfilename(); - pwdfile = AllocateFile(filename, "r"); - - if (pwdfile == NULL && errno != ENOENT) - elog(LOG, "could not open %s: %m", filename); - - pfree(filename); - - return pwdfile; -} - -/* - * Compare two password-file lines on the basis of their usernames. - * - * Can also be used to compare just a username against a password-file - * line (for bsearch). - */ -static int -compar_user(const void *user_a, const void *user_b) -{ - char *login_a; - char *login_b; - int len_a, - len_b, - result; - - login_a = *((char **) user_a); - login_b = *((char **) user_b); - - /* - * We only really want to compare the user logins which are first and - * are terminated by CRYPT_PWD_FILE_SEPSTR. (NB: this code - * effectively assumes that CRYPT_PWD_FILE_SEPSTR is just one char.) - */ - len_a = strcspn(login_a, CRYPT_PWD_FILE_SEPSTR); - len_b = strcspn(login_b, CRYPT_PWD_FILE_SEPSTR); - - result = strncmp(login_a, login_b, Min(len_a, len_b)); - - if (result == 0) /* one could be a prefix of the other */ - result = (len_a - len_b); - - return result; -} - -/* - * Load or reload the password-file cache - */ -void -load_password_cache(void) -{ - FILE *pwd_file; - char buffer[1024]; - - /* - * If for some reason we fail to open the password file, preserve the - * old cache contents; this seems better than dropping the cache if, - * say, we are temporarily out of filetable slots. - */ - if (!(pwd_file = crypt_openpwdfile())) - return; - - /* free any old data */ - if (pwd_cache) - { - while (--pwd_cache_count >= 0) - pfree(pwd_cache[pwd_cache_count]); - pfree(pwd_cache); - pwd_cache = NULL; - pwd_cache_count = 0; - } - - /* - * Read the file and store its lines in current memory context, which - * we expect will be PostmasterContext. That context will live as - * long as we need the cache to live, ie, until just after each - * postmaster child has completed client authentication. - */ - while (fgets(buffer, sizeof(buffer), pwd_file) != NULL) - { - int blen; - - /* - * We must remove the return char at the end of the string, as - * this will affect the correct parsing of the password entry. - */ - if (buffer[(blen = strlen(buffer) - 1)] == '\n') - buffer[blen] = '\0'; - - if (pwd_cache == NULL) - pwd_cache = (char **) - palloc(sizeof(char *) * (pwd_cache_count + 1)); - else - pwd_cache = (char **) - repalloc((void *) pwd_cache, - sizeof(char *) * (pwd_cache_count + 1)); - pwd_cache[pwd_cache_count++] = pstrdup(buffer); - } - - FreeFile(pwd_file); - - /* - * Now sort the entries in the cache for faster searching later. - */ - qsort((void *) pwd_cache, pwd_cache_count, sizeof(char *), compar_user); -} - -/* - * Parse a line of the password file to extract password and valid-until date. - */ -static bool -crypt_parsepwdentry(char *buffer, char **pwd, char **valdate) -{ - char *parse = buffer; - int count, - i; - - *pwd = NULL; - *valdate = NULL; - - /* - * skip to the password field - */ - for (i = 0; i < 6; i++) - { - parse += strcspn(parse, CRYPT_PWD_FILE_SEPSTR); - if (*parse == '\0') - return false; - parse++; - } - - /* - * store a copy of user password to return - */ - count = strcspn(parse, CRYPT_PWD_FILE_SEPSTR); - *pwd = (char *) palloc(count + 1); - memcpy(*pwd, parse, count); - (*pwd)[count] = '\0'; - parse += count; - if (*parse == '\0') - { - pfree(*pwd); - *pwd = NULL; - return false; - } - parse++; - - /* - * store a copy of the date login becomes invalid - */ - count = strcspn(parse, CRYPT_PWD_FILE_SEPSTR); - *valdate = (char *) palloc(count + 1); - memcpy(*valdate, parse, count); - (*valdate)[count] = '\0'; - - return true; -} - -/* - * Lookup a username in the password-file cache, - * return his password and valid-until date. - */ -static bool -crypt_getloginfo(const char *user, char **passwd, char **valuntil) -{ - *passwd = NULL; - *valuntil = NULL; - - if (pwd_cache) - { - char **pwd_entry; - - pwd_entry = (char **) bsearch((void *) &user, - (void *) pwd_cache, - pwd_cache_count, - sizeof(char *), - compar_user); - if (pwd_entry) - { - if (crypt_parsepwdentry(*pwd_entry, passwd, valuntil)) - return true; - } - } - - return false; -} - -/*-------------------------------------------------------------------------*/ - int md5_crypt_verify(const Port *port, const char *user, const char *pgpass) { @@ -257,10 +35,14 @@ md5_crypt_verify(const Port *port, const char *user, const char *pgpass) *valuntil, *crypt_pwd; int retval = STATUS_ERROR; + List **line; - if (!crypt_getloginfo(user, &passwd, &valuntil)) + if ((line = get_user_line(user)) == NULL) return STATUS_ERROR; + passwd = lfirst(lnext(lnext(*line))); + valuntil = lfirst(lnext(lnext(lnext(*line)))); + if (passwd == NULL || *passwd == '\0') { if (passwd) diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c index b86cb817302..fce63ab2436 100644 --- a/src/backend/libpq/hba.c +++ b/src/backend/libpq/hba.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/libpq/hba.c,v 1.80 2002/03/04 01:46:03 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/libpq/hba.c,v 1.81 2002/04/04 04:25:47 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -30,18 +30,20 @@ #include #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 MAX_TOKEN 80 -/* Maximum size of one token in the configuration file */ - #define IDENT_USERNAME_MAX 512 /* Max size of username ident server can return */ +/* This is used to separate values in multi-valued column strings */ +#define MULTI_VALUE_SEP "\001" + /* * These variables hold the pre-parsed contents of the hba and ident * configuration files. Each is a list of sublists, one sublist for @@ -53,7 +55,17 @@ */ static List *hba_lines = NIL; /* pre-parsed contents of hba file */ static List *ident_lines = NIL; /* pre-parsed contents of ident file */ +static List *group_lines = NIL; /* pre-parsed contents of group file */ +static List *user_lines = NIL; /* pre-parsed contents of user password file */ +/* sorted entries so we can do binary search lookups */ +static List **user_sorted = NULL; /* sorted user list, for bsearch() */ +static List **group_sorted = NULL; /* sorted group list, for bsearch() */ +static int user_length; +static int group_length; + +static List *tokenize_file(FILE *file); +static char *tokenize_inc_file(const char *inc_filename); /* * Some standard C libraries, including GNU, have an isblank() function. @@ -67,41 +79,76 @@ isblank(const char c) /* - * Grab one token out of fp. Tokens are strings of non-blank - * characters bounded by blank characters, beginning of line, and end - * of line. Blank means space or tab. Return the token as *buf. - * Leave file positioned to character immediately after the token or - * EOF, whichever comes first. If no more tokens on line, return null - * string as *buf and position file to beginning of next line or EOF, - * whichever comes first. + * Grab one token out of fp. Tokens are strings of non-blank + * characters bounded by blank characters, beginning of line, and + * end of line. Blank means space or tab. Return the token as + * *buf. Leave file positioned to character immediately after the + * token or EOF, whichever comes first. If no more tokens on line, + * return null string as *buf and position file to beginning of + * next line or EOF, whichever comes first. Allow spaces in quoted + * strings. Terminate on unquoted commas. Handle comments. */ -static void +void next_token(FILE *fp, char *buf, const int bufsz) { int c; - char *eb = buf + (bufsz - 1); + char *end_buf = buf + (bufsz - 1); + bool in_quote = false; + bool was_quote = false; - /* Move over initial token-delimiting blanks */ - while ((c = getc(fp)) != EOF && isblank(c)) + /* Move over initial whitespace and commas */ + while ((c = getc(fp)) != EOF && (isblank(c) || c == ',')) ; if (c != EOF && c != '\n') { /* - * build a token in buf of next characters up to EOF, eol, or - * blank. If the token gets too long, we still parse it - * correctly, but the excess characters are not stored into *buf. + * Build a token in buf of next characters up to EOF, EOL, unquoted + * comma, or unquoted whitespace. */ - while (c != EOF && c != '\n' && !isblank(c)) + while (c != EOF && c != '\n' && + (!isblank(c) || in_quote == true)) { - if (buf < eb) + if (c == '"') + in_quote = !in_quote; + + /* skip comments to EOL */ + if (c == '#' && !in_quote) + { + while ((c = getc(fp)) != EOF && c != '\n') + ; + continue; + } + + if (buf >= end_buf) + { + elog(LOG, "Token too long in authentication file, skipping, %s", buf); + /* Discard remainder of line */ + while ((c = getc(fp)) != EOF && c != '\n') + ; + buf[0] = '\0'; + break; + } + + if (c != '"' || (c == '"' && was_quote)) *buf++ = c; + + /* We pass back the comma so the caller knows there is more */ + if ((isblank(c) || c == ',') && !in_quote) + break; + + /* Literal double-quote is two double-quotes */ + if (c == '"') + was_quote = !was_quote; + else + was_quote = false; + c = getc(fp); } /* * Put back the char right after the token (critical in case it is - * eol, since we need to detect end-of-line at next call). + * EOL, since we need to detect end-of-line at next call). */ if (c != EOF) ungetc(c, fp); @@ -109,74 +156,57 @@ next_token(FILE *fp, char *buf, const int bufsz) *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. + * 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 List * -tokenize_file(FILE *file) +static char * +next_token_expand(FILE *file) { - List *lines = NIL; - List *next_line = NIL; - int line_number = 1; char buf[MAX_TOKEN]; - char *comment_ptr; + char *comma_str = pstrdup(""); + bool trailing_comma; + char *incbuf; - while (!feof(file)) + do { next_token(file, buf, sizeof(buf)); + if (!*buf) + break; - /* 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 (buf[strlen(buf)-1] == ',') { - if (next_line == NIL) - { - /* make a new line List */ - next_line = makeListi1(line_number); - lines = lappend(lines, next_line); - } - /* append token to current line's list */ - next_line = lappend(next_line, pstrdup(buf)); + 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) { - /* we are at real or logical eol, so force a new line List */ - next_line = NIL; + comma_str = repalloc(comma_str, strlen(comma_str) + 1 + 1); + strcat(comma_str, MULTI_VALUE_SEP); } + } while (trailing_comma); - 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++; - } - - return lines; + return comma_str; } /* - * Free memory used by lines/tokens (ie, structure built by tokenize_file) + * Free memory used by lines/tokens (i.e., structure built by tokenize_file) */ static void free_lines(List **lines) @@ -204,6 +234,220 @@ free_lines(List **lines) } +static char * +tokenize_inc_file(const char *inc_filename) +{ + char *inc_fullname; + FILE *inc_file; + List *inc_lines; + List *line; + char *comma_str = pstrdup(""); + + inc_fullname = (char *) palloc(strlen(DataDir) + 1 + + strlen(inc_filename) + 1); + strcpy(inc_fullname, DataDir); + strcat(inc_fullname, "/"); + strcat(inc_fullname, inc_filename); + + inc_file = AllocateFile(inc_fullname, "r"); + if (!inc_file) + { + elog(LOG, "tokenize_inc_file: Unable to open secondary authentication file \"@%s\" as \"%s\": %m", + inc_filename, inc_fullname); + pfree(inc_fullname); + + /* return empty string, it matches nothing */ + return pstrdup(""); + } + pfree(inc_fullname); + + /* There is possible recursion here if the file contains @ */ + inc_lines = tokenize_file(inc_file); + FreeFile(inc_file); + + /* Create comma-separate string from List */ + foreach(line, inc_lines) + { + List *ln = lfirst(line); + List *token; + + /* First entry is line number */ + foreach(token, lnext(ln)) + { + if (strlen(comma_str)) + { + comma_str = repalloc(comma_str, strlen(comma_str) + 1); + strcat(comma_str, MULTI_VALUE_SEP); + } + comma_str = repalloc(comma_str, + strlen(comma_str) + strlen(lfirst(token)) + 1); + strcat(comma_str, lfirst(token)); + } + } + + free_lines(&inc_lines); + + return comma_str; +} + + + +/* + * Read the given file and create a list of line sublists. + */ +static List * +tokenize_file(FILE *file) +{ + 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) + { + /* make a new line List */ + next_line = makeListi1(line_number); + 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++; + } + + return lines; +} + + +/* + * 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, MULTI_VALUE_SEP); tok != NULL; tok = strtok(NULL, MULTI_VALUE_SEP)) + { + 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, MULTI_VALUE_SEP); tok != NULL; tok = strtok(NULL, MULTI_VALUE_SEP)) + { + if (strcmp(tok, "all") == 0) + return 1; + else if (strcmp(tok, "sameuser") == 0) + { + if (strcmp(dbname, user) == 0) + return 1; + } + else if (strcmp(tok, "samegroup") == 0) + { + if (check_group(dbname, user)) + return 1; + } + else if (strcmp(tok, dbname) == 0) + return 1; + } + return 0; +} + + /* * Scan the rest of a host record (after the mask field) * and return the interpretation of it as *userauth_p, auth_arg, and @@ -278,6 +522,7 @@ parse_hba(List *line, hbaPort *port, bool *found_p, bool *error_p) int line_number; char *token; char *db; + char *user; Assert(line != NIL); line_number = lfirsti(line); @@ -293,10 +538,17 @@ parse_hba(List *line, hbaPort *port, bool *found_p, bool *error_p) goto hba_syntax; db = lfirst(line); - /* Read the rest of the line. */ + /* Get the user. */ line = lnext(line); if (!line) goto hba_syntax; + user = lfirst(line); + + line = lnext(line); + if (!line) + goto hba_syntax; + + /* Read the rest of the line. */ parse_hba_auth(line, &port->auth_method, port->auth_arg, error_p); if (*error_p) goto hba_syntax; @@ -308,15 +560,7 @@ parse_hba(List *line, hbaPort *port, bool *found_p, bool *error_p) port->auth_method == uaKrb5) goto hba_syntax; - /* - * If this record doesn't match the parameters of the connection - * attempt, ignore it. - */ - if ((strcmp(db, port->database) != 0 && - strcmp(db, "all") != 0 && - (strcmp(db, "sameuser") != 0 || - strcmp(port->database, port->user) != 0)) || - port->raddr.sa.sa_family != AF_UNIX) + if (port->raddr.sa.sa_family != AF_UNIX) return; } else if (strcmp(token, "host") == 0 || strcmp(token, "hostssl") == 0) @@ -347,6 +591,12 @@ parse_hba(List *line, hbaPort *port, bool *found_p, bool *error_p) goto hba_syntax; db = lfirst(line); + /* Get the user. */ + line = lnext(line); + if (!line) + goto hba_syntax; + user = lfirst(line); + /* Read the IP address field. */ line = lnext(line); if (!line) @@ -371,21 +621,19 @@ parse_hba(List *line, hbaPort *port, bool *found_p, bool *error_p) if (*error_p) goto hba_syntax; - /* - * If this record doesn't match the parameters of the connection - * attempt, ignore it. - */ - if ((strcmp(db, port->database) != 0 && - strcmp(db, "all") != 0 && - (strcmp(db, "sameuser") != 0 || - strcmp(port->database, port->user) != 0)) || - port->raddr.sa.sa_family != AF_INET || + /* Must meet network restrictions */ + if (port->raddr.sa.sa_family != AF_INET || ((file_ip_addr.s_addr ^ port->raddr.in.sin_addr.s_addr) & mask.s_addr) != 0) return; } else goto hba_syntax; + if (!check_db(port->database, port->user, db)) + return; + if (!check_user(port->user, user)) + return; + /* Success */ *found_p = true; return; @@ -430,6 +678,127 @@ check_hba(hbaPort *port) } + +/* + * Open the group file if possible (return NULL if not) + */ +static FILE * +group_openfile(void) +{ + char *filename; + FILE *groupfile; + + filename = group_getfilename(); + groupfile = AllocateFile(filename, "r"); + + if (groupfile == NULL && errno != ENOENT) + elog(LOG, "could not open %s: %m", filename); + + pfree(filename); + + return groupfile; +} + + + +/* + * Open the password file if possible (return NULL if not) + */ +static FILE * +user_openfile(void) +{ + char *filename; + FILE *pwdfile; + + filename = user_getfilename(); + pwdfile = AllocateFile(filename, "r"); + + if (pwdfile == NULL && errno != ENOENT) + elog(LOG, "could not open %s: %m", filename); + + pfree(filename); + + return pwdfile; +} + + + +/* + * Load group/user name mapping file + */ +void +load_group() +{ + FILE *group_file; + List *line; + + if (group_lines) + free_lines(&group_lines); + + group_file = group_openfile(); + if (!group_file) + return; + group_lines = tokenize_file(group_file); + FreeFile(group_file); + + /* create sorted lines for binary searching */ + if (group_sorted) + pfree(group_sorted); + group_length = length(group_lines); + if (group_length) + { + int i = 0; + + group_sorted = palloc(group_length * sizeof(List *)); + + foreach(line, group_lines) + group_sorted[i++] = lfirst(line); + + qsort((void *) group_sorted, group_length, sizeof(List *), user_group_cmp); + } + else + group_sorted = NULL; +} + + +/* + * Load user/password mapping file + */ +void +load_user() +{ + FILE *user_file; + List *line; + + if (user_lines) + free_lines(&user_lines); + + user_file = user_openfile(); + if (!user_file) + return; + user_lines = tokenize_file(user_file); + FreeFile(user_file); + + /* create sorted lines for binary searching */ + if (user_sorted) + pfree(user_sorted); + user_length = length(user_lines); + if (user_length) + { + int i = 0; + + user_sorted = palloc(user_length * sizeof(List *)); + + foreach(line, user_lines) + user_sorted[i++] = lfirst(line); + + qsort((void *) user_sorted, user_length, sizeof(List *), user_group_cmp); + } + else + user_sorted = NULL; +} + + /* * Read the config file and create a List of Lists of tokens in the file. * If we find a file by the old name of the config file (pg_hba), we issue @@ -437,60 +806,35 @@ check_hba(hbaPort *port) * follow directions and just installed his old hba file in the new database * system. */ -static void +void load_hba(void) { - int fd, - bufsize; + int bufsize; FILE *file; /* The config file we have to read */ - char *old_conf_file; + char *conf_file; /* The name of the config file */ if (hba_lines) free_lines(&hba_lines); - /* - * The name of old config file that better not exist. Fail if config - * file by old name exists. Put together the full pathname to the old - * config file. - */ - bufsize = (strlen(DataDir) + strlen(OLD_CONF_FILE) + 2) * sizeof(char); - old_conf_file = (char *) palloc(bufsize); - snprintf(old_conf_file, bufsize, "%s/%s", DataDir, OLD_CONF_FILE); + /* Put together the full pathname to the config file. */ + bufsize = (strlen(DataDir) + strlen(CONF_FILE) + 2) * sizeof(char); + conf_file = (char *) palloc(bufsize); + snprintf(conf_file, bufsize, "%s/%s", DataDir, CONF_FILE); - if ((fd = open(old_conf_file, O_RDONLY | PG_BINARY, 0)) != -1) + file = AllocateFile(conf_file, "r"); + if (file == NULL) { - /* Old config file exists. Tell this guy he needs to upgrade. */ - close(fd); - elog(LOG, "A file exists by the name used for host-based authentication " - "in prior releases of Postgres (%s). The name and format of " - "the configuration file have changed, so this file should be " - "converted.", old_conf_file); + /* The open of the config file failed. */ + elog(LOG, "load_hba: Unable to open authentication config file \"%s\": %m", + conf_file); + pfree(conf_file); } else { - char *conf_file; /* The name of the config file we have to - * read */ - - /* put together the full pathname to the config file */ - bufsize = (strlen(DataDir) + strlen(CONF_FILE) + 2) * sizeof(char); - conf_file = (char *) palloc(bufsize); - snprintf(conf_file, bufsize, "%s/%s", DataDir, CONF_FILE); - - file = AllocateFile(conf_file, "r"); - if (file == NULL) - { - /* The open of the config file failed. */ - elog(LOG, "load_hba: Unable to open authentication config file \"%s\": %m", - conf_file); - } - else - { - hba_lines = tokenize_file(file); - FreeFile(file); - } - pfree(conf_file); + hba_lines = tokenize_file(file); + FreeFile(file); } - pfree(old_conf_file); + pfree(conf_file); } @@ -606,7 +950,7 @@ check_ident_usermap(const char *usermap_name, /* * Read the ident config file and create a List of Lists of tokens in the file. */ -static void +void load_ident(void) { FILE *file; /* The map file we have to read */ @@ -622,7 +966,7 @@ load_ident(void) map_file = (char *) palloc(bufsize); snprintf(map_file, bufsize, "%s/%s", DataDir, USERMAP_FILE); - file = AllocateFile(map_file, PG_BINARY_R); + file = AllocateFile(map_file, "r"); if (file == NULL) { /* The open of the map file failed. */ @@ -640,8 +984,8 @@ load_ident(void) /* * Parse the string "*ident_response" as a response from a query to an Ident - * server. If it's a normal response indicating a username, return true - * and store the username at *ident_user. If it's anything else, + * server. If it's a normal response indicating a user name, return true + * and store the user name at *ident_user. If it's anything else, * return false. */ static bool @@ -708,7 +1052,7 @@ interpret_ident_response(char *ident_response, cursor++; /* Go over colon */ while (isblank(*cursor)) cursor++; /* skip blanks */ - /* Rest of line is username. Copy it over. */ + /* Rest of line is user name. Copy it over. */ i = 0; while (*cursor != '\r' && i < IDENT_USERNAME_MAX) ident_user[i++] = *cursor++; @@ -725,7 +1069,7 @@ interpret_ident_response(char *ident_response, /* * Talk to the ident server on host "remote_ip_addr" and find out who * owns the tcp connection from his port "remote_port" to port - * "local_port_addr" on host "local_ip_addr". Return the username the + * "local_port_addr" on host "local_ip_addr". Return the user name the * ident server gives as "*ident_user". * * IP addresses and port numbers are in network byte order. @@ -955,6 +1299,8 @@ ident_unix(int sock, char *ident_user) #endif } + + /* * Determine the username of the initiator of the connection described * by "port". Then look in the usermap file under the usermap @@ -1010,211 +1356,3 @@ hba_getauthmethod(hbaPort *port) return STATUS_ERROR; } -/* - * Clear and reload tokenized file contents. - */ -void -load_hba_and_ident(void) -{ - load_hba(); - load_ident(); -} - - -/* Character set stuff. Not sure it really belongs in this file. */ - -#ifdef CYR_RECODE - -#define CHARSET_FILE "charset.conf" -#define MAX_CHARSETS 10 -#define KEY_HOST 1 -#define KEY_BASE 2 -#define KEY_TABLE 3 - -struct CharsetItem -{ - char Orig[MAX_TOKEN]; - char Dest[MAX_TOKEN]; - char Table[MAX_TOKEN]; -}; - - -static bool -CharSetInRange(char *buf, int host) -{ - int valid, - i, - FromAddr, - ToAddr, - tmp; - struct in_addr file_ip_addr; - char *p; - unsigned int one = 0x80000000, - NetMask = 0; - unsigned char mask; - - p = strchr(buf, '/'); - if (p) - { - *p++ = '\0'; - valid = inet_aton(buf, &file_ip_addr); - if (valid) - { - mask = strtoul(p, 0, 0); - FromAddr = ntohl(file_ip_addr.s_addr); - ToAddr = ntohl(file_ip_addr.s_addr); - for (i = 0; i < mask; i++) - { - NetMask |= one; - one >>= 1; - } - FromAddr &= NetMask; - ToAddr = ToAddr | ~NetMask; - tmp = ntohl(host); - return ((unsigned) tmp >= (unsigned) FromAddr && - (unsigned) tmp <= (unsigned) ToAddr); - } - } - else - { - p = strchr(buf, '-'); - if (p) - { - *p++ = '\0'; - valid = inet_aton(buf, &file_ip_addr); - if (valid) - { - FromAddr = ntohl(file_ip_addr.s_addr); - valid = inet_aton(p, &file_ip_addr); - if (valid) - { - ToAddr = ntohl(file_ip_addr.s_addr); - tmp = ntohl(host); - return ((unsigned) tmp >= (unsigned) FromAddr && - (unsigned) tmp <= (unsigned) ToAddr); - } - } - } - else - { - valid = inet_aton(buf, &file_ip_addr); - if (valid) - { - FromAddr = file_ip_addr.s_addr; - return (unsigned) FromAddr == (unsigned) host; - } - } - } - return false; -} - -void -GetCharSetByHost(char *TableName, int host, const char *DataDir) -{ - FILE *file; - char buf[MAX_TOKEN], - BaseCharset[MAX_TOKEN], - OrigCharset[MAX_TOKEN], - DestCharset[MAX_TOKEN], - HostCharset[MAX_TOKEN], - *map_file; - int key, - ChIndex = 0, - c, - i, - bufsize; - struct CharsetItem *ChArray[MAX_CHARSETS]; - - *TableName = '\0'; - bufsize = (strlen(DataDir) + strlen(CHARSET_FILE) + 2) * sizeof(char); - map_file = (char *) palloc(bufsize); - snprintf(map_file, bufsize, "%s/%s", DataDir, CHARSET_FILE); - file = AllocateFile(map_file, PG_BINARY_R); - pfree(map_file); - if (file == NULL) - { - /* XXX should we log a complaint? */ - return; - } - while ((c = getc(file)) != EOF) - { - if (c == '#') - read_through_eol(file); - else - { - /* Read the key */ - ungetc(c, file); - next_token(file, buf, sizeof(buf)); - if (buf[0] != '\0') - { - key = 0; - if (strcasecmp(buf, "HostCharset") == 0) - key = KEY_HOST; - if (strcasecmp(buf, "BaseCharset") == 0) - key = KEY_BASE; - if (strcasecmp(buf, "RecodeTable") == 0) - key = KEY_TABLE; - switch (key) - { - case KEY_HOST: - /* Read the host */ - next_token(file, buf, sizeof(buf)); - if (buf[0] != '\0') - { - if (CharSetInRange(buf, host)) - { - /* Read the charset */ - next_token(file, buf, sizeof(buf)); - if (buf[0] != '\0') - strcpy(HostCharset, buf); - } - } - break; - case KEY_BASE: - /* Read the base charset */ - next_token(file, buf, sizeof(buf)); - if (buf[0] != '\0') - strcpy(BaseCharset, buf); - break; - case KEY_TABLE: - /* Read the original charset */ - next_token(file, buf, sizeof(buf)); - if (buf[0] != '\0') - { - strcpy(OrigCharset, buf); - /* Read the destination charset */ - next_token(file, buf, sizeof(buf)); - if (buf[0] != '\0') - { - strcpy(DestCharset, buf); - /* Read the table filename */ - next_token(file, buf, sizeof(buf)); - if (buf[0] != '\0') - { - ChArray[ChIndex] = - (struct CharsetItem *) palloc(sizeof(struct CharsetItem)); - strcpy(ChArray[ChIndex]->Orig, OrigCharset); - strcpy(ChArray[ChIndex]->Dest, DestCharset); - strcpy(ChArray[ChIndex]->Table, buf); - ChIndex++; - } - } - } - break; - } - read_through_eol(file); - } - } - } - FreeFile(file); - - for (i = 0; i < ChIndex; i++) - { - if (strcasecmp(BaseCharset, ChArray[i]->Orig) == 0 && - strcasecmp(HostCharset, ChArray[i]->Dest) == 0) - strncpy(TableName, ChArray[i]->Table, 79); - pfree(ChArray[i]); - } -} - -#endif /* CYR_RECODE */ diff --git a/src/backend/libpq/password.c b/src/backend/libpq/password.c deleted file mode 100644 index f8490f8bd57..00000000000 --- a/src/backend/libpq/password.c +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * $Id: password.c,v 1.41 2002/03/04 01:46:03 tgl Exp $ - * - */ - -#include -#include - -#include "postgres.h" - -#ifdef HAVE_CRYPT_H -#include -#endif - -#include "libpq/libpq.h" -#include "libpq/password.h" -#include "libpq/crypt.h" -#include "miscadmin.h" -#include "storage/fd.h" - - -int -verify_password(const Port *port, const char *user, const char *password) -{ - char *pw_file_fullname; - FILE *pw_file; - - pw_file_fullname = (char *) palloc(strlen(DataDir) + strlen(port->auth_arg) + 2); - strcpy(pw_file_fullname, DataDir); - strcat(pw_file_fullname, "/"); - strcat(pw_file_fullname, port->auth_arg); - - pw_file = AllocateFile(pw_file_fullname, PG_BINARY_R); - if (!pw_file) - { - elog(LOG, "verify_password: Unable to open password file \"%s\": %m", - pw_file_fullname); - - pfree(pw_file_fullname); - - return STATUS_ERROR; - } - - pfree(pw_file_fullname); - - while (!feof(pw_file)) - { - char pw_file_line[255], - *p, - *test_user, - *test_pw; - - if (fgets(pw_file_line, sizeof(pw_file_line), pw_file) == NULL) - pw_file_line[0] = '\0'; - /* kill the newline */ - if (strlen(pw_file_line) > 0 && - pw_file_line[strlen(pw_file_line) - 1] == '\n') - pw_file_line[strlen(pw_file_line) - 1] = '\0'; - - p = pw_file_line; - - test_user = strtok(p, ":"); - if (!test_user || test_user[0] == '\0') - continue; - test_pw = strtok(NULL, ":"); - - if (strcmp(user, test_user) == 0) - { - /* we're outta here one way or the other, so close file */ - FreeFile(pw_file); - - /* - * If the password is empty or "+" then we use the regular - * pg_shadow passwords. If we use crypt then we have to use - * pg_shadow passwords no matter what. This is because the - * current code needs non-encrypted passwords to encrypt with - * a random salt. - */ - if (port->auth_method == uaMD5 || - port->auth_method == uaCrypt || - test_pw == NULL || - test_pw[0] == '\0' || - strcmp(test_pw, "+") == 0) - return md5_crypt_verify(port, user, password); - - /* external password file is crypt-only */ - if (strcmp(crypt(password, test_pw), test_pw) == 0) - { - /* it matched. */ - return STATUS_OK; - } - - elog(LOG, "verify_password: password mismatch for '%s'", - user); - - return STATUS_ERROR; - } - } - - FreeFile(pw_file); - - elog(LOG, "verify_password: user '%s' not found in password file", - user); - - return STATUS_ERROR; -} diff --git a/src/backend/libpq/pg_hba.conf.sample b/src/backend/libpq/pg_hba.conf.sample index 669588e517c..05e6959b4de 100644 --- a/src/backend/libpq/pg_hba.conf.sample +++ b/src/backend/libpq/pg_hba.conf.sample @@ -42,22 +42,36 @@ # # Format: # -# host DBNAME IP_ADDRESS ADDRESS_MASK AUTH_TYPE [AUTH_ARGUMENT] +# host DATABASE USER IP_ADDRESS MASK AUTH_TYPE # -# 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 +# 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 '@' # -# IP_ADDRESS and ADDRESS_MASK are standard dotted decimal IP address and +# 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 and AUTH_ARGUMENT are described below. +# AUTH_TYPE is described below. # # # hostssl @@ -65,10 +79,8 @@ # # The format of this record is identical to "host". # -# -# -# It specifies hosts that required connection via secure SSL. "host" -# records allow SSL connections too, but "hostssl" only allows SSL-secured +# It specifies hosts that require connection via secure SSL. "host" +# allows SSL connections too, but "hostssl" requires SSL-secured # connections. # # This keyword is only available if the server was compiled with SSL @@ -82,10 +94,10 @@ # connections. Without this record, UNIX-socket connections are disallowed # # Format: -# local DBNAME AUTH_TYPE [AUTH_ARGUMENT] +# local DATABASE USER AUTH_TYPE # # This format is identical to the "host" record type except there are no -# IP_ADDRESS and ADDRESS_MASK fields. +# IP_ADDRESS and MASK fields. # # # @@ -96,57 +108,38 @@ # has an AUTH_TYPE. # # trust: -# No authentication is done. Any valid username is accepted, +# No authentication is done. Any valid user name is accepted, # including the PostgreSQL superuser. This option should # be used only for hosts where all users are trusted. # -# password: -# Authentication is done by matching a password supplied -# in clear by the host. If no AUTH_ARGUMENT is used, the -# password is compared with the user's entry in the -# pg_shadow table. -# -# If AUTH_ARGUMENT is specified, the username is looked up -# in that file in the $PGDATA directory. If the username -# is found but there is no password, the password is looked -# up in pg_shadow. If a password exists in the file, it is -# used instead. These secondary files allow fine-grained -# control over who can access which databases and whether -# a non-default password is required. The same file can be -# used in multiple records for easier administration. -# Password files can be maintained with the pg_passwd(1) -# utility. Remember, these passwords override pg_shadow -# passwords. Also, such passwords are passed over the network -# in cleartext, meaning this should not be used on untrusted -# networks. -# # md5: -# Same as "password", except the password is encrypted over the -# network. This method is preferable to "password" and "crypt" -# except for pre-7.2 clients that don't support it. NOTE: md5 can -# use usernames stored in secondary password files but ignores -# passwords stored there. The pg_shadow password will always be -# used. +# Requires the client to supply an MD5 encrypted password for +# authentication. This is the only method that allows encrypted +# passwords to be stored in pg_shadow. # # crypt: -# Same as "md5", but uses crypt for pre-7.2 clients. You can -# not store encrypted passwords in pg_shadow if you use this -# method. +# Same as "md5", but uses crypt for pre-7.2 clients. # +# password: +# Same as "md5", but the password is sent in cleartext over +# the network. This should not be used on untrusted +# networks. +# # ident: # For TCP/IP connections, authentication is done by contacting the # ident server on the client host. This is only as secure as the -# client machine. On machines that support unix-domain socket -# credentials (currently Linux, FreeBSD, NetBSD, and BSD/OS), this -# method also works for "local" connections. +# client machine. You must specify the map name after the 'ident' +# keyword. It determines how to map remote user names to +# PostgreSQL user names. If you use "sameuser", the user names are +# assumed to be identical. If not, the map name is looked up +# in the $PGDATA/pg_ident.conf file. The connection is accepted if +# that file contains an entry for this map name with the +# ident-supplied username and the requested PostgreSQL username. # -# AUTH_ARGUMENT is required. It determines how to map remote user -# names to PostgreSQL user names. If you use "sameuser", the user -# names are assumed to be the identical. If not, AUTH_ARGUMENT is -# assumed to be a map name found in the $PGDATA/pg_ident.conf -# file. The connection is accepted if that file contains an entry -# for this map name with the ident-supplied username and the -# requested PostgreSQL username. +# On machines that support unix-domain socket credentials +# (currently Linux, FreeBSD, NetBSD, and BSD/OS), ident allows +# reliable authentication of 'local' connections without ident +# running on the local machine. # # krb4: # Kerberos V4 authentication is used. Allowed only for @@ -157,10 +150,10 @@ # TCP/IP connections, not for local UNIX-domain sockets. # # pam: -# Authentication is passed off to PAM (PostgreSQL must be -# configured --with-pam), using the default service name -# "postgresql" - you can specify your own service name by -# setting AUTH_ARGUMENT to the desired service name. +# Authentication is done by PAM using the default service name +# "postgresql". You can specify your own service name by adding +# the service name after the 'pam' keyword. To use this option, +# PostgreSQL must be configured --with-pam. # # reject: # Reject the connection. This is used to reject certain hosts @@ -177,60 +170,70 @@ # Allow any user on the local system to connect to any database under any # username using Unix-domain sockets (the default for local connections): # -# TYPE DATABASE IP_ADDRESS MASK AUTH_TYPE AUTH_ARGUMENT -# local all trust +# TYPE DATABASE USER IP_ADDRESS MASK AUTH_TYPE +# local all all trust # # The same using local loopback TCP/IP connections: # -# TYPE DATABASE IP_ADDRESS MASK AUTH_TYPE AUTH_ARGUMENT -# host all 127.0.0.1 255.255.255.255 trust +# TYPE DATABASE USER IP_ADDRESS MASK AUTH_TYPE +# host all all 127.0.0.1 255.255.255.255 trust # # Allow any user from any host with IP address 192.168.93.x to # connect to database "template1" as the same username that ident reports # for the connection (typically his Unix username): # -# TYPE DATABASE IP_ADDRESS MASK AUTH_TYPE AUTH_ARGUMENT -# host template1 192.168.93.0 255.255.255.0 ident sameuser +# TYPE DATABASE USER IP_ADDRESS MASK AUTH_TYPE +# host template1 all 192.168.93.0 255.255.255.0 ident sameuser # # Allow a user from host 192.168.12.10 to connect to database "template1" -# if the user's password in pg_shadow is correctly supplied: +# if the user's password is correctly supplied: # -# TYPE DATABASE IP_ADDRESS MASK AUTH_TYPE AUTH_ARGUMENT -# host template1 192.168.12.10 255.255.255.255 md5 +# TYPE DATABASE USER IP_ADDRESS MASK AUTH_TYPE +# host template1 all 192.168.12.10 255.255.255.255 md5 # # In the absence of preceding "host" lines, these two lines will reject # all connection from 192.168.54.1 (since that entry will be matched # first), but allow Kerberos V5 connections from anywhere else on the # Internet. The zero mask means that no bits of the host IP address are -# considered, so it matches any host: +# considered so it matches any host: # # -# TYPE DATABASE IP_ADDRESS MASK AUTH_TYPE AUTH_ARGUMENT -# host all 192.168.54.1 255.255.255.255 reject -# host all 0.0.0.0 0.0.0.0 krb5 +# TYPE DATABASE USER IP_ADDRESS MASK AUTH_TYPE +# host all all 192.168.54.1 255.255.255.255 reject +# host all all 0.0.0.0 0.0.0.0 krb5 # # Allow users from 192.168.x.x hosts to connect to any database if they # pass the ident check. For example, if ident says the user is "james" and # he requests to connect as PostgreSQL user "guest", the connection is # allowed if there is an entry in $PGDATA/pg_ident.conf with map name # "phoenix" that says "james" is allowed to connect as "guest": -# -# 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. +# +# 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,7 +253,7 @@ # configuration is probably too liberal for you. Change it to use # something other than "trust" authentication. # -# TYPE DATABASE IP_ADDRESS MASK AUTH_TYPE AUTH_ARGUMENT +# TYPE DATABASE USER IP_ADDRESS MASK AUTH_TYPE -local all trust -host all 127.0.0.1 255.255.255.255 trust +local all all trust +host all all 127.0.0.1 255.255.255.255 trust diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index a6d6097a54c..0ce817b5b39 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -37,7 +37,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.271 2002/03/15 19:20:35 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.272 2002/04/04 04:25:48 momjian Exp $ * * NOTES * @@ -748,8 +748,10 @@ PostmasterMain(int argc, char *argv[]) /* * Load cached files for client authentication. */ - load_hba_and_ident(); - load_password_cache(); + load_hba(); + load_ident(); + load_user(); + load_group(); /* * We're ready to rock and roll... @@ -1389,7 +1391,8 @@ SIGHUP_handler(SIGNAL_ARGS) elog(LOG, "Received SIGHUP, reloading configuration files"); SignalChildren(SIGHUP); ProcessConfigFile(PGC_SIGHUP); - load_hba_and_ident(); + load_hba(); + load_ident(); } PG_SETMASK(&UnBlockSig); @@ -2288,9 +2291,10 @@ sigusr1_handler(SIGNAL_ARGS) if (CheckPostmasterSignal(PMSIGNAL_PASSWORD_CHANGE)) { /* - * Password file has changed. + * Password or group file has changed. */ - load_password_cache(); + load_user(); + load_group(); } if (CheckPostmasterSignal(PMSIGNAL_WAKEN_CHILDREN)) diff --git a/src/backend/utils/adt/quote.c b/src/backend/utils/adt/quote.c index 39a1ec4df16..537e97a50ee 100644 --- a/src/backend/utils/adt/quote.c +++ b/src/backend/utils/adt/quote.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/quote.c,v 1.6 2001/10/28 06:25:53 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/quote.c,v 1.7 2002/04/04 04:25:49 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -124,8 +124,6 @@ do_quote_ident(text *iptr) { if (*cp1 == '"') *cp2++ = '"'; - if (*cp1 == '\\') - *cp2++ = '\\'; *cp2++ = *cp1++; } *cp2++ = '"'; @@ -234,8 +232,6 @@ do_quote_ident(text *iptr) if (*cp1 == '"') *cp2++ = '"'; - if (*cp1 == '\\') - *cp2++ = '\\'; *cp2++ = *cp1++; len--; diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index dceb8b9cd6f..9ef8b1c87c5 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/init/miscinit.c,v 1.85 2002/03/04 04:45:27 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/init/miscinit.c,v 1.86 2002/04/04 04:25:49 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -236,85 +236,17 @@ pg_convert2(PG_FUNCTION_ARGS) #ifdef CYR_RECODE -#define MAX_TOKEN 80 - -/* - * Some standard C libraries, including GNU, have an isblank() function. - * Others, including Solaris, do not. So we have our own. - */ -static bool -isblank(const char c) -{ - return c == ' ' || c == '\t'; -} - - -/* - * Grab one token out of fp. Tokens are strings of non-blank - * characters bounded by blank characters, beginning of line, and end - * of line. Blank means space or tab. Return the token as *buf. - * Leave file positioned to character immediately after the token or - * EOF, whichever comes first. If no more tokens on line, return null - * string as *buf and position file to beginning of next line or EOF, - * whichever comes first. - */ -static void -next_token(FILE *fp, char *buf, const int bufsz) -{ - int c; - char *eb = buf + (bufsz - 1); - - /* Move over initial token-delimiting blanks */ - while ((c = getc(fp)) != EOF && isblank(c)) - ; - - if (c != EOF && c != '\n') - { - /* - * build a token in buf of next characters up to EOF, eol, or - * blank. If the token gets too long, we still parse it - * correctly, but the excess characters are not stored into *buf. - */ - while (c != EOF && c != '\n' && !isblank(c)) - { - if (buf < eb) - *buf++ = c; - c = getc(fp); - } - - /* - * Put back the char right after the token (critical in case it is - * eol, since we need to detect end-of-line at next call). - */ - if (c != EOF) - ungetc(c, fp); - } - *buf = '\0'; -} - - -static void -read_through_eol(FILE *file) -{ - int c; - - while ((c = getc(file)) != EOF && c != '\n') - ; -} - - -void SetCharSet(void) { FILE *file; - char *p; + char *filename; char *map_file; char buf[MAX_TOKEN]; int i, c; unsigned char FromChar, ToChar; - char ChTable[80]; + char ChTable[MAX_TOKEN]; for (i = 0; i < 128; i++) { @@ -325,39 +257,40 @@ SetCharSet(void) if (IsUnderPostmaster) { GetCharSetByHost(ChTable, MyProcPort->raddr.in.sin_addr.s_addr, DataDir); - p = ChTable; + filename = ChTable; } else - p = getenv("PG_RECODETABLE"); + filename = getenv("PG_RECODETABLE"); - if (p && *p != '\0') + if (filename && *filename != '\0') { - map_file = palloc(strlen(DataDir) + strlen(p) + 2); - sprintf(map_file, "%s/%s", DataDir, p); - file = AllocateFile(map_file, PG_BINARY_R); + map_file = palloc(strlen(DataDir) + strlen(filename) + 2); + sprintf(map_file, "%s/%s", DataDir, filename); + file = AllocateFile(map_file, "r"); pfree(map_file); if (file == NULL) return; - while ((c = getc(file)) != EOF) + + while (!feof(file)) { - if (c == '#') - read_through_eol(file); - else + next_token(file, buf, sizeof(buf)); + if (buf[0] != '\0') { - /* Read the FromChar */ - ungetc(c, file); + FromChar = strtoul(buf, 0, 0); + /* Read the ToChar */ next_token(file, buf, sizeof(buf)); if (buf[0] != '\0') { - FromChar = strtoul(buf, 0, 0); - /* Read the ToChar */ - next_token(file, buf, sizeof(buf)); - if (buf[0] != '\0') + ToChar = strtoul(buf, 0, 0); + RecodeForwTable[FromChar - 128] = ToChar; + RecodeBackTable[ToChar - 128] = FromChar; + + /* read to EOL */ + while (!feof(file) && buf[0]) { - ToChar = strtoul(buf, 0, 0); - RecodeForwTable[FromChar - 128] = ToChar; - RecodeBackTable[ToChar - 128] = FromChar; - read_through_eol(file); + next_token(file, buf, sizeof(buf)); + elog(LOG, "SetCharSet: unknown tag %s in file %s" + buf, filename); } } } @@ -366,6 +299,7 @@ SetCharSet(void) } } + char * convertstr(unsigned char *buff, int len, int dest) { @@ -384,7 +318,206 @@ convertstr(unsigned char *buff, int len, int dest) } return ch; } -#endif + +#define CHARSET_FILE "charset.conf" +#define MAX_CHARSETS 10 +#define KEY_HOST 1 +#define KEY_BASE 2 +#define KEY_TABLE 3 + +struct CharsetItem +{ + char Orig[MAX_TOKEN]; + char Dest[MAX_TOKEN]; + char Table[MAX_TOKEN]; +}; + + +static bool +CharSetInRange(char *buf, int host) +{ + int valid, + i, + FromAddr, + ToAddr, + tmp; + struct in_addr file_ip_addr; + char *p; + unsigned int one = 0x80000000, + NetMask = 0; + unsigned char mask; + + p = strchr(buf, '/'); + if (p) + { + *p++ = '\0'; + valid = inet_aton(buf, &file_ip_addr); + if (valid) + { + mask = strtoul(p, 0, 0); + FromAddr = ntohl(file_ip_addr.s_addr); + ToAddr = ntohl(file_ip_addr.s_addr); + for (i = 0; i < mask; i++) + { + NetMask |= one; + one >>= 1; + } + FromAddr &= NetMask; + ToAddr = ToAddr | ~NetMask; + tmp = ntohl(host); + return ((unsigned) tmp >= (unsigned) FromAddr && + (unsigned) tmp <= (unsigned) ToAddr); + } + } + else + { + p = strchr(buf, '-'); + if (p) + { + *p++ = '\0'; + valid = inet_aton(buf, &file_ip_addr); + if (valid) + { + FromAddr = ntohl(file_ip_addr.s_addr); + valid = inet_aton(p, &file_ip_addr); + if (valid) + { + ToAddr = ntohl(file_ip_addr.s_addr); + tmp = ntohl(host); + return ((unsigned) tmp >= (unsigned) FromAddr && + (unsigned) tmp <= (unsigned) ToAddr); + } + } + } + else + { + valid = inet_aton(buf, &file_ip_addr); + if (valid) + { + FromAddr = file_ip_addr.s_addr; + return (unsigned) FromAddr == (unsigned) host; + } + } + } + return false; +} + + +static void +GetCharSetByHost(char *TableName, int host, const char *DataDir) +{ + FILE *file; + char buf[MAX_TOKEN], + BaseCharset[MAX_TOKEN], + OrigCharset[MAX_TOKEN], + DestCharset[MAX_TOKEN], + HostCharset[MAX_TOKEN], + *map_file; + int key, + ChIndex = 0, + c, + i, + bufsize; + struct CharsetItem *ChArray[MAX_CHARSETS]; + + *TableName = '\0'; + bufsize = (strlen(DataDir) + strlen(CHARSET_FILE) + 2) * sizeof(char); + map_file = (char *) palloc(bufsize); + snprintf(map_file, bufsize, "%s/%s", DataDir, CHARSET_FILE); + file = AllocateFile(map_file, "r"); + pfree(map_file); + if (file == NULL) + { + /* XXX should we log a complaint? */ + return; + } + + while (!feof(file)) + { + next_token(file, buf, sizeof(buf)); + if (buf[0] != '\0') + { + key = 0; + if (strcasecmp(buf, "HostCharset") == 0) + key = KEY_HOST; + else if (strcasecmp(buf, "BaseCharset") == 0) + key = KEY_BASE; + else if (strcasecmp(buf, "RecodeTable") == 0) + key = KEY_TABLE; + else + elog(LOG, "GetCharSetByHost: unknown tag %s in file %s" + buf, CHARSET_FILE); + + switch (key) + { + case KEY_HOST: + /* Read the host */ + next_token(file, buf, sizeof(buf)); + if (buf[0] != '\0') + { + if (CharSetInRange(buf, host)) + { + /* Read the charset */ + next_token(file, buf, sizeof(buf)); + if (buf[0] != '\0') + strcpy(HostCharset, buf); + } + } + break; + case KEY_BASE: + /* Read the base charset */ + next_token(file, buf, sizeof(buf)); + if (buf[0] != '\0') + strcpy(BaseCharset, buf); + break; + case KEY_TABLE: + /* Read the original charset */ + next_token(file, buf, sizeof(buf)); + if (buf[0] != '\0') + { + strcpy(OrigCharset, buf); + /* Read the destination charset */ + next_token(file, buf, sizeof(buf)); + if (buf[0] != '\0') + { + strcpy(DestCharset, buf); + /* Read the table filename */ + next_token(file, buf, sizeof(buf)); + if (buf[0] != '\0') + { + ChArray[ChIndex] = + (struct CharsetItem *) palloc(sizeof(struct CharsetItem)); + strcpy(ChArray[ChIndex]->Orig, OrigCharset); + strcpy(ChArray[ChIndex]->Dest, DestCharset); + strcpy(ChArray[ChIndex]->Table, buf); + ChIndex++; + } + } + } + break; + } + + /* read to EOL */ + while (!feof(file) && buf[0]) + { + next_token(file, buf, sizeof(buf)); + elog(LOG, "GetCharSetByHost: unknown tag %s in file %s" + buf, CHARSET_FILE); + } + } + } + FreeFile(file); + + for (i = 0; i < ChIndex; i++) + { + if (strcasecmp(BaseCharset, ChArray[i]->Orig) == 0 && + strcasecmp(HostCharset, ChArray[i]->Dest) == 0) + strncpy(TableName, ChArray[i]->Table, 79); + pfree(ChArray[i]); + } +} + +#endif /* CYR_RECODE */ diff --git a/src/bin/Makefile b/src/bin/Makefile index a1d4b1e8fc0..7a293c35a26 100644 --- a/src/bin/Makefile +++ b/src/bin/Makefile @@ -5,7 +5,7 @@ # Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group # Portions Copyright (c) 1994, Regents of the University of California # -# $Header: /cvsroot/pgsql/src/bin/Makefile,v 1.34 2001/02/18 18:33:59 momjian Exp $ +# $Header: /cvsroot/pgsql/src/bin/Makefile,v 1.35 2002/04/04 04:25:50 momjian Exp $ # #------------------------------------------------------------------------- @@ -14,7 +14,7 @@ top_builddir = ../.. include $(top_builddir)/src/Makefile.global DIRS := initdb initlocation ipcclean pg_ctl pg_dump pg_id \ - pg_passwd psql scripts pg_config + psql scripts pg_config ifdef MULTIBYTE DIRS += pg_encoding diff --git a/src/bin/initdb/initdb.sh b/src/bin/initdb/initdb.sh index e028ac1b10e..30f7de1f201 100644 --- a/src/bin/initdb/initdb.sh +++ b/src/bin/initdb/initdb.sh @@ -27,7 +27,7 @@ # Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group # Portions Copyright (c) 1994, Regents of the University of California # -# $Header: /cvsroot/pgsql/src/bin/initdb/Attic/initdb.sh,v 1.146 2002/04/03 05:39:32 petere Exp $ +# $Header: /cvsroot/pgsql/src/bin/initdb/Attic/initdb.sh,v 1.147 2002/04/04 04:25:50 momjian Exp $ # #------------------------------------------------------------------------- @@ -603,9 +603,11 @@ $ECHO_N "initializing pg_shadow... "$ECHO_C "$PGPATH"/postgres $PGSQL_OPT template1 >/dev/null <&2 exit_nicely fi + if [ ! -f "$PGDATA"/global/pg_group ]; then + echo + echo "The group file wasn't generated. Please report this problem." 1>&2 + exit_nicely + fi echo "ok" fi diff --git a/src/bin/pg_passwd/Makefile b/src/bin/pg_passwd/Makefile deleted file mode 100644 index f6f4acd0118..00000000000 --- a/src/bin/pg_passwd/Makefile +++ /dev/null @@ -1,37 +0,0 @@ -# $Header: /cvsroot/pgsql/src/bin/pg_passwd/Attic/Makefile,v 1.14 2001/05/12 19:49:47 petere Exp $ - -subdir = src/bin/pg_passwd -top_builddir = ../../.. -include $(top_builddir)/src/Makefile.global - -OBJS = pg_passwd.o -ifdef STRDUP -OBJS += $(top_builddir)/src/utils/strdup.o -endif - -all: pg_passwd - -pg_passwd: $(OBJS) - $(CC) $(CFLAGS) $(LDFLAGS) $^ $(LIBS) -o $@ - -$(top_builddir)/src/utils/strdup.o: - $(MAKE) -C $(top_builddir)/src/utils strdup.o - -install: all installdirs - $(INSTALL_PROGRAM) pg_passwd$(X) $(DESTDIR)$(bindir)/pg_passwd$(X) - -installdirs: - $(mkinstalldirs) $(DESTDIR)$(bindir) - -uninstall: - rm -f $(DESTDIR)$(bindir)/pg_passwd$(X) - -depend dep: - $(CC) -MM $(CFLAGS) *.c >depend - -clean distclean maintainer-clean: - rm -f pg_passwd$(X) pg_passwd.o - -ifeq (depend,$(wildcard depend)) -include depend -endif diff --git a/src/bin/pg_passwd/pg_passwd.c b/src/bin/pg_passwd/pg_passwd.c deleted file mode 100644 index 831c3823708..00000000000 --- a/src/bin/pg_passwd/pg_passwd.c +++ /dev/null @@ -1,412 +0,0 @@ -/* - * @(#) pg_passwd.c 1.8 09:13:16 97/07/02 Y. Ichikawa - */ -#include "postgres_fe.h" - -#include -#include -#include -#include -#define issaltchar(c) (isalnum((unsigned char) (c)) || (c) == '.' || (c) == '/') - -#ifdef HAVE_TERMIOS_H -#include -#endif -#ifdef HAVE_CRYPT_H -#include -#else -extern char *crypt(const char *, const char *); -#endif - -/* - * We assume that the output of crypt(3) is always 13 characters, - * and that at most 8 characters can usefully be sent to it. - * - * Postgres usernames are assumed to be less than NAMEDATALEN chars long. - */ -#define CLEAR_PASSWD_LEN 8 /* not including null */ -#define CRYPTED_PASSWD_LEN 13 /* not including null */ - -const char *progname; - -static void usage(void); -static void read_pwd_file(char *filename); -static void write_pwd_file(char *filename, char *bkname); -static void encrypt_pwd(char key[CLEAR_PASSWD_LEN + 1], - char salt[3], - char passwd[CRYPTED_PASSWD_LEN + 1]); -static void prompt_for_username(char *username); -static void prompt_for_password(char *prompt, char *password); - -static void -usage(void) -{ - printf("%s manipulates flat text password files for PostgreSQL.\n\n", progname); - printf("Usage:\n %s PASSWORD-FILE\n\n", progname); - printf("Report bugs to .\n"); -} - -typedef struct -{ - char *uname; - char *pwd; - char *rest; -} pg_pwd; - -#define MAXPWDS 1024 - -pg_pwd pwds[MAXPWDS]; -int npwds = 0; - - -static void -read_pwd_file(char *filename) -{ - FILE *fp; - static char line[512]; - static char ans[128]; - int i; - -try_again: - fp = fopen(filename, PG_BINARY_R); - if (fp == NULL) - { - if (errno == ENOENT) - { - printf("File \"%s\" does not exist. Create? (y/n): ", filename); - fflush(stdout); - if (fgets(ans, sizeof(ans), stdin) == NULL) - exit(1); - switch (ans[0]) - { - case 'y': - case 'Y': - fp = fopen(filename, PG_BINARY_W); - if (fp == NULL) - { - perror(filename); - exit(1); - } - fclose(fp); - goto try_again; - default: - /* cannot continue */ - exit(1); - } - } - else - { - perror(filename); - exit(1); - } - } - - /* read all the entries */ - for (npwds = 0; - npwds < MAXPWDS && fgets(line, sizeof(line), fp) != NULL; - ++npwds) - { - int l; - char *p, - *q; - - l = strlen(line); - if (line[l - 1] == '\n') - line[l - 1] = '\0'; - else - { - fprintf(stderr, "%s:%d: line too long\n", - filename, npwds + 1); - exit(1); - } - - /* get user name */ - p = line; - if ((q = strchr(p, ':')) != NULL) - *q = '\0'; - - if (strlen(p) == 0) - { - fprintf(stderr, "%s:%d: null user name\n", - filename, npwds + 1); - exit(1); - } - pwds[npwds].uname = strdup(p); - - /* check for duplicate user name */ - for (i = 0; i < npwds; ++i) - { - if (strcmp(pwds[i].uname, pwds[npwds].uname) == 0) - { - fprintf(stderr, "Duplicate username %s in entry %d\n", - pwds[npwds].uname, npwds + 1); - exit(1); - } - } - - /* get password field */ - if (q) - { - p = q + 1; - q = strchr(p, ':'); - - if (q != NULL) - *(q++) = '\0'; - - if (strlen(p) != CRYPTED_PASSWD_LEN && strcmp(p, "+") != 0) - { - fprintf(stderr, "%s:%d: warning: invalid password length\n", - filename, npwds + 1); - } - pwds[npwds].pwd = strdup(p); - } - else - pwds[npwds].pwd = NULL; - - /* rest of the line is treated as is */ - if (q == NULL) - pwds[npwds].rest = NULL; - else - pwds[npwds].rest = strdup(q); - } - - fclose(fp); -} - -static void -write_pwd_file(char *filename, char *bkname) -{ - FILE *fp; - int i; - - /* make the backup file */ -link_again: - if (link(filename, bkname)) - { - if (errno == EEXIST) - { - unlink(bkname); - goto link_again; - } - perror(bkname); - exit(1); - } - if (unlink(filename)) - { - perror(filename); - exit(1); - } - - /* open file */ - if ((fp = fopen(filename, PG_BINARY_W)) == NULL) - { - perror(filename); - exit(1); - } - - /* write file */ - for (i = 0; i < npwds; ++i) - { - fprintf(fp, "%s", pwds[i].uname); - if (pwds[i].pwd) - fprintf(fp, ":%s", pwds[i].pwd); - if (pwds[i].rest) - fprintf(fp, ":%s", pwds[i].rest); - fprintf(fp, "\n"); - } - - fclose(fp); -} - -static void -encrypt_pwd(char key[CLEAR_PASSWD_LEN + 1], - char salt[3], - char passwd[CRYPTED_PASSWD_LEN + 1]) -{ - int n; - - /* select a salt, if not already given */ - if (salt[0] == '\0') - { - srand(time(NULL)); - do - { - n = rand() % 256; - } while (!issaltchar(n)); - salt[0] = n; - do - { - n = rand() % 256; - } while (!issaltchar(n)); - salt[1] = n; - salt[2] = '\0'; - } - - /* get encrypted password */ - strcpy(passwd, crypt(key, salt)); - -#ifdef PG_PASSWD_DEBUG - /* show it */ - fprintf(stderr, "key = %s, salt = %s, password = %s\n", - key, salt, passwd); -#endif -} - -static void -prompt_for_username(char *username) -{ - int length; - - printf("Username: "); - fflush(stdout); - if (fgets(username, NAMEDATALEN, stdin) == NULL) - username[0] = '\0'; - - length = strlen(username); - if (length > 0 && username[length - 1] != '\n') - { - /* eat rest of the line */ - char buf[128]; - int buflen; - - do - { - if (fgets(buf, sizeof(buf), stdin) == NULL) - break; - buflen = strlen(buf); - } while (buflen > 0 && buf[buflen - 1] != '\n'); - } - if (length > 0 && username[length - 1] == '\n') - username[length - 1] = '\0'; -} - -static void -prompt_for_password(char *prompt, char *password) -{ - int length; - -#ifdef HAVE_TERMIOS_H - struct termios t_orig, - t; -#endif - -#ifdef HAVE_TERMIOS_H - tcgetattr(0, &t); - t_orig = t; - t.c_lflag &= ~ECHO; - tcsetattr(0, TCSADRAIN, &t); -#endif - - printf(prompt); - fflush(stdout); - - if (fgets(password, CLEAR_PASSWD_LEN + 1, stdin) == NULL) - password[0] = '\0'; - -#ifdef HAVE_TERMIOS_H - tcsetattr(0, TCSADRAIN, &t_orig); -#endif - - length = strlen(password); - if (length > 0 && password[length - 1] != '\n') - { - /* eat rest of the line */ - char buf[128]; - int buflen; - - do - { - if (fgets(buf, sizeof(buf), stdin) == NULL) - break; - buflen = strlen(buf); - } while (buflen > 0 && buf[buflen - 1] != '\n'); - } - if (length > 0 && password[length - 1] == '\n') - password[length - 1] = '\0'; - printf("\n"); -} - - -int -main(int argc, char *argv[]) -{ - char *filename; - char bkname[MAXPGPATH]; - char username[NAMEDATALEN]; - char salt[3]; - char key[CLEAR_PASSWD_LEN + 1], - key2[CLEAR_PASSWD_LEN + 1]; - char e_passwd[CRYPTED_PASSWD_LEN + 1]; - int i; - - progname = argv[0]; - - if (argc != 2) - { - fprintf(stderr, "%s: too %s arguments\nTry '%s --help' for more information.\n", - progname, argc > 2 ? "many" : "few", progname); - exit(1); - } - - if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) - { - usage(); - exit(0); - } - if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) - { - puts("pg_passwd (PostgreSQL) " PG_VERSION); - exit(0); - } - if (argv[1][0] == '-') - { - fprintf(stderr, "%s: invalid option: %s\nTry '%s --help' for more information.\n", - progname, argv[1], progname); - exit(1); - } - - filename = argv[1]; - - /* open file */ - read_pwd_file(filename); - - /* ask for the user name and the password */ - prompt_for_username(username); - prompt_for_password("New password: ", key); - prompt_for_password("Re-enter new password: ", key2); - if (strcmp(key, key2) != 0) - { - fprintf(stderr, "Password mismatch\n"); - exit(1); - } - salt[0] = '\0'; - encrypt_pwd(key, salt, e_passwd); - - /* check password entry */ - for (i = 0; i < npwds; ++i) - { - if (strcmp(pwds[i].uname, username) == 0) - { /* found */ - pwds[i].pwd = strdup(e_passwd); - break; - } - } - if (i == npwds) - { /* did not exist */ - if (npwds == MAXPWDS) - { - fprintf(stderr, "Cannot handle so many entries\n"); - exit(1); - } - pwds[npwds].uname = strdup(username); - pwds[npwds].pwd = strdup(e_passwd); - pwds[npwds].rest = NULL; - ++npwds; - } - - /* write back the file */ - sprintf(bkname, "%s.bk", filename); - write_pwd_file(filename, bkname); - - return 0; -} diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index ac32bd69aa0..cd939a9c10a 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pg_proc.h,v 1.224 2002/03/29 19:06:19 tgl Exp $ + * $Id: pg_proc.h,v 1.225 2002/04/04 04:25:52 momjian Exp $ * * NOTES * The script catalog/genbki.sh reads this file and generates .bki @@ -2101,8 +2101,8 @@ DESCR("does not match LIKE expression, case-insensitive"); DATA(insert OID = 1637 ( like_escape PGUID 12 f t t t 2 f 25 "25 25" 100 0 0 100 like_escape - _null_ )); DESCR("convert match pattern to use backslash escapes"); -DATA(insert OID = 1689 ( update_pg_pwd PGUID 12 f t f t 0 f 0 "" 100 0 0 100 update_pg_pwd - _null_ )); -DESCR("update pg_pwd file"); +DATA(insert OID = 1689 ( update_pg_pwd_and_pg_group PGUID 12 f t f t 0 f 0 "" 100 0 0 100 update_pg_pwd_and_pg_group - _null_ )); +DESCR("update pg_pwd and pg_group files"); /* Oracle Compatibility Related Functions - By Edmund Mergl */ DATA(insert OID = 868 ( strpos PGUID 12 f t t t 2 f 23 "25 25" 100 0 0 100 textpos - _null_ )); diff --git a/src/include/commands/user.h b/src/include/commands/user.h index 351c2d6ef6d..046e022ae8f 100644 --- a/src/include/commands/user.h +++ b/src/include/commands/user.h @@ -3,15 +3,23 @@ * user.h * * - * $Id: user.h,v 1.17 2002/03/01 22:45:17 petere Exp $ + * $Id: user.h,v 1.18 2002/04/04 04:25:53 momjian Exp $ * *------------------------------------------------------------------------- */ #ifndef USER_H #define USER_H +#include "fmgr.h" #include "nodes/parsenodes.h" +#define PWD_FILE "pg_pwd" + +#define USER_GROUP_FILE "pg_group" + + +extern char *group_getfilename(void); +extern char *user_getfilename(void); extern void CreateUser(CreateUserStmt *stmt); extern void AlterUser(AlterUserStmt *stmt); extern void AlterUserSet(AlterUserSetStmt *stmt); @@ -21,6 +29,6 @@ extern void CreateGroup(CreateGroupStmt *stmt); extern void AlterGroup(AlterGroupStmt *stmt, const char *tag); extern void DropGroup(DropGroupStmt *stmt); -extern Datum update_pg_pwd(PG_FUNCTION_ARGS); +extern Datum update_pg_pwd_and_pg_group(PG_FUNCTION_ARGS); #endif /* USER_H */ diff --git a/src/include/libpq/crypt.h b/src/include/libpq/crypt.h index 458be1fd2cf..a521a0e2cee 100644 --- a/src/include/libpq/crypt.h +++ b/src/include/libpq/crypt.h @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: crypt.h,v 1.19 2001/11/12 01:52:46 momjian Exp $ + * $Id: crypt.h,v 1.20 2002/04/04 04:25:53 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -15,8 +15,6 @@ #include "libpq/libpq-be.h" -#define CRYPT_PWD_FILE_SEPSTR "\t" - /* Also defined in interfaces/odbc/md5.h */ #define MD5_PASSWD_LEN 35 @@ -24,9 +22,6 @@ strlen(passwd) == MD5_PASSWD_LEN) -extern char *crypt_getpwdfilename(void); -extern void load_password_cache(void); - extern int md5_crypt_verify(const Port *port, const char *user, const char *pgpass); extern bool md5_hash(const void *buff, size_t len, char *hexsum); diff --git a/src/include/libpq/hba.h b/src/include/libpq/hba.h index 0d5ddbaf702..b9daf985f5c 100644 --- a/src/include/libpq/hba.h +++ b/src/include/libpq/hba.h @@ -4,7 +4,7 @@ * Interface to hba.c * * - * $Id: hba.h,v 1.31 2001/11/05 17:46:33 momjian Exp $ + * $Id: hba.h,v 1.32 2002/04/04 04:25:54 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -15,15 +15,14 @@ #include #endif +#include "nodes/pg_list.h" + #define CONF_FILE "pg_hba.conf" /* Name of the config file */ #define USERMAP_FILE "pg_ident.conf" /* Name of the usermap file */ -#define OLD_CONF_FILE "pg_hba" - /* Name of the config file in prior releases of Postgres. */ - #define IDENT_PORT 113 /* Standard TCP port number for Ident service. Assigned by IANA */ @@ -46,8 +45,15 @@ typedef enum UserAuth typedef struct Port hbaPort; +#define MAX_TOKEN 256 + +extern void next_token(FILE *fp, char *buf, const int bufsz); +extern List **get_user_line(const char *user); +extern void load_hba(void); +extern void load_ident(void); +extern void load_user(void); +extern void load_group(void); extern int hba_getauthmethod(hbaPort *port); extern int authident(hbaPort *port); -extern void load_hba_and_ident(void); #endif diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h index 4fb64473924..99f7fae88bf 100644 --- a/src/include/miscadmin.h +++ b/src/include/miscadmin.h @@ -12,7 +12,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: miscadmin.h,v 1.101 2002/03/04 01:46:04 tgl Exp $ + * $Id: miscadmin.h,v 1.102 2002/04/04 04:25:51 momjian Exp $ * * NOTES * some of the information in this file should be moved to @@ -219,7 +219,6 @@ extern int FindExec(char *full_path, const char *argv0, extern int CheckPathAccess(char *path, char *name, int open_mode); #ifdef CYR_RECODE -extern void GetCharSetByHost(char *TableName, int host, const char *DataDir); extern void SetCharSet(void); extern char *convertstr(unsigned char *buff, int len, int dest); #endif diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out index be08da82371..39130f2a5e3 100644 --- a/src/test/regress/expected/opr_sanity.out +++ b/src/test/regress/expected/opr_sanity.out @@ -30,7 +30,7 @@ WHERE (p1.prolang = 0 OR p1.prorettype = 0 OR AND p1.proname !~ '^pl[^_]+_call_handler$' AND p1.proname !~ '^RI_FKey_' AND p1.proname !~ 'costestimate$' - AND p1.proname != 'update_pg_pwd'; + AND p1.proname != 'update_pg_pwd_and_pg_group'; oid | proname -----+--------- (0 rows) diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql index 7a5991a74a1..6a95c4cd1be 100644 --- a/src/test/regress/sql/opr_sanity.sql +++ b/src/test/regress/sql/opr_sanity.sql @@ -33,7 +33,7 @@ WHERE (p1.prolang = 0 OR p1.prorettype = 0 OR AND p1.proname !~ '^pl[^_]+_call_handler$' AND p1.proname !~ '^RI_FKey_' AND p1.proname !~ 'costestimate$' - AND p1.proname != 'update_pg_pwd'; + AND p1.proname != 'update_pg_pwd_and_pg_group'; -- Look for conflicting proc definitions (same names and input datatypes). -- (This test should be dead code now that we have the unique index