diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c index 8ff1782d776..93ffef3c1d2 100644 --- a/src/backend/commands/user.c +++ b/src/backend/commands/user.c @@ -1452,6 +1452,37 @@ AlterRole(AlterRoleStmt *stmt) { char *shadow_pass; char *logdetail; + + /* Like in CREATE USER, don't allow an empty password. */ + if (password[0] == '\0' || + plain_crypt_verify(rolename, password, "", &logdetail) == STATUS_OK) + { + ereport(NOTICE, + (errmsg("empty string is not a valid password, clearing password"))); + new_record_nulls[Anum_pg_authid_rolpassword - 1] = true; + } + else + { + /* Encrypt the password to the requested format. */ + shadow_pass = encrypt_password(Password_encryption, rolename, + password); + new_record[Anum_pg_authid_rolpassword - 1] = + CStringGetTextDatum(shadow_pass); + } + new_record_repl[Anum_pg_authid_rolpassword - 1] = true; + } + + /* unset password */ + if (dpassword && dpassword->arg == NULL) + { + new_record_repl[Anum_pg_authid_rolpassword - 1] = true; + new_record_nulls[Anum_pg_authid_rolpassword - 1] = true; + } + + + if ((password || (dpassword && dpassword->arg == NULL)) && + (authform->rolenableprofile || enable_profile) && enable_password_profile) + { Datum datum; bool isnull; bool setat_isnull; @@ -1459,55 +1490,38 @@ AlterRole(AlterRoleStmt *stmt) int32 profile_reuse_max = 0; SysScanDesc password_history_scan; HeapTuple profiletuple; + char *logdetail; + bool ignore_password_history = false; pg_profile_rel = table_open(ProfileRelationId, AccessShareLock); pg_profile_dsc = RelationGetDescr(pg_profile_rel); datum = SysCacheGetAttr(AUTHNAME, tuple, - Anum_pg_authid_rolprofile, &isnull); + Anum_pg_authid_rolprofile, &isnull); Assert(!isnull); profileid = DatumGetObjectId(datum); profiletuple = SearchSysCache1(PROFILEID, ObjectIdGetDatum(profileid)); if (!HeapTupleIsValid(profiletuple)) ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("profile \"%d\" does not exist", profileid))); + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("profile \"%d\" does not exist", profileid))); /* Get PASSWORD_REUSE_MAX from profile tuple and transform it to normal value */ profileform = (Form_pg_profile) GETSTRUCT(profiletuple); profile_reuse_max = tranformProfileValueToNormal(profileform->prfpasswordreusemax, - Anum_pg_profile_prfpasswordreusemax); + Anum_pg_profile_prfpasswordreusemax); ReleaseSysCache(profiletuple); if (profile_reuse_max == 0) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PASSWORD), - errmsg("can't alter user password for profile's password_reuse_max is zero."))); + ignore_password_history = true; /* * Get shadow password from pg_authid */ datum = SysCacheGetAttr(AUTHNAME, tuple, - Anum_pg_authid_rolpassword, &isnull); - - /* Like in CREATE USER, don't allow an empty password. */ - if (password[0] == '\0' || - plain_crypt_verify(rolename, password, "", &logdetail) == STATUS_OK) - { - ereport(NOTICE, - (errmsg("empty string is not a valid password, clearing password"))); - new_record_nulls[Anum_pg_authid_rolpassword - 1] = true; - } - else - { - /* Encrypt the password to the requested format. */ - shadow_pass = encrypt_password(Password_encryption, rolename, - password); - new_record[Anum_pg_authid_rolpassword - 1] = - CStringGetTextDatum(shadow_pass); - } + Anum_pg_authid_rolpassword, &isnull); /* * Disallow to use recently passwords which controlled by @@ -1536,7 +1550,7 @@ AlterRole(AlterRoleStmt *stmt) history_shadow_pass = TextDatumGetCString(datum); datum = SysCacheGetAttr(AUTHNAME, tuple, - Anum_pg_authid_rolpasswordsetat, &setat_isnull); + Anum_pg_authid_rolpasswordsetat, &setat_isnull); Assert(!setat_isnull); history_password_set_at = DatumGetInt64(datum); @@ -1553,9 +1567,9 @@ AlterRole(AlterRoleStmt *stmt) /* Form the insert tuple */ password_history_tuple = heap_form_tuple(pg_password_history_dsc, - password_history_record, password_nulls); + password_history_record, password_nulls); - /* Insert new record into the pg_password_history table */ + /* Insert new record into the pg_password_history table */ CatalogTupleInsert(pg_password_history_rel, password_history_tuple); /* Advance command counter so we can see new record */ @@ -1563,16 +1577,16 @@ AlterRole(AlterRoleStmt *stmt) /* form a scan key */ ScanKeyInit(&skey, - Anum_pg_password_history_passhistroleid, - BTEqualStrategyNumber, F_OIDEQ, - ObjectIdGetDatum(roleid)); + Anum_pg_password_history_passhistroleid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(roleid)); /* * Get only recently PASSWORD_REUSE_MAX tuples. */ password_history_scan = systable_beginscan_ordered(pg_password_history_rel, - pg_password_history_passwordsetat_idx, - NULL, 1, &skey); + pg_password_history_passwordsetat_idx, + NULL, 1, &skey); for (i = 0; i < profile_reuse_max; i++) { password_history_tuple = systable_getnext_ordered(password_history_scan, BackwardScanDirection); @@ -1581,7 +1595,7 @@ AlterRole(AlterRoleStmt *stmt) break; datum = heap_getattr(password_history_tuple, Anum_pg_password_history_passhistpassword, - pg_password_history_dsc, &isnull); + pg_password_history_dsc, &isnull); Assert(!isnull); history_shadow_pass = text_to_cstring(DatumGetTextP(datum)); @@ -1589,11 +1603,12 @@ AlterRole(AlterRoleStmt *stmt) * Use password verify function to check whether password * has been recorded in pg_password_history. */ - if (plain_crypt_verify(rolename, history_shadow_pass, password, &logdetail) == STATUS_OK) + if (!ignore_password_history && password && + plain_crypt_verify(rolename, history_shadow_pass, password, &logdetail) == STATUS_OK) ereport(ERROR, - (errcode(ERRCODE_INVALID_PASSWORD), - errmsg("The new password should not be the same with latest %d history password", - profile_reuse_max))); + (errcode(ERRCODE_INVALID_PASSWORD), + errmsg("The new password should not be the same with latest %d history password", + profile_reuse_max))); } systable_endscan_ordered(password_history_scan); @@ -1606,18 +1621,10 @@ AlterRole(AlterRoleStmt *stmt) new_record[Anum_pg_authid_rolpasswordsetat - 1] = Int64GetDatum(password_set_at); new_record_repl[Anum_pg_authid_rolpasswordsetat - 1] = true; - new_record_repl[Anum_pg_authid_rolpassword - 1] = true; table_close(pg_profile_rel, NoLock); } - /* unset password */ - if (dpassword && dpassword->arg == NULL) - { - new_record_repl[Anum_pg_authid_rolpassword - 1] = true; - new_record_nulls[Anum_pg_authid_rolpassword - 1] = true; - } - /* valid until */ new_record[Anum_pg_authid_rolvaliduntil - 1] = validUntil_datum; new_record_nulls[Anum_pg_authid_rolvaliduntil - 1] = validUntil_null; diff --git a/src/backend/postmaster/loginmonitor.c b/src/backend/postmaster/loginmonitor.c index d67fcc818cf..7af7006984e 100644 --- a/src/backend/postmaster/loginmonitor.c +++ b/src/backend/postmaster/loginmonitor.c @@ -313,7 +313,8 @@ LoginMonitorLauncherMain(int argc, char *argv[]) { RESUME_INTERRUPTS(); /* reset and notify process by latch */ - SetLatch(LoginMonitorShmem->latch); + if (LoginMonitorShmem->latch) + SetLatch(LoginMonitorShmem->latch); ResetLatch(LoginMonitorShmem->lm_latch); LoginMonitorShmemReset(); diff --git a/src/include/catalog/pg_profile.dat b/src/include/catalog/pg_profile.dat index d76b974e753..80a193d6740 100644 --- a/src/include/catalog/pg_profile.dat +++ b/src/include/catalog/pg_profile.dat @@ -22,7 +22,7 @@ descr => 'default profile', prfname => 'pg_default', prffailedloginattempts => '-2', prfpasswordlocktime => '-2', prfpasswordlifetime => '-2', prfpasswordgracetime => '-2', prfpasswordreusetime => '-2', - prfpasswordreusemax => '-2', prfpasswordallowhashed => '1', prfpasswordverifyfuncdb => '_null_', + prfpasswordreusemax => '0', prfpasswordallowhashed => '1', prfpasswordverifyfuncdb => '_null_', prfpasswordverifyfunc => '_null_' } ] diff --git a/src/test/regress/expected/profile.out b/src/test/regress/expected/profile.out index 9541ced3c6d..b7fcbd66024 100644 --- a/src/test/regress/expected/profile.out +++ b/src/test/regress/expected/profile.out @@ -148,7 +148,7 @@ SELECT prfname, prffailedloginattempts, prfpasswordlocktime, prfpasswordreusemax FROM pg_profile; prfname | prffailedloginattempts | prfpasswordlocktime | prfpasswordreusemax ------------+------------------------+---------------------+--------------------- - pg_default | -2 | -2 | -2 + pg_default | -2 | -2 | 0 (1 row) -- Test CREATE PROFILE @@ -162,7 +162,7 @@ SELECT prfname, prffailedloginattempts, prfpasswordlocktime, prfpasswordreusemax FROM pg_profile; prfname | prffailedloginattempts | prfpasswordlocktime | prfpasswordreusemax ------------+------------------------+---------------------+--------------------- - pg_default | -2 | -2 | -2 + pg_default | -2 | -2 | 0 myprofile1 | -1 | -1 | -1 myprofile2 | -1 | -2 | -1 myprofile3 | 4 | 1 | -1 @@ -215,7 +215,7 @@ SELECT prfname, prffailedloginattempts, prfpasswordlocktime, prfpasswordreusemax FROM pg_profile; prfname | prffailedloginattempts | prfpasswordlocktime | prfpasswordreusemax ------------+------------------------+---------------------+--------------------- - pg_default | -2 | -2 | -2 + pg_default | -2 | -2 | 0 myprofile1 | -1 | -1 | -1 myprofile2 | -1 | -2 | -1 myprofile3 | 4 | 1 | -1 @@ -512,31 +512,24 @@ ALTER USER profile_user1 PASSWORD 'test'; ALTER USER profile_user1 PASSWORD 'a_nice_long_password_123'; ALTER USER profile_user1 PASSWORD 'a_new_password'; ALTER USER profile_user1 PASSWORD 'test'; -ERROR: The new password should not be the same with latest 3 history password ALTER USER profile_user1 PASSWORD 'a_nice_long_password_123'; -ERROR: The new password should not be the same with latest 3 history password ALTER USER profile_user1 PASSWORD 'a_new_password'; -ERROR: The new password should not be the same with latest 3 history password ALTER USER profile_user1 PASSWORD 'ABCD'; ALTER USER profile_user1 PASSWORD 'test'; ALTER PROFILE pg_default LIMIT PASSWORD_REUSE_MAX 4; ALTER USER profile_user1 PASSWORD 'a_nice_long_password_123'; -ERROR: The new password should not be the same with latest 4 history password ALTER USER profile_user2 PASSWORD 'test2'; ALTER USER profile_user2 PASSWORD 'a_bad_password'; -ALTER USER profile_user2 PASSWORD 'test2'; -ERROR: The new password should not be the same with latest 2 history password +ALTER USER profile_user2 PASSWORD 'test2' ENABLE PROFILE; ALTER USER profile_user2 PASSWORD 'a_bad_password'; ERROR: The new password should not be the same with latest 2 history password ALTER USER profile_user2 PASSWORD 'a_nice_password'; ALTER USER profile_user2 PASSWORD 'a_bad_password'; -ERROR: The new password should not be the same with latest 2 history password ALTER USER profile_user2 PASSWORD 'test2'; ALTER PROFILE myprofile3 LIMIT PASSWORD_REUSE_MAX 1; ALTER USER profile_user2 PASSWORD 'a_bad_password'; -- OK ALTER USER profile_user2 PASSWORD 'test2'; -- OK -ALTER USER profile_user4 PASSWORD 'test3'; -- failed -ERROR: can't alter user password for profile's password_reuse_max is zero. +ALTER USER profile_user4 PASSWORD 'test3'; -- OK DELETE FROM pg_password_history; -- failed for catalog can't be deleted ERROR: permission denied: "pg_password_history" is a system catalog -- Test ALTER USER ... ACCOUNT LOCK/UNLOCK @@ -554,7 +547,7 @@ AND rolname like '%profile_user%'; profile_user5 | myprofile3 | 0 | 0 | f profile_user7 | myprofile4 | 0 | 0 | t profile_user1 | myprofile1 | 2 | 0 | f - profile_user2 | myprofile3 | 0 | 0 | f + profile_user2 | myprofile3 | 0 | 0 | t profile_user3 | myprofile2 | 2 | 0 | f profile_user4 | myprofile4 | 0 | 0 | f (7 rows) @@ -600,7 +593,7 @@ AND rolname like '%profile_user%'; profile_user5 | myprofile3 | 0 | 0 | f profile_user7 | myprofile4 | 0 | 0 | t profile_user1 | myprofile1 | 2 | 0 | f - profile_user2 | myprofile3 | 0 | 0 | f + profile_user2 | myprofile3 | 0 | 0 | t profile_user3 | myprofile2 | 2 | 0 | f profile_user4 | myprofile4 | 0 | 0 | f (7 rows) @@ -637,7 +630,7 @@ AND rolname like '%profile_user%'; profile_user5 | myprofile3 | 0 | 0 | f profile_user7 | myprofile4 | 0 | 0 | t profile_user1 | myprofile1 | 2 | 0 | f - profile_user2 | myprofile3 | 0 | 0 | f + profile_user2 | myprofile3 | 0 | 0 | t profile_user3 | myprofile2 | 2 | 0 | f profile_user4 | myprofile4 | 0 | 0 | f (7 rows) @@ -687,3 +680,5 @@ FROM pg_profile; pg_default | 2 | 1 | 4 (1 row) +-- Reset pg_default +ALTER PROFILE pg_default LIMIT FAILED_LOGIN_ATTEMPTS -2 PASSWORD_LOCK_TIME -2 PASSWORD_REUSE_MAX 0; diff --git a/src/test/regress/sql/profile.sql b/src/test/regress/sql/profile.sql index 18435149afe..9d404375878 100644 --- a/src/test/regress/sql/profile.sql +++ b/src/test/regress/sql/profile.sql @@ -203,7 +203,7 @@ ALTER USER profile_user1 PASSWORD 'a_nice_long_password_123'; ALTER USER profile_user2 PASSWORD 'test2'; ALTER USER profile_user2 PASSWORD 'a_bad_password'; -ALTER USER profile_user2 PASSWORD 'test2'; +ALTER USER profile_user2 PASSWORD 'test2' ENABLE PROFILE; ALTER USER profile_user2 PASSWORD 'a_bad_password'; ALTER USER profile_user2 PASSWORD 'a_nice_password'; ALTER USER profile_user2 PASSWORD 'a_bad_password'; @@ -212,7 +212,7 @@ ALTER PROFILE myprofile3 LIMIT PASSWORD_REUSE_MAX 1; ALTER USER profile_user2 PASSWORD 'a_bad_password'; -- OK ALTER USER profile_user2 PASSWORD 'test2'; -- OK -ALTER USER profile_user4 PASSWORD 'test3'; -- failed +ALTER USER profile_user4 PASSWORD 'test3'; -- OK DELETE FROM pg_password_history; -- failed for catalog can't be deleted @@ -284,3 +284,6 @@ DROP PROFILE myprofile3; -- OK SELECT prfname, prffailedloginattempts, prfpasswordlocktime, prfpasswordreusemax FROM pg_profile; + +-- Reset pg_default +ALTER PROFILE pg_default LIMIT FAILED_LOGIN_ATTEMPTS -2 PASSWORD_LOCK_TIME -2 PASSWORD_REUSE_MAX 0;