diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml new file mode 100644 index 515a40e..26bf35f *** a/doc/src/sgml/catalogs.sgml --- b/doc/src/sgml/catalogs.sgml *************** *** 1416,1430 **** - rolcatupdate - bool - - Role can update system catalogs directly. (Even a superuser cannot do - this unless this column is true) - - - - rolcanlogin bool --- 1416,1421 ---- *************** SELECT * FROM pg_locks pl LEFT JOIN pg_p *** 8492,8507 **** - rolcatupdate - bool - - - Role can update system catalogs directly. (Even a superuser cannot do - this unless this column is true) - - - - rolcanlogin bool --- 8483,8488 ---- diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c new file mode 100644 index 1e3888e..da4ba4e *** 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 *** 3619,3646 **** classForm = (Form_pg_class) GETSTRUCT(tuple); /* ! * 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 ! * themselves. ACL_USAGE is if we ever have system sequences. ! */ ! 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 ! elog(DEBUG2, "permission denied for system catalog update"); ! #endif ! mask &= ~(ACL_INSERT | ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE | ACL_USAGE); ! } ! ! /* ! * Otherwise, superusers bypass all permission-checking. */ if (superuser_arg(roleid)) { --- 3599,3605 ---- classForm = (Form_pg_class) GETSTRUCT(tuple); /* ! * Superusers bypass all permission-checking. */ if (superuser_arg(roleid)) { diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql new file mode 100644 index 5e69e2b..2800f73 *** 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, rolbypassrls AS usebypassrls, rolpassword AS passwd, --- 30,35 ---- *************** CREATE VIEW pg_user AS *** 57,63 **** usesysid, usecreatedb, usesuper, - usecatupd, userepl, usebypassrls, '********'::text as passwd, --- 55,60 ---- diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c new file mode 100644 index 2210eed..1d3b658 *** 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 e01e6aa..f60d08b *** 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 74b0450..0db1df3 *** a/src/test/regress/expected/privileges.out --- b/src/test/regress/expected/privileges.out *************** ERROR: role "nosuchuser" does not exist *** 676,682 **** 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 --------------------- --- 676,686 ---- 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 d50b103..4af4e05 *** a/src/test/regress/expected/rules.out --- b/src/test/regress/expected/rules.out *************** pg_roles| SELECT pg_authid.rolname, *** 1406,1412 **** pg_authid.rolinherit, pg_authid.rolcreaterole, pg_authid.rolcreatedb, - pg_authid.rolcatupdate, pg_authid.rolcanlogin, pg_authid.rolreplication, pg_authid.rolconnlimit, --- 1406,1411 ---- *************** pg_shadow| SELECT pg_authid.rolname AS u *** 1607,1613 **** 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.rolbypassrls AS usebypassrls, pg_authid.rolpassword AS passwd, --- 1606,1611 ---- *************** pg_user| SELECT pg_shadow.usename, *** 2062,2068 **** pg_shadow.usesysid, pg_shadow.usecreatedb, pg_shadow.usesuper, - pg_shadow.usecatupd, pg_shadow.userepl, pg_shadow.usebypassrls, '********'::text AS passwd, --- 2060,2065 ----