mirror of https://github.com/citusdata/citus.git
Perform permission checks in functions manipulating distributed tables.
Previously several commands, amongst them commands like master_create_distributed_table(), were allowed for everyone. That's not good: Even though citus currently requires superuser permissions, we shouldn't allow non-superusers to perform actions as sensitive as making a table distributed. There's no checks on the worker_* functions, as these usually just punt the action to underlying postgres functionality, which then perform the necessary checks.pull/471/head
parent
25f919576f
commit
12a246de37
|
@ -98,6 +98,8 @@ master_create_distributed_table(PG_FUNCTION_ARGS)
|
|||
distributedRelation = relation_open(distributedRelationId, AccessExclusiveLock);
|
||||
distributedRelationName = RelationGetRelationName(distributedRelation);
|
||||
|
||||
EnsureTableOwner(distributedRelationId);
|
||||
|
||||
/* open system catalog and insert new tuple */
|
||||
pgDistPartition = heap_open(DistPartitionRelationId(), RowExclusiveLock);
|
||||
|
||||
|
|
|
@ -88,6 +88,14 @@ master_create_worker_shards(PG_FUNCTION_ARGS)
|
|||
/* make sure table is hash partitioned */
|
||||
CheckHashPartitionedTable(distributedTableId);
|
||||
|
||||
/*
|
||||
* In contrast to append/range partitioned tables it makes more sense to
|
||||
* require ownership privileges - shards for hash-partitioned tables are
|
||||
* only created once, not continually during ingest as for the other
|
||||
* partitioning types.
|
||||
*/
|
||||
EnsureTableOwner(distributedTableId);
|
||||
|
||||
/* we plan to add shards: get an exclusive metadata lock */
|
||||
LockRelationDistributionMetadata(distributedTableId, ExclusiveLock);
|
||||
|
||||
|
|
|
@ -112,7 +112,9 @@ master_apply_delete_command(PG_FUNCTION_ARGS)
|
|||
schemaName = deleteStatement->relation->schemaname;
|
||||
relationName = deleteStatement->relation->relname;
|
||||
relationId = RangeVarGetRelid(deleteStatement->relation, NoLock, failOK);
|
||||
|
||||
CheckDistributedTable(relationId);
|
||||
EnsureTablePermissions(relationId, ACL_DELETE);
|
||||
|
||||
queryTreeList = pg_analyze_and_rewrite(queryTreeNode, queryString, NULL, 0);
|
||||
deleteQuery = (Query *) linitial(queryTreeList);
|
||||
|
@ -187,12 +189,29 @@ master_drop_all_shards(PG_FUNCTION_ARGS)
|
|||
/* ensure proper values are used if the table exists */
|
||||
Oid schemaId = get_rel_namespace(relationId);
|
||||
schemaName = get_namespace_name(schemaId);
|
||||
|
||||
/*
|
||||
* Only allow the owner to drop all shards, this is more akin to DDL
|
||||
* than DELETE.
|
||||
*/
|
||||
EnsureTableOwner(relationId);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* table has been dropped, rely on user-supplied values */
|
||||
schemaName = text_to_cstring(schemaNameText);
|
||||
relationName = text_to_cstring(relationNameText);
|
||||
|
||||
/*
|
||||
* Verify that this only is run as superuser - that's how it's used in
|
||||
* our drop event trigger, and we can't verify permissions for an
|
||||
* already dropped relation.
|
||||
*/
|
||||
if (!superuser())
|
||||
{
|
||||
ereport(ERROR, (errmsg("cannot drop all shards of a dropped table as "
|
||||
"non-superuser")));
|
||||
}
|
||||
}
|
||||
|
||||
shardIntervalList = LoadShardIntervalList(relationId);
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
#include "postgres.h"
|
||||
#include "funcapi.h"
|
||||
#include "miscadmin.h"
|
||||
|
||||
#include "access/htup_details.h"
|
||||
#include "access/xact.h"
|
||||
|
@ -28,6 +29,7 @@
|
|||
#include "distributed/worker_manager.h"
|
||||
#include "nodes/makefuncs.h"
|
||||
#include "parser/scansup.h"
|
||||
#include "utils/acl.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/datum.h"
|
||||
#include "utils/fmgroids.h"
|
||||
|
@ -611,3 +613,37 @@ BuildDistributionKeyFromColumnName(Relation distributedRelation, char *columnNam
|
|||
|
||||
return (Node *) column;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Check that the current user has `mode` permissions on relationId, error out
|
||||
* if not. Superusers always have such permissions.
|
||||
*/
|
||||
void
|
||||
EnsureTablePermissions(Oid relationId, AclMode mode)
|
||||
{
|
||||
AclResult aclresult;
|
||||
|
||||
aclresult = pg_class_aclcheck(relationId, GetUserId(), mode);
|
||||
|
||||
if (aclresult != ACLCHECK_OK)
|
||||
{
|
||||
aclcheck_error(aclresult, ACL_KIND_CLASS,
|
||||
get_rel_name(relationId));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Check that the current user has owner rights to relationId, error out if
|
||||
* not. Superusers are regarded as owners.
|
||||
*/
|
||||
void
|
||||
EnsureTableOwner(Oid relationId)
|
||||
{
|
||||
if (!pg_class_ownercheck(relationId, GetUserId()))
|
||||
{
|
||||
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
|
||||
get_rel_name(relationId));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,6 +76,8 @@ master_copy_shard_placement(PG_FUNCTION_ARGS)
|
|||
bool dataCopied = false;
|
||||
char relationKind = '\0';
|
||||
|
||||
EnsureTableOwner(distributedTableId);
|
||||
|
||||
/*
|
||||
* By taking an exclusive lock on the shard, we both stop all modifications
|
||||
* (INSERT, UPDATE, or DELETE) and prevent concurrent repair operations from
|
||||
|
|
|
@ -82,6 +82,8 @@ master_create_empty_shard(PG_FUNCTION_ARGS)
|
|||
char storageType = SHARD_STORAGE_TABLE;
|
||||
|
||||
Oid relationId = ResolveRelationId(relationNameText);
|
||||
|
||||
EnsureTablePermissions(relationId, ACL_INSERT);
|
||||
CheckDistributedTable(relationId);
|
||||
|
||||
if (CStoreTable(relationId))
|
||||
|
@ -170,6 +172,9 @@ master_append_table_to_shard(PG_FUNCTION_ARGS)
|
|||
bool cstoreTable = CStoreTable(relationId);
|
||||
|
||||
char storageType = shardInterval->storageType;
|
||||
|
||||
EnsureTablePermissions(relationId, ACL_INSERT);
|
||||
|
||||
if (storageType != SHARD_STORAGE_TABLE && !cstoreTable)
|
||||
{
|
||||
ereport(ERROR, (errmsg("cannot append to shardId " UINT64_FORMAT, shardId),
|
||||
|
|
|
@ -669,6 +669,9 @@ CurrentUserName(void)
|
|||
* master_dist_partition_cache_invalidate is a trigger function that performs
|
||||
* relcache invalidations when the contents of pg_dist_partition are changed
|
||||
* on the SQL level.
|
||||
*
|
||||
* NB: We decided there is little point in checking permissions here, there
|
||||
* are much easier ways to waste CPU than causing cache invalidations.
|
||||
*/
|
||||
Datum
|
||||
master_dist_partition_cache_invalidate(PG_FUNCTION_ARGS)
|
||||
|
@ -727,6 +730,9 @@ master_dist_partition_cache_invalidate(PG_FUNCTION_ARGS)
|
|||
* master_dist_shard_cache_invalidate is a trigger function that performs
|
||||
* relcache invalidations when the contents of pg_dist_shard are changed
|
||||
* on the SQL level.
|
||||
*
|
||||
* NB: We decided there is little point in checking permissions here, there
|
||||
* are much easier ways to waste CPU than causing cache invalidations.
|
||||
*/
|
||||
Datum
|
||||
master_dist_shard_cache_invalidate(PG_FUNCTION_ARGS)
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "access/tupdesc.h"
|
||||
#include "distributed/citus_nodes.h"
|
||||
#include "distributed/relay_utility.h"
|
||||
#include "utils/acl.h"
|
||||
#include "utils/relcache.h"
|
||||
|
||||
|
||||
|
@ -77,5 +78,7 @@ extern void DeleteShardPlacementRow(uint64 shardId, char *workerName, uint32 wor
|
|||
/* Remaining metadata utility functions */
|
||||
extern Node * BuildDistributionKeyFromColumnName(Relation distributedRelation,
|
||||
char *columnName);
|
||||
extern void EnsureTablePermissions(Oid relationId, AclMode mode);
|
||||
extern void EnsureTableOwner(Oid relationId);
|
||||
|
||||
#endif /* MASTER_METADATA_UTILITY_H */
|
||||
|
|
Loading…
Reference in New Issue