!2351 回合PG漏洞:CVE-2022-2625,创建extension中带有create or replace语句可能导致越权

Merge pull request !2351 from luajk/CVE-2022-2625
This commit is contained in:
opengauss-bot
2022-11-08 09:34:30 +00:00
committed by Gitee
10 changed files with 125 additions and 17 deletions

View File

@ -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 */

View File

@ -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.
*/

View File

@ -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);
}

View File

@ -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);

View File

@ -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.

View File

@ -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);
}
/*

View File

@ -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 */

View File

@ -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.
*/

View File

@ -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);

View File

@ -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);