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