1 /*-------------------------------------------------------------------------
2 *
3 * policy.c
4 * Commands for manipulating policies.
5 *
6 * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
8 *
9 * src/backend/commands/policy.c
10 *
11 *-------------------------------------------------------------------------
12 */
13 #include "postgres.h"
14
15 #include "access/genam.h"
16 #include "access/htup.h"
17 #include "access/htup_details.h"
18 #include "access/relation.h"
19 #include "access/sysattr.h"
20 #include "access/table.h"
21 #include "access/xact.h"
22 #include "catalog/catalog.h"
23 #include "catalog/dependency.h"
24 #include "catalog/indexing.h"
25 #include "catalog/namespace.h"
26 #include "catalog/objectaccess.h"
27 #include "catalog/pg_authid.h"
28 #include "catalog/pg_policy.h"
29 #include "catalog/pg_type.h"
30 #include "commands/policy.h"
31 #include "miscadmin.h"
32 #include "nodes/makefuncs.h"
33 #include "nodes/pg_list.h"
34 #include "parser/parse_clause.h"
35 #include "parser/parse_collate.h"
36 #include "parser/parse_node.h"
37 #include "parser/parse_relation.h"
38 #include "rewrite/rewriteManip.h"
39 #include "rewrite/rowsecurity.h"
40 #include "storage/lock.h"
41 #include "utils/acl.h"
42 #include "utils/array.h"
43 #include "utils/builtins.h"
44 #include "utils/fmgroids.h"
45 #include "utils/inval.h"
46 #include "utils/lsyscache.h"
47 #include "utils/memutils.h"
48 #include "utils/rel.h"
49 #include "utils/syscache.h"
50
51 static void RangeVarCallbackForPolicy(const RangeVar *rv,
52 Oid relid, Oid oldrelid, void *arg);
53 static char parse_policy_command(const char *cmd_name);
54 static Datum *policy_role_list_to_array(List *roles, int *num_roles);
55
56 /*
57 * Callback to RangeVarGetRelidExtended().
58 *
59 * Checks the following:
60 * - the relation specified is a table.
61 * - current user owns the table.
62 * - the table is not a system table.
63 *
64 * If any of these checks fails then an error is raised.
65 */
66 static void
RangeVarCallbackForPolicy(const RangeVar * rv,Oid relid,Oid oldrelid,void * arg)67 RangeVarCallbackForPolicy(const RangeVar *rv, Oid relid, Oid oldrelid,
68 void *arg)
69 {
70 HeapTuple tuple;
71 Form_pg_class classform;
72 char relkind;
73
74 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
75 if (!HeapTupleIsValid(tuple))
76 return;
77
78 classform = (Form_pg_class) GETSTRUCT(tuple);
79 relkind = classform->relkind;
80
81 /* Must own relation. */
82 if (!pg_class_ownercheck(relid, GetUserId()))
83 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relid)), rv->relname);
84
85 /* No system table modifications unless explicitly allowed. */
86 if (!allowSystemTableMods && IsSystemClass(relid, classform))
87 ereport(ERROR,
88 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
89 errmsg("permission denied: \"%s\" is a system catalog",
90 rv->relname)));
91
92 /* Relation type MUST be a table. */
93 if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE)
94 ereport(ERROR,
95 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
96 errmsg("\"%s\" is not a table", rv->relname)));
97
98 ReleaseSysCache(tuple);
99 }
100
101 /*
102 * parse_policy_command -
103 * helper function to convert full command strings to their char
104 * representation.
105 *
106 * cmd_name - full string command name. Valid values are 'all', 'select',
107 * 'insert', 'update' and 'delete'.
108 *
109 */
110 static char
parse_policy_command(const char * cmd_name)111 parse_policy_command(const char *cmd_name)
112 {
113 char polcmd;
114
115 if (!cmd_name)
116 elog(ERROR, "unrecognized policy command");
117
118 if (strcmp(cmd_name, "all") == 0)
119 polcmd = '*';
120 else if (strcmp(cmd_name, "select") == 0)
121 polcmd = ACL_SELECT_CHR;
122 else if (strcmp(cmd_name, "insert") == 0)
123 polcmd = ACL_INSERT_CHR;
124 else if (strcmp(cmd_name, "update") == 0)
125 polcmd = ACL_UPDATE_CHR;
126 else if (strcmp(cmd_name, "delete") == 0)
127 polcmd = ACL_DELETE_CHR;
128 else
129 elog(ERROR, "unrecognized policy command");
130
131 return polcmd;
132 }
133
134 /*
135 * policy_role_list_to_array
136 * helper function to convert a list of RoleSpecs to an array of
137 * role id Datums.
138 */
139 static Datum *
policy_role_list_to_array(List * roles,int * num_roles)140 policy_role_list_to_array(List *roles, int *num_roles)
141 {
142 Datum *role_oids;
143 ListCell *cell;
144 int i = 0;
145
146 /* Handle no roles being passed in as being for public */
147 if (roles == NIL)
148 {
149 *num_roles = 1;
150 role_oids = (Datum *) palloc(*num_roles * sizeof(Datum));
151 role_oids[0] = ObjectIdGetDatum(ACL_ID_PUBLIC);
152
153 return role_oids;
154 }
155
156 *num_roles = list_length(roles);
157 role_oids = (Datum *) palloc(*num_roles * sizeof(Datum));
158
159 foreach(cell, roles)
160 {
161 RoleSpec *spec = lfirst(cell);
162
163 /*
164 * PUBLIC covers all roles, so it only makes sense alone.
165 */
166 if (spec->roletype == ROLESPEC_PUBLIC)
167 {
168 if (*num_roles != 1)
169 {
170 ereport(WARNING,
171 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
172 errmsg("ignoring specified roles other than PUBLIC"),
173 errhint("All roles are members of the PUBLIC role.")));
174 *num_roles = 1;
175 }
176 role_oids[0] = ObjectIdGetDatum(ACL_ID_PUBLIC);
177
178 return role_oids;
179 }
180 else
181 role_oids[i++] =
182 ObjectIdGetDatum(get_rolespec_oid(spec, false));
183 }
184
185 return role_oids;
186 }
187
188 /*
189 * Load row security policy from the catalog, and store it in
190 * the relation's relcache entry.
191 *
192 * Note that caller should have verified that pg_class.relrowsecurity
193 * is true for this relation.
194 */
195 void
RelationBuildRowSecurity(Relation relation)196 RelationBuildRowSecurity(Relation relation)
197 {
198 MemoryContext rscxt;
199 MemoryContext oldcxt = CurrentMemoryContext;
200 RowSecurityDesc *rsdesc;
201 Relation catalog;
202 ScanKeyData skey;
203 SysScanDesc sscan;
204 HeapTuple tuple;
205
206 /*
207 * Create a memory context to hold everything associated with this
208 * relation's row security policy. This makes it easy to clean up during
209 * a relcache flush. However, to cover the possibility of an error
210 * partway through, we don't make the context long-lived till we're done.
211 */
212 rscxt = AllocSetContextCreate(CurrentMemoryContext,
213 "row security descriptor",
214 ALLOCSET_SMALL_SIZES);
215 MemoryContextCopyAndSetIdentifier(rscxt,
216 RelationGetRelationName(relation));
217
218 rsdesc = MemoryContextAllocZero(rscxt, sizeof(RowSecurityDesc));
219 rsdesc->rscxt = rscxt;
220
221 /*
222 * Now scan pg_policy for RLS policies associated with this relation.
223 * Because we use the index on (polrelid, polname), we should consistently
224 * visit the rel's policies in name order, at least when system indexes
225 * aren't disabled. This simplifies equalRSDesc().
226 */
227 catalog = table_open(PolicyRelationId, AccessShareLock);
228
229 ScanKeyInit(&skey,
230 Anum_pg_policy_polrelid,
231 BTEqualStrategyNumber, F_OIDEQ,
232 ObjectIdGetDatum(RelationGetRelid(relation)));
233
234 sscan = systable_beginscan(catalog, PolicyPolrelidPolnameIndexId, true,
235 NULL, 1, &skey);
236
237 while (HeapTupleIsValid(tuple = systable_getnext(sscan)))
238 {
239 Form_pg_policy policy_form = (Form_pg_policy) GETSTRUCT(tuple);
240 RowSecurityPolicy *policy;
241 Datum datum;
242 bool isnull;
243 char *str_value;
244
245 policy = MemoryContextAllocZero(rscxt, sizeof(RowSecurityPolicy));
246
247 /*
248 * Note: we must be sure that pass-by-reference data gets copied into
249 * rscxt. We avoid making that context current over wider spans than
250 * we have to, though.
251 */
252
253 /* Get policy command */
254 policy->polcmd = policy_form->polcmd;
255
256 /* Get policy, permissive or restrictive */
257 policy->permissive = policy_form->polpermissive;
258
259 /* Get policy name */
260 policy->policy_name =
261 MemoryContextStrdup(rscxt, NameStr(policy_form->polname));
262
263 /* Get policy roles */
264 datum = heap_getattr(tuple, Anum_pg_policy_polroles,
265 RelationGetDescr(catalog), &isnull);
266 /* shouldn't be null, but let's check for luck */
267 if (isnull)
268 elog(ERROR, "unexpected null value in pg_policy.polroles");
269 MemoryContextSwitchTo(rscxt);
270 policy->roles = DatumGetArrayTypePCopy(datum);
271 MemoryContextSwitchTo(oldcxt);
272
273 /* Get policy qual */
274 datum = heap_getattr(tuple, Anum_pg_policy_polqual,
275 RelationGetDescr(catalog), &isnull);
276 if (!isnull)
277 {
278 str_value = TextDatumGetCString(datum);
279 MemoryContextSwitchTo(rscxt);
280 policy->qual = (Expr *) stringToNode(str_value);
281 MemoryContextSwitchTo(oldcxt);
282 pfree(str_value);
283 }
284 else
285 policy->qual = NULL;
286
287 /* Get WITH CHECK qual */
288 datum = heap_getattr(tuple, Anum_pg_policy_polwithcheck,
289 RelationGetDescr(catalog), &isnull);
290 if (!isnull)
291 {
292 str_value = TextDatumGetCString(datum);
293 MemoryContextSwitchTo(rscxt);
294 policy->with_check_qual = (Expr *) stringToNode(str_value);
295 MemoryContextSwitchTo(oldcxt);
296 pfree(str_value);
297 }
298 else
299 policy->with_check_qual = NULL;
300
301 /* We want to cache whether there are SubLinks in these expressions */
302 policy->hassublinks = checkExprHasSubLink((Node *) policy->qual) ||
303 checkExprHasSubLink((Node *) policy->with_check_qual);
304
305 /*
306 * Add this object to list. For historical reasons, the list is built
307 * in reverse order.
308 */
309 MemoryContextSwitchTo(rscxt);
310 rsdesc->policies = lcons(policy, rsdesc->policies);
311 MemoryContextSwitchTo(oldcxt);
312 }
313
314 systable_endscan(sscan);
315 table_close(catalog, AccessShareLock);
316
317 /*
318 * Success. Reparent the descriptor's memory context under
319 * CacheMemoryContext so that it will live indefinitely, then attach the
320 * policy descriptor to the relcache entry.
321 */
322 MemoryContextSetParent(rscxt, CacheMemoryContext);
323
324 relation->rd_rsdesc = rsdesc;
325 }
326
327 /*
328 * RemovePolicyById -
329 * remove a policy by its OID. If a policy does not exist with the provided
330 * oid, then an error is raised.
331 *
332 * policy_id - the oid of the policy.
333 */
334 void
RemovePolicyById(Oid policy_id)335 RemovePolicyById(Oid policy_id)
336 {
337 Relation pg_policy_rel;
338 SysScanDesc sscan;
339 ScanKeyData skey[1];
340 HeapTuple tuple;
341 Oid relid;
342 Relation rel;
343
344 pg_policy_rel = table_open(PolicyRelationId, RowExclusiveLock);
345
346 /*
347 * Find the policy to delete.
348 */
349 ScanKeyInit(&skey[0],
350 Anum_pg_policy_oid,
351 BTEqualStrategyNumber, F_OIDEQ,
352 ObjectIdGetDatum(policy_id));
353
354 sscan = systable_beginscan(pg_policy_rel, PolicyOidIndexId, true,
355 NULL, 1, skey);
356
357 tuple = systable_getnext(sscan);
358
359 /* If the policy exists, then remove it, otherwise raise an error. */
360 if (!HeapTupleIsValid(tuple))
361 elog(ERROR, "could not find tuple for policy %u", policy_id);
362
363 /*
364 * Open and exclusive-lock the relation the policy belongs to. (We need
365 * exclusive lock to lock out queries that might otherwise depend on the
366 * set of policies the rel has; furthermore we've got to hold the lock
367 * till commit.)
368 */
369 relid = ((Form_pg_policy) GETSTRUCT(tuple))->polrelid;
370
371 rel = table_open(relid, AccessExclusiveLock);
372 if (rel->rd_rel->relkind != RELKIND_RELATION &&
373 rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
374 ereport(ERROR,
375 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
376 errmsg("\"%s\" is not a table",
377 RelationGetRelationName(rel))));
378
379 if (!allowSystemTableMods && IsSystemRelation(rel))
380 ereport(ERROR,
381 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
382 errmsg("permission denied: \"%s\" is a system catalog",
383 RelationGetRelationName(rel))));
384
385 CatalogTupleDelete(pg_policy_rel, &tuple->t_self);
386
387 systable_endscan(sscan);
388
389 /*
390 * Note that, unlike some of the other flags in pg_class, relrowsecurity
391 * is not just an indication of if policies exist. When relrowsecurity is
392 * set by a user, then all access to the relation must be through a
393 * policy. If no policy is defined for the relation then a default-deny
394 * policy is created and all records are filtered (except for queries from
395 * the owner).
396 */
397 CacheInvalidateRelcache(rel);
398
399 table_close(rel, NoLock);
400
401 /* Clean up */
402 table_close(pg_policy_rel, RowExclusiveLock);
403 }
404
405 /*
406 * RemoveRoleFromObjectPolicy -
407 * remove a role from a policy's applicable-roles list.
408 *
409 * Returns true if the role was successfully removed from the policy.
410 * Returns false if the role was not removed because it would have left
411 * polroles empty (which is disallowed, though perhaps it should not be).
412 * On false return, the caller should instead drop the policy altogether.
413 *
414 * roleid - the oid of the role to remove
415 * classid - should always be PolicyRelationId
416 * policy_id - the oid of the policy.
417 */
418 bool
RemoveRoleFromObjectPolicy(Oid roleid,Oid classid,Oid policy_id)419 RemoveRoleFromObjectPolicy(Oid roleid, Oid classid, Oid policy_id)
420 {
421 Relation pg_policy_rel;
422 SysScanDesc sscan;
423 ScanKeyData skey[1];
424 HeapTuple tuple;
425 Oid relid;
426 ArrayType *policy_roles;
427 Datum roles_datum;
428 Oid *roles;
429 int num_roles;
430 Datum *role_oids;
431 bool attr_isnull;
432 bool keep_policy = true;
433 int i,
434 j;
435
436 Assert(classid == PolicyRelationId);
437
438 pg_policy_rel = table_open(PolicyRelationId, RowExclusiveLock);
439
440 /*
441 * Find the policy to update.
442 */
443 ScanKeyInit(&skey[0],
444 Anum_pg_policy_oid,
445 BTEqualStrategyNumber, F_OIDEQ,
446 ObjectIdGetDatum(policy_id));
447
448 sscan = systable_beginscan(pg_policy_rel, PolicyOidIndexId, true,
449 NULL, 1, skey);
450
451 tuple = systable_getnext(sscan);
452
453 /* Raise an error if we don't find the policy. */
454 if (!HeapTupleIsValid(tuple))
455 elog(ERROR, "could not find tuple for policy %u", policy_id);
456
457 /* Identify rel the policy belongs to */
458 relid = ((Form_pg_policy) GETSTRUCT(tuple))->polrelid;
459
460 /* Get the current set of roles */
461 roles_datum = heap_getattr(tuple,
462 Anum_pg_policy_polroles,
463 RelationGetDescr(pg_policy_rel),
464 &attr_isnull);
465
466 Assert(!attr_isnull);
467
468 policy_roles = DatumGetArrayTypePCopy(roles_datum);
469 roles = (Oid *) ARR_DATA_PTR(policy_roles);
470 num_roles = ARR_DIMS(policy_roles)[0];
471
472 /*
473 * Rebuild the polroles array, without any mentions of the target role.
474 * Ordinarily there'd be exactly one, but we must cope with duplicate
475 * mentions, since CREATE/ALTER POLICY historically have allowed that.
476 */
477 role_oids = (Datum *) palloc(num_roles * sizeof(Datum));
478 for (i = 0, j = 0; i < num_roles; i++)
479 {
480 if (roles[i] != roleid)
481 role_oids[j++] = ObjectIdGetDatum(roles[i]);
482 }
483 num_roles = j;
484
485 /* If any roles remain, update the policy entry. */
486 if (num_roles > 0)
487 {
488 ArrayType *role_ids;
489 Datum values[Natts_pg_policy];
490 bool isnull[Natts_pg_policy];
491 bool replaces[Natts_pg_policy];
492 HeapTuple new_tuple;
493 HeapTuple reltup;
494 ObjectAddress target;
495 ObjectAddress myself;
496
497 /* zero-clear */
498 memset(values, 0, sizeof(values));
499 memset(replaces, 0, sizeof(replaces));
500 memset(isnull, 0, sizeof(isnull));
501
502 /* This is the array for the new tuple */
503 role_ids = construct_array(role_oids, num_roles, OIDOID,
504 sizeof(Oid), true, TYPALIGN_INT);
505
506 replaces[Anum_pg_policy_polroles - 1] = true;
507 values[Anum_pg_policy_polroles - 1] = PointerGetDatum(role_ids);
508
509 new_tuple = heap_modify_tuple(tuple,
510 RelationGetDescr(pg_policy_rel),
511 values, isnull, replaces);
512 CatalogTupleUpdate(pg_policy_rel, &new_tuple->t_self, new_tuple);
513
514 /* Remove all the old shared dependencies (roles) */
515 deleteSharedDependencyRecordsFor(PolicyRelationId, policy_id, 0);
516
517 /* Record the new shared dependencies (roles) */
518 myself.classId = PolicyRelationId;
519 myself.objectId = policy_id;
520 myself.objectSubId = 0;
521
522 target.classId = AuthIdRelationId;
523 target.objectSubId = 0;
524 for (i = 0; i < num_roles; i++)
525 {
526 target.objectId = DatumGetObjectId(role_oids[i]);
527 /* no need for dependency on the public role */
528 if (target.objectId != ACL_ID_PUBLIC)
529 recordSharedDependencyOn(&myself, &target,
530 SHARED_DEPENDENCY_POLICY);
531 }
532
533 InvokeObjectPostAlterHook(PolicyRelationId, policy_id, 0);
534
535 heap_freetuple(new_tuple);
536
537 /* Make updates visible */
538 CommandCounterIncrement();
539
540 /*
541 * Invalidate relcache entry for rel the policy belongs to, to force
542 * redoing any dependent plans. In case of a race condition where the
543 * rel was just dropped, we need do nothing.
544 */
545 reltup = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
546 if (HeapTupleIsValid(reltup))
547 {
548 CacheInvalidateRelcacheByTuple(reltup);
549 ReleaseSysCache(reltup);
550 }
551 }
552 else
553 {
554 /* No roles would remain, so drop the policy instead. */
555 keep_policy = false;
556 }
557
558 /* Clean up. */
559 systable_endscan(sscan);
560
561 table_close(pg_policy_rel, RowExclusiveLock);
562
563 return keep_policy;
564 }
565
566 /*
567 * CreatePolicy -
568 * handles the execution of the CREATE POLICY command.
569 *
570 * stmt - the CreatePolicyStmt that describes the policy to create.
571 */
572 ObjectAddress
CreatePolicy(CreatePolicyStmt * stmt)573 CreatePolicy(CreatePolicyStmt *stmt)
574 {
575 Relation pg_policy_rel;
576 Oid policy_id;
577 Relation target_table;
578 Oid table_id;
579 char polcmd;
580 Datum *role_oids;
581 int nitems = 0;
582 ArrayType *role_ids;
583 ParseState *qual_pstate;
584 ParseState *with_check_pstate;
585 ParseNamespaceItem *nsitem;
586 Node *qual;
587 Node *with_check_qual;
588 ScanKeyData skey[2];
589 SysScanDesc sscan;
590 HeapTuple policy_tuple;
591 Datum values[Natts_pg_policy];
592 bool isnull[Natts_pg_policy];
593 ObjectAddress target;
594 ObjectAddress myself;
595 int i;
596
597 /* Parse command */
598 polcmd = parse_policy_command(stmt->cmd_name);
599
600 /*
601 * If the command is SELECT or DELETE then WITH CHECK should be NULL.
602 */
603 if ((polcmd == ACL_SELECT_CHR || polcmd == ACL_DELETE_CHR)
604 && stmt->with_check != NULL)
605 ereport(ERROR,
606 (errcode(ERRCODE_SYNTAX_ERROR),
607 errmsg("WITH CHECK cannot be applied to SELECT or DELETE")));
608
609 /*
610 * If the command is INSERT then WITH CHECK should be the only expression
611 * provided.
612 */
613 if (polcmd == ACL_INSERT_CHR && stmt->qual != NULL)
614 ereport(ERROR,
615 (errcode(ERRCODE_SYNTAX_ERROR),
616 errmsg("only WITH CHECK expression allowed for INSERT")));
617
618 /* Collect role ids */
619 role_oids = policy_role_list_to_array(stmt->roles, &nitems);
620 role_ids = construct_array(role_oids, nitems, OIDOID,
621 sizeof(Oid), true, TYPALIGN_INT);
622
623 /* Parse the supplied clause */
624 qual_pstate = make_parsestate(NULL);
625 with_check_pstate = make_parsestate(NULL);
626
627 /* zero-clear */
628 memset(values, 0, sizeof(values));
629 memset(isnull, 0, sizeof(isnull));
630
631 /* Get id of table. Also handles permissions checks. */
632 table_id = RangeVarGetRelidExtended(stmt->table, AccessExclusiveLock,
633 0,
634 RangeVarCallbackForPolicy,
635 (void *) stmt);
636
637 /* Open target_table to build quals. No additional lock is necessary. */
638 target_table = relation_open(table_id, NoLock);
639
640 /* Add for the regular security quals */
641 nsitem = addRangeTableEntryForRelation(qual_pstate, target_table,
642 AccessShareLock,
643 NULL, false, false);
644 addNSItemToQuery(qual_pstate, nsitem, false, true, true);
645
646 /* Add for the with-check quals */
647 nsitem = addRangeTableEntryForRelation(with_check_pstate, target_table,
648 AccessShareLock,
649 NULL, false, false);
650 addNSItemToQuery(with_check_pstate, nsitem, false, true, true);
651
652 qual = transformWhereClause(qual_pstate,
653 stmt->qual,
654 EXPR_KIND_POLICY,
655 "POLICY");
656
657 with_check_qual = transformWhereClause(with_check_pstate,
658 stmt->with_check,
659 EXPR_KIND_POLICY,
660 "POLICY");
661
662 /* Fix up collation information */
663 assign_expr_collations(qual_pstate, qual);
664 assign_expr_collations(with_check_pstate, with_check_qual);
665
666 /* Open pg_policy catalog */
667 pg_policy_rel = table_open(PolicyRelationId, RowExclusiveLock);
668
669 /* Set key - policy's relation id. */
670 ScanKeyInit(&skey[0],
671 Anum_pg_policy_polrelid,
672 BTEqualStrategyNumber, F_OIDEQ,
673 ObjectIdGetDatum(table_id));
674
675 /* Set key - policy's name. */
676 ScanKeyInit(&skey[1],
677 Anum_pg_policy_polname,
678 BTEqualStrategyNumber, F_NAMEEQ,
679 CStringGetDatum(stmt->policy_name));
680
681 sscan = systable_beginscan(pg_policy_rel,
682 PolicyPolrelidPolnameIndexId, true, NULL, 2,
683 skey);
684
685 policy_tuple = systable_getnext(sscan);
686
687 /* Complain if the policy name already exists for the table */
688 if (HeapTupleIsValid(policy_tuple))
689 ereport(ERROR,
690 (errcode(ERRCODE_DUPLICATE_OBJECT),
691 errmsg("policy \"%s\" for table \"%s\" already exists",
692 stmt->policy_name, RelationGetRelationName(target_table))));
693
694 policy_id = GetNewOidWithIndex(pg_policy_rel, PolicyOidIndexId,
695 Anum_pg_policy_oid);
696 values[Anum_pg_policy_oid - 1] = ObjectIdGetDatum(policy_id);
697 values[Anum_pg_policy_polrelid - 1] = ObjectIdGetDatum(table_id);
698 values[Anum_pg_policy_polname - 1] = DirectFunctionCall1(namein,
699 CStringGetDatum(stmt->policy_name));
700 values[Anum_pg_policy_polcmd - 1] = CharGetDatum(polcmd);
701 values[Anum_pg_policy_polpermissive - 1] = BoolGetDatum(stmt->permissive);
702 values[Anum_pg_policy_polroles - 1] = PointerGetDatum(role_ids);
703
704 /* Add qual if present. */
705 if (qual)
706 values[Anum_pg_policy_polqual - 1] = CStringGetTextDatum(nodeToString(qual));
707 else
708 isnull[Anum_pg_policy_polqual - 1] = true;
709
710 /* Add WITH CHECK qual if present */
711 if (with_check_qual)
712 values[Anum_pg_policy_polwithcheck - 1] = CStringGetTextDatum(nodeToString(with_check_qual));
713 else
714 isnull[Anum_pg_policy_polwithcheck - 1] = true;
715
716 policy_tuple = heap_form_tuple(RelationGetDescr(pg_policy_rel), values,
717 isnull);
718
719 CatalogTupleInsert(pg_policy_rel, policy_tuple);
720
721 /* Record Dependencies */
722 target.classId = RelationRelationId;
723 target.objectId = table_id;
724 target.objectSubId = 0;
725
726 myself.classId = PolicyRelationId;
727 myself.objectId = policy_id;
728 myself.objectSubId = 0;
729
730 recordDependencyOn(&myself, &target, DEPENDENCY_AUTO);
731
732 recordDependencyOnExpr(&myself, qual, qual_pstate->p_rtable,
733 DEPENDENCY_NORMAL);
734
735 recordDependencyOnExpr(&myself, with_check_qual,
736 with_check_pstate->p_rtable, DEPENDENCY_NORMAL);
737
738 /* Register role dependencies */
739 target.classId = AuthIdRelationId;
740 target.objectSubId = 0;
741 for (i = 0; i < nitems; i++)
742 {
743 target.objectId = DatumGetObjectId(role_oids[i]);
744 /* no dependency if public */
745 if (target.objectId != ACL_ID_PUBLIC)
746 recordSharedDependencyOn(&myself, &target,
747 SHARED_DEPENDENCY_POLICY);
748 }
749
750 InvokeObjectPostCreateHook(PolicyRelationId, policy_id, 0);
751
752 /* Invalidate Relation Cache */
753 CacheInvalidateRelcache(target_table);
754
755 /* Clean up. */
756 heap_freetuple(policy_tuple);
757 free_parsestate(qual_pstate);
758 free_parsestate(with_check_pstate);
759 systable_endscan(sscan);
760 relation_close(target_table, NoLock);
761 table_close(pg_policy_rel, RowExclusiveLock);
762
763 return myself;
764 }
765
766 /*
767 * AlterPolicy -
768 * handles the execution of the ALTER POLICY command.
769 *
770 * stmt - the AlterPolicyStmt that describes the policy and how to alter it.
771 */
772 ObjectAddress
AlterPolicy(AlterPolicyStmt * stmt)773 AlterPolicy(AlterPolicyStmt *stmt)
774 {
775 Relation pg_policy_rel;
776 Oid policy_id;
777 Relation target_table;
778 Oid table_id;
779 Datum *role_oids = NULL;
780 int nitems = 0;
781 ArrayType *role_ids = NULL;
782 List *qual_parse_rtable = NIL;
783 List *with_check_parse_rtable = NIL;
784 Node *qual = NULL;
785 Node *with_check_qual = NULL;
786 ScanKeyData skey[2];
787 SysScanDesc sscan;
788 HeapTuple policy_tuple;
789 HeapTuple new_tuple;
790 Datum values[Natts_pg_policy];
791 bool isnull[Natts_pg_policy];
792 bool replaces[Natts_pg_policy];
793 ObjectAddress target;
794 ObjectAddress myself;
795 Datum polcmd_datum;
796 char polcmd;
797 bool polcmd_isnull;
798 int i;
799
800 /* Parse role_ids */
801 if (stmt->roles != NULL)
802 {
803 role_oids = policy_role_list_to_array(stmt->roles, &nitems);
804 role_ids = construct_array(role_oids, nitems, OIDOID,
805 sizeof(Oid), true, TYPALIGN_INT);
806 }
807
808 /* Get id of table. Also handles permissions checks. */
809 table_id = RangeVarGetRelidExtended(stmt->table, AccessExclusiveLock,
810 0,
811 RangeVarCallbackForPolicy,
812 (void *) stmt);
813
814 target_table = relation_open(table_id, NoLock);
815
816 /* Parse the using policy clause */
817 if (stmt->qual)
818 {
819 ParseNamespaceItem *nsitem;
820 ParseState *qual_pstate = make_parsestate(NULL);
821
822 nsitem = addRangeTableEntryForRelation(qual_pstate, target_table,
823 AccessShareLock,
824 NULL, false, false);
825
826 addNSItemToQuery(qual_pstate, nsitem, false, true, true);
827
828 qual = transformWhereClause(qual_pstate, stmt->qual,
829 EXPR_KIND_POLICY,
830 "POLICY");
831
832 /* Fix up collation information */
833 assign_expr_collations(qual_pstate, qual);
834
835 qual_parse_rtable = qual_pstate->p_rtable;
836 free_parsestate(qual_pstate);
837 }
838
839 /* Parse the with-check policy clause */
840 if (stmt->with_check)
841 {
842 ParseNamespaceItem *nsitem;
843 ParseState *with_check_pstate = make_parsestate(NULL);
844
845 nsitem = addRangeTableEntryForRelation(with_check_pstate, target_table,
846 AccessShareLock,
847 NULL, false, false);
848
849 addNSItemToQuery(with_check_pstate, nsitem, false, true, true);
850
851 with_check_qual = transformWhereClause(with_check_pstate,
852 stmt->with_check,
853 EXPR_KIND_POLICY,
854 "POLICY");
855
856 /* Fix up collation information */
857 assign_expr_collations(with_check_pstate, with_check_qual);
858
859 with_check_parse_rtable = with_check_pstate->p_rtable;
860 free_parsestate(with_check_pstate);
861 }
862
863 /* zero-clear */
864 memset(values, 0, sizeof(values));
865 memset(replaces, 0, sizeof(replaces));
866 memset(isnull, 0, sizeof(isnull));
867
868 /* Find policy to update. */
869 pg_policy_rel = table_open(PolicyRelationId, RowExclusiveLock);
870
871 /* Set key - policy's relation id. */
872 ScanKeyInit(&skey[0],
873 Anum_pg_policy_polrelid,
874 BTEqualStrategyNumber, F_OIDEQ,
875 ObjectIdGetDatum(table_id));
876
877 /* Set key - policy's name. */
878 ScanKeyInit(&skey[1],
879 Anum_pg_policy_polname,
880 BTEqualStrategyNumber, F_NAMEEQ,
881 CStringGetDatum(stmt->policy_name));
882
883 sscan = systable_beginscan(pg_policy_rel,
884 PolicyPolrelidPolnameIndexId, true, NULL, 2,
885 skey);
886
887 policy_tuple = systable_getnext(sscan);
888
889 /* Check that the policy is found, raise an error if not. */
890 if (!HeapTupleIsValid(policy_tuple))
891 ereport(ERROR,
892 (errcode(ERRCODE_UNDEFINED_OBJECT),
893 errmsg("policy \"%s\" for table \"%s\" does not exist",
894 stmt->policy_name,
895 RelationGetRelationName(target_table))));
896
897 /* Get policy command */
898 polcmd_datum = heap_getattr(policy_tuple, Anum_pg_policy_polcmd,
899 RelationGetDescr(pg_policy_rel),
900 &polcmd_isnull);
901 Assert(!polcmd_isnull);
902 polcmd = DatumGetChar(polcmd_datum);
903
904 /*
905 * If the command is SELECT or DELETE then WITH CHECK should be NULL.
906 */
907 if ((polcmd == ACL_SELECT_CHR || polcmd == ACL_DELETE_CHR)
908 && stmt->with_check != NULL)
909 ereport(ERROR,
910 (errcode(ERRCODE_SYNTAX_ERROR),
911 errmsg("only USING expression allowed for SELECT, DELETE")));
912
913 /*
914 * If the command is INSERT then WITH CHECK should be the only expression
915 * provided.
916 */
917 if ((polcmd == ACL_INSERT_CHR)
918 && stmt->qual != NULL)
919 ereport(ERROR,
920 (errcode(ERRCODE_SYNTAX_ERROR),
921 errmsg("only WITH CHECK expression allowed for INSERT")));
922
923 policy_id = ((Form_pg_policy) GETSTRUCT(policy_tuple))->oid;
924
925 if (role_ids != NULL)
926 {
927 replaces[Anum_pg_policy_polroles - 1] = true;
928 values[Anum_pg_policy_polroles - 1] = PointerGetDatum(role_ids);
929 }
930 else
931 {
932 Oid *roles;
933 Datum roles_datum;
934 bool attr_isnull;
935 ArrayType *policy_roles;
936
937 /*
938 * We need to pull the set of roles this policy applies to from what's
939 * in the catalog, so that we can recreate the dependencies correctly
940 * for the policy.
941 */
942
943 roles_datum = heap_getattr(policy_tuple, Anum_pg_policy_polroles,
944 RelationGetDescr(pg_policy_rel),
945 &attr_isnull);
946 Assert(!attr_isnull);
947
948 policy_roles = DatumGetArrayTypePCopy(roles_datum);
949
950 roles = (Oid *) ARR_DATA_PTR(policy_roles);
951
952 nitems = ARR_DIMS(policy_roles)[0];
953
954 role_oids = (Datum *) palloc(nitems * sizeof(Datum));
955
956 for (i = 0; i < nitems; i++)
957 role_oids[i] = ObjectIdGetDatum(roles[i]);
958 }
959
960 if (qual != NULL)
961 {
962 replaces[Anum_pg_policy_polqual - 1] = true;
963 values[Anum_pg_policy_polqual - 1]
964 = CStringGetTextDatum(nodeToString(qual));
965 }
966 else
967 {
968 Datum value_datum;
969 bool attr_isnull;
970
971 /*
972 * We need to pull the USING expression and build the range table for
973 * the policy from what's in the catalog, so that we can recreate the
974 * dependencies correctly for the policy.
975 */
976
977 /* Check if the policy has a USING expr */
978 value_datum = heap_getattr(policy_tuple, Anum_pg_policy_polqual,
979 RelationGetDescr(pg_policy_rel),
980 &attr_isnull);
981 if (!attr_isnull)
982 {
983 char *qual_value;
984 ParseState *qual_pstate;
985
986 /* parsestate is built just to build the range table */
987 qual_pstate = make_parsestate(NULL);
988
989 qual_value = TextDatumGetCString(value_datum);
990 qual = stringToNode(qual_value);
991
992 /* Add this rel to the parsestate's rangetable, for dependencies */
993 (void) addRangeTableEntryForRelation(qual_pstate, target_table,
994 AccessShareLock,
995 NULL, false, false);
996
997 qual_parse_rtable = qual_pstate->p_rtable;
998 free_parsestate(qual_pstate);
999 }
1000 }
1001
1002 if (with_check_qual != NULL)
1003 {
1004 replaces[Anum_pg_policy_polwithcheck - 1] = true;
1005 values[Anum_pg_policy_polwithcheck - 1]
1006 = CStringGetTextDatum(nodeToString(with_check_qual));
1007 }
1008 else
1009 {
1010 Datum value_datum;
1011 bool attr_isnull;
1012
1013 /*
1014 * We need to pull the WITH CHECK expression and build the range table
1015 * for the policy from what's in the catalog, so that we can recreate
1016 * the dependencies correctly for the policy.
1017 */
1018
1019 /* Check if the policy has a WITH CHECK expr */
1020 value_datum = heap_getattr(policy_tuple, Anum_pg_policy_polwithcheck,
1021 RelationGetDescr(pg_policy_rel),
1022 &attr_isnull);
1023 if (!attr_isnull)
1024 {
1025 char *with_check_value;
1026 ParseState *with_check_pstate;
1027
1028 /* parsestate is built just to build the range table */
1029 with_check_pstate = make_parsestate(NULL);
1030
1031 with_check_value = TextDatumGetCString(value_datum);
1032 with_check_qual = stringToNode(with_check_value);
1033
1034 /* Add this rel to the parsestate's rangetable, for dependencies */
1035 (void) addRangeTableEntryForRelation(with_check_pstate,
1036 target_table,
1037 AccessShareLock,
1038 NULL, false, false);
1039
1040 with_check_parse_rtable = with_check_pstate->p_rtable;
1041 free_parsestate(with_check_pstate);
1042 }
1043 }
1044
1045 new_tuple = heap_modify_tuple(policy_tuple,
1046 RelationGetDescr(pg_policy_rel),
1047 values, isnull, replaces);
1048 CatalogTupleUpdate(pg_policy_rel, &new_tuple->t_self, new_tuple);
1049
1050 /* Update Dependencies. */
1051 deleteDependencyRecordsFor(PolicyRelationId, policy_id, false);
1052
1053 /* Record Dependencies */
1054 target.classId = RelationRelationId;
1055 target.objectId = table_id;
1056 target.objectSubId = 0;
1057
1058 myself.classId = PolicyRelationId;
1059 myself.objectId = policy_id;
1060 myself.objectSubId = 0;
1061
1062 recordDependencyOn(&myself, &target, DEPENDENCY_AUTO);
1063
1064 recordDependencyOnExpr(&myself, qual, qual_parse_rtable, DEPENDENCY_NORMAL);
1065
1066 recordDependencyOnExpr(&myself, with_check_qual, with_check_parse_rtable,
1067 DEPENDENCY_NORMAL);
1068
1069 /* Register role dependencies */
1070 deleteSharedDependencyRecordsFor(PolicyRelationId, policy_id, 0);
1071 target.classId = AuthIdRelationId;
1072 target.objectSubId = 0;
1073 for (i = 0; i < nitems; i++)
1074 {
1075 target.objectId = DatumGetObjectId(role_oids[i]);
1076 /* no dependency if public */
1077 if (target.objectId != ACL_ID_PUBLIC)
1078 recordSharedDependencyOn(&myself, &target,
1079 SHARED_DEPENDENCY_POLICY);
1080 }
1081
1082 InvokeObjectPostAlterHook(PolicyRelationId, policy_id, 0);
1083
1084 heap_freetuple(new_tuple);
1085
1086 /* Invalidate Relation Cache */
1087 CacheInvalidateRelcache(target_table);
1088
1089 /* Clean up. */
1090 systable_endscan(sscan);
1091 relation_close(target_table, NoLock);
1092 table_close(pg_policy_rel, RowExclusiveLock);
1093
1094 return myself;
1095 }
1096
1097 /*
1098 * rename_policy -
1099 * change the name of a policy on a relation
1100 */
1101 ObjectAddress
rename_policy(RenameStmt * stmt)1102 rename_policy(RenameStmt *stmt)
1103 {
1104 Relation pg_policy_rel;
1105 Relation target_table;
1106 Oid table_id;
1107 Oid opoloid;
1108 ScanKeyData skey[2];
1109 SysScanDesc sscan;
1110 HeapTuple policy_tuple;
1111 ObjectAddress address;
1112
1113 /* Get id of table. Also handles permissions checks. */
1114 table_id = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
1115 0,
1116 RangeVarCallbackForPolicy,
1117 (void *) stmt);
1118
1119 target_table = relation_open(table_id, NoLock);
1120
1121 pg_policy_rel = table_open(PolicyRelationId, RowExclusiveLock);
1122
1123 /* First pass -- check for conflict */
1124
1125 /* Add key - policy's relation id. */
1126 ScanKeyInit(&skey[0],
1127 Anum_pg_policy_polrelid,
1128 BTEqualStrategyNumber, F_OIDEQ,
1129 ObjectIdGetDatum(table_id));
1130
1131 /* Add key - policy's name. */
1132 ScanKeyInit(&skey[1],
1133 Anum_pg_policy_polname,
1134 BTEqualStrategyNumber, F_NAMEEQ,
1135 CStringGetDatum(stmt->newname));
1136
1137 sscan = systable_beginscan(pg_policy_rel,
1138 PolicyPolrelidPolnameIndexId, true, NULL, 2,
1139 skey);
1140
1141 if (HeapTupleIsValid(systable_getnext(sscan)))
1142 ereport(ERROR,
1143 (errcode(ERRCODE_DUPLICATE_OBJECT),
1144 errmsg("policy \"%s\" for table \"%s\" already exists",
1145 stmt->newname, RelationGetRelationName(target_table))));
1146
1147 systable_endscan(sscan);
1148
1149 /* Second pass -- find existing policy and update */
1150 /* Add key - policy's relation id. */
1151 ScanKeyInit(&skey[0],
1152 Anum_pg_policy_polrelid,
1153 BTEqualStrategyNumber, F_OIDEQ,
1154 ObjectIdGetDatum(table_id));
1155
1156 /* Add key - policy's name. */
1157 ScanKeyInit(&skey[1],
1158 Anum_pg_policy_polname,
1159 BTEqualStrategyNumber, F_NAMEEQ,
1160 CStringGetDatum(stmt->subname));
1161
1162 sscan = systable_beginscan(pg_policy_rel,
1163 PolicyPolrelidPolnameIndexId, true, NULL, 2,
1164 skey);
1165
1166 policy_tuple = systable_getnext(sscan);
1167
1168 /* Complain if we did not find the policy */
1169 if (!HeapTupleIsValid(policy_tuple))
1170 ereport(ERROR,
1171 (errcode(ERRCODE_UNDEFINED_OBJECT),
1172 errmsg("policy \"%s\" for table \"%s\" does not exist",
1173 stmt->subname, RelationGetRelationName(target_table))));
1174
1175 opoloid = ((Form_pg_policy) GETSTRUCT(policy_tuple))->oid;
1176
1177 policy_tuple = heap_copytuple(policy_tuple);
1178
1179 namestrcpy(&((Form_pg_policy) GETSTRUCT(policy_tuple))->polname,
1180 stmt->newname);
1181
1182 CatalogTupleUpdate(pg_policy_rel, &policy_tuple->t_self, policy_tuple);
1183
1184 InvokeObjectPostAlterHook(PolicyRelationId, opoloid, 0);
1185
1186 ObjectAddressSet(address, PolicyRelationId, opoloid);
1187
1188 /*
1189 * Invalidate relation's relcache entry so that other backends (and this
1190 * one too!) are sent SI message to make them rebuild relcache entries.
1191 * (Ideally this should happen automatically...)
1192 */
1193 CacheInvalidateRelcache(target_table);
1194
1195 /* Clean up. */
1196 systable_endscan(sscan);
1197 table_close(pg_policy_rel, RowExclusiveLock);
1198 relation_close(target_table, NoLock);
1199
1200 return address;
1201 }
1202
1203 /*
1204 * get_relation_policy_oid - Look up a policy by name to find its OID
1205 *
1206 * If missing_ok is false, throw an error if policy not found. If
1207 * true, just return InvalidOid.
1208 */
1209 Oid
get_relation_policy_oid(Oid relid,const char * policy_name,bool missing_ok)1210 get_relation_policy_oid(Oid relid, const char *policy_name, bool missing_ok)
1211 {
1212 Relation pg_policy_rel;
1213 ScanKeyData skey[2];
1214 SysScanDesc sscan;
1215 HeapTuple policy_tuple;
1216 Oid policy_oid;
1217
1218 pg_policy_rel = table_open(PolicyRelationId, AccessShareLock);
1219
1220 /* Add key - policy's relation id. */
1221 ScanKeyInit(&skey[0],
1222 Anum_pg_policy_polrelid,
1223 BTEqualStrategyNumber, F_OIDEQ,
1224 ObjectIdGetDatum(relid));
1225
1226 /* Add key - policy's name. */
1227 ScanKeyInit(&skey[1],
1228 Anum_pg_policy_polname,
1229 BTEqualStrategyNumber, F_NAMEEQ,
1230 CStringGetDatum(policy_name));
1231
1232 sscan = systable_beginscan(pg_policy_rel,
1233 PolicyPolrelidPolnameIndexId, true, NULL, 2,
1234 skey);
1235
1236 policy_tuple = systable_getnext(sscan);
1237
1238 if (!HeapTupleIsValid(policy_tuple))
1239 {
1240 if (!missing_ok)
1241 ereport(ERROR,
1242 (errcode(ERRCODE_UNDEFINED_OBJECT),
1243 errmsg("policy \"%s\" for table \"%s\" does not exist",
1244 policy_name, get_rel_name(relid))));
1245
1246 policy_oid = InvalidOid;
1247 }
1248 else
1249 policy_oid = ((Form_pg_policy) GETSTRUCT(policy_tuple))->oid;
1250
1251 /* Clean up. */
1252 systable_endscan(sscan);
1253 table_close(pg_policy_rel, AccessShareLock);
1254
1255 return policy_oid;
1256 }
1257
1258 /*
1259 * relation_has_policies - Determine if relation has any policies
1260 */
1261 bool
relation_has_policies(Relation rel)1262 relation_has_policies(Relation rel)
1263 {
1264 Relation catalog;
1265 ScanKeyData skey;
1266 SysScanDesc sscan;
1267 HeapTuple policy_tuple;
1268 bool ret = false;
1269
1270 catalog = table_open(PolicyRelationId, AccessShareLock);
1271 ScanKeyInit(&skey,
1272 Anum_pg_policy_polrelid,
1273 BTEqualStrategyNumber, F_OIDEQ,
1274 ObjectIdGetDatum(RelationGetRelid(rel)));
1275 sscan = systable_beginscan(catalog, PolicyPolrelidPolnameIndexId, true,
1276 NULL, 1, &skey);
1277 policy_tuple = systable_getnext(sscan);
1278 if (HeapTupleIsValid(policy_tuple))
1279 ret = true;
1280
1281 systable_endscan(sscan);
1282 table_close(catalog, AccessShareLock);
1283
1284 return ret;
1285 }
1286