Skip to content

Commit 705ee0b

Browse files
zhangwenchao-123my-ship-it
authored andcommitted
Fix record password_history when the role is not allowed to use profile.
We will always record the password history when alter user/role password whatever the user is allowed to use profile which is not reasonable. In this commit, the password history will be recorded only when the user is allowed to use profile. We fix another minor problem about profile in this commit meanwhile. Authored-by: Zhang Wenchao zwcpostgres@gmail.com
1 parent 344efe8 commit 705ee0b

5 files changed

Lines changed: 71 additions & 65 deletions

File tree

src/backend/commands/user.c

Lines changed: 53 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1452,62 +1452,76 @@ AlterRole(AlterRoleStmt *stmt)
14521452
{
14531453
char *shadow_pass;
14541454
char *logdetail;
1455+
1456+
/* Like in CREATE USER, don't allow an empty password. */
1457+
if (password[0] == '\0' ||
1458+
plain_crypt_verify(rolename, password, "", &logdetail) == STATUS_OK)
1459+
{
1460+
ereport(NOTICE,
1461+
(errmsg("empty string is not a valid password, clearing password")));
1462+
new_record_nulls[Anum_pg_authid_rolpassword - 1] = true;
1463+
}
1464+
else
1465+
{
1466+
/* Encrypt the password to the requested format. */
1467+
shadow_pass = encrypt_password(Password_encryption, rolename,
1468+
password);
1469+
new_record[Anum_pg_authid_rolpassword - 1] =
1470+
CStringGetTextDatum(shadow_pass);
1471+
}
1472+
new_record_repl[Anum_pg_authid_rolpassword - 1] = true;
1473+
}
1474+
1475+
/* unset password */
1476+
if (dpassword && dpassword->arg == NULL)
1477+
{
1478+
new_record_repl[Anum_pg_authid_rolpassword - 1] = true;
1479+
new_record_nulls[Anum_pg_authid_rolpassword - 1] = true;
1480+
}
1481+
1482+
1483+
if ((password || (dpassword && dpassword->arg == NULL)) &&
1484+
(authform->rolenableprofile || enable_profile) && enable_password_profile)
1485+
{
14551486
Datum datum;
14561487
bool isnull;
14571488
bool setat_isnull;
14581489
TimestampTz password_set_at = 0;
14591490
int32 profile_reuse_max = 0;
14601491
SysScanDesc password_history_scan;
14611492
HeapTuple profiletuple;
1493+
char *logdetail;
1494+
bool ignore_password_history = false;
14621495

14631496
pg_profile_rel = table_open(ProfileRelationId, AccessShareLock);
14641497
pg_profile_dsc = RelationGetDescr(pg_profile_rel);
14651498

14661499
datum = SysCacheGetAttr(AUTHNAME, tuple,
1467-
Anum_pg_authid_rolprofile, &isnull);
1500+
Anum_pg_authid_rolprofile, &isnull);
14681501
Assert(!isnull);
14691502

14701503
profileid = DatumGetObjectId(datum);
14711504
profiletuple = SearchSysCache1(PROFILEID, ObjectIdGetDatum(profileid));
14721505
if (!HeapTupleIsValid(profiletuple))
14731506
ereport(ERROR,
1474-
(errcode(ERRCODE_UNDEFINED_OBJECT),
1475-
errmsg("profile \"%d\" does not exist", profileid)));
1507+
(errcode(ERRCODE_UNDEFINED_OBJECT),
1508+
errmsg("profile \"%d\" does not exist", profileid)));
14761509

14771510
/* Get PASSWORD_REUSE_MAX from profile tuple and transform it to normal value */
14781511
profileform = (Form_pg_profile) GETSTRUCT(profiletuple);
14791512
profile_reuse_max = tranformProfileValueToNormal(profileform->prfpasswordreusemax,
1480-
Anum_pg_profile_prfpasswordreusemax);
1513+
Anum_pg_profile_prfpasswordreusemax);
14811514

