Version in base suite: 7.4.7-6sarge5 Version in overlay suite: 7.4.7-6sarge6 Base version: postgresql_7.4.7-6sarge5 Target version: postgresql_7.4.7-6sarge6 Base file: /org/ftp.debian.org/ftp/pool/main/p/postgresql/postgresql_7.4.7-6sarge5.dsc Target file: /org/ftp.debian.org/ftp/pool/main/p/postgresql/postgresql_7.4.7-6sarge6.dsc diff -u postgresql-7.4.7/debian/changelog postgresql-7.4.7/debian/changelog --- postgresql-7.4.7/debian/changelog +++ postgresql-7.4.7/debian/changelog @@ -1,3 +1,17 @@ +postgresql (7.4.7-6sarge6) oldstable-security; urgency=low + + * SECURITY UPDATE: User privilege escalation. + * Add debian/patches/63dblink_restrictions.patch: + - Require non-superusers who use "/contrib/dblink" to use only password + authentication, to prevent abusing the postmaster as source for remote + attacks with trust or ident authentication. + [CVE-2007-3278, CVE-2007-6601] + - Patch backported from 7.4.19 CVS: + http://developer.postgresql.org/cvsweb.cgi/pgsql/contrib/dblink/dblink.c.diff?r1=1.25.4.4;r2=1.25.4.6 + http://developer.postgresql.org/cvsweb.cgi/pgsql/contrib/dblink/dblink.sql.in.diff?r1=1.8;r2=1.8.4.1 + + -- Martin Pitt Sun, 13 Jan 2008 14:56:44 +0100 + postgresql (7.4.7-6sarge5) oldstable-security; urgency=high * SECURITY UPDATE: User privilege escalation. reverted: --- postgresql-7.4.7/debian/patches/62secure_search_path.path +++ postgresql-7.4.7.orig/debian/patches/62secure_search_path.path @@ -1,481 +0,0 @@ -diff -ruN postgresql-7.4.7-old/src/backend/catalog/namespace.c postgresql-7.4.7/src/backend/catalog/namespace.c ---- postgresql-7.4.7-old/src/backend/catalog/namespace.c 2003-09-25 08:57:57.000000000 +0200 -+++ postgresql-7.4.7/src/backend/catalog/namespace.c 2007-04-20 10:46:48.000000000 +0200 -@@ -64,14 +64,32 @@ - * SQL99. Also, this provides a way to search the system namespace first - * without thereby making it the default creation target namespace.) - * -+ * For security reasons, searches using the search path will ignore the temp -+ * namespace when searching for any object type other than relations and -+ * types. (We must allow types since temp tables have rowtypes.) -+ * - * The default creation target namespace is normally equal to the first - * element of the explicit list, but is the "special" namespace when one - * has been set. If the explicit list is empty and there is no special - * namespace, there is no default target. - * -- * In bootstrap mode, the search path is set equal to 'pg_catalog', so that -+ * The textual specification of search_path can include "$user" to refer to -+ * the namespace named the same as the current user, if any. (This is just -+ * ignored if there is no such namespace.) Also, it can include "pg_temp" -+ * to refer to the current backend's temp namespace. This is usually also -+ * ignorable if the temp namespace hasn't been set up, but there's a special -+ * case: if "pg_temp" appears first then it should be the default creation -+ * target. We kluge this case a little bit so that the temp namespace isn't -+ * set up until the first attempt to create something in it. (The reason for -+ * klugery is that we can't create the temp namespace outside a transaction, -+ * but initial GUC processing of search_path happens outside a transaction.) -+ * tempCreationPending is TRUE if "pg_temp" appears first in the string but -+ * is not reflected in defaultCreationNamespace because the namespace isn't -+ * set up yet. -+ * -+ * In bootstrap mode, the search path is set equal to "pg_catalog", so that - * the system namespace is the only one searched or inserted into. -- * The initdb script is also careful to set search_path to 'pg_catalog' for -+ * The initdb script is also careful to set search_path to "pg_catalog" for - * its post-bootstrap standalone backend runs. Otherwise the default search - * path is determined by GUC. The factory default path contains the PUBLIC - * namespace (if it exists), preceded by the user's personal namespace -@@ -99,7 +117,10 @@ - /* first explicit member of list; usually same as defaultCreationNamespace */ - static Oid firstExplicitNamespace = InvalidOid; - --/* The above four values are valid only if namespaceSearchPathValid */ -+/* if TRUE, defaultCreationNamespace is wrong, it should be temp namespace */ -+static bool tempCreationPending = false; -+ -+/* The above five values are valid only if namespaceSearchPathValid */ - static bool namespaceSearchPathValid = true; - - /* -@@ -235,6 +256,14 @@ - - if (newRelation->schemaname) - { -+ /* check for pg_temp alias */ -+ if (strcmp(newRelation->schemaname, "pg_temp") == 0) -+ { -+ /* Initialize temp namespace if first time through */ -+ if (!OidIsValid(myTempNamespace)) -+ InitTempTableNamespace(); -+ return myTempNamespace; -+ } - /* use exact schema given */ - namespaceId = GetSysCacheOid(NAMESPACENAME, - CStringGetDatum(newRelation->schemaname), -@@ -250,6 +279,12 @@ - { - /* use the default creation namespace */ - recomputeNamespacePath(); -+ if (tempCreationPending) -+ { -+ /* Need to initialize temp namespace */ -+ InitTempTableNamespace(); -+ return myTempNamespace; -+ } - namespaceId = defaultCreationNamespace; - if (!OidIsValid(namespaceId)) - ereport(ERROR, -@@ -490,12 +525,16 @@ - } - else - { -- /* Consider only procs that are in the search path */ -+ /* -+ * Consider only procs that are in the search path and are not -+ * in the temp namespace. -+ */ - List *nsp; - - foreach(nsp, namespaceSearchPath) - { -- if (procform->pronamespace == lfirsto(nsp)) -+ if (procform->pronamespace == lfirsto(nsp) && -+ procform->pronamespace != myTempNamespace) - break; - pathpos++; - } -@@ -705,12 +744,16 @@ - } - else - { -- /* Consider only opers that are in the search path */ -+ /* -+ * Consider only opers that are in the search path and are not -+ * in the temp namespace. -+ */ - List *nsp; - - foreach(nsp, namespaceSearchPath) - { -- if (operform->oprnamespace == lfirsto(nsp)) -+ if (operform->oprnamespace == lfirsto(nsp) && -+ operform->oprnamespace != myTempNamespace) - break; - pathpos++; - } -@@ -886,7 +929,8 @@ - /* Consider only opclasses that are in the search path */ - foreach(nsp, namespaceSearchPath) - { -- if (opcform->opcnamespace == lfirsto(nsp)) -+ if (opcform->opcnamespace == lfirsto(nsp) && -+ opcform->opcnamespace != myTempNamespace) - break; - pathpos++; - } -@@ -984,6 +1028,9 @@ - { - Oid namespaceId = lfirsto(lptr); - -+ if (namespaceId == myTempNamespace) -+ continue; /* do not look in temp namespace */ -+ - opcid = GetSysCacheOid(CLAAMNAMENSP, - ObjectIdGetDatum(amid), - PointerGetDatum(opcname), -@@ -1066,6 +1113,9 @@ - { - Oid namespaceId = lfirsto(lptr); - -+ if (namespaceId == myTempNamespace) -+ continue; /* do not look in temp namespace */ -+ - conid = GetSysCacheOid(CONNAMENSP, - PointerGetDatum(conname), - ObjectIdGetDatum(namespaceId), -@@ -1191,6 +1241,19 @@ - Oid namespaceId; - AclResult aclresult; - -+ /* check for pg_temp alias */ -+ if (strcmp(nspname, "pg_temp") == 0) -+ { -+ if (OidIsValid(myTempNamespace)) -+ return myTempNamespace; -+ /* -+ * Since this is used only for looking up existing objects, there -+ * is no point in trying to initialize the temp namespace here; -+ * and doing so might create problems for some callers. -+ * Just fall through and give the "does not exist" error. -+ */ -+ } -+ - namespaceId = GetSysCacheOid(NAMESPACENAME, - CStringGetDatum(nspname), - 0, 0, 0); -@@ -1213,21 +1276,28 @@ - * format), determine what namespace the object should be created in. - * Also extract and return the object name (last component of list). - * -- * This is *not* used for tables. Hence, the TEMP table namespace is -- * never selected as the creation target. -+ * Note: calling this may result in a CommandCounterIncrement operation, -+ * if we have to create or clean out the temp namespace. - */ - Oid - QualifiedNameGetCreationNamespace(List *names, char **objname_p) - { - char *schemaname; -- char *objname; - Oid namespaceId; - - /* deconstruct the name list */ -- DeconstructQualifiedName(names, &schemaname, &objname); -+ DeconstructQualifiedName(names, &schemaname, objname_p); - - if (schemaname) - { -+ /* check for pg_temp alias */ -+ if (strcmp(schemaname, "pg_temp") == 0) -+ { -+ /* Initialize temp namespace if first time through */ -+ if (!OidIsValid(myTempNamespace)) -+ InitTempTableNamespace(); -+ return myTempNamespace; -+ } - /* use exact schema given */ - namespaceId = GetSysCacheOid(NAMESPACENAME, - CStringGetDatum(schemaname), -@@ -1242,6 +1312,12 @@ - { - /* use the default creation namespace */ - recomputeNamespacePath(); -+ if (tempCreationPending) -+ { -+ /* Need to initialize temp namespace */ -+ InitTempTableNamespace(); -+ return myTempNamespace; -+ } - namespaceId = defaultCreationNamespace; - if (!OidIsValid(namespaceId)) - ereport(ERROR, -@@ -1251,7 +1327,6 @@ - - /* Note: callers will check for CREATE rights when appropriate */ - -- *objname_p = objname; - return namespaceId; - } - -@@ -1431,6 +1506,10 @@ - foreach(lptr, namespaceSearchPath) - { - namespaceId = lfirsto(lptr); -+ -+ if (namespaceId == myTempNamespace) -+ continue; /* do not look in temp namespace */ -+ - conoid = FindConversion(conversion_name, namespaceId); - if (OidIsValid(conoid)) - return conoid; -@@ -1456,6 +1535,9 @@ - { - Oid namespaceId = lfirsto(lptr); - -+ if (namespaceId == myTempNamespace) -+ continue; /* do not look in temp namespace */ -+ - proc = FindDefaultConversion(namespaceId, for_encoding, to_encoding); - if (OidIsValid(proc)) - return proc; -@@ -1477,6 +1559,7 @@ - List *oidlist; - List *newpath; - List *l; -+ bool temp_missing; - Oid firstNS; - MemoryContext oldcxt; - -@@ -1504,6 +1587,7 @@ - * has already been accepted.) Don't make duplicate entries, either. - */ - oidlist = NIL; -+ temp_missing = false; - foreach(l, namelist) - { - char *curname = (char *) lfirst(l); -@@ -1533,6 +1617,21 @@ - oidlist = lappendo(oidlist, namespaceId); - } - } -+ else if (strcmp(curname, "pg_temp") == 0) -+ { -+ /* pg_temp --- substitute temp namespace, if any */ -+ if (OidIsValid(myTempNamespace)) -+ { -+ if (!oidMember(myTempNamespace, oidlist)) -+ oidlist = lappendo(oidlist, myTempNamespace); -+ } -+ else -+ { -+ /* If it ought to be the creation namespace, set flag */ -+ if (oidlist == NIL) -+ temp_missing = true; -+ } -+ } - else - { - /* normal namespace reference */ -@@ -1548,7 +1647,9 @@ - } - - /* -- * Remember the first member of the explicit list. -+ * Remember the first member of the explicit list. (Note: this is -+ * nominally wrong if temp_missing, but we need it anyway to distinguish -+ * explicit from implicit mention of pg_catalog.) - */ - if (oidlist == NIL) - firstNS = InvalidOid; -@@ -1588,9 +1689,16 @@ - */ - firstExplicitNamespace = firstNS; - if (OidIsValid(mySpecialNamespace)) -+ { - defaultCreationNamespace = mySpecialNamespace; -+ /* don't have to create temp in this state */ -+ tempCreationPending = false; -+ } - else -+ { - defaultCreationNamespace = firstNS; -+ tempCreationPending = temp_missing; -+ } - - /* Mark the path valid. */ - namespaceSearchPathValid = true; -@@ -1612,6 +1720,8 @@ - char namespaceName[NAMEDATALEN]; - Oid namespaceId; - -+ Assert(!OidIsValid(myTempNamespace)); -+ - /* - * First, do permission check to see if we are authorized to make temp - * tables. We use a nonstandard error message here since -@@ -1782,9 +1892,9 @@ - { - /* - * Verify that all the names are either valid namespace names or -- * "$user". We do not require $user to correspond to a valid -- * namespace. We do not check for USAGE rights, either; should -- * we? -+ * "$user" or "pg_temp". We do not require $user to correspond to a -+ * valid namespace, and pg_temp might not exist yet. We do not check -+ * for USAGE rights, either; should we? - */ - foreach(l, namelist) - { -@@ -1792,6 +1902,8 @@ - - if (strcmp(curname, "$user") == 0) - continue; -+ if (strcmp(curname, "pg_temp") == 0) -+ continue; - if (!SearchSysCacheExists(NAMESPACENAME, - CStringGetDatum(curname), - 0, 0, 0)) -@@ -1837,6 +1949,7 @@ - MemoryContextSwitchTo(oldcxt); - defaultCreationNamespace = PG_CATALOG_NAMESPACE; - firstExplicitNamespace = PG_CATALOG_NAMESPACE; -+ tempCreationPending = false; - namespaceSearchPathValid = true; - namespaceUser = GetUserId(); - } -@@ -1872,6 +1985,9 @@ - * includeImplicit is true. - * - * NB: caller must treat the list as read-only! -+ * -+ * Note: calling this may result in a CommandCounterIncrement operation, -+ * if we have to create or clean out the temp namespace. - */ - List * - fetch_search_path(bool includeImplicit) -@@ -1880,6 +1996,19 @@ - - recomputeNamespacePath(); - -+ /* -+ * If the temp namespace should be first, force it to exist. This is -+ * so that callers can trust the result to reflect the actual default -+ * creation namespace. It's a bit bogus to do this here, since -+ * current_schema() is supposedly a stable function without side-effects, -+ * but the alternatives seem worse. -+ */ -+ if (tempCreationPending) -+ { -+ InitTempTableNamespace(); -+ recomputeNamespacePath(); -+ } -+ - result = namespaceSearchPath; - if (!includeImplicit) - { -diff -ruN postgresql-7.4.7-old/src/test/regress/expected/temp.out postgresql-7.4.7/src/test/regress/expected/temp.out ---- postgresql-7.4.7-old/src/test/regress/expected/temp.out 2003-09-25 08:58:06.000000000 +0200 -+++ postgresql-7.4.7/src/test/regress/expected/temp.out 2007-04-20 10:46:26.000000000 +0200 -@@ -82,3 +82,61 @@ - -- ON COMMIT is only allowed for TEMP - CREATE TABLE temptest(col int) ON COMMIT DELETE ROWS; - ERROR: ON COMMIT can only be used on temporary tables -+-- Test manipulation of temp schema's placement in search path -+create table public.whereami (f1 text); -+insert into public.whereami values ('public'); -+create temp table whereami (f1 text); -+insert into whereami values ('temp'); -+create function public.whoami() returns text -+ as 'select ''public''::text' language sql; -+create function pg_temp.whoami() returns text -+ as 'select ''temp''::text' language sql; -+-- default should have pg_temp implicitly first, but only for tables -+select * from whereami; -+ f1 -+------ -+ temp -+(1 row) -+ -+select whoami(); -+ whoami -+-------- -+ public -+(1 row) -+ -+-- can list temp first explicitly, but it still doesn't affect functions -+set search_path = pg_temp, public; -+select * from whereami; -+ f1 -+------ -+ temp -+(1 row) -+ -+select whoami(); -+ whoami -+-------- -+ public -+(1 row) -+ -+-- or put it last for security -+set search_path = public, pg_temp; -+select * from whereami; -+ f1 -+-------- -+ public -+(1 row) -+ -+select whoami(); -+ whoami -+-------- -+ public -+(1 row) -+ -+-- you can invoke a temp function explicitly, though -+select pg_temp.whoami(); -+ whoami -+-------- -+ temp -+(1 row) -+ -+drop table public.whereami; -diff -ruN postgresql-7.4.7-old/src/test/regress/sql/temp.sql postgresql-7.4.7/src/test/regress/sql/temp.sql ---- postgresql-7.4.7-old/src/test/regress/sql/temp.sql 2003-05-14 05:26:03.000000000 +0200 -+++ postgresql-7.4.7/src/test/regress/sql/temp.sql 2007-04-20 10:46:26.000000000 +0200 -@@ -83,3 +83,36 @@ - -- ON COMMIT is only allowed for TEMP - - CREATE TABLE temptest(col int) ON COMMIT DELETE ROWS; -+ -+-- Test manipulation of temp schema's placement in search path -+ -+create table public.whereami (f1 text); -+insert into public.whereami values ('public'); -+ -+create temp table whereami (f1 text); -+insert into whereami values ('temp'); -+ -+create function public.whoami() returns text -+ as 'select ''public''::text' language sql; -+ -+create function pg_temp.whoami() returns text -+ as 'select ''temp''::text' language sql; -+ -+-- default should have pg_temp implicitly first, but only for tables -+select * from whereami; -+select whoami(); -+ -+-- can list temp first explicitly, but it still doesn't affect functions -+set search_path = pg_temp, public; -+select * from whereami; -+select whoami(); -+ -+-- or put it last for security -+set search_path = public, pg_temp; -+select * from whereami; -+select whoami(); -+ -+-- you can invoke a temp function explicitly, though -+select pg_temp.whoami(); -+ -+drop table public.whereami; only in patch2: unchanged: --- postgresql-7.4.7.orig/debian/patches/62secure_search_path.patch +++ postgresql-7.4.7/debian/patches/62secure_search_path.patch @@ -0,0 +1,481 @@ +diff -ruN postgresql-7.4.7-old/src/backend/catalog/namespace.c postgresql-7.4.7/src/backend/catalog/namespace.c +--- postgresql-7.4.7-old/src/backend/catalog/namespace.c 2003-09-25 08:57:57.000000000 +0200 ++++ postgresql-7.4.7/src/backend/catalog/namespace.c 2007-04-20 10:46:48.000000000 +0200 +@@ -64,14 +64,32 @@ + * SQL99. Also, this provides a way to search the system namespace first + * without thereby making it the default creation target namespace.) + * ++ * For security reasons, searches using the search path will ignore the temp ++ * namespace when searching for any object type other than relations and ++ * types. (We must allow types since temp tables have rowtypes.) ++ * + * The default creation target namespace is normally equal to the first + * element of the explicit list, but is the "special" namespace when one + * has been set. If the explicit list is empty and there is no special + * namespace, there is no default target. + * +- * In bootstrap mode, the search path is set equal to 'pg_catalog', so that ++ * The textual specification of search_path can include "$user" to refer to ++ * the namespace named the same as the current user, if any. (This is just ++ * ignored if there is no such namespace.) Also, it can include "pg_temp" ++ * to refer to the current backend's temp namespace. This is usually also ++ * ignorable if the temp namespace hasn't been set up, but there's a special ++ * case: if "pg_temp" appears first then it should be the default creation ++ * target. We kluge this case a little bit so that the temp namespace isn't ++ * set up until the first attempt to create something in it. (The reason for ++ * klugery is that we can't create the temp namespace outside a transaction, ++ * but initial GUC processing of search_path happens outside a transaction.) ++ * tempCreationPending is TRUE if "pg_temp" appears first in the string but ++ * is not reflected in defaultCreationNamespace because the namespace isn't ++ * set up yet. ++ * ++ * In bootstrap mode, the search path is set equal to "pg_catalog", so that + * the system namespace is the only one searched or inserted into. +- * The initdb script is also careful to set search_path to 'pg_catalog' for ++ * The initdb script is also careful to set search_path to "pg_catalog" for + * its post-bootstrap standalone backend runs. Otherwise the default search + * path is determined by GUC. The factory default path contains the PUBLIC + * namespace (if it exists), preceded by the user's personal namespace +@@ -99,7 +117,10 @@ + /* first explicit member of list; usually same as defaultCreationNamespace */ + static Oid firstExplicitNamespace = InvalidOid; + +-/* The above four values are valid only if namespaceSearchPathValid */ ++/* if TRUE, defaultCreationNamespace is wrong, it should be temp namespace */ ++static bool tempCreationPending = false; ++ ++/* The above five values are valid only if namespaceSearchPathValid */ + static bool namespaceSearchPathValid = true; + + /* +@@ -235,6 +256,14 @@ + + if (newRelation->schemaname) + { ++ /* check for pg_temp alias */ ++ if (strcmp(newRelation->schemaname, "pg_temp") == 0) ++ { ++ /* Initialize temp namespace if first time through */ ++ if (!OidIsValid(myTempNamespace)) ++ InitTempTableNamespace(); ++ return myTempNamespace; ++ } + /* use exact schema given */ + namespaceId = GetSysCacheOid(NAMESPACENAME, + CStringGetDatum(newRelation->schemaname), +@@ -250,6 +279,12 @@ + { + /* use the default creation namespace */ + recomputeNamespacePath(); ++ if (tempCreationPending) ++ { ++ /* Need to initialize temp namespace */ ++ InitTempTableNamespace(); ++ return myTempNamespace; ++ } + namespaceId = defaultCreationNamespace; + if (!OidIsValid(namespaceId)) + ereport(ERROR, +@@ -490,12 +525,16 @@ + } + else + { +- /* Consider only procs that are in the search path */ ++ /* ++ * Consider only procs that are in the search path and are not ++ * in the temp namespace. ++ */ + List *nsp; + + foreach(nsp, namespaceSearchPath) + { +- if (procform->pronamespace == lfirsto(nsp)) ++ if (procform->pronamespace == lfirsto(nsp) && ++ procform->pronamespace != myTempNamespace) + break; + pathpos++; + } +@@ -705,12 +744,16 @@ + } + else + { +- /* Consider only opers that are in the search path */ ++ /* ++ * Consider only opers that are in the search path and are not ++ * in the temp namespace. ++ */ + List *nsp; + + foreach(nsp, namespaceSearchPath) + { +- if (operform->oprnamespace == lfirsto(nsp)) ++ if (operform->oprnamespace == lfirsto(nsp) && ++ operform->oprnamespace != myTempNamespace) + break; + pathpos++; + } +@@ -886,7 +929,8 @@ + /* Consider only opclasses that are in the search path */ + foreach(nsp, namespaceSearchPath) + { +- if (opcform->opcnamespace == lfirsto(nsp)) ++ if (opcform->opcnamespace == lfirsto(nsp) && ++ opcform->opcnamespace != myTempNamespace) + break; + pathpos++; + } +@@ -984,6 +1028,9 @@ + { + Oid namespaceId = lfirsto(lptr); + ++ if (namespaceId == myTempNamespace) ++ continue; /* do not look in temp namespace */ ++ + opcid = GetSysCacheOid(CLAAMNAMENSP, + ObjectIdGetDatum(amid), + PointerGetDatum(opcname), +@@ -1066,6 +1113,9 @@ + { + Oid namespaceId = lfirsto(lptr); + ++ if (namespaceId == myTempNamespace) ++ continue; /* do not look in temp namespace */ ++ + conid = GetSysCacheOid(CONNAMENSP, + PointerGetDatum(conname), + ObjectIdGetDatum(namespaceId), +@@ -1191,6 +1241,19 @@ + Oid namespaceId; + AclResult aclresult; + ++ /* check for pg_temp alias */ ++ if (strcmp(nspname, "pg_temp") == 0) ++ { ++ if (OidIsValid(myTempNamespace)) ++ return myTempNamespace; ++ /* ++ * Since this is used only for looking up existing objects, there ++ * is no point in trying to initialize the temp namespace here; ++ * and doing so might create problems for some callers. ++ * Just fall through and give the "does not exist" error. ++ */ ++ } ++ + namespaceId = GetSysCacheOid(NAMESPACENAME, + CStringGetDatum(nspname), + 0, 0, 0); +@@ -1213,21 +1276,28 @@ + * format), determine what namespace the object should be created in. + * Also extract and return the object name (last component of list). + * +- * This is *not* used for tables. Hence, the TEMP table namespace is +- * never selected as the creation target. ++ * Note: calling this may result in a CommandCounterIncrement operation, ++ * if we have to create or clean out the temp namespace. + */ + Oid + QualifiedNameGetCreationNamespace(List *names, char **objname_p) + { + char *schemaname; +- char *objname; + Oid namespaceId; + + /* deconstruct the name list */ +- DeconstructQualifiedName(names, &schemaname, &objname); ++ DeconstructQualifiedName(names, &schemaname, objname_p); + + if (schemaname) + { ++ /* check for pg_temp alias */ ++ if (strcmp(schemaname, "pg_temp") == 0) ++ { ++ /* Initialize temp namespace if first time through */ ++ if (!OidIsValid(myTempNamespace)) ++ InitTempTableNamespace(); ++ return myTempNamespace; ++ } + /* use exact schema given */ + namespaceId = GetSysCacheOid(NAMESPACENAME, + CStringGetDatum(schemaname), +@@ -1242,6 +1312,12 @@ + { + /* use the default creation namespace */ + recomputeNamespacePath(); ++ if (tempCreationPending) ++ { ++ /* Need to initialize temp namespace */ ++ InitTempTableNamespace(); ++ return myTempNamespace; ++ } + namespaceId = defaultCreationNamespace; + if (!OidIsValid(namespaceId)) + ereport(ERROR, +@@ -1251,7 +1327,6 @@ + + /* Note: callers will check for CREATE rights when appropriate */ + +- *objname_p = objname; + return namespaceId; + } + +@@ -1431,6 +1506,10 @@ + foreach(lptr, namespaceSearchPath) + { + namespaceId = lfirsto(lptr); ++ ++ if (namespaceId == myTempNamespace) ++ continue; /* do not look in temp namespace */ ++ + conoid = FindConversion(conversion_name, namespaceId); + if (OidIsValid(conoid)) + return conoid; +@@ -1456,6 +1535,9 @@ + { + Oid namespaceId = lfirsto(lptr); + ++ if (namespaceId == myTempNamespace) ++ continue; /* do not look in temp namespace */ ++ + proc = FindDefaultConversion(namespaceId, for_encoding, to_encoding); + if (OidIsValid(proc)) + return proc; +@@ -1477,6 +1559,7 @@ + List *oidlist; + List *newpath; + List *l; ++ bool temp_missing; + Oid firstNS; + MemoryContext oldcxt; + +@@ -1504,6 +1587,7 @@ + * has already been accepted.) Don't make duplicate entries, either. + */ + oidlist = NIL; ++ temp_missing = false; + foreach(l, namelist) + { + char *curname = (char *) lfirst(l); +@@ -1533,6 +1617,21 @@ + oidlist = lappendo(oidlist, namespaceId); + } + } ++ else if (strcmp(curname, "pg_temp") == 0) ++ { ++ /* pg_temp --- substitute temp namespace, if any */ ++ if (OidIsValid(myTempNamespace)) ++ { ++ if (!oidMember(myTempNamespace, oidlist)) ++ oidlist = lappendo(oidlist, myTempNamespace); ++ } ++ else ++ { ++ /* If it ought to be the creation namespace, set flag */ ++ if (oidlist == NIL) ++ temp_missing = true; ++ } ++ } + else + { + /* normal namespace reference */ +@@ -1548,7 +1647,9 @@ + } + + /* +- * Remember the first member of the explicit list. ++ * Remember the first member of the explicit list. (Note: this is ++ * nominally wrong if temp_missing, but we need it anyway to distinguish ++ * explicit from implicit mention of pg_catalog.) + */ + if (oidlist == NIL) + firstNS = InvalidOid; +@@ -1588,9 +1689,16 @@ + */ + firstExplicitNamespace = firstNS; + if (OidIsValid(mySpecialNamespace)) ++ { + defaultCreationNamespace = mySpecialNamespace; ++ /* don't have to create temp in this state */ ++ tempCreationPending = false; ++ } + else ++ { + defaultCreationNamespace = firstNS; ++ tempCreationPending = temp_missing; ++ } + + /* Mark the path valid. */ + namespaceSearchPathValid = true; +@@ -1612,6 +1720,8 @@ + char namespaceName[NAMEDATALEN]; + Oid namespaceId; + ++ Assert(!OidIsValid(myTempNamespace)); ++ + /* + * First, do permission check to see if we are authorized to make temp + * tables. We use a nonstandard error message here since +@@ -1782,9 +1892,9 @@ + { + /* + * Verify that all the names are either valid namespace names or +- * "$user". We do not require $user to correspond to a valid +- * namespace. We do not check for USAGE rights, either; should +- * we? ++ * "$user" or "pg_temp". We do not require $user to correspond to a ++ * valid namespace, and pg_temp might not exist yet. We do not check ++ * for USAGE rights, either; should we? + */ + foreach(l, namelist) + { +@@ -1792,6 +1902,8 @@ + + if (strcmp(curname, "$user") == 0) + continue; ++ if (strcmp(curname, "pg_temp") == 0) ++ continue; + if (!SearchSysCacheExists(NAMESPACENAME, + CStringGetDatum(curname), + 0, 0, 0)) +@@ -1837,6 +1949,7 @@ + MemoryContextSwitchTo(oldcxt); + defaultCreationNamespace = PG_CATALOG_NAMESPACE; + firstExplicitNamespace = PG_CATALOG_NAMESPACE; ++ tempCreationPending = false; + namespaceSearchPathValid = true; + namespaceUser = GetUserId(); + } +@@ -1872,6 +1985,9 @@ + * includeImplicit is true. + * + * NB: caller must treat the list as read-only! ++ * ++ * Note: calling this may result in a CommandCounterIncrement operation, ++ * if we have to create or clean out the temp namespace. + */ + List * + fetch_search_path(bool includeImplicit) +@@ -1880,6 +1996,19 @@ + + recomputeNamespacePath(); + ++ /* ++ * If the temp namespace should be first, force it to exist. This is ++ * so that callers can trust the result to reflect the actual default ++ * creation namespace. It's a bit bogus to do this here, since ++ * current_schema() is supposedly a stable function without side-effects, ++ * but the alternatives seem worse. ++ */ ++ if (tempCreationPending) ++ { ++ InitTempTableNamespace(); ++ recomputeNamespacePath(); ++ } ++ + result = namespaceSearchPath; + if (!includeImplicit) + { +diff -ruN postgresql-7.4.7-old/src/test/regress/expected/temp.out postgresql-7.4.7/src/test/regress/expected/temp.out +--- postgresql-7.4.7-old/src/test/regress/expected/temp.out 2003-09-25 08:58:06.000000000 +0200 ++++ postgresql-7.4.7/src/test/regress/expected/temp.out 2007-04-20 10:46:26.000000000 +0200 +@@ -82,3 +82,61 @@ + -- ON COMMIT is only allowed for TEMP + CREATE TABLE temptest(col int) ON COMMIT DELETE ROWS; + ERROR: ON COMMIT can only be used on temporary tables ++-- Test manipulation of temp schema's placement in search path ++create table public.whereami (f1 text); ++insert into public.whereami values ('public'); ++create temp table whereami (f1 text); ++insert into whereami values ('temp'); ++create function public.whoami() returns text ++ as 'select ''public''::text' language sql; ++create function pg_temp.whoami() returns text ++ as 'select ''temp''::text' language sql; ++-- default should have pg_temp implicitly first, but only for tables ++select * from whereami; ++ f1 ++------ ++ temp ++(1 row) ++ ++select whoami(); ++ whoami ++-------- ++ public ++(1 row) ++ ++-- can list temp first explicitly, but it still doesn't affect functions ++set search_path = pg_temp, public; ++select * from whereami; ++ f1 ++------ ++ temp ++(1 row) ++ ++select whoami(); ++ whoami ++-------- ++ public ++(1 row) ++ ++-- or put it last for security ++set search_path = public, pg_temp; ++select * from whereami; ++ f1 ++-------- ++ public ++(1 row) ++ ++select whoami(); ++ whoami ++-------- ++ public ++(1 row) ++ ++-- you can invoke a temp function explicitly, though ++select pg_temp.whoami(); ++ whoami ++-------- ++ temp ++(1 row) ++ ++drop table public.whereami; +diff -ruN postgresql-7.4.7-old/src/test/regress/sql/temp.sql postgresql-7.4.7/src/test/regress/sql/temp.sql +--- postgresql-7.4.7-old/src/test/regress/sql/temp.sql 2003-05-14 05:26:03.000000000 +0200 ++++ postgresql-7.4.7/src/test/regress/sql/temp.sql 2007-04-20 10:46:26.000000000 +0200 +@@ -83,3 +83,36 @@ + -- ON COMMIT is only allowed for TEMP + + CREATE TABLE temptest(col int) ON COMMIT DELETE ROWS; ++ ++-- Test manipulation of temp schema's placement in search path ++ ++create table public.whereami (f1 text); ++insert into public.whereami values ('public'); ++ ++create temp table whereami (f1 text); ++insert into whereami values ('temp'); ++ ++create function public.whoami() returns text ++ as 'select ''public''::text' language sql; ++ ++create function pg_temp.whoami() returns text ++ as 'select ''temp''::text' language sql; ++ ++-- default should have pg_temp implicitly first, but only for tables ++select * from whereami; ++select whoami(); ++ ++-- can list temp first explicitly, but it still doesn't affect functions ++set search_path = pg_temp, public; ++select * from whereami; ++select whoami(); ++ ++-- or put it last for security ++set search_path = public, pg_temp; ++select * from whereami; ++select whoami(); ++ ++-- you can invoke a temp function explicitly, though ++select pg_temp.whoami(); ++ ++drop table public.whereami; only in patch2: unchanged: --- postgresql-7.4.7.orig/debian/patches/63dblink_restrictions.patch +++ postgresql-7.4.7/debian/patches/63dblink_restrictions.patch @@ -0,0 +1,234 @@ +diff -ruN postgresql-7.4.7-old/contrib/dblink/dblink.c postgresql-7.4.7/contrib/dblink/dblink.c +--- postgresql-7.4.7-old/contrib/dblink/dblink.c 2004-02-24 07:07:34.000000000 +0100 ++++ postgresql-7.4.7/contrib/dblink/dblink.c 2008-01-13 14:55:24.000000000 +0100 +@@ -31,9 +31,12 @@ + */ + #include "postgres.h" + ++#include ++ + #include "libpq-fe.h" + #include "fmgr.h" + #include "funcapi.h" ++#include "miscadmin.h" + #include "access/tupdesc.h" + #include "access/heapam.h" + #include "catalog/catname.h" +@@ -83,6 +86,8 @@ + static HeapTuple get_tuple_of_interest(Oid relid, int16 *pkattnums, int16 pknumatts, char **src_pkattvals); + static Oid get_relid_from_relname(text *relname_text); + static char *generate_relation_name(Oid relid); ++static char *connstr_strip_password(const char *connstr); ++static void dblink_security_check(PGconn *conn, remoteConn *rcon, const char *connstr); + + /* Global */ + List *res_id = NIL; +@@ -157,6 +162,7 @@ + else \ + { \ + connstr = conname_or_str; \ ++ dblink_security_check(conn, rcon, connstr); \ + conn = PQconnectdb(connstr); \ + if (PQstatus(conn) == CONNECTION_BAD) \ + { \ +@@ -197,6 +203,9 @@ + + if (connname) + rcon = (remoteConn *) palloc(sizeof(remoteConn)); ++ ++ /* check password used if not superuser */ ++ dblink_security_check(conn, rcon, connstr); + conn = PQconnectdb(connstr); + + MemoryContextSwitchTo(oldcontext); +@@ -1946,3 +1955,158 @@ + errmsg("undefined connection name"))); + + } ++ ++/* ++ * Modified version of conninfo_parse() from fe-connect.c ++ * Used to remove any password from the connection string ++ * in order to test whether the server auth method will ++ * require it. ++ */ ++static char * ++connstr_strip_password(const char *connstr) ++{ ++ char *pname; ++ char *pval; ++ char *buf; ++ char *cp; ++ char *cp2; ++ StringInfoData result; ++ ++ /* initialize return value */ ++ initStringInfo(&result); ++ ++ /* Need a modifiable copy of the input string */ ++ buf = pstrdup(connstr); ++ cp = buf; ++ ++ while (*cp) ++ { ++ /* Skip blanks before the parameter name */ ++ if (isspace((unsigned char) *cp)) ++ { ++ cp++; ++ continue; ++ } ++ ++ /* Get the parameter name */ ++ pname = cp; ++ while (*cp) ++ { ++ if (*cp == '=') ++ break; ++ if (isspace((unsigned char) *cp)) ++ { ++ *cp++ = '\0'; ++ while (*cp) ++ { ++ if (!isspace((unsigned char) *cp)) ++ break; ++ cp++; ++ } ++ break; ++ } ++ cp++; ++ } ++ ++ /* Check that there is a following '=' */ ++ if (*cp != '=') ++ ereport(ERROR, ++ (errcode(ERRCODE_INVALID_PARAMETER_VALUE), ++ errmsg("missing \"=\" after \"%s\" in connection string", pname))); ++ *cp++ = '\0'; ++ ++ /* Skip blanks after the '=' */ ++ while (*cp) ++ { ++ if (!isspace((unsigned char) *cp)) ++ break; ++ cp++; ++ } ++ ++ /* Get the parameter value */ ++ pval = cp; ++ ++ if (*cp != '\'') ++ { ++ cp2 = pval; ++ while (*cp) ++ { ++ if (isspace((unsigned char) *cp)) ++ { ++ *cp++ = '\0'; ++ break; ++ } ++ if (*cp == '\\') ++ { ++ cp++; ++ if (*cp != '\0') ++ *cp2++ = *cp++; ++ } ++ else ++ *cp2++ = *cp++; ++ } ++ *cp2 = '\0'; ++ } ++ else ++ { ++ cp2 = pval; ++ cp++; ++ for (;;) ++ { ++ if (*cp == '\0') ++ ereport(ERROR, ++ (errcode(ERRCODE_INVALID_PARAMETER_VALUE), ++ errmsg("unterminated quoted string in connection string"))); ++ if (*cp == '\\') ++ { ++ cp++; ++ if (*cp != '\0') ++ *cp2++ = *cp++; ++ continue; ++ } ++ if (*cp == '\'') ++ { ++ *cp2 = '\0'; ++ cp++; ++ break; ++ } ++ *cp2++ = *cp++; ++ } ++ } ++ ++ /* ++ * Now we have the name and the value. If it is not a password, ++ * append to the return connstr. ++ */ ++ if (strcmp("password", pname) != 0) ++ /* append the value */ ++ appendStringInfo(&result, " %s='%s'", pname, pval); ++ } ++ ++ return result.data; ++} ++ ++static void ++dblink_security_check(PGconn *conn, remoteConn *rcon, const char *connstr) ++{ ++ if (!superuser()) ++ { ++ /* this attempt must fail */ ++ conn = PQconnectdb(connstr_strip_password(connstr)); ++ ++ if (PQstatus(conn) == CONNECTION_OK) ++ { ++ PQfinish(conn); ++ if (rcon) ++ pfree(rcon); ++ ++ ereport(ERROR, ++ (errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED), ++ errmsg("password is required"), ++ errdetail("Non-superuser cannot connect if the server does not request a password."), ++ errhint("Target server's authentication method must be changed."))); ++ } ++ else ++ PQfinish(conn); ++ } ++} +diff -ruN postgresql-7.4.7-old/contrib/dblink/dblink.sql.in postgresql-7.4.7/contrib/dblink/dblink.sql.in +--- postgresql-7.4.7-old/contrib/dblink/dblink.sql.in 2003-06-25 03:10:15.000000000 +0200 ++++ postgresql-7.4.7/contrib/dblink/dblink.sql.in 2008-01-13 14:55:24.000000000 +0100 +@@ -1,3 +1,5 @@ ++-- dblink_connect now restricts non-superusers to password ++-- authenticated connections + CREATE OR REPLACE FUNCTION dblink_connect (text) + RETURNS text + AS 'MODULE_PATHNAME','dblink_connect' +@@ -8,6 +10,22 @@ + AS 'MODULE_PATHNAME','dblink_connect' + LANGUAGE 'C' WITH (isstrict); + ++-- dblink_connect_u allows non-superusers to use ++-- non-password authenticated connections, but initially ++-- privileges are revoked from public ++CREATE OR REPLACE FUNCTION dblink_connect_u (text) ++RETURNS text ++AS 'MODULE_PATHNAME','dblink_connect' ++LANGUAGE C STRICT SECURITY DEFINER; ++ ++CREATE OR REPLACE FUNCTION dblink_connect_u (text, text) ++RETURNS text ++AS 'MODULE_PATHNAME','dblink_connect' ++LANGUAGE C STRICT SECURITY DEFINER; ++ ++REVOKE ALL ON FUNCTION dblink_connect_u (text) FROM public; ++REVOKE ALL ON FUNCTION dblink_connect_u (text, text) FROM public; ++ + CREATE OR REPLACE FUNCTION dblink_disconnect () + RETURNS text + AS 'MODULE_PATHNAME','dblink_disconnect'