aboutsummaryrefslogtreecommitdiffstats
path: root/meta-oe/recipes-support/postgresql/files/0004-Prevent-privilege-escalation-in-explicit-calls-to-PL.patch
blob: cc2183a2ab2778f74b98d056f9e3b357e24daa33 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
From 1d701d28a796ea2d1a4d2be9e9ee06209eaea040 Mon Sep 17 00:00:00 2001
From: Noah Misch <noah@leadboat.com>
Date: Mon, 17 Feb 2014 09:33:31 -0500
Subject: [PATCH] Prevent privilege escalation in explicit calls to PL
 validators.

commit 1d701d28a796ea2d1a4d2be9e9ee06209eaea040 REL9_2_STABLE

The primary role of PL validators is to be called implicitly during
CREATE FUNCTION, but they are also normal functions that a user can call
explicitly.  Add a permissions check to each validator to ensure that a
user cannot use explicit validator calls to achieve things he could not
otherwise achieve.  Back-patch to 8.4 (all supported versions).
Non-core procedural language extensions ought to make the same two-line
change to their own validators.

Andres Freund, reviewed by Tom Lane and Noah Misch.

Security: CVE-2014-0061

Upstream-Status: Backport
Signed-off-by: Kai Kang <kai.kang@windriver.com>
---
 doc/src/sgml/plhandler.sgml         |    5 ++-
 src/backend/catalog/pg_proc.c       |    9 ++++
 src/backend/commands/functioncmds.c |    1 -
 src/backend/utils/fmgr/fmgr.c       |   84 +++++++++++++++++++++++++++++++++++
 src/include/fmgr.h                  |    1 +
 src/pl/plperl/plperl.c              |    4 ++
 src/pl/plpgsql/src/pl_handler.c     |    3 +
 src/pl/plpython/plpy_main.c         |    4 ++
 8 files changed, 109 insertions(+), 2 deletions(-)

diff --git a/doc/src/sgml/plhandler.sgml b/doc/src/sgml/plhandler.sgml
index 024ef9d..aa4bba3 100644
--- a/doc/src/sgml/plhandler.sgml
+++ b/doc/src/sgml/plhandler.sgml
@@ -178,7 +178,10 @@ CREATE LANGUAGE plsample
     or updated a function written in the procedural language.
     The passed-in OID is the OID of the function's <classname>pg_proc</>
     row.  The validator must fetch this row in the usual way, and do
-    whatever checking is appropriate.  Typical checks include verifying
+    whatever checking is appropriate.
+    First, call <function>CheckFunctionValidatorAccess()</> to diagnose
+    explicit calls to the validator that the user could not achieve through
+    <command>CREATE FUNCTION</>.  Typical checks then include verifying
     that the function's argument and result types are supported by the
     language, and that the function's body is syntactically correct
     in the language.  If the validator finds the function to be okay,
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index 3812408..3124868 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -718,6 +718,9 @@ fmgr_internal_validator(PG_FUNCTION_ARGS)
 	Datum		tmp;
 	char	   *prosrc;
 
