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