PostgreSQL Source Code  git master
dml.c
Go to the documentation of this file.
1 /* -------------------------------------------------------------------------
2  *
3  * contrib/sepgsql/dml.c
4  *
5  * Routines to handle DML permission checks
6  *
7  * Copyright (c) 2010-2018, PostgreSQL Global Development Group
8  *
9  * -------------------------------------------------------------------------
10  */
11 #include "postgres.h"
12 
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.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"
28 
29 #include "sepgsql.h"
30 
31 /*
32  * fixup_whole_row_references
33  *
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.
37  */
38 static Bitmapset *
40 {
41  Bitmapset *result;
42  HeapTuple tuple;
43  AttrNumber natts;
44  AttrNumber attno;
45  int index;
46 
47  /* if no whole of row references, do not anything */
49  if (!bms_is_member(index, columns))
50  return columns;
51 
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);
58 
59  /* fix up the given columns */
60  result = bms_copy(columns);
61  result = bms_del_member(result, index);
62 
63  for (attno = 1; attno <= natts; attno++)
64  {
65  tuple = SearchSysCache2(ATTNUM,
66  ObjectIdGetDatum(relOid),
67  Int16GetDatum(attno));
68  if (!HeapTupleIsValid(tuple))
69  continue;
70 
71  if (((Form_pg_attribute) GETSTRUCT(tuple))->attisdropped)
72  continue;
73 
74  index = attno - FirstLowInvalidHeapAttributeNumber;
75 
76  result = bms_add_member(result, index);
77 
78  ReleaseSysCache(tuple);
79  }
80  return result;
81 }
82 
83 /*
84  * fixup_inherited_columns
85  *
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.
92  */
93 static Bitmapset *
94 fixup_inherited_columns(Oid parentId, Oid childId, Bitmapset *columns)
95 {
96  Bitmapset *result = NULL;
97  int index;
98 
99  /*
100  * obviously, no need to do anything here
101  */
102  if (parentId == childId)
103  return columns;
104 
105  index = -1;
106  while ((index = bms_next_member(columns, index)) >= 0)
107  {
108  /* bit numbers are offset by FirstLowInvalidHeapAttributeNumber */
110  char *attname;
111 
112  /*
113  * whole-row-reference shall be fixed-up later
114  */
115  if (attno == InvalidAttrNumber)
116  {
117  result = bms_add_member(result, index);
118  continue;
119  }
120 
121  attname = get_attname(parentId, attno, false);
122  attno = get_attnum(childId, attname);
123  if (attno == InvalidAttrNumber)
124  elog(ERROR, "cache lookup failed for attribute %s of relation %u",
125  attname, childId);
126 
127  result = bms_add_member(result,
128  attno - FirstLowInvalidHeapAttributeNumber);
129 
130  pfree(attname);
131  }
132 
133  return result;
134 }
135 
136 /*
137  * check_relation_privileges
138  *
139  * It actually checks required permissions on a certain relation
140  * and its columns.
141  */
142 static bool
144  Bitmapset *selected,
145  Bitmapset *inserted,
146  Bitmapset *updated,
148  bool abort_on_violation)
149 {
150  ObjectAddress object;
151  char *audit_name;
152  Bitmapset *columns;
153  int index;
154  char relkind = get_rel_relkind(relOid);
155  bool result = true;
156 
157  /*
158  * Hardwired Policies: SE-PostgreSQL enforces - clients cannot modify
159  * system catalogs using DMLs - clients cannot reference/modify toast
160  * relations using DMLs
161  */
162  if (sepgsql_getenforce() > 0)
163  {
165 
166  if (IsSystemNamespace(relnamespace) &&
167  (required & (SEPG_DB_TABLE__UPDATE |
169  SEPG_DB_TABLE__DELETE)) != 0)
170  ereport(ERROR,
171  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
172  errmsg("SELinux: hardwired security policy violation")));
173 
174  if (relkind == RELKIND_TOASTVALUE)
175  ereport(ERROR,
176  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
177  errmsg("SELinux: hardwired security policy violation")));
178  }
179 
180  /*
181  * Check permissions on the relation
182  */
183  object.classId = RelationRelationId;
184  object.objectId = relOid;
185  object.objectSubId = 0;
186  audit_name = getObjectIdentity(&object);
187  switch (relkind)
188  {
189  case RELKIND_RELATION:
190  case RELKIND_PARTITIONED_TABLE:
191  result = sepgsql_avc_check_perms(&object,
193  required,
194  audit_name,
195  abort_on_violation);
196  break;
197 
198  case RELKIND_SEQUENCE:
199  Assert((required & ~SEPG_DB_TABLE__SELECT) == 0);
200 
201  if (required & SEPG_DB_TABLE__SELECT)
202  result = sepgsql_avc_check_perms(&object,
205  audit_name,
206  abort_on_violation);
207  break;
208 
209  case RELKIND_VIEW:
210  result = sepgsql_avc_check_perms(&object,
213  audit_name,
214  abort_on_violation);
215  break;
216 
217  default:
218  /* nothing to be checked */
219  break;
220  }
221  pfree(audit_name);
222 
223  /*
224  * Only columns owned by relations shall be checked
225  */
226  if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE)
227  return true;
228 
229  /*
230  * Check permissions on the columns
231  */
232  selected = fixup_whole_row_references(relOid, selected);
233  inserted = fixup_whole_row_references(relOid, inserted);
234  updated = fixup_whole_row_references(relOid, updated);
235  columns = bms_union(selected, bms_union(inserted, updated));
236 
237  while ((index = bms_first_member(columns)) >= 0)
238  {
240  uint32 column_perms = 0;
241 
242  if (bms_is_member(index, selected))
243  column_perms |= SEPG_DB_COLUMN__SELECT;
244  if (bms_is_member(index, inserted))
245  {
246  if (required & SEPG_DB_TABLE__INSERT)
247  column_perms |= SEPG_DB_COLUMN__INSERT;
248  }
249  if (bms_is_member(index, updated))
250  {
251  if (required & SEPG_DB_TABLE__UPDATE)
252  column_perms |= SEPG_DB_COLUMN__UPDATE;
253  }
254  if (column_perms == 0)
255  continue;
256 
257  /* obtain column's permission */
258  attnum = index + FirstLowInvalidHeapAttributeNumber;
259 
260  object.classId = RelationRelationId;
261  object.objectId = relOid;
262  object.objectSubId = attnum;
263  audit_name = getObjectDescription(&object);
264 
265  result = sepgsql_avc_check_perms(&object,
267  column_perms,
268  audit_name,
269  abort_on_violation);
270  pfree(audit_name);
271 
272  if (!result)
273  return result;
274  }
275  return true;
276 }
277 
278 /*
279  * sepgsql_dml_privileges
280  *
281  * Entrypoint of the DML permission checks
282  */
283 bool
284 sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation)
285 {
286  ListCell *lr;
287 
288  foreach(lr, rangeTabls)
289  {
290  RangeTblEntry *rte = lfirst(lr);
291  uint32 required = 0;
292  List *tableIds;
293  ListCell *li;
294 
295  /*
296  * Only regular relations shall be checked
297  */
298  if (rte->rtekind != RTE_RELATION)
299  continue;
300 
301  /*
302  * Find out required permissions
303  */
304  if (rte->requiredPerms & ACL_SELECT)
305  required |= SEPG_DB_TABLE__SELECT;
306  if (rte->requiredPerms & ACL_INSERT)
307  required |= SEPG_DB_TABLE__INSERT;
308  if (rte->requiredPerms & ACL_UPDATE)
309  {
310  if (!bms_is_empty(rte->updatedCols))
311  required |= SEPG_DB_TABLE__UPDATE;
312  else
313  required |= SEPG_DB_TABLE__LOCK;
314  }
315  if (rte->requiredPerms & ACL_DELETE)
316  required |= SEPG_DB_TABLE__DELETE;
317 
318  /*
319  * Skip, if nothing to be checked
320  */
321  if (required == 0)
322  continue;
323 
324  /*
325  * If this RangeTblEntry is also supposed to reference inherited
326  * tables, we need to check security label of the child tables. So, we
327  * expand rte->relid into list of OIDs of inheritance hierarchy, then
328  * checker routine will be invoked for each relations.
329  */
330  if (!rte->inh)
331  tableIds = list_make1_oid(rte->relid);
332  else
333  tableIds = find_all_inheritors(rte->relid, NoLock, NULL);
334 
335  foreach(li, tableIds)
336  {
337  Oid tableOid = lfirst_oid(li);
338  Bitmapset *selectedCols;
339  Bitmapset *insertedCols;
340  Bitmapset *updatedCols;
341 
342  /*
343  * child table has different attribute numbers, so we need to fix
344  * up them.
345  */
346  selectedCols = fixup_inherited_columns(rte->relid, tableOid,
347  rte->selectedCols);
348  insertedCols = fixup_inherited_columns(rte->relid, tableOid,
349  rte->insertedCols);
350  updatedCols = fixup_inherited_columns(rte->relid, tableOid,
351  rte->updatedCols);
352 
353  /*
354  * check permissions on individual tables
355  */
356  if (!check_relation_privileges(tableOid,
357  selectedCols,
358  insertedCols,
359  updatedCols,
360  required, abort_on_violation))
361  return false;
362  }
363  list_free(tableIds);
364  }
365  return true;
366 }
#define SEPG_DB_VIEW__EXPAND
Definition: sepgsql.h:212
int bms_first_member(Bitmapset *a)
Definition: bitmapset.c:1023
Oid relnamespace
Definition: pg_class.h:32
#define GETSTRUCT(TUP)
Definition: htup_details.h:673
Bitmapset * bms_copy(const Bitmapset *a)
Definition: bitmapset.c:133
#define SEPG_CLASS_DB_COLUMN
Definition: sepgsql.h:49
static Bitmapset * fixup_whole_row_references(Oid relOid, Bitmapset *columns)
Definition: dml.c:39
static bool check_relation_privileges(Oid relOid, Bitmapset *selected, Bitmapset *inserted, Bitmapset *updated, uint32 required, bool abort_on_violation)
Definition: dml.c:143
char get_rel_relkind(Oid relid)
Definition: lsyscache.c:1805
bool sepgsql_avc_check_perms(const ObjectAddress *tobject, uint16 tclass, uint32 required, const char *audit_name, bool abort_on_violation)
Definition: uavc.c:428
int bms_next_member(const Bitmapset *a, int prevbit)
Definition: bitmapset.c:1075
Oid get_rel_namespace(Oid relid)
Definition: lsyscache.c:1754
#define SEPG_DB_TABLE__DELETE
Definition: sepgsql.h:146
#define Int16GetDatum(X)
Definition: postgres.h:434
static Bitmapset * fixup_inherited_columns(Oid parentId, Oid childId, Bitmapset *columns)
Definition: dml.c:94
int errcode(int sqlerrcode)
Definition: elog.c:575
bool sepgsql_getenforce(void)
Definition: selinux.c:648
#define ACL_DELETE
Definition: parsenodes.h:77
#define SEPG_DB_COLUMN__INSERT
Definition: sepgsql.h:177
#define FirstLowInvalidHeapAttributeNumber
Definition: sysattr.h:28
AclMode requiredPerms
Definition: parsenodes.h:1070
unsigned int Oid
Definition: postgres_ext.h:31
char * getObjectDescription(const ObjectAddress *object)
char relkind
Definition: pg_class.h:51
#define SEPG_DB_TABLE__SELECT
Definition: sepgsql.h:143
Definition: type.h:89
Bitmapset * selectedCols
Definition: parsenodes.h:1072
void pfree(void *pointer)
Definition: mcxt.c:1031
#define SEPG_CLASS_DB_SEQUENCE
Definition: sepgsql.h:47
#define ObjectIdGetDatum(X)
Definition: postgres.h:490
#define ERROR
Definition: elog.h:43
NameData attname
Definition: pg_attribute.h:40
#define SEPG_DB_COLUMN__SELECT
Definition: sepgsql.h:175
#define SEPG_DB_SEQUENCE__GET_VALUE
Definition: sepgsql.h:155
char * getObjectIdentity(const ObjectAddress *object)
#define SEPG_DB_TABLE__UPDATE
Definition: sepgsql.h:144
#define NoLock
Definition: lockdefs.h:34
#define SEPG_DB_TABLE__INSERT
Definition: sepgsql.h:145
AttrNumber get_attnum(Oid relid, const char *attname)
Definition: lsyscache.c:806
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:197
unsigned int uint32
Definition: c.h:325
bool sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation)
Definition: dml.c:284
#define ereport(elevel, rest)
Definition: elog.h:122
#define SEPG_DB_COLUMN__UPDATE
Definition: sepgsql.h:176
int16 relnatts
Definition: pg_class.h:52
bool bms_is_empty(const Bitmapset *a)
Definition: bitmapset.c:729
#define SEPG_CLASS_DB_VIEW
Definition: sepgsql.h:53
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1112
#define ACL_UPDATE
Definition: parsenodes.h:76
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1160
#define ACL_SELECT
Definition: parsenodes.h:75
#define list_make1_oid(x1)
Definition: pg_list.h:151
bool IsSystemNamespace(Oid namespaceId)
Definition: catalog.c:163
int16 attnum
Definition: pg_attribute.h:79
Bitmapset * updatedCols
Definition: parsenodes.h:1074
#define SEPG_CLASS_DB_TABLE
Definition: sepgsql.h:46
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define Assert(condition)
Definition: c.h:699
#define lfirst(lc)
Definition: pg_list.h:106
#define ACL_INSERT
Definition: parsenodes.h:74
Bitmapset * bms_union(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:284
HeapTuple SearchSysCache2(int cacheId, Datum key1, Datum key2)
Definition: syscache.c:1123
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:764
#define InvalidAttrNumber
Definition: attnum.h:23
RTEKind rtekind
Definition: parsenodes.h:962
FormData_pg_class * Form_pg_class
Definition: pg_class.h:92
List * find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
Definition: pg_inherits.c:166
int errmsg(const char *fmt,...)
Definition: elog.c:797
void list_free(List *list)
Definition: list.c:1133
Bitmapset * insertedCols
Definition: parsenodes.h:1073
char * get_attname(Oid relid, AttrNumber attnum, bool missing_ok)
Definition: lsyscache.c:775
#define elog
Definition: elog.h:219
Bitmapset * bms_del_member(Bitmapset *a, int x)
Definition: bitmapset.c:801
Definition: pg_list.h:45
bool bms_is_member(int x, const Bitmapset *a)
Definition: bitmapset.c:486
int16 AttrNumber
Definition: attnum.h:21
#define SEPG_DB_TABLE__LOCK
Definition: sepgsql.h:147
#define lfirst_oid(lc)
Definition: pg_list.h:108