+	if (!CheckFunctionValidatorAccess(fcinfo->flinfo->fn_oid, funcoid))
+		PG_RETURN_VOID();
+
 	/*
 	 * We do not honor check_function_bodies since it's unlikely the function
 	 * name will be found later if it isn't there now.
@@ -763,6 +766,9 @@ fmgr_c_validator(PG_FUNCTION_ARGS)
 	char	   *prosrc;
 	char	   *probin;
 
+	if (!CheckFunctionValidatorAccess(fcinfo->flinfo->fn_oid, funcoid))
+		PG_RETURN_VOID();
+
 	/*
 	 * It'd be most consistent to skip the check if !check_function_bodies,
 	 * but the purpose of that switch is to be helpful for pg_dump loading,
@@ -814,6 +820,9 @@ fmgr_sql_validator(PG_FUNCTION_ARGS)
 	bool		haspolyarg;
 	int			i;
 
+	if (!CheckFunctionValidatorAccess(fcinfo->flinfo->fn_oid, funcoid))
+		PG_RETURN_VOID();
+
 	tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcoid));
 	if (!HeapTupleIsValid(tuple))
 		elog(ERROR, "cache lookup failed for function %u", funcoid);
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 9ba6dd8..ea74b5e 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -997,7 +997,6 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString)
 					prorows);
 }
 
-
 /*
  * Guts of function deletion.
  *
diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c
index 2ec63fa..8d6f183 100644
--- a/src/backend/utils/fmgr/fmgr.c
+++ b/src/backend/utils/fmgr/fmgr.c
@@ -24,6 +24,7 @@
 #include "miscadmin.h"
 #include "nodes/nodeFuncs.h"
 #include "pgstat.h"
+#include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/fmgrtab.h"
 #include "utils/guc.h"
@@ -2445,3 +2446,86 @@ get_call_expr_arg_stable(Node *expr, int argnum)
 
 	return false;
 }
+
+/*-------------------------------------------------------------------------
+ *		Support routines for procedural language implementations
+ *-------------------------------------------------------------------------
+ */
+
+/*
+ * Verify that a validator is actually associated with the language of a
+ * particular function and that the user has access to both the language and
+ * the function.  All validators should call this before doing anything
+ * substantial.  Doing so ensures a user cannot achieve anything with explicit
+ * calls to validators that he could not achieve with CREATE FUNCTION or by
+ * simply calling an existing function.
+ *
+ * When this function returns false, callers should skip all validation work
+ * and call PG_RETURN_VOID().  This never happens at present; it is reserved
+ * for future expansion.
+ *
+ * In particular, checking that the validator corresponds to the function's
+ * language allows untrusted language validators to assume they process only
+ * superuser-chosen source code.  (Untrusted language call handlers, by
+ * definition, do assume that.)  A user lacking the USAGE language privilege
+ * would be unable to reach the validator through CREATE FUNCTION, so we check
+ * that to block explicit calls as well.  Checking the EXECUTE privilege on
+ * the function is often superfluous, because most users can clone the
+ * function to get an executable copy.  It is meaningful against users with no
+ * database TEMP right and no permanent schema CREATE right, thereby unable to
+ * create any function.  Also, if the function tracks persistent state by
+ * function OID or name, validating the original function might permit more
+ * mischief than creating and validating a clone thereof.
+ */
+bool
+CheckFunctionValidatorAccess(Oid validatorOid, Oid functionOid)
+{
+	HeapTuple	procTup;
+	HeapTuple	langTup;
+	Form_pg_proc procStruct;
+	Form_pg_language langStruct;
+	AclResult	aclresult;
+
+	/* Get the function's pg_proc entry */
+	procTup = SearchSysCache1(PROCOID, ObjectIdGetDatum(functionOid));
+	if (!HeapTupleIsValid(procTup))
+		elog(ERROR, "cache lookup failed for function %u", functionOid);
+	procStruct = (Form_pg_proc) GETSTRUCT(procTup);
+
+	/*
+	 * Fetch pg_language entry to know if this is the correct validation
+	 * function for that pg_proc entry.
+	 */
+	langTup = SearchSysCache1(LANGOID, ObjectIdGetDatum(procStruct->prolang));
+	if (!HeapTupleIsValid(langTup))
+		elog(ERROR, "cache lookup failed for language %u", procStruct->prolang);
+	langStruct = (Form_pg_language) GETSTRUCT(langTup);
+
+	if (langStruct->lanvalidator != validatorOid)
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("language validation function %u called for language %u instead of %u",
+						validatorOid, procStruct->prolang,
+						langStruct->lanvalidator)));
+
+	/* first validate that we have permissions to use the language */
+	aclresult = pg_language_aclcheck(procStruct->prolang, GetUserId(),
+									 ACL_USAGE);
+	if (aclresult != ACLCHECK_OK)
+		aclcheck_error(aclresult, ACL_KIND_LANGUAGE,
+					   NameStr(langStruct->lanname));
+
+	/*
+	 * Check whether we are allowed to execute the function itself. If we can
+	 * execute it, there should be no possible side-effect of
+	 * compiling/validation that execution can't have.
+	 */
+	aclresult = pg_proc_aclcheck(functionOid, GetUserId(), ACL_EXECUTE);
+	if (aclresult != ACLCHECK_OK)
+		aclcheck_error(aclresult, ACL_KIND_PROC, NameStr(procStruct->proname));
+
+	ReleaseSysCache(procTup);
+	ReleaseSysCache(langTup);
+
+	return true;
+}
diff --git a/src/include/fmgr.h b/src/include/fmgr.h
index 0a25776..f944cc6 100644
--- a/src/include/fmgr.h
+++ b/src/include/fmgr.h
@@ -624,6 +624,7 @@ extern Oid	get_fn_expr_argtype(FmgrInfo *flinfo, int argnum);
 extern Oid	get_call_expr_argtype(fmNodePtr expr, int argnum);
 extern bool get_fn_expr_arg_stable(FmgrInfo *flinfo, int argnum);
 extern bool get_call_expr_arg_stable(fmNodePtr expr, int argnum);
