All,

On Sat, Dec 27, 2014 at 6:31 PM, Noah Misch <n...@leadboat.com> wrote:

> On Sat, Dec 27, 2014 at 06:26:02PM -0500, Tom Lane wrote:
> > Stephen Frost <sfr...@snowman.net> writes:
> > > * Tom Lane (t...@sss.pgh.pa.us) wrote:
> > >> Plan C (remove CATUPDATE altogether) also has some merit.  But adding
> a
> > >> superuser override there would be entirely pointless.
> >
> > > This is be my recommendation.  I do not see the point of carrying the
> > > option around just to confuse new users of pg_authid and reviewers of
> > > the code.
> >
> > Yeah ... if no one's found it interesting in the 20 years since the
> > code left Berkeley, it's unlikely that interest will emerge in the
> > future.
>
> No objection here.
>

Given this discussion, I have attached a patch that removes CATUPDATE for
review/discussion.

One of the interesting behaviors (or perhaps not) is how 'pg_class_aclmask'
handles an invalid role id when checking permissions against 'rolsuper'
instead of 'rolcatupdate'.  This is demonstrated by the
'has_table_privilege' regression test expected results.  In summary,
'has_rolcatupdate' raises an error when an invalid OID is provided,
however, as documented in the source 'superuser_arg' does not, simply
returning false.  Therefore, when checking for superuser-ness of an
non-existent role, the result will be false and not an error.  Perhaps this
is OK, but my concern would be on the expected behavior around items like
'has_table_privilege'.  My natural thought would be that we would want to
preserve that specific functionality, though short of adding a
'has_rolsuper' function that will raise an appropriate error, I'm uncertain
of an approach.  Thoughts?

Thanks,
Adam

-- 
Adam Brightwell - adam.brightw...@crunchydatasolutions.com
Database Engineer - www.crunchydatasolutions.com
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
new file mode 100644
index 9ceb96b..635032d
*** a/doc/src/sgml/catalogs.sgml
--- b/doc/src/sgml/catalogs.sgml
***************
*** 1416,1430 ****
       </row>
  
       <row>
-       <entry><structfield>rolcatupdate</structfield></entry>
-       <entry><type>bool</type></entry>
-       <entry>
-        Role can update system catalogs directly.  (Even a superuser cannot do
-        this unless this column is true)
-       </entry>
-      </row>
- 
-      <row>
        <entry><structfield>rolcanlogin</structfield></entry>
        <entry><type>bool</type></entry>
        <entry>
--- 1416,1421 ----
*************** SELECT * FROM pg_locks pl LEFT JOIN pg_p
*** 8479,8494 ****
       </row>
  
       <row>
-       <entry><structfield>rolcatupdate</structfield></entry>
-       <entry><type>bool</type></entry>
-       <entry></entry>
-       <entry>
-        Role can update system catalogs directly.  (Even a superuser cannot do
-        this unless this column is true)
-       </entry>
-      </row>
- 
-      <row>
        <entry><structfield>rolcanlogin</structfield></entry>
        <entry><type>bool</type></entry>
        <entry></entry>
--- 8470,8475 ----
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
new file mode 100644
index d30612c..18623ef
*** a/src/backend/catalog/aclchk.c
--- b/src/backend/catalog/aclchk.c
*************** aclcheck_error_type(AclResult aclerr, Oi
*** 3423,3448 ****
  }
  
  
- /* Check if given user has rolcatupdate privilege according to pg_authid */
- static bool
- has_rolcatupdate(Oid roleid)
- {
- 	bool		rolcatupdate;
- 	HeapTuple	tuple;
- 
- 	tuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
- 	if (!HeapTupleIsValid(tuple))
- 		ereport(ERROR,
- 				(errcode(ERRCODE_UNDEFINED_OBJECT),
- 				 errmsg("role with OID %u does not exist", roleid)));
- 
- 	rolcatupdate = ((Form_pg_authid) GETSTRUCT(tuple))->rolcatupdate;
- 
- 	ReleaseSysCache(tuple);
- 
- 	return rolcatupdate;
- }
- 
  /*
   * Relay for the various pg_*_mask routines depending on object kind
   */
