Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/backend/commands/functioncmds.c
Original file line number Diff line number Diff line change
Expand Up @@ -1526,7 +1526,7 @@ CreateFunction(ParseState *pstate, CreateFunctionStmt *stmt)
*/
if (isLeakProof && !superuser())
{
Oid role = get_role_oid("mdb_admin", true);
Oid role = get_role_oid("mdb_admin", true /*if nodoby created mdb_admin role in this database*/);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nobody?

if (!is_member_of_role(GetUserId(), role))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
Expand Down Expand Up @@ -1857,7 +1857,7 @@ AlterFunction(ParseState *pstate, AlterFunctionStmt *stmt)
procForm->proleakproof = intVal(leakproof_item->arg);
if (procForm->proleakproof && !superuser())
{
Oid role = get_role_oid("mdb_admin", true);
Oid role = get_role_oid("mdb_admin", true /*if nodoby created mdb_admin role in this database*/);
if (!is_member_of_role(GetUserId(), role))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
Expand Down
126 changes: 94 additions & 32 deletions src/backend/utils/adt/acl.c
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ static AclResult pg_role_aclcheck(Oid role_oid, Oid roleid, AclMode mode);

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

static bool has_privs_of_unwanted_system_role(Oid role);

/*
* getid
Expand Down Expand Up @@ -4991,9 +4992,65 @@ roles_is_member_of(Oid roleid, enum RoleRecurseType type,
* set; for such roles, membership implies the ability to do SET ROLE, but
* the privileges are not available until you've done so.
*/

/*
* This is basically original postgresql privs-check function
*/

// -- mdb_superuser patch

bool
has_privs_of_role_strict(Oid member, Oid role)
{
/* Fast path for simple case */
if (member == role)
return true;

/* Superusers have every privilege, so are part of every role */
if (superuser_arg(member))
return true;

/*
* Find all the roles that member has the privileges of, including
* multi-level recursion, then see if target role is any one of them.
*/
return list_member_oid(roles_is_member_of(member, ROLERECURSE_PRIVS,
InvalidOid, NULL),
role);
}

/*
* Check that role is either one of "dangerous" system role
* or has "strict" (not through mdb_admin or mdb_superuser)
* privs of this role
*/

static bool
has_privs_of_unwanted_system_role(Oid role) {
if (has_privs_of_role_strict(role, ROLE_PG_READ_SERVER_FILES)) {
return true;
}
if (has_privs_of_role_strict(role, ROLE_PG_WRITE_SERVER_FILES)) {
return true;
}
if (has_privs_of_role_strict(role, ROLE_PG_EXECUTE_SERVER_PROGRAM)) {
return true;
}
if (has_privs_of_role_strict(role, ROLE_PG_READ_ALL_DATA)) {
return true;
}
if (has_privs_of_role_strict(role, ROLE_PG_WRITE_ALL_DATA)) {
return true;
}

return false;
}

bool
has_privs_of_role(Oid member, Oid role)
{
Oid mdb_superuser_roleoid;

/* Fast path for simple case */
if (member == role)
return true;
Expand All @@ -5002,6 +5059,23 @@ has_privs_of_role(Oid member, Oid role)
if (superuser_arg(member))
return true;

mdb_superuser_roleoid = get_role_oid("mdb_superuser", true /*if nodoby created mdb_superuser role in this database*/);

if (is_member_of_role(member, mdb_superuser_roleoid)) {
/* if target role is superuser, disallow */
if (!superuser_arg(role)) {
/* we want mdb_roles_admin to bypass
* has_priv_of_roles test
* if target role is neither superuser nor
* some dangerous system role
*/
if (!has_privs_of_unwanted_system_role(role)) {
return true;
}
}
}


/*
* Find all the roles that member has the privileges of, including
* multi-level recursion, then see if target role is any one of them.
Expand All @@ -5011,6 +5085,7 @@ has_privs_of_role(Oid member, Oid role)
role);
}

// -- mdb_superuser patch

// -- non-upstream patch begin
/*
Expand All @@ -5032,7 +5107,7 @@ mdb_admin_allow_bypass_owner_checks(Oid userId, Oid ownerId)
return false;
}

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

if (has_privs_of_role(ownerId, ROLE_PG_READ_SERVER_FILES)) {
return false;
}

if (has_privs_of_role(ownerId, ROLE_PG_WRITE_SERVER_FILES)) {
return false;
}

if (has_privs_of_role(ownerId, ROLE_PG_EXECUTE_SERVER_PROGRAM)) {
return false;
}

/* All checks passed, hope will not be hacked here (again) */
return true;
return !has_privs_of_unwanted_system_role(ownerId);
}

// -- non-upstream patch end
Expand Down Expand Up @@ -5110,7 +5173,7 @@ check_is_member_of_role(Oid member, Oid role)
* check_mdb_admin_is_member_of_role
* is_member_of_role with a standard permission-violation error if not in usual case
* Is case `member` in mdb_admin we check that role is neither of superuser, pg_read/write
* server files nor pg_execute_server_program
* server files nor pg_execute_server_program or pg_read/write all data
*/
void
check_mdb_admin_is_member_of_role(Oid member, Oid role)
Expand All @@ -5121,9 +5184,10 @@ check_mdb_admin_is_member_of_role(Oid member, Oid role)
return;
}

mdb_admin_roleoid = get_role_oid("mdb_admin", true /* superuser suggested to be mdb_admin*/);
mdb_admin_roleoid = get_role_oid("mdb_admin", true /*if nodoby created mdb_admin role in this database*/);
/* Is userId actually member of mdb admin? */
if (is_member_of_role(member, mdb_admin_roleoid)) {

/* role is mdb admin */
if (superuser_arg(role)) {
ereport(ERROR,
Expand All @@ -5132,22 +5196,10 @@ check_mdb_admin_is_member_of_role(Oid member, Oid role)
GetUserNameFromId(role, false))));
}

