From bc7157dbe65859578ad8d57da0cd4f87869f5034 Mon Sep 17 00:00:00 2001 From: Konstantin Knizhnik Date: Tue, 10 Mar 2026 21:24:22 +0200 Subject: [PATCH] Prevent possible search_path attacks --- src/backend/catalog/namespace.c | 23 ++++++++++++++++++++++- src/include/catalog/namespace.h | 1 + 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c index 66f196e96b2..6f1f4d9fa1d 100644 --- a/src/backend/catalog/namespace.c +++ b/src/backend/catalog/namespace.c @@ -210,7 +210,7 @@ static SubTransactionId myTempNamespaceSubID = InvalidSubTransactionId; * of the GUC variable 'search_path'. */ char *namespace_search_path = NULL; - +bool prohibit_superuser_overrides; /* Local functions */ static bool RelationIsVisibleExt(Oid relid, bool *is_missing); @@ -1201,6 +1201,7 @@ FuncnameGetCandidates(List *names, int nargs, List *argnames, Oid namespaceId; CatCList *catlist; int i; + bool has_superuser_candidate = false; /* check for caller error */ Assert(nargs >= 0 || !(expand_variadic | expand_defaults)); @@ -1264,6 +1265,22 @@ FuncnameGetCandidates(List *names, int nargs, List *argnames, continue; /* proc is not in search path */ } + /* prohibit overrides under superuser */ + if (prohibit_superuser_overrides && superuser()) + { + bool owned_by_superuser = superuser_arg(procform->proowner); + + /* If we have superuser condidate, then ignore all non-supoeruser alternatives */ + if (resultList && has_superuser_candidate && !owned_by_superuser) + continue; + + /* If new candidate is owned by superuser then forget all non-superuser candidates */ + if (owned_by_superuser && !has_superuser_candidate) + resultList = NULL; + + has_superuser_candidate = owned_by_superuser; + } + /* * If we are asked to match to OUT arguments, then use the * proallargtypes array (which includes those); otherwise use @@ -4252,6 +4269,10 @@ finalNamespacePath(List *oidlist, Oid *firstNS) !list_member_oid(finalPath, myTempNamespace)) finalPath = lcons_oid(myTempNamespace, finalPath); + /* Always place pg_catalog at the beginning of search path */ + if (prohibit_superuser_overrides && superuser()) + finalPath = lcons_oid(PG_CATALOG_NAMESPACE, finalPath); + return finalPath; } diff --git a/src/include/catalog/namespace.h b/src/include/catalog/namespace.h index 8c7ccc69a3c..fcd5006dc05 100644 --- a/src/include/catalog/namespace.h +++ b/src/include/catalog/namespace.h @@ -182,6 +182,7 @@ extern void AtEOSubXact_Namespace(bool isCommit, SubTransactionId mySubid, /* stuff for search_path GUC variable */ extern PGDLLIMPORT char *namespace_search_path; +extern bool prohibit_superuser_overrides; extern List *fetch_search_path(bool includeImplicit); extern int fetch_search_path_array(Oid *sarray, int sarray_len);