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