mirror of
https://git.postgresql.org/git/postgresql.git
synced 2025-02-05 19:09:58 +08:00
When using the following functions, users could see various types of errors of the type "cache lookup failed for OID XXX" with elog(), that can only be used for internal errors: * pg_describe_object() * pg_identify_object() * pg_identify_object_as_address() The set of APIs managing object addresses for all object types are made smarter by gaining a new argument "missing_ok" that allows any caller to control if an error is raised or not on an undefined object. The SQL functions listed above are changed to handle the case where an object is missing. Regression tests are added for all object types for the cases where these are undefined. Before this commit, these cases failed with cache lookup errors, and now they basically return NULL (minus the name of the object type requested). Author: Michael Paquier Reviewed-by: Aleksander Alekseev, Dmitry Dolgov, Daniel Gustafsson, Álvaro Herrera, Kyotaro Horiguchi Discussion: https://postgr.es/m/CAB7nPqSZxrSmdHK-rny7z8mi=EAFXJ5J-0RbzDw6aus=wB5azQ@mail.gmail.com
335 lines
7.9 KiB
C
335 lines
7.9 KiB
C
/* -------------------------------------------------------------------------
|
|
*
|
|
* contrib/sepgsql/proc.c
|
|
*
|
|
* Routines corresponding to procedure objects
|
|
*
|
|
* Copyright (c) 2010-2020, PostgreSQL Global Development Group
|
|
*
|
|
* -------------------------------------------------------------------------
|
|
*/
|
|
#include "postgres.h"
|
|
|
|
#include "access/genam.h"
|
|
#include "access/htup_details.h"
|
|
#include "access/sysattr.h"
|
|
#include "access/table.h"
|
|
#include "catalog/dependency.h"
|
|
#include "catalog/indexing.h"
|
|
#include "catalog/pg_namespace.h"
|
|
#include "catalog/pg_proc.h"
|
|
#include "catalog/pg_type.h"
|
|
#include "commands/seclabel.h"
|
|
#include "lib/stringinfo.h"
|
|
#include "sepgsql.h"
|
|
#include "utils/builtins.h"
|
|
#include "utils/fmgroids.h"
|
|
#include "utils/lsyscache.h"
|
|
#include "utils/snapmgr.h"
|
|
#include "utils/syscache.h"
|
|
|
|
/*
|
|
* sepgsql_proc_post_create
|
|
*
|
|
* This routine assigns a default security label on a newly defined
|
|
* procedure.
|
|
*/
|
|
void
|
|
sepgsql_proc_post_create(Oid functionId)
|
|
{
|
|
Relation rel;
|
|
ScanKeyData skey;
|
|
SysScanDesc sscan;
|
|
HeapTuple tuple;
|
|
char *nsp_name;
|
|
char *scontext;
|
|
char *tcontext;
|
|
char *ncontext;
|
|
uint32 required;
|
|
int i;
|
|
StringInfoData audit_name;
|
|
ObjectAddress object;
|
|
Form_pg_proc proForm;
|
|
|
|
/*
|
|
* Fetch namespace of the new procedure. Because pg_proc entry is not
|
|
* visible right now, we need to scan the catalog using SnapshotSelf.
|
|
*/
|
|
rel = table_open(ProcedureRelationId, AccessShareLock);
|
|
|
|
ScanKeyInit(&skey,
|
|
Anum_pg_proc_oid,
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
ObjectIdGetDatum(functionId));
|
|
|
|
sscan = systable_beginscan(rel, ProcedureOidIndexId, true,
|
|
SnapshotSelf, 1, &skey);
|
|
|
|
tuple = systable_getnext(sscan);
|
|
if (!HeapTupleIsValid(tuple))
|
|
elog(ERROR, "could not find tuple for function %u", functionId);
|
|
|
|
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,
|
|
getObjectIdentity(&object, false),
|
|
true);
|
|
|
|
/*
|
|
* XXX - db_language:{implement} also should be checked here
|
|
*/
|
|
|
|
|
|
/*
|
|
* Compute a default security label when we create a new procedure object
|
|
* under the specified namespace.
|
|
*/
|
|
scontext = sepgsql_get_client_label();
|
|
tcontext = sepgsql_get_label(NamespaceRelationId,
|
|
proForm->pronamespace, 0);
|
|
ncontext = sepgsql_compute_create(scontext, tcontext,
|
|
SEPG_CLASS_DB_PROCEDURE,
|
|
NameStr(proForm->proname));
|
|
|
|
/*
|
|
* check db_procedure:{create (install)} permission
|
|
*/
|
|
initStringInfo(&audit_name);
|
|
nsp_name = get_namespace_name(proForm->pronamespace);
|
|
appendStringInfo(&audit_name, "%s(",
|
|
quote_qualified_identifier(nsp_name, NameStr(proForm->proname)));
|
|
for (i = 0; i < proForm->pronargs; i++)
|
|
{
|
|
if (i > 0)
|
|
appendStringInfoChar(&audit_name, ',');
|
|
|
|
object.classId = TypeRelationId;
|
|
object.objectId = proForm->proargtypes.values[i];
|
|
object.objectSubId = 0;
|
|
appendStringInfoString(&audit_name, getObjectIdentity(&object, false));
|
|
}
|
|
appendStringInfoChar(&audit_name, ')');
|
|
|
|
required = SEPG_DB_PROCEDURE__CREATE;
|
|
if (proForm->proleakproof)
|
|
required |= SEPG_DB_PROCEDURE__INSTALL;
|
|
|
|
sepgsql_avc_check_perms_label(ncontext,
|
|
SEPG_CLASS_DB_PROCEDURE,
|
|
required,
|
|
audit_name.data,
|
|
true);
|
|
|
|
/*
|
|
* Assign the default security label on a new procedure
|
|
*/
|
|
object.classId = ProcedureRelationId;
|
|
object.objectId = functionId;
|
|
object.objectSubId = 0;
|
|
SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, ncontext);
|
|
|
|
/*
|
|
* Cleanup
|
|
*/
|
|
systable_endscan(sscan);
|
|
table_close(rel, AccessShareLock);
|
|
|
|
pfree(audit_name.data);
|
|
pfree(tcontext);
|
|
pfree(ncontext);
|
|
}
|
|
|
|
/*
|
|
* sepgsql_proc_drop
|
|
*
|
|
* It checks privileges to drop the supplied function.
|
|
*/
|
|
void
|
|
sepgsql_proc_drop(Oid functionId)
|
|
{
|
|
ObjectAddress object;
|
|
char *audit_name;
|
|
|
|
/*
|
|
* check db_schema:{remove_name} permission
|
|
*/
|
|
object.classId = NamespaceRelationId;
|
|
object.objectId = get_func_namespace(functionId);
|
|
object.objectSubId = 0;
|
|
audit_name = getObjectIdentity(&object, false);
|
|
|
|
sepgsql_avc_check_perms(&object,
|
|
SEPG_CLASS_DB_SCHEMA,
|
|
SEPG_DB_SCHEMA__REMOVE_NAME,
|
|
audit_name,
|
|
true);
|
|
pfree(audit_name);
|
|
|
|
/*
|
|
* check db_procedure:{drop} permission
|
|
*/
|
|
object.classId = ProcedureRelationId;
|
|
object.objectId = functionId;
|
|
object.objectSubId = 0;
|
|
audit_name = getObjectIdentity(&object, false);
|
|
|
|
sepgsql_avc_check_perms(&object,
|
|
SEPG_CLASS_DB_PROCEDURE,
|
|
SEPG_DB_PROCEDURE__DROP,
|
|
audit_name,
|
|
true);
|
|
pfree(audit_name);
|
|
}
|
|
|
|
/*
|
|
* sepgsql_proc_relabel
|
|
*
|
|
* It checks privileges to relabel the supplied function
|
|
* by the `seclabel'.
|
|
*/
|
|
void
|
|
sepgsql_proc_relabel(Oid functionId, const char *seclabel)
|
|
{
|
|
ObjectAddress object;
|
|
char *audit_name;
|
|
|
|
object.classId = ProcedureRelationId;
|
|
object.objectId = functionId;
|
|
object.objectSubId = 0;
|
|
audit_name = getObjectIdentity(&object, false);
|
|
|
|
/*
|
|
* check db_procedure:{setattr relabelfrom} permission
|
|
*/
|
|
sepgsql_avc_check_perms(&object,
|
|
SEPG_CLASS_DB_PROCEDURE,
|
|
SEPG_DB_PROCEDURE__SETATTR |
|
|
SEPG_DB_PROCEDURE__RELABELFROM,
|
|
audit_name,
|
|
true);
|
|
|
|
/*
|
|
* check db_procedure:{relabelto} permission
|
|
*/
|
|
sepgsql_avc_check_perms_label(seclabel,
|
|
SEPG_CLASS_DB_PROCEDURE,
|
|
SEPG_DB_PROCEDURE__RELABELTO,
|
|
audit_name,
|
|
true);
|
|
pfree(audit_name);
|
|
}
|
|
|
|
/*
|
|
* sepgsql_proc_setattr
|
|
*
|
|
* It checks privileges to alter the supplied function.
|
|
*/
|
|
void
|
|
sepgsql_proc_setattr(Oid functionId)
|
|
{
|
|
Relation rel;
|
|
ScanKeyData skey;
|
|
SysScanDesc sscan;
|
|
HeapTuple oldtup;
|
|
HeapTuple newtup;
|
|
Form_pg_proc oldform;
|
|
Form_pg_proc newform;
|
|
uint32 required;
|
|
ObjectAddress object;
|
|
char *audit_name;
|
|
|
|
/*
|
|
* Fetch newer catalog
|
|
*/
|
|
rel = table_open(ProcedureRelationId, AccessShareLock);
|
|
|
|
ScanKeyInit(&skey,
|
|
Anum_pg_proc_oid,
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
ObjectIdGetDatum(functionId));
|
|
|
|
sscan = systable_beginscan(rel, ProcedureOidIndexId, true,
|
|
SnapshotSelf, 1, &skey);
|
|
newtup = systable_getnext(sscan);
|
|
if (!HeapTupleIsValid(newtup))
|
|
elog(ERROR, "could not find tuple for function %u", functionId);
|
|
newform = (Form_pg_proc) GETSTRUCT(newtup);
|
|
|
|
/*
|
|
* Fetch older catalog
|
|
*/
|
|
oldtup = SearchSysCache1(PROCOID, ObjectIdGetDatum(functionId));
|
|
if (!HeapTupleIsValid(oldtup))
|
|
elog(ERROR, "cache lookup failed for function %u", functionId);
|
|
oldform = (Form_pg_proc) GETSTRUCT(oldtup);
|
|
|
|
/*
|
|
* Does this ALTER command takes operation to namespace?
|
|
*/
|
|
if (newform->pronamespace != oldform->pronamespace)
|
|
{
|
|
sepgsql_schema_remove_name(oldform->pronamespace);
|
|
sepgsql_schema_add_name(oldform->pronamespace);
|
|
}
|
|
if (strcmp(NameStr(newform->proname), NameStr(oldform->proname)) != 0)
|
|
sepgsql_schema_rename(oldform->pronamespace);
|
|
|
|
/*
|
|
* check db_procedure:{setattr (install)} permission
|
|
*/
|
|
required = SEPG_DB_PROCEDURE__SETATTR;
|
|
if (!oldform->proleakproof && newform->proleakproof)
|
|
required |= SEPG_DB_PROCEDURE__INSTALL;
|
|
|
|
object.classId = ProcedureRelationId;
|
|
object.objectId = functionId;
|
|
object.objectSubId = 0;
|
|
audit_name = getObjectIdentity(&object, false);
|
|
|
|
sepgsql_avc_check_perms(&object,
|
|
SEPG_CLASS_DB_PROCEDURE,
|
|
required,
|
|
audit_name,
|
|
true);
|
|
/* cleanups */
|
|
pfree(audit_name);
|
|
|
|
ReleaseSysCache(oldtup);
|
|
systable_endscan(sscan);
|
|
table_close(rel, AccessShareLock);
|
|
}
|
|
|
|
/*
|
|
* sepgsql_proc_execute
|
|
*
|
|
* It checks privileges to execute the supplied function
|
|
*/
|
|
void
|
|
sepgsql_proc_execute(Oid functionId)
|
|
{
|
|
ObjectAddress object;
|
|
char *audit_name;
|
|
|
|
/*
|
|
* check db_procedure:{execute} permission
|
|
*/
|
|
object.classId = ProcedureRelationId;
|
|
object.objectId = functionId;
|
|
object.objectSubId = 0;
|
|
audit_name = getObjectIdentity(&object, false);
|
|
sepgsql_avc_check_perms(&object,
|
|
SEPG_CLASS_DB_PROCEDURE,
|
|
SEPG_DB_PROCEDURE__EXECUTE,
|
|
audit_name,
|
|
true);
|
|
pfree(audit_name);
|
|
}
|