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