Skip to content

Commit ea46000

Browse files
committed
Arrange for client authentication to occur before we select a specific
database to connect to. This is necessary for the walsender code to work properly (it was previously using an untenable assumption that template1 would always be available to connect to). This also gets rid of a small security shortcoming that was introduced in the original patch to eliminate the flat authentication files: before, you could find out whether or not the requested database existed even if you couldn't pass the authentication checks. The changes needed to support this are mainly just to treat pg_authid and pg_auth_members as nailed relations, so that we can read them without having to be able to locate real pg_class entries for them. This mechanism was already debugged for pg_database, but we hadn't recognized the value of applying it to those catalogs too. Since the current code doesn't have support for accessing toast tables before we've brought up all of the relcache, remove pg_authid's toast table to ensure that no one can store an out-of-line toasted value of rolpassword. The case seems quite unlikely to occur in practice, and was effectively unsupported anyway in the old "flatfiles" implementation. Update genbki.pl to actually implement the same rules as bootstrap.c does for not-nullability of catalog columns. The previous coding was a bit cheesy but worked all right for the previous set of bootstrap catalogs. It does not work for pg_authid, where rolvaliduntil needs to be nullable. Initdb forced due to minor catalog changes (mainly the toast table removal).
1 parent 7de2dfc commit ea46000

File tree

10 files changed

+178
-123
lines changed

10 files changed

+178
-123
lines changed

