Skip to content

Commit ea9e36f

Browse files
authored
Role mdb_superuser: feature and regress testsing (#5)
This commit introduces new mdb internal role mdb_superuser. Role is capaple of: GRANT/REVOKE any set of priviledges to/from any object in database. Has power of pg_database_owner in any database, including: DROP any object in database (except system catalog and stuff) Role is NOT capaple of: Create database, role, extension or alter other roles with such priviledges. Transfer ownership to /pass has_priv of roles: PG_READ_ALL_DATA PG_WRITE_ALL_DATA PG_EXECUTE_SERVER_PROGRAM PG_READ_SERVER_FILES PG_WRITE_SERVER_FILES PG_DATABASE_OWNER Fix configure.ac USE_MDBLOCALES option handling Apply autoreconf stuff Set missing ok parameter ito true while acquiring mdb_superuser oid In regress tests, nobody creates mdb_superuser role, so missing ok is fine Allow mdb_superuser to have power of pg_database_owner Allow mdb_superuser to alter objects and grant ACl to objects, owner by pg_database_owner. Also, when acl check, allow mdb_supersuer use pg_database_owner role power to pass check
1 parent 956a8ec commit ea9e36f

11 files changed

Lines changed: 431 additions & 61 deletions

File tree

src/backend/commands/functioncmds.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1526,7 +1526,7 @@ CreateFunction(ParseState *pstate, CreateFunctionStmt *stmt)
15261526
*/
15271527
if (isLeakProof && !superuser())
15281528
{
1529-
Oid role = get_role_oid("mdb_admin", true);
1529+
Oid role = get_role_oid("mdb_admin", true /*if nodoby created mdb_admin role in this database*/);
15301530
if (!is_member_of_role(GetUserId(), role))
15311531
ereport(ERROR,
15321532
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
@@ -1857,7 +1857,7 @@ AlterFunction(ParseState *pstate, AlterFunctionStmt *stmt)
18571857
procForm->proleakproof = intVal(leakproof_item->arg);
18581858
if (procForm->proleakproof && !superuser())
18591859
{
1860-
Oid role = get_role_oid("mdb_admin", true);
1860+
Oid role = get_role_oid("mdb_admin", true /*if nodoby created mdb_admin role in this database*/);
18611861
if (!is_member_of_role(GetUserId(), role))
18621862
ereport(ERROR,
18631863
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),

src/backend/utils/adt/acl.c

Lines changed: 94 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ static AclResult pg_role_aclcheck(Oid role_oid, Oid roleid, AclMode mode);
116116

117117
static void RoleMembershipCacheCallback(Datum arg, int cacheid, uint32 hashvalue);
118118

119+
static bool has_privs_of_unwanted_system_role(Oid role);
119120

120121
/*
121122
* getid
@@ -4991,9 +4992,65 @@ roles_is_member_of(Oid roleid, enum RoleRecurseType type,
49914992
* set; for such roles, membership implies the ability to do SET ROLE, but
49924993
* the privileges are not available until you've done so.
49934994
*/
4995+
4996+
/*
4997+
* This is basically original postgresql privs-check function
4998+
*/
4999+
5000+
// -- mdb_superuser patch
5001+
5002+
bool
5003+
has_privs_of_role_strict(Oid member, Oid role)
5004+
{
5005+
/* Fast path for simple case */
5006+
if (member == role)
5007+
return true;
5008+
5009+
/* Superusers have every privilege, so are part of every role */
5010+
if (superuser_arg(member))
5011+
return true;
5012+
5013+
/*
5014+
* Find all the roles that member has the privileges of, including
5015+
* multi-level recursion, then see if target role is any one of them.
5016+
*/
5017+
return list_member_oid(roles_is_member_of(member, ROLERECURSE_PRIVS,
5018+
InvalidOid, NULL),
5019+
role);
5020+
}
5021+
5022+
/*
5023+
* Check that role is either one of "dangerous" system role
5024+
* or has "strict" (not through mdb_admin or mdb_superuser)
5025+
* privs of this role
5026+
*/
5027+
5028+
static bool
5029+
has_privs_of_unwanted_system_role(Oid role) {
5030+
if (has_privs_of_role_strict(role, ROLE_PG_READ_SERVER_FILES)) {
5031+
return true;
5032+
}
5033+
if (has_privs_of_role_strict(role, ROLE_PG_WRITE_SERVER_FILES)) {
5034+
return true;
5035+
}
5036+
if (has_privs_of_role_strict(role, ROLE_PG_EXECUTE_SERVER_PROGRAM)) {
5037+
return true;
5038+
}
5039+
if (has_privs_of_role_strict(role, ROLE_PG_READ_ALL_DATA)) {
5040+
return true;
5041+
}
5042+
if (has_privs_of_role_strict(role, ROLE_PG_WRITE_ALL_DATA)) {
5043+
return true;
5044+
}
5045+
5046+
return false;
5047+
}
5048+
49945049
bool
49955050
has_privs_of_role(Oid member, Oid role)
49965051
{
5052+
Oid mdb_superuser_roleoid;
5053+
49975054
/* Fast path for simple case */
49985055
if (member == role)
49995056
return true;
@@ -5002,6 +5059,23 @@ has_privs_of_role(Oid member, Oid role)
50025059
if (superuser_arg(member))
50035060
return true;
50045061

5062+
mdb_superuser_roleoid = get_role_oid("mdb_superuser", true /*if nodoby created mdb_superuser role in this database*/);
5063+
5064+
if (is_member_of_role(member, mdb_superuser_roleoid)) {
5065+
/* if target role is superuser, disallow */
5066+
if (!superuser_arg(role)) {
5067+
/* we want mdb_roles_admin to bypass
5068+
* has_priv_of_roles test
5069+
* if target role is neither superuser nor
5070+
* some dangerous system role
5071+
*/
5072+
if (!has_privs_of_unwanted_system_role(role)) {
5073+
return true;
5074+
}
5075+
}
5076+
}
5077+
5078+
50055079
/*
50065080
* Find all the roles that member has the privileges of, including
50075081
* multi-level recursion, then see if target role is any one of them.
@@ -5011,6 +5085,7 @@ has_privs_of_role(Oid member, Oid role)
50115085
role);
50125086
}
50135087

5088+
// -- mdb_superuser patch
50145089

50155090
// -- non-upstream patch begin
50165091
/*
@@ -5032,7 +5107,7 @@ mdb_admin_allow_bypass_owner_checks(Oid userId, Oid ownerId)
50325107
return false;
50335108
}
50345109

5035-
mdb_admin_roleoid = get_role_oid("mdb_admin", true /* superuser suggested to be mdb_admin*/);
5110+
mdb_admin_roleoid = get_role_oid("mdb_admin", true /*if nodoby created mdb_admin role in this database*/);
50365111
/* Is userId actually member of mdb admin? */
50375112
if (!is_member_of_role(userId, mdb_admin_roleoid)) {
50385113
/* if no, disallow. */
@@ -5045,23 +5120,11 @@ mdb_admin_allow_bypass_owner_checks(Oid userId, Oid ownerId)
50455120
*
50465121
* For now, we check that ownerId does not have
50475122
* priviledge to execute server program or/and
5048-
* read/write server files.
5123+
* read/write server files, or/and pg read/write all data
50495124
*/
50505125

5051-
if (has_privs_of_role(ownerId, ROLE_PG_READ_SERVER_FILES)) {
5052-
return false;
5053-
}
5054-
5055-
if (has_privs_of_role(ownerId, ROLE_PG_WRITE_SERVER_FILES)) {
5056-
return false;
5057-
}
5058-
5059-
if (has_privs_of_role(ownerId, ROLE_PG_EXECUTE_SERVER_PROGRAM)) {
5060-
return false;
5061-
}
5062-
50635126
/* All checks passed, hope will not be hacked here (again) */
5064-
return true;
5127+
return !has_privs_of_unwanted_system_role(ownerId);
50655128
}
50665129

50675130
// -- non-upstream patch end
@@ -5110,7 +5173,7 @@ check_is_member_of_role(Oid member, Oid role)
51105173
* check_mdb_admin_is_member_of_role
51115174
* is_member_of_role with a standard permission-violation error if not in usual case
51125175
* Is case `member` in mdb_admin we check that role is neither of superuser, pg_read/write
5113-
* server files nor pg_execute_server_program
5176+
* server files nor pg_execute_server_program or pg_read/write all data
51145177
*/
51155178
void
51165179
check_mdb_admin_is_member_of_role(Oid member, Oid role)
@@ -5121,9 +5184,10 @@ check_mdb_admin_is_member_of_role(Oid member, Oid role)
51215184
return;
51225185
}
51235186

5124-
mdb_admin_roleoid = get_role_oid("mdb_admin", true /* superuser suggested to be mdb_admin*/);
5187+
mdb_admin_roleoid = get_role_oid("mdb_admin", true /*if nodoby created mdb_admin role in this database*/);
51255188
/* Is userId actually member of mdb admin? */
51265189
if (is_member_of_role(member, mdb_admin_roleoid)) {
5190+
51275191
/* role is mdb admin */
51285192
if (superuser_arg(role)) {
51295193
ereport(ERROR,
@@ -5132,22 +5196,10 @@ check_mdb_admin_is_member_of_role(Oid member, Oid role)
51325196
GetUserNameFromId(role, false))));
51335197
}
51345198

5135-
if (has_privs_of_role(role, ROLE_PG_READ_SERVER_FILES)) {
5199+
if (has_privs_of_unwanted_system_role(role)) {
51365200
ereport(ERROR,
51375201
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
5138-
errmsg("cannot transfer ownership to pg_read_server_files role in Cloud")));
5139-
}
5140-
5141-
if (has_privs_of_role(role, ROLE_PG_WRITE_SERVER_FILES)) {
5142-
ereport(ERROR,
5143-
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
5144-
errmsg("cannot transfer ownership to pg_write_server_files role in Cloud")));
5145-
}
5146-
5147-
if (has_privs_of_role(role, ROLE_PG_EXECUTE_SERVER_PROGRAM)) {
5148-
ereport(ERROR,
5149-
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
5150-
errmsg("cannot transfer ownership to pg_execute_server_program role in Cloud")));
5202+
errmsg("forbidden to transfer ownership to this system role in Cloud")));
51515203
}
51525204
} else {
51535205
/* if no, check membership transfer in usual way. */
@@ -5287,6 +5339,7 @@ select_best_grantor(Oid roleId, AclMode privileges,
52875339
List *roles_list;
52885340
int nrights;
52895341
ListCell *l;
5342+
Oid mdb_superuser_roleoid;
52905343

52915344
/*
52925345
* The object owner is always treated as having all grant options, so if
@@ -5301,6 +5354,16 @@ select_best_grantor(Oid roleId, AclMode privileges,
53015354
return;
53025355
}
53035356

5357+
mdb_superuser_roleoid = get_role_oid("mdb_superuser", true /*if nodoby created mdb_superuser role in this database*/);
5358+
5359+
if (is_member_of_role(GetUserId(), mdb_superuser_roleoid)
5360+
&& has_privs_of_role(GetUserId(), ownerId)) {
5361+
*grantorId = mdb_superuser_roleoid;
5362+
AclMode mdb_superuser_allowed_privs = needed_goptions;
5363+
*grantOptions = mdb_superuser_allowed_privs;
5364+
return;
5365+
}
5366+
53045367
/*
53055368
* Otherwise we have to do a careful search to see if roleId has the
53065369
* privileges of any suitable role. Note: we can hang onto the result of
@@ -5309,7 +5372,6 @@ select_best_grantor(Oid roleId, AclMode privileges,
53095372
*/
53105373
roles_list = roles_is_member_of(roleId, ROLERECURSE_PRIVS,
53115374
InvalidOid, NULL);
5312-
53135375
/* initialize candidate result as default */
53145376
*grantorId = roleId;
53155377
*grantOptions = ACL_NO_RIGHTS;

src/backend/utils/misc/guc.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7615,6 +7615,7 @@ set_config_option(const char *name, const char *value,
76157615
void *newextra = NULL;
76167616
bool prohibitValueChange = false;
76177617
bool makeDefault;
7618+
Oid role;
76187619

76197620
if (elevel == 0)
76207621
{
@@ -7772,10 +7773,13 @@ set_config_option(const char *name, const char *value,
77727773
case PGC_SUSET:
77737774
if (context == PGC_USERSET || context == PGC_BACKEND)
77747775
{
7775-
ereport(elevel,
7776-
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
7777-
errmsg("permission denied to set parameter \"%s\"",
7778-
name)));
7776+
role = get_role_oid("mdb_admin", true /*if nodoby created mdb_admin role in this database*/);
7777+
if (!(record->mdb_admin_allowed && is_member_of_role(GetUserId(), role))) {
7778+
ereport(elevel,
7779+
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
7780+
errmsg("permission denied to set parameter \"%s\"",
7781+
name)));
7782+
}
77797783
return 0;
77807784
}
77817785
break;

src/include/utils/acl.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@ extern AclMode aclmask(const Acl *acl, Oid roleid, Oid ownerId,
207207
extern int aclmembers(const Acl *acl, Oid **roleids);
208208

209209
extern bool has_privs_of_role(Oid member, Oid role);
210+
extern bool has_privs_of_role_strict(Oid member, Oid role);
210211
extern bool is_member_of_role(Oid member, Oid role);
211212
extern bool is_member_of_role_nosuper(Oid member, Oid role);
212213
extern bool is_admin_of_role(Oid member, Oid role);

src/test/regress/expected/mdb_admin.out

Lines changed: 37 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
CREATE ROLE regress_mdb_admin_user1;
22
CREATE ROLE regress_mdb_admin_user2;
33
CREATE ROLE regress_mdb_admin_user3;
4-
CREATE ROLE mdb_admin;
54
CREATE ROLE regress_superuser WITH SUPERUSER;
65
GRANT mdb_admin TO regress_mdb_admin_user1;
76
GRANT CREATE ON DATABASE regression TO regress_mdb_admin_user2;
@@ -24,7 +23,7 @@ ALTER VIEW regress_mdb_admin_view OWNER TO regress_mdb_admin_user3;
2423
ALTER TABLE regress_mdb_admin_schema.regress_mdb_admin_table OWNER TO regress_mdb_admin_user3;
2524
ALTER TABLE regress_mdb_admin_table OWNER TO regress_mdb_admin_user3;
2625
ALTER SCHEMA regress_mdb_admin_schema OWNER TO regress_mdb_admin_user3;
27-
-- mdb admin fails to transfer ownership to superusers and system roles
26+
-- mdb admin fails to transfer ownership to superusers and particular system roles
2827
ALTER FUNCTION regress_mdb_admin_add (integer, integer) OWNER TO regress_superuser;
2928
ERROR: cannot transfer ownership to superuser "regress_superuser"
3029
ALTER VIEW regress_mdb_admin_view OWNER TO regress_superuser;
@@ -36,35 +35,55 @@ ERROR: cannot transfer ownership to superuser "regress_superuser"
3635
ALTER SCHEMA regress_mdb_admin_schema OWNER TO regress_superuser;
3736
ERROR: cannot transfer ownership to superuser "regress_superuser"
3837
ALTER FUNCTION regress_mdb_admin_add (integer, integer) OWNER TO pg_execute_server_program;
39-
ERROR: cannot transfer ownership to pg_execute_server_program role in Cloud
38+
ERROR: forbidden to transfer ownership to this system role in Cloud
4039
ALTER VIEW regress_mdb_admin_view OWNER TO pg_execute_server_program;
41-
ERROR: cannot transfer ownership to pg_execute_server_program role in Cloud
40+
ERROR: forbidden to transfer ownership to this system role in Cloud
4241
ALTER TABLE regress_mdb_admin_schema.regress_mdb_admin_table OWNER TO pg_execute_server_program;
43-
ERROR: cannot transfer ownership to pg_execute_server_program role in Cloud
42+
ERROR: forbidden to transfer ownership to this system role in Cloud
4443
ALTER TABLE regress_mdb_admin_table OWNER TO pg_execute_server_program;
45-
ERROR: cannot transfer ownership to pg_execute_server_program role in Cloud
44+
ERROR: forbidden to transfer ownership to this system role in Cloud
4645
ALTER SCHEMA regress_mdb_admin_schema OWNER TO pg_execute_server_program;
47-
ERROR: cannot transfer ownership to pg_execute_server_program role in Cloud
46+
ERROR: forbidden to transfer ownership to this system role in Cloud
4847
ALTER FUNCTION regress_mdb_admin_add (integer, integer) OWNER TO pg_write_server_files;
49-
ERROR: cannot transfer ownership to pg_write_server_files role in Cloud
48+
ERROR: forbidden to transfer ownership to this system role in Cloud
5049
ALTER VIEW regress_mdb_admin_view OWNER TO pg_write_server_files;
51-
ERROR: cannot transfer ownership to pg_write_server_files role in Cloud
50+
ERROR: forbidden to transfer ownership to this system role in Cloud
5251
ALTER TABLE regress_mdb_admin_schema.regress_mdb_admin_table OWNER TO pg_write_server_files;
53-
ERROR: cannot transfer ownership to pg_write_server_files role in Cloud
52+
ERROR: forbidden to transfer ownership to this system role in Cloud
5453
ALTER TABLE regress_mdb_admin_table OWNER TO pg_write_server_files;
55-
ERROR: cannot transfer ownership to pg_write_server_files role in Cloud
54+
ERROR: forbidden to transfer ownership to this system role in Cloud
5655
ALTER SCHEMA regress_mdb_admin_schema OWNER TO pg_write_server_files;
57-
ERROR: cannot transfer ownership to pg_write_server_files role in Cloud
56+
ERROR: forbidden to transfer ownership to this system role in Cloud
5857
ALTER FUNCTION regress_mdb_admin_add (integer, integer) OWNER TO pg_read_server_files;
59-
ERROR: cannot transfer ownership to pg_read_server_files role in Cloud
58+
ERROR: forbidden to transfer ownership to this system role in Cloud
6059
ALTER VIEW regress_mdb_admin_view OWNER TO pg_read_server_files;
61-
ERROR: cannot transfer ownership to pg_read_server_files role in Cloud
60+
ERROR: forbidden to transfer ownership to this system role in Cloud
6261
ALTER TABLE regress_mdb_admin_schema.regress_mdb_admin_table OWNER TO pg_read_server_files;
63-
ERROR: cannot transfer ownership to pg_read_server_files role in Cloud
62+
ERROR: forbidden to transfer ownership to this system role in Cloud
6463
ALTER TABLE regress_mdb_admin_table OWNER TO pg_read_server_files;
65-
ERROR: cannot transfer ownership to pg_read_server_files role in Cloud
64+
ERROR: forbidden to transfer ownership to this system role in Cloud
6665
ALTER SCHEMA regress_mdb_admin_schema OWNER TO pg_read_server_files;
67-
ERROR: cannot transfer ownership to pg_read_server_files role in Cloud
66+
ERROR: forbidden to transfer ownership to this system role in Cloud
67+
ALTER FUNCTION regress_mdb_admin_add (integer, integer) OWNER TO pg_write_all_data;
68+
ERROR: forbidden to transfer ownership to this system role in Cloud
69+
ALTER VIEW regress_mdb_admin_view OWNER TO pg_write_all_data;
70+
ERROR: forbidden to transfer ownership to this system role in Cloud
71+
ALTER TABLE regress_mdb_admin_schema.regress_mdb_admin_table OWNER TO pg_write_all_data;
72+
ERROR: forbidden to transfer ownership to this system role in Cloud
73+
ALTER TABLE regress_mdb_admin_table OWNER TO pg_write_all_data;
74+
ERROR: forbidden to transfer ownership to this system role in Cloud
75+
ALTER SCHEMA regress_mdb_admin_schema OWNER TO pg_write_all_data;
76+
ERROR: forbidden to transfer ownership to this system role in Cloud
77+
ALTER FUNCTION regress_mdb_admin_add (integer, integer) OWNER TO pg_read_all_data;
78+
ERROR: forbidden to transfer ownership to this system role in Cloud
79+
ALTER VIEW regress_mdb_admin_view OWNER TO pg_read_all_data;
80+
ERROR: forbidden to transfer ownership to this system role in Cloud
81+
ALTER TABLE regress_mdb_admin_schema.regress_mdb_admin_table OWNER TO pg_read_all_data;
82+
ERROR: forbidden to transfer ownership to this system role in Cloud
83+
ALTER TABLE regress_mdb_admin_table OWNER TO pg_read_all_data;
84+
ERROR: forbidden to transfer ownership to this system role in Cloud
85+
ALTER SCHEMA regress_mdb_admin_schema OWNER TO pg_read_all_data;
86+
ERROR: forbidden to transfer ownership to this system role in Cloud
6887
-- end tests
6988
RESET SESSION AUTHORIZATION;
7089
--
@@ -78,4 +97,4 @@ DROP SCHEMA regress_mdb_admin_schema;
7897
DROP ROLE regress_mdb_admin_user1;
7998
DROP ROLE regress_mdb_admin_user2;
8099
DROP ROLE regress_mdb_admin_user3;
81-
DROP ROLE mdb_admin;
100+
DROP ROLE regress_superuser;

0 commit comments

Comments
 (0)