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