14821515
ReleaseSysCache(profiletuple);
14831516

14841517
if (profile_reuse_max == 0)
1485-
ereport(ERROR,
1486-
(errcode(ERRCODE_INVALID_PASSWORD),
1487-
errmsg("can't alter user password for profile's password_reuse_max is zero.")));
1518+
ignore_password_history = true;
14881519

14891520
/*
14901521
* Get shadow password from pg_authid
14911522
*/
14921523
datum = SysCacheGetAttr(AUTHNAME, tuple,
1493-
Anum_pg_authid_rolpassword, &isnull);
1494-
1495-
/* Like in CREATE USER, don't allow an empty password. */
1496-
if (password[0] == '\0' ||
1497-
plain_crypt_verify(rolename, password, "", &logdetail) == STATUS_OK)
1498-
{
1499-
ereport(NOTICE,
1500-
(errmsg("empty string is not a valid password, clearing password")));
1501-
new_record_nulls[Anum_pg_authid_rolpassword - 1] = true;
1502-
}
1503-
else
1504-
{
1505-
/* Encrypt the password to the requested format. */
1506-
shadow_pass = encrypt_password(Password_encryption, rolename,
1507-
password);
1508-
new_record[Anum_pg_authid_rolpassword - 1] =
1509-
CStringGetTextDatum(shadow_pass);
1510-
}
1524+
Anum_pg_authid_rolpassword, &isnull);
15111525

15121526
/*
15131527
* Disallow to use recently passwords which controlled by
@@ -1536,7 +1550,7 @@ AlterRole(AlterRoleStmt *stmt)
15361550
history_shadow_pass = TextDatumGetCString(datum);
15371551

15381552
datum = SysCacheGetAttr(AUTHNAME, tuple,
1539-
Anum_pg_authid_rolpasswordsetat, &setat_isnull);
1553+
Anum_pg_authid_rolpasswordsetat, &setat_isnull);
15401554
Assert(!setat_isnull);
15411555
history_password_set_at = DatumGetInt64(datum);
15421556

@@ -1553,26 +1567,26 @@ AlterRole(AlterRoleStmt *stmt)
15531567

15541568
/* Form the insert tuple */
15551569
password_history_tuple = heap_form_tuple(pg_password_history_dsc,
1556-
password_history_record, password_nulls);
1570+
password_history_record, password_nulls);
15571571

1558-
/* Insert new record into the pg_password_history table */
1572+
/* Insert new record into the pg_password_history table */
15591573
CatalogTupleInsert(pg_password_history_rel, password_history_tuple);
15601574

15611575
/* Advance command counter so we can see new record */
15621576
CommandCounterIncrement();
15631577

15641578
/* form a scan key */
15651579
ScanKeyInit(&skey,
1566-
Anum_pg_password_history_passhistroleid,
1567-
BTEqualStrategyNumber, F_OIDEQ,
1568-
ObjectIdGetDatum(roleid));
1580+
Anum_pg_password_history_passhistroleid,
1581+
BTEqualStrategyNumber, F_OIDEQ,
1582+
ObjectIdGetDatum(roleid));
15691583

15701584
/*
15711585
* Get only recently PASSWORD_REUSE_MAX tuples.
15721586
*/
15731587
password_history_scan = systable_beginscan_ordered(pg_password_history_rel,
1574-
pg_password_history_passwordsetat_idx,
1575-
NULL, 1, &skey);
1588+
pg_password_history_passwordsetat_idx,
1589+
NULL, 1, &skey);
15761590
for (i = 0; i < profile_reuse_max; i++)
15771591
{
15781592
password_history_tuple = systable_getnext_ordered(password_history_scan, BackwardScanDirection);
@@ -1581,19 +1595,20 @@ AlterRole(AlterRoleStmt *stmt)
15811595
break;
15821596

15831597
datum = heap_getattr(password_history_tuple, Anum_pg_password_history_passhistpassword,
1584-
pg_password_history_dsc, &isnull);
1598+
pg_password_history_dsc, &isnull);
15851599
Assert(!isnull);
15861600
history_shadow_pass = text_to_cstring(DatumGetTextP(datum));
15871601

