contrib/sepgsql/expected/create.out | 14 +++- contrib/sepgsql/hooks.c | 12 +++- contrib/sepgsql/proc.c | 144 +++++++++++++++++++++++++-------- contrib/sepgsql/sepgsql.h | 7 ++- contrib/sepgsql/sql/create.sql | 8 ++ src/backend/catalog/pg_aggregate.c | 6 +- src/backend/catalog/pg_proc.c | 7 +- src/backend/commands/aggregatecmds.c | 11 +++- src/backend/commands/functioncmds.c | 8 ++- src/backend/commands/proclang.c | 9 ++- src/backend/commands/typecmds.c | 3 +- src/include/catalog/objectaccess.h | 28 +++++++ src/include/catalog/pg_aggregate.h | 3 +- src/include/catalog/pg_proc_fn.h | 3 +- 14 files changed, 213 insertions(+), 50 deletions(-) diff --git a/contrib/sepgsql/expected/create.out b/contrib/sepgsql/expected/create.out index d450dc5..68d7e61 100644 --- a/contrib/sepgsql/expected/create.out +++ b/contrib/sepgsql/expected/create.out @@ -39,13 +39,25 @@ LOG: SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:unconfine 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_type AS (a int, b text); 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" +CREATE FUNCTION regtest_func (text) RETURNS bool LANGUAGE plpgsql + AS 'BEGIN RAISE NOTICE ''hello => %'', $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)" +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 4 other objects +NOTICE: drop cascades to 6 other objects DETAIL: drop cascades to table regtest_table drop cascades to view regtest_view drop cascades to sequence regtest_seq drop cascades to type regtest_type +drop cascades to function regtest_func(text) +drop cascades to function regtest_agg(integer) diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c index aa56d9c..1eeec1e 100644 --- a/contrib/sepgsql/hooks.c +++ b/contrib/sepgsql/hooks.c @@ -155,6 +155,15 @@ sepgsql_object_prep_create(Oid classId, Oid objectId, int subId, args->pg_class.tupdesc); break; + case ProcedureRelationId: + cinfo->ncontext = + sepgsql_proc_prep_create(args->pg_proc.proname, + args->pg_proc.proargs, + args->pg_proc.namespaceId, + args->pg_proc.languageId, + args->pg_proc.leakproof); + break; + default: /* Ignore unsupported object classes */ break; @@ -196,7 +205,8 @@ sepgsql_object_post_create(Oid classId, Oid objectId, int subId, break; case ProcedureRelationId: - sepgsql_proc_post_create(objectId); + sepgsql_proc_post_create(objectId, + !cinfo ? NULL : cinfo->ncontext); break; default: diff --git a/contrib/sepgsql/proc.c b/contrib/sepgsql/proc.c index 9630d45..c0e2c0e 100644 --- a/contrib/sepgsql/proc.c +++ b/contrib/sepgsql/proc.c @@ -18,6 +18,7 @@ #include "catalog/pg_namespace.h" #include "catalog/pg_proc.h" #include "commands/seclabel.h" +#include "utils/builtins.h" #include "utils/fmgroids.h" #include "utils/lsyscache.h" #include "utils/tqual.h" @@ -25,55 +26,131 @@ #include "sepgsql.h" /* - * sepgsql_proc_post_create + * sepgsql_proc_prep_create + * + * * - * This routine assigns a default security label on a newly defined - * procedure. */ -void -sepgsql_proc_post_create(Oid functionId) +const char * +sepgsql_proc_prep_create(const char *proname, + oidvector *proargs, + Oid namespaceId, + Oid languageId, + bool leakproof) { - Relation rel; - ScanKeyData skey; - SysScanDesc sscan; - HeapTuple tuple; - Oid namespaceId; - ObjectAddress object; char *scontext; char *tcontext; char *ncontext; + uint32 required; + StringInfoData audit_name; + ObjectAddress object; + int i; /* - * Fetch namespace of the new procedure. Because pg_proc entry is not - * visible right now, we need to scan the catalog using SnapshotSelf. + * check db_schema:{add_name} permission */ - rel = heap_open(ProcedureRelationId, AccessShareLock); + object.classId = NamespaceRelationId; + object.objectId = namespaceId; + object.objectSubId = 0; + sepgsql_avc_check_perms(&object, + SEPG_CLASS_DB_SCHEMA, + SEPG_DB_SCHEMA__ADD_NAME, + getObjectDescription(&object), + true); - ScanKeyInit(&skey, - ObjectIdAttributeNumber, - BTEqualStrategyNumber, F_OIDEQ, - ObjectIdGetDatum(functionId)); + /* + * compute default security label of new function + */ + scontext = sepgsql_get_client_label(); + tcontext = sepgsql_get_label(NamespaceRelationId, namespaceId, 0); + ncontext = sepgsql_compute_create(scontext, tcontext, + SEPG_CLASS_DB_PROCEDURE); - sscan = systable_beginscan(rel, ProcedureOidIndexId, true, - SnapshotSelf, 1, &skey); + /* + * check db_procedure:{create (install)} permission + */ + required = SEPG_DB_PROCEDURE__CREATE; + if (leakproof) + required |= SEPG_DB_PROCEDURE__INSTALL; - tuple = systable_getnext(sscan); - if (!HeapTupleIsValid(tuple)) - elog(ERROR, "catalog lookup failed for proc %u", functionId); + /* + * XXX - format_procedure() is not available at this point, + * so we need to construct human readable function names by hand. + */ + initStringInfo(&audit_name); + appendStringInfo(&audit_name, _("function %s("), + quote_identifier(proname)); + for (i = 0; i < proargs->dim1; i++) + { + if (i > 0) + appendStringInfoChar(&audit_name, ','); + appendStringInfoString(&audit_name, + format_type_be(proargs->values[i])); + } + appendStringInfoChar(&audit_name, ')'); + + sepgsql_avc_check_perms_label(ncontext, + SEPG_CLASS_DB_PROCEDURE, + required, + audit_name.data, + true); - namespaceId = ((Form_pg_proc) GETSTRUCT(tuple))->pronamespace; + /* + * XXX - db_language:{implement} also shoule be checked here. + */ + return ncontext; +} - systable_endscan(sscan); - heap_close(rel, AccessShareLock); +/* + * sepgsql_proc_post_create + * + * This routine assigns a default security label on a newly defined + * procedure. + */ +void +sepgsql_proc_post_create(Oid functionId, const char *ncontext) +{ + ObjectAddress object; /* - * Compute a default security label when we create a new procedure object - * under the specified namespace. + * In the case when system internal functions are constructed, + * prep-creation hook is not invoked, security label to be + * assigned on is not also computed yet. Thus, we try to obtain + * the default security label based on the properties of system + * catalogs. */ - scontext = sepgsql_get_client_label(); - tcontext = sepgsql_get_label(NamespaceRelationId, namespaceId, 0); - ncontext = sepgsql_compute_create(scontext, tcontext, - SEPG_CLASS_DB_PROCEDURE); + if (!ncontext) + { + Relation rel; + ScanKeyData skey; + SysScanDesc sscan; + HeapTuple tuple; + Form_pg_proc procForm; + + rel = heap_open(ProcedureRelationId, AccessShareLock); + + ScanKeyInit(&skey, + ObjectIdAttributeNumber, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(functionId)); + + sscan = systable_beginscan(rel, ProcedureOidIndexId, true, + SnapshotSelf, 1, &skey); + + tuple = systable_getnext(sscan); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "catalog lookup failed for proc %u", functionId); + + procForm = (Form_pg_proc) GETSTRUCT(tuple); + + ncontext = sepgsql_proc_prep_create(NameStr(procForm->proname), + &procForm->proargtypes, + procForm->pronamespace, + procForm->prolang, + false); + systable_endscan(sscan); + heap_close(rel, AccessShareLock); + } /* * Assign the default security label on a new procedure @@ -82,9 +159,6 @@ sepgsql_proc_post_create(Oid functionId) object.objectId = functionId; object.objectSubId = 0; SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, ncontext); - - pfree(tcontext); - pfree(ncontext); } /* diff --git a/contrib/sepgsql/sepgsql.h b/contrib/sepgsql/sepgsql.h index cd9c4c6..56d2056 100644 --- a/contrib/sepgsql/sepgsql.h +++ b/contrib/sepgsql/sepgsql.h @@ -338,7 +338,12 @@ extern void sepgsql_relation_relabel(Oid relOid, const char *seclabel); /* * proc.c */ -extern void sepgsql_proc_post_create(Oid functionId); +extern const char *sepgsql_proc_prep_create(const char *proname, + oidvector *proargs, + Oid namespaceId, + Oid languageId, + bool leakproof); +extern void sepgsql_proc_post_create(Oid functionId, const char *ncontext); extern void sepgsql_proc_relabel(Oid functionId, const char *seclabel); #endif /* SEPGSQL_H */ diff --git a/contrib/sepgsql/sql/create.sql b/contrib/sepgsql/sql/create.sql index 4e12411..bc4a664 100644 --- a/contrib/sepgsql/sql/create.sql +++ b/contrib/sepgsql/sql/create.sql @@ -21,6 +21,14 @@ CREATE SEQUENCE regtest_seq; CREATE TYPE regtest_type AS (a int, b text); +CREATE FUNCTION regtest_func (text) RETURNS bool LANGUAGE plpgsql + AS 'BEGIN RAISE NOTICE ''hello => %'', $1; RETURN true; END'; + +CREATE AGGREGATE regtest_agg ( + sfunc1 = int4pl, basetype = int4, stype1 = int4, + initcond1 = '0' +); + -- -- clean-up -- diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c index 86e8c6b..0cd0ca3 100644 --- a/src/backend/catalog/pg_aggregate.c +++ b/src/backend/catalog/pg_aggregate.c @@ -50,7 +50,8 @@ AggregateCreate(const char *aggName, List *aggfinalfnName, List *aggsortopName, Oid aggTransType, - const char *agginitval) + const char *agginitval, + Datum hook_private) { Relation aggdesc; HeapTuple tup; @@ -229,7 +230,8 @@ AggregateCreate(const char *aggName, NIL, /* parameterDefaults */ PointerGetDatum(NULL), /* proconfig */ 1, /* procost */ - 0); /* prorows */ + 0, /* prorows */ + hook_private); /* hook_private */ /* * Okay to create the pg_aggregate entry. diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c index 8378c36..7fb5ef2 100644 --- a/src/backend/catalog/pg_proc.c +++ b/src/backend/catalog/pg_proc.c @@ -85,7 +85,8 @@ ProcedureCreate(const char *procedureName, List *parameterDefaults, Datum proconfig, float4 procost, - float4 prorows) + float4 prorows, + Datum hook_private) { Oid retval; int parameterCount; @@ -646,7 +647,9 @@ ProcedureCreate(const char *procedureName, heap_freetuple(tup); /* Post creation hook for new function */ - InvokeObjectAccessHook(OAT_POST_CREATE, ProcedureRelationId, retval, 0); + InvokeObjectAccessHookArg(OAT_POST_CREATE, + ProcedureRelationId, retval, 0, + hook_private); heap_close(rel, RowExclusiveLock); diff --git a/src/backend/commands/aggregatecmds.c b/src/backend/commands/aggregatecmds.c index a2122c1..f02a467 100644 --- a/src/backend/commands/aggregatecmds.c +++ b/src/backend/commands/aggregatecmds.c @@ -25,7 +25,9 @@ #include "access/heapam.h" #include "catalog/dependency.h" #include "catalog/indexing.h" +#include "catalog/objectaccess.h" #include "catalog/pg_aggregate.h" +#include "catalog/pg_language.h" #include "catalog/pg_proc.h" #include "catalog/pg_type.h" #include "commands/defrem.h" @@ -60,6 +62,7 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters) Oid *aggArgTypes; int numArgs; Oid transTypeId; + Datum hook_private = 0; ListCell *pl; /* Convert list of names to a name and namespace */ @@ -192,6 +195,11 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters) format_type_be(transTypeId)))); } + /* Prep-creation hook for new aggregate functions */ + InvokePrepCreateProcedureHook(&hook_private, aggName, + buildoidvector(aggArgTypes, numArgs), + aggNamespace, INTERNALlanguageId, false); + /* * Most of the argument-checking is done inside of AggregateCreate */ @@ -203,7 +211,8 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters) finalfuncName, /* final function name */ sortoperatorName, /* sort operator name */ transTypeId, /* transition data type */ - initval); /* initial condition */ + initval, /* initial condition */ + hook_private); } diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c index 92abd44..33c65be 100644 --- a/src/backend/commands/functioncmds.c +++ b/src/backend/commands/functioncmds.c @@ -801,6 +801,7 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString) HeapTuple languageTuple; Form_pg_language languageStruct; List *as_clause; + Datum hook_private = 0; /* Convert list of names to a name and namespace */ namespaceId = QualifiedNameGetCreationNamespace(stmt->funcname, @@ -935,6 +936,10 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString) (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("ROWS is not applicable when function does not return a set"))); + /* Prep-creation hook for new function */ + InvokePrepCreateProcedureHook(&hook_private, funcname, parameterTypes, + namespaceId, languageOid, false); + /* * And now that we have all the parameters, and know we're permitted to do * so, go ahead and create the function. @@ -960,7 +965,8 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString) parameterDefaults, PointerGetDatum(proconfig), procost, - prorows); + prorows, + hook_private); } diff --git a/src/backend/commands/proclang.c b/src/backend/commands/proclang.c index 98770c5..1392dca 100644 --- a/src/backend/commands/proclang.c +++ b/src/backend/commands/proclang.c @@ -146,7 +146,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt) NIL, PointerGetDatum(NULL), 1, - 0); + 0, + (Datum) 0); } /* @@ -181,7 +182,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt) NIL, PointerGetDatum(NULL), 1, - 0); + 0, + (Datum) 0); } } else @@ -219,7 +221,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt) NIL, PointerGetDatum(NULL), 1, - 0); + 0, + (Datum) 0); } } else diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index 91488bb..307dcbf 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -1461,7 +1461,8 @@ makeRangeConstructor(char *name, Oid namespace, Oid rangeOid, Oid subtype) NIL, /* parameterDefaults */ PointerGetDatum(NULL), /* proconfig */ 1.0, /* procost */ - 0.0); /* prorows */ + 0.0, /* prorows */ + (Datum) 0); /* hook_private */ /* * Make the constructor internally-dependent on the range type so that diff --git a/src/include/catalog/objectaccess.h b/src/include/catalog/objectaccess.h index d151ee5..db57e03 100644 --- a/src/include/catalog/objectaccess.h +++ b/src/include/catalog/objectaccess.h @@ -65,6 +65,14 @@ typedef union Oid tablespaceId; TupleDesc tupdesc; } pg_class; + struct { + Datum *private; + const char *proname; + oidvector *proargs; + Oid namespaceId; + Oid languageId; + bool leakproof; + } pg_proc; } ObjectAccessCreateObjectArgs; /* @@ -151,4 +159,24 @@ extern PGDLLIMPORT object_access_hook_type object_access_hook; } \ } while(0) +#define InvokePrepCreateProcedureHook(_private,_proname,_proargs, \ + _namespace,_language,_leakproof) \ + do { \ + if (object_access_hook) \ + { \ + ObjectAccessCreateObjectArgs __args; \ + \ + __args.pg_proc.private = (_private); \ + __args.pg_proc.proname = (_proname); \ + __args.pg_proc.proargs = (_proargs); \ + __args.pg_proc.namespaceId = (_namespace); \ + __args.pg_proc.languageId = (_language); \ + __args.pg_proc.leakproof = (_leakproof); \ + \ + (*object_access_hook)(OAT_PREP_CREATE, \ + ProcedureRelationId, InvalidOid, 0, \ + PointerGetDatum(&__args)); \ + } \ + } while(0) + #endif /* OBJECTACCESS_H */ diff --git a/src/include/catalog/pg_aggregate.h b/src/include/catalog/pg_aggregate.h index 26966d2..7b2d172 100644 --- a/src/include/catalog/pg_aggregate.h +++ b/src/include/catalog/pg_aggregate.h @@ -237,6 +237,7 @@ extern void AggregateCreate(const char *aggName, List *aggfinalfnName, List *aggsortopName, Oid aggTransType, - const char *agginitval); + const char *agginitval, + Datum hook_private); #endif /* PG_AGGREGATE_H */ diff --git a/src/include/catalog/pg_proc_fn.h b/src/include/catalog/pg_proc_fn.h index 09d779f..a28786f 100644 --- a/src/include/catalog/pg_proc_fn.h +++ b/src/include/catalog/pg_proc_fn.h @@ -37,7 +37,8 @@ extern Oid ProcedureCreate(const char *procedureName, List *parameterDefaults, Datum proconfig, float4 procost, - float4 prorows); + float4 prorows, + Datum hook_private); extern bool function_parse_error_transpose(const char *prosrc);