/* * 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 = getObjectDescription(&object); /* * 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_schema_relabel * * It checks privileges to relabel the supplied schema * by the `seclabel'. */ void sepgsql_schema_relabel(Oid namespaceId, const char *seclabel) { ObjectAddress object; char *audit_name; object.classId = NamespaceRelationId; object.objectId = namespaceId; object.objectSubId = 0; audit_name = getObjectDescription(&object); /* * check db_schema:{setattr relabelfrom} permission */ sepgsql_avc_check_perms(&object, SEPG_CLASS_DB_SCHEMA, SEPG_DB_SCHEMA__SETATTR | SEPG_DB_SCHEMA__RELABELFROM, audit_name, true); /* * check db_schema:{relabelto} permission */ sepgsql_avc_check_perms_label(seclabel, SEPG_CLASS_DB_SCHEMA, SEPG_DB_SCHEMA__RELABELTO, audit_name, true); pfree(audit_name); }
/* * sepgsql_database_relabel * * It checks privileges to relabel the supplied database with the `seclabel' */ void sepgsql_database_relabel(Oid databaseId, const char *seclabel) { ObjectAddress object; char *audit_name; object.classId = DatabaseRelationId; object.objectId = databaseId; object.objectSubId = 0; audit_name = getObjectDescription(&object); /* * check db_database:{setattr relabelfrom} permission */ sepgsql_avc_check_perms(&object, SEPG_CLASS_DB_DATABASE, SEPG_DB_DATABASE__SETATTR | SEPG_DB_DATABASE__RELABELFROM, audit_name, true); /* * check db_database:{relabelto} permission */ sepgsql_avc_check_perms_label(seclabel, SEPG_CLASS_DB_DATABASE, SEPG_DB_DATABASE__RELABELTO, audit_name, true); pfree(audit_name); }
/* * sepgsql_relation_setattr * * It checks privileges to set attribute of the supplied relation */ void sepgsql_relation_setattr(Oid relOid) { ObjectAddress object; char *audit_name; uint16_t tclass; switch (get_rel_relkind(relOid)) { case RELKIND_RELATION: tclass = SEPG_CLASS_DB_TABLE; break; case RELKIND_SEQUENCE: tclass = SEPG_CLASS_DB_SEQUENCE; break; case RELKIND_VIEW: tclass = SEPG_CLASS_DB_VIEW; break; case RELKIND_INDEX: /* deal with indexes specially */ sepgsql_index_modify(relOid); return; default: /* other relkinds don't need additional work */ return; } object.classId = RelationRelationId; object.objectId = relOid; object.objectSubId = 0; audit_name = getObjectDescription(&object); /* * XXX - we should add checks related to namespace stuff, when * object_access_hook get support for ALTER statement. Right now, there is * no invocation path on ALTER ... RENAME TO / SET SCHEMA. */ /* * check db_xxx:{setattr} permission */ sepgsql_avc_check_perms(&object, tclass, SEPG_DB_TABLE__SETATTR, audit_name, true); pfree(audit_name); }
/* * sepgsql_relation_relabel * * It checks privileges to relabel the supplied relation by the `seclabel'. */ void sepgsql_relation_relabel(Oid relOid, const char *seclabel) { ObjectAddress object; char *audit_name; char relkind; uint16_t tclass = 0; relkind = get_rel_relkind(relOid); if (relkind == RELKIND_RELATION) tclass = SEPG_CLASS_DB_TABLE; else if (relkind == RELKIND_SEQUENCE) tclass = SEPG_CLASS_DB_SEQUENCE; else if (relkind == RELKIND_VIEW) tclass = SEPG_CLASS_DB_VIEW; else ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("cannot set security labels on relations except " "for tables, sequences or views"))); object.classId = RelationRelationId; object.objectId = relOid; object.objectSubId = 0; audit_name = getObjectDescription(&object); /* * check db_xxx:{setattr relabelfrom} permission */ sepgsql_avc_check_perms(&object, tclass, SEPG_DB_TABLE__SETATTR | SEPG_DB_TABLE__RELABELFROM, audit_name, true); /* * check db_xxx:{relabelto} permission */ sepgsql_avc_check_perms_label(seclabel, tclass, SEPG_DB_TABLE__RELABELTO, audit_name, true); pfree(audit_name); }
/* * sepgsql_attribute_relabel * * It checks privileges to relabel the supplied column * by the `seclabel'. */ void sepgsql_attribute_relabel(Oid relOid, AttrNumber attnum, const char *seclabel) { char *scontext = sepgsql_get_client_label(); char *tcontext; char *audit_name; ObjectAddress object; if (get_rel_relkind(relOid) != RELKIND_RELATION) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("cannot set security label on non-regular columns"))); object.classId = RelationRelationId; object.objectId = relOid; object.objectSubId = attnum; audit_name = getObjectDescription(&object); /* * check db_column:{setattr relabelfrom} permission */ tcontext = sepgsql_get_label(RelationRelationId, relOid, attnum); sepgsql_check_perms(scontext, tcontext, SEPG_CLASS_DB_COLUMN, SEPG_DB_COLUMN__SETATTR | SEPG_DB_COLUMN__RELABELFROM, audit_name, true); /* * check db_column:{relabelto} permission */ sepgsql_check_perms(scontext, seclabel, SEPG_CLASS_DB_COLUMN, SEPG_DB_PROCEDURE__RELABELTO, audit_name, true); pfree(tcontext); pfree(audit_name); }
/* * sepgsql_database_drop * * It checks privileges to drop the supplied database */ void sepgsql_database_drop(Oid databaseId) { ObjectAddress object; char *audit_name; /* * check db_database:{drop} permission */ object.classId = DatabaseRelationId; object.objectId = databaseId; object.objectSubId = 0; audit_name = getObjectDescription(&object); sepgsql_avc_check_perms(&object, SEPG_CLASS_DB_DATABASE, SEPG_DB_DATABASE__DROP, audit_name, true); pfree(audit_name); }
/* * sepgsql_schema_drop * * It checks privileges to drop the supplied schema object. */ void sepgsql_schema_drop(Oid namespaceId) { ObjectAddress object; char *audit_name; /* * check db_schema:{drop} permission */ object.classId = NamespaceRelationId; object.objectId = namespaceId; object.objectSubId = 0; audit_name = getObjectDescription(&object); sepgsql_avc_check_perms(&object, SEPG_CLASS_DB_SCHEMA, SEPG_DB_SCHEMA__DROP, audit_name, true); pfree(audit_name); }
/* * If we are executing a CREATE EXTENSION operation, mark the given object * as being a member of the extension. Otherwise, do nothing. * * This must be called during creation of any user-definable object type * that could be a member of an extension. * * If isReplace is true, the object already existed (or might have already * existed), so we must check for a pre-existing extension membership entry. * Passing false is a guarantee that the object is newly created, and so * could not already be a member of any extension. */ void recordDependencyOnCurrentExtension(const ObjectAddress *object, bool isReplace) { /* Only whole objects can be extension members */ Assert(object->objectSubId == 0); if (creating_extension) { ObjectAddress extension; /* Only need to check for existing membership if isReplace */ if (isReplace) { Oid oldext; oldext = getExtensionOfObject(object->classId, object->objectId); if (OidIsValid(oldext)) { /* If already a member of this extension, nothing to do */ if (oldext == CurrentExtensionObject) return; /* Already a member of some other extension, so reject */ ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("%s is already a member of extension \"%s\"", getObjectDescription(object), get_extension_name(oldext)))); } } /* OK, record it as a member of CurrentExtensionObject */ extension.classId = ExtensionRelationId; extension.objectId = CurrentExtensionObject; extension.objectSubId = 0; recordDependencyOn(object, &extension, DEPENDENCY_EXTENSION); } }
/* * sepgsql_attribute_drop * * It checks privileges to drop the supplied column. */ void sepgsql_attribute_drop(Oid relOid, AttrNumber attnum) { ObjectAddress object; char *audit_name; if (get_rel_relkind(relOid) != RELKIND_RELATION) return; /* * check db_column:{drop} permission */ object.classId = RelationRelationId; object.objectId = relOid; object.objectSubId = attnum; audit_name = getObjectDescription(&object); sepgsql_avc_check_perms(&object, SEPG_CLASS_DB_COLUMN, SEPG_DB_COLUMN__DROP, audit_name, true); pfree(audit_name); }
/* * checkSharedDependencies * * Check whether there are shared dependency entries for a given shared * object; return true if so. * * In addition, return a string containing a newline-separated list of object * descriptions that depend on the shared object, or NULL if none is found. * We actually return two such strings; the "detail" result is suitable for * returning to the client as an errdetail() string, and is limited in size. * The "detail_log" string is potentially much longer, and should be emitted * to the server log only. * * We can find three different kinds of dependencies: dependencies on objects * of the current database; dependencies on shared objects; and dependencies * on objects local to other databases. We can (and do) provide descriptions * of the two former kinds of objects, but we can't do that for "remote" * objects, so we just provide a count of them. * * If we find a SHARED_DEPENDENCY_PIN entry, we can error out early. */ bool checkSharedDependencies(Oid classId, Oid objectId, char **detail_msg, char **detail_log_msg) { Relation sdepRel; ScanKeyData key[2]; SysScanDesc scan; HeapTuple tup; int numReportedDeps = 0; int numNotReportedDeps = 0; int numNotReportedDbs = 0; List *remDeps = NIL; ListCell *cell; ObjectAddress object; StringInfoData descs; StringInfoData alldescs; /* * We limit the number of dependencies reported to the client to * MAX_REPORTED_DEPS, since client software may not deal well with * enormous error strings. The server log always gets a full report. */ #define MAX_REPORTED_DEPS 100 initStringInfo(&descs); initStringInfo(&alldescs); sdepRel = heap_open(SharedDependRelationId, AccessShareLock); ScanKeyInit(&key[0], Anum_pg_shdepend_refclassid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(classId)); ScanKeyInit(&key[1], Anum_pg_shdepend_refobjid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(objectId)); scan = systable_beginscan(sdepRel, SharedDependReferenceIndexId, true, NULL, 2, key); while (HeapTupleIsValid(tup = systable_getnext(scan))) { Form_pg_shdepend sdepForm = (Form_pg_shdepend) GETSTRUCT(tup); /* This case can be dispatched quickly */ if (sdepForm->deptype == SHARED_DEPENDENCY_PIN) { object.classId = classId; object.objectId = objectId; object.objectSubId = 0; ereport(ERROR, (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST), errmsg("cannot drop %s because it is required by the database system", getObjectDescription(&object)))); } object.classId = sdepForm->classid; object.objectId = sdepForm->objid; object.objectSubId = sdepForm->objsubid; /* * If it's a dependency local to this database or it's a shared * object, describe it. * * If it's a remote dependency, keep track of it so we can report the * number of them later. */ if (sdepForm->dbid == MyDatabaseId) { if (numReportedDeps < MAX_REPORTED_DEPS) { numReportedDeps++; storeObjectDescription(&descs, LOCAL_OBJECT, &object, sdepForm->deptype, 0); } else numNotReportedDeps++; storeObjectDescription(&alldescs, LOCAL_OBJECT, &object, sdepForm->deptype, 0); } else if (sdepForm->dbid == InvalidOid) { if (numReportedDeps < MAX_REPORTED_DEPS) { numReportedDeps++; storeObjectDescription(&descs, SHARED_OBJECT, &object, sdepForm->deptype, 0); } else numNotReportedDeps++; storeObjectDescription(&alldescs, SHARED_OBJECT, &object, sdepForm->deptype, 0); } else { /* It's not local nor shared, so it must be remote. */ remoteDep *dep; bool stored = false; /* * XXX this info is kept on a simple List. Maybe it's not good * for performance, but using a hash table seems needlessly * complex. The expected number of databases is not high anyway, * I suppose. */ foreach(cell, remDeps) { dep = lfirst(cell); if (dep->dbOid == sdepForm->dbid) { dep->count++; stored = true; break; } } if (!stored) { dep = (remoteDep *) palloc(sizeof(remoteDep)); dep->dbOid = sdepForm->dbid; dep->count = 1; remDeps = lappend(remDeps, dep); } } }
/* * sepgsql_relation_drop * * It checks privileges to drop the supplied relation. */ void sepgsql_relation_drop(Oid relOid) { ObjectAddress object; char *audit_name; uint16_t tclass; char relkind; relkind = get_rel_relkind(relOid); switch (relkind) { case RELKIND_RELATION: tclass = SEPG_CLASS_DB_TABLE; break; case RELKIND_SEQUENCE: tclass = SEPG_CLASS_DB_SEQUENCE; break; case RELKIND_VIEW: tclass = SEPG_CLASS_DB_VIEW; break; case RELKIND_INDEX: /* ignore indexes on toast tables */ if (get_rel_namespace(relOid) == PG_TOAST_NAMESPACE) return; /* other indexes are handled specially below; no need for tclass */ break; default: /* ignore other relkinds */ return; } /* * check db_schema:{remove_name} permission */ object.classId = NamespaceRelationId; object.objectId = get_rel_namespace(relOid); object.objectSubId = 0; audit_name = getObjectDescription(&object); sepgsql_avc_check_perms(&object, SEPG_CLASS_DB_SCHEMA, SEPG_DB_SCHEMA__REMOVE_NAME, audit_name, true); pfree(audit_name); /* deal with indexes specially */ if (relkind == RELKIND_INDEX) { sepgsql_index_modify(relOid); return; } /* * check db_table/sequence/view:{drop} permission */ object.classId = RelationRelationId; object.objectId = relOid; object.objectSubId = 0; audit_name = getObjectDescription(&object); sepgsql_avc_check_perms(&object, tclass, SEPG_DB_TABLE__DROP, audit_name, true); pfree(audit_name); /* * check db_column:{drop} permission */ if (relkind == RELKIND_RELATION) { Form_pg_attribute attForm; CatCList *attrList; HeapTuple atttup; int i; attrList = SearchSysCacheList1(ATTNUM, ObjectIdGetDatum(relOid)); for (i = 0; i < attrList->n_members; i++) { atttup = &attrList->members[i]->tuple; attForm = (Form_pg_attribute) GETSTRUCT(atttup); if (attForm->attisdropped) continue; object.classId = RelationRelationId; object.objectId = relOid; object.objectSubId = attForm->attnum; audit_name = getObjectDescription(&object); sepgsql_avc_check_perms(&object, SEPG_CLASS_DB_COLUMN, SEPG_DB_COLUMN__DROP, audit_name, true); pfree(audit_name); } ReleaseCatCacheList(attrList); } }
/* * sepgsql_relation_post_create * * The post creation hook of relation/attribute */ void sepgsql_relation_post_create(Oid relOid) { Relation rel; ScanKeyData skey; SysScanDesc sscan; HeapTuple tuple; Form_pg_class classForm; ObjectAddress object; uint16 tclass; const char *tclass_text; char *scontext; /* subject */ char *tcontext; /* schema */ char *rcontext; /* relation */ char *ccontext; /* column */ char audit_name[2 * NAMEDATALEN + 20]; /* * Fetch catalog record of the new relation. Because pg_class entry is not * visible right now, we need to scan the catalog using SnapshotSelf. */ rel = heap_open(RelationRelationId, AccessShareLock); ScanKeyInit(&skey, ObjectIdAttributeNumber, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relOid)); sscan = systable_beginscan(rel, ClassOidIndexId, true, SnapshotSelf, 1, &skey); tuple = systable_getnext(sscan); if (!HeapTupleIsValid(tuple)) elog(ERROR, "catalog lookup failed for relation %u", relOid); classForm = (Form_pg_class) GETSTRUCT(tuple); /* ignore indexes on toast tables */ if (classForm->relkind == RELKIND_INDEX && classForm->relnamespace == PG_TOAST_NAMESPACE) 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); switch (classForm->relkind) { case RELKIND_RELATION: tclass = SEPG_CLASS_DB_TABLE; tclass_text = "table"; break; case RELKIND_SEQUENCE: tclass = SEPG_CLASS_DB_SEQUENCE; tclass_text = "sequence"; break; case RELKIND_VIEW: tclass = SEPG_CLASS_DB_VIEW; tclass_text = "view"; break; case RELKIND_INDEX: /* deal with indexes specially; no need for tclass */ sepgsql_index_modify(relOid); goto out; default: /* ignore other relkinds */ goto out; } /* * Compute a default security label when we create a new relation object * under the specified namespace. */ scontext = sepgsql_get_client_label(); tcontext = sepgsql_get_label(NamespaceRelationId, classForm->relnamespace, 0); 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 */ object.classId = RelationRelationId; object.objectId = relOid; object.objectSubId = 0; SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, rcontext); /* * We also assigns a default security label on columns of the new regular * tables. */ if (classForm->relkind == RELKIND_RELATION) { Relation arel; ScanKeyData akey; SysScanDesc ascan; HeapTuple atup; Form_pg_attribute attForm; arel = heap_open(AttributeRelationId, AccessShareLock); ScanKeyInit(&akey, Anum_pg_attribute_attrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relOid)); ascan = systable_beginscan(arel, AttributeRelidNumIndexId, true, SnapshotSelf, 1, &akey); while (HeapTupleIsValid(atup = systable_getnext(ascan))) { attForm = (Form_pg_attribute) GETSTRUCT(atup); snprintf(audit_name, sizeof(audit_name), "%s %s column %s", 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.objectId = relOid; object.objectSubId = attForm->attnum; SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, ccontext); pfree(ccontext); } systable_endscan(ascan); heap_close(arel, AccessShareLock); } pfree(rcontext); out: systable_endscan(sscan); heap_close(rel, AccessShareLock); }
/* * check_relation_privileges * * It actually checks required permissions on a certain relation * and its columns. */ static bool check_relation_privileges(Oid relOid, Bitmapset *selected, Bitmapset *inserted, Bitmapset *updated, uint32 required, bool abort_on_violation) { ObjectAddress object; char *audit_name; Bitmapset *columns; int index; char relkind = get_rel_relkind(relOid); bool result = true; /* * Hardwired Policies: SE-PostgreSQL enforces - clients cannot modify * system catalogs using DMLs - clients cannot reference/modify toast * relations using DMLs */ if (sepgsql_getenforce() > 0) { Oid relnamespace = get_rel_namespace(relOid); if (IsSystemNamespace(relnamespace) && (required & (SEPG_DB_TABLE__UPDATE | SEPG_DB_TABLE__INSERT | SEPG_DB_TABLE__DELETE)) != 0) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("SELinux: hardwired security policy violation"))); if (relkind == RELKIND_TOASTVALUE) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("SELinux: hardwired security policy violation"))); } /* * Check permissions on the relation */ object.classId = RelationRelationId; object.objectId = relOid; object.objectSubId = 0; audit_name = getObjectIdentity(&object); switch (relkind) { case RELKIND_RELATION: case RELKIND_PARTITIONED_TABLE: result = sepgsql_avc_check_perms(&object, SEPG_CLASS_DB_TABLE, required, audit_name, abort_on_violation); break; case RELKIND_SEQUENCE: Assert((required & ~SEPG_DB_TABLE__SELECT) == 0); if (required & SEPG_DB_TABLE__SELECT) result = sepgsql_avc_check_perms(&object, SEPG_CLASS_DB_SEQUENCE, SEPG_DB_SEQUENCE__GET_VALUE, audit_name, abort_on_violation); break; case RELKIND_VIEW: result = sepgsql_avc_check_perms(&object, SEPG_CLASS_DB_VIEW, SEPG_DB_VIEW__EXPAND, audit_name, abort_on_violation); break; default: /* nothing to be checked */ break; } pfree(audit_name); /* * Only columns owned by relations shall be checked */ if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE) return true; /* * Check permissions on the columns */ selected = fixup_whole_row_references(relOid, selected); inserted = fixup_whole_row_references(relOid, inserted); updated = fixup_whole_row_references(relOid, updated); columns = bms_union(selected, bms_union(inserted, updated)); while ((index = bms_first_member(columns)) >= 0) { AttrNumber attnum; uint32 column_perms = 0; if (bms_is_member(index, selected)) column_perms |= SEPG_DB_COLUMN__SELECT; if (bms_is_member(index, inserted)) { if (required & SEPG_DB_TABLE__INSERT) column_perms |= SEPG_DB_COLUMN__INSERT; } if (bms_is_member(index, updated)) { if (required & SEPG_DB_TABLE__UPDATE) column_perms |= SEPG_DB_COLUMN__UPDATE; } if (column_perms == 0) continue; /* obtain column's permission */ attnum = index + FirstLowInvalidHeapAttributeNumber; object.classId = RelationRelationId; object.objectId = relOid; object.objectSubId = attnum; audit_name = getObjectDescription(&object); result = sepgsql_avc_check_perms(&object, SEPG_CLASS_DB_COLUMN, column_perms, audit_name, abort_on_violation); pfree(audit_name); if (!result) return result; } return true; }
/* * Adjust dependency record(s) to point to a different object of the same type * * classId/objectId specify the referencing object. * refClassId/oldRefObjectId specify the old referenced object. * newRefObjectId is the new referenced object (must be of class refClassId). * * Note the lack of objsubid parameters. If there are subobject references * they will all be readjusted. * * Returns the number of records updated. */ long changeDependencyFor(Oid classId, Oid objectId, Oid refClassId, Oid oldRefObjectId, Oid newRefObjectId) { long count = 0; Relation depRel; ScanKeyData key[2]; SysScanDesc scan; HeapTuple tup; ObjectAddress objAddr; bool newIsPinned; depRel = heap_open(DependRelationId, RowExclusiveLock); /* * If oldRefObjectId is pinned, there won't be any dependency entries on * it --- we can't cope in that case. (This isn't really worth expending * code to fix, in current usage; it just means you can't rename stuff out * of pg_catalog, which would likely be a bad move anyway.) */ objAddr.classId = refClassId; objAddr.objectId = oldRefObjectId; objAddr.objectSubId = 0; if (isObjectPinned(&objAddr, depRel)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot remove dependency on %s because it is a system object", getObjectDescription(&objAddr)))); /* * We can handle adding a dependency on something pinned, though, since * that just means deleting the dependency entry. */ objAddr.objectId = newRefObjectId; newIsPinned = isObjectPinned(&objAddr, depRel); /* Now search for dependency records */ ScanKeyInit(&key[0], Anum_pg_depend_classid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(classId)); ScanKeyInit(&key[1], Anum_pg_depend_objid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(objectId)); scan = systable_beginscan(depRel, DependDependerIndexId, true, NULL, 2, key); while (HeapTupleIsValid((tup = systable_getnext(scan)))) { Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup); if (depform->refclassid == refClassId && depform->refobjid == oldRefObjectId) { if (newIsPinned) simple_heap_delete(depRel, &tup->t_self); else { /* make a modifiable copy */ tup = heap_copytuple(tup); depform = (Form_pg_depend) GETSTRUCT(tup); depform->refobjid = newRefObjectId; simple_heap_update(depRel, &tup->t_self, tup); CatalogUpdateIndexes(depRel, tup); heap_freetuple(tup); } count++; } } systable_endscan(scan); heap_close(depRel, RowExclusiveLock); return count; }
/* * sepgsql_relation_drop * * It checks privileges to drop the supplied relation. */ void sepgsql_relation_drop(Oid relOid) { ObjectAddress object; char *audit_name; uint16_t tclass = 0; char relkind; relkind = get_rel_relkind(relOid); if (relkind == RELKIND_RELATION) tclass = SEPG_CLASS_DB_TABLE; else if (relkind == RELKIND_SEQUENCE) tclass = SEPG_CLASS_DB_SEQUENCE; else if (relkind == RELKIND_VIEW) tclass = SEPG_CLASS_DB_VIEW; else return; /* * check db_schema:{remove_name} permission */ object.classId = NamespaceRelationId; object.objectId = get_rel_namespace(relOid); object.objectSubId = 0; audit_name = getObjectDescription(&object); sepgsql_avc_check_perms(&object, SEPG_CLASS_DB_SCHEMA, SEPG_DB_SCHEMA__REMOVE_NAME, audit_name, true); pfree(audit_name); /* * check db_table/sequence/view:{drop} permission */ object.classId = RelationRelationId; object.objectId = relOid; object.objectSubId = 0; audit_name = getObjectDescription(&object); sepgsql_avc_check_perms(&object, tclass, SEPG_DB_TABLE__DROP, audit_name, true); pfree(audit_name); /* * check db_column:{drop} permission */ if (relkind == RELKIND_RELATION) { Form_pg_attribute attForm; CatCList *attrList; HeapTuple atttup; int i; attrList = SearchSysCacheList1(ATTNUM, ObjectIdGetDatum(relOid)); for (i = 0; i < attrList->n_members; i++) { atttup = &attrList->members[i]->tuple; attForm = (Form_pg_attribute) GETSTRUCT(atttup); if (attForm->attisdropped) continue; object.classId = RelationRelationId; object.objectId = relOid; object.objectSubId = attForm->attnum; audit_name = getObjectDescription(&object); sepgsql_avc_check_perms(&object, SEPG_CLASS_DB_COLUMN, SEPG_DB_COLUMN__DROP, audit_name, true); pfree(audit_name); } ReleaseCatCacheList(attrList); } }