Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 53 additions & 46 deletions src/backend/commands/user.c
Original file line number Diff line number Diff line change
Expand Up @@ -1452,62 +1452,76 @@ 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;
TimestampTz password_set_at = 0;
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
Expand Down Expand Up @@ -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);

Expand All @@ -1553,26 +1567,26 @@ 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 */
CommandCounterIncrement();

/* 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);
Expand All @@ -1581,19 +1595,20 @@ 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));

/*
* 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);
Expand All @@ -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;
Expand Down
3 changes: 2 additions & 1 deletion src/backend/postmaster/loginmonitor.c
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
2 changes: 1 addition & 1 deletion src/include/catalog/pg_profile.dat
Original file line number Diff line number Diff line change
Expand Up @@ -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_' }

]
25 changes: 10 additions & 15 deletions src/test/regress/expected/profile.out
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;
7 changes: 5 additions & 2 deletions src/test/regress/sql/profile.sql
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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

Expand Down Expand Up @@ -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;