15881602
/*
15891603
* Use password verify function to check whether password
15901604
* has been recorded in pg_password_history.
15911605
*/
1592-
if (plain_crypt_verify(rolename, history_shadow_pass, password, &logdetail) == STATUS_OK)
1606+
if (!ignore_password_history && password &&
1607+
plain_crypt_verify(rolename, history_shadow_pass, password, &logdetail) == STATUS_OK)
15931608
ereport(ERROR,
1594-
(errcode(ERRCODE_INVALID_PASSWORD),
1595-
errmsg("The new password should not be the same with latest %d history password",
1596-
profile_reuse_max)));
1609+
(errcode(ERRCODE_INVALID_PASSWORD),
1610+
errmsg("The new password should not be the same with latest %d history password",
1611+
profile_reuse_max)));
15971612
}
15981613

15991614
systable_endscan_ordered(password_history_scan);
@@ -1606,18 +1621,10 @@ AlterRole(AlterRoleStmt *stmt)
16061621
new_record[Anum_pg_authid_rolpasswordsetat - 1] =
16071622
Int64GetDatum(password_set_at);
16081623
new_record_repl[Anum_pg_authid_rolpasswordsetat - 1] = true;
1609-
new_record_repl[Anum_pg_authid_rolpassword - 1] = true;
16101624

16111625
table_close(pg_profile_rel, NoLock);
16121626
}
16131627

1614-
/* unset password */
1615-
if (dpassword && dpassword->arg == NULL)
1616-
{
1617-
new_record_repl[Anum_pg_authid_rolpassword - 1] = true;
1618-
new_record_nulls[Anum_pg_authid_rolpassword - 1] = true;
1619-
}
1620-
16211628
/* valid until */
16221629
new_record[Anum_pg_authid_rolvaliduntil - 1] = validUntil_datum;
16231630
new_record_nulls[Anum_pg_authid_rolvaliduntil - 1] = validUntil_null;

src/backend/postmaster/loginmonitor.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,8 @@ LoginMonitorLauncherMain(int argc, char *argv[]) {
313313
RESUME_INTERRUPTS();
314314

315315
/* reset and notify process by latch */
316-
SetLatch(LoginMonitorShmem->latch);
316+
if (LoginMonitorShmem->latch)
317+
SetLatch(LoginMonitorShmem->latch);
317318
ResetLatch(LoginMonitorShmem->lm_latch);
318319
LoginMonitorShmemReset();
319320

src/include/catalog/pg_profile.dat

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
descr => 'default profile',
2323
prfname => 'pg_default', prffailedloginattempts => '-2', prfpasswordlocktime => '-2',
2424
prfpasswordlifetime => '-2', prfpasswordgracetime => '-2', prfpasswordreusetime => '-2',
25-
prfpasswordreusemax => '-2', prfpasswordallowhashed => '1', prfpasswordverifyfuncdb => '_null_',
25+
prfpasswordreusemax => '0', prfpasswordallowhashed => '1', prfpasswordverifyfuncdb => '_null_',
2626
prfpasswordverifyfunc => '_null_' }
2727

2828
]

src/test/regress/expected/profile.out

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ SELECT prfname, prffailedloginattempts, prfpasswordlocktime, prfpasswordreusemax
148148
FROM pg_profile;
149149
prfname | prffailedloginattempts | prfpasswordlocktime | prfpasswordreusemax
150150
------------+------------------------+---------------------+---------------------
151-
pg_default | -2 | -2 | -2
151+
pg_default | -2 | -2 | 0
152152
(1 row)
153153

