diff --git a/contrib/passwordcheck/expected/passwordcheck.out b/contrib/passwordcheck/expected/passwordcheck.out
index 83472c76d278..602e91c50ac1 100644
--- a/contrib/passwordcheck/expected/passwordcheck.out
+++ b/contrib/passwordcheck/expected/passwordcheck.out
@@ -3,6 +3,9 @@ LOAD 'passwordcheck';
CREATE USER regress_passwordcheck_user1;
-- ok
ALTER USER regress_passwordcheck_user1 PASSWORD 'a_nice_long_password';
+WARNING: using a clear text password
+DETAIL: Sending a password using plain text is deprecated and may be removed in a future release of PostgreSQL.
+HINT: Use a client that can change the password without sending it in clear text
-- error: too short
ALTER USER regress_passwordcheck_user1 PASSWORD 'tooshrt';
ERROR: password is too short
@@ -10,6 +13,9 @@ DETAIL: password must be at least "passwordcheck.min_password_length" (8) bytes
-- ok
SET passwordcheck.min_password_length = 6;
ALTER USER regress_passwordcheck_user1 PASSWORD 'v_shrt';
+WARNING: using a clear text password
+DETAIL: Sending a password using plain text is deprecated and may be removed in a future release of PostgreSQL.
+HINT: Use a client that can change the password without sending it in clear text
-- error: contains user name
ALTER USER regress_passwordcheck_user1 PASSWORD 'xyzregress_passwordcheck_user1';
ERROR: password must not contain user name
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 20ccb2d6b544..b7c707b11b96 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -1104,6 +1104,24 @@ include_dir 'conf.d'
+
+ cleartext_passwords_action (enum)
+
+ cleartext_passwords_action configuration parameter
+
+
+
+
+ Controls what action to take when a password is sent unencrypted
+ (i.e. in clear text) to the server via the CREATE ROLE
+ or ALTER ROLE command. Valid options are
+ allow, warn, or
+ disallow.
+ The default value is warn.
+
+
+
+
password_encryption (enum)
diff --git a/src/backend/libpq/crypt.c b/src/backend/libpq/crypt.c
index f6b641e726ec..808f6a306557 100644
--- a/src/backend/libpq/crypt.c
+++ b/src/backend/libpq/crypt.c
@@ -21,12 +21,16 @@
#include "libpq/crypt.h"
#include "libpq/scram.h"
#include "utils/builtins.h"
+#include "utils/guc.h"
#include "utils/syscache.h"
#include "utils/timestamp.h"
/* Enables deprecation warnings for MD5 passwords. */
bool md5_password_warnings = true;
+/* Action to take when clear text passwords are used. */
+int cleartext_passwords_action = CLEARTEXT_ACTION_WARN;
+
/*
* Fetch stored password for a user, for authentication.
*
@@ -131,6 +135,36 @@ encrypt_password(PasswordType target_type, const char *role,
}
else
{
+
+ /*
+ * We are sending clear text passwords to the server. What should we
+ * do about that?
+ */
+ if (cleartext_passwords_action == CLEARTEXT_ACTION_WARN)
+ {
+ ereport(WARNING,
+ (errcode(ERRCODE_WARNING_DEPRECATED_FEATURE),
+ errmsg("using a clear text password"),
+ errdetail("Sending a password using plain text is deprecated and may be removed in a future release of PostgreSQL."),
+ strncmp(application_name, "psql", 5) == 0
+ ? errhint("If using psql, you can set the password with \\password")
+ : errhint("Use a client that can change the password without sending it in clear text")));
+ }
+ else if (cleartext_passwords_action == CLEARTEXT_ACTION_DISALLOW)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PASSWORD),
+ errmsg("using a clear text password"),
+ errdetail("Sending a password using plain text is not allowed."),
+ strncmp(application_name, "psql", 5) == 0
+ ? errhint("If using psql, you can change the password with \\password")
+ : errhint("Use a client that can change the password without sending it in clear text")));
+ }
+ else
+ {
+ /* Silently accept this bad practice. */
+ }
+
switch (target_type)
{
case PASSWORD_TYPE_MD5:
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index d14b1678e7fe..627ee9b6fcc0 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -417,6 +417,13 @@ static const struct config_enum_entry password_encryption_options[] = {
{NULL, 0, false}
};
+static const struct config_enum_entry cleartext_action_options[] = {
+ {"allow", CLEARTEXT_ACTION_ALLOW, false},
+ {"warn", CLEARTEXT_ACTION_WARN, false},
+ {"disallow", CLEARTEXT_ACTION_DISALLOW, false},
+ {NULL, 0, false}
+};
+
static const struct config_enum_entry ssl_protocol_versions_info[] = {
{"", PG_TLS_ANY, false},
{"TLSv1", PG_TLS1_VERSION, false},
@@ -5418,6 +5425,16 @@ struct config_enum ConfigureNamesEnum[] =
NULL, assign_io_method, NULL
},
+ {
+ {"cleartext_passwords_action", PGC_SIGHUP, CONN_AUTH_AUTH,
+ gettext_noop("Action to take when clear text passwords are used."),
+ },
+ &cleartext_passwords_action,
+ CLEARTEXT_ACTION_WARN, cleartext_action_options,
+ NULL, NULL, NULL
+ },
+
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, NULL, NULL, NULL, NULL
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index a9d8293474af..978ac20c73df 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -97,6 +97,7 @@
#password_encryption = scram-sha-256 # scram-sha-256 or md5
#scram_iterations = 4096
#md5_password_warnings = on
+#cleartext_passwords_action = warn # can be allow, warn, or disallow
#oauth_validator_libraries = '' # comma-separated list of trusted validator modules
# GSSAPI using Kerberos
diff --git a/src/include/libpq/crypt.h b/src/include/libpq/crypt.h
index a1b4b3631433..921de2d8e6fb 100644
--- a/src/include/libpq/crypt.h
+++ b/src/include/libpq/crypt.h
@@ -28,6 +28,9 @@
/* Enables deprecation warnings for MD5 passwords. */
extern PGDLLIMPORT bool md5_password_warnings;
+/* Specifies action when clear text passwords are used. */
+extern PGDLLIMPORT int cleartext_passwords_action;
+
/*
* Types of password hashes or secrets.
*
@@ -44,6 +47,22 @@ typedef enum PasswordType
PASSWORD_TYPE_SCRAM_SHA_256,
} PasswordType;
+/*
+ * Actions to take when clear text passwords are used.
+ *
+ * Passwords that are sent in clear text via the CREATE/ALTER USER
+ * command can cause a reaction by the server. We can either allow
+ * (the old behavior), warn (throw a warning and hint), or simply
+ * disallow (throws an exception).
+ */
+typedef enum CleartextAction
+{
+ CLEARTEXT_ACTION_ALLOW = 0,
+ CLEARTEXT_ACTION_WARN,
+ CLEARTEXT_ACTION_DISALLOW,
+} CleartextAction;
+
+
extern PasswordType get_password_type(const char *shadow_pass);
extern char *encrypt_password(PasswordType target_type, const char *role,
const char *password);
diff --git a/src/interfaces/ecpg/test/expected/connect-test5.stderr b/src/interfaces/ecpg/test/expected/connect-test5.stderr
index 037db217586d..d74cc1f1e818 100644
--- a/src/interfaces/ecpg/test/expected/connect-test5.stderr
+++ b/src/interfaces/ecpg/test/expected/connect-test5.stderr
@@ -4,16 +4,24 @@
[NO_PID]: sqlca: code: 0, state: 00000
[NO_PID]: ecpg_execute on line 24: query: alter user regress_ecpg_user2 encrypted password 'insecure'; with 0 parameter(s) on connection main
[NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 24: using PQexec
+[NO_PID]: ECPGnoticeReceiver: using a clear text password
[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: raising sqlcode 0
+[NO_PID]: sqlca: code: 0, state: 01P01
+[NO_PID]: ecpg_execute on line 24: using PQexec
+[NO_PID]: sqlca: code: 0, state: 01P01
[NO_PID]: ecpg_process_output on line 24: OK: ALTER ROLE
-[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: sqlca: code: 0, state: 01P01
[NO_PID]: ecpg_execute on line 25: query: alter user regress_ecpg_user1 encrypted password 'connectpw'; with 0 parameter(s) on connection main
[NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 25: using PQexec
+[NO_PID]: ECPGnoticeReceiver: using a clear text password
[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: raising sqlcode 0
+[NO_PID]: sqlca: code: 0, state: 01P01
+[NO_PID]: ecpg_execute on line 25: using PQexec
+[NO_PID]: sqlca: code: 0, state: 01P01
[NO_PID]: ecpg_process_output on line 25: OK: ALTER ROLE
-[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: sqlca: code: 0, state: 01P01
[NO_PID]: ECPGtrans on line 26: action "commit"; connection "main"
[NO_PID]: sqlca: code: 0, state: 00000
[NO_PID]: ecpg_finish: connection main closed
diff --git a/src/test/regress/expected/create_role.out b/src/test/regress/expected/create_role.out
index 46d4f9efe997..8347205e4aa8 100644
--- a/src/test/regress/expected/create_role.out
+++ b/src/test/regress/expected/create_role.out
@@ -64,6 +64,9 @@ CREATE ROLE regress_login LOGIN;
CREATE ROLE regress_inherit INHERIT;
CREATE ROLE regress_connection_limit CONNECTION LIMIT 5;
CREATE ROLE regress_encrypted_password ENCRYPTED PASSWORD 'foo';
+WARNING: using a clear text password
+DETAIL: Sending a password using plain text is deprecated and may be removed in a future release of PostgreSQL.
+HINT: Use a client that can change the password without sending it in clear text
CREATE ROLE regress_password_null PASSWORD NULL;
-- ok, backwards compatible noise words should be ignored
CREATE ROLE regress_noiseword SYSID 12345;
diff --git a/src/test/regress/expected/password.out b/src/test/regress/expected/password.out
index 9bb3ab2818bf..ec76afcbcef9 100644
--- a/src/test/regress/expected/password.out
+++ b/src/test/regress/expected/password.out
@@ -14,16 +14,25 @@ SET password_encryption = 'scram-sha-256'; -- ok
SET password_encryption = 'md5';
CREATE ROLE regress_passwd1;
ALTER ROLE regress_passwd1 PASSWORD 'role_pwd1';
+WARNING: using a clear text password
+DETAIL: Sending a password using plain text is deprecated and may be removed in a future release of PostgreSQL.
+HINT: Use a client that can change the password without sending it in clear text
WARNING: setting an MD5-encrypted password
DETAIL: MD5 password support is deprecated and will be removed in a future release of PostgreSQL.
HINT: Refer to the PostgreSQL documentation for details about migrating to another password type.
CREATE ROLE regress_passwd2;
ALTER ROLE regress_passwd2 PASSWORD 'role_pwd2';
+WARNING: using a clear text password
+DETAIL: Sending a password using plain text is deprecated and may be removed in a future release of PostgreSQL.
+HINT: Use a client that can change the password without sending it in clear text
WARNING: setting an MD5-encrypted password
DETAIL: MD5 password support is deprecated and will be removed in a future release of PostgreSQL.
HINT: Refer to the PostgreSQL documentation for details about migrating to another password type.
SET password_encryption = 'scram-sha-256';
CREATE ROLE regress_passwd3 PASSWORD 'role_pwd3';
+WARNING: using a clear text password
+DETAIL: Sending a password using plain text is deprecated and may be removed in a future release of PostgreSQL.
+HINT: Use a client that can change the password without sending it in clear text
CREATE ROLE regress_passwd4 PASSWORD NULL;
-- check list of created entries
--
@@ -63,6 +72,9 @@ ALTER ROLE regress_passwd2_new RENAME TO regress_passwd2;
SET password_encryption = 'md5';
-- encrypt with MD5
ALTER ROLE regress_passwd2 PASSWORD 'foo';
+WARNING: using a clear text password
+DETAIL: Sending a password using plain text is deprecated and may be removed in a future release of PostgreSQL.
+HINT: Use a client that can change the password without sending it in clear text
WARNING: setting an MD5-encrypted password
DETAIL: MD5 password support is deprecated and will be removed in a future release of PostgreSQL.
HINT: Refer to the PostgreSQL documentation for details about migrating to another password type.
@@ -75,6 +87,9 @@ ALTER ROLE regress_passwd3 PASSWORD 'SCRAM-SHA-256$4096:VLK4RMaQLCvNtQ==$6YtlR4t
SET password_encryption = 'scram-sha-256';
-- create SCRAM secret
ALTER ROLE regress_passwd4 PASSWORD 'foo';
+WARNING: using a clear text password
+DETAIL: Sending a password using plain text is deprecated and may be removed in a future release of PostgreSQL.
+HINT: Use a client that can change the password without sending it in clear text
-- already encrypted with MD5, use as it is
CREATE ROLE regress_passwd5 PASSWORD 'md5e73a4b11df52a6068f8b39f90be36023';
WARNING: setting an MD5-encrypted password
@@ -83,15 +98,27 @@ HINT: Refer to the PostgreSQL documentation for details about migrating to anot
-- This looks like a valid SCRAM-SHA-256 secret, but it is not
-- so it should be hashed with SCRAM-SHA-256.
CREATE ROLE regress_passwd6 PASSWORD 'SCRAM-SHA-256$1234';
+WARNING: using a clear text password
+DETAIL: Sending a password using plain text is deprecated and may be removed in a future release of PostgreSQL.
+HINT: Use a client that can change the password without sending it in clear text
-- These may look like valid MD5 secrets, but they are not, so they
-- should be hashed with SCRAM-SHA-256.
-- trailing garbage at the end
CREATE ROLE regress_passwd7 PASSWORD 'md5012345678901234567890123456789zz';
+WARNING: using a clear text password
+DETAIL: Sending a password using plain text is deprecated and may be removed in a future release of PostgreSQL.
+HINT: Use a client that can change the password without sending it in clear text
-- invalid length
CREATE ROLE regress_passwd8 PASSWORD 'md501234567890123456789012345678901zz';
+WARNING: using a clear text password
+DETAIL: Sending a password using plain text is deprecated and may be removed in a future release of PostgreSQL.
+HINT: Use a client that can change the password without sending it in clear text
-- Changing the SCRAM iteration count
SET scram_iterations = 1024;
CREATE ROLE regress_passwd9 PASSWORD 'alterediterationcount';
+WARNING: using a clear text password
+DETAIL: Sending a password using plain text is deprecated and may be removed in a future release of PostgreSQL.
+HINT: Use a client that can change the password without sending it in clear text
SELECT rolname, regexp_replace(rolpassword, '(SCRAM-SHA-256)\$(\d+):([a-zA-Z0-9+/=]+)\$([a-zA-Z0-9+=/]+):([a-zA-Z0-9+/=]+)', '\1$\2:$:') as rolpassword_masked
FROM pg_authid
WHERE rolname LIKE 'regress_passwd%'
@@ -128,7 +155,13 @@ SELECT rolpassword FROM pg_authid WHERE rolname='regress_passwd_empty';
-- stored/server keys. They will be re-hashed.
CREATE ROLE regress_passwd_sha_len0 PASSWORD 'SCRAM-SHA-256$4096:A6xHKoH/494E941doaPOYg==$Ky+A30sewHIH3VHQLRN9vYsuzlgNyGNKCh37dy96Rqw=:COPdlNiIkrsacU5QoxydEuOH6e/KfiipeETb/bPw8ZI=';
CREATE ROLE regress_passwd_sha_len1 PASSWORD 'SCRAM-SHA-256$4096:A6xHKoH/494E941doaPOYg==$Ky+A30sewHIH3VHQLRN9vYsuzlgNyGNKCh37dy96RqwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=:COPdlNiIkrsacU5QoxydEuOH6e/KfiipeETb/bPw8ZI=';
+WARNING: using a clear text password
+DETAIL: Sending a password using plain text is deprecated and may be removed in a future release of PostgreSQL.
+HINT: Use a client that can change the password without sending it in clear text
CREATE ROLE regress_passwd_sha_len2 PASSWORD 'SCRAM-SHA-256$4096:A6xHKoH/494E941doaPOYg==$Ky+A30sewHIH3VHQLRN9vYsuzlgNyGNKCh37dy96Rqw=:COPdlNiIkrsacU5QoxydEuOH6e/KfiipeETb/bPw8ZIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=';
+WARNING: using a clear text password
+DETAIL: Sending a password using plain text is deprecated and may be removed in a future release of PostgreSQL.
+HINT: Use a client that can change the password without sending it in clear text
-- Check that the invalid secrets were re-hashed. A re-hashed secret
-- should not contain the original salt.
SELECT rolname, rolpassword not like '%A6xHKoH/494E941doaPOYg==%' as is_rolpassword_rehashed