diff --git a/src/common/backend/catalog/gs_package.cpp b/src/common/backend/catalog/gs_package.cpp index b5bcac453..4bd9582f2 100644 --- a/src/common/backend/catalog/gs_package.cpp +++ b/src/common/backend/catalog/gs_package.cpp @@ -393,9 +393,7 @@ Oid PackageSpecCreate(Oid pkgNamespace, const char* pkgName, const Oid ownerId, heap_close(pkgDesc, RowExclusiveLock); /* Record dependencies */ - myself.classId = PackageRelationId; - myself.objectId = pkgOid; - myself.objectSubId = 0; + ObjectAddressSet(myself, PackageRelationId, pkgOid); isUpgrade = u_sess->attr.attr_common.IsInplaceUpgrade && myself.objectId < FirstBootstrapObjectId && !isReplaced; if (isUpgrade) { recordPinnedDependency(&myself); @@ -408,7 +406,7 @@ Oid PackageSpecCreate(Oid pkgNamespace, const char* pkgName, const Oid ownerId, recordDependencyOnOwner(PackageRelationId, pkgOid, ownerId); - recordDependencyOnCurrentExtension(&myself, false); + recordDependencyOnCurrentExtension(&myself, isReplaced); } /* Post creation hook for new schema */ diff --git a/src/common/backend/catalog/pg_depend.cpp b/src/common/backend/catalog/pg_depend.cpp index 8ee5746f4..3e1935be5 100644 --- a/src/common/backend/catalog/pg_depend.cpp +++ b/src/common/backend/catalog/pg_depend.cpp @@ -161,6 +161,14 @@ void recordDependencyOnCurrentExtension(const ObjectAddress* object, bool isRepl getObjectDescription(object), get_extension_name(oldext)))); } + + /* It's a free-standing object, so reject */ + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("%s is not a member of extension \"%s\"", + getObjectDescription(object), + get_extension_name(u_sess->cmd_cxt.CurrentExtensionObject)), + errdetail("An extension is not allowed to replace an object that it does not own."))); } /* OK, record it as a member of CurrentExtensionObject */ @@ -172,6 +180,49 @@ void recordDependencyOnCurrentExtension(const ObjectAddress* object, bool isRepl } } +/* + * If we are executing a CREATE EXTENSION operation, check that the given + * object is a member of the extension, and throw an error if it isn't. + * Otherwise, do nothing. + * + * This must be called whenever a CREATE IF NOT EXISTS operation (for an + * object type that can be an extension member) has found that an object of + * the desired name already exists. It is insecure for an extension to use + * IF NOT EXISTS except when the conflicting object is already an extension + * member; otherwise a hostile user could substitute an object with arbitrary + * properties. + */ +void checkMembershipInCurrentExtension(const ObjectAddress *object) +{ + /* + * This is actually the same condition tested in + * recordDependencyOnCurrentExtension; but we want to issue a + * differently-worded error, and anyway it would be pretty confusing to + * call recordDependencyOnCurrentExtension in these circumstances. + */ + + /* Only whole objects can be extension members */ + Assert(object->objectSubId == 0); + + if (creating_extension) { + Oid oldext; + + oldext = getExtensionOfObject(object->classId, object->objectId); + /* If already a member of this extension, OK */ + if (oldext == u_sess->cmd_cxt.CurrentExtensionObject) + return; + /* Else complain */ + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("%s is not a member of extension \"%s\"", + getObjectDescription(object), + get_extension_name(u_sess->cmd_cxt.CurrentExtensionObject)), + errdetail("An extension may only use CREATE ... IF NOT EXISTS to skip object creation" + "if the conflicting object is one that it already owns."))); + } +} + + /* * Record pinned dependency for fabricated system tables during in-place upgrade. */ diff --git a/src/common/backend/catalog/pg_operator.cpp b/src/common/backend/catalog/pg_operator.cpp index ac7962d04..2b2f30c4d 100644 --- a/src/common/backend/catalog/pg_operator.cpp +++ b/src/common/backend/catalog/pg_operator.cpp @@ -47,7 +47,7 @@ static void OperatorUpd(Oid baseId, Oid commId, Oid negId); static Oid get_other_operator(List* otherOp, Oid otherLeftTypeId, Oid otherRightTypeId, const char* operatorName, Oid operatorNamespace, Oid leftTypeId, Oid rightTypeId, bool isCommutator); -static void makeOperatorDependencies(HeapTuple tuple); +static void makeOperatorDependencies(HeapTuple tuple, bool isUpdate); /* * Check whether a proposed operator name is legal @@ -234,7 +234,7 @@ static Oid OperatorShellMake(const char* operatorName, Oid operatorNamespace, Oi CatalogUpdateIndexes(pg_operator_desc, tup); /* Add dependencies for the entry */ - makeOperatorDependencies(tup); + makeOperatorDependencies(tup, false); heap_freetuple_ext(tup); @@ -306,6 +306,7 @@ void OperatorCreate(const char* operatorName, Oid operatorNamespace, Oid leftTyp TupleDesc tupDesc; int i; Oid oprowner = InvalidOid; + bool isUpdate = false; /* * isalter is true, change the owner of the objects as the owner of the * namespace, if the owner of the namespce has the same name as the namescpe @@ -454,6 +455,7 @@ void OperatorCreate(const char* operatorName, Oid operatorNamespace, Oid leftTyp * If we are replacing an operator shell, update; else insert */ if (operatorObjectId) { + isUpdate = true; tup = SearchSysCacheCopy1(OPEROID, ObjectIdGetDatum(operatorObjectId)); if (!HeapTupleIsValid(tup)) ereport(ERROR, @@ -474,7 +476,7 @@ void OperatorCreate(const char* operatorName, Oid operatorNamespace, Oid leftTyp CatalogUpdateIndexes(pg_operator_desc, tup); /* Add dependencies for the entry */ - makeOperatorDependencies(tup); + makeOperatorDependencies(tup, isUpdate); /* Post creation hook for new operator */ InvokeObjectAccessHook(OAT_POST_CREATE, OperatorRelationId, operatorObjectId, 0, NULL); @@ -680,7 +682,7 @@ static void OperatorUpd(Oid baseId, Oid commId, Oid negId) * NB: the OidIsValid tests in this routine are necessary, in case * the given operator is a shell. */ -static void makeOperatorDependencies(HeapTuple tuple) +static void makeOperatorDependencies(HeapTuple tuple, bool isUpdate) { Form_pg_operator oper = (Form_pg_operator)GETSTRUCT(tuple); ObjectAddress myself, referenced; @@ -693,8 +695,10 @@ static void makeOperatorDependencies(HeapTuple tuple) * In case we are updating a shell, delete any existing entries, except * for extension membership which should remain the same. */ - (void)deleteDependencyRecordsFor(myself.classId, myself.objectId, true); - deleteSharedDependencyRecordsFor(myself.classId, myself.objectId, 0); + if (isUpdate) { + (void)deleteDependencyRecordsFor(myself.classId, myself.objectId, true); + deleteSharedDependencyRecordsFor(myself.classId, myself.objectId, 0); + } /* Dependency on namespace */ if (OidIsValid(oper->oprnamespace)) { @@ -765,5 +769,5 @@ static void makeOperatorDependencies(HeapTuple tuple) recordDependencyOnOwner(OperatorRelationId, HeapTupleGetOid(tuple), oper->oprowner); /* Dependency on extension */ - recordDependencyOnCurrentExtension(&myself, true); + recordDependencyOnCurrentExtension(&myself, isUpdate); } diff --git a/src/common/backend/catalog/pg_synonym.cpp b/src/common/backend/catalog/pg_synonym.cpp index 5798356e1..ebbcb4d2f 100644 --- a/src/common/backend/catalog/pg_synonym.cpp +++ b/src/common/backend/catalog/pg_synonym.cpp @@ -223,13 +223,11 @@ static Oid SynonymCreate( /* update the index if any */ CatalogUpdateIndexes(rel, tuple); + ObjectAddressSet(myself, PgSynonymRelationId, HeapTupleGetOid(tuple)); + /* record the dependencies, for the first create. */ if (!isUpdate) { /* dependency on namespace of synonym object */ - myself.classId = PgSynonymRelationId; - myself.objectId = HeapTupleGetOid(tuple); - myself.objectSubId = 0; - referenced.classId = NamespaceRelationId; referenced.objectId = synNamespace; referenced.objectSubId = 0; @@ -239,6 +237,8 @@ static Oid SynonymCreate( recordDependencyOnOwner(myself.classId, myself.objectId, synOwner); } + recordDependencyOnCurrentExtension(&myself, isUpdate); + heap_freetuple(tuple); heap_close(rel, RowExclusiveLock); diff --git a/src/common/backend/parser/parse_utilcmd.cpp b/src/common/backend/parser/parse_utilcmd.cpp index 369eb59e3..78d9785db 100644 --- a/src/common/backend/parser/parse_utilcmd.cpp +++ b/src/common/backend/parser/parse_utilcmd.cpp @@ -291,6 +291,16 @@ Oid *namespaceid, bool isFirstNode) */ if (stmt->if_not_exists && OidIsValid(existing_relid)) { bool exists_ok = true; + + /* + * If we are in an extension script, insist that the pre-existing + * object be a member of the extension, to avoid security risks. + */ + ObjectAddress address; + + ObjectAddressSet(address, RelationRelationId, existing_relid); + checkMembershipInCurrentExtension(&address); + /* * Emit the right error or warning message for a "CREATE" command issued on a exist relation. * remote node : should have relation if recieve "IF NOT EXISTS" stmt. diff --git a/src/gausskernel/optimizer/commands/directory.cpp b/src/gausskernel/optimizer/commands/directory.cpp index e7f177167..cbe6c7450 100644 --- a/src/gausskernel/optimizer/commands/directory.cpp +++ b/src/gausskernel/optimizer/commands/directory.cpp @@ -155,6 +155,7 @@ void CreatePgDirectory(CreateDirectoryStmt* stmt) HeapTuple oldtup = NULL; HeapTuple tup = NULL; TupleDesc tupDesc = NULL; + bool isUpdate = false; errno_t rc; /* Permission check. */ @@ -191,6 +192,7 @@ void CreatePgDirectory(CreateDirectoryStmt* stmt) if (OidIsValid(targetoid)) { /* existing a entry before */ if (stmt->replace) { + isUpdate = true; replaces[Anum_pg_directory_directory_name - 1] = false; oldtup = SearchSysCache1(DIRECTORYOID, targetoid); tupDesc = RelationGetDescr(rel); @@ -224,6 +226,10 @@ void CreatePgDirectory(CreateDirectoryStmt* stmt) pfree(location); heap_close(rel, RowExclusiveLock); } + + ObjectAddress myself; + ObjectAddressSet(myself, PgDirectoryRelationId, isUpdate ? targetoid : directoryId); + recordDependencyOnCurrentExtension(&myself, isUpdate); } /* diff --git a/src/gausskernel/optimizer/commands/schemacmds.cpp b/src/gausskernel/optimizer/commands/schemacmds.cpp index 7412749ba..01adb9526 100644 --- a/src/gausskernel/optimizer/commands/schemacmds.cpp +++ b/src/gausskernel/optimizer/commands/schemacmds.cpp @@ -104,6 +104,7 @@ void CreateSchemaCommand(CreateSchemaStmt* stmt, const char* queryString) int save_sec_context; AclResult aclresult; char* queryStringwithinfo = (char*)queryString; + ObjectAddress address; GetUserIdAndSecContext(&saved_uid, &save_sec_context); if (IsExistPackageName(schemaName)) { @@ -220,10 +221,19 @@ void CreateSchemaCommand(CreateSchemaStmt* stmt, const char* queryString) /* make sure there is no existing namespace of same name */ if (stmt->missing_ok) { - if (SearchSysCacheExists1(NAMESPACENAME, PointerGetDatum(schemaName))) { + namespaceId = get_namespace_oid(schemaName, true); + if (OidIsValid(namespaceId)) { + /* + * If we are in an extension script, insist that the pre-existing + * object be a member of the extension, to avoid security risks. + */ + ObjectAddressSet(address, NamespaceRelationId, namespaceId); + checkMembershipInCurrentExtension(&address); + + /* OK to skip */ ereport(NOTICE, (errmsg("schema \"%s\" already exists,skipping", schemaName))); return; - } + } } /* Create the schema's namespace */ diff --git a/src/gausskernel/optimizer/commands/view.cpp b/src/gausskernel/optimizer/commands/view.cpp index 0f5e90984..bc3ca1ed5 100644 --- a/src/gausskernel/optimizer/commands/view.cpp +++ b/src/gausskernel/optimizer/commands/view.cpp @@ -162,6 +162,7 @@ static Oid DefineVirtualRelation(RangeVar* relation, List* tlist, bool replace, TupleDesc descriptor; List* atcmds = NIL; AlterTableCmd* atcmd = NULL; + ObjectAddress address; /* * During inplace upgrade, if we are doing rolling back, the old-versioned @@ -259,6 +260,22 @@ static Oid DefineVirtualRelation(RangeVar* relation, List* tlist, bool replace, /* OK, let's do it. */ AlterTableInternal(viewOid, atcmds, true); + /* + * There is very little to do here to update the view's dependencies. + * Most view-level dependency relationships, such as those on the + * owner, schema, and associated composite type, aren't changing. + * Because we don't allow changing type or collation of an existing + * view column, those dependencies of the existing columns don't + * change either, while the AT_AddColumnToView machinery took care of + * adding such dependencies for new view columns. The dependencies of + * the view's query could have changed arbitrarily, but that was dealt + * with inside StoreViewQuery. What remains is only to check that + * view replacement is allowed when we're creating an extension. + */ + ObjectAddressSet(address, RelationRelationId, viewOid); + + recordDependencyOnCurrentExtension(&address, true); + /* * Seems okay, so return the OID of the pre-existing view. */ diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h index 3d268d1ea..f539f89f6 100644 --- a/src/include/catalog/dependency.h +++ b/src/include/catalog/dependency.h @@ -289,6 +289,8 @@ extern void recordMultipleDependencies(const ObjectAddress *depender, extern void recordDependencyOnCurrentExtension(const ObjectAddress *object, bool isReplace); +extern void checkMembershipInCurrentExtension(const ObjectAddress *object); + extern void recordPinnedDependency(const ObjectAddress *object); extern bool IsPackageDependType(Oid typOid, Oid pkgOid, bool isRefCur = false); diff --git a/src/include/catalog/objectaddress.h b/src/include/catalog/objectaddress.h index 96ac90de1..f64ee0c4f 100644 --- a/src/include/catalog/objectaddress.h +++ b/src/include/catalog/objectaddress.h @@ -41,6 +41,16 @@ typedef struct ObjectAddress char deptype; /* Indicates the deptype that the object is referenced by other object. */ } ObjectAddress; +#define ObjectAddressSubSet(addr, class_id, object_id, object_sub_id) \ + do { \ + (addr).classId = (class_id); \ + (addr).objectId = (object_id); \ + (addr).objectSubId = (object_sub_id); \ + } while (0) + +#define ObjectAddressSet(addr, class_id, object_id) \ + ObjectAddressSubSet(addr, class_id, object_id, 0) + extern ObjectAddress get_object_address(ObjectType objtype, List *objname, List *objargs, Relation *relp, LOCKMODE lockmode, bool missing_ok);