src/backend/catalog/catalog.c

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
*
1111
*
1212
* IDENTIFICATION
13-
* $PostgreSQL: pgsql/src/backend/catalog/catalog.c,v 1.89 2010/02/26 02:00:36 momjian Exp $
13+
* $PostgreSQL: pgsql/src/backend/catalog/catalog.c,v 1.90 2010/04/20 23:48:47 tgl Exp $
1414
*
1515
*-------------------------------------------------------------------------
1616
*/
@@ -327,9 +327,7 @@ IsSharedRelation(Oid relationId)
327327
relationId == DbRoleSettingDatidRolidIndexId)
328328
return true;
329329
/* These are their toast tables and toast indexes (see toasting.h) */
330-
if (relationId == PgAuthidToastTable ||
331-
relationId == PgAuthidToastIndex ||
332-
relationId == PgDatabaseToastTable ||
330+
if (relationId == PgDatabaseToastTable ||
333331
relationId == PgDatabaseToastIndex ||
334332
relationId == PgShdescriptionToastTable ||
335333
relationId == PgShdescriptionToastIndex ||

src/backend/catalog/genbki.pl

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
# Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
1111
# Portions Copyright (c) 1994, Regents of the University of California
1212
#
13-
# $PostgreSQL: pgsql/src/backend/catalog/genbki.pl,v 1.7 2010/01/22 16:40:18 rhaas Exp $
13+
# $PostgreSQL: pgsql/src/backend/catalog/genbki.pl,v 1.8 2010/04/20 23:48:47 tgl Exp $
1414
#
1515
#----------------------------------------------------------------------
1616

@@ -183,13 +183,15 @@
183183

184184
# Generate entries for user attributes.
185185
my $attnum = 0;
186+
my $priornotnull = 1;
186187
my @user_attrs = @{ $table->{columns} };
187188
foreach my $attr (@user_attrs)
188189
{
189190
$attnum++;
190-
my $row = emit_pgattr_row($table_name, $attr);
191+
my $row = emit_pgattr_row($table_name, $attr, $priornotnull);
191192
$row->{attnum} = $attnum;
192193
$row->{attstattarget} = '-1';
194+
$priornotnull &= ($row->{attnotnull} eq 't');
193195

194196
# If it's bootstrapped, put an entry in postgres.bki.
195197
if ($is_bootstrap eq ' bootstrap')
@@ -221,7 +223,7 @@
221223
foreach my $attr (@SYS_ATTRS)
222224
{
223225
$attnum--;
224-
my $row = emit_pgattr_row($table_name, $attr);
226+
my $row = emit_pgattr_row($table_name, $attr, 1);
225227
$row->{attnum} = $attnum;
226228
$row->{attstattarget} = '0';
227229

@@ -308,10 +310,11 @@
308310

309311
# Given a system catalog name and a reference to a key-value pair corresponding
310312
# to the name and type of a column, generate a reference to a hash that
311-
# represents a pg_attribute entry
313+
# represents a pg_attribute entry. We must also be told whether preceding
314+
# columns were all not-null.
312315
sub emit_pgattr_row
313316
{
314-
my ($table_name, $attr) = @_;
317+
my ($table_name, $attr, $priornotnull) = @_;
315318
my ($attname, $atttype) = %$attr;
316319
my %row;
317320

@@ -337,15 +340,21 @@ sub emit_pgattr_row
337340
$row{attalign} = $type->{typalign};
338341
# set attndims if it's an array type
339342
$row{attndims} = $type->{typcategory} eq 'A' ? '1' : '0';
340-
# This approach to attnotnull is not really good enough;
341-
# we need to know about prior column types too. Look at
342-
# DefineAttr in bootstrap.c. For the moment it's okay for
343-
# the column orders appearing in bootstrapped catalogs.
344-
$row{attnotnull} =
345-
$type->{typname} eq 'oidvector' ? 't'
346-
: $type->{typname} eq 'int2vector' ? 't'
347-
: $type->{typlen} eq 'NAMEDATALEN' ? 't'
348-
: $type->{typlen} > 0 ? 't' : 'f';
343+
# attnotnull must be set true if the type is fixed-width and
344+
# prior columns are too --- compare DefineAttr in bootstrap.c.
345+
# oidvector and int2vector are also treated as not-nullable.
346+
if ($priornotnull)
347+
{
348+
$row{attnotnull} =
349+
$type->{typname} eq 'oidvector' ? 't'
350+
: $type->{typname} eq 'int2vector' ? 't'
351+
: $type->{typlen} eq 'NAMEDATALEN' ? 't'
352+
: $type->{typlen} > 0 ? 't' : 'f';
353+
}
354+
else
355+
{
356+
$row{attnotnull} = 'f';
357+
}
349358
last;
350359
}
351360
}

src/backend/utils/cache/catcache.c

Lines changed: 40 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/utils/cache/catcache.c,v 1.151 2010/02/14 18:42:17 rhaas Exp $
11+
* $PostgreSQL: pgsql/src/backend/utils/cache/catcache.c,v 1.152 2010/04/20 23:48:47 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -981,34 +981,52 @@ InitCatCachePhase2(CatCache *cache, bool touch_index)
981981
* certain system indexes that support critical syscaches.
982982
* We can't use an indexscan to fetch these, else we'll get into
983983
* infinite recursion. A plain heap scan will work, however.
984-
*
985984
* Once we have completed relcache initialization (signaled by
986985
* criticalRelcachesBuilt), we don't have to worry anymore.
986+
*
987+
* Similarly, during backend startup we have to be able to use the
988+
* pg_authid and pg_auth_members syscaches for authentication even if
989+
* we don't yet have relcache entries for those catalogs' indexes.
987990
*/
988991
static bool
989992
IndexScanOK(CatCache *cache, ScanKey cur_skey)
990993
{
991-
if (cache->id == INDEXRELID)
994+
switch (cache->id)
992995
{
993-
/*
994-
* Rather than tracking exactly which indexes have to be loaded before
995-
* we can use indexscans (which changes from time to time), just force
996-
* all pg_index searches to be heap scans until we've built the
997-
* critical relcaches.
998-
*/
999-
if (!criticalRelcachesBuilt)
996+
case INDEXRELID:
997+
/*
998+
* Rather than tracking exactly which indexes have to be loaded
999+
* before we can use indexscans (which changes from time to time),
1000+
* just force all pg_index searches to be heap scans until we've
1001+
* built the critical relcaches.
1002+
*/
1003+
if (!criticalRelcachesBuilt)
1004+
return false;
1005+
break;
1006+
1007+
case AMOID:
1008+
case AMNAME:
1009+
/*
1010+
* Always do heap scans in pg_am, because it's so small there's
1011+
* not much point in an indexscan anyway. We *must* do this when
1012+
* initially building critical relcache entries, but we might as
1013+
* well just always do it.
1014+
*/
10001015
return false;
1001-
}
1002-
else if (cache->id == AMOID ||
1003-
cache->id == AMNAME)
1004-
{
1005-
/*
1006-
* Always do heap scans in pg_am, because it's so small there's not
1007-
* much point in an indexscan anyway. We *must* do this when
1008-
* initially building critical relcache entries, but we might as well
1009-
* just always do it.
1010-
*/
1011-
return false;
1016+
1017+
case AUTHNAME:
1018+
case AUTHOID:
1019+
case AUTHMEMMEMROLE:
1020+
/*
1021+
* Protect authentication lookups occurring before relcache has
1022+
* collected entries for shared indexes.
1023+
*/
1024+
if (!criticalSharedRelcachesBuilt)
1025+
return false;
1026+
break;
1027+
1028+
default:
1029+
break;
10121030
}
10131031

10141032
/* Normal case, allow index scan */
@@ -1397,7 +1415,7 @@ SearchCatCacheList(CatCache *cache,
13971415

13981416
scandesc = systable_beginscan(relation,
13991417
cache->cc_indexoid,
1400-
true,
1418+
IndexScanOK(cache, cur_skey),
14011419
SnapshotNow,
14021420
nkeys,
14031421
cur_skey);

src/backend/utils/cache/relcache.c

Lines changed: 39 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.309 2010/04/14 21:31:11 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.310 2010/04/20 23:48:47 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -43,6 +43,7 @@
4343
#include "catalog/pg_amproc.h"
4444
#include "catalog/pg_attrdef.h"
4545
#include "catalog/pg_authid.h"
46+
#include "catalog/pg_auth_members.h"
4647
#include "catalog/pg_constraint.h"
4748
#include "catalog/pg_database.h"
4849
#include "catalog/pg_namespace.h"
@@ -87,13 +88,15 @@
8788
#define RELCACHE_INIT_FILEMAGIC 0x573265 /* version ID value */
8889

8990
/*
90-
* hardcoded tuple descriptors. see include/catalog/pg_attribute.h
91+
* hardcoded tuple descriptors, generated by genbki.pl
9192
*/
9293
static const FormData_pg_attribute Desc_pg_class[Natts_pg_class] = {Schema_pg_class};
9394
static const FormData_pg_attribute Desc_pg_attribute[Natts_pg_attribute] = {Schema_pg_attribute};
9495
static const FormData_pg_attribute Desc_pg_proc[Natts_pg_proc] = {Schema_pg_proc};
9596
static const FormData_pg_attribute Desc_pg_type[Natts_pg_type] = {Schema_pg_type};
9697
static const FormData_pg_attribute Desc_pg_database[Natts_pg_database] = {Schema_pg_database};
98+
static const FormData_pg_attribute Desc_pg_authid[Natts_pg_authid] = {Schema_pg_authid};
99+
static const FormData_pg_attribute Desc_pg_auth_members[Natts_pg_auth_members] = {Schema_pg_auth_members};
97100
static const FormData_pg_attribute Desc_pg_index[Natts_pg_index] = {Schema_pg_index};
98101

99102
/*
@@ -118,7 +121,7 @@ bool criticalRelcachesBuilt = false;
118121

119122
/*
120123
* This flag is false until we have prepared the critical relcache entries
121-
* for shared catalogs (specifically, pg_database and its indexes).
124+
* for shared catalogs (which are the tables needed for login).
122125
*/
123126
bool criticalSharedRelcachesBuilt = false;
124127

@@ -1379,8 +1382,9 @@ LookupOpclassInfo(Oid operatorClassOid,
13791382
* quite a lot since we only need to work for a few basic system
13801383
* catalogs.
13811384
*
1382-
* formrdesc is currently used for: pg_database, pg_class, pg_attribute,
1383-
* pg_proc, and pg_type (see RelationCacheInitializePhase2/3).
1385+
* formrdesc is currently used for: pg_database, pg_authid, pg_auth_members,
1386+
* pg_class, pg_attribute, pg_proc, and pg_type
1387+
* (see RelationCacheInitializePhase2/3).
13841388
*
13851389
* Note that these catalogs can't have constraints (except attnotnull),
13861390
* default values, rules, or triggers, since we don't cope with any of that.
@@ -1461,8 +1465,8 @@ formrdesc(const char *relationName, Oid relationReltype,
14611465
* initialize attribute tuple form
14621466
*
14631467
* Unlike the case with the relation tuple, this data had better be right
1464-
* because it will never be replaced. The input values must be correctly
1465-
* defined by macros in src/include/catalog/ headers.
1468+
* because it will never be replaced. The data comes from
1469+
* src/include/catalog/ headers via genbki.pl.
14661470
*/
14671471
relation->rd_att = CreateTemplateTupleDesc(natts, hasoids);
14681472
relation->rd_att->tdrefcount = 1; /* mark as refcounted */
@@ -2455,6 +2459,8 @@ RelationBuildLocalRelation(const char *relname,
24552459
switch (relid)
24562460
{
24572461
case DatabaseRelationId:
2462+
case AuthIdRelationId:
2463+
case AuthMemRelationId:
24582464
case RelationRelationId:
24592465
case AttributeRelationId:
24602466
case ProcedureRelationId:
@@ -2750,12 +2756,13 @@ RelationCacheInitialize(void)
27502756
/*
27512757
* RelationCacheInitializePhase2
27522758
*
2753-
* This is called to prepare for access to pg_database during startup.
2754-
* We must at least set up a nailed reldesc for pg_database. Ideally
2755-
* we'd like to have reldescs for its indexes, too. We attempt to
2756-
* load this information from the shared relcache init file. If that's
2757-
* missing or broken, just make a phony entry for pg_database.
2758-
* RelationCacheInitializePhase3 will clean up as needed.
2759+
* This is called to prepare for access to shared catalogs during startup.
2760+
* We must at least set up nailed reldescs for pg_database, pg_authid,
2761+
* and pg_auth_members. Ideally we'd like to have reldescs for their
2762+
* indexes, too. We attempt to load this information from the shared
2763+
* relcache init file. If that's missing or broken, just make phony
2764+
* entries for the catalogs themselves. RelationCacheInitializePhase3
2765+
* will clean up as needed.
27592766
*/
27602767
void
27612768
RelationCacheInitializePhase2(void)
@@ -2768,7 +2775,8 @@ RelationCacheInitializePhase2(void)
27682775
RelationMapInitializePhase2();
27692776

27702777
/*
2771-
* In bootstrap mode, pg_database isn't there yet anyway, so do nothing.
2778+
* In bootstrap mode, the shared catalogs aren't there yet anyway,
2779+
* so do nothing.
27722780
*/
27732781
if (IsBootstrapProcessingMode())
27742782
return;
@@ -2780,14 +2788,18 @@ RelationCacheInitializePhase2(void)
27802788

27812789
/*
27822790
* Try to load the shared relcache cache file. If unsuccessful, bootstrap
2783-
* the cache with a pre-made descriptor for pg_database.
2791+
* the cache with pre-made descriptors for the critical shared catalogs.
27842792
*/
27852793
if (!load_relcache_init_file(true))
27862794
{
27872795
formrdesc("pg_database", DatabaseRelation_Rowtype_Id, true,
27882796
true, Natts_pg_database, Desc_pg_database);
2797+
formrdesc("pg_authid", AuthIdRelation_Rowtype_Id, true,
2798+
true, Natts_pg_authid, Desc_pg_authid);
2799+
formrdesc("pg_auth_members", AuthMemRelation_Rowtype_Id, true,
2800+
false, Natts_pg_auth_members, Desc_pg_auth_members);
27892801

2790-
#define NUM_CRITICAL_SHARED_RELS 1 /* fix if you change list above */
2802+
#define NUM_CRITICAL_SHARED_RELS 3 /* fix if you change list above */
27912803
}
27922804

27932805
MemoryContextSwitchTo(oldcxt);
@@ -2910,16 +2922,24 @@ RelationCacheInitializePhase3(void)
29102922
* DatabaseNameIndexId isn't critical for relcache loading, but rather for
29112923
* initial lookup of MyDatabaseId, without which we'll never find any
29122924
* non-shared catalogs at all. Autovacuum calls InitPostgres with a
2913-
* database OID, so it instead depends on DatabaseOidIndexId.
2925+
* database OID, so it instead depends on DatabaseOidIndexId. We also
2926+
* need to nail up some indexes on pg_authid and pg_auth_members for use
2927+
* during client authentication.
29142928
*/
29152929
if (!criticalSharedRelcachesBuilt)
29162930
{
29172931
load_critical_index(DatabaseNameIndexId,
29182932
DatabaseRelationId);
29192933
load_critical_index(DatabaseOidIndexId,
29202934
DatabaseRelationId);
2921-
2922-
#define NUM_CRITICAL_SHARED_INDEXES 2 /* fix if you change list above */
2935+
load_critical_index(AuthIdRolnameIndexId,
2936+
AuthIdRelationId);
2937+
load_critical_index(AuthIdOidIndexId,
2938+
AuthIdRelationId);
2939+
load_critical_index(AuthMemMemRoleIndexId,
2940+
AuthMemRelationId);
2941+
2942+
#define NUM_CRITICAL_SHARED_INDEXES 5 /* fix if you change list above */
29232943

29242944
criticalSharedRelcachesBuilt = true;
29252945
}

src/backend/utils/init/miscinit.c

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/utils/init/miscinit.c,v 1.183 2010/02/26 02:01:13 momjian Exp $
11+
* $PostgreSQL: pgsql/src/backend/utils/init/miscinit.c,v 1.184 2010/04/20 23:48:47 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -429,10 +429,8 @@ InitializeSessionUserId(const char *rolename)
429429
* These next checks are not enforced when in standalone mode, so that
430430
* there is a way to recover from sillinesses like "UPDATE pg_authid SET
431431
* rolcanlogin = false;".
432-
*
433-
* We do not enforce them for the autovacuum process either.
434432
*/
435-
if (IsUnderPostmaster && !IsAutoVacuumWorkerProcess())
433+
if (IsUnderPostmaster)
436434
{
437435
/*
438436
* Is role allowed to login at all?
@@ -479,7 +477,10 @@ InitializeSessionUserId(const char *rolename)
479477
void
480478
InitializeSessionUserIdStandalone(void)
481479
{
482-
/* This function should only be called in a single-user backend. */
480+
/*
481+
* This function should only be called in single-user mode and in
482+
* autovacuum workers.
483+
*/
483484
AssertState(!IsUnderPostmaster || IsAutoVacuumWorkerProcess());
484485

485486
/* call only once */

0 commit comments

Comments
 (0)