1 /*-------------------------------------------------------------------------
2 *
3 * policy.c
4 * Commands for manipulating policies.
5 *
6 * Portions Copyright (c) 1996-2017, 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 && relkind != RELKIND_PARTITIONED_TABLE)
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(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, permissive or restrictive */
254 policy->permissive = policy_form->polpermissive;
255
256 /* Get policy name */
257 policy->policy_name =
258 MemoryContextStrdup(rscxt, NameStr(policy_form->polname));
259
260 /* Get policy roles */
261 datum = heap_getattr(tuple, Anum_pg_policy_polroles,
262 RelationGetDescr(catalog), &isnull);
263 /* shouldn't be null, but let's check for luck */
264 if (isnull)
265 elog(ERROR, "unexpected null value in pg_policy.polroles");
266 MemoryContextSwitchTo(rscxt);
267 policy->roles = DatumGetArrayTypePCopy(datum);
268 MemoryContextSwitchTo(oldcxt);
269
270 /* Get policy qual */
271 datum = heap_getattr(tuple, Anum_pg_policy_polqual,
272 RelationGetDescr(catalog), &isnull);
273 if (!isnull)
274 {
275 str_value = TextDatumGetCString(datum);
276 MemoryContextSwitchTo(rscxt);
277 policy->qual = (Expr *) stringToNode(str_value);
278 MemoryContextSwitchTo(oldcxt);
279 pfree(str_value);
280 }
281 else
282 policy->qual = NULL;
283
284 /* Get WITH CHECK qual */
285 datum = heap_getattr(tuple, Anum_pg_policy_polwithcheck,
286 RelationGetDescr(catalog), &isnull);
287 if (!isnull)
288 {
289 str_value = TextDatumGetCString(datum);
290 MemoryContextSwitchTo(rscxt);
291 policy->with_check_qual = (Expr *) stringToNode(str_value);
292 MemoryContextSwitchTo(oldcxt);
293 pfree(str_value);
294 }
295 else
296 policy->with_check_qual = NULL;
297
298 /* We want to cache whether there are SubLinks in these expressions */
299 policy->hassublinks = checkExprHasSubLink((Node *) policy->qual) ||
300 checkExprHasSubLink((Node *) policy->with_check_qual);
301
302 /*
303 * Add this object to list. For historical reasons, the list is built
304 * in reverse order.
305 */
306 MemoryContextSwitchTo(rscxt);
307 rsdesc->policies = lcons(policy, rsdesc->policies);
308 MemoryContextSwitchTo(oldcxt);
309 }
310
311 systable_endscan(sscan);
312 heap_close(catalog, AccessShareLock);
313
314 /*
315 * Success. Reparent the descriptor's memory context under
316 * CacheMemoryContext so that it will live indefinitely, then attach the
317 * policy descriptor to the relcache entry.
318 */
319 MemoryContextSetParent(rscxt, CacheMemoryContext);
320
321 relation->rd_rsdesc = rsdesc;
322 }
323
324 /*
325 * RemovePolicyById -
326 * remove a policy by its OID. If a policy does not exist with the provided
327 * oid, then an error is raised.
328 *
329 * policy_id - the oid of the policy.
330 */
331 void
RemovePolicyById(Oid policy_id)332 RemovePolicyById(Oid policy_id)
333 {
334 Relation pg_policy_rel;
335 SysScanDesc sscan;
336 ScanKeyData skey[1];
337 HeapTuple tuple;
338 Oid relid;
339 Relation rel;
340
341 pg_policy_rel = heap_open(PolicyRelationId, RowExclusiveLock);
342
343 /*
344 * Find the policy to delete.
345 */
346 ScanKeyInit(&skey[0],
347 ObjectIdAttributeNumber,
348 BTEqualStrategyNumber, F_OIDEQ,
349 ObjectIdGetDatum(policy_id));
350
351 sscan = systable_beginscan(pg_policy_rel, PolicyOidIndexId, true,
352 NULL, 1, skey);
353
354 tuple = systable_getnext(sscan);
355
356 /* If the policy exists, then remove it, otherwise raise an error. */
357 if (!HeapTupleIsValid(tuple))
358 elog(ERROR, "could not find tuple for policy %u", policy_id);
359
360 /*
361 * Open and exclusive-lock the relation the policy belongs to. (We need
362 * exclusive lock to lock out queries that might otherwise depend on the
363 * set of policies the rel has; furthermore we've got to hold the lock
364 * till commit.)
365 */
366 relid = ((Form_pg_policy) GETSTRUCT(tuple))->polrelid;
367
368 rel = heap_open(relid, AccessExclusiveLock);
369 if (rel->rd_rel->relkind != RELKIND_RELATION &&
370 rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
371 ereport(ERROR,
372 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
373 errmsg("\"%s\" is not a table",
374 RelationGetRelationName(rel))));
375
376 if (!allowSystemTableMods && IsSystemRelation(rel))
377 ereport(ERROR,
378 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
379 errmsg("permission denied: \"%s\" is a system catalog",
380 RelationGetRelationName(rel))));
381
382 CatalogTupleDelete(pg_policy_rel, &tuple->t_self);
383
384 systable_endscan(sscan);
385
386 /*
387 * Note that, unlike some of the other flags in pg_class, relrowsecurity
388 * is not just an indication of if policies exist. When relrowsecurity is
389 * set by a user, then all access to the relation must be through a
390 * policy. If no policy is defined for the relation then a default-deny
391 * policy is created and all records are filtered (except for queries from
392 * the owner).
393 */
394 CacheInvalidateRelcache(rel);
395
396 heap_close(rel, NoLock);
397
398 /* Clean up */
399 heap_close(pg_policy_rel, RowExclusiveLock);
400 }
401
402 /*
403 * RemoveRoleFromObjectPolicy -
404 * remove a role from a policy's applicable-roles list.
405 *
406 * Returns true if the role was successfully removed from the policy.
407 * Returns false if the role was not removed because it would have left
408 * polroles empty (which is disallowed, though perhaps it should not be).
409 * On false return, the caller should instead drop the policy altogether.
410 *
411 * roleid - the oid of the role to remove
412 * classid - should always be PolicyRelationId
413 * policy_id - the oid of the policy.
414 */
415 bool
RemoveRoleFromObjectPolicy(Oid roleid,Oid classid,Oid policy_id)416 RemoveRoleFromObjectPolicy(Oid roleid, Oid classid, Oid policy_id)
417 {
418 Relation pg_policy_rel;
419 SysScanDesc sscan;
420 ScanKeyData skey[1];
421 HeapTuple tuple;
422 Oid relid;
423 ArrayType *policy_roles;
424 Datum roles_datum;
425 Oid *roles;
426 int num_roles;
427 Datum *role_oids;
428 bool attr_isnull;
429 bool keep_policy = true;
430 int i,
431 j;
432
433 Assert(classid == PolicyRelationId);
434
435 pg_policy_rel = heap_open(PolicyRelationId, RowExclusiveLock);
436
437 /*
438 * Find the policy to update.
439 */
440 ScanKeyInit(&skey[0],
441 ObjectIdAttributeNumber,
442 BTEqualStrategyNumber, F_OIDEQ,
443 ObjectIdGetDatum(policy_id));
444
445 sscan = systable_beginscan(pg_policy_rel, PolicyOidIndexId, true,
446 NULL, 1, skey);
447
448 tuple = systable_getnext(sscan);
449
450 /* Raise an error if we don't find the policy. */
451 if (!HeapTupleIsValid(tuple))
452 elog(ERROR, "could not find tuple for policy %u", policy_id);
453
454 /* Identify rel the policy belongs to */
455 relid = ((Form_pg_policy) GETSTRUCT(tuple))->polrelid;
456
457 /* Get the current set of roles */
458 roles_datum = heap_getattr(tuple,
459 Anum_pg_policy_polroles,
460 RelationGetDescr(pg_policy_rel),
461 &attr_isnull);
462
463 Assert(!attr_isnull);
464
465 policy_roles = DatumGetArrayTypePCopy(roles_datum);
466 roles = (Oid *) ARR_DATA_PTR(policy_roles);
467 num_roles = ARR_DIMS(policy_roles)[0];
468
469 /*
470 * Rebuild the polroles array, without any mentions of the target role.
471 * Ordinarily there'd be exactly one, but we must cope with duplicate
472 * mentions, since CREATE/ALTER POLICY historically have allowed that.
473 */
474 role_oids = (Datum *) palloc(num_roles * sizeof(Datum));
475 for (i = 0, j = 0; i < num_roles; i++)
476 {
477 if (roles[i] != roleid)
478 role_oids[j++] = ObjectIdGetDatum(roles[i]);
479 }
480 num_roles = j;
481
482 /* If any roles remain, update the policy entry. */
483 if (num_roles > 0)
484 {
485 ArrayType *role_ids;
486 Datum values[Natts_pg_policy];
487 bool isnull[Natts_pg_policy];
488 bool replaces[Natts_pg_policy];
489 HeapTuple new_tuple;
490 HeapTuple reltup;
491 ObjectAddress target;
492 ObjectAddress myself;
493
494 /* zero-clear */
495 memset(values, 0, sizeof(values));
496 memset(replaces, 0, sizeof(replaces));
497 memset(isnull, 0, sizeof(isnull));
498
499 /* This is the array for the new tuple */
500 role_ids = construct_array(role_oids, num_roles, OIDOID,
501 sizeof(Oid), true, 'i');
502
503 replaces[Anum_pg_policy_polroles - 1] = true;
504 values[Anum_pg_policy_polroles - 1] = PointerGetDatum(role_ids);
505
506 new_tuple = heap_modify_tuple(tuple,
507 RelationGetDescr(pg_policy_rel),
508 values, isnull, replaces);
509 CatalogTupleUpdate(pg_policy_rel, &new_tuple->t_self, new_tuple);
510
511 /* Remove all the old shared dependencies (roles) */
512 deleteSharedDependencyRecordsFor(PolicyRelationId, policy_id, 0);
513
514 /* Record the new shared dependencies (roles) */
515 myself.classId = PolicyRelationId;
516 myself.objectId = policy_id;
517 myself.objectSubId = 0;
518
519 target.classId = AuthIdRelationId;
520 target.objectSubId = 0;
521 for (i = 0; i < num_roles; i++)
522 {
523 target.objectId = DatumGetObjectId(role_oids[i]);
524 /* no need for dependency on the public role */
525 if (target.objectId != ACL_ID_PUBLIC)
526 recordSharedDependencyOn(&myself, &target,
527 SHARED_DEPENDENCY_POLICY);
528 }
529
530 InvokeObjectPostAlterHook(PolicyRelationId, policy_id, 0);
531
532 heap_freetuple(new_tuple);
533
534 /* Make updates visible */
535 CommandCounterIncrement();
536
537 /*
538 * Invalidate relcache entry for rel the policy belongs to, to force
539 * redoing any dependent plans. In case of a race condition where the
540 * rel was just dropped, we need do nothing.
541 */
542 reltup = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
543 if (HeapTupleIsValid(reltup))
544 {
545 CacheInvalidateRelcacheByTuple(reltup);
546 ReleaseSysCache(reltup);
547 }
548 }
549 else
550 {
551 /* No roles would remain, so drop the policy instead. */
552 keep_policy = false;
553 }
554
555 /* Clean up. */
556 systable_endscan(sscan);
557
558 heap_close(pg_policy_rel, RowExclusiveLock);
559
560 return keep_policy;
561 }
562
563 /*
564 * CreatePolicy -
565 * handles the execution of the CREATE POLICY command.
566 *
567 * stmt - the CreatePolicyStmt that describes the policy to create.
568 */
569 ObjectAddress
CreatePolicy(CreatePolicyStmt * stmt)570 CreatePolicy(CreatePolicyStmt *stmt)
571 {
572 Relation pg_policy_rel;
573 Oid policy_id;
574 Relation target_table;
575 Oid table_id;
576 char polcmd;
577 Datum *role_oids;
578 int nitems = 0;
579 ArrayType *role_ids;
580 ParseState *qual_pstate;
581 ParseState *with_check_pstate;
582 RangeTblEntry *rte;
583 Node *qual;
584 Node *with_check_qual;
585 ScanKeyData skey[2];
586 SysScanDesc sscan;
587 HeapTuple policy_tuple;
588 Datum values[Natts_pg_policy];
589 bool isnull[Natts_pg_policy];
590 ObjectAddress target;
591 ObjectAddress myself;
592 int i;
593
594 /* Parse command */
595 polcmd = parse_policy_command(stmt->cmd_name);
596
597 /*
598 * If the command is SELECT or DELETE then WITH CHECK should be NULL.
599 */
600 if ((polcmd == ACL_SELECT_CHR || polcmd == ACL_DELETE_CHR)
601 && stmt->with_check != NULL)
602 ereport(ERROR,
603 (errcode(ERRCODE_SYNTAX_ERROR),
604 errmsg("WITH CHECK cannot be applied to SELECT or DELETE")));
605
606 /*
607 * If the command is INSERT then WITH CHECK should be the only expression
608 * provided.
609 */
610 if (polcmd == ACL_INSERT_CHR && stmt->qual != NULL)
611 ereport(ERROR,
612 (errcode(ERRCODE_SYNTAX_ERROR),
613 errmsg("only WITH CHECK expression allowed for INSERT")));
614
615 /* Collect role ids */
616 role_oids = policy_role_list_to_array(stmt->roles, &nitems);
617 role_ids = construct_array(role_oids, nitems, OIDOID,
618 sizeof(Oid), true, 'i');
619
620 /* Parse the supplied clause */
621 qual_pstate = make_parsestate(NULL);
622 with_check_pstate = make_parsestate(NULL);
623
624 /* zero-clear */
625 memset(values, 0, sizeof(values));
626 memset(isnull, 0, sizeof(isnull));
627
628 /* Get id of table. Also handles permissions checks. */
629 table_id = RangeVarGetRelidExtended(stmt->table, AccessExclusiveLock,
630 false, false,
631 RangeVarCallbackForPolicy,
632 (void *) stmt);
633
634 /* Open target_table to build quals. No additional lock is necessary. */
635 target_table = relation_open(table_id, NoLock);
636
637 /* Add for the regular security quals */
638 rte = addRangeTableEntryForRelation(qual_pstate, target_table,
639 NULL, false, false);
640 addRTEtoQuery(qual_pstate, rte, false, true, true);
641
642 /* Add for the with-check quals */
643 rte = addRangeTableEntryForRelation(with_check_pstate, target_table,
644 NULL, false, false);
645 addRTEtoQuery(with_check_pstate, rte, false, true, true);
646
647 qual = transformWhereClause(qual_pstate,
648 copyObject(stmt->qual),
649 EXPR_KIND_POLICY,
650 "POLICY");
651
652 with_check_qual = transformWhereClause(with_check_pstate,
653 copyObject(stmt->with_check),
654 EXPR_KIND_POLICY,
655 "POLICY");
656
657 /* Fix up collation information */
658 assign_expr_collations(qual_pstate, qual);
659 assign_expr_collations(with_check_pstate, with_check_qual);
660
661 /* Open pg_policy catalog */
662 pg_policy_rel = heap_open(PolicyRelationId, RowExclusiveLock);
663
664 /* Set key - policy's relation id. */
665 ScanKeyInit(&skey[0],
666 Anum_pg_policy_polrelid,
667 BTEqualStrategyNumber, F_OIDEQ,
668 ObjectIdGetDatum(table_id));
669
670 /* Set key - policy's name. */
671 ScanKeyInit(&skey[1],
672 Anum_pg_policy_polname,
673 BTEqualStrategyNumber, F_NAMEEQ,
674 CStringGetDatum(stmt->policy_name));
675
676 sscan = systable_beginscan(pg_policy_rel,
677 PolicyPolrelidPolnameIndexId, true, NULL, 2,
678 skey);
679
680 policy_tuple = systable_getnext(sscan);
681
682 /* Complain if the policy name already exists for the table */
683 if (HeapTupleIsValid(policy_tuple))
684 ereport(ERROR,
685 (errcode(ERRCODE_DUPLICATE_OBJECT),
686 errmsg("policy \"%s\" for table \"%s\" already exists",
687 stmt->policy_name, RelationGetRelationName(target_table))));
688
689 values[Anum_pg_policy_polrelid - 1] = ObjectIdGetDatum(table_id);
690 values[Anum_pg_policy_polname - 1] = DirectFunctionCall1(namein,
691 CStringGetDatum(stmt->policy_name));
692 values[Anum_pg_policy_polcmd - 1] = CharGetDatum(polcmd);
693 values[Anum_pg_policy_polpermissive - 1] = BoolGetDatum(stmt->permissive);
694 values[Anum_pg_policy_polroles - 1] = PointerGetDatum(role_ids);
695
696 /* Add qual if present. */
697 if (qual)
698 values[Anum_pg_policy_polqual - 1] = CStringGetTextDatum(nodeToString(qual));
699 else
700 isnull[Anum_pg_policy_polqual - 1] = true;
701
702 /* Add WITH CHECK qual if present */
703 if (with_check_qual)
704 values[Anum_pg_policy_polwithcheck - 1] = CStringGetTextDatum(nodeToString(with_check_qual));
705 else
706 isnull[Anum_pg_policy_polwithcheck - 1] = true;
707
708 policy_tuple = heap_form_tuple(RelationGetDescr(pg_policy_rel), values,
709 isnull);
710
711 policy_id = CatalogTupleInsert(pg_policy_rel, policy_tuple);
712
713 /* Record Dependencies */
714 target.classId = RelationRelationId;
715 target.objectId = table_id;
716 target.objectSubId = 0;
717
718 myself.classId = PolicyRelationId;
719 myself.objectId = policy_id;
720 myself.objectSubId = 0;
721
722 recordDependencyOn(&myself, &target, DEPENDENCY_AUTO);
723
724 recordDependencyOnExpr(&myself, qual, qual_pstate->p_rtable,
725 DEPENDENCY_NORMAL);
726
727 recordDependencyOnExpr(&myself, with_check_qual,
728 with_check_pstate->p_rtable, DEPENDENCY_NORMAL);
729
730 /* Register role dependencies */
731 target.classId = AuthIdRelationId;
732 target.objectSubId = 0;
733 for (i = 0; i < nitems; i++)
734 {
735 target.objectId = DatumGetObjectId(role_oids[i]);
736 /* no dependency if public */
737 if (target.objectId != ACL_ID_PUBLIC)
738 recordSharedDependencyOn(&myself, &target,
739 SHARED_DEPENDENCY_POLICY);
740 }
741
742 InvokeObjectPostCreateHook(PolicyRelationId, policy_id, 0);
743
744 /* Invalidate Relation Cache */
745 CacheInvalidateRelcache(target_table);
746
747 /* Clean up. */
748 heap_freetuple(policy_tuple);
749 free_parsestate(qual_pstate);
750 free_parsestate(with_check_pstate);
751 systable_endscan(sscan);
752 relation_close(target_table, NoLock);
753 heap_close(pg_policy_rel, RowExclusiveLock);
754
755 return myself;
756 }
757
758 /*
759 * AlterPolicy -
760 * handles the execution of the ALTER POLICY command.
761 *
762 * stmt - the AlterPolicyStmt that describes the policy and how to alter it.
763 */
764 ObjectAddress
AlterPolicy(AlterPolicyStmt * stmt)765 AlterPolicy(AlterPolicyStmt *stmt)
766 {
767 Relation pg_policy_rel;
768 Oid policy_id;
769 Relation target_table;
770 Oid table_id;
771 Datum *role_oids = NULL;
772 int nitems = 0;
773 ArrayType *role_ids = NULL;
774 List *qual_parse_rtable = NIL;
775 List *with_check_parse_rtable = NIL;
776 Node *qual = NULL;
777 Node *with_check_qual = NULL;
778 ScanKeyData skey[2];
779 SysScanDesc sscan;
780 HeapTuple policy_tuple;
781 HeapTuple new_tuple;
782 Datum values[Natts_pg_policy];
783 bool isnull[Natts_pg_policy];
784 bool replaces[Natts_pg_policy];
785 ObjectAddress target;
786 ObjectAddress myself;
787 Datum polcmd_datum;
788 char polcmd;
789 bool polcmd_isnull;
790 int i;
791
792 /* Parse role_ids */
793 if (stmt->roles != NULL)
794 {
795 role_oids = policy_role_list_to_array(stmt->roles, &nitems);
796 role_ids = construct_array(role_oids, nitems, OIDOID,
797 sizeof(Oid), true, 'i');
798 }
799
800 /* Get id of table. Also handles permissions checks. */
801 table_id = RangeVarGetRelidExtended(stmt->table, AccessExclusiveLock,
802 false, false,
803 RangeVarCallbackForPolicy,
804 (void *) stmt);
805
806 target_table = relation_open(table_id, NoLock);
807
808 /* Parse the using policy clause */
809 if (stmt->qual)
810 {
811 RangeTblEntry *rte;
812 ParseState *qual_pstate = make_parsestate(NULL);
813
814 rte = addRangeTableEntryForRelation(qual_pstate, target_table,
815 NULL, false, false);
816
817 addRTEtoQuery(qual_pstate, rte, false, true, true);
818
819 qual = transformWhereClause(qual_pstate, copyObject(stmt->qual),
820 EXPR_KIND_POLICY,
821 "POLICY");
822
823 /* Fix up collation information */
824 assign_expr_collations(qual_pstate, qual);
825
826 qual_parse_rtable = qual_pstate->p_rtable;
827 free_parsestate(qual_pstate);
828 }
829
830 /* Parse the with-check policy clause */
831 if (stmt->with_check)
832 {
833 RangeTblEntry *rte;
834 ParseState *with_check_pstate = make_parsestate(NULL);
835
836 rte = addRangeTableEntryForRelation(with_check_pstate, target_table,
837 NULL, false, false);
838
839 addRTEtoQuery(with_check_pstate, rte, false, true, true);
840
841 with_check_qual = transformWhereClause(with_check_pstate,
842 copyObject(stmt->with_check),
843 EXPR_KIND_POLICY,
844 "POLICY");
845
846 /* Fix up collation information */
847 assign_expr_collations(with_check_pstate, with_check_qual);
848
849 with_check_parse_rtable = with_check_pstate->p_rtable;
850 free_parsestate(with_check_pstate);
851 }
852
853 /* zero-clear */
854 memset(values, 0, sizeof(values));
855 memset(replaces, 0, sizeof(replaces));
856 memset(isnull, 0, sizeof(isnull));
857
858 /* Find policy to update. */
859 pg_policy_rel = heap_open(PolicyRelationId, RowExclusiveLock);
860
861 /* Set key - policy's relation id. */
862 ScanKeyInit(&skey[0],
863 Anum_pg_policy_polrelid,
864 BTEqualStrategyNumber, F_OIDEQ,
865 ObjectIdGetDatum(table_id));
866
867 /* Set key - policy's name. */
868 ScanKeyInit(&skey[1],
869 Anum_pg_policy_polname,
870 BTEqualStrategyNumber, F_NAMEEQ,
871 CStringGetDatum(stmt->policy_name));
872
873 sscan = systable_beginscan(pg_policy_rel,
874 PolicyPolrelidPolnameIndexId, true, NULL, 2,
875 skey);
876
877 policy_tuple = systable_getnext(sscan);
878
879 /* Check that the policy is found, raise an error if not. */
880 if (!HeapTupleIsValid(policy_tuple))
881 ereport(ERROR,
882 (errcode(ERRCODE_UNDEFINED_OBJECT),
883 errmsg("policy \"%s\" for table \"%s\" does not exist",
884 stmt->policy_name,
885 RelationGetRelationName(target_table))));
886
887 /* Get policy command */
888 polcmd_datum = heap_getattr(policy_tuple, Anum_pg_policy_polcmd,
889 RelationGetDescr(pg_policy_rel),
890 &polcmd_isnull);
891 Assert(!polcmd_isnull);
892 polcmd = DatumGetChar(polcmd_datum);
893
894 /*
895 * If the command is SELECT or DELETE then WITH CHECK should be NULL.
896 */
897 if ((polcmd == ACL_SELECT_CHR || polcmd == ACL_DELETE_CHR)
898 && stmt->with_check != NULL)
899 ereport(ERROR,
900 (errcode(ERRCODE_SYNTAX_ERROR),
901 errmsg("only USING expression allowed for SELECT, DELETE")));
902
903 /*
904 * If the command is INSERT then WITH CHECK should be the only expression
905 * provided.
906 */
907 if ((polcmd == ACL_INSERT_CHR)
908 && stmt->qual != NULL)
909 ereport(ERROR,
910 (errcode(ERRCODE_SYNTAX_ERROR),
911 errmsg("only WITH CHECK expression allowed for INSERT")));
912
913 policy_id = HeapTupleGetOid(policy_tuple);
914
915 if (role_ids != NULL)
916 {
917 replaces[Anum_pg_policy_polroles - 1] = true;
918 values[Anum_pg_policy_polroles - 1] = PointerGetDatum(role_ids);
919 }
920 else
921 {
922 Oid *roles;
923 Datum roles_datum;
924 bool attr_isnull;
925 ArrayType *policy_roles;
926
927 /*
928 * We need to pull the set of roles this policy applies to from what's
929 * in the catalog, so that we can recreate the dependencies correctly
930 * for the policy.
931 */
932
933 roles_datum = heap_getattr(policy_tuple, Anum_pg_policy_polroles,
934 RelationGetDescr(pg_policy_rel),
935 &attr_isnull);
936 Assert(!attr_isnull);
937
938 policy_roles = DatumGetArrayTypePCopy(roles_datum);
939
940 roles = (Oid *) ARR_DATA_PTR(policy_roles);
941
942 nitems = ARR_DIMS(policy_roles)[0];
943
944 role_oids = (Datum *) palloc(nitems * sizeof(Datum));
945
946 for (i = 0; i < nitems; i++)
947 role_oids[i] = ObjectIdGetDatum(roles[i]);
948 }
949
950 if (qual != NULL)
951 {
952 replaces[Anum_pg_policy_polqual - 1] = true;
953 values[Anum_pg_policy_polqual - 1]
954 = CStringGetTextDatum(nodeToString(qual));
955 }
956 else
957 {
958 Datum value_datum;
959 bool attr_isnull;
960
961 /*
962 * We need to pull the USING expression and build the range table for
963 * the policy from what's in the catalog, so that we can recreate the
964 * dependencies correctly for the policy.
965 */
966
967 /* Check if the policy has a USING expr */
968 value_datum = heap_getattr(policy_tuple, Anum_pg_policy_polqual,
969 RelationGetDescr(pg_policy_rel),
970 &attr_isnull);
971 if (!attr_isnull)
972 {
973 char *qual_value;
974 ParseState *qual_pstate;
975
976 /* parsestate is built just to build the range table */
977 qual_pstate = make_parsestate(NULL);
978
979 qual_value = TextDatumGetCString(value_datum);
980 qual = stringToNode(qual_value);
981
982 /* Add this rel to the parsestate's rangetable, for dependencies */
983 addRangeTableEntryForRelation(qual_pstate, target_table, NULL,
984 false, false);
985
986 qual_parse_rtable = qual_pstate->p_rtable;
987 free_parsestate(qual_pstate);
988 }
989 }
990
991 if (with_check_qual != NULL)
992 {
993 replaces[Anum_pg_policy_polwithcheck - 1] = true;
994 values[Anum_pg_policy_polwithcheck - 1]
995 = CStringGetTextDatum(nodeToString(with_check_qual));
996 }
997 else
998 {
999 Datum value_datum;
1000 bool attr_isnull;
1001
1002 /*
1003 * We need to pull the WITH CHECK expression and build the range table
1004 * for the policy from what's in the catalog, so that we can recreate
1005 * the dependencies correctly for the policy.
1006 */
1007
1008 /* Check if the policy has a WITH CHECK expr */
1009 value_datum = heap_getattr(policy_tuple, Anum_pg_policy_polwithcheck,
1010 RelationGetDescr(pg_policy_rel),
1011 &attr_isnull);
1012 if (!attr_isnull)
1013 {
1014 char *with_check_value;
1015 ParseState *with_check_pstate;
1016
1017 /* parsestate is built just to build the range table */
1018 with_check_pstate = make_parsestate(NULL);
1019
1020 with_check_value = TextDatumGetCString(value_datum);
1021 with_check_qual = stringToNode(with_check_value);
1022
1023 /* Add this rel to the parsestate's rangetable, for dependencies */
1024 addRangeTableEntryForRelation(with_check_pstate, target_table, NULL,
1025 false, false);
1026
1027 with_check_parse_rtable = with_check_pstate->p_rtable;
1028 free_parsestate(with_check_pstate);
1029 }
1030 }
1031
1032 new_tuple = heap_modify_tuple(policy_tuple,
1033 RelationGetDescr(pg_policy_rel),
1034 values, isnull, replaces);
1035 CatalogTupleUpdate(pg_policy_rel, &new_tuple->t_self, new_tuple);
1036
1037 /* Update Dependencies. */
1038 deleteDependencyRecordsFor(PolicyRelationId, policy_id, false);
1039
1040 /* Record Dependencies */
1041 target.classId = RelationRelationId;
1042 target.objectId = table_id;
1043 target.objectSubId = 0;
1044
1045 myself.classId = PolicyRelationId;
1046 myself.objectId = policy_id;
1047 myself.objectSubId = 0;
1048
1049 recordDependencyOn(&myself, &target, DEPENDENCY_AUTO);
1050
1051 recordDependencyOnExpr(&myself, qual, qual_parse_rtable, DEPENDENCY_NORMAL);
1052
1053 recordDependencyOnExpr(&myself, with_check_qual, with_check_parse_rtable,
1054 DEPENDENCY_NORMAL);
1055
1056 /* Register role dependencies */
1057 deleteSharedDependencyRecordsFor(PolicyRelationId, policy_id, 0);
1058 target.classId = AuthIdRelationId;
1059 target.objectSubId = 0;
1060 for (i = 0; i < nitems; i++)
1061 {
1062 target.objectId = DatumGetObjectId(role_oids[i]);
1063 /* no dependency if public */
1064 if (target.objectId != ACL_ID_PUBLIC)
1065 recordSharedDependencyOn(&myself, &target,
1066 SHARED_DEPENDENCY_POLICY);
1067 }
1068
1069 InvokeObjectPostAlterHook(PolicyRelationId, policy_id, 0);
1070
1071 heap_freetuple(new_tuple);
1072
1073 /* Invalidate Relation Cache */
1074 CacheInvalidateRelcache(target_table);
1075
1076 /* Clean up. */
1077 systable_endscan(sscan);
1078 relation_close(target_table, NoLock);
1079 heap_close(pg_policy_rel, RowExclusiveLock);
1080
1081 return myself;
1082 }
1083
1084 /*
1085 * rename_policy -
1086 * change the name of a policy on a relation
1087 */
1088 ObjectAddress
rename_policy(RenameStmt * stmt)1089 rename_policy(RenameStmt *stmt)
1090 {
1091 Relation pg_policy_rel;
1092 Relation target_table;
1093 Oid table_id;
1094 Oid opoloid;
1095 ScanKeyData skey[2];
1096 SysScanDesc sscan;
1097 HeapTuple policy_tuple;
1098 ObjectAddress address;
1099
1100 /* Get id of table. Also handles permissions checks. */
1101 table_id = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
1102 false, false,
1103 RangeVarCallbackForPolicy,
1104 (void *) stmt);
1105
1106 target_table = relation_open(table_id, NoLock);
1107
1108 pg_policy_rel = heap_open(PolicyRelationId, RowExclusiveLock);
1109
1110 /* First pass -- check for conflict */
1111
1112 /* Add key - policy's relation id. */
1113 ScanKeyInit(&skey[0],
1114 Anum_pg_policy_polrelid,
1115 BTEqualStrategyNumber, F_OIDEQ,
1116 ObjectIdGetDatum(table_id));
1117
1118 /* Add key - policy's name. */
1119 ScanKeyInit(&skey[1],
1120 Anum_pg_policy_polname,
1121 BTEqualStrategyNumber, F_NAMEEQ,
1122 CStringGetDatum(stmt->newname));
1123
1124 sscan = systable_beginscan(pg_policy_rel,
1125 PolicyPolrelidPolnameIndexId, true, NULL, 2,
1126 skey);
1127
1128 if (HeapTupleIsValid(systable_getnext(sscan)))
1129 ereport(ERROR,
1130 (errcode(ERRCODE_DUPLICATE_OBJECT),
1131 errmsg("policy \"%s\" for table \"%s\" already exists",
1132 stmt->newname, RelationGetRelationName(target_table))));
1133
1134 systable_endscan(sscan);
1135
1136 /* Second pass -- find existing policy and update */
1137 /* Add key - policy's relation id. */
1138 ScanKeyInit(&skey[0],
1139 Anum_pg_policy_polrelid,
1140 BTEqualStrategyNumber, F_OIDEQ,
1141 ObjectIdGetDatum(table_id));
1142
1143 /* Add key - policy's name. */
1144 ScanKeyInit(&skey[1],
1145 Anum_pg_policy_polname,
1146 BTEqualStrategyNumber, F_NAMEEQ,
1147 CStringGetDatum(stmt->subname));
1148
1149 sscan = systable_beginscan(pg_policy_rel,
1150 PolicyPolrelidPolnameIndexId, true, NULL, 2,
1151 skey);
1152
1153 policy_tuple = systable_getnext(sscan);
1154
1155 /* Complain if we did not find the policy */
1156 if (!HeapTupleIsValid(policy_tuple))
1157 ereport(ERROR,
1158 (errcode(ERRCODE_UNDEFINED_OBJECT),
1159 errmsg("policy \"%s\" for table \"%s\" does not exist",
1160 stmt->subname, RelationGetRelationName(target_table))));
1161
1162 opoloid = HeapTupleGetOid(policy_tuple);
1163
1164 policy_tuple = heap_copytuple(policy_tuple);
1165
1166 namestrcpy(&((Form_pg_policy) GETSTRUCT(policy_tuple))->polname,
1167 stmt->newname);
1168
1169 CatalogTupleUpdate(pg_policy_rel, &policy_tuple->t_self, policy_tuple);
1170
1171 InvokeObjectPostAlterHook(PolicyRelationId,
1172 HeapTupleGetOid(policy_tuple), 0);
1173
1174 ObjectAddressSet(address, PolicyRelationId, opoloid);
1175
1176 /*
1177 * Invalidate relation's relcache entry so that other backends (and this
1178 * one too!) are sent SI message to make them rebuild relcache entries.
1179 * (Ideally this should happen automatically...)
1180 */
1181 CacheInvalidateRelcache(target_table);
1182
1183 /* Clean up. */
1184 systable_endscan(sscan);
1185 heap_close(pg_policy_rel, RowExclusiveLock);
1186 relation_close(target_table, NoLock);
1187
1188 return address;
1189 }
1190
1191 /*
1192 * get_relation_policy_oid - Look up a policy by name to find its OID
1193 *
1194 * If missing_ok is false, throw an error if policy not found. If
1195 * true, just return InvalidOid.
1196 */
1197 Oid
get_relation_policy_oid(Oid relid,const char * policy_name,bool missing_ok)1198 get_relation_policy_oid(Oid relid, const char *policy_name, bool missing_ok)
1199 {
1200 Relation pg_policy_rel;
1201 ScanKeyData skey[2];
1202 SysScanDesc sscan;
1203 HeapTuple policy_tuple;
1204 Oid policy_oid;
1205
1206 pg_policy_rel = heap_open(PolicyRelationId, AccessShareLock);
1207
1208 /* Add key - policy's relation id. */
1209 ScanKeyInit(&skey[0],
1210 Anum_pg_policy_polrelid,
1211 BTEqualStrategyNumber, F_OIDEQ,
1212 ObjectIdGetDatum(relid));
1213
1214 /* Add key - policy's name. */
1215 ScanKeyInit(&skey[1],
1216 Anum_pg_policy_polname,
1217 BTEqualStrategyNumber, F_NAMEEQ,
1218 CStringGetDatum(policy_name));
1219
1220 sscan = systable_beginscan(pg_policy_rel,
1221 PolicyPolrelidPolnameIndexId, true, NULL, 2,
1222 skey);
1223
1224 policy_tuple = systable_getnext(sscan);
1225
1226 if (!HeapTupleIsValid(policy_tuple))
1227 {
1228 if (!missing_ok)
1229 ereport(ERROR,
1230 (errcode(ERRCODE_UNDEFINED_OBJECT),
1231 errmsg("policy \"%s\" for table \"%s\" does not exist",
1232 policy_name, get_rel_name(relid))));
1233
1234 policy_oid = InvalidOid;
1235 }
1236 else
1237 policy_oid = HeapTupleGetOid(policy_tuple);
1238
1239 /* Clean up. */
1240 systable_endscan(sscan);
1241 heap_close(pg_policy_rel, AccessShareLock);
1242
1243 return policy_oid;
1244 }
1245
1246 /*
1247 * relation_has_policies - Determine if relation has any policies
1248 */
1249 bool
relation_has_policies(Relation rel)1250 relation_has_policies(Relation rel)
1251 {
1252 Relation catalog;
1253 ScanKeyData skey;
1254 SysScanDesc sscan;
1255 HeapTuple policy_tuple;
1256 bool ret = false;
1257
1258 catalog = heap_open(PolicyRelationId, AccessShareLock);
1259 ScanKeyInit(&skey,
1260 Anum_pg_policy_polrelid,
1261 BTEqualStrategyNumber, F_OIDEQ,
1262 ObjectIdGetDatum(RelationGetRelid(rel)));
1263 sscan = systable_beginscan(catalog, PolicyPolrelidPolnameIndexId, true,
1264 NULL, 1, &skey);
1265 policy_tuple = systable_getnext(sscan);
1266 if (HeapTupleIsValid(policy_tuple))
1267 ret = true;
1268
1269 systable_endscan(sscan);
1270 heap_close(catalog, AccessShareLock);
1271
1272 return ret;
1273 }
1274