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-2020, 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/dependency.h"
18 #include "catalog/heap.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 "sepgsql.h"
27 #include "utils/lsyscache.h"
28 #include "utils/syscache.h"
29 
30 /*
31  * fixup_whole_row_references
32  *
33  * When user references a whole-row Var, it is equivalent to referencing
34  * all the user columns (not system columns). So, we need to fix up the
35  * given bitmapset, if it contains a whole-row reference.
36  */
37 static Bitmapset *
39 {
40  Bitmapset *result;
41  HeapTuple tuple;
42  AttrNumber natts;
43  AttrNumber attno;
44  int index;
45 
46  /* if no whole-row references, nothing to do */
48  if (!bms_is_member(index, columns))
49  return columns;
50 
51  /* obtain number of attributes */
52  tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
53  if (!HeapTupleIsValid(tuple))
54  elog(ERROR, "cache lookup failed for relation %u", relOid);
55  natts = ((Form_pg_class) GETSTRUCT(tuple))->relnatts;
56  ReleaseSysCache(tuple);
57 
58  /* remove bit 0 from column set, add in all the non-dropped columns */
59  result = bms_copy(columns);
60  result = bms_del_member(result, index);
61 
62  for (attno = 1; attno <= natts; attno++)
63  {
64  tuple = SearchSysCache2(ATTNUM,
65  ObjectIdGetDatum(relOid),
66  Int16GetDatum(attno));
67  if (!HeapTupleIsValid(tuple))
68  continue; /* unexpected case, should we error? */
69 
70  if (!((Form_pg_attribute) GETSTRUCT(tuple))->attisdropped)
71  {
72  index = attno - FirstLowInvalidHeapAttributeNumber;
73  result = bms_add_member(result, index);
74  }
75 
76  ReleaseSysCache(tuple);
77  }
78  return result;
79 }
80 
81 /*
82  * fixup_inherited_columns
83  *
84  * When user is querying on a table with children, it implicitly accesses
85  * child tables also. So, we also need to check security label of child
86  * tables and columns, but here is no guarantee attribute numbers are
87  * same between the parent and children.
88  * It returns a bitmapset which contains attribute number of the child
89  * table based on the given bitmapset of the parent.
90  */
91 static Bitmapset *
92 fixup_inherited_columns(Oid parentId, Oid childId, Bitmapset *columns)
93 {
94  Bitmapset *result = NULL;
95  int index;
96 
97  /*
98  * obviously, no need to do anything here
99  */
100  if (parentId == childId)
101  return columns;
102 
103  index = -1;
104  while ((index = bms_next_member(columns, index)) >= 0)
105  {
106  /* bit numbers are offset by FirstLowInvalidHeapAttributeNumber */
108  char *attname;
109 
110  /*
111  * whole-row-reference shall be fixed-up later
112  */
113  if (attno == InvalidAttrNumber)
114  {
115  result = bms_add_member(result, index);
116  continue;
117  }
118 
119  attname = get_attname(parentId, attno, false);
120  attno = get_attnum(childId, attname);
121  if (attno == InvalidAttrNumber)
122  elog(ERROR, "cache lookup failed for attribute %s of relation %u",
123  attname, childId);
124 
125  result = bms_add_member(result,
126  attno - FirstLowInvalidHeapAttributeNumber);
127 
128  pfree(attname);
129  }
130 
131  return result;
132 }
133 
134 /*
135  * check_relation_privileges
136  *
137  * It actually checks required permissions on a certain relation
138  * and its columns.
139  */
140 static bool
142  Bitmapset *selected,
143  Bitmapset *inserted,
144  Bitmapset *updated,
146  bool abort_on_violation)
147 {
148  ObjectAddress object;
149  char *audit_name;
150  Bitmapset *columns;
151  int index;
152  char relkind = get_rel_relkind(relOid);
153  bool result = true;
154 
155  /*
156  * Hardwired Policies: SE-PostgreSQL enforces - clients cannot modify
157  * system catalogs using DMLs - clients cannot reference/modify toast
158  * relations using DMLs
159  */
160  if (sepgsql_getenforce() > 0)
161  {
162  if ((required & (SEPG_DB_TABLE__UPDATE |
164  SEPG_DB_TABLE__DELETE)) != 0 &&
165  IsCatalogRelationOid(relOid))
166  ereport(ERROR,
167  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
168  errmsg("SELinux: hardwired security policy violation")));
169 
170  if (relkind == RELKIND_TOASTVALUE)
171  ereport(ERROR,
172  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
173  errmsg("SELinux: hardwired security policy violation")));
174  }
175 
176  /*
177  * Check permissions on the relation
178  */
179  object.classId = RelationRelationId;
180  object.objectId = relOid;
181  object.objectSubId = 0;
182  audit_name = getObjectIdentity(&object, false);
183  switch (relkind)
184  {
185  case RELKIND_RELATION:
186  case RELKIND_PARTITIONED_TABLE:
187  result = sepgsql_avc_check_perms(&object,
189  required,
190  audit_name,
191  abort_on_violation);
192  break;
193 
194  case RELKIND_SEQUENCE:
195  Assert((required & ~SEPG_DB_TABLE__SELECT) == 0);
196 
197  if (required & SEPG_DB_TABLE__SELECT)
198  result = sepgsql_avc_check_perms(&object,
201  audit_name,
202  abort_on_violation);
203  break;
204 
205  case RELKIND_VIEW:
206  result = sepgsql_avc_check_perms(&object,
209  audit_name,
210  abort_on_violation);
211  break;
212 
213  default:
214  /* nothing to be checked */
215  break;
216  }
217  pfree(audit_name);
218 
219  /*
220  * Only columns owned by relations shall be checked
221  */
222  if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE)
223  return true;
224 
225  /*
226  * Check permissions on the columns
227  */
228  selected = fixup_whole_row_references(relOid, selected);
229  inserted = fixup_whole_row_references(relOid, inserted);
230  updated = fixup_whole_row_references(relOid, updated);
231  columns = bms_union(selected, bms_union(inserted, updated));
232 
233  while ((index = bms_first_member(columns)) >= 0)
234  {
236  uint32 column_perms = 0;
237 
238  if (bms_is_member(index, selected))
239  column_perms |= SEPG_DB_COLUMN__SELECT;
240  if (bms_is_member(index, inserted))
241  {
242  if (required & SEPG_DB_TABLE__INSERT)
243  column_perms |= SEPG_DB_COLUMN__INSERT;
244  }
245  if (bms_is_member(index, updated))
246  {
247  if (required & SEPG_DB_TABLE__UPDATE)
248  column_perms |= SEPG_DB_COLUMN__UPDATE;
249  }
250  if (column_perms == 0)
251  continue;
252 
253  /* obtain column's permission */
254  attnum = index + FirstLowInvalidHeapAttributeNumber;
255 
256  object.classId = RelationRelationId;
257  object.objectId = relOid;
258  object.objectSubId = attnum;
259  audit_name = getObjectDescription(&object, false);
260 
261  result = sepgsql_avc_check_perms(&object,
263  column_perms,
264  audit_name,
265  abort_on_violation);
266  pfree(audit_name);
267 
268  if (!result)
269  return result;
270  }
271  return true;
272 }
273 
274 /*
275  * sepgsql_dml_privileges
276  *
277  * Entrypoint of the DML permission checks
278  */
279 bool
280 sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation)
281 {
282  ListCell *lr;
283 
284  foreach(lr, rangeTabls)
285  {
286  RangeTblEntry *rte = lfirst(lr);
287  uint32 required = 0;
288  List *tableIds;
289  ListCell *li;
290 
291  /*
292  * Only regular relations shall be checked
293  */
294  if (rte->rtekind != RTE_RELATION)
295  continue;
296 
297  /*
298  * Find out required permissions
299  */
300  if (rte->requiredPerms & ACL_SELECT)
301  required |= SEPG_DB_TABLE__SELECT;
302  if (rte->requiredPerms & ACL_INSERT)
303  required |= SEPG_DB_TABLE__INSERT;
304  if (rte->requiredPerms & ACL_UPDATE)
305  {
306  if (!bms_is_empty(rte->updatedCols))
307  required |= SEPG_DB_TABLE__UPDATE;
308  else
309  required |= SEPG_DB_TABLE__LOCK;
310  }
311  if (rte->requiredPerms & ACL_DELETE)
312  required |= SEPG_DB_TABLE__DELETE;
313 
314  /*
315  * Skip, if nothing to be checked
316  */
317  if (required == 0)
318  continue;
319 
320  /*
321  * If this RangeTblEntry is also supposed to reference inherited
322  * tables, we need to check security label of the child tables. So, we
323  * expand rte->relid into list of OIDs of inheritance hierarchy, then
324  * checker routine will be invoked for each relations.
325  */
326  if (!rte->inh)
327  tableIds = list_make1_oid(rte->relid);
328  else
329  tableIds = find_all_inheritors(rte->relid, NoLock, NULL);
330 
331  foreach(li, tableIds)
332  {
333  Oid tableOid = lfirst_oid(li);
334  Bitmapset *selectedCols;
335  Bitmapset *insertedCols;
336  Bitmapset *updatedCols;
337 
338  /*
339  * child table has different attribute numbers, so we need to fix
340  * up them.
341  */
342  selectedCols = fixup_inherited_columns(rte->relid, tableOid,
343  rte->selectedCols);
344  insertedCols = fixup_inherited_columns(rte->relid, tableOid,
345  rte->insertedCols);
346  updatedCols = fixup_inherited_columns(rte->relid, tableOid,
347  rte->updatedCols);
348 
349  /*
350  * check permissions on individual tables
351  */
352  if (!check_relation_privileges(tableOid,
353  selectedCols,
354  insertedCols,
355  updatedCols,
356  required, abort_on_violation))
357  return false;
358  }
359  list_free(tableIds);
360  }
361  return true;
362 }
#define SEPG_DB_VIEW__EXPAND
Definition: sepgsql.h:213
int bms_first_member(Bitmapset *a)
Definition: bitmapset.c:996
char * getObjectDescription(const ObjectAddress *object, bool missing_ok)
#define GETSTRUCT(TUP)
Definition: htup_details.h:655
Bitmapset * bms_copy(const Bitmapset *a)
Definition: bitmapset.c:74
#define SEPG_CLASS_DB_COLUMN
Definition: sepgsql.h:49
static Bitmapset * fixup_whole_row_references(Oid relOid, Bitmapset *columns)
Definition: dml.c:38
static bool check_relation_privileges(Oid relOid, Bitmapset *selected, Bitmapset *inserted, Bitmapset *updated, uint32 required, bool abort_on_violation)
Definition: dml.c:141
char get_rel_relkind(Oid relid)
Definition: lsyscache.c:1920
bool sepgsql_avc_check_perms(const ObjectAddress *tobject, uint16 tclass, uint32 required, const char *audit_name, bool abort_on_violation)
Definition: uavc.c:419
int bms_next_member(const Bitmapset *a, int prevbit)
Definition: bitmapset.c:1043
#define SEPG_DB_TABLE__DELETE
Definition: sepgsql.h:146
#define Int16GetDatum(X)
Definition: postgres.h:451
static Bitmapset * fixup_inherited_columns(Oid parentId, Oid childId, Bitmapset *columns)
Definition: dml.c:92
int errcode(int sqlerrcode)
Definition: elog.c:691
bool sepgsql_getenforce(void)
Definition: selinux.c:651
#define ACL_DELETE
Definition: parsenodes.h:77
#define SEPG_DB_COLUMN__INSERT
Definition: sepgsql.h:178
#define FirstLowInvalidHeapAttributeNumber
Definition: sysattr.h:27
AclMode requiredPerms
Definition: parsenodes.h:1124
unsigned int Oid
Definition: postgres_ext.h:31
bool IsCatalogRelationOid(Oid relid)
Definition: catalog.c:113
#define SEPG_DB_TABLE__SELECT
Definition: sepgsql.h:143
Definition: type.h:89
Bitmapset * selectedCols
Definition: parsenodes.h:1126
void pfree(void *pointer)
Definition: mcxt.c:1057
#define SEPG_CLASS_DB_SEQUENCE
Definition: sepgsql.h:47
#define ObjectIdGetDatum(X)
Definition: postgres.h:507
#define ERROR
Definition: elog.h:43
NameData attname
Definition: pg_attribute.h:40
#define SEPG_DB_COLUMN__SELECT
Definition: sepgsql.h:176
#define SEPG_DB_SEQUENCE__GET_VALUE
Definition: sepgsql.h:156
#define SEPG_DB_TABLE__UPDATE
Definition: sepgsql.h:144
char * getObjectIdentity(const ObjectAddress *object, bool missing_ok)
#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:856
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:193
unsigned int uint32
Definition: c.h:429
bool sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation)
Definition: dml.c:280
#define SEPG_DB_COLUMN__UPDATE
Definition: sepgsql.h:177
bool bms_is_empty(const Bitmapset *a)
Definition: bitmapset.c:701
#define SEPG_CLASS_DB_VIEW
Definition: sepgsql.h:53
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1115
#define ACL_UPDATE
Definition: parsenodes.h:76
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1163
#define ACL_SELECT
Definition: parsenodes.h:75
#define list_make1_oid(x1)
Definition: pg_list.h:228
int16 attnum
Definition: pg_attribute.h:79
#define ereport(elevel,...)
Definition: elog.h:155
Bitmapset * updatedCols
Definition: parsenodes.h:1128
#define SEPG_CLASS_DB_TABLE
Definition: sepgsql.h:46
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define Assert(condition)
Definition: c.h:800
#define lfirst(lc)
Definition: pg_list.h:169
#define ACL_INSERT
Definition: parsenodes.h:74
Bitmapset * bms_union(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:225
HeapTuple SearchSysCache2(int cacheId, Datum key1, Datum key2)
Definition: syscache.c:1126
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:736
#define InvalidAttrNumber
Definition: attnum.h:23
RTEKind rtekind
Definition: parsenodes.h:981
FormData_pg_class * Form_pg_class
Definition: pg_class.h:153
List * find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
Definition: pg_inherits.c:165
int errmsg(const char *fmt,...)
Definition: elog.c:902
void list_free(List *list)
Definition: list.c:1376
#define elog(elevel,...)
Definition: elog.h:228
Bitmapset * insertedCols
Definition: parsenodes.h:1127
char * get_attname(Oid relid, AttrNumber attnum, bool missing_ok)
Definition: lsyscache.c:825
Bitmapset * bms_del_member(Bitmapset *a, int x)
Definition: bitmapset.c:773
Definition: pg_list.h:50
bool bms_is_member(int x, const Bitmapset *a)
Definition: bitmapset.c:427
int16 AttrNumber
Definition: attnum.h:21
#define SEPG_DB_TABLE__LOCK
Definition: sepgsql.h:147
#define lfirst_oid(lc)
Definition: pg_list.h:171