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