1 /* -------------------------------------------------------------------------
2  *
3  * contrib/sepgsql/proc.c
4  *
5  * Routines corresponding to procedure objects
6  *
7  * Copyright (c) 2010-2019, PostgreSQL Global Development Group
8  *
9  * -------------------------------------------------------------------------
10  */
11 #include "postgres.h"
12 
13 #include "access/genam.h"
14 #include "access/htup_details.h"
15 #include "access/sysattr.h"
16 #include "access/table.h"
17 #include "catalog/dependency.h"
18 #include "catalog/indexing.h"
19 #include "catalog/pg_namespace.h"
20 #include "catalog/pg_proc.h"
21 #include "catalog/pg_type.h"
22 #include "commands/seclabel.h"
23 #include "lib/stringinfo.h"
24 #include "utils/builtins.h"
25 #include "utils/fmgroids.h"
26 #include "utils/lsyscache.h"
27 #include "utils/snapmgr.h"
28 #include "utils/syscache.h"
29 
30 #include "sepgsql.h"
31 
32 /*
33  * sepgsql_proc_post_create
34  *
35  * This routine assigns a default security label on a newly defined
36  * procedure.
37  */
38 void
39 sepgsql_proc_post_create(Oid functionId)
40 {
41 	Relation	rel;
42 	ScanKeyData skey;
43 	SysScanDesc sscan;
44 	HeapTuple	tuple;
45 	char	   *nsp_name;
46 	char	   *scontext;
47 	char	   *tcontext;
48 	char	   *ncontext;
49 	uint32		required;
50 	int			i;
51 	StringInfoData audit_name;
52 	ObjectAddress object;
53 	Form_pg_proc proForm;
54 
55 	/*
56 	 * Fetch namespace of the new procedure. Because pg_proc entry is not
57 	 * visible right now, we need to scan the catalog using SnapshotSelf.
58 	 */
59 	rel = table_open(ProcedureRelationId, AccessShareLock);
60 
61 	ScanKeyInit(&skey,
62 				Anum_pg_proc_oid,
63 				BTEqualStrategyNumber, F_OIDEQ,
64 				ObjectIdGetDatum(functionId));
65 
66 	sscan = systable_beginscan(rel, ProcedureOidIndexId, true,
67 							   SnapshotSelf, 1, &skey);
68 
69 	tuple = systable_getnext(sscan);
70 	if (!HeapTupleIsValid(tuple))
71 		elog(ERROR, "could not find tuple for function %u", functionId);
72 
73 	proForm = (Form_pg_proc) GETSTRUCT(tuple);
74 
75 	/*
76 	 * check db_schema:{add_name} permission of the namespace
77 	 */
78 	object.classId = NamespaceRelationId;
79 	object.objectId = proForm->pronamespace;
80 	object.objectSubId = 0;
81 	sepgsql_avc_check_perms(&object,
82 							SEPG_CLASS_DB_SCHEMA,
83 							SEPG_DB_SCHEMA__ADD_NAME,
84 							getObjectIdentity(&object),
85 							true);
86 
87 	/*
88 	 * XXX - db_language:{implement} also should be checked here
89 	 */
90 
91 
92 	/*
93 	 * Compute a default security label when we create a new procedure object
94 	 * under the specified namespace.
95 	 */
96 	scontext = sepgsql_get_client_label();
97 	tcontext = sepgsql_get_label(NamespaceRelationId,
98 								 proForm->pronamespace, 0);
99 	ncontext = sepgsql_compute_create(scontext, tcontext,
100 									  SEPG_CLASS_DB_PROCEDURE,
101 									  NameStr(proForm->proname));
102 
103 	/*
104 	 * check db_procedure:{create (install)} permission
105 	 */
106 	initStringInfo(&audit_name);
107 	nsp_name = get_namespace_name(proForm->pronamespace);
108 	appendStringInfo(&audit_name, "%s(",
109 					 quote_qualified_identifier(nsp_name, NameStr(proForm->proname)));
110 	for (i = 0; i < proForm->pronargs; i++)
111 	{
112 		if (i > 0)
113 			appendStringInfoChar(&audit_name, ',');
114 
115 		object.classId = TypeRelationId;
116 		object.objectId = proForm->proargtypes.values[i];
117 		object.objectSubId = 0;
118 		appendStringInfoString(&audit_name, getObjectIdentity(&object));
119 	}
120 	appendStringInfoChar(&audit_name, ')');
121 
122 	required = SEPG_DB_PROCEDURE__CREATE;
123 	if (proForm->proleakproof)
124 		required |= SEPG_DB_PROCEDURE__INSTALL;
125 
126 	sepgsql_avc_check_perms_label(ncontext,
127 								  SEPG_CLASS_DB_PROCEDURE,
128 								  required,
129 								  audit_name.data,
130 								  true);
131 
132 	/*
133 	 * Assign the default security label on a new procedure
134 	 */
135 	object.classId = ProcedureRelationId;
136 	object.objectId = functionId;
137 	object.objectSubId = 0;
138 	SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, ncontext);
139 
140 	/*
141 	 * Cleanup
142 	 */
143 	systable_endscan(sscan);
144 	table_close(rel, AccessShareLock);
145 
146 	pfree(audit_name.data);
147 	pfree(tcontext);
148 	pfree(ncontext);
149 }
150 
151 /*
152  * sepgsql_proc_drop
153  *
154  * It checks privileges to drop the supplied function.
155  */
156 void
157 sepgsql_proc_drop(Oid functionId)
158 {
159 	ObjectAddress object;
160 	char	   *audit_name;
161 
162 	/*
163 	 * check db_schema:{remove_name} permission
164 	 */
165 	object.classId = NamespaceRelationId;
166 	object.objectId = get_func_namespace(functionId);
167 	object.objectSubId = 0;
168 	audit_name = getObjectIdentity(&object);
169 
170 	sepgsql_avc_check_perms(&object,
171 							SEPG_CLASS_DB_SCHEMA,
172 							SEPG_DB_SCHEMA__REMOVE_NAME,
173 							audit_name,
174 							true);
175 	pfree(audit_name);
176 
177 	/*
178 	 * check db_procedure:{drop} permission
179 	 */
180 	object.classId = ProcedureRelationId;
181 	object.objectId = functionId;
182 	object.objectSubId = 0;
183 	audit_name = getObjectIdentity(&object);
184 
185 	sepgsql_avc_check_perms(&object,
186 							SEPG_CLASS_DB_PROCEDURE,
187 							SEPG_DB_PROCEDURE__DROP,
188 							audit_name,
189 							true);
190 	pfree(audit_name);
191 }
192 
193 /*
194  * sepgsql_proc_relabel
195  *
196  * It checks privileges to relabel the supplied function
197  * by the `seclabel'.
198  */
199 void
200 sepgsql_proc_relabel(Oid functionId, const char *seclabel)
201 {
202 	ObjectAddress object;
203 	char	   *audit_name;
204 
205 	object.classId = ProcedureRelationId;
206 	object.objectId = functionId;
207 	object.objectSubId = 0;
208 	audit_name = getObjectIdentity(&object);
209 
210 	/*
211 	 * check db_procedure:{setattr relabelfrom} permission
212 	 */
213 	sepgsql_avc_check_perms(&object,
214 							SEPG_CLASS_DB_PROCEDURE,
215 							SEPG_DB_PROCEDURE__SETATTR |
216 							SEPG_DB_PROCEDURE__RELABELFROM,
217 							audit_name,
218 							true);
219 
220 	/*
221 	 * check db_procedure:{relabelto} permission
222 	 */
223 	sepgsql_avc_check_perms_label(seclabel,
224 								  SEPG_CLASS_DB_PROCEDURE,
225 								  SEPG_DB_PROCEDURE__RELABELTO,
226 								  audit_name,
227 								  true);
228 	pfree(audit_name);
229 }
230 
231 /*
232  * sepgsql_proc_setattr
233  *
234  * It checks privileges to alter the supplied function.
235  */
236 void
237 sepgsql_proc_setattr(Oid functionId)
238 {
239 	Relation	rel;
240 	ScanKeyData skey;
241 	SysScanDesc sscan;
242 	HeapTuple	oldtup;
243 	HeapTuple	newtup;
244 	Form_pg_proc oldform;
245 	Form_pg_proc newform;
246 	uint32		required;
247 	ObjectAddress object;
248 	char	   *audit_name;
249 
250 	/*
251 	 * Fetch newer catalog
252 	 */
253 	rel = table_open(ProcedureRelationId, AccessShareLock);
254 
255 	ScanKeyInit(&skey,
256 				Anum_pg_proc_oid,
257 				BTEqualStrategyNumber, F_OIDEQ,
258 				ObjectIdGetDatum(functionId));
259 
260 	sscan = systable_beginscan(rel, ProcedureOidIndexId, true,
261 							   SnapshotSelf, 1, &skey);
262 	newtup = systable_getnext(sscan);
263 	if (!HeapTupleIsValid(newtup))
264 		elog(ERROR, "could not find tuple for function %u", functionId);
265 	newform = (Form_pg_proc) GETSTRUCT(newtup);
266 
267 	/*
268 	 * Fetch older catalog
269 	 */
270 	oldtup = SearchSysCache1(PROCOID, ObjectIdGetDatum(functionId));
271 	if (!HeapTupleIsValid(oldtup))
272 		elog(ERROR, "cache lookup failed for function %u", functionId);
273 	oldform = (Form_pg_proc) GETSTRUCT(oldtup);
274 
275 	/*
276 	 * Does this ALTER command takes operation to namespace?
277 	 */
278 	if (newform->pronamespace != oldform->pronamespace)
279 	{
280 		sepgsql_schema_remove_name(oldform->pronamespace);
281 		sepgsql_schema_add_name(oldform->pronamespace);
282 	}
283 	if (strcmp(NameStr(newform->proname), NameStr(oldform->proname)) != 0)
284 		sepgsql_schema_rename(oldform->pronamespace);
285 
286 	/*
287 	 * check db_procedure:{setattr (install)} permission
288 	 */
289 	required = SEPG_DB_PROCEDURE__SETATTR;
290 	if (!oldform->proleakproof && newform->proleakproof)
291 		required |= SEPG_DB_PROCEDURE__INSTALL;
292 
293 	object.classId = ProcedureRelationId;
294 	object.objectId = functionId;
295 	object.objectSubId = 0;
296 	audit_name = getObjectIdentity(&object);
297 
298 	sepgsql_avc_check_perms(&object,
299 							SEPG_CLASS_DB_PROCEDURE,
300 							required,
301 							audit_name,
302 							true);
303 	/* cleanups */
304 	pfree(audit_name);
305 
306 	ReleaseSysCache(oldtup);
307 	systable_endscan(sscan);
308 	table_close(rel, AccessShareLock);
309 }
310 
311 /*
312  * sepgsql_proc_execute
313  *
314  * It checks privileges to execute the supplied function
315  */
316 void
317 sepgsql_proc_execute(Oid functionId)
318 {
319 	ObjectAddress object;
320 	char	   *audit_name;
321 
322 	/*
323 	 * check db_procedure:{execute} permission
324 	 */
325 	object.classId = ProcedureRelationId;
326 	object.objectId = functionId;
327 	object.objectSubId = 0;
328 	audit_name = getObjectIdentity(&object);
329 	sepgsql_avc_check_perms(&object,
330 							SEPG_CLASS_DB_PROCEDURE,
331 							SEPG_DB_PROCEDURE__EXECUTE,
332 							audit_name,
333 							true);
334 	pfree(audit_name);
335 }
336