1 /* -------------------------------------------------------------------------
2 *
3 * contrib/sepgsql/proc.c
4 *
5 * Routines corresponding to procedure objects
6 *
7 * Copyright (c) 2010-2017, PostgreSQL Global Development Group
8 *
9 * -------------------------------------------------------------------------
10 */
11 #include "postgres.h"
12
13 #include "access/genam.h"
14 #include "access/heapam.h"
15 #include "access/htup_details.h"
16 #include "access/sysattr.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/syscache.h"
28 #include "utils/tqual.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
sepgsql_proc_post_create(Oid functionId)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 = heap_open(ProcedureRelationId, AccessShareLock);
60
61 ScanKeyInit(&skey,
62 ObjectIdAttributeNumber,
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 heap_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
sepgsql_proc_drop(Oid functionId)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
sepgsql_proc_relabel(Oid functionId,const char * seclabel)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
sepgsql_proc_setattr(Oid functionId)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 = heap_open(ProcedureRelationId, AccessShareLock);
254
255 ScanKeyInit(&skey,
256 ObjectIdAttributeNumber,
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 heap_close(rel, AccessShareLock);
309 }
310
311 /*
312 * sepgsql_proc_execute
313 *
314 * It checks privileges to execute the supplied function
315 */
316 void
sepgsql_proc_execute(Oid functionId)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