--- 3423,3428 ----
*************** pg_class_aclmask(Oid table_oid, Oid role
*** 3620,3627 ****
  
  	/*
  	 * Deny anyone permission to update a system catalog unless
! 	 * pg_authid.rolcatupdate is set.   (This is to let superusers protect
! 	 * themselves from themselves.)  Also allow it if allowSystemTableMods.
  	 *
  	 * As of 7.4 we have some updatable system views; those shouldn't be
  	 * protected in this way.  Assume the view rules can take care of
--- 3600,3606 ----
  
  	/*
  	 * Deny anyone permission to update a system catalog unless
! 	 * pg_authid.rolsuper is set.  Also allow it if allowSystemTableMods.
  	 *
  	 * As of 7.4 we have some updatable system views; those shouldn't be
  	 * protected in this way.  Assume the view rules can take care of
*************** pg_class_aclmask(Oid table_oid, Oid role
*** 3630,3636 ****
  	if ((mask & (ACL_INSERT | ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE | ACL_USAGE)) &&
  		IsSystemClass(table_oid, classForm) &&
  		classForm->relkind != RELKIND_VIEW &&
! 		!has_rolcatupdate(roleid) &&
  		!allowSystemTableMods)
  	{
  #ifdef ACLDEBUG
--- 3609,3615 ----
  	if ((mask & (ACL_INSERT | ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE | ACL_USAGE)) &&
  		IsSystemClass(table_oid, classForm) &&
  		classForm->relkind != RELKIND_VIEW &&
! 		!superuser_arg(roleid) &&
  		!allowSystemTableMods)
  	{
  #ifdef ACLDEBUG
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
new file mode 100644
index 22b8cee..fc3fa9e
*** a/src/backend/catalog/system_views.sql
--- b/src/backend/catalog/system_views.sql
*************** CREATE VIEW pg_roles AS
*** 13,19 ****
          rolinherit,
          rolcreaterole,
          rolcreatedb,
-         rolcatupdate,
          rolcanlogin,
          rolreplication,
          rolconnlimit,
--- 13,18 ----
*************** CREATE VIEW pg_shadow AS
*** 31,37 ****
          pg_authid.oid AS usesysid,
          rolcreatedb AS usecreatedb,
          rolsuper AS usesuper,
-         rolcatupdate AS usecatupd,
          rolreplication AS userepl,
          rolpassword AS passwd,
          rolvaliduntil::abstime AS valuntil,
--- 30,35 ----
*************** CREATE VIEW pg_user AS
*** 56,62 ****
          usesysid,
          usecreatedb,
          usesuper,
-         usecatupd,
          userepl,
          '********'::text as passwd,
          valuntil,
--- 54,59 ----
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
new file mode 100644
index 1a73fd8..d8b3898
*** a/src/backend/commands/user.c
--- b/src/backend/commands/user.c
*************** CreateRole(CreateRoleStmt *stmt)
*** 368,375 ****
  	new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(inherit);
  	new_record[Anum_pg_authid_rolcreaterole - 1] = BoolGetDatum(createrole);
  	new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(createdb);
- 	/* superuser gets catupdate right by default */
- 	new_record[Anum_pg_authid_rolcatupdate - 1] = BoolGetDatum(issuper);
  	new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(canlogin);
  	new_record[Anum_pg_authid_rolreplication - 1] = BoolGetDatum(isreplication);
  	new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit);
--- 368,373 ----
*************** AlterRole(AlterRoleStmt *stmt)
*** 734,753 ****
  	MemSet(new_record_repl, false, sizeof(new_record_repl));
  
  	/*
! 	 * issuper/createrole/catupdate/etc
! 	 *
! 	 * XXX It's rather unclear how to handle catupdate.  It's probably best to
! 	 * keep it equal to the superuser status, otherwise you could end up with
! 	 * a situation where no existing superuser can alter the catalogs,
! 	 * including pg_authid!
  	 */
  	if (issuper >= 0)
  	{
  		new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(issuper > 0);
  		new_record_repl[Anum_pg_authid_rolsuper - 1] = true;
- 
- 		new_record[Anum_pg_authid_rolcatupdate - 1] = BoolGetDatum(issuper > 0);
- 		new_record_repl[Anum_pg_authid_rolcatupdate - 1] = true;
  	}
  
  	if (inherit >= 0)
--- 732,743 ----
  	MemSet(new_record_repl, false, sizeof(new_record_repl));
  
  	/*
! 	 * issuper/createrole/etc
  	 */
  	if (issuper >= 0)
  	{
  		new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(issuper > 0);
  		new_record_repl[Anum_pg_authid_rolsuper - 1] = true;
  	}
  
  	if (inherit >= 0)
diff --git a/src/include/catalog/pg_authid.h b/src/include/catalog/pg_authid.h
new file mode 100644
index 3b63d2b..6defde5
*** a/src/include/catalog/pg_authid.h
--- b/src/include/catalog/pg_authid.h
*************** CATALOG(pg_authid,1260) BKI_SHARED_RELAT
*** 49,55 ****
  	bool		rolinherit;		/* inherit privileges from other roles? */
  	bool		rolcreaterole;	/* allowed to create more roles? */
  	bool		rolcreatedb;	/* allowed to create databases? */
- 	bool		rolcatupdate;	/* allowed to alter catalogs manually? */
  	bool		rolcanlogin;	/* allowed to log in as session user? */
  	bool		rolreplication; /* role used for streaming replication */
  	bool		rolbypassrls;	/* allowed to bypass row level security? */