154154
-- Test CREATE PROFILE
@@ -162,7 +162,7 @@ SELECT prfname, prffailedloginattempts, prfpasswordlocktime, prfpasswordreusemax
162162
FROM pg_profile;
163163
prfname | prffailedloginattempts | prfpasswordlocktime | prfpasswordreusemax
164164
------------+------------------------+---------------------+---------------------
165-
pg_default | -2 | -2 | -2
165+
pg_default | -2 | -2 | 0
166166
myprofile1 | -1 | -1 | -1
167167
myprofile2 | -1 | -2 | -1
168168
myprofile3 | 4 | 1 | -1
@@ -215,7 +215,7 @@ SELECT prfname, prffailedloginattempts, prfpasswordlocktime, prfpasswordreusemax
215215
FROM pg_profile;
216216
prfname | prffailedloginattempts | prfpasswordlocktime | prfpasswordreusemax
217217
------------+------------------------+---------------------+---------------------
218-
pg_default | -2 | -2 | -2
218+
pg_default | -2 | -2 | 0
219219
myprofile1 | -1 | -1 | -1
220220
myprofile2 | -1 | -2 | -1
221221
myprofile3 | 4 | 1 | -1
@@ -512,31 +512,24 @@ ALTER USER profile_user1 PASSWORD 'test';
512512
ALTER USER profile_user1 PASSWORD 'a_nice_long_password_123';
513513
ALTER USER profile_user1 PASSWORD 'a_new_password';
514514
ALTER USER profile_user1 PASSWORD 'test';
515-
ERROR: The new password should not be the same with latest 3 history password
516515
ALTER USER profile_user1 PASSWORD 'a_nice_long_password_123';
517-
ERROR: The new password should not be the same with latest 3 history password
518516
ALTER USER profile_user1 PASSWORD 'a_new_password';
519-
ERROR: The new password should not be the same with latest 3 history password
520517
ALTER USER profile_user1 PASSWORD 'ABCD';
521518
ALTER USER profile_user1 PASSWORD 'test';
522519
ALTER PROFILE pg_default LIMIT PASSWORD_REUSE_MAX 4;
523520
ALTER USER profile_user1 PASSWORD 'a_nice_long_password_123';
524-
ERROR: The new password should not be the same with latest 4 history password
525521
ALTER USER profile_user2 PASSWORD 'test2';
526522
ALTER USER profile_user2 PASSWORD 'a_bad_password';
527-
ALTER USER profile_user2 PASSWORD 'test2';
528-
ERROR: The new password should not be the same with latest 2 history password
523+
ALTER USER profile_user2 PASSWORD 'test2' ENABLE PROFILE;
529524
ALTER USER profile_user2 PASSWORD 'a_bad_password';
530525
ERROR: The new password should not be the same with latest 2 history password
531526
ALTER USER profile_user2 PASSWORD 'a_nice_password';
532527
ALTER USER profile_user2 PASSWORD 'a_bad_password';
533-
ERROR: The new password should not be the same with latest 2 history password
534528
ALTER USER profile_user2 PASSWORD 'test2';
535529
ALTER PROFILE myprofile3 LIMIT PASSWORD_REUSE_MAX 1;
536530
ALTER USER profile_user2 PASSWORD 'a_bad_password'; -- OK
537531
ALTER USER profile_user2 PASSWORD 'test2'; -- OK
538-
ALTER USER profile_user4 PASSWORD 'test3'; -- failed
539-
ERROR: can't alter user password for profile's password_reuse_max is zero.
532+
ALTER USER profile_user4 PASSWORD 'test3'; -- OK
540533
DELETE FROM pg_password_history; -- failed for catalog can't be deleted
541534
ERROR: permission denied: "pg_password_history" is a system catalog
542535
-- Test ALTER USER ... ACCOUNT LOCK/UNLOCK
@@ -554,7 +547,7 @@ AND rolname like '%profile_user%';
554547
profile_user5 | myprofile3 | 0 | 0 | f
555548
profile_user7 | myprofile4 | 0 | 0 | t
556549
profile_user1 | myprofile1 | 2 | 0 | f
557-
profile_user2 | myprofile3 | 0 | 0 | f
550+
profile_user2 | myprofile3 | 0 | 0 | t
558551
profile_user3 | myprofile2 | 2 | 0 | f
559552
profile_user4 | myprofile4 | 0 | 0 | f
560553
(7 rows)
@@ -600,7 +593,7 @@ AND rolname like '%profile_user%';
600593
profile_user5 | myprofile3 | 0 | 0 | f
601594
profile_user7 | myprofile4 | 0 | 0 | t
602595
profile_user1 | myprofile1 | 2 | 0 | f
603-
profile_user2 | myprofile3 | 0 | 0 | f
596+
profile_user2 | myprofile3 | 0 | 0 | t
604597
profile_user3 | myprofile2 | 2 | 0 | f
605598
profile_user4 | myprofile4 | 0 | 0 | f
606599
(7 rows)
@@ -637,7 +630,7 @@ AND rolname like '%profile_user%';
637630
profile_user5 | myprofile3 | 0 | 0 | f
638631
profile_user7 | myprofile4 | 0 | 0 | t
639632
profile_user1 | myprofile1 | 2 | 0 | f
640-
profile_user2 | myprofile3 | 0 | 0 | f
633+
profile_user2 | myprofile3 | 0 | 0 | t
641634
profile_user3 | myprofile2 | 2 | 0 | f
642635
profile_user4 | myprofile4 | 0 | 0 | f
643636
(7 rows)
@@ -687,3 +680,5 @@ FROM pg_profile;
687680
pg_default | 2 | 1 | 4
688681
(1 row)
689682

