1 /* -------------------------------------------------------------------------
3 * contrib/sepgsql/dml.c
5 * Routines to handle DML permission checks
7 * Copyright (c) 2010-2014, 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)
98 Bitmapset *result = NULL;
103 * obviously, no need to do anything here
105 if (parentId == childId)
108 tmpset = bms_copy(columns);
109 while ((index = bms_first_member(tmpset)) > 0)
111 attno = index + FirstLowInvalidHeapAttributeNumber;
114 * whole-row-reference shall be fixed-up later
116 if (attno == InvalidAttrNumber)
118 result = bms_add_member(result, index);
122 attname = get_attname(parentId, attno);
124 elog(ERROR, "cache lookup failed for attribute %d of relation %u",
126 attno = get_attnum(childId, attname);
127 if (attno == InvalidAttrNumber)
128 elog(ERROR, "cache lookup failed for attribute %s of relation %u",
131 index = attno - FirstLowInvalidHeapAttributeNumber;
132 result = bms_add_member(result, index);
142 * check_relation_privileges
144 * It actually checks required permissions on a certain relation
148 check_relation_privileges(Oid relOid,
152 bool abort_on_violation)
154 ObjectAddress object;
158 char relkind = get_rel_relkind(relOid);
162 * Hardwired Policies: SE-PostgreSQL enforces - clients cannot modify
163 * system catalogs using DMLs - clients cannot reference/modify toast
164 * relations using DMLs
166 if (sepgsql_getenforce() > 0)
168 Oid relnamespace = get_rel_namespace(relOid);
170 if (IsSystemNamespace(relnamespace) &&
171 (required & (SEPG_DB_TABLE__UPDATE |
172 SEPG_DB_TABLE__INSERT |
173 SEPG_DB_TABLE__DELETE)) != 0)
175 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
176 errmsg("SELinux: hardwired security policy violation")));
178 if (relkind == RELKIND_TOASTVALUE)
180 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
181 errmsg("SELinux: hardwired security policy violation")));
185 * Check permissions on the relation
187 object.classId = RelationRelationId;
188 object.objectId = relOid;
189 object.objectSubId = 0;
190 audit_name = getObjectIdentity(&object);
193 case RELKIND_RELATION:
194 result = sepgsql_avc_check_perms(&object,
201 case RELKIND_SEQUENCE:
202 Assert((required & ~SEPG_DB_TABLE__SELECT) == 0);
204 if (required & SEPG_DB_TABLE__SELECT)
205 result = sepgsql_avc_check_perms(&object,
206 SEPG_CLASS_DB_SEQUENCE,
207 SEPG_DB_SEQUENCE__GET_VALUE,
213 result = sepgsql_avc_check_perms(&object,
215 SEPG_DB_VIEW__EXPAND,
221 /* nothing to be checked */
227 * Only columns owned by relations shall be checked
229 if (relkind != RELKIND_RELATION)
233 * Check permissions on the columns
235 selected = fixup_whole_row_references(relOid, selected);
236 modified = fixup_whole_row_references(relOid, modified);
237 columns = bms_union(selected, modified);
239 while ((index = bms_first_member(columns)) >= 0)
242 uint32 column_perms = 0;
244 if (bms_is_member(index, selected))
245 column_perms |= SEPG_DB_COLUMN__SELECT;
246 if (bms_is_member(index, modified))
248 if (required & SEPG_DB_TABLE__UPDATE)
249 column_perms |= SEPG_DB_COLUMN__UPDATE;
250 if (required & SEPG_DB_TABLE__INSERT)
251 column_perms |= SEPG_DB_COLUMN__INSERT;
253 if (column_perms == 0)
256 /* obtain column's permission */
257 attnum = index + FirstLowInvalidHeapAttributeNumber;
259 object.classId = RelationRelationId;
260 object.objectId = relOid;
261 object.objectSubId = attnum;
262 audit_name = getObjectDescription(&object);
264 result = sepgsql_avc_check_perms(&object,
265 SEPG_CLASS_DB_COLUMN,
278 * sepgsql_dml_privileges
280 * Entrypoint of the DML permission checks
283 sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation)
287 foreach(lr, rangeTabls)
289 RangeTblEntry *rte = lfirst(lr);
295 * Only regular relations shall be checked
297 if (rte->rtekind != RTE_RELATION)
301 * Find out required permissions
303 if (rte->requiredPerms & ACL_SELECT)
304 required |= SEPG_DB_TABLE__SELECT;
305 if (rte->requiredPerms & ACL_INSERT)
306 required |= SEPG_DB_TABLE__INSERT;
307 if (rte->requiredPerms & ACL_UPDATE)
309 if (!bms_is_empty(rte->modifiedCols))
310 required |= SEPG_DB_TABLE__UPDATE;
312 required |= SEPG_DB_TABLE__LOCK;
314 if (rte->requiredPerms & ACL_DELETE)
315 required |= SEPG_DB_TABLE__DELETE;
318 * Skip, if nothing to be checked
324 * If this RangeTblEntry is also supposed to reference inherited
325 * tables, we need to check security label of the child tables. So, we
326 * expand rte->relid into list of OIDs of inheritance hierarchy, then
327 * checker routine will be invoked for each relations.
330 tableIds = list_make1_oid(rte->relid);
332 tableIds = find_all_inheritors(rte->relid, NoLock, NULL);
334 foreach(li, tableIds)
336 Oid tableOid = lfirst_oid(li);
337 Bitmapset *selectedCols;
338 Bitmapset *modifiedCols;
341 * child table has different attribute numbers, so we need to fix
344 selectedCols = fixup_inherited_columns(rte->relid, tableOid,
346 modifiedCols = fixup_inherited_columns(rte->relid, tableOid,
350 * check permissions on individual tables
352 if (!check_relation_privileges(tableOid,
355 required, abort_on_violation))