+extern bool CheckFunctionValidatorAccess(Oid validatorOid, Oid functionOid);
 
 /*
  * Routines in dfmgr.c
diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c
index 7c2aee9..49d50c4 100644
--- a/src/pl/plperl/plperl.c
+++ b/src/pl/plperl/plperl.c
@@ -1847,6 +1847,9 @@ plperl_validator(PG_FUNCTION_ARGS)
 	bool		istrigger = false;
 	int			i;
 
+	if (!CheckFunctionValidatorAccess(fcinfo->flinfo->fn_oid, funcoid))
+		PG_RETURN_VOID();
+
 	/* Get the new function's pg_proc entry */
 	tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcoid));
 	if (!HeapTupleIsValid(tuple))
@@ -1926,6 +1929,7 @@ PG_FUNCTION_INFO_V1(plperlu_validator);
 Datum
 plperlu_validator(PG_FUNCTION_ARGS)
 {
+	/* call plperl validator with our fcinfo so it gets our oid */
 	return plperl_validator(fcinfo);
 }
 
diff --git a/src/pl/plpgsql/src/pl_handler.c b/src/pl/plpgsql/src/pl_handler.c
index 022ec3f..00b1a6f 100644
--- a/src/pl/plpgsql/src/pl_handler.c
+++ b/src/pl/plpgsql/src/pl_handler.c
@@ -227,6 +227,9 @@ plpgsql_validator(PG_FUNCTION_ARGS)
 	bool		istrigger = false;
 	int			i;
 
+	if (!CheckFunctionValidatorAccess(fcinfo->flinfo->fn_oid, funcoid))
+		PG_RETURN_VOID();
+
 	/* Get the new function's pg_proc entry */
 	tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcoid));
 	if (!HeapTupleIsValid(tuple))
diff --git a/src/pl/plpython/plpy_main.c b/src/pl/plpython/plpy_main.c
index c4de762..3847847 100644
--- a/src/pl/plpython/plpy_main.c
+++ b/src/pl/plpython/plpy_main.c
@@ -159,6 +159,9 @@ plpython_validator(PG_FUNCTION_ARGS)
 	Form_pg_proc procStruct;
 	bool		is_trigger;
 
+	if (!CheckFunctionValidatorAccess(fcinfo->flinfo->fn_oid, funcoid))
+		PG_RETURN_VOID();
+
 	if (!check_function_bodies)
 	{
 		PG_RETURN_VOID();
@@ -184,6 +187,7 @@ plpython_validator(PG_FUNCTION_ARGS)
 Datum
 plpython2_validator(PG_FUNCTION_ARGS)
 {
+	/* call plpython validator with our fcinfo so it gets our oid */
 	return plpython_validator(fcinfo);
 }
 #endif   /* PY_MAJOR_VERSION < 3 */
-- 
1.7.5.4