--- 49,54 ----
*************** typedef FormData_pg_authid *Form_pg_auth
*** 74,92 ****
   *		compiler constants for pg_authid
   * ----------------
   */
! #define Natts_pg_authid					12
  #define Anum_pg_authid_rolname			1
  #define Anum_pg_authid_rolsuper			2
  #define Anum_pg_authid_rolinherit		3
  #define Anum_pg_authid_rolcreaterole	4
  #define Anum_pg_authid_rolcreatedb		5
! #define Anum_pg_authid_rolcatupdate		6
! #define Anum_pg_authid_rolcanlogin		7
! #define Anum_pg_authid_rolreplication	8
! #define Anum_pg_authid_rolbypassrls		9
! #define Anum_pg_authid_rolconnlimit		10
! #define Anum_pg_authid_rolpassword		11
! #define Anum_pg_authid_rolvaliduntil	12
  
  /* ----------------
   *		initial contents of pg_authid
--- 73,90 ----
   *		compiler constants for pg_authid
   * ----------------
   */
! #define Natts_pg_authid					11
  #define Anum_pg_authid_rolname			1
  #define Anum_pg_authid_rolsuper			2
  #define Anum_pg_authid_rolinherit		3
  #define Anum_pg_authid_rolcreaterole	4
  #define Anum_pg_authid_rolcreatedb		5
! #define Anum_pg_authid_rolcanlogin		6
! #define Anum_pg_authid_rolreplication	7
! #define Anum_pg_authid_rolbypassrls		8
! #define Anum_pg_authid_rolconnlimit		9
! #define Anum_pg_authid_rolpassword		10
! #define Anum_pg_authid_rolvaliduntil	11
  
  /* ----------------
   *		initial contents of pg_authid
*************** typedef FormData_pg_authid *Form_pg_auth
*** 95,101 ****
   * user choices.
   * ----------------
   */
! DATA(insert OID = 10 ( "POSTGRES" t t t t t t t t -1 _null_ _null_));
  
  #define BOOTSTRAP_SUPERUSERID 10
  
--- 93,99 ----
   * user choices.
   * ----------------
   */
! DATA(insert OID = 10 ( "POSTGRES" t t t t t t t -1 _null_ _null_));
  
  #define BOOTSTRAP_SUPERUSERID 10
  
diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out
new file mode 100644
index 5359dd8..f84e7c7
*** a/src/test/regress/expected/privileges.out
--- b/src/test/regress/expected/privileges.out
*************** ERROR:  role "nosuchuser" does not exist
*** 645,651 ****
  select has_table_privilege('pg_authid','sel');
  ERROR:  unrecognized privilege type: "sel"
  select has_table_privilege(-999999,'pg_authid','update');
! ERROR:  role with OID 4293967297 does not exist
  select has_table_privilege(1,'select');
   has_table_privilege 
  ---------------------
--- 645,655 ----
  select has_table_privilege('pg_authid','sel');
  ERROR:  unrecognized privilege type: "sel"
  select has_table_privilege(-999999,'pg_authid','update');
!  has_table_privilege 
! ---------------------
!  f
! (1 row)
! 
  select has_table_privilege(1,'select');
   has_table_privilege 
  ---------------------
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
new file mode 100644
index 80c3351..c4ecfe6
*** a/src/test/regress/expected/rules.out
--- b/src/test/regress/expected/rules.out
*************** pg_roles| SELECT pg_authid.rolname,
*** 1409,1415 ****
      pg_authid.rolinherit,
      pg_authid.rolcreaterole,
      pg_authid.rolcreatedb,
-     pg_authid.rolcatupdate,
      pg_authid.rolcanlogin,
      pg_authid.rolreplication,
      pg_authid.rolconnlimit,
--- 1409,1414 ----
*************** pg_shadow| SELECT pg_authid.rolname AS u
*** 1610,1616 ****
      pg_authid.oid AS usesysid,
      pg_authid.rolcreatedb AS usecreatedb,
      pg_authid.rolsuper AS usesuper,
-     pg_authid.rolcatupdate AS usecatupd,
      pg_authid.rolreplication AS userepl,
      pg_authid.rolpassword AS passwd,
      (pg_authid.rolvaliduntil)::abstime AS valuntil,
--- 1609,1614 ----
*************** pg_user| SELECT pg_shadow.usename,
*** 2064,2070 ****
      pg_shadow.usesysid,
      pg_shadow.usecreatedb,
      pg_shadow.usesuper,
-     pg_shadow.usecatupd,
      pg_shadow.userepl,
      '********'::text AS passwd,
      pg_shadow.valuntil,
--- 2062,2067 ----
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to