From 554e56e6280f380f1c55d487aa4b8ea75fa4daad Mon Sep 17 00:00:00 2001 From: Peter Eisentraut Date: Tue, 4 Jul 2000 16:32:01 +0000 Subject: [PATCH] Expand secondary password file feature, so that you can use these files to restrict the set of users that can connect to a database but can still use the pg_shadow password. (You just leave off the password field in the secondary file.) --- doc/src/sgml/client-auth.sgml | 102 +++++++++++++++++++++------------- src/backend/libpq/auth.c | 18 ++---- src/backend/libpq/crypt.c | 4 +- src/backend/libpq/password.c | 30 ++++++---- src/bin/pg_passwd/pg_passwd.c | 49 ++++++++-------- src/include/libpq/crypt.h | 2 +- src/include/libpq/password.h | 2 +- 7 files changed, 115 insertions(+), 92 deletions(-) diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml index b8970f27ec1..500fc6ea3fa 100644 --- a/doc/src/sgml/client-auth.sgml +++ b/doc/src/sgml/client-auth.sgml @@ -1,4 +1,4 @@ - + Client Authentication @@ -202,15 +202,15 @@ host all 192.168.2.0 255.255.255.0 ident othermap password - The client is required to supply a password for the connection + The client is required to supply a password with the connection attempt which is required to match the password that was set up - for the user. (These passwords are separate from any operating - sytem password.) + for the user. - An optional password file may be specified after the - password keyword to obtain the password from - that file rather than the pg_shadow system catalog. + An optional file name may be specified after the + password keyword. This file is expected to + contain a list of users that this record pertains to, and + optionally alternative passwords. The password is sent over the wire in clear text. For better @@ -225,11 +225,11 @@ host all 192.168.2.0 255.255.255.0 ident othermap Like the password method, but the password is sent over the wire encrypted using a simple - challenge-response protocol. Note that this is still not + challenge-response protocol. This is still not cryptographically secure but it protects against incidental - wire-sniffing. Interestingly enough, the - crypt does not support secondary password - files. + wire-sniffing. The name of a file may follow the + crypt keyword that contains a list of users + that this record pertains to. @@ -276,24 +276,36 @@ host all 192.168.2.0 255.255.255.0 ident othermap Password authentication - 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 explicitly been set up, the stored password is - NULL and password authentication will always fail - for that user. + Postgres database passwords are separate from any + 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 + explicitly been set up, the stored password is NULL + and password authentication will always fail for that user. - Secondary password files can be used if a given set of passwords - should only apply to a particular database or set thereof. - Secondary password files have a format similar to the standard - Unix password file /etc/passwd, that is, - + 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 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 (as long as he passes password authentication, of course). + + + + These files can also be used a 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 @@ -303,20 +315,29 @@ host all 192.168.2.0 255.255.255.0 ident othermap - Secondary password files can also be used to restrict certain - users from connecting to certain databases at all. This is - currently not possible to achieve using the normal password - mechanism (because users and passwords are global across all - databases). If a user is not listed in the applicable password - file the connection will be refused. + Lines with and without passwords can be mixed in secondary + password files. Lines without password indicate use 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. - Note that using secondary password files means that one can no - longer use ALTER USER to change one's password. - It will still appear to work but the password one is actually - changing is not the password that the system will end up using. + Alternative passwords cannot be used when using the + crypt method. The file will still be evaluated as + usual but the password field will simply be ignored and the + pg_shadow password will 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 still appear to work but the password one is + actually changing is not the password that the system will end up + using. + + @@ -361,14 +382,15 @@ integrated here. --> The Identification Protocol is described in RFC 1413. Virtually every Unix-like operating systems ships with an ident server that listens on TCP - port 113 by default. The basic functionality of the ident server + port 113 by default. The basic functionality of an ident server is to answer questions like What user initiated the connection that goes out of your port X and connects to my port Y?. - Since both X and - Y are known, - Postgres could theoretically determine - the operating system user for any given connection this way. + Since Postgres knows both X and + Y when a physical connection is established, it + can interrogate the ident server on the host of the connecting + client and could theoretically determine the operating system user + for any given connection this way. diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c index 5cd049062cb..4f0dc6a31a0 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.47 2000/05/27 04:13:05 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.48 2000/07/04 16:31:53 petere Exp $ * *------------------------------------------------------------------------- */ @@ -52,9 +52,6 @@ static void auth_failed(Port *port); #ifdef KRB4 -/* This has to be ifdef'd out because krb.h does exist. This needs - to be fixed. -*/ /*---------------------------------------------------------------- * MIT Kerberos authentication system - protocol version 4 *---------------------------------------------------------------- @@ -141,9 +138,6 @@ pg_krb4_recvauth(Port *port) #ifdef KRB5 -/* This needs to be ifdef'd out because krb5.h doesn't exist. This needs - to be fixed. -*/ /*---------------------------------------------------------------- * MIT Kerberos authentication system - protocol version 5 *---------------------------------------------------------------- @@ -692,16 +686,14 @@ readPasswordPacket(void *arg, PacketLen len, void *pkt) /* - * Use the local flat password file if clear passwords are used and the file is - * specified. Otherwise use the password in the pg_shadow table, encrypted or - * not. + * 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_method == uaPassword && port->auth_arg[0] != '\0') - return verify_password(port->auth_arg, user, password); + if (port->auth_arg[0] != '\0') + return verify_password(port, user, password); return crypt_verify(port, user, password); } diff --git a/src/backend/libpq/crypt.c b/src/backend/libpq/crypt.c index 38b8e0ed383..8b9eace73ef 100644 --- a/src/backend/libpq/crypt.c +++ b/src/backend/libpq/crypt.c @@ -9,7 +9,7 @@ * Dec 17, 1997 - Todd A. Brandys * Orignal Version Completed. * - * $Id: crypt.c,v 1.26 2000/07/03 20:48:30 petere Exp $ + * $Id: crypt.c,v 1.27 2000/07/04 16:31:53 petere Exp $ * *------------------------------------------------------------------------- */ @@ -249,7 +249,7 @@ crypt_getloginfo(const char *user, char **passwd, char **valuntil) /*-------------------------------------------------------------------------*/ int -crypt_verify(Port *port, const char *user, const char *pgpass) +crypt_verify(const Port *port, const char *user, const char *pgpass) { char *passwd, diff --git a/src/backend/libpq/password.c b/src/backend/libpq/password.c index c7656f8b98f..6f47d5d4378 100644 --- a/src/backend/libpq/password.c +++ b/src/backend/libpq/password.c @@ -2,7 +2,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: password.c,v 1.29 2000/06/02 15:57:21 momjian Exp $ + * $Id: password.c,v 1.30 2000/07/04 16:31:53 petere Exp $ * */ @@ -15,18 +15,19 @@ #include "libpq/libpq.h" #include "libpq/password.h" +#include "libpq/crypt.h" #include "miscadmin.h" int -verify_password(char *auth_arg, char *user, char *password) +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(auth_arg) + 2); + 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, auth_arg); + strcat(pw_file_fullname, port->auth_arg); pw_file = AllocateFile(pw_file_fullname, PG_BINARY_R); if (!pw_file) @@ -52,23 +53,32 @@ verify_password(char *auth_arg, char *user, char *password) *test_pw; fgets(pw_file_line, sizeof(pw_file_line), pw_file); + /* kill the newline */ + if (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, ":"); test_pw = strtok(NULL, ":"); - if (!test_user || !test_pw || - test_user[0] == '\0' || test_pw[0] == '\0') + if (!test_user || test_user[0] == '\0') continue; - /* kill the newline */ - if (test_pw[strlen(test_pw) - 1] == '\n') - test_pw[strlen(test_pw) - 1] = '\0'; - 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 of "+" then we use the regular + * pg_shadow passwords. If we use crypt then we have to + * use pg_shadow passwords no matter what. + */ + if (port->auth_method == uaCrypt + || test_pw == NULL || test_pw[0] == '\0' + || strcmp(test_pw, "+")==0) + return crypt_verify(port, user, password); + if (strcmp(crypt(password, test_pw), test_pw) == 0) { /* it matched. */ diff --git a/src/bin/pg_passwd/pg_passwd.c b/src/bin/pg_passwd/pg_passwd.c index b8d1aae97ac..50aa4011b51 100644 --- a/src/bin/pg_passwd/pg_passwd.c +++ b/src/bin/pg_passwd/pg_passwd.c @@ -105,13 +105,9 @@ try_again: /* get user name */ p = line; - if ((q = strchr(p, ':')) == NULL) - { - fprintf(stderr, "%s: line %d: illegal format.\n", - filename, npwds + 1); - exit(1); - } - *(q++) = '\0'; + if ((q = strchr(p, ':')) != NULL) + *q = '\0'; + if (strlen(p) == 0) { fprintf(stderr, "%s: line %d: null user name.\n", @@ -131,23 +127,23 @@ try_again: } /* get password field */ - p = q; - q = strchr(p, ':'); - - /* - * --- don't care ----- if ((q = strchr(p, ':')) == NULL) { - * fprintf(stderr, "%s: line %d: illegal format.\n", filename, - * npwds + 1); exit(1); } - */ - - if (q != NULL) - *(q++) = '\0'; - if (strlen(p) != 13) + if (q) { - fprintf(stderr, "WARNING: %s: line %d: illegal password length.\n", - filename, npwds + 1); + p = q + 1; + q = strchr(p, ':'); + + if (q != NULL) + *(q++) = '\0'; + + if (strlen(p) != 13 && strcmp(p, "+")!=0) + { + fprintf(stderr, "WARNING: %s: line %d: invalid password length.\n", + filename, npwds + 1); + } + pwds[npwds].pwd = strdup(p); } - pwds[npwds].pwd = strdup(p); + else + pwds[npwds].pwd = NULL; /* rest of the line is treated as is */ if (q == NULL) @@ -193,9 +189,12 @@ link_again: /* write file */ for (i = 0; i < npwds; ++i) { - fprintf(fp, "%s:%s%s%s\n", pwds[i].uname, pwds[i].pwd, - pwds[i].rest ? ":" : "", - pwds[i].rest ? pwds[i].rest : ""); + 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); diff --git a/src/include/libpq/crypt.h b/src/include/libpq/crypt.h index 7827b464081..c3f58ee1639 100644 --- a/src/include/libpq/crypt.h +++ b/src/include/libpq/crypt.h @@ -26,6 +26,6 @@ extern char *crypt_getpwdreloadfilename(void); extern MsgType crypt_salt(const char *user); #endif -extern int crypt_verify(Port *port, const char *user, const char *pgpass); +extern int crypt_verify(const Port *port, const char *user, const char *pgpass); #endif diff --git a/src/include/libpq/password.h b/src/include/libpq/password.h index 9c7421d8935..c704edeb345 100644 --- a/src/include/libpq/password.h +++ b/src/include/libpq/password.h @@ -1,6 +1,6 @@ #ifndef PASSWORD_H #define PASSWORD_H -int verify_password(char *auth_arg, char *user, char *password); +int verify_password(const Port *port, const char *user, const char *password); #endif