683+
-- Reset pg_default
684+
ALTER PROFILE pg_default LIMIT FAILED_LOGIN_ATTEMPTS -2 PASSWORD_LOCK_TIME -2 PASSWORD_REUSE_MAX 0;

src/test/regress/sql/profile.sql

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ ALTER USER profile_user1 PASSWORD 'a_nice_long_password_123';
203203

204204
ALTER USER profile_user2 PASSWORD 'test2';
205205
ALTER USER profile_user2 PASSWORD 'a_bad_password';
206-
ALTER USER profile_user2 PASSWORD 'test2';
206+
ALTER USER profile_user2 PASSWORD 'test2' ENABLE PROFILE;
207207
ALTER USER profile_user2 PASSWORD 'a_bad_password';
208208
ALTER USER profile_user2 PASSWORD 'a_nice_password';
209209
ALTER USER profile_user2 PASSWORD 'a_bad_password';
@@ -212,7 +212,7 @@ ALTER PROFILE myprofile3 LIMIT PASSWORD_REUSE_MAX 1;
212212
ALTER USER profile_user2 PASSWORD 'a_bad_password'; -- OK
213213
ALTER USER profile_user2 PASSWORD 'test2'; -- OK
214214

215-
ALTER USER profile_user4 PASSWORD 'test3'; -- failed
215+
ALTER USER profile_user4 PASSWORD 'test3'; -- OK
216216

217217
DELETE FROM pg_password_history; -- failed for catalog can't be deleted
218218

@@ -284,3 +284,6 @@ DROP PROFILE myprofile3; -- OK
284284

285285
SELECT prfname, prffailedloginattempts, prfpasswordlocktime, prfpasswordreusemax
286286
FROM pg_profile;
287+
288+
-- Reset pg_default
289+
ALTER PROFILE pg_default LIMIT FAILED_LOGIN_ATTEMPTS -2 PASSWORD_LOCK_TIME -2 PASSWORD_REUSE_MAX 0;

0 commit comments

Comments
 (0)