mirror of
https://github.com/postgres/postgres.git
synced 2025-07-27 12:41:57 +03:00
sepgsql: Check CREATE permissions for some object types.
KaiGai Kohei, reviewed by Dimitri Fontaine and me.
This commit is contained in:
@ -10,33 +10,100 @@
|
|||||||
*/
|
*/
|
||||||
#include "postgres.h"
|
#include "postgres.h"
|
||||||
|
|
||||||
|
#include "access/genam.h"
|
||||||
|
#include "access/heapam.h"
|
||||||
|
#include "access/sysattr.h"
|
||||||
#include "catalog/dependency.h"
|
#include "catalog/dependency.h"
|
||||||
#include "catalog/pg_database.h"
|
#include "catalog/pg_database.h"
|
||||||
|
#include "catalog/indexing.h"
|
||||||
|
#include "commands/dbcommands.h"
|
||||||
#include "commands/seclabel.h"
|
#include "commands/seclabel.h"
|
||||||
|
#include "utils/fmgroids.h"
|
||||||
|
#include "utils/tqual.h"
|
||||||
#include "sepgsql.h"
|
#include "sepgsql.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sepgsql_database_post_create
|
||||||
|
*
|
||||||
|
* This routine assigns a default security label on a newly defined
|
||||||
|
* database, and check permission needed for its creation.
|
||||||
|
*/
|
||||||
void
|
void
|
||||||
sepgsql_database_post_create(Oid databaseId)
|
sepgsql_database_post_create(Oid databaseId, const char *dtemplate)
|
||||||
{
|
{
|
||||||
char *scontext = sepgsql_get_client_label();
|
Relation rel;
|
||||||
char *tcontext;
|
ScanKeyData skey;
|
||||||
char *ncontext;
|
SysScanDesc sscan;
|
||||||
ObjectAddress object;
|
HeapTuple tuple;
|
||||||
|
char *tcontext;
|
||||||
|
char *ncontext;
|
||||||
|
char audit_name[NAMEDATALEN + 20];
|
||||||
|
ObjectAddress object;
|
||||||
|
Form_pg_database datForm;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Oid of the source database is not saved in pg_database catalog,
|
||||||
|
* so we collect its identifier using contextual information.
|
||||||
|
* If NULL, its default is "template1" according to createdb().
|
||||||
|
*/
|
||||||
|
if (!dtemplate)
|
||||||
|
dtemplate = "template1";
|
||||||
|
|
||||||
|
object.classId = DatabaseRelationId;
|
||||||
|
object.objectId = get_database_oid(dtemplate, false);
|
||||||
|
object.objectSubId = 0;
|
||||||
|
|
||||||
|
tcontext = sepgsql_get_label(object.classId,
|
||||||
|
object.objectId,
|
||||||
|
object.objectSubId);
|
||||||
|
/*
|
||||||
|
* check db_database:{getattr} permission
|
||||||
|
*/
|
||||||
|
snprintf(audit_name, sizeof(audit_name), "database %s", dtemplate);
|
||||||
|
sepgsql_avc_check_perms_label(tcontext,
|
||||||
|
SEPG_CLASS_DB_DATABASE,
|
||||||
|
SEPG_DB_DATABASE__GETATTR,
|
||||||
|
audit_name,
|
||||||
|
true);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Compute a default security label of the newly created database
|
* Compute a default security label of the newly created database
|
||||||
* based on a pair of security label of client and source database.
|
* based on a pair of security label of client and source database.
|
||||||
*
|
*
|
||||||
* XXX - Right now, this logic uses "template1" as its source, because
|
* XXX - uncoming version of libselinux supports to take object
|
||||||
* here is no way to know the Oid of source database.
|
* name to handle special treatment on default security label.
|
||||||
*/
|
*/
|
||||||
object.classId = DatabaseRelationId;
|
rel = heap_open(DatabaseRelationId, AccessShareLock);
|
||||||
object.objectId = TemplateDbOid;
|
|
||||||
object.objectSubId = 0;
|
|
||||||
tcontext = GetSecurityLabel(&object, SEPGSQL_LABEL_TAG);
|
|
||||||
|
|
||||||
ncontext = sepgsql_compute_create(scontext, tcontext,
|
ScanKeyInit(&skey,
|
||||||
|
ObjectIdAttributeNumber,
|
||||||
|
BTEqualStrategyNumber, F_OIDEQ,
|
||||||
|
ObjectIdGetDatum(databaseId));
|
||||||
|
|
||||||
|
sscan = systable_beginscan(rel, DatabaseOidIndexId, true,
|
||||||
|
SnapshotSelf, 1, &skey);
|
||||||
|
tuple = systable_getnext(sscan);
|
||||||
|
if (!HeapTupleIsValid(tuple))
|
||||||
|
elog(ERROR, "catalog lookup failed for database %u", databaseId);
|
||||||
|
|
||||||
|
datForm = (Form_pg_database) GETSTRUCT(tuple);
|
||||||
|
|
||||||
|
ncontext = sepgsql_compute_create(sepgsql_get_client_label(),
|
||||||
|
tcontext,
|
||||||
SEPG_CLASS_DB_DATABASE);
|
SEPG_CLASS_DB_DATABASE);
|
||||||
|
/*
|
||||||
|
* check db_database:{create} permission
|
||||||
|
*/
|
||||||
|
snprintf(audit_name, sizeof(audit_name),
|
||||||
|
"database %s", NameStr(datForm->datname));
|
||||||
|
sepgsql_avc_check_perms_label(ncontext,
|
||||||
|
SEPG_CLASS_DB_DATABASE,
|
||||||
|
SEPG_DB_DATABASE__CREATE,
|
||||||
|
audit_name,
|
||||||
|
true);
|
||||||
|
|
||||||
|
systable_endscan(sscan);
|
||||||
|
heap_close(rel, AccessShareLock);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Assign the default security label on the new database
|
* Assign the default security label on the new database
|
||||||
|
80
contrib/sepgsql/expected/create.out
Normal file
80
contrib/sepgsql/expected/create.out
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
--
|
||||||
|
-- Regression Test for Creation of Object Permission Checks
|
||||||
|
--
|
||||||
|
-- confirm required permissions using audit messages
|
||||||
|
SELECT sepgsql_getcon(); -- confirm client privilege
|
||||||
|
sepgsql_getcon
|
||||||
|
-------------------------------------------
|
||||||
|
unconfined_u:unconfined_r:unconfined_t:s0
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SET sepgsql.debug_audit = true;
|
||||||
|
SET client_min_messages = LOG;
|
||||||
|
CREATE DATABASE regtest_sepgsql_test_database;
|
||||||
|
LOG: SELinux: allowed { getattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_db_t:s0 tclass=db_database name="database template1"
|
||||||
|
LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_db_t:s0 tclass=db_database name="database regtest_sepgsql_test_database"
|
||||||
|
CREATE SCHEMA regtest_schema;
|
||||||
|
LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema"
|
||||||
|
SET search_path = regtest_schema, public;
|
||||||
|
CREATE TABLE regtest_table (x serial primary key, y text);
|
||||||
|
NOTICE: CREATE TABLE will create implicit sequence "regtest_table_x_seq" for serial column "regtest_table.x"
|
||||||
|
LOG: SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema"
|
||||||
|
LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_seq_t:s0 tclass=db_sequence name="sequence regtest_table_x_seq"
|
||||||
|
LOG: SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema"
|
||||||
|
LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="table regtest_table"
|
||||||
|
LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table column tableoid"
|
||||||
|
LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table column cmax"
|
||||||
|
LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table column xmax"
|
||||||
|
LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table column cmin"
|
||||||
|
LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table column xmin"
|
||||||
|
LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table column ctid"
|
||||||
|
LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table column x"
|
||||||
|
LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table column y"
|
||||||
|
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "regtest_table_pkey" for table "regtest_table"
|
||||||
|
ALTER TABLE regtest_table ADD COLUMN z int;
|
||||||
|
LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table column z"
|
||||||
|
CREATE TABLE regtest_table_2 (a int) WITH OIDS;
|
||||||
|
LOG: SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema"
|
||||||
|
LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="table regtest_table_2"
|
||||||
|
LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table_2 column tableoid"
|
||||||
|
LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table_2 column cmax"
|
||||||
|
LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table_2 column xmax"
|
||||||
|
LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table_2 column cmin"
|
||||||
|
LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table_2 column xmin"
|
||||||
|
LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table_2 column oid"
|
||||||
|
LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table_2 column ctid"
|
||||||
|
LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table_2 column a"
|
||||||
|
-- corresponding toast table should not have label and permission checks
|
||||||
|
ALTER TABLE regtest_table_2 ADD COLUMN b text;
|
||||||
|
LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table_2 column b"
|
||||||
|
-- VACUUM FULL internally create a new table and swap them later.
|
||||||
|
VACUUM FULL regtest_table;
|
||||||
|
CREATE VIEW regtest_view AS SELECT * FROM regtest_table WHERE x < 100;
|
||||||
|
LOG: SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema"
|
||||||
|
LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_view_t:s0 tclass=db_view name="view regtest_view"
|
||||||
|
CREATE SEQUENCE regtest_seq;
|
||||||
|
LOG: SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema"
|
||||||
|
LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_seq_t:s0 tclass=db_sequence name="sequence regtest_seq"
|
||||||
|
CREATE TYPE regtest_comptype AS (a int, b text);
|
||||||
|
CREATE FUNCTION regtest_func(text,int[]) RETURNS bool LANGUAGE plpgsql
|
||||||
|
AS 'BEGIN RAISE NOTICE ''regtest_func => %'', $1; RETURN true; END';
|
||||||
|
LOG: SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema"
|
||||||
|
LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="function regtest_func(text,integer[])"
|
||||||
|
CREATE AGGREGATE regtest_agg (
|
||||||
|
sfunc1 = int4pl, basetype = int4, stype1 = int4, initcond1 = '0'
|
||||||
|
);
|
||||||
|
LOG: SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema"
|
||||||
|
LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="function regtest_agg(integer)"
|
||||||
|
--
|
||||||
|
-- clean-up
|
||||||
|
--
|
||||||
|
DROP DATABASE IF EXISTS regtest_sepgsql_test_database;
|
||||||
|
DROP SCHEMA IF EXISTS regtest_schema CASCADE;
|
||||||
|
NOTICE: drop cascades to 7 other objects
|
||||||
|
DETAIL: drop cascades to table regtest_table
|
||||||
|
drop cascades to table regtest_table_2
|
||||||
|
drop cascades to view regtest_view
|
||||||
|
drop cascades to sequence regtest_seq
|
||||||
|
drop cascades to type regtest_comptype
|
||||||
|
drop cascades to function regtest_func(text,integer[])
|
||||||
|
drop cascades to function regtest_agg(integer)
|
@ -41,6 +41,23 @@ static ExecutorCheckPerms_hook_type next_exec_check_perms_hook = NULL;
|
|||||||
static needs_fmgr_hook_type next_needs_fmgr_hook = NULL;
|
static needs_fmgr_hook_type next_needs_fmgr_hook = NULL;
|
||||||
static fmgr_hook_type next_fmgr_hook = NULL;
|
static fmgr_hook_type next_fmgr_hook = NULL;
|
||||||
static ProcessUtility_hook_type next_ProcessUtility_hook = NULL;
|
static ProcessUtility_hook_type next_ProcessUtility_hook = NULL;
|
||||||
|
static ExecutorStart_hook_type next_ExecutorStart_hook = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Contextual information on DDL commands
|
||||||
|
*/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
NodeTag cmdtype;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Name of the template database given by users on CREATE DATABASE
|
||||||
|
* command. Elsewhere (including the case of default) NULL.
|
||||||
|
*/
|
||||||
|
const char *createdb_dtemplate;
|
||||||
|
} sepgsql_context_info_t;
|
||||||
|
|
||||||
|
static sepgsql_context_info_t sepgsql_context_info;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* GUC: sepgsql.permissive = (on|off)
|
* GUC: sepgsql.permissive = (on|off)
|
||||||
@ -127,7 +144,8 @@ sepgsql_object_access(ObjectAccessType access,
|
|||||||
switch (classId)
|
switch (classId)
|
||||||
{
|
{
|
||||||
case DatabaseRelationId:
|
case DatabaseRelationId:
|
||||||
sepgsql_database_post_create(objectId);
|
sepgsql_database_post_create(objectId,
|
||||||
|
sepgsql_context_info.createdb_dtemplate);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NamespaceRelationId:
|
case NamespaceRelationId:
|
||||||
@ -136,7 +154,30 @@ sepgsql_object_access(ObjectAccessType access,
|
|||||||
|
|
||||||
case RelationRelationId:
|
case RelationRelationId:
|
||||||
if (subId == 0)
|
if (subId == 0)
|
||||||
sepgsql_relation_post_create(objectId);
|
{
|
||||||
|
/*
|
||||||
|
* All cases we want to apply permission checks on
|
||||||
|
* creation of a new relation are invocation of the
|
||||||
|
* heap_create_with_catalog via DefineRelation or
|
||||||
|
* OpenIntoRel.
|
||||||
|
* Elsewhere, we need neither assignment of security
|
||||||
|
* label nor permission checks.
|
||||||
|
*/
|
||||||
|
switch (sepgsql_context_info.cmdtype)
|
||||||
|
{
|
||||||
|
case T_CreateStmt:
|
||||||
|
case T_ViewStmt:
|
||||||
|
case T_CreateSeqStmt:
|
||||||
|
case T_CompositeTypeStmt:
|
||||||
|
case T_CreateForeignTableStmt:
|
||||||
|
case T_SelectStmt:
|
||||||
|
sepgsql_relation_post_create(objectId);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* via make_new_heap() */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
sepgsql_attribute_post_create(objectId, subId);
|
sepgsql_attribute_post_create(objectId, subId);
|
||||||
break;
|
break;
|
||||||
@ -294,6 +335,46 @@ sepgsql_fmgr_hook(FmgrHookEventType event,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sepgsql_executor_start
|
||||||
|
*
|
||||||
|
* It saves contextual information during ExecutorStart to distinguish
|
||||||
|
* a case with/without permission checks later.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
sepgsql_executor_start(QueryDesc *queryDesc, int eflags)
|
||||||
|
{
|
||||||
|
sepgsql_context_info_t saved_context_info = sepgsql_context_info;
|
||||||
|
|
||||||
|
PG_TRY();
|
||||||
|
{
|
||||||
|
if (queryDesc->operation == CMD_SELECT)
|
||||||
|
sepgsql_context_info.cmdtype = T_SelectStmt;
|
||||||
|
else if (queryDesc->operation == CMD_INSERT)
|
||||||
|
sepgsql_context_info.cmdtype = T_InsertStmt;
|
||||||
|
else if (queryDesc->operation == CMD_DELETE)
|
||||||
|
sepgsql_context_info.cmdtype = T_DeleteStmt;
|
||||||
|
else if (queryDesc->operation == CMD_UPDATE)
|
||||||
|
sepgsql_context_info.cmdtype = T_UpdateStmt;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* XXX - If queryDesc->operation is not above four cases, an error
|
||||||
|
* shall be raised on the following executor stage soon.
|
||||||
|
*/
|
||||||
|
if (next_ExecutorStart_hook)
|
||||||
|
(*next_ExecutorStart_hook) (queryDesc, eflags);
|
||||||
|
else
|
||||||
|
standard_ExecutorStart(queryDesc, eflags);
|
||||||
|
}
|
||||||
|
PG_CATCH();
|
||||||
|
{
|
||||||
|
sepgsql_context_info = saved_context_info;
|
||||||
|
PG_RE_THROW();
|
||||||
|
}
|
||||||
|
PG_END_TRY();
|
||||||
|
sepgsql_context_info = saved_context_info;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* sepgsql_utility_command
|
* sepgsql_utility_command
|
||||||
*
|
*
|
||||||
@ -308,44 +389,74 @@ sepgsql_utility_command(Node *parsetree,
|
|||||||
DestReceiver *dest,
|
DestReceiver *dest,
|
||||||
char *completionTag)
|
char *completionTag)
|
||||||
{
|
{
|
||||||
if (next_ProcessUtility_hook)
|
sepgsql_context_info_t saved_context_info = sepgsql_context_info;
|
||||||
(*next_ProcessUtility_hook) (parsetree, queryString, params,
|
ListCell *cell;
|
||||||
isTopLevel, dest, completionTag);
|
|
||||||
|
|
||||||
/*
|
PG_TRY();
|
||||||
* Check command tag to avoid nefarious operations
|
|
||||||
*/
|
|
||||||
switch (nodeTag(parsetree))
|
|
||||||
{
|
{
|
||||||
case T_LoadStmt:
|
/*
|
||||||
|
* Check command tag to avoid nefarious operations, and save the
|
||||||
|
* current contextual information to determine whether we should
|
||||||
|
* apply permission checks here, or not.
|
||||||
|
*/
|
||||||
|
sepgsql_context_info.cmdtype = nodeTag(parsetree);
|
||||||
|
|
||||||
/*
|
switch (nodeTag(parsetree))
|
||||||
* We reject LOAD command across the board on enforcing mode,
|
{
|
||||||
* because a binary module can arbitrarily override hooks.
|
case T_CreatedbStmt:
|
||||||
*/
|
/*
|
||||||
if (sepgsql_getenforce())
|
* We hope to reference name of the source database, but it
|
||||||
{
|
* does not appear in system catalog. So, we save it here.
|
||||||
ereport(ERROR,
|
*/
|
||||||
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
foreach (cell, ((CreatedbStmt *) parsetree)->options)
|
||||||
errmsg("SELinux: LOAD is not permitted")));
|
{
|
||||||
}
|
DefElem *defel = (DefElem *) lfirst(cell);
|
||||||
break;
|
|
||||||
default:
|
|
||||||
|
|
||||||
/*
|
if (strcmp(defel->defname, "template") == 0)
|
||||||
* Right now we don't check any other utility commands, because it
|
{
|
||||||
* needs more detailed information to make access control decision
|
sepgsql_context_info.createdb_dtemplate
|
||||||
* here, but we don't want to have two parse and analyze routines
|
= strVal(defel->arg);
|
||||||
* individually.
|
break;
|
||||||
*/
|
}
|
||||||
break;
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case T_LoadStmt:
|
||||||
|
/*
|
||||||
|
* We reject LOAD command across the board on enforcing mode,
|
||||||
|
* because a binary module can arbitrarily override hooks.
|
||||||
|
*/
|
||||||
|
if (sepgsql_getenforce())
|
||||||
|
{
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||||
|
errmsg("SELinux: LOAD is not permitted")));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/*
|
||||||
|
* Right now we don't check any other utility commands,
|
||||||
|
* because it needs more detailed information to make access
|
||||||
|
* control decision here, but we don't want to have two parse
|
||||||
|
* and analyze routines individually.
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (next_ProcessUtility_hook)
|
||||||
|
(*next_ProcessUtility_hook) (parsetree, queryString, params,
|
||||||
|
isTopLevel, dest, completionTag);
|
||||||
|
else
|
||||||
|
standard_ProcessUtility(parsetree, queryString, params,
|
||||||
|
isTopLevel, dest, completionTag);
|
||||||
}
|
}
|
||||||
|
PG_CATCH();
|
||||||
/*
|
{
|
||||||
* Original implementation
|
sepgsql_context_info = saved_context_info;
|
||||||
*/
|
PG_RE_THROW();
|
||||||
standard_ProcessUtility(parsetree, queryString, params,
|
}
|
||||||
isTopLevel, dest, completionTag);
|
PG_END_TRY();
|
||||||
|
sepgsql_context_info = saved_context_info;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -456,4 +567,11 @@ _PG_init(void)
|
|||||||
/* ProcessUtility hook */
|
/* ProcessUtility hook */
|
||||||
next_ProcessUtility_hook = ProcessUtility_hook;
|
next_ProcessUtility_hook = ProcessUtility_hook;
|
||||||
ProcessUtility_hook = sepgsql_utility_command;
|
ProcessUtility_hook = sepgsql_utility_command;
|
||||||
|
|
||||||
|
/* ExecutorStart hook */
|
||||||
|
next_ExecutorStart_hook = ExecutorStart_hook;
|
||||||
|
ExecutorStart_hook = sepgsql_executor_start;
|
||||||
|
|
||||||
|
/* init contextual info */
|
||||||
|
memset(&sepgsql_context_info, 0, sizeof(sepgsql_context_info));
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
#include "catalog/pg_namespace.h"
|
#include "catalog/pg_namespace.h"
|
||||||
#include "catalog/pg_proc.h"
|
#include "catalog/pg_proc.h"
|
||||||
#include "commands/seclabel.h"
|
#include "commands/seclabel.h"
|
||||||
|
#include "utils/builtins.h"
|
||||||
#include "utils/fmgroids.h"
|
#include "utils/fmgroids.h"
|
||||||
#include "utils/lsyscache.h"
|
#include "utils/lsyscache.h"
|
||||||
#include "utils/tqual.h"
|
#include "utils/tqual.h"
|
||||||
@ -37,11 +38,13 @@ sepgsql_proc_post_create(Oid functionId)
|
|||||||
ScanKeyData skey;
|
ScanKeyData skey;
|
||||||
SysScanDesc sscan;
|
SysScanDesc sscan;
|
||||||
HeapTuple tuple;
|
HeapTuple tuple;
|
||||||
Oid namespaceId;
|
|
||||||
ObjectAddress object;
|
|
||||||
char *scontext;
|
char *scontext;
|
||||||
char *tcontext;
|
char *tcontext;
|
||||||
char *ncontext;
|
char *ncontext;
|
||||||
|
int i;
|
||||||
|
StringInfoData audit_name;
|
||||||
|
ObjectAddress object;
|
||||||
|
Form_pg_proc proForm;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Fetch namespace of the new procedure. Because pg_proc entry is not
|
* Fetch namespace of the new procedure. Because pg_proc entry is not
|
||||||
@ -61,20 +64,53 @@ sepgsql_proc_post_create(Oid functionId)
|
|||||||
if (!HeapTupleIsValid(tuple))
|
if (!HeapTupleIsValid(tuple))
|
||||||
elog(ERROR, "catalog lookup failed for proc %u", functionId);
|
elog(ERROR, "catalog lookup failed for proc %u", functionId);
|
||||||
|
|
||||||
namespaceId = ((Form_pg_proc) GETSTRUCT(tuple))->pronamespace;
|
proForm = (Form_pg_proc) GETSTRUCT(tuple);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* check db_schema:{add_name} permission of the namespace
|
||||||
|
*/
|
||||||
|
object.classId = NamespaceRelationId;
|
||||||
|
object.objectId = proForm->pronamespace;
|
||||||
|
object.objectSubId = 0;
|
||||||
|
sepgsql_avc_check_perms(&object,
|
||||||
|
SEPG_CLASS_DB_SCHEMA,
|
||||||
|
SEPG_DB_SCHEMA__ADD_NAME,
|
||||||
|
getObjectDescription(&object),
|
||||||
|
true);
|
||||||
|
/*
|
||||||
|
* XXX - db_language:{implement} also should be checked here
|
||||||
|
*/
|
||||||
|
|
||||||
systable_endscan(sscan);
|
|
||||||
heap_close(rel, AccessShareLock);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Compute a default security label when we create a new procedure object
|
* Compute a default security label when we create a new procedure object
|
||||||
* under the specified namespace.
|
* under the specified namespace.
|
||||||
*/
|
*/
|
||||||
scontext = sepgsql_get_client_label();
|
scontext = sepgsql_get_client_label();
|
||||||
tcontext = sepgsql_get_label(NamespaceRelationId, namespaceId, 0);
|
tcontext = sepgsql_get_label(NamespaceRelationId,
|
||||||
|
proForm->pronamespace, 0);
|
||||||
ncontext = sepgsql_compute_create(scontext, tcontext,
|
ncontext = sepgsql_compute_create(scontext, tcontext,
|
||||||
SEPG_CLASS_DB_PROCEDURE);
|
SEPG_CLASS_DB_PROCEDURE);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* check db_procedure:{create} permission
|
||||||
|
*/
|
||||||
|
initStringInfo(&audit_name);
|
||||||
|
appendStringInfo(&audit_name, "function %s(", NameStr(proForm->proname));
|
||||||
|
for (i=0; i < proForm->pronargs; i++)
|
||||||
|
{
|
||||||
|
Oid typeoid = proForm->proargtypes.values[i];
|
||||||
|
if (i > 0)
|
||||||
|
appendStringInfoChar(&audit_name, ',');
|
||||||
|
appendStringInfoString(&audit_name, format_type_be(typeoid));
|
||||||
|
}
|
||||||
|
appendStringInfoChar(&audit_name, ')');
|
||||||
|
|
||||||
|
sepgsql_avc_check_perms_label(ncontext,
|
||||||
|
SEPG_CLASS_DB_PROCEDURE,
|
||||||
|
SEPG_DB_PROCEDURE__CREATE,
|
||||||
|
audit_name.data,
|
||||||
|
true);
|
||||||
/*
|
/*
|
||||||
* Assign the default security label on a new procedure
|
* Assign the default security label on a new procedure
|
||||||
*/
|
*/
|
||||||
@ -83,6 +119,13 @@ sepgsql_proc_post_create(Oid functionId)
|
|||||||
object.objectSubId = 0;
|
object.objectSubId = 0;
|
||||||
SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, ncontext);
|
SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, ncontext);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Cleanup
|
||||||
|
*/
|
||||||
|
systable_endscan(sscan);
|
||||||
|
heap_close(rel, AccessShareLock);
|
||||||
|
|
||||||
|
pfree(audit_name.data);
|
||||||
pfree(tcontext);
|
pfree(tcontext);
|
||||||
pfree(ncontext);
|
pfree(ncontext);
|
||||||
}
|
}
|
||||||
|
@ -36,10 +36,16 @@
|
|||||||
void
|
void
|
||||||
sepgsql_attribute_post_create(Oid relOid, AttrNumber attnum)
|
sepgsql_attribute_post_create(Oid relOid, AttrNumber attnum)
|
||||||
{
|
{
|
||||||
char *scontext = sepgsql_get_client_label();
|
Relation rel;
|
||||||
|
ScanKeyData skey[2];
|
||||||
|
SysScanDesc sscan;
|
||||||
|
HeapTuple tuple;
|
||||||
|
char *scontext;
|
||||||
char *tcontext;
|
char *tcontext;
|
||||||
char *ncontext;
|
char *ncontext;
|
||||||
|
char audit_name[2*NAMEDATALEN + 20];
|
||||||
ObjectAddress object;
|
ObjectAddress object;
|
||||||
|
Form_pg_attribute attForm;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Only attributes within regular relation have individual security
|
* Only attributes within regular relation have individual security
|
||||||
@ -49,13 +55,44 @@ sepgsql_attribute_post_create(Oid relOid, AttrNumber attnum)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Compute a default security label when we create a new procedure object
|
* Compute a default security label of the new column underlying the
|
||||||
* under the specified namespace.
|
* specified relation, and check permission to create it.
|
||||||
*/
|
*/
|
||||||
|
rel = heap_open(AttributeRelationId, AccessShareLock);
|
||||||
|
|
||||||
|
ScanKeyInit(&skey[0],
|
||||||
|
Anum_pg_attribute_attrelid,
|
||||||
|
BTEqualStrategyNumber, F_OIDEQ,
|
||||||
|
ObjectIdGetDatum(relOid));
|
||||||
|
ScanKeyInit(&skey[1],
|
||||||
|
Anum_pg_attribute_attnum,
|
||||||
|
BTEqualStrategyNumber, F_INT2EQ,
|
||||||
|
Int16GetDatum(attnum));
|
||||||
|
|
||||||
|
sscan = systable_beginscan(rel, AttributeRelidNumIndexId, true,
|
||||||
|
SnapshotSelf, 2, &skey[0]);
|
||||||
|
|
||||||
|
tuple = systable_getnext(sscan);
|
||||||
|
if (!HeapTupleIsValid(tuple))
|
||||||
|
elog(ERROR, "catalog lookup failed for column %d of relation %u",
|
||||||
|
attnum, relOid);
|
||||||
|
|
||||||
|
attForm = (Form_pg_attribute) GETSTRUCT(tuple);
|
||||||
|
|
||||||
scontext = sepgsql_get_client_label();
|
scontext = sepgsql_get_client_label();
|
||||||
tcontext = sepgsql_get_label(RelationRelationId, relOid, 0);
|
tcontext = sepgsql_get_label(RelationRelationId, relOid, 0);
|
||||||
ncontext = sepgsql_compute_create(scontext, tcontext,
|
ncontext = sepgsql_compute_create(scontext, tcontext,
|
||||||
SEPG_CLASS_DB_COLUMN);
|
SEPG_CLASS_DB_COLUMN);
|
||||||
|
/*
|
||||||
|
* check db_column:{create} permission
|
||||||
|
*/
|
||||||
|
snprintf(audit_name, sizeof(audit_name), "table %s column %s",
|
||||||
|
get_rel_name(relOid), NameStr(attForm->attname));
|
||||||
|
sepgsql_avc_check_perms_label(ncontext,
|
||||||
|
SEPG_CLASS_DB_COLUMN,
|
||||||
|
SEPG_DB_COLUMN__CREATE,
|
||||||
|
audit_name,
|
||||||
|
true);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Assign the default security label on a new procedure
|
* Assign the default security label on a new procedure
|
||||||
@ -65,6 +102,9 @@ sepgsql_attribute_post_create(Oid relOid, AttrNumber attnum)
|
|||||||
object.objectSubId = attnum;
|
object.objectSubId = attnum;
|
||||||
SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, ncontext);
|
SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, ncontext);
|
||||||
|
|
||||||
|
systable_endscan(sscan);
|
||||||
|
heap_close(rel, AccessShareLock);
|
||||||
|
|
||||||
pfree(tcontext);
|
pfree(tcontext);
|
||||||
pfree(ncontext);
|
pfree(ncontext);
|
||||||
}
|
}
|
||||||
@ -127,10 +167,12 @@ sepgsql_relation_post_create(Oid relOid)
|
|||||||
Form_pg_class classForm;
|
Form_pg_class classForm;
|
||||||
ObjectAddress object;
|
ObjectAddress object;
|
||||||
uint16 tclass;
|
uint16 tclass;
|
||||||
|
const char *tclass_text;
|
||||||
char *scontext; /* subject */
|
char *scontext; /* subject */
|
||||||
char *tcontext; /* schema */
|
char *tcontext; /* schema */
|
||||||
char *rcontext; /* relation */
|
char *rcontext; /* relation */
|
||||||
char *ccontext; /* column */
|
char *ccontext; /* column */
|
||||||
|
char audit_name[2*NAMEDATALEN + 20];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Fetch catalog record of the new relation. Because pg_class entry is not
|
* Fetch catalog record of the new relation. Because pg_class entry is not
|
||||||
@ -152,15 +194,35 @@ sepgsql_relation_post_create(Oid relOid)
|
|||||||
|
|
||||||
classForm = (Form_pg_class) GETSTRUCT(tuple);
|
classForm = (Form_pg_class) GETSTRUCT(tuple);
|
||||||
|
|
||||||
if (classForm->relkind == RELKIND_RELATION)
|
switch (classForm->relkind)
|
||||||
tclass = SEPG_CLASS_DB_TABLE;
|
{
|
||||||
else if (classForm->relkind == RELKIND_SEQUENCE)
|
case RELKIND_RELATION:
|
||||||
tclass = SEPG_CLASS_DB_SEQUENCE;
|
tclass = SEPG_CLASS_DB_TABLE;
|
||||||
else if (classForm->relkind == RELKIND_VIEW)
|
tclass_text = "table";
|
||||||
tclass = SEPG_CLASS_DB_VIEW;
|
break;
|
||||||
else
|
case RELKIND_SEQUENCE:
|
||||||
goto out; /* No need to assign individual labels */
|
tclass = SEPG_CLASS_DB_SEQUENCE;
|
||||||
|
tclass_text = "sequence";
|
||||||
|
break;
|
||||||
|
case RELKIND_VIEW:
|
||||||
|
tclass = SEPG_CLASS_DB_VIEW;
|
||||||
|
tclass_text = "view";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* check db_schema:{add_name} permission of the namespace
|
||||||
|
*/
|
||||||
|
object.classId = NamespaceRelationId;
|
||||||
|
object.objectId = classForm->relnamespace;
|
||||||
|
object.objectSubId = 0;
|
||||||
|
sepgsql_avc_check_perms(&object,
|
||||||
|
SEPG_CLASS_DB_SCHEMA,
|
||||||
|
SEPG_DB_SCHEMA__ADD_NAME,
|
||||||
|
getObjectDescription(&object),
|
||||||
|
true);
|
||||||
/*
|
/*
|
||||||
* Compute a default security label when we create a new relation object
|
* Compute a default security label when we create a new relation object
|
||||||
* under the specified namespace.
|
* under the specified namespace.
|
||||||
@ -170,6 +232,16 @@ sepgsql_relation_post_create(Oid relOid)
|
|||||||
classForm->relnamespace, 0);
|
classForm->relnamespace, 0);
|
||||||
rcontext = sepgsql_compute_create(scontext, tcontext, tclass);
|
rcontext = sepgsql_compute_create(scontext, tcontext, tclass);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* check db_xxx:{create} permission
|
||||||
|
*/
|
||||||
|
snprintf(audit_name, sizeof(audit_name), "%s %s",
|
||||||
|
tclass_text, NameStr(classForm->relname));
|
||||||
|
sepgsql_avc_check_perms_label(rcontext,
|
||||||
|
tclass,
|
||||||
|
SEPG_DB_DATABASE__CREATE,
|
||||||
|
audit_name,
|
||||||
|
true);
|
||||||
/*
|
/*
|
||||||
* Assign the default security label on the new relation
|
* Assign the default security label on the new relation
|
||||||
*/
|
*/
|
||||||
@ -184,26 +256,52 @@ sepgsql_relation_post_create(Oid relOid)
|
|||||||
*/
|
*/
|
||||||
if (classForm->relkind == RELKIND_RELATION)
|
if (classForm->relkind == RELKIND_RELATION)
|
||||||
{
|
{
|
||||||
AttrNumber index;
|
Relation arel;
|
||||||
|
ScanKeyData akey;
|
||||||
|
SysScanDesc ascan;
|
||||||
|
HeapTuple atup;
|
||||||
|
Form_pg_attribute attForm;
|
||||||
|
|
||||||
ccontext = sepgsql_compute_create(scontext, rcontext,
|
arel = heap_open(AttributeRelationId, AccessShareLock);
|
||||||
SEPG_CLASS_DB_COLUMN);
|
|
||||||
for (index = FirstLowInvalidHeapAttributeNumber + 1;
|
ScanKeyInit(&akey,
|
||||||
index <= classForm->relnatts;
|
Anum_pg_attribute_attrelid,
|
||||||
index++)
|
BTEqualStrategyNumber, F_OIDEQ,
|
||||||
|
ObjectIdGetDatum(relOid));
|
||||||
|
|
||||||
|
ascan = systable_beginscan(arel, AttributeRelidNumIndexId, true,
|
||||||
|
SnapshotSelf, 1, &akey);
|
||||||
|
|
||||||
|
while (HeapTupleIsValid(atup = systable_getnext(ascan)))
|
||||||
{
|
{
|
||||||
if (index == InvalidAttrNumber)
|
attForm = (Form_pg_attribute) GETSTRUCT(atup);
|
||||||
continue;
|
|
||||||
|
|
||||||
if (index == ObjectIdAttributeNumber && !classForm->relhasoids)
|
snprintf(audit_name, sizeof(audit_name), "%s %s column %s",
|
||||||
continue;
|
tclass_text,
|
||||||
|
NameStr(classForm->relname),
|
||||||
|
NameStr(attForm->attname));
|
||||||
|
|
||||||
|
ccontext = sepgsql_compute_create(scontext,
|
||||||
|
rcontext,
|
||||||
|
SEPG_CLASS_DB_COLUMN);
|
||||||
|
/*
|
||||||
|
* check db_column:{create} permission
|
||||||
|
*/
|
||||||
|
sepgsql_avc_check_perms_label(ccontext,
|
||||||
|
SEPG_CLASS_DB_COLUMN,
|
||||||
|
SEPG_DB_COLUMN__CREATE,
|
||||||
|
audit_name,
|
||||||
|
true);
|
||||||
|
|
||||||
object.classId = RelationRelationId;
|
object.classId = RelationRelationId;
|
||||||
object.objectId = relOid;
|
object.objectId = relOid;
|
||||||
object.objectSubId = index;
|
object.objectSubId = attForm->attnum;
|
||||||
SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, ccontext);
|
SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, ccontext);
|
||||||
|
|
||||||
|
pfree(ccontext);
|
||||||
}
|
}
|
||||||
pfree(ccontext);
|
systable_endscan(ascan);
|
||||||
|
heap_close(arel, AccessShareLock);
|
||||||
}
|
}
|
||||||
pfree(rcontext);
|
pfree(rcontext);
|
||||||
out:
|
out:
|
||||||
|
@ -10,12 +10,18 @@
|
|||||||
*/
|
*/
|
||||||
#include "postgres.h"
|
#include "postgres.h"
|
||||||
|
|
||||||
|
#include "access/genam.h"
|
||||||
|
#include "access/heapam.h"
|
||||||
|
#include "access/sysattr.h"
|
||||||
#include "catalog/dependency.h"
|
#include "catalog/dependency.h"
|
||||||
|
#include "catalog/indexing.h"
|
||||||
#include "catalog/pg_database.h"
|
#include "catalog/pg_database.h"
|
||||||
#include "catalog/pg_namespace.h"
|
#include "catalog/pg_namespace.h"
|
||||||
#include "commands/seclabel.h"
|
#include "commands/seclabel.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
|
#include "utils/fmgroids.h"
|
||||||
#include "utils/lsyscache.h"
|
#include "utils/lsyscache.h"
|
||||||
|
#include "utils/tqual.h"
|
||||||
|
|
||||||
#include "sepgsql.h"
|
#include "sepgsql.h"
|
||||||
|
|
||||||
@ -28,19 +34,55 @@
|
|||||||
void
|
void
|
||||||
sepgsql_schema_post_create(Oid namespaceId)
|
sepgsql_schema_post_create(Oid namespaceId)
|
||||||
{
|
{
|
||||||
char *scontext;
|
Relation rel;
|
||||||
|
ScanKeyData skey;
|
||||||
|
SysScanDesc sscan;
|
||||||
|
HeapTuple tuple;
|
||||||
char *tcontext;
|
char *tcontext;
|
||||||
char *ncontext;
|
char *ncontext;
|
||||||
ObjectAddress object;
|
char audit_name[NAMEDATALEN + 20];
|
||||||
|
ObjectAddress object;
|
||||||
|
Form_pg_namespace nspForm;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Compute a default security label when we create a new schema object
|
* Compute a default security label when we create a new schema object
|
||||||
* under the working database.
|
* under the working database.
|
||||||
|
*
|
||||||
|
* XXX - uncoming version of libselinux supports to take object
|
||||||
|
* name to handle special treatment on default security label;
|
||||||
|
* such as special label on "pg_temp" schema.
|
||||||
*/
|
*/
|
||||||
scontext = sepgsql_get_client_label();
|
rel = heap_open(NamespaceRelationId, AccessShareLock);
|
||||||
|
|
||||||
|
ScanKeyInit(&skey,
|
||||||
|
ObjectIdAttributeNumber,
|
||||||
|
BTEqualStrategyNumber, F_OIDEQ,
|
||||||
|
ObjectIdGetDatum(namespaceId));
|
||||||
|
|
||||||
|
sscan = systable_beginscan(rel, NamespaceOidIndexId, true,
|
||||||
|
SnapshotSelf, 1, &skey);
|
||||||
|
tuple = systable_getnext(sscan);
|
||||||
|
if (!HeapTupleIsValid(tuple))
|
||||||
|
elog(ERROR, "catalog lookup failed for namespace %u", namespaceId);
|
||||||
|
|
||||||
|
nspForm = (Form_pg_namespace) GETSTRUCT(tuple);
|
||||||
|
|
||||||
tcontext = sepgsql_get_label(DatabaseRelationId, MyDatabaseId, 0);
|
tcontext = sepgsql_get_label(DatabaseRelationId, MyDatabaseId, 0);
|
||||||
ncontext = sepgsql_compute_create(scontext, tcontext,
|
ncontext = sepgsql_compute_create(sepgsql_get_client_label(),
|
||||||
|
tcontext,
|
||||||
SEPG_CLASS_DB_SCHEMA);
|
SEPG_CLASS_DB_SCHEMA);
|
||||||
|
/*
|
||||||
|
* check db_schema:{create}
|
||||||
|
*/
|
||||||
|
snprintf(audit_name, sizeof(audit_name),
|
||||||
|
"schema %s", NameStr(nspForm->nspname));
|
||||||
|
sepgsql_avc_check_perms_label(ncontext,
|
||||||
|
SEPG_CLASS_DB_SCHEMA,
|
||||||
|
SEPG_DB_SCHEMA__CREATE,
|
||||||
|
audit_name,
|
||||||
|
true);
|
||||||
|
systable_endscan(sscan);
|
||||||
|
heap_close(rel, AccessShareLock);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Assign the default security label on a new procedure
|
* Assign the default security label on a new procedure
|
||||||
|
@ -286,7 +286,8 @@ extern bool sepgsql_dml_privileges(List *rangeTabls, bool abort);
|
|||||||
/*
|
/*
|
||||||
* database.c
|
* database.c
|
||||||
*/
|
*/
|
||||||
extern void sepgsql_database_post_create(Oid databaseId);
|
extern void sepgsql_database_post_create(Oid databaseId,
|
||||||
|
const char *dtemplate);
|
||||||
extern void sepgsql_database_relabel(Oid databaseId, const char *seclabel);
|
extern void sepgsql_database_relabel(Oid databaseId, const char *seclabel);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
46
contrib/sepgsql/sql/create.sql
Normal file
46
contrib/sepgsql/sql/create.sql
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
--
|
||||||
|
-- Regression Test for Creation of Object Permission Checks
|
||||||
|
--
|
||||||
|
|
||||||
|
-- confirm required permissions using audit messages
|
||||||
|
-- @SECURITY-CONTEXT=unconfined_u:unconfined_r:unconfined_t:s0
|
||||||
|
SET sepgsql.debug_audit = true;
|
||||||
|
SET client_min_messages = LOG;
|
||||||
|
|
||||||
|
CREATE DATABASE regtest_sepgsql_test_database;
|
||||||
|
|
||||||
|
CREATE SCHEMA regtest_schema;
|
||||||
|
|
||||||
|
SET search_path = regtest_schema, public;
|
||||||
|
|
||||||
|
CREATE TABLE regtest_table (x serial primary key, y text);
|
||||||
|
|
||||||
|
ALTER TABLE regtest_table ADD COLUMN z int;
|
||||||
|
|
||||||
|
CREATE TABLE regtest_table_2 (a int) WITH OIDS;
|
||||||
|
|
||||||
|
-- corresponding toast table should not have label and permission checks
|
||||||
|
ALTER TABLE regtest_table_2 ADD COLUMN b text;
|
||||||
|
|
||||||
|
-- VACUUM FULL internally create a new table and swap them later.
|
||||||
|
VACUUM FULL regtest_table;
|
||||||
|
|
||||||
|
CREATE VIEW regtest_view AS SELECT * FROM regtest_table WHERE x < 100;
|
||||||
|
|
||||||
|
CREATE SEQUENCE regtest_seq;
|
||||||
|
|
||||||
|
CREATE TYPE regtest_comptype AS (a int, b text);
|
||||||
|
|
||||||
|
CREATE FUNCTION regtest_func(text,int[]) RETURNS bool LANGUAGE plpgsql
|
||||||
|
AS 'BEGIN RAISE NOTICE ''regtest_func => %'', $1; RETURN true; END';
|
||||||
|
|
||||||
|
CREATE AGGREGATE regtest_agg (
|
||||||
|
sfunc1 = int4pl, basetype = int4, stype1 = int4, initcond1 = '0'
|
||||||
|
);
|
||||||
|
|
||||||
|
--
|
||||||
|
-- clean-up
|
||||||
|
--
|
||||||
|
DROP DATABASE IF EXISTS regtest_sepgsql_test_database;
|
||||||
|
|
||||||
|
DROP SCHEMA IF EXISTS regtest_schema CASCADE;
|
@ -259,6 +259,6 @@ echo "found ${NUM}"
|
|||||||
echo
|
echo
|
||||||
echo "============== running sepgsql regression tests =============="
|
echo "============== running sepgsql regression tests =============="
|
||||||
|
|
||||||
make REGRESS="label dml misc" REGRESS_OPTS="--launcher ./launcher" installcheck
|
make REGRESS="label dml create misc" REGRESS_OPTS="--launcher ./launcher" installcheck
|
||||||
|
|
||||||
# exit with the exit code provided by "make"
|
# exit with the exit code provided by "make"
|
||||||
|
@ -420,6 +420,33 @@ UPDATE t1 SET x = 2, y = md5sum(y) WHERE z = 100;
|
|||||||
|
|
||||||
<sect3>
|
<sect3>
|
||||||
<title>DDL Permissions</title>
|
<title>DDL Permissions</title>
|
||||||
|
<para>
|
||||||
|
<productname>SELinux</> defines several permissions to control common
|
||||||
|
operations for each object types; such as creation, alter, drop and
|
||||||
|
relabel of security label. In addition, several object types has its
|
||||||
|
special permissions to control its characteristic operations; such as
|
||||||
|
addition or deletion of name entries underlying a particular schema.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
When <literal>CREATE</> command is executed, <literal>create</> will
|
||||||
|
be checked on the object being constructed for each object types.
|
||||||
|
A default security label shall be assigned on the new database object,
|
||||||
|
and the <literal>create</> permission needs to be allowed on the pair
|
||||||
|
of security label of the client and the new object itself.
|
||||||
|
We consider <xref linkend="sql-createtable"> construct a table and
|
||||||
|
underlying columns at the same time, so it requires users permission
|
||||||
|
to create both of table and columns.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
A few additional checks are applied depending on object types.
|
||||||
|
On <xref linkend="sql-createdatabase">, <literal>getattr</> permission
|
||||||
|
shall be checked on the source or template database of the new database,
|
||||||
|
not only <literal>create</> on the new database.
|
||||||
|
On creation of objects underlying a particula schema (tables, views,
|
||||||
|
sequences and procedures), <literal>add_name</> shall be also chechked
|
||||||
|
on the schema, not only <literal>create</> on the new object itself.
|
||||||
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
When <xref linkend="sql-security-label"> is executed, <literal>setattr</>
|
When <xref linkend="sql-security-label"> is executed, <literal>setattr</>
|
||||||
and <literal>relabelfrom</> will be checked on the object being relabeled
|
and <literal>relabelfrom</> will be checked on the object being relabeled
|
||||||
@ -509,7 +536,8 @@ postgres=# SELECT cid, cname, show_credit(cid) FROM customer;
|
|||||||
<term>Data Definition Language (DDL) Permissions</term>
|
<term>Data Definition Language (DDL) Permissions</term>
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
Due to implementation restrictions, DDL permissions are not checked.
|
Due to implementation restrictions, some of DDL permissions are not
|
||||||
|
checked.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
Reference in New Issue
Block a user