You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

dml.c 9.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. /* -------------------------------------------------------------------------
  2. *
  3. * contrib/sepgsql/dml.c
  4. *
  5. * Routines to handle DML permission checks
  6. *
  7. * Copyright (c) 2010-2019, PostgreSQL Global Development Group
  8. *
  9. * -------------------------------------------------------------------------
  10. */
  11. #include "postgres.h"
  12. #include "access/htup_details.h"
  13. #include "access/sysattr.h"
  14. #include "access/tupdesc.h"
  15. #include "catalog/catalog.h"
  16. #include "catalog/heap.h"
  17. #include "catalog/dependency.h"
  18. #include "catalog/pg_attribute.h"
  19. #include "catalog/pg_class.h"
  20. #include "catalog/pg_inherits.h"
  21. #include "commands/seclabel.h"
  22. #include "commands/tablecmds.h"
  23. #include "executor/executor.h"
  24. #include "nodes/bitmapset.h"
  25. #include "utils/lsyscache.h"
  26. #include "utils/syscache.h"
  27. #include "sepgsql.h"
  28. /*
  29. * fixup_whole_row_references
  30. *
  31. * When user reference a whole of row, it is equivalent to reference to
  32. * all the user columns (not system columns). So, we need to fix up the
  33. * given bitmapset, if it contains a whole of the row reference.
  34. */
  35. static Bitmapset *
  36. fixup_whole_row_references(Oid relOid, Bitmapset *columns)
  37. {
  38. Bitmapset *result;
  39. HeapTuple tuple;
  40. AttrNumber natts;
  41. AttrNumber attno;
  42. int index;
  43. /* if no whole of row references, do not anything */
  44. index = InvalidAttrNumber - FirstLowInvalidHeapAttributeNumber;
  45. if (!bms_is_member(index, columns))
  46. return columns;
  47. /* obtain number of attributes */
  48. tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
  49. if (!HeapTupleIsValid(tuple))
  50. elog(ERROR, "cache lookup failed for relation %u", relOid);
  51. natts = ((Form_pg_class) GETSTRUCT(tuple))->relnatts;
  52. ReleaseSysCache(tuple);
  53. /* fix up the given columns */
  54. result = bms_copy(columns);
  55. result = bms_del_member(result, index);
  56. for (attno = 1; attno <= natts; attno++)
  57. {
  58. tuple = SearchSysCache2(ATTNUM,
  59. ObjectIdGetDatum(relOid),
  60. Int16GetDatum(attno));
  61. if (!HeapTupleIsValid(tuple))
  62. continue;
  63. if (((Form_pg_attribute) GETSTRUCT(tuple))->attisdropped)
  64. continue;
  65. index = attno - FirstLowInvalidHeapAttributeNumber;
  66. result = bms_add_member(result, index);
  67. ReleaseSysCache(tuple);
  68. }
  69. return result;
  70. }
  71. /*
  72. * fixup_inherited_columns
  73. *
  74. * When user is querying on a table with children, it implicitly accesses
  75. * child tables also. So, we also need to check security label of child
  76. * tables and columns, but here is no guarantee attribute numbers are
  77. * same between the parent ans children.
  78. * It returns a bitmapset which contains attribute number of the child
  79. * table based on the given bitmapset of the parent.
  80. */
  81. static Bitmapset *
  82. fixup_inherited_columns(Oid parentId, Oid childId, Bitmapset *columns)
  83. {
  84. Bitmapset *result = NULL;
  85. int index;
  86. /*
  87. * obviously, no need to do anything here
  88. */
  89. if (parentId == childId)
  90. return columns;
  91. index = -1;
  92. while ((index = bms_next_member(columns, index)) >= 0)
  93. {
  94. /* bit numbers are offset by FirstLowInvalidHeapAttributeNumber */
  95. AttrNumber attno = index + FirstLowInvalidHeapAttributeNumber;
  96. char *attname;
  97. /*
  98. * whole-row-reference shall be fixed-up later
  99. */
  100. if (attno == InvalidAttrNumber)
  101. {
  102. result = bms_add_member(result, index);
  103. continue;
  104. }
  105. attname = get_attname(parentId, attno, false);
  106. attno = get_attnum(childId, attname);
  107. if (attno == InvalidAttrNumber)
  108. elog(ERROR, "cache lookup failed for attribute %s of relation %u",
  109. attname, childId);
  110. result = bms_add_member(result,
  111. attno - FirstLowInvalidHeapAttributeNumber);
  112. pfree(attname);
  113. }
  114. return result;
  115. }
  116. /*
  117. * check_relation_privileges
  118. *
  119. * It actually checks required permissions on a certain relation
  120. * and its columns.
  121. */
  122. static bool
  123. check_relation_privileges(Oid relOid,
  124. Bitmapset *selected,
  125. Bitmapset *inserted,
  126. Bitmapset *updated,
  127. uint32 required,
  128. bool abort_on_violation)
  129. {
  130. ObjectAddress object;
  131. char *audit_name;
  132. Bitmapset *columns;
  133. int index;
  134. char relkind = get_rel_relkind(relOid);
  135. bool result = true;
  136. /*
  137. * Hardwired Policies: SE-PostgreSQL enforces - clients cannot modify
  138. * system catalogs using DMLs - clients cannot reference/modify toast
  139. * relations using DMLs
  140. */
  141. if (sepgsql_getenforce() > 0)
  142. {
  143. Oid relnamespace = get_rel_namespace(relOid);
  144. if (IsSystemNamespace(relnamespace) &&
  145. (required & (SEPG_DB_TABLE__UPDATE |
  146. SEPG_DB_TABLE__INSERT |
  147. SEPG_DB_TABLE__DELETE)) != 0)
  148. ereport(ERROR,
  149. (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  150. errmsg("SELinux: hardwired security policy violation")));
  151. if (relkind == RELKIND_TOASTVALUE)
  152. ereport(ERROR,
  153. (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  154. errmsg("SELinux: hardwired security policy violation")));
  155. }
  156. /*
  157. * Check permissions on the relation
  158. */
  159. object.classId = RelationRelationId;
  160. object.objectId = relOid;
  161. object.objectSubId = 0;
  162. audit_name = getObjectIdentity(&object);
  163. switch (relkind)
  164. {
  165. case RELKIND_RELATION:
  166. case RELKIND_PARTITIONED_TABLE:
  167. result = sepgsql_avc_check_perms(&object,
  168. SEPG_CLASS_DB_TABLE,
  169. required,
  170. audit_name,
  171. abort_on_violation);
  172. break;
  173. case RELKIND_SEQUENCE:
  174. Assert((required & ~SEPG_DB_TABLE__SELECT) == 0);
  175. if (required & SEPG_DB_TABLE__SELECT)
  176. result = sepgsql_avc_check_perms(&object,
  177. SEPG_CLASS_DB_SEQUENCE,
  178. SEPG_DB_SEQUENCE__GET_VALUE,
  179. audit_name,
  180. abort_on_violation);
  181. break;
  182. case RELKIND_VIEW:
  183. result = sepgsql_avc_check_perms(&object,
  184. SEPG_CLASS_DB_VIEW,
  185. SEPG_DB_VIEW__EXPAND,
  186. audit_name,
  187. abort_on_violation);
  188. break;
  189. default:
  190. /* nothing to be checked */
  191. break;
  192. }
  193. pfree(audit_name);
  194. /*
  195. * Only columns owned by relations shall be checked
  196. */
  197. if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE)
  198. return true;
  199. /*
  200. * Check permissions on the columns
  201. */
  202. selected = fixup_whole_row_references(relOid, selected);
  203. inserted = fixup_whole_row_references(relOid, inserted);
  204. updated = fixup_whole_row_references(relOid, updated);
  205. columns = bms_union(selected, bms_union(inserted, updated));
  206. while ((index = bms_first_member(columns)) >= 0)
  207. {
  208. AttrNumber attnum;
  209. uint32 column_perms = 0;
  210. if (bms_is_member(index, selected))
  211. column_perms |= SEPG_DB_COLUMN__SELECT;
  212. if (bms_is_member(index, inserted))
  213. {
  214. if (required & SEPG_DB_TABLE__INSERT)
  215. column_perms |= SEPG_DB_COLUMN__INSERT;
  216. }
  217. if (bms_is_member(index, updated))
  218. {
  219. if (required & SEPG_DB_TABLE__UPDATE)
  220. column_perms |= SEPG_DB_COLUMN__UPDATE;
  221. }
  222. if (column_perms == 0)
  223. continue;
  224. /* obtain column's permission */
  225. attnum = index + FirstLowInvalidHeapAttributeNumber;
  226. object.classId = RelationRelationId;
  227. object.objectId = relOid;
  228. object.objectSubId = attnum;
  229. audit_name = getObjectDescription(&object);
  230. result = sepgsql_avc_check_perms(&object,
  231. SEPG_CLASS_DB_COLUMN,
  232. column_perms,
  233. audit_name,
  234. abort_on_violation);
  235. pfree(audit_name);
  236. if (!result)
  237. return result;
  238. }
  239. return true;
  240. }
  241. /*
  242. * sepgsql_dml_privileges
  243. *
  244. * Entrypoint of the DML permission checks
  245. */
  246. bool
  247. sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation)
  248. {
  249. ListCell *lr;
  250. foreach(lr, rangeTabls)
  251. {
  252. RangeTblEntry *rte = lfirst(lr);
  253. uint32 required = 0;
  254. List *tableIds;
  255. ListCell *li;
  256. /*
  257. * Only regular relations shall be checked
  258. */
  259. if (rte->rtekind != RTE_RELATION)
  260. continue;
  261. /*
  262. * Find out required permissions
  263. */
  264. if (rte->requiredPerms & ACL_SELECT)
  265. required |= SEPG_DB_TABLE__SELECT;
  266. if (rte->requiredPerms & ACL_INSERT)
  267. required |= SEPG_DB_TABLE__INSERT;
  268. if (rte->requiredPerms & ACL_UPDATE)
  269. {
  270. if (!bms_is_empty(rte->updatedCols))
  271. required |= SEPG_DB_TABLE__UPDATE;
  272. else
  273. required |= SEPG_DB_TABLE__LOCK;
  274. }
  275. if (rte->requiredPerms & ACL_DELETE)
  276. required |= SEPG_DB_TABLE__DELETE;
  277. /*
  278. * Skip, if nothing to be checked
  279. */
  280. if (required == 0)
  281. continue;
  282. /*
  283. * If this RangeTblEntry is also supposed to reference inherited
  284. * tables, we need to check security label of the child tables. So, we
  285. * expand rte->relid into list of OIDs of inheritance hierarchy, then
  286. * checker routine will be invoked for each relations.
  287. */
  288. if (!rte->inh)
  289. tableIds = list_make1_oid(rte->relid);
  290. else
  291. tableIds = find_all_inheritors(rte->relid, NoLock, NULL);
  292. foreach(li, tableIds)
  293. {
  294. Oid tableOid = lfirst_oid(li);
  295. Bitmapset *selectedCols;
  296. Bitmapset *insertedCols;
  297. Bitmapset *updatedCols;
  298. /*
  299. * child table has different attribute numbers, so we need to fix
  300. * up them.
  301. */
  302. selectedCols = fixup_inherited_columns(rte->relid, tableOid,
  303. rte->selectedCols);
  304. insertedCols = fixup_inherited_columns(rte->relid, tableOid,
  305. rte->insertedCols);
  306. updatedCols = fixup_inherited_columns(rte->relid, tableOid,
  307. rte->updatedCols);
  308. /*
  309. * check permissions on individual tables
  310. */
  311. if (!check_relation_privileges(tableOid,
  312. selectedCols,
  313. insertedCols,
  314. updatedCols,
  315. required, abort_on_violation))
  316. return false;
  317. }
  318. list_free(tableIds);
  319. }
  320. return true;
  321. }