1 /* -------------------------------------------------------------------------
3 * contrib/sepgsql/dml.c
5 * Routines to handle DML permission checks
7 * Copyright (c) 2010-2015, PostgreSQL Global Development Group
9 * -------------------------------------------------------------------------
13 #include "access/htup_details.h"
14 #include "access/sysattr.h"
15 #include "access/tupdesc.h"
16 #include "catalog/catalog.h"
17 #include "catalog/heap.h"
18 #include "catalog/dependency.h"
19 #include "catalog/pg_attribute.h"
20 #include "catalog/pg_class.h"
21 #include "catalog/pg_inherits_fn.h"
22 #include "commands/seclabel.h"
23 #include "commands/tablecmds.h"
24 #include "executor/executor.h"
25 #include "nodes/bitmapset.h"
26 #include "utils/lsyscache.h"
27 #include "utils/syscache.h"
32 * fixup_whole_row_references
34 * When user reference a whole of row, it is equivalent to reference to
35 * all the user columns (not system columns). So, we need to fix up the
36 * given bitmapset, if it contains a whole of the row reference.
39 fixup_whole_row_references(Oid relOid, Bitmapset *columns)
47 /* if no whole of row references, do not anything */
48 index = InvalidAttrNumber - FirstLowInvalidHeapAttributeNumber;
49 if (!bms_is_member(index, columns))
52 /* obtain number of attributes */
53 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
54 if (!HeapTupleIsValid(tuple))
55 elog(ERROR, "cache lookup failed for relation %u", relOid);
56 natts = ((Form_pg_class) GETSTRUCT(tuple))->relnatts;
57 ReleaseSysCache(tuple);
59 /* fix up the given columns */
60 result = bms_copy(columns);
61 result = bms_del_member(result, index);
63 for (attno = 1; attno <= natts; attno++)
65 tuple = SearchSysCache2(ATTNUM,
66 ObjectIdGetDatum(relOid),
67 Int16GetDatum(attno));
68 if (!HeapTupleIsValid(tuple))
71 if (((Form_pg_attribute) GETSTRUCT(tuple))->attisdropped)
74 index = attno - FirstLowInvalidHeapAttributeNumber;
76 result = bms_add_member(result, index);
78 ReleaseSysCache(tuple);
84 * fixup_inherited_columns
86 * When user is querying on a table with children, it implicitly accesses
87 * child tables also. So, we also need to check security label of child
88 * tables and columns, but here is no guarantee attribute numbers are
89 * same between the parent ans children.
90 * It returns a bitmapset which contains attribute number of the child
91 * table based on the given bitmapset of the parent.
94 fixup_inherited_columns(Oid parentId, Oid childId, Bitmapset *columns)
96 Bitmapset *result = NULL;
100 * obviously, no need to do anything here
102 if (parentId == childId)
106 while ((index = bms_next_member(columns, index)) >= 0)
108 /* bit numbers are offset by FirstLowInvalidHeapAttributeNumber */
109 AttrNumber attno = index + FirstLowInvalidHeapAttributeNumber;
113 * whole-row-reference shall be fixed-up later
115 if (attno == InvalidAttrNumber)
117 result = bms_add_member(result, index);
121 attname = get_attname(parentId, attno);
123 elog(ERROR, "cache lookup failed for attribute %d of relation %u",
125 attno = get_attnum(childId, attname);
126 if (attno == InvalidAttrNumber)
127 elog(ERROR, "cache lookup failed for attribute %s of relation %u",
130 result = bms_add_member(result,
131 attno - FirstLowInvalidHeapAttributeNumber);
140 * check_relation_privileges
142 * It actually checks required permissions on a certain relation
146 check_relation_privileges(Oid relOid,
150 bool abort_on_violation)
152 ObjectAddress object;
156 char relkind = get_rel_relkind(relOid);
160 * Hardwired Policies: SE-PostgreSQL enforces - clients cannot modify
161 * system catalogs using DMLs - clients cannot reference/modify toast
162 * relations using DMLs
164 if (sepgsql_getenforce() > 0)
166 Oid relnamespace = get_rel_namespace(relOid);
168 if (IsSystemNamespace(relnamespace) &&
169 (required & (SEPG_DB_TABLE__UPDATE |
170 SEPG_DB_TABLE__INSERT |
171 SEPG_DB_TABLE__DELETE)) != 0)
173 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
174 errmsg("SELinux: hardwired security policy violation")));
176 if (relkind == RELKIND_TOASTVALUE)
178 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
179 errmsg("SELinux: hardwired security policy violation")));
183 * Check permissions on the relation
185 object.classId = RelationRelationId;
186 object.objectId = relOid;
187 object.objectSubId = 0;
188 audit_name = getObjectIdentity(&object);
191 case RELKIND_RELATION:
192 result = sepgsql_avc_check_perms(&object,
199 case RELKIND_SEQUENCE:
200 Assert((required & ~SEPG_DB_TABLE__SELECT) == 0);
202 if (required & SEPG_DB_TABLE__SELECT)
203 result = sepgsql_avc_check_perms(&object,
204 SEPG_CLASS_DB_SEQUENCE,
205 SEPG_DB_SEQUENCE__GET_VALUE,
211 result = sepgsql_avc_check_perms(&object,
213 SEPG_DB_VIEW__EXPAND,
219 /* nothing to be checked */
225 * Only columns owned by relations shall be checked
227 if (relkind != RELKIND_RELATION)
231 * Check permissions on the columns
233 selected = fixup_whole_row_references(relOid, selected);
234 modified = fixup_whole_row_references(relOid, modified);
235 columns = bms_union(selected, modified);
237 while ((index = bms_first_member(columns)) >= 0)
240 uint32 column_perms = 0;
242 if (bms_is_member(index, selected))
243 column_perms |= SEPG_DB_COLUMN__SELECT;
244 if (bms_is_member(index, modified))
246 if (required & SEPG_DB_TABLE__UPDATE)
247 column_perms |= SEPG_DB_COLUMN__UPDATE;
248 if (required & SEPG_DB_TABLE__INSERT)
249 column_perms |= SEPG_DB_COLUMN__INSERT;
251 if (column_perms == 0)
254 /* obtain column's permission */
255 attnum = index + FirstLowInvalidHeapAttributeNumber;
257 object.classId = RelationRelationId;
258 object.objectId = relOid;
259 object.objectSubId = attnum;
260 audit_name = getObjectDescription(&object);
262 result = sepgsql_avc_check_perms(&object,
263 SEPG_CLASS_DB_COLUMN,
276 * sepgsql_dml_privileges
278 * Entrypoint of the DML permission checks
281 sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation)
285 foreach(lr, rangeTabls)
287 RangeTblEntry *rte = lfirst(lr);
293 * Only regular relations shall be checked
295 if (rte->rtekind != RTE_RELATION)
299 * Find out required permissions
301 if (rte->requiredPerms & ACL_SELECT)
302 required |= SEPG_DB_TABLE__SELECT;
303 if (rte->requiredPerms & ACL_INSERT)
304 required |= SEPG_DB_TABLE__INSERT;
305 if (rte->requiredPerms & ACL_UPDATE)
307 if (!bms_is_empty(rte->modifiedCols))
308 required |= SEPG_DB_TABLE__UPDATE;
310 required |= SEPG_DB_TABLE__LOCK;
312 if (rte->requiredPerms & ACL_DELETE)
313 required |= SEPG_DB_TABLE__DELETE;
316 * Skip, if nothing to be checked
322 * If this RangeTblEntry is also supposed to reference inherited
323 * tables, we need to check security label of the child tables. So, we
324 * expand rte->relid into list of OIDs of inheritance hierarchy, then
325 * checker routine will be invoked for each relations.
328 tableIds = list_make1_oid(rte->relid);
330 tableIds = find_all_inheritors(rte->relid, NoLock, NULL);
332 foreach(li, tableIds)
334 Oid tableOid = lfirst_oid(li);
335 Bitmapset *selectedCols;
336 Bitmapset *modifiedCols;
339 * child table has different attribute numbers, so we need to fix
342 selectedCols = fixup_inherited_columns(rte->relid, tableOid,
344 modifiedCols = fixup_inherited_columns(rte->relid, tableOid,
348 * check permissions on individual tables
350 if (!check_relation_privileges(tableOid,
353 required, abort_on_violation))