From d92a7e2c1310acc32d73be32c9cb422259872e94 Mon Sep 17 00:00:00 2001 From: Bo Peng Date: Tue, 13 May 2025 17:37:06 +0900 Subject: [PATCH] Fix incorrect client authentication in some cases. If enable_pool_hba = on, it's auth method is "password", no password is registered in pool_passwd, and auth method in pg_hba.conf is "scram-sha-256" or "md5", for the first time when a client connects to pgpool, authentication is performed as expected. But if a client connects to the cached connection, any password from the client is accepted. authenticate_frontend() asks password to the client and stores it in frontend->password. When pgpool authenticate backend, authenticate_frontend_SCRAM() or authenticate_frontend_md5() is called depending on pg_hba.conf setting. authenticate_frontend_*() calls get_auth_password() to get backend cached password but it mistakenly returned frontend->password if pool_passwd does not have an entry for the user. Then authenticate_frontend_*() tries to challenge based on frontend->password. As a result, they compared frontend->password itself, which always succeed. To fix this, when get_auth_password() is called with reauth parameter being non 0, return backend->password. Also if enable_pool_hba = off, in some cases a client is not asked password for the first time, or when a client connects to cached connection, even if it should be. If pool_hba.conf is disabled, get_backend_connection() does not call Client_authentication(), thus frontend->password is not set. Then pool_do_reauth() calls do_clear_text_password(). It should have called authenticate_frontend_clear_text() to get a password from the client, but a mistake in a if statement prevented it. The mistake was fixed in this commit. Pgpool-II versions affected: v4.0 or later. Also this commit does followings: - Remove single PostgreSQL code path to simplify the authentication code. As a result, following cases are no more Ok. - Remove crypt authentication support for frontend and backend. The feature had not been documented and never tested. Moreover crypt authentication was removed long time ago in PostgreSQL (8.4, 2009). - Add new regression test "040.client_auth". The test performs exhaustive client authentication tests using a test specification file formatted in CSV. The csv files have 7 fields: username: the username used for the test case pool_hba.conf: takes "scram", "md5", "password", "pam", "ldap" or "off". If "scram", "md5" , "password", "pam" or "ldap", the user will have an entry in pool_hba.conf accordingly. If "off", enable_pool_hba.conf will be off. allow_clear_text_frontend_auth: takes "on" or "off". pool_passwd: takes "AES", "md5" or "off". If "AES" or "md5" the user's password will be stored in pool_passwd using ASE256 or md5 encryption method accordingly. If "off" is specified, no entry will be created. pg_hba.conf: almost same as pool_hba.conf except this is for pg_hba.conf. expected: takes "ok" or "fail". If ok, the authentication is expected to be succeeded. If failed, the test is regarded as failed. "fail" is opposite. The authentication is expected to be failed. If succeeds, the test regarded as failed. comment: arbitrary comment By changing these fields, we can easily modify or add test cases. The merit of this method is possible higher test coverage. For human, it is easier to find uncovered test cases in a table than in a program code. Backpatch-through: v4.2 The patch was created by Tatsuo Ishii. --- doc.ja/src/sgml/client-auth.sgml | 37 +- doc/src/sgml/client-auth.sgml | 48 +- src/auth/pool_auth.c | 366 +++----------- src/sample/pgpool.pam | 3 +- .../040.client_auth/client_auth_2node.csv | 126 +++++ .../tests/040.client_auth/create_ldap_user.sh | 20 + .../tests/040.client_auth/create_pam_user.sh | 17 + .../tests/040.client_auth/del_ldap_users.sh | 8 + .../tests/040.client_auth/del_pam_users.sh | 7 + .../tests/040.client_auth/list_ldap_user.sh | 17 + .../tests/040.client_auth/list_pam_user.sh | 16 + .../tests/040.client_auth/pam_users.txt | 41 ++ .../regression/tests/040.client_auth/test.sh | 474 ++++++++++++++++++ 13 files changed, 830 insertions(+), 350 deletions(-) create mode 100644 src/test/regression/tests/040.client_auth/client_auth_2node.csv create mode 100755 src/test/regression/tests/040.client_auth/create_ldap_user.sh create mode 100755 src/test/regression/tests/040.client_auth/create_pam_user.sh create mode 100755 src/test/regression/tests/040.client_auth/del_ldap_users.sh create mode 100755 src/test/regression/tests/040.client_auth/del_pam_users.sh create mode 100755 src/test/regression/tests/040.client_auth/list_ldap_user.sh create mode 100755 src/test/regression/tests/040.client_auth/list_pam_user.sh create mode 100644 src/test/regression/tests/040.client_auth/pam_users.txt create mode 100755 src/test/regression/tests/040.client_auth/test.sh diff --git a/doc.ja/src/sgml/client-auth.sgml b/doc.ja/src/sgml/client-auth.sgml index 996cc9f07..571c0c65c 100644 --- a/doc.ja/src/sgml/client-auth.sgml +++ b/doc.ja/src/sgml/client-auth.sgml @@ -37,7 +37,11 @@ - クライアントがPgpool-IIに接続する際の認証用のパスワードを管理するのがpool_passwdファイルです(詳細はをご覧ください。)pool_passwdに登録されるパスワードは、PostgreSQLに登録されるパスワードと一致している必要があります。PostgreSQLに登録されたパスワードを変更しても、pool_passwdのパスワードは自動変更されないことに注意してください。scram-shar-256認証とMD5認証では、pool_passwdにユーザ名とパスワードを登録するのが必須ですが、clear text password認証では必須ではありません。このため、pool_passwdのパスワードの保守を避けたい場合には、clear text password認証の検討をお勧めします。 + クライアントがPgpool-IIに接続する際の認証用のパスワードを管理するのがpool_passwdファイルです(詳細はをご覧ください。) + pool_passwdに登録されるパスワードは、PostgreSQLに登録されるパスワードと一致している必要があります。 + PostgreSQLに登録されたパスワードを変更しても、pool_passwdのパスワードは自動変更されないことに注意してください。 + scram-shar-256認証とMD5認証では、pool_passwdにユーザ名とパスワードを登録するのが必須ですが、clear text password認証、あるいはが有効である場合では必須ではありません。 + このため、pool_passwdのパスワードの保守を避けたい場合には、clear text password認証あるいはの検討をお勧めします。 @@ -721,7 +725,7 @@ - 以下の小節では、認証方式について詳細に説明します。 + 以下の小節では、pool_hba.confで指定する認証方式について詳細に説明します。 @@ -807,17 +811,6 @@ そのため、Pgpool-IIのmd5認証は認証ファイルを使ってサポートしています。 - - - - Pgpool-IIをrawモードで使用している場合、あるいはバックエンドが1つしかない場合は、を設定する必要はありません。 - - - - md5認証を使用するには、認証ファイルに平文、md5またはAES暗号化形式のいずれかのユーザのパスワード + md5認証を使用するには、認証ファイルに平文、AES、md5暗号化形式のいずれかのユーザのパスワード が含まれている必要があります。 @@ -839,11 +832,15 @@ --> pool_passwdファイルは以下の形式の行を含みます。 - "username:plain_text_passwd" + "username:TEXT_plain_text_passwd" + + + "username:AES_encrypted_passwd" - "username:encrypted_passwd" + "username:md5_encrypted_passwd" + (実際には、"TEXT"、"AES"、"md5"の後の"_"は存在しません。) @@ -889,6 +886,7 @@ --> 2. pool_hba.confにmd5認証のエントリを作成します。 詳細についてはを参照してください。 + pool_hba.confを有効にしない場合は、PostgreSQLのpg_hba.confでmd5認証が指定されていることを確認してください。 2- pool_hba.confに適切なscram-sha-256エントリを追加します。 詳細はを参照してください。 + pool_hba.confを有効にしない場合は、PostgreSQLのpg_hba.confでscram-sha-256認証が指定されていることを確認してください。 PAM認証を有効にするには、Pgpool-IIのサービス設定ファイルをシステムのPAM設定ディレクトリ(通常は"/etc/pam.d"にあります)に作成しなければなりません。 - サービス設定ファイルのサンプルはインストールディレクトリ下に"share/pgpool-II/pgpool.pam"としてインストールされています。 diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml index 5096c8718..791855743 100644 --- a/doc/src/sgml/client-auth.sgml +++ b/doc/src/sgml/client-auth.sgml @@ -45,10 +45,13 @@ authentication require that the user name and the password have been already registered on pool_passwd, while clear text password - authentication does not require that. Therefore, if you want - to avoid maintaining the pool_passwd, it would - be worth to check clear text password - authentication. + authentication + or does not + require that. Therefore, if you want to avoid maintaining + the pool_passwd, it would be worth to + check clear text password + authentication + or . @@ -564,7 +567,8 @@ Authentication Methods - The following subsections describe the authentication methods in more detail. + The following subsections describe the authentication methods + specified by pool_hba.conf in more detail. @@ -624,30 +628,26 @@ authentication file. - - - If Pgpool-II is operated in raw - mode or there's only 1 backend configured, you don't need to - setup . - - - Authentication file format To use the md5 authentication - authentication file - must contain the user password in either plain text - md5 or AES encrypted format. + authentication file must contain + the user password in either plain text, AES + or md5 encrypted format. The file should contain lines in the following format: - "username:plain_text_passwd" + "username:TEXT_plain_text_passwd" + + + "username:AES_encrypted_passwd" - "username:encrypted_passwd" + "username:md5_encrypted_passwd" + (Actually "_" after "TEXT", "AES" or "md5" does not exist.) @@ -678,6 +678,10 @@ 2- Add an appropriate md5 entry to pool_hba.conf. See for more details. + If pool_hba.conf is not enabled, make sure + that md5 authentication is specified + in pg_hba.conf + of PostgreSQL. 3- After changing md5 password (in both pool_passwd @@ -715,11 +719,12 @@ or AES encrypted format. - "username:plain_text_passwd" + "username:TEXT_plain_text_passwd" "username:AES_encrypted_passwd" + (Actually "_" after "TEXT" or "AES" does not exist.) md5 type user passwords in @@ -758,6 +763,7 @@ 2- Add an appropriate scram-sha-256 entry to pool_hba.conf. See for more details. + If pool_hba.conf is not enabled, make sure that md5 authentication is specified in pg_hba.conf of PostgreSQL. 3- After changing SCRAM password (in both pool_passwd @@ -820,10 +826,8 @@ To enable PAM authentication, you need to create a service-configuration - file for Pgpool-II in the system's + file named for Pgpool-II in the system's PAM configuration directory (which is usually at "/etc/pam.d"). - A sample service-configuration file is installed as - "share/pgpool-II/pgpool.pam" under the install directory. diff --git a/src/auth/pool_auth.c b/src/auth/pool_auth.c index 862e2f733..33d887f78 100644 --- a/src/auth/pool_auth.c +++ b/src/auth/pool_auth.c @@ -3,7 +3,7 @@ * pgpool: a language independent connection pool server for PostgreSQL * written by Tatsuo Ishii * - * Copyright (c) 2003-2020 PgPool Global Development Group + * Copyright (c) 2003-2025 PgPool Global Development Group * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby @@ -37,9 +37,6 @@ #include "auth/md5.h" #include -#ifdef HAVE_CRYPT_H -#include -#endif #ifdef HAVE_SYS_TYPES_H #include #endif @@ -64,10 +61,8 @@ static POOL_STATUS pool_send_backend_key_data(POOL_CONNECTION * frontend, int pid, int key, int protoMajor); static int do_clear_text_password(POOL_CONNECTION * backend, POOL_CONNECTION * frontend, int reauth, int protoMajor); static void pool_send_auth_fail(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * cp); -static int do_crypt(POOL_CONNECTION * backend, POOL_CONNECTION * frontend, int reauth, int protoMajor); static int do_md5(POOL_CONNECTION * backend, POOL_CONNECTION * frontend, int reauth, int protoMajor, char *storedPassword, PasswordType passwordType); -static int do_md5_single_backend(POOL_CONNECTION * backend, POOL_CONNECTION * frontend, int reauth, int protoMajor); static void send_md5auth_request(POOL_CONNECTION * frontend, int protoMajor, char *salt); static int read_password_packet(POOL_CONNECTION * frontend, int protoMajor, char *password, int *pwdSize); static int send_password_packet(POOL_CONNECTION * backend, int protoMajor, char *password); @@ -464,30 +459,6 @@ pool_do_auth(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * cp) } } - /* crypt authentication? */ - else if (authkind == AUTH_REQ_CRYPT) - { - for (i = 0; i < NUM_BACKENDS; i++) - { - if (!VALID_BACKEND(i)) - continue; - - ereport(DEBUG1, - (errmsg("authentication backend"), - errdetail("trying crypt authentication"))); - - authkind = do_crypt(CONNECTION(cp, i), frontend, 0, protoMajor); - - if (authkind < 0) - { - pool_send_auth_fail(frontend, cp); - ereport(ERROR, - (errmsg("failed to authenticate with backend"), - errdetail("do_crypt_text_password failed in slot %d", i))); - } - } - } - /* md5 authentication? */ else if (authkind == AUTH_REQ_MD5) { @@ -497,41 +468,38 @@ pool_do_auth(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * cp) /* * check if we can use md5 authentication. */ - if (!RAW_MODE && NUM_BACKENDS > 1) + if (get_auth_password(MAIN(cp), frontend, 0, + &password, &passwordType) == false) { - if (get_auth_password(MAIN(cp), frontend, 0, - &password, &passwordType) == false) + /* + * We do not have any password, we can still get the password + * from client using plain text authentication if it is + * allowed by user + */ + if (frontend->pool_hba == NULL && pool_config->allow_clear_text_frontend_auth) { - /* - * We do not have any password, we can still get the password - * from client using plain text authentication if it is - * allowed by user - */ - if (frontend->pool_hba == NULL && pool_config->allow_clear_text_frontend_auth) + ereport(LOG, + (errmsg("using clear text authentication with frontend"), + errdetail("backend will still use md5 auth"), + errhint("you can disable this behavior by setting allow_clear_text_frontend_auth to off"))); + authenticate_frontend_clear_text(frontend); + /* now check again if we have a password now */ + if (get_auth_password(MAIN(cp), frontend, 0, + &password, &passwordType) == false) { - ereport(LOG, - (errmsg("using clear text authentication with frontend"), - errdetail("backend will still use md5 auth"), - errhint("you can disable this behavior by setting allow_clear_text_frontend_auth to off"))); - authenticate_frontend_clear_text(frontend); - /* now check again if we have a password now */ - if (get_auth_password(MAIN(cp), frontend, 0, - &password, &passwordType) == false) - { - ereport(ERROR, - (errmsg("failed to authenticate with backend using md5"), - errdetail("unable to get the password"))); - } + ereport(ERROR, + (errmsg("failed to authenticate with backend using md5"), + errdetail("unable to get the password"))); } } - /* we have a password to use, validate the password type */ - if (passwordType != PASSWORD_TYPE_PLAINTEXT && passwordType != PASSWORD_TYPE_MD5 - && passwordType != PASSWORD_TYPE_AES) - { - ereport(ERROR, - (errmsg("failed to authenticate with backend using md5"), - errdetail("valid password not found"))); - } + } + /* we have a password to use, validate the password type */ + if (passwordType != PASSWORD_TYPE_PLAINTEXT && passwordType != PASSWORD_TYPE_MD5 + && passwordType != PASSWORD_TYPE_AES) + { + ereport(ERROR, + (errmsg("failed to authenticate with backend using md5"), + errdetail("valid password not found"))); } for (i = 0; i < NUM_BACKENDS; i++) @@ -838,19 +806,16 @@ pool_do_reauth(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * cp) do_clear_text_password(MAIN(cp), frontend, 1, protoMajor); break; - case AUTH_REQ_CRYPT: - /* crypt password */ - do_crypt(MAIN(cp), frontend, 1, protoMajor); - break; - case AUTH_REQ_MD5: /* md5 password */ authenticate_frontend_md5(MAIN(cp), frontend, 1, protoMajor); break; + case AUTH_REQ_SASL: /* SCRAM */ authenticate_frontend_SCRAM(MAIN(cp), frontend, 1); break; + default: ereport(ERROR, (errmsg("authentication failed"), @@ -1017,6 +982,7 @@ do_clear_text_password(POOL_CONNECTION * backend, POOL_CONNECTION * frontend, in char *pwd = NULL; int kind; PasswordType passwordType = PASSWORD_TYPE_UNKNOWN; + bool rtn; if (reauth && frontend->frontend_authenticated) { @@ -1024,7 +990,23 @@ do_clear_text_password(POOL_CONNECTION * backend, POOL_CONNECTION * frontend, in return 0; } - if (get_auth_password(backend, frontend, reauth, &pwd, &passwordType) == false) + rtn = get_auth_password(backend, frontend, reauth, &pwd, &passwordType); + + /* + * If pool_hba.conf == "password", we should already have frontend + * password. When reauth, we want the password, rather than + * backend->password, which is returned by get_auth_password() if + * pool_passwd does not have an entry for the user. + */ + if (reauth && rtn && frontend->passwordMapping == NULL && + frontend->pwd_size > 0 && + frontend->passwordType == PASSWORD_TYPE_PLAINTEXT) + { + pwd = frontend->password; + passwordType = frontend->passwordType; + } + + else if (!rtn || frontend->pwd_size == 0) { /* * We do not have any password, we can still get the password @@ -1043,8 +1025,7 @@ do_clear_text_password(POOL_CONNECTION * backend, POOL_CONNECTION * frontend, in authenticate_frontend_clear_text(frontend); /* now check again if we have a password now */ - - if (get_auth_password(backend, frontend, reauth, &pwd, &passwordType) == false) + if (get_auth_password(backend, frontend, 0, &pwd, &passwordType) == false) { ereport(FATAL, (return_code(2), @@ -1114,157 +1095,6 @@ do_clear_text_password(POOL_CONNECTION * backend, POOL_CONNECTION * frontend, in return kind; } -/* - * perform crypt authentication - */ -static int -do_crypt(POOL_CONNECTION * backend, POOL_CONNECTION * frontend, int reauth, int protoMajor) -{ - char salt[2]; - static int size; - static char password[MAX_PASSWORD_SIZE]; - char response; - int kind; - int len; - - if (!reauth) - { - /* read salt */ - pool_read(backend, salt, sizeof(salt)); - } - else - { - memcpy(salt, backend->salt, sizeof(salt)); - } - - /* main? */ - if (IS_MAIN_NODE_ID(backend->db_node_id)) - { - pool_write(frontend, "R", 1); /* authentication */ - if (protoMajor == PROTO_MAJOR_V3) - { - len = htonl(10); - pool_write(frontend, &len, sizeof(len)); - } - kind = htonl(4); /* crypt authentication */ - pool_write(frontend, &kind, sizeof(kind)); /* indicating crypt - * authentication */ - pool_write_and_flush(frontend, salt, sizeof(salt)); /* salt */ - - /* read password packet */ - if (protoMajor == PROTO_MAJOR_V2) - { - pool_read(frontend, &size, sizeof(size)); - } - else - { - char k; - - pool_read(frontend, &k, sizeof(k)); - if (k != 'p') - { - ereport(ERROR, - (errmsg("crypt authentication failed"), - errdetail("invalid password packet. Packet does not starts with \"p\""))); - } - pool_read(frontend, &size, sizeof(size)); - } - - if ((ntohl(size) - 4) > sizeof(password)) - { - ereport(ERROR, - (errmsg("crypt authentication failed"), - errdetail("password is too long, password size is %d", ntohl(size) - 4))); - } - - pool_read(frontend, password, ntohl(size) - 4); - } - - /* connection reusing? */ - if (reauth) - { - ereport(DEBUG1, - (errmsg("performing crypt authentication"), - errdetail("size: %d saved_size: %d", (ntohl(size) - 4), backend->pwd_size))); - - if ((ntohl(size) - 4) != backend->pwd_size) - ereport(ERROR, - (errmsg("crypt authentication failed"), - errdetail("password size does not match"))); - - - if (memcmp(password, backend->password, backend->pwd_size) != 0) - ereport(ERROR, - (errmsg("crypt authentication failed"), - errdetail("password does not match"))); - - return 0; - } - - /* send password packet to backend */ - if (protoMajor == PROTO_MAJOR_V3) - pool_write(backend, "p", 1); - pool_write(backend, &size, sizeof(size)); - pool_write_and_flush(backend, password, ntohl(size) - 4); - pool_read(backend, &response, sizeof(response)); - - if (response != 'R') - { - if (response == 'E') /* Backend has thrown an error instead */ - { - char *message = NULL; - - if (pool_extract_error_message(false, backend, protoMajor, false, &message) == 1) - { - ereport(ERROR, - (errmsg("crypt authentication failed"), - errdetail("%s", message ? message : "backend throws authentication error"))); - } - if (message) - pfree(message); - } - ereport(ERROR, - (errmsg("crypt authentication failed"), - errdetail("invalid packet from backend. backend does not return R while processing clear text password authentication"))); - } - - if (protoMajor == PROTO_MAJOR_V3) - { - pool_read(backend, &len, sizeof(len)); - - if (ntohl(len) != 8) - ereport(ERROR, - (errmsg("crypt authentication failed"), - errdetail("invalid packet from backend. incorrect authentication packet size (%d)", ntohl(len)))); - } - - /* expect to read "Authentication OK" response. kind should be 0... */ - pool_read(backend, &kind, sizeof(kind)); - - /* if authenticated, save info */ - if (kind == 0) - { - int msglen; - - pool_write(frontend, "R", 1); - - if (protoMajor == PROTO_MAJOR_V3) - { - msglen = htonl(8); - pool_write(frontend, &msglen, sizeof(msglen)); - } - - msglen = htonl(0); - pool_write_and_flush(frontend, &msglen, sizeof(msglen)); - - backend->auth_kind = 4; - backend->pwd_size = ntohl(size) - 4; - memcpy(backend->password, password, backend->pwd_size); - memcpy(backend->salt, salt, sizeof(salt)); - } - return kind; -} - /* * Do the SCRAM authentication with the frontend using the stored * password in the pool_passwd file. @@ -1540,13 +1370,6 @@ authenticate_frontend_md5(POOL_CONNECTION * backend, POOL_CONNECTION * frontend, PasswordType storedPasswordType = PASSWORD_TYPE_UNKNOWN; char *storedPassword = NULL; - if (RAW_MODE || NUM_BACKENDS == 1) - { - if (backend) - do_md5_single_backend(backend, frontend, reauth, protoMajor); - return; /* This will be handled later */ - } - if (get_auth_password(backend, frontend, reauth,&storedPassword, &storedPasswordType) == false) { ereport(FATAL, @@ -1616,79 +1439,12 @@ authenticate_frontend_md5(POOL_CONNECTION * backend, POOL_CONNECTION * frontend, } /* - * perform MD5 authentication + * Get user's password from various sources. Firstly try pool_passwd and + * return it if the user's password is there. If reauth == 0 (first time the + * user connect to pgpool) and the password is in frontend, return it. If + * reauth != 0 (reuse connection pool) and the password is in backendm return + * it. */ -static int -do_md5_single_backend(POOL_CONNECTION * backend, POOL_CONNECTION * frontend, int reauth, int protoMajor) -{ - char salt[4]; - static int size; - static char password[MAX_PASSWORD_SIZE]; - int kind; - - if (!reauth) - { - /* read salt from backend */ - pool_read(backend, salt, sizeof(salt)); - ereport(DEBUG1, - (errmsg("performing md5 authentication"), - errdetail("DB node id: %d salt: %hhx%hhx%hhx%hhx", backend->db_node_id, - salt[0], salt[1], salt[2], salt[3]))); - } - else - { - /* Use the saved salt */ - memcpy(salt, backend->salt, sizeof(salt)); - } - - /* Send md5 auth request to frontend */ - send_md5auth_request(frontend, protoMajor, salt); - - /* Read password packet */ - read_password_packet(frontend, protoMajor, password, &size); - - /* connection reusing? compare it with saved password */ - if (reauth) - { - if (backend->passwordType != PASSWORD_TYPE_MD5) - ereport(ERROR, - (errmsg("md5 authentication failed"), - errdetail("invalid password type"))); - - if (size != backend->pwd_size) - ereport(ERROR, - (errmsg("md5 authentication failed"), - errdetail("password does not match"))); - - if (memcmp(password, backend->password, backend->pwd_size) != 0) - ereport(ERROR, - (errmsg("md5 authentication failed"), - errdetail("password does not match"))); - return 0; - } - else - { - /* Send password packet to backend and receive auth response */ - kind = send_password_packet(backend, protoMajor, password); - if (kind < 0) - ereport(ERROR, - (errmsg("md5 authentication failed"), - errdetail("backend replied with invalid kind"))); - - /* If authenticated, reply back to frontend and save info */ - if (kind == AUTH_REQ_OK) - { - send_auth_ok(frontend, protoMajor); - backend->passwordType = PASSWORD_TYPE_MD5; - backend->auth_kind = AUTH_REQ_MD5; - backend->pwd_size = size; - memcpy(backend->password, password, backend->pwd_size); - memcpy(backend->salt, salt, sizeof(salt)); - } - } - return kind; -} - static bool get_auth_password(POOL_CONNECTION * backend, POOL_CONNECTION * frontend, int reauth, char **password, PasswordType *passwordType) @@ -1703,7 +1459,8 @@ get_auth_password(POOL_CONNECTION * backend, POOL_CONNECTION * frontend, int rea * check if we have password stored in the frontend connection. that * could come by using the clear text auth */ - if (frontend->pwd_size > 0 && frontend->passwordType == PASSWORD_TYPE_PLAINTEXT) + if (!reauth && + frontend->pwd_size > 0 && frontend->passwordType == PASSWORD_TYPE_PLAINTEXT) { *password = frontend->password; *passwordType = frontend->passwordType; @@ -1748,9 +1505,6 @@ do_md5(POOL_CONNECTION * backend, POOL_CONNECTION * frontend, int reauth, int pr char encbuf[POOL_PASSWD_LEN + 1]; char *pool_passwd = NULL; - if (RAW_MODE || NUM_BACKENDS == 1) - return do_md5_single_backend(backend, frontend, reauth, protoMajor); - if (passwordType == PASSWORD_TYPE_AES) { /* @@ -1792,8 +1546,8 @@ do_md5(POOL_CONNECTION * backend, POOL_CONNECTION * frontend, int reauth, int pr * already received the password from frontend using the clear text * auth, we may not need to authenticate it */ - if (pool_config->allow_clear_text_frontend_auth && - frontend->auth_kind == AUTH_REQ_PASSWORD && + if ((pool_config->allow_clear_text_frontend_auth || + frontend->auth_kind == AUTH_REQ_PASSWORD) && frontend->pwd_size > 0 && frontend->passwordType == PASSWORD_TYPE_PLAINTEXT) { @@ -2119,8 +1873,8 @@ do_SCRAM(POOL_CONNECTION * frontend, POOL_CONNECTION * backend, int protoMajor, * already received the password from frontend using the clear text * auth, we may not need to authenticate it */ - if (pool_config->allow_clear_text_frontend_auth && - frontend->auth_kind == AUTH_REQ_PASSWORD && + if ((pool_config->allow_clear_text_frontend_auth || + frontend->auth_kind == AUTH_REQ_PASSWORD) && frontend->pwd_size > 0 && frontend->passwordType == PASSWORD_TYPE_PLAINTEXT) { diff --git a/src/sample/pgpool.pam b/src/sample/pgpool.pam index 320f086f5..b31d5e2b2 100644 --- a/src/sample/pgpool.pam +++ b/src/sample/pgpool.pam @@ -1,3 +1,2 @@ #%PAM-1.0 -auth required pam_permit.so -account required pam_permit.so +# create your own PAM configurations diff --git a/src/test/regression/tests/040.client_auth/client_auth_2node.csv b/src/test/regression/tests/040.client_auth/client_auth_2node.csv new file mode 100644 index 000000000..71a47e372 --- /dev/null +++ b/src/test/regression/tests/040.client_auth/client_auth_2node.csv @@ -0,0 +1,126 @@ +username,pool_hba.conf,allow_clear_text_frontend_auth,pool_passwd,pg_hba.conf,expected,comment +scram1,scram,off,AES,scram,ok, +scram2,scram,off,AES,md5,ok, +scram3,scram,off,AES,password,ok, +scram4,scram,off,AES,pam,ok, +scram5,scram,off,AES,ldap,ok, +scram6,scram,off,md5,scram,fail, +scram7,scram,off,md5,md5,fail, +scram8,scram,off,md5,password,fail, +scram9,scram,off,md5,pam,fail, +scram10,scram,off,md5,ldap,fail, +scram11,scram,off,text,scram,ok, +scram12,scram,off,text,md5,ok, +scram13,scram,off,text,password,ok, +scram14,scram,off,text,pam,ok, +scram15,scram,off,text,ldap,ok, +scram16,scram,off,off,scram,fail, +scram17,scram,off,off,md5,fail, +scram18,scram,off,off,password,fail, +scram19,scram,off,off,pam,fail, +scram20,scram,off,off,ldap,fail, +md51,md5,off,AES,scram,ok, +md52,md5,off,AES,md5,ok, +md53,md5,off,AES,password,ok, +md54,md5,off,AES,pam,ok, +md55,md5,off,AES,ldap,ok, +md56,md5,off,md5,scram,fail, +md57,md5,off,md5,md5,ok, +md58,md5,off,md5,password,fail, +md59,md5,off,md5,pam,fail, +md510,md5,off,md5,ldap,fail, +md511,md5,off,text,scram,ok, +md512,md5,off,text,md5,ok, +md513,md5,off,text,password,ok, +md514,md5,off,text,pam,ok, +md515,md5,off,text,ldap,ok, +md516,md5,off,off,scram,fail, +md517,md5,off,off,md5,fail, +md518,md5,off,off,password,fail, +md519,md5,off,off,pam,fail, +md520,md5,off,off,ldap,fail, +p1,password,off,AES,scram,ok, +p2,password,off,AES,md5,ok, +p3,password,off,AES,password,ok, +p4,password,off,AES,pam,ok, +p5,password,off,AES,ldap,ok, +p6,password,off,md5,scram,fail, +p7,password,off,md5,md5,ok, +p8,password,off,md5,password,fail, +p9,password,off,md5,pam,fail, +p10,password,off,md5,ldap,fail, +p11,password,off,text,scram,ok, +p12,password,off,text,md5,ok, +p13,password,off,text,password,ok, +p14,password,off,text,pam,ok, +p15,password,off,text,ldap,ok, +p16,password,off,off,scram,ok, +p17,password,off,off,md5,ok, +p18,password,off,off,password,ok, +p19,password,off,off,pam,ok, +p20,password,off,off,ldap,ok, +o1,off,on,off,scram,ok, +o2,off,on,off,md5,ok, +o3,off,on,off,password,ok, +o4,off,on,off,pam,ok, +o5,off,on,off,ldap,ok, +o6,off,off,AES,scram,ok, +o7,off,off,AES,md5,ok, +o8,off,off,AES,password,ok, +o9,off,off,AES,pam,ok, +o10,off,off,AES,ldap,ok, +o11,off,off,md5,scram,fail, +o12,off,off,md5,md5,ok, +o13,off,off,md5,password,fail, +o14,off,off,md5,pam,fail, +o15,off,off,md5,ldap,fail, +o16,off,off,text,scram,ok, +o17,off,off,text,md5,ok, +o18,off,off,text,password,ok, +o19,off,off,text,pam,ok, +o20,off,off,text,ldap,ok, +o21,off,off,off,scram,fail, +o22,off,off,off,md5,fail, +o23,off,off,off,password,ok, +o24,off,off,off,pam,ok, +o25,off,off,off,ldap,ok, +pam1,pam,off,AES,scram,ok, +pam2,pam,off,AES,md5,ok, +pam3,pam,off,AES,password,ok, +pam4,pam,off,AES,pam,ok, +pam5,pam,off,AES,ldap,ok, +pam6,pam,off,md5,scram,fail, +pam7,pam,off,md5,md5,ok, +pam8,pam,off,md5,password,fail, +pam9,pam,off,md5,pam,fail, +pam10,pam,off,md5,ldap,fail, +pam11,pam,off,text,scram,ok, +pam12,pam,off,text,md5,ok, +pam13,pam,off,text,password,ok, +pam14,pam,off,text,pam,ok, +pam15,pam,off,text,ldap,ok, +pam16,pam,off,off,scram,fail, +pam17,pam,off,off,md5,fail, +pam18,pam,off,off,password,fail, +pam19,pam,off,off,pam,fail, +pam20,pam,off,off,ldap,fail, +ldap1,ldap,off,AES,scram,ok, +ldap2,ldap,off,AES,md5,ok, +ldap3,ldap,off,AES,password,ok, +ldap4,ldap,off,AES,pam,ok, +ldap5,ldap,off,AES,ldap,ok, +ldap6,ldap,off,md5,scram,fail, +ldap7,ldap,off,md5,md5,ok, +ldap8,ldap,off,md5,password,fail, +ldap9,ldap,off,md5,pam,fail, +ldap10,ldap,off,md5,ldap,fail, +ldap11,ldap,off,text,scram,ok, +ldap12,ldap,off,text,md5,ok, +ldap13,ldap,off,text,password,ok, +ldap14,ldap,off,text,pam,ok, +ldap15,ldap,off,text,ldap,ok, +ldap16,ldap,off,off,scram,fail, +ldap17,ldap,off,off,md5,fail, +ldap18,ldap,off,off,password,fail, +ldap19,ldap,off,off,pam,fail, +ldap20,ldap,off,off,ldap,fail, diff --git a/src/test/regression/tests/040.client_auth/create_ldap_user.sh b/src/test/regression/tests/040.client_auth/create_ldap_user.sh new file mode 100755 index 000000000..0d1583583 --- /dev/null +++ b/src/test/regression/tests/040.client_auth/create_ldap_user.sh @@ -0,0 +1,20 @@ +#! /usr/bin/bash +# Create input file for ldapadd command to stdout. +# Users' list must be provided from stdin. +if [ $# -ne 0 ];then + echo "usage: $0" + exit 1 +fi + +while read username +do + cat <> $USERS +done diff --git a/src/test/regression/tests/040.client_auth/del_ldap_users.sh b/src/test/regression/tests/040.client_auth/del_ldap_users.sh new file mode 100755 index 000000000..af77529d8 --- /dev/null +++ b/src/test/regression/tests/040.client_auth/del_ldap_users.sh @@ -0,0 +1,8 @@ +#! /usr/bin/bash +# read users list from stdin and perform ldapdelete command. +PASSWORD=ldapadmin +set -e +while read i +do + ldapdelete -x -w $PASSWORD -D cn=admin,dc=nodomain uid=$i,ou=people,dc=nodomain +done diff --git a/src/test/regression/tests/040.client_auth/del_pam_users.sh b/src/test/regression/tests/040.client_auth/del_pam_users.sh new file mode 100755 index 000000000..4336c0181 --- /dev/null +++ b/src/test/regression/tests/040.client_auth/del_pam_users.sh @@ -0,0 +1,7 @@ +#! /usr/bin/bash +# read users list from stdin and perform userdel command. +set -e +while read i +do + /usr/sbin/userdel $i +done diff --git a/src/test/regression/tests/040.client_auth/list_ldap_user.sh b/src/test/regression/tests/040.client_auth/list_ldap_user.sh new file mode 100755 index 000000000..dffa928fd --- /dev/null +++ b/src/test/regression/tests/040.client_auth/list_ldap_user.sh @@ -0,0 +1,17 @@ +#! /usr/bin/bash +# List up user names used in the LDAP authentication test. +# The result is put to stdout. +spec=client_auth_2node.csv +IFS="," +cat $spec|while read line +do + set $line + if [ $1 != 'username' ];then + username=$1 + pool_hba=$2 + pg_hba=$5 + if [ $pool_hba = "ldap" -o $pg_hba = "ldap" ];then + echo $username + fi + fi +done diff --git a/src/test/regression/tests/040.client_auth/list_pam_user.sh b/src/test/regression/tests/040.client_auth/list_pam_user.sh new file mode 100755 index 000000000..cbcfe9e4b --- /dev/null +++ b/src/test/regression/tests/040.client_auth/list_pam_user.sh @@ -0,0 +1,16 @@ +#! /usr/bin/bash +# List up user names used in the PAM authentication test. +spec=client_auth_2node.csv +IFS="," +cat $spec|while read line +do + set $line + if [ $1 != 'username' ];then + username=$1 + pool_hba=$2 + pg_hba=$5 + if [ $pool_hba = "pam" -o $pg_hba = "pam" ];then + echo $username + fi + fi +done diff --git a/src/test/regression/tests/040.client_auth/pam_users.txt b/src/test/regression/tests/040.client_auth/pam_users.txt new file mode 100644 index 000000000..e1f5a95d8 --- /dev/null +++ b/src/test/regression/tests/040.client_auth/pam_users.txt @@ -0,0 +1,41 @@ +scram4 +scram9 +scram14 +scram19 +md54 +md59 +md514 +md519 +p4 +p9 +p14 +p19 +o4 +o9 +o14 +o19 +o24 +pam1 +pam2 +pam3 +pam4 +pam5 +pam6 +pam7 +pam8 +pam9 +pam10 +pam11 +pam12 +pam13 +pam14 +pam15 +pam16 +pam17 +pam18 +pam19 +pam20 +ldap4 +ldap9 +ldap14 +ldap19 diff --git a/src/test/regression/tests/040.client_auth/test.sh b/src/test/regression/tests/040.client_auth/test.sh new file mode 100755 index 000000000..ad5f6934b --- /dev/null +++ b/src/test/regression/tests/040.client_auth/test.sh @@ -0,0 +1,474 @@ +#!/usr/bin/bash +#------------------------------------------------------------------- +# test script for client authentication +# + +# This test is only valid with PostgreSQL 10 or later. +if [ $PGVERSION -le 9 ];then + echo "all tests skipped due to PostgreSQL version: $PGVERSION" + exit 0 +fi + +# -------------------------------------------------------------------- +# PAM authentication tests are not performed by default. In order to +# test PAM authentication (both frontend and backend), run following +# scripts here (040.client_auth). +# +# Create pam users list. +# $ ./list_pam_user.sh > pam_users.txt +# +# Create input file for newusers command. +# The result file is newusers_input.txt. +# $ ./create_pam_user.sh pam_users.txt +# +# Finally run newusers command as root. +# the users are not allowed to login (login shell is /usr/sbin/nologin) +# /usr/sbin/newusers newusers_input.txt +# +# Note that newusers just ignore users that are already registered in +# the system. So it is harmless to generate new newusers_input.txt and +# run newusers command. It will happily register new users. +# +# Then set the environment variable DO_PAM_TEST to "true". +# +if [ "$DO_PAM_TEST" != "true" ];then + echo "all pam tests will be skipped" +fi +# -------------------------------------------------------------------- +# LDAP authentication tests are not performed by default. In order to +# test LDAP authentication (both frontend and backend), run following +# scripts here (040.client_auth). The procedure below assumes that +# LDAP server is created on localhost, with LDAP admin name is +# "admin", domain is "nodomain". +# +# To create LDAP ldif file, run: +# $ cat client_auth_2node.csv|./list_ldap_user.sh|./create_ldap_user.sh > ldap_users.ldif +# +# Then run ldapadd command. +# ldapadd -x -D cn=admin,dc=nodomain -W -f ldap_users.ldif +# +# Set the environment variable DO_LDAP_TEST to "true". +# +if [ "$DO_LDAP_TEST" != "true" ];then + echo "all ldap tests will be skipped" +fi +# -------------------------------------------------------------------- +# Function declarations + +# create user +# $1: username +# $2: "scram", "md5", "password", "pam" or "ldap". +# If "password" is specified, encryption method becomes 'scram-sha-256'. +function add_user() +{ + createuser $1 + if [ $2 = "scram" ];then + psql -c "SET password_encryption = 'scram-sha-256'; ALTER USER $1 WITH ENCRYPTED PASSWORD '$1'" + elif [ $2 = "md5" ];then + psql -c "SET password_encryption = 'md5'; ALTER USER $1 WITH ENCRYPTED PASSWORD '$1'" + elif [ $2 = "password" ];then + psql -c "SET password_encryption = 'scram-sha-256'; ALTER USER $1 WITH ENCRYPTED PASSWORD '$1'" + elif [ "$DO_PAM_TEST" = "true" -a $2 = "pam" ];then + psql -c "SET password_encryption = 'scram-sha-256'; ALTER USER $1 WITH ENCRYPTED PASSWORD '$1'" + elif [ "$DO_PAM_TEST" != "true" -a $2 = "pam" ];then + echo "do nothing because DO_PAM_TEST is not true and auth method is pam" + elif [ "$DO_LDAP_TEST" = "true" -a $2 = "ldap" ];then + psql -c "SET password_encryption = 'scram-sha-256'; ALTER USER $1 WITH ENCRYPTED PASSWORD '$1'" + elif [ "$DO_LDAP_TEST" != "true" -a $2 = "ldap" ];then + echo "do nothing because DO_LDAP_TEST is not true and auth method is ldap" + else + echo "wrong auth method \"$2\" for add_user $1" + exit 1 + fi +} + +# create pool_hba.conf entry +# $1: username +# $2: auth method for pool_hba.conf +# "scram", "md5" or "password" +# $3: pgpool major version first digit (e.g. "4") +function add_pool_hba() +{ + # "scram-sha-256" is only supported in pgpool version 4.0 or later + if [ $3 -gt 3 -a $2 = "scram" ];then + echo "host all $1 127.0.0.1/32 scram-sha-256" >> $POOL_HBA + echo "host all $1 ::1/128 scram-sha-256" >> $POOL_HBA + elif [ $2 = "md5" ];then + echo "host all $1 127.0.0.1/32 md5" >> $POOL_HBA + echo "host all $1 ::1/128 md5" >> $POOL_HBA + elif [ "$DO_PAM_TEST" = "true" -a $2 = "pam" ];then + echo "host all $1 127.0.0.1/32 pam" >> $POOL_HBA + echo "host all $1 ::1/128 pam" >> $POOL_HBA + elif [ "$DO_PAM_TEST" != "true" -a $2 = "pam" ];then + echo "do nothing because DO_PAM_TEST is not true and auth method is pam" + elif [ "$DO_LDAP_TEST" = "true" -a $2 = "ldap" ];then + echo "host all $1 127.0.0.1/32 ldap ldapserver=localhost ldapbasedn="dc=nodomain" ldapsearchattribute=uid" >> $POOL_HBA + echo "host all $1 ::1/128 ldap ldapserver=localhost ldapbasedn="dc=nodomain" ldapsearchattribute=uid" >> $POOL_HBA + elif [ "$DO_LDAP_TEST" != "true" -a $2 = "ldap" ];then + echo "do nothing because DO_LDAP_TEST is not true and auth method is ldap" + # "password" is only supported in pgpool version 4.0 or later + elif [ $3 -gt 3 -a $2 = "password" ];then + echo "host all $1 127.0.0.1/32 password" >> $POOL_HBA + echo "host all $1 ::1/128 password" >> $POOL_HBA + else + echo "skip adding to pool_hba.conf" + fi +} + +# create pool_passwd entry +# $1: username (also password) +# $2: encryption method (AES, md5 or text) +# $3: pgpool major version first digit (e.g. "4") +function add_pool_passwd() +{ + # "AES" is only supported in pgpool version 4.0 or later + if [ $3 -gt 3 -a $2 = "AES" ];then + echo $1|$PG_ENC -m -f $PGPOOL_CONF -u $1 $1 + elif [ $2 = "md5" ];then + echo $1|$PG_MD5 -m -f $PGPOOL_CONF -u $1 $1 + # "text" is only supported in pgpool version 4.0 or later + elif [ $3 -gt 3 -a $2 = "text" ];then + if [ $3 -gt 3 ];then + echo "$1:TEXT$1" >> $POOL_PASSWD + else + echo "$1:$1" >> $POOL_PASSWD + fi + else + echo "skip adding to pool_passwd" + fi +} + +# create pg_hba.conf entry +# $1: username +# $2: "scram", "md5", "password" or "pam". +function add_pg_hba() +{ + for i in data0 data1 + do + if [ ! -d $i ];then + continue + fi + + PG_HBA=$i/pg_hba.conf + if [ $2 = "scram" ];then + echo "host all $1 127.0.0.1/32 scram-sha-256" >> $PG_HBA + echo "host all $1 ::1/128 scram-sha-256" >> $PG_HBA + echo "local all $1 scram-sha-256" >> $PG_HBA + elif [ $2 = "md5" ];then + echo "host all $1 127.0.0.1/32 md5" >> $PG_HBA + echo "host all $1 ::1/128 md5" >> $PG_HBA + echo "local all $1 md5" >> $PG_HBA + elif [ $2 = "password" ];then + echo "host all $1 127.0.0.1/32 password" >> $PG_HBA + echo "host all $1 ::1/128 password" >> $PG_HBA + echo "local all $1 password" >> $PG_HBA + elif [ "$DO_PAM_TEST" = "true" -a $2 = "pam" ];then + echo "host all $1 127.0.0.1/32 pam" >> $PG_HBA + echo "host all $1 ::1/128 pam" >> $PG_HBA + echo "local all $1 pam" >> $PG_HBA + elif [ "$DO_PAM_TEST" != "true" -a $2 = "pam" ];then + echo "do nothing because DO_PAM_TEST is not true and auth method is pam" + elif [ "$DO_LDAP_TEST" = "true" -a $2 = "ldap" ];then + echo "host all $1 127.0.0.1/32 ldap ldapserver=localhost ldapbasedn="dc=nodomain" ldapsearchattribute=uid" >> $PG_HBA + echo "host all $1 ::1/128 ldap ldapserver=localhost ldapbasedn="dc=nodomain" ldapsearchattribute=uid" >> $PG_HBA + echo "local all $1 ldap ldapserver=localhost ldapbasedn="dc=nodomain" ldapsearchattribute=uid" >> $PG_HBA + elif [ "$DO_LDAP_TEST" != "true" -a $2 = "ldap" ];then + echo "do nothing because DO_LDAP_TEST is not true and auth method is ldap" + else + echo "wrong auth method \"$2\" for add_pg_hba $1" + exit 1 + fi + done +} + +# create pgpass entry +# $1: username +function add_pgpass() +{ + echo "127.0.0.1:$PGPORT:$PGDATABASE:$1:$1" >> pgpass + echo "127.0.0.1:$PGPORT:$PGDATABASE:$1:$1foo" >> pgpasswrong +} + +# Perform actual tests +# $1: test spec csv file +# $2: number of PostgreSQL nodes +# $3: clustering mode +function do_auth +{ + rm -fr $TESTDIR + mkdir $TESTDIR + cd $TESTDIR + + cp /dev/null $failed_usernames + mode=$3 + + # create test environment + echo -n "creating test environment..." + $PGPOOL_SETUP -m $mode -n $2 || exit 1 + #$PGPOOL_SETUP -m $mode -n 1 || exit 1 + echo "done." + + source ./bashrc.ports + export PGPORT=$PGPOOL_PORT + export PGDATABASE=test + + # Set max_init_children to 1 to make sure we reuse the + # connection to test wrong password rejection + echo "num_init_children = 1" >> etc/pgpool.conf + + PGPOOL_CONF=etc/pgpool.conf + POOL_HBA=etc/pool_hba.conf + if [ ! -f $POOL_HBA ];then + cp ../../../../../sample/pool_hba.conf.sample $POOL_HBA + fi + POOL_PASSWD=etc/pool_passwd + + # + # replace trust auth for all users with superuser in pg_hba.conf + for i in data0 data1 + do + if [ ! -d $i ];then + continue + fi + + sed -i "s/local *all *all *trust/local all $superuser trust/" $i/pg_hba.conf + sed -i "s/host *all *all *all *trust/host all $superuser all trust/" $i/pg_hba.conf + done + + # + # replace trust auth for all users with superuser in pool_hba.conf + sed -i "s@host all all 127.0.0.1/32 trust@host all $superuser all trust@" $POOL_HBA + sed -i "s@host all all ::1/128 trust@host all $superuser ::1/128 trust@" $POOL_HBA + + # set up pgpass + cp /dev/null pgpass + chmod 0600 pgpass + cp /dev/null pgpasswrong + chmod 0600 pgpasswrong + + # pgpool.conf opt + cp /dev/null $PGPOOL_CONF_OPT +# echo "include pgpool.conf.opt" >> etc/pgpool.conf + # add last line marker to pgpool.conf + echo "#last" >> etc/pgpool.conf + + # start pgpool + ./startall + wait_for_pgpool_startup + + IFS="," + + # + # setup each user + # + cat $spec|while read line + do + set $line + if [ $1 = 'username' ];then + echo "skip title row" + else + username=$1 + echo "==== $username ====" + pool_hba=$2 + allow_clear=$3 + pool_passwd=$4 + pg_hba=$5 + expected=$6 + + add_user $username $pg_hba + add_pool_hba $username $pool_hba $PGPOOL_VERSION_DIGIT + add_pool_passwd $username $pool_passwd $PGPOOL_VERSION_DIGIT + add_pg_hba $username $pg_hba + add_pgpass $username + fi + done + ./shutdownall + + # + # do auth tests + # + cat $spec|while read line + do + set $line + username=$1 + echo "==== $username ====" + pool_hba=$2 + allow_clear=$3 + pool_passwd=$4 + pg_hba=$5 + expected=$6 + + if [ $1 = 'username' ];then + echo "skip tile row" + elif [ "$DO_PAM_TEST" != "true" ] && [ $pool_hba = "pam" -o $pg_hba = "pam" ];then + echo "skip pam test" + elif [ "$DO_LDAP_TEST" != "true" ] && [ $pool_hba = "ldap" -o $pg_hba = "ldap" ];then + echo "skip ldap test" + else + cp $PGPOOL_CONF_OPT $PGPOOL_CONF_OPT.old + if [ $pool_hba = "scram" -o $pool_hba = "md5" -o $pool_hba = "password" -o $pool_hba = "pam" -o $pool_hba = "ldap" ];then + echo "enable_pool_hba = on" > $PGPOOL_CONF_OPT + last=`tail -1 etc/pgpool.conf` + if [ "$last" != "#last" ];then + # remove the last line if it's not a marker + echo "remove the last line in pgpool.conf because \"$last\"" + sed -i '$d' etc/pgpool.conf + fi + cat $PGPOOL_CONF_OPT >> etc/pgpool.conf + echo "add \"`cat $PGPOOL_CONF_OPT`\"" + else + if [ $allow_clear = "off" ];then + cp /dev/null $PGPOOL_CONF_OPT + else + echo "allow_clear_text_frontend_auth = on" > $PGPOOL_CONF_OPT + fi + last=`tail -1 etc/pgpool.conf` + if [ $last != "#last" ];then + # remove the last line if it's not a marker + echo "remove the last line in pgpool.conf because \"$last\"" + sed -i '$d' etc/pgpool.conf + fi + cat $PGPOOL_CONF_OPT >> etc/pgpool.conf + echo "add \"`cat $PGPOOL_CONF_OPT`\"" + fi + + # if pgpool.conf.opt was changed, restart pgpool + cmp $PGPOOL_CONF_OPT $PGPOOL_CONF_OPT.old + if [ $? != 0 ];then + ./shutdownall + ./startall + wait_for_pgpool_startup + fi + + result="" + PGPASSFILE=pgpass $PSQL -h 127.0.0.1 -U $username -c "SELECT user" >/dev/null 2>&1 + rtn=$? + if [ $expected = "ok" ];then + echo -n "checking $username auth expecting success..." + if [ $rtn = 0 ];then + echo "ok." + echo -n "checking $username auth expecting success in reauth..." + PGPASSFILE=pgpass $PSQL -h 127.0.0.1 -U $username -c "SELECT user" >/dev/null 2>&1 + if [ $? != 0 ];then + echo "$username: password verification on reauth failed." + else + # reauth was Ok. + echo "ok." + echo -n "$username: try with wrong password expecting rejected..." + PGPASSFILE=pgpasswrong $PSQL -h 127.0.0.1 -U $username -c "SELECT user">/dev/null 2>&1 + if [ $? = 0 ];then + echo "$username: wrong password verification failed." + echo "$mode $num_backends $username" >> $unexpected_passwd_verifi + else + echo "ok." + result=ok + fi + fi + else + echo "$username: password verification failed." + fi + else # expecting fail + echo -n "checking $username auth expecting failure..." + if [ $rtn != 0 ];then + echo "ok." + result=ok + else + # make sure that wrong password is rejected + PGPASSFILE=pgpasswrong $PSQL -h 127.0.0.1 -U $username -c "SELECT user">/dev/null 2>&1 + if [ $? = 0 ];then + echo "$username: unexpected successfull password verification" + echo "$mode $num_backends $username" >> $unexpected_passwd_verifi + else + # maybe this test case should have been "ok", not "fail"? + echo "$username: while expecting fail, wrong password was rejected but proper password was accepted" + fi + fi + fi + + if [ "$result" != "ok" ];then + echo -n " $username" >> $failed_usernames + fi + fi + done + + ./shutdownall + cd .. + + if [ -s $failed_usernames ];then + echo "failed tests:" + cat $failed_usernames + echo + fi +} + +# +#---------------------------------------- +# Misc preparations + +# pgpool major version first digit (e.g. "4") +PGPOOL_VERSION_DIGIT=`echo $PGPOOL_VERSION|awk '{print $3}'|sed 's/\([1-9]\).*$/\1/'` +failed_usernames=/tmp/failed_usernames +failed_modes=/tmp/failed_modes +unexpected_passwd_verifi=/tmp/unexpected_passwd_verificatio +total_failed_usernames=/tmp/total_failed_usernames +trap "rm $failed_usernames $failed_modes $unexpected_passwd_verifi $total_failed_usernames" EXIT +cp /dev/null $failed_usernames +cp /dev/null $failed_modes +cp /dev/null $unexpected_passwd_verifi +cp /dev/null $total_failed_usernames + +source $TESTLIBS +TESTDIR=testdir +PSQL=$PGBIN/psql +PG_ENC=$PGPOOL_INSTALL_DIR/bin/pg_enc +PG_MD5=$PGPOOL_INSTALL_DIR/bin/pg_md5 +export CREATEUSER=$PGBIN/createuser +superuser=`whoami` +PGPOOL_CONF_OPT=etc/pgpool.conf.opt +# .pgpoolkey +export PGPOOLKEYFILE=`pwd`/pgpoolkey +echo "secret" > $PGPOOLKEYFILE +chmod 0600 $PGPOOLKEYFILE +# +#---------------------------------------- +# Test execution starts here + +# pgpool_setup does not create a replication environment for logical +# replication mode and slony mode, which makes the test failed +# horribly because PostgreSQL roles are not replicated. Therefore we +# skip the tests for these modes. + +for mode in s r i n +#for mode in s r i n l y +do + echo "==== testing mode: $mode ===" + anyfail="" + spec=../client_auth_2node.csv + num_backends=2 + do_auth $spec $num_backends $mode + if [ -s $failed_usernames ];then + anyfail="yes" + echo "$mode $num_backends `cat $failed_usernames`" >> $total_failed_usernames + fi + + if [ -n "$anyfail" ];then + echo -n "$mode " >> $failed_modes + fi +done + +if [ -s $unexpected_passwd_verifi ];then + echo + echo "IMPORTANT: unexpected successful password verfication found:" + cat $unexpected_passwd_verifi +fi + +if [ -s $failed_modes ];then + echo + echo "failed mode: `cat $failed_modes`" + echo + echo "failed tests:" + cat $total_failed_usernames + exit 1 +fi + +exit 0 -- 2.30.2