if (has_privs_of_role(role, ROLE_PG_READ_SERVER_FILES)) {
if (has_privs_of_unwanted_system_role(role)) {
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("cannot transfer ownership to pg_read_server_files role in Cloud")));
}

if (has_privs_of_role(role, ROLE_PG_WRITE_SERVER_FILES)) {
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("cannot transfer ownership to pg_write_server_files role in Cloud")));
}

if (has_privs_of_role(role, ROLE_PG_EXECUTE_SERVER_PROGRAM)) {
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("cannot transfer ownership to pg_execute_server_program role in Cloud")));
errmsg("forbidden to transfer ownership to this system role in Cloud")));
}
} else {
/* if no, check membership transfer in usual way. */
Expand Down Expand Up @@ -5287,6 +5339,7 @@ select_best_grantor(Oid roleId, AclMode privileges,
List *roles_list;
int nrights;
ListCell *l;
Oid mdb_superuser_roleoid;

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

mdb_superuser_roleoid = get_role_oid("mdb_superuser", true /*if nodoby created mdb_superuser role in this database*/);

if (is_member_of_role(GetUserId(), mdb_superuser_roleoid)
&& has_privs_of_role(GetUserId(), ownerId)) {
*grantorId = mdb_superuser_roleoid;
AclMode mdb_superuser_allowed_privs = needed_goptions;
*grantOptions = mdb_superuser_allowed_privs;
return;
}

/*
* Otherwise we have to do a careful search to see if roleId has the
* privileges of any suitable role. Note: we can hang onto the result of
Expand All @@ -5309,7 +5372,6 @@ select_best_grantor(Oid roleId, AclMode privileges,
*/
roles_list = roles_is_member_of(roleId, ROLERECURSE_PRIVS,
InvalidOid, NULL);

/* initialize candidate result as default */
*grantorId = roleId;
*grantOptions = ACL_NO_RIGHTS;
Expand Down
12 changes: 8 additions & 4 deletions src/backend/utils/misc/guc.c
Original file line number Diff line number Diff line change
Expand Up @@ -7615,6 +7615,7 @@ set_config_option(const char *name, const char *value,
void *newextra = NULL;
bool prohibitValueChange = false;
bool makeDefault;
Oid role;

if (elevel == 0)
{
Expand Down Expand Up @@ -7772,10 +7773,13 @@ set_config_option(const char *name, const char *value,
case PGC_SUSET:
if (context == PGC_USERSET || context == PGC_BACKEND)
{
ereport(elevel,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied to set parameter \"%s\"",
name)));
role = get_role_oid("mdb_admin", true /*if nodoby created mdb_admin role in this database*/);
if (!(record->mdb_admin_allowed && is_member_of_role(GetUserId(), role))) {
ereport(elevel,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied to set parameter \"%s\"",
name)));
}
return 0;
}
break;
Expand Down
1 change: 1 addition & 0 deletions src/include/utils/acl.h
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ extern AclMode aclmask(const Acl *acl, Oid roleid, Oid ownerId,
extern int aclmembers(const Acl *acl, Oid **roleids);

extern bool has_privs_of_role(Oid member, Oid role);
extern bool has_privs_of_role_strict(Oid member, Oid role);
extern bool is_member_of_role(Oid member, Oid role);
extern bool is_member_of_role_nosuper(Oid member, Oid role);
extern bool is_admin_of_role(Oid member, Oid role);
Expand Down
55 changes: 37 additions & 18 deletions src/test/regress/expected/mdb_admin.out
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
CREATE ROLE regress_mdb_admin_user1;
CREATE ROLE regress_mdb_admin_user2;
CREATE ROLE regress_mdb_admin_user3;
CREATE ROLE mdb_admin;
CREATE ROLE regress_superuser WITH SUPERUSER;
GRANT mdb_admin TO regress_mdb_admin_user1;
GRANT CREATE ON DATABASE regression TO regress_mdb_admin_user2;
Expand All @@ -24,7 +23,7 @@ ALTER VIEW regress_mdb_admin_view OWNER TO regress_mdb_admin_user3;
ALTER TABLE regress_mdb_admin_schema.regress_mdb_admin_table OWNER TO regress_mdb_admin_user3;
ALTER TABLE regress_mdb_admin_table OWNER TO regress_mdb_admin_user3;
ALTER SCHEMA regress_mdb_admin_schema OWNER TO regress_mdb_admin_user3;
-- mdb admin fails to transfer ownership to superusers and system roles
-- mdb admin fails to transfer ownership to superusers and particular system roles
ALTER FUNCTION regress_mdb_admin_add (integer, integer) OWNER TO regress_superuser;
ERROR: cannot transfer ownership to superuser "regress_superuser"
ALTER VIEW regress_mdb_admin_view OWNER TO regress_superuser;
Expand All @@ -36,35 +35,55 @@ ERROR: cannot transfer ownership to superuser "regress_superuser"
ALTER SCHEMA regress_mdb_admin_schema OWNER TO regress_superuser;
ERROR: cannot transfer ownership to superuser "regress_superuser"
ALTER FUNCTION regress_mdb_admin_add (integer, integer) OWNER TO pg_execute_server_program;
ERROR: cannot transfer ownership to pg_execute_server_program role in Cloud
ERROR: forbidden to transfer ownership to this system role in Cloud
ALTER VIEW regress_mdb_admin_view OWNER TO pg_execute_server_program;
ERROR: cannot transfer ownership to pg_execute_server_program role in Cloud
ERROR: forbidden to transfer ownership to this system role in Cloud
ALTER TABLE regress_mdb_admin_schema.regress_mdb_admin_table OWNER TO pg_execute_server_program;
ERROR: cannot transfer ownership to pg_execute_server_program role in Cloud
ERROR: forbidden to transfer ownership to this system role in Cloud
ALTER TABLE regress_mdb_admin_table OWNER TO pg_execute_server_program;
ERROR: cannot transfer ownership to pg_execute_server_program role in Cloud
ERROR: forbidden to transfer ownership to this system role in Cloud
ALTER SCHEMA regress_mdb_admin_schema OWNER TO pg_execute_server_program;
ERROR: cannot transfer ownership to pg_execute_server_program role in Cloud
ERROR: forbidden to transfer ownership to this system role in Cloud
ALTER FUNCTION regress_mdb_admin_add (integer, integer) OWNER TO pg_write_server_files;
ERROR: cannot transfer ownership to pg_write_server_files role in Cloud
ERROR: forbidden to transfer ownership to this system role in Cloud
ALTER VIEW regress_mdb_admin_view OWNER TO pg_write_server_files;
ERROR: cannot transfer ownership to pg_write_server_files role in Cloud
ERROR: forbidden to transfer ownership to this system role in Cloud
ALTER TABLE regress_mdb_admin_schema.regress_mdb_admin_table OWNER TO pg_write_server_files;
ERROR: cannot transfer ownership to pg_write_server_files role in Cloud
ERROR: forbidden to transfer ownership to this system role in Cloud
ALTER TABLE regress_mdb_admin_table OWNER TO pg_write_server_files;
ERROR: cannot transfer ownership to pg_write_server_files role in Cloud
ERROR: forbidden to transfer ownership to this system role in Cloud
ALTER SCHEMA regress_mdb_admin_schema OWNER TO pg_write_server_files;
ERROR: cannot transfer ownership to pg_write_server_files role in Cloud
ERROR: forbidden to transfer ownership to this system role in Cloud
ALTER FUNCTION regress_mdb_admin_add (integer, integer) OWNER TO pg_read_server_files;
ERROR: cannot transfer ownership to pg_read_server_files role in Cloud
ERROR: forbidden to transfer ownership to this system role in Cloud
ALTER VIEW regress_mdb_admin_view OWNER TO pg_read_server_files;
ERROR: cannot transfer ownership to pg_read_server_files role in Cloud
ERROR: forbidden to transfer ownership to this system role in Cloud
ALTER TABLE regress_mdb_admin_schema.regress_mdb_admin_table OWNER TO pg_read_server_files;
ERROR: cannot transfer ownership to pg_read_server_files role in Cloud
ERROR: forbidden to transfer ownership to this system role in Cloud
ALTER TABLE regress_mdb_admin_table OWNER TO pg_read_server_files;
ERROR: cannot transfer ownership to pg_read_server_files role in Cloud
ERROR: forbidden to transfer ownership to this system role in Cloud
ALTER SCHEMA regress_mdb_admin_schema OWNER TO pg_read_server_files;
ERROR: cannot transfer ownership to pg_read_server_files role in Cloud
ERROR: forbidden to transfer ownership to this system role in Cloud
ALTER FUNCTION regress_mdb_admin_add (integer, integer) OWNER TO pg_write_all_data;
ERROR: forbidden to transfer ownership to this system role in Cloud
ALTER VIEW regress_mdb_admin_view OWNER TO pg_write_all_data;
ERROR: forbidden to transfer ownership to this system role in Cloud
ALTER TABLE regress_mdb_admin_schema.regress_mdb_admin_table OWNER TO pg_write_all_data;
ERROR: forbidden to transfer ownership to this system role in Cloud
ALTER TABLE regress_mdb_admin_table OWNER TO pg_write_all_data;
ERROR: forbidden to transfer ownership to this system role in Cloud
ALTER SCHEMA regress_mdb_admin_schema OWNER TO pg_write_all_data;
ERROR: forbidden to transfer ownership to this system role in Cloud
ALTER FUNCTION regress_mdb_admin_add (integer, integer) OWNER TO pg_read_all_data;
ERROR: forbidden to transfer ownership to this system role in Cloud
ALTER VIEW regress_mdb_admin_view OWNER TO pg_read_all_data;
ERROR: forbidden to transfer ownership to this system role in Cloud
ALTER TABLE regress_mdb_admin_schema.regress_mdb_admin_table OWNER TO pg_read_all_data;
ERROR: forbidden to transfer ownership to this system role in Cloud
ALTER TABLE regress_mdb_admin_table OWNER TO pg_read_all_data;
ERROR: forbidden to transfer ownership to this system role in Cloud
ALTER SCHEMA regress_mdb_admin_schema OWNER TO pg_read_all_data;
ERROR: forbidden to transfer ownership to this system role in Cloud
-- end tests
RESET SESSION AUTHORIZATION;
--
Expand All @@ -78,4 +97,4 @@ DROP SCHEMA regress_mdb_admin_schema;
DROP ROLE regress_mdb_admin_user1;
DROP ROLE regress_mdb_admin_user2;
DROP ROLE regress_mdb_admin_user3;
DROP ROLE mdb_admin;
DROP ROLE regress_superuser;
Loading
Loading