Coverage Report

Created: 2026-08-13 07:12

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgres/src/backend/utils/adt/acl.c
Line
Count
Source
1
/*-------------------------------------------------------------------------
2
 *
3
 * acl.c
4
 *    Basic access control list data structures manipulation routines.
5
 *
6
 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7
 * Portions Copyright (c) 1994, Regents of the University of California
8
 *
9
 *
10
 * IDENTIFICATION
11
 *    src/backend/utils/adt/acl.c
12
 *
13
 *-------------------------------------------------------------------------
14
 */
15
#include "postgres.h"
16
17
#include <ctype.h>
18
19
#include "access/htup_details.h"
20
#include "bootstrap/bootstrap.h"
21
#include "catalog/catalog.h"
22
#include "catalog/namespace.h"
23
#include "catalog/pg_auth_members.h"
24
#include "catalog/pg_authid.h"
25
#include "catalog/pg_class.h"
26
#include "catalog/pg_database.h"
27
#include "catalog/pg_foreign_data_wrapper.h"
28
#include "catalog/pg_foreign_server.h"
29
#include "catalog/pg_language.h"
30
#include "catalog/pg_largeobject.h"
31
#include "catalog/pg_namespace.h"
32
#include "catalog/pg_proc.h"
33
#include "catalog/pg_tablespace.h"
34
#include "catalog/pg_type.h"
35
#include "commands/proclang.h"
36
#include "commands/tablespace.h"
37
#include "common/hashfn.h"
38
#include "foreign/foreign.h"
39
#include "funcapi.h"
40
#include "lib/bloomfilter.h"
41
#include "lib/qunique.h"
42
#include "miscadmin.h"
43
#include "port/pg_bitutils.h"
44
#include "storage/large_object.h"
45
#include "utils/acl.h"
46
#include "utils/array.h"
47
#include "utils/builtins.h"
48
#include "utils/catcache.h"
49
#include "utils/inval.h"
50
#include "utils/lsyscache.h"
51
#include "utils/memutils.h"
52
#include "utils/snapmgr.h"
53
#include "utils/syscache.h"
54
#include "utils/varlena.h"
55
56
typedef struct
57
{
58
  const char *name;
59
  AclMode   value;
60
} priv_map;
61
62
/*
63
 * We frequently need to test whether a given role is a member of some other
64
 * role.  In most of these tests the "given role" is the same, namely the
65
 * active current user.  So we can optimize it by keeping cached lists of all
66
 * the roles the "given role" is a member of, directly or indirectly.
67
 *
68
 * Possibly this mechanism should be generalized to allow caching membership
69
 * info for multiple roles?
70
 *
71
 * Each element of cached_roles is an OID list of constituent roles for the
72
 * corresponding element of cached_role (always including the cached_role
73
 * itself).  There's a separate cache for each RoleRecurseType, with the
74
 * corresponding semantics.
75
 */
76
enum RoleRecurseType
77
{
78
  ROLERECURSE_MEMBERS = 0,  /* recurse unconditionally */
79
  ROLERECURSE_PRIVS = 1,    /* recurse through inheritable grants */
80
  ROLERECURSE_SETROLE = 2   /* recurse through grants with set_option */
81
};
82
static Oid  cached_role[] = {InvalidOid, InvalidOid, InvalidOid};
83
static List *cached_roles[] = {NIL, NIL, NIL};
84
uint32    cached_db_hash;
85
86
/*
87
 * If the list of roles gathered by roles_is_member_of() grows larger than the
88
 * below threshold, a Bloom filter is created to speed up list membership
89
 * checks.  This threshold is set arbitrarily high to avoid the overhead of
90
 * creating the Bloom filter until it seems likely to provide a net benefit.
91
 */
92
0
#define ROLES_LIST_BLOOM_THRESHOLD 1024
93
94
static const char *getid(const char *s, char *n, Node *escontext);
95
static void putid(char *p, const char *s);
96
static Acl *allocacl(int n);
97
static void check_acl(const Acl *acl);
98
static const char *aclparse(const char *s, AclItem *aip, Node *escontext);
99
static bool aclitem_match(const AclItem *a1, const AclItem *a2);
100
static int  aclitemComparator(const void *arg1, const void *arg2);
101
static void check_circularity(const Acl *old_acl, const AclItem *mod_aip,
102
                Oid ownerId);
103
static Acl *recursive_revoke(Acl *acl, Oid grantee, AclMode revoke_privs,
104
               Oid ownerId, DropBehavior behavior);
105
106
static AclMode convert_any_priv_string(text *priv_type_text,
107
                     const priv_map *privileges);
108
109
static Oid  convert_table_name(text *tablename);
110
static AclMode convert_table_priv_string(text *priv_type_text);
111
static AclMode convert_sequence_priv_string(text *priv_type_text);
112
static AttrNumber convert_column_name(Oid tableoid, text *column);
113
static AclMode convert_column_priv_string(text *priv_type_text);
114
static Oid  convert_database_name(text *databasename);
115
static AclMode convert_database_priv_string(text *priv_type_text);
116
static Oid  convert_foreign_data_wrapper_name(text *fdwname);
117
static AclMode convert_foreign_data_wrapper_priv_string(text *priv_type_text);
118
static Oid  convert_function_name(text *functionname);
119
static AclMode convert_function_priv_string(text *priv_type_text);
120
static Oid  convert_language_name(text *languagename);
121
static AclMode convert_language_priv_string(text *priv_type_text);
122
static Oid  convert_schema_name(text *schemaname);
123
static AclMode convert_schema_priv_string(text *priv_type_text);
124
static Oid  convert_server_name(text *servername);
125
static AclMode convert_server_priv_string(text *priv_type_text);
126
static Oid  convert_tablespace_name(text *tablespacename);
127
static AclMode convert_tablespace_priv_string(text *priv_type_text);
128
static Oid  convert_type_name(text *typename);
129
static AclMode convert_type_priv_string(text *priv_type_text);
130
static AclMode convert_parameter_priv_string(text *priv_text);
131
static AclMode convert_largeobject_priv_string(text *priv_type_text);
132
static AclMode convert_role_priv_string(text *priv_type_text);
133
static AclResult pg_role_aclcheck(Oid role_oid, Oid roleid, AclMode mode);
134
135
static void RoleMembershipCacheCallback(Datum arg, SysCacheIdentifier cacheid,
136
                    uint32 hashvalue);
137
138
139
/*
140
 * Test whether an identifier char can be left unquoted in ACLs.
141
 *
142
 * Formerly, we used isalnum() even on non-ASCII characters, resulting in
143
 * unportable behavior.  To ensure dump compatibility with old versions,
144
 * we now treat high-bit-set characters as always requiring quoting during
145
 * putid(), but getid() will always accept them without quotes.
146
 */
147
static inline bool
148
is_safe_acl_char(unsigned char c, bool is_getid)
149
0
{
150
0
  if (IS_HIGHBIT_SET(c))
151
0
    return is_getid;
152
0
  return isalnum(c) || c == '_';
153
0
}
154
155
/*
156
 * getid
157
 *    Consumes the first alphanumeric string (identifier) found in string
158
 *    's', ignoring any leading white space.  If it finds a double quote
159
 *    it returns the word inside the quotes.
160
 *
161
 * RETURNS:
162
 *    the string position in 's' that points to the next non-space character
163
 *    in 's', after any quotes.  Also:
164
 *    - loads the identifier into 'n'.  (If no identifier is found, 'n'
165
 *      contains an empty string.)  'n' must be NAMEDATALEN bytes.
166
 *
167
 * Errors are reported via ereport, unless escontext is an ErrorSaveData node,
168
 * in which case we log the error there and return NULL.
169
 */
170
static const char *
171
getid(const char *s, char *n, Node *escontext)
172
0
{
173
0
  int     len = 0;
174
0
  bool    in_quotes = false;
175
176
0
  Assert(s && n);
177
178
0
  while (isspace((unsigned char) *s))
179
0
    s++;
180
0
  for (;
181
0
     *s != '\0' &&
182
0
     (in_quotes || *s == '"' || is_safe_acl_char(*s, true));
183
0
     s++)
184
0
  {
185
0
    if (*s == '"')
186
0
    {
187
0
      if (!in_quotes)
188
0
      {
189
0
        in_quotes = true;
190
0
        continue;
191
0
      }
192
      /* safe to look at next char (could be '\0' though) */
193
0
      if (*(s + 1) != '"')
194
0
      {
195
0
        in_quotes = false;
196
0
        continue;
197
0
      }
198
      /* it's an escaped double quote; skip the escaping char */
199
0
      s++;
200
0
    }
201
202
    /* Add the character to the string */
203
0
    if (len >= NAMEDATALEN - 1)
204
0
      ereturn(escontext, NULL,
205
0
          (errcode(ERRCODE_NAME_TOO_LONG),
206
0
           errmsg("identifier too long"),
207
0
           errdetail("Identifier must be less than %d characters.",
208
0
                 NAMEDATALEN)));
209
210
0
    n[len++] = *s;
211
0
  }
212
0
  n[len] = '\0';
213
0
  while (isspace((unsigned char) *s))
214
0
    s++;
215
0
  return s;
216
0
}
217
218
/*
219
 * Write a role name at *p, adding double quotes if needed.
220
 * There must be at least (2*NAMEDATALEN)+2 bytes available at *p.
221
 * This needs to be kept in sync with dequoteAclUserName in pg_dump/dumputils.c
222
 */
223
static void
224
putid(char *p, const char *s)
225
0
{
226
0
  const char *src;
227
0
  bool    safe = true;
228
229
  /* Detect whether we need to use double quotes */
230
0
  for (src = s; *src; src++)
231
0
  {
232
0
    if (!is_safe_acl_char(*src, false))
233
0
    {
234
0
      safe = false;
235
0
      break;
236
0
    }
237
0
  }
238
0
  if (!safe)
239
0
    *p++ = '"';
240
0
  for (src = s; *src; src++)
241
0
  {
242
    /* A double quote character in a username is encoded as "" */
243
0
    if (*src == '"')
244
0
      *p++ = '"';
245
0
    *p++ = *src;
246
0
  }
247
0
  if (!safe)
248
0
    *p++ = '"';
249
0
  *p = '\0';
250
0
}
251
252
/*
253
 * aclparse
254
 *    Consumes and parses an ACL specification of the form:
255
 *        [group|user] [A-Za-z0-9]*=[rwaR]*
256
 *    from string 's', ignoring any leading white space or white space
257
 *    between the optional id type keyword (group|user) and the actual
258
 *    ACL specification.
259
 *
260
 *    The group|user decoration is unnecessary in the roles world,
261
 *    but we still accept it for backward compatibility.
262
 *
263
 *    This routine is called by the parser as well as aclitemin(), hence
264
 *    the added generality.
265
 *
266
 *    In bootstrap mode, we consult a hard-wired list of role names
267
 *    (see bootstrap.c) rather than trying to access the catalogs.
268
 *
269
 * RETURNS:
270
 *    the string position in 's' immediately following the ACL
271
 *    specification.  Also:
272
 *    - loads the structure pointed to by 'aip' with the appropriate
273
 *      UID/GID, id type identifier and mode type values.
274
 *
275
 * Errors are reported via ereport, unless escontext is an ErrorSaveData node,
276
 * in which case we log the error there and return NULL.
277
 */
278
static const char *
279
aclparse(const char *s, AclItem *aip, Node *escontext)
280
{
281
  AclMode   privs,
282
        goption,
283
        read;
284
  char    name[NAMEDATALEN];
285
  char    name2[NAMEDATALEN];
286
287
  Assert(s && aip);
288
289
  s = getid(s, name, escontext);
290
  if (s == NULL)
291
    return NULL;
292
  if (*s != '=')
293
  {
294
    /* we just read a keyword, not a name */
295
    if (strcmp(name, "group") != 0 && strcmp(name, "user") != 0)
296
      ereturn(escontext, NULL,
297
          (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
298
           errmsg("unrecognized key word: \"%s\"", name),
299
           errhint("ACL key word must be \"group\" or \"user\".")));
300
    /* move s to the name beyond the keyword */
301
    s = getid(s, name, escontext);
302
    if (s == NULL)
303
      return NULL;
304
    if (name[0] == '\0')
305
      ereturn(escontext, NULL,
306
          (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
307
           errmsg("missing name"),
308
           errhint("A name must follow the \"group\" or \"user\" key word.")));
309
  }
310
311
  if (*s != '=')
312
    ereturn(escontext, NULL,
313
        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
314
         errmsg("missing \"=\" sign")));
315
316
  privs = goption = ACL_NO_RIGHTS;
317
318
  for (++s, read = 0; isalpha((unsigned char) *s) || *s == '*'; s++)
319
  {
320
    switch (*s)
321
    {
322
      case '*':
323
        goption |= read;
324
        break;
325
      case ACL_INSERT_CHR:
326
        read = ACL_INSERT;
327
        break;
328
      case ACL_SELECT_CHR:
329
        read = ACL_SELECT;
330
        break;
331
      case ACL_UPDATE_CHR:
332
        read = ACL_UPDATE;
333
        break;
334
      case ACL_DELETE_CHR:
335
        read = ACL_DELETE;
336
        break;
337
      case ACL_TRUNCATE_CHR:
338
        read = ACL_TRUNCATE;
339
        break;
340
      case ACL_REFERENCES_CHR:
341
        read = ACL_REFERENCES;
342
        break;
343
      case ACL_TRIGGER_CHR:
344
        read = ACL_TRIGGER;
345
        break;
346
      case ACL_EXECUTE_CHR:
347
        read = ACL_EXECUTE;
348
        break;
349
      case ACL_USAGE_CHR:
350
        read = ACL_USAGE;
351
        break;
352
      case ACL_CREATE_CHR:
353
        read = ACL_CREATE;
354
        break;
355
      case ACL_CREATE_TEMP_CHR:
356
        read = ACL_CREATE_TEMP;
357
        break;
358
      case ACL_CONNECT_CHR:
359
        read = ACL_CONNECT;
360
        break;
361
      case ACL_SET_CHR:
362
        read = ACL_SET;
363
        break;
364
      case ACL_ALTER_SYSTEM_CHR:
365
        read = ACL_ALTER_SYSTEM;
366
        break;
367
      case ACL_MAINTAIN_CHR:
368
        read = ACL_MAINTAIN;
369
        break;
370
      default:
371
        ereturn(escontext, NULL,
372
            (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
373
             errmsg("invalid mode character: must be one of \"%s\"",
374
                ACL_ALL_RIGHTS_STR)));
375
    }
376
377
    privs |= read;
378
  }
379
380
  if (name[0] == '\0')
381
    aip->ai_grantee = ACL_ID_PUBLIC;
382
  else
383
  {
384
    if (IsBootstrapProcessingMode())
385
      aip->ai_grantee = boot_get_role_oid(name);
386
    else
387
      aip->ai_grantee = get_role_oid(name, true);
388
    if (!OidIsValid(aip->ai_grantee))
389
      ereturn(escontext, NULL,
390
          (errcode(ERRCODE_UNDEFINED_OBJECT),
391
           errmsg("role \"%s\" does not exist", name)));
392
  }
393
394
  /*
395
   * XXX Allow a degree of backward compatibility by defaulting the grantor
396
   * to the superuser.  We condone that practice in the catalog .dat files
397
   * (i.e., in bootstrap mode) for brevity; otherwise, issue a warning.
398
   */
399
  if (*s == '/')
400
  {
401
    s = getid(s + 1, name2, escontext);
402
    if (s == NULL)
403
      return NULL;
404
    if (name2[0] == '\0')
405
      ereturn(escontext, NULL,
406
          (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
407
           errmsg("a name must follow the \"/\" sign")));
408
    if (IsBootstrapProcessingMode())
409
      aip->ai_grantor = boot_get_role_oid(name2);
410
    else
411
      aip->ai_grantor = get_role_oid(name2, true);
412
    if (!OidIsValid(aip->ai_grantor))
413
      ereturn(escontext, NULL,
414
          (errcode(ERRCODE_UNDEFINED_OBJECT),
415
           errmsg("role \"%s\" does not exist", name2)));
416
  }
417
  else
418
  {
419
    aip->ai_grantor = BOOTSTRAP_SUPERUSERID;
420
    if (!IsBootstrapProcessingMode())
421
      ereport(WARNING,
422
          (errcode(ERRCODE_INVALID_GRANTOR),
423
           errmsg("defaulting grantor to user ID %u",
424
              BOOTSTRAP_SUPERUSERID)));
425
  }
426
427
  ACLITEM_SET_PRIVS_GOPTIONS(*aip, privs, goption);
428
429
  return s;
430
}
431
432
/*
433
 * allocacl
434
 *    Allocates storage for a new Acl with 'n' entries.
435
 *
436
 * RETURNS:
437
 *    the new Acl
438
 */
439
static Acl *
440
allocacl(int n)
441
0
{
442
0
  Acl      *new_acl;
443
0
  Size    size;
444
445
0
  if (n < 0)
446
0
    elog(ERROR, "invalid size: %d", n);
447
0
  size = ACL_N_SIZE(n);
448
0
  new_acl = (Acl *) palloc0(size);
449
0
  SET_VARSIZE(new_acl, size);
450
0
  new_acl->ndim = 1;
451
0
  new_acl->dataoffset = 0;  /* we never put in any nulls */
452
0
  new_acl->elemtype = ACLITEMOID;
453
0
  ARR_LBOUND(new_acl)[0] = 1;
454
0
  ARR_DIMS(new_acl)[0] = n;
455
0
  return new_acl;
456
0
}
457
458
/*
459
 * Create a zero-entry ACL
460
 */
461
Acl *
462
make_empty_acl(void)
463
0
{
464
0
  return allocacl(0);
465
0
}
466
467
/*
468
 * Copy an ACL
469
 */
470
Acl *
471
aclcopy(const Acl *orig_acl)
472
0
{
473
0
  Acl      *result_acl;
474
475
0
  result_acl = allocacl(ACL_NUM(orig_acl));
476
477
0
  memcpy(ACL_DAT(result_acl),
478
0
       ACL_DAT(orig_acl),
479
0
       ACL_NUM(orig_acl) * sizeof(AclItem));
480
481
0
  return result_acl;
482
0
}
483
484
/*
485
 * Concatenate two ACLs
486
 *
487
 * This is a bit cheesy, since we may produce an ACL with redundant entries.
488
 * Be careful what the result is used for!
489
 */
490
Acl *
491
aclconcat(const Acl *left_acl, const Acl *right_acl)
492
0
{
493
0
  Acl      *result_acl;
494
495
0
  result_acl = allocacl(ACL_NUM(left_acl) + ACL_NUM(right_acl));
496
497
0
  memcpy(ACL_DAT(result_acl),
498
0
       ACL_DAT(left_acl),
499
0
       ACL_NUM(left_acl) * sizeof(AclItem));
500
501
0
  memcpy(ACL_DAT(result_acl) + ACL_NUM(left_acl),
502
0
       ACL_DAT(right_acl),
503
0
       ACL_NUM(right_acl) * sizeof(AclItem));
504
505
0
  return result_acl;
506
0
}
507
508
/*
509
 * Merge two ACLs
510
 *
511
 * This produces a properly merged ACL with no redundant entries.
512
 * Returns NULL on NULL input.
513
 */
514
Acl *
515
aclmerge(const Acl *left_acl, const Acl *right_acl, Oid ownerId)
516
0
{
517
0
  Acl      *result_acl;
518
0
  AclItem    *aip;
519
0
  int     i,
520
0
        num;
521
522
  /* Check for cases where one or both are empty/null */
523
0
  if (left_acl == NULL || ACL_NUM(left_acl) == 0)
524
0
  {
525
0
    if (right_acl == NULL || ACL_NUM(right_acl) == 0)
526
0
      return NULL;
527
0
    else
528
0
      return aclcopy(right_acl);
529
0
  }
530
0
  else
531
0
  {
532
0
    if (right_acl == NULL || ACL_NUM(right_acl) == 0)
533
0
      return aclcopy(left_acl);
534
0
  }
535
536
  /* Merge them the hard way, one item at a time */
537
0
  result_acl = aclcopy(left_acl);
538
539
0
  aip = ACL_DAT(right_acl);
540
0
  num = ACL_NUM(right_acl);
541
542
0
  for (i = 0; i < num; i++, aip++)
543
0
  {
544
0
    Acl      *tmp_acl;
545
546
0
    tmp_acl = aclupdate(result_acl, aip, ACL_MODECHG_ADD,
547
0
              ownerId, DROP_RESTRICT);
548
0
    pfree(result_acl);
549
0
    result_acl = tmp_acl;
550
0
  }
551
552
0
  return result_acl;
553
0
}
554
555
/*
556
 * Sort the items in an ACL (into an arbitrary but consistent order)
557
 */
558
void
559
aclitemsort(Acl *acl)
560
0
{
561
0
  if (acl != NULL && ACL_NUM(acl) > 1)
562
0
    qsort(ACL_DAT(acl), ACL_NUM(acl), sizeof(AclItem), aclitemComparator);
563
0
}
564
565
/*
566
 * Check if two ACLs are exactly equal
567
 *
568
 * This will not detect equality if the two arrays contain the same items
569
 * in different orders.  To handle that case, sort both inputs first,
570
 * using aclitemsort().
571
 */
572
bool
573
aclequal(const Acl *left_acl, const Acl *right_acl)
574
0
{
575
  /* Check for cases where one or both are empty/null */
576
0
  if (left_acl == NULL || ACL_NUM(left_acl) == 0)
577
0
  {
578
0
    if (right_acl == NULL || ACL_NUM(right_acl) == 0)
579
0
      return true;
580
0
    else
581
0
      return false;
582
0
  }
583
0
  else
584
0
  {
585
0
    if (right_acl == NULL || ACL_NUM(right_acl) == 0)
586
0
      return false;
587
0
  }
588
589
0
  if (ACL_NUM(left_acl) != ACL_NUM(right_acl))
590
0
    return false;
591
592
0
  if (memcmp(ACL_DAT(left_acl),
593
0
         ACL_DAT(right_acl),
594
0
         ACL_NUM(left_acl) * sizeof(AclItem)) == 0)
595
0
    return true;
596
597
0
  return false;
598
0
}
599
600
/*
601
 * Verify that an ACL array is acceptable (one-dimensional and has no nulls)
602
 */
603
static void
604
check_acl(const Acl *acl)
605
0
{
606
0
  if (ARR_ELEMTYPE(acl) != ACLITEMOID)
607
0
    ereport(ERROR,
608
0
        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
609
0
         errmsg("ACL array contains wrong data type")));
610
0
  if (ARR_NDIM(acl) != 1)
611
0
    ereport(ERROR,
612
0
        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
613
0
         errmsg("ACL arrays must be one-dimensional")));
614
0
  if (ARR_HASNULL(acl))
615
0
    ereport(ERROR,
616
0
        (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
617
0
         errmsg("ACL arrays must not contain null values")));
618
0
}
619
620
/*
621
 * aclitemin
622
 *    Allocates storage for, and fills in, a new AclItem given a string
623
 *    's' that contains an ACL specification.  See aclparse for details.
624
 *
625
 * RETURNS:
626
 *    the new AclItem
627
 */
628
Datum
629
aclitemin(PG_FUNCTION_ARGS)
630
0
{
631
0
  const char *s = PG_GETARG_CSTRING(0);
632
0
  Node     *escontext = fcinfo->context;
633
0
  AclItem    *aip;
634
635
0
  aip = palloc_object(AclItem);
636
637
0
  s = aclparse(s, aip, escontext);
638
0
  if (s == NULL)
639
0
    PG_RETURN_NULL();
640
641
0
  while (isspace((unsigned char) *s))
642
0
    ++s;
643
0
  if (*s)
644
0
    ereturn(escontext, (Datum) 0,
645
0
        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
646
0
         errmsg("extra garbage at the end of the ACL specification")));
647
648
0
  PG_RETURN_ACLITEM_P(aip);
649
0
}
650
651
/*
652
 * aclitemout
653
 *    Allocates storage for, and fills in, a new null-delimited string
654
 *    containing a formatted ACL specification.  See aclparse for details.
655
 *
656
 *    In bootstrap mode, this is called for debug printouts (initdb -d).
657
 *    We could ask bootstrap.c to provide an inverse of boot_get_role_oid(),
658
 *    but it seems at least as useful to just print numeric role OIDs.
659
 *
660
 * RETURNS:
661
 *    the new string
662
 */
663
Datum
664
aclitemout(PG_FUNCTION_ARGS)
665
0
{
666
0
  AclItem    *aip = PG_GETARG_ACLITEM_P(0);
667
0
  char     *p;
668
0
  char     *out;
669
0
  HeapTuple htup;
670
0
  unsigned  i;
671
672
0
  out = palloc(strlen("=/") +
673
0
         2 * N_ACL_RIGHTS +
674
0
         2 * (2 * NAMEDATALEN + 2) +
675
0
         1);
676
677
0
  p = out;
678
0
  *p = '\0';
679
680
0
  if (aip->ai_grantee != ACL_ID_PUBLIC)
681
0
  {
682
0
    if (!IsBootstrapProcessingMode())
683
0
      htup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(aip->ai_grantee));
684
0
    else
685
0
      htup = NULL;
686
0
    if (HeapTupleIsValid(htup))
687
0
    {
688
0
      putid(p, NameStr(((Form_pg_authid) GETSTRUCT(htup))->rolname));
689
0
      ReleaseSysCache(htup);
690
0
    }
691
0
    else
692
0
    {
693
      /* No such entry, or bootstrap mode: print numeric OID */
694
0
      sprintf(p, "%u", aip->ai_grantee);
695
0
    }
696
0
  }
697
0
  while (*p)
698
0
    ++p;
699
700
0
  *p++ = '=';
701
702
0
  for (i = 0; i < N_ACL_RIGHTS; ++i)
703
0
  {
704
0
    if (ACLITEM_GET_PRIVS(*aip) & (UINT64CONST(1) << i))
705
0
      *p++ = ACL_ALL_RIGHTS_STR[i];
706
0
    if (ACLITEM_GET_GOPTIONS(*aip) & (UINT64CONST(1) << i))
707
0
      *p++ = '*';
708
0
  }
709
710
0
  *p++ = '/';
711
0
  *p = '\0';
712
713
0
  if (!IsBootstrapProcessingMode())
714
0
    htup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(aip->ai_grantor));
715
0
  else
716
0
    htup = NULL;
717
0
  if (HeapTupleIsValid(htup))
718
0
  {
719
0
    putid(p, NameStr(((Form_pg_authid) GETSTRUCT(htup))->rolname));
720
0
    ReleaseSysCache(htup);
721
0
  }
722
0
  else
723
0
  {
724
    /* No such entry, or bootstrap mode: print numeric OID */
725
0
    sprintf(p, "%u", aip->ai_grantor);
726
0
  }
727
728
0
  PG_RETURN_CSTRING(out);
729
0
}
730
731
/*
732
 * aclitem_match
733
 *    Two AclItems are considered to match iff they have the same
734
 *    grantee and grantor; the privileges are ignored.
735
 */
736
static bool
737
aclitem_match(const AclItem *a1, const AclItem *a2)
738
0
{
739
0
  return a1->ai_grantee == a2->ai_grantee &&
740
0
    a1->ai_grantor == a2->ai_grantor;
741
0
}
742
743
/*
744
 * aclitemComparator
745
 *    qsort comparison function for AclItems
746
 */
747
static int
748
aclitemComparator(const void *arg1, const void *arg2)
749
0
{
750
0
  const AclItem *a1 = (const AclItem *) arg1;
751
0
  const AclItem *a2 = (const AclItem *) arg2;
752
753
0
  if (a1->ai_grantee > a2->ai_grantee)
754
0
    return 1;
755
0
  if (a1->ai_grantee < a2->ai_grantee)
756
0
    return -1;
757
0
  if (a1->ai_grantor > a2->ai_grantor)
758
0
    return 1;
759
0
  if (a1->ai_grantor < a2->ai_grantor)
760
0
    return -1;
761
0
  if (a1->ai_privs > a2->ai_privs)
762
0
    return 1;
763
0
  if (a1->ai_privs < a2->ai_privs)
764
0
    return -1;
765
0
  return 0;
766
0
}
767
768
/*
769
 * aclitem equality operator
770
 */
771
Datum
772
aclitem_eq(PG_FUNCTION_ARGS)
773
0
{
774
0
  AclItem    *a1 = PG_GETARG_ACLITEM_P(0);
775
0
  AclItem    *a2 = PG_GETARG_ACLITEM_P(1);
776
0
  bool    result;
777
778
0
  result = a1->ai_privs == a2->ai_privs &&
779
0
    a1->ai_grantee == a2->ai_grantee &&
780
0
    a1->ai_grantor == a2->ai_grantor;
781
0
  PG_RETURN_BOOL(result);
782
0
}
783
784
/*
785
 * aclitem hash function
786
 *
787
 * We make aclitems hashable not so much because anyone is likely to hash
788
 * them, as because we want array equality to work on aclitem arrays, and
789
 * with the typcache mechanism we must have a hash or btree opclass.
790
 */
791
Datum
792
hash_aclitem(PG_FUNCTION_ARGS)
793
0
{
794
0
  AclItem    *a = PG_GETARG_ACLITEM_P(0);
795
796
  /* not very bright, but avoids any issue of padding in struct */
797
0
  PG_RETURN_UINT32((uint32) (a->ai_privs + a->ai_grantee + a->ai_grantor));
798
0
}
799
800
/*
801
 * 64-bit hash function for aclitem.
802
 *
803
 * Similar to hash_aclitem, but accepts a seed and returns a uint64 value.
804
 */
805
Datum
806
hash_aclitem_extended(PG_FUNCTION_ARGS)
807
0
{
808
0
  AclItem    *a = PG_GETARG_ACLITEM_P(0);
809
0
  uint64    seed = PG_GETARG_INT64(1);
810
0
  uint32    sum = (uint32) (a->ai_privs + a->ai_grantee + a->ai_grantor);
811
812
0
  return (seed == 0) ? UInt64GetDatum(sum) : hash_uint32_extended(sum, seed);
813
0
}
814
815
/*
816
 * acldefault()  --- create an ACL describing default access permissions
817
 *
818
 * Change this routine if you want to alter the default access policy for
819
 * newly-created objects (or any object with a NULL acl entry).  When
820
 * you make a change here, don't forget to update the GRANT man page,
821
 * which explains all the default permissions.
822
 *
823
 * Note that these are the hard-wired "defaults" that are used in the
824
 * absence of any pg_default_acl entry.
825
 */
826
Acl *
827
acldefault(ObjectType objtype, Oid ownerId)
828
0
{
829
0
  AclMode   world_default;
830
0
  AclMode   owner_default;
831
0
  int     nacl;
832
0
  Acl      *acl;
833
0
  AclItem    *aip;
834
835
0
  switch (objtype)
836
0
  {
837
0
    case OBJECT_COLUMN:
838
      /* by default, columns have no extra privileges */
839
0
      world_default = ACL_NO_RIGHTS;
840
0
      owner_default = ACL_NO_RIGHTS;
841
0
      break;
842
0
    case OBJECT_TABLE:
843
0
      world_default = ACL_NO_RIGHTS;
844
0
      owner_default = ACL_ALL_RIGHTS_RELATION;
845
0
      break;
846
0
    case OBJECT_SEQUENCE:
847
0
      world_default = ACL_NO_RIGHTS;
848
0
      owner_default = ACL_ALL_RIGHTS_SEQUENCE;
849
0
      break;
850
0
    case OBJECT_DATABASE:
851
      /* for backwards compatibility, grant some rights by default */
852
0
      world_default = ACL_CREATE_TEMP | ACL_CONNECT;
853
0
      owner_default = ACL_ALL_RIGHTS_DATABASE;
854
0
      break;
855
0
    case OBJECT_FUNCTION:
856
      /* Grant EXECUTE by default, for now */
857
0
      world_default = ACL_EXECUTE;
858
0
      owner_default = ACL_ALL_RIGHTS_FUNCTION;
859
0
      break;
860
0
    case OBJECT_LANGUAGE:
861
      /* Grant USAGE by default, for now */
862
0
      world_default = ACL_USAGE;
863
0
      owner_default = ACL_ALL_RIGHTS_LANGUAGE;
864
0
      break;
865
0
    case OBJECT_LARGEOBJECT:
866
0
      world_default = ACL_NO_RIGHTS;
867
0
      owner_default = ACL_ALL_RIGHTS_LARGEOBJECT;
868
0
      break;
869
0
    case OBJECT_SCHEMA:
870
0
      world_default = ACL_NO_RIGHTS;
871
0
      owner_default = ACL_ALL_RIGHTS_SCHEMA;
872
0
      break;
873
0
    case OBJECT_TABLESPACE:
874
0
      world_default = ACL_NO_RIGHTS;
875
0
      owner_default = ACL_ALL_RIGHTS_TABLESPACE;
876
0
      break;
877
0
    case OBJECT_FDW:
878
0
      world_default = ACL_NO_RIGHTS;
879
0
      owner_default = ACL_ALL_RIGHTS_FDW;
880
0
      break;
881
0
    case OBJECT_FOREIGN_SERVER:
882
0
      world_default = ACL_NO_RIGHTS;
883
0
      owner_default = ACL_ALL_RIGHTS_FOREIGN_SERVER;
884
0
      break;
885
0
    case OBJECT_DOMAIN:
886
0
    case OBJECT_TYPE:
887
0
      world_default = ACL_USAGE;
888
0
      owner_default = ACL_ALL_RIGHTS_TYPE;
889
0
      break;
890
0
    case OBJECT_PARAMETER_ACL:
891
0
      world_default = ACL_NO_RIGHTS;
892
0
      owner_default = ACL_ALL_RIGHTS_PARAMETER_ACL;
893
0
      break;
894
0
    case OBJECT_PROPGRAPH:
895
0
      world_default = ACL_NO_RIGHTS;
896
0
      owner_default = ACL_ALL_RIGHTS_PROPGRAPH;
897
0
      break;
898
0
    default:
899
0
      elog(ERROR, "unrecognized object type: %d", (int) objtype);
900
0
      world_default = ACL_NO_RIGHTS; /* keep compiler quiet */
901
0
      owner_default = ACL_NO_RIGHTS;
902
0
      break;
903
0
  }
904
905
0
  nacl = 0;
906
0
  if (world_default != ACL_NO_RIGHTS)
907
0
    nacl++;
908
0
  if (owner_default != ACL_NO_RIGHTS)
909
0
    nacl++;
910
911
0
  acl = allocacl(nacl);
912
0
  aip = ACL_DAT(acl);
913
914
0
  if (world_default != ACL_NO_RIGHTS)
915
0
  {
916
0
    aip->ai_grantee = ACL_ID_PUBLIC;
917
0
    aip->ai_grantor = ownerId;
918
0
    ACLITEM_SET_PRIVS_GOPTIONS(*aip, world_default, ACL_NO_RIGHTS);
919
0
    aip++;
920
0
  }
921
922
  /*
923
   * Note that the owner's entry shows all ordinary privileges but no grant
924
   * options.  This is because his grant options come "from the system" and
925
   * not from his own efforts.  (The SQL spec says that the owner's rights
926
   * come from a "_SYSTEM" authid.)  However, we do consider that the
927
   * owner's ordinary privileges are self-granted; this lets him revoke
928
   * them.  We implement the owner's grant options without any explicit
929
   * "_SYSTEM"-like ACL entry, by internally special-casing the owner
930
   * wherever we are testing grant options.
931
   */
932
0
  if (owner_default != ACL_NO_RIGHTS)
933
0
  {
934
0
    aip->ai_grantee = ownerId;
935
0
    aip->ai_grantor = ownerId;
936
0
    ACLITEM_SET_PRIVS_GOPTIONS(*aip, owner_default, ACL_NO_RIGHTS);
937
0
  }
938
939
0
  return acl;
940
0
}
941
942
943
/*
944
 * SQL-accessible version of acldefault().  Hackish mapping from "char" type to
945
 * OBJECT_* values.
946
 */
947
Datum
948
acldefault_sql(PG_FUNCTION_ARGS)
949
0
{
950
0
  char    objtypec = PG_GETARG_CHAR(0);
951
0
  Oid     owner = PG_GETARG_OID(1);
952
0
  ObjectType  objtype = 0;
953
954
0
  switch (objtypec)
955
0
  {
956
0
    case 'c':
957
0
      objtype = OBJECT_COLUMN;
958
0
      break;
959
0
    case 'g':
960
0
      objtype = OBJECT_PROPGRAPH;
961
0
      break;
962
0
    case 'r':
963
0
      objtype = OBJECT_TABLE;
964
0
      break;
965
0
    case 's':
966
0
      objtype = OBJECT_SEQUENCE;
967
0
      break;
968
0
    case 'd':
969
0
      objtype = OBJECT_DATABASE;
970
0
      break;
971
0
    case 'f':
972
0
      objtype = OBJECT_FUNCTION;
973
0
      break;
974
0
    case 'l':
975
0
      objtype = OBJECT_LANGUAGE;
976
0
      break;
977
0
    case 'L':
978
0
      objtype = OBJECT_LARGEOBJECT;
979
0
      break;
980
0
    case 'n':
981
0
      objtype = OBJECT_SCHEMA;
982
0
      break;
983
0
    case 'p':
984
0
      objtype = OBJECT_PARAMETER_ACL;
985
0
      break;
986
0
    case 't':
987
0
      objtype = OBJECT_TABLESPACE;
988
0
      break;
989
0
    case 'F':
990
0
      objtype = OBJECT_FDW;
991
0
      break;
992
0
    case 'S':
993
0
      objtype = OBJECT_FOREIGN_SERVER;
994
0
      break;
995
0
    case 'T':
996
0
      objtype = OBJECT_TYPE;
997
0
      break;
998
0
    default:
999
0
      elog(ERROR, "unrecognized object type abbreviation: %c", objtypec);
1000
0
  }
1001
1002
0
  PG_RETURN_ACL_P(acldefault(objtype, owner));
1003
0
}
1004
1005
1006
/*
1007
 * Update an ACL array to add or remove specified privileges.
1008
 *
1009
 *  old_acl: the input ACL array
1010
 *  mod_aip: defines the privileges to be added, removed, or substituted
1011
 *  modechg: ACL_MODECHG_ADD, ACL_MODECHG_DEL, or ACL_MODECHG_EQL
1012
 *  ownerId: Oid of object owner
1013
 *  behavior: RESTRICT or CASCADE behavior for recursive removal
1014
 *
1015
 * ownerid and behavior are only relevant when the update operation specifies
1016
 * deletion of grant options.
1017
 *
1018
 * The result is a modified copy; the input object is not changed.
1019
 *
1020
 * NB: caller is responsible for having detoasted the input ACL, if needed.
1021
 */
1022
Acl *
1023
aclupdate(const Acl *old_acl, const AclItem *mod_aip,
1024
      int modechg, Oid ownerId, DropBehavior behavior)
1025
0
{
1026
0
  Acl      *new_acl = NULL;
1027
0
  AclItem    *old_aip,
1028
0
         *new_aip = NULL;
1029
0
  AclMode   old_rights,
1030
0
        old_goptions,
1031
0
        new_rights,
1032
0
        new_goptions;
1033
0
  int     dst,
1034
0
        num;
1035
1036
  /* Caller probably already checked old_acl, but be safe */
1037
0
  check_acl(old_acl);
1038
1039
  /* If granting grant options, check for circularity */
1040
0
  if (modechg != ACL_MODECHG_DEL &&
1041
0
    ACLITEM_GET_GOPTIONS(*mod_aip) != ACL_NO_RIGHTS)
1042
0
    check_circularity(old_acl, mod_aip, ownerId);
1043
1044
0
  num = ACL_NUM(old_acl);
1045
0
  old_aip = ACL_DAT(old_acl);
1046
1047
  /*
1048
   * Search the ACL for an existing entry for this grantee and grantor. If
1049
   * one exists, just modify the entry in-place (well, in the same position,
1050
   * since we actually return a copy); otherwise, insert the new entry at
1051
   * the end.
1052
   */
1053
1054
0
  for (dst = 0; dst < num; ++dst)
1055
0
  {
1056
0
    if (aclitem_match(mod_aip, old_aip + dst))
1057
0
    {
1058
      /* found a match, so modify existing item */
1059
0
      new_acl = allocacl(num);
1060
0
      new_aip = ACL_DAT(new_acl);
1061
0
      memcpy(new_acl, old_acl, ACL_SIZE(old_acl));
1062
0
      break;
1063
0
    }
1064
0
  }
1065
1066
0
  if (dst == num)
1067
0
  {
1068
    /* need to append a new item */
1069
0
    new_acl = allocacl(num + 1);
1070
0
    new_aip = ACL_DAT(new_acl);
1071
0
    memcpy(new_aip, old_aip, num * sizeof(AclItem));
1072
1073
    /* initialize the new entry with no permissions */
1074
0
    new_aip[dst].ai_grantee = mod_aip->ai_grantee;
1075
0
    new_aip[dst].ai_grantor = mod_aip->ai_grantor;
1076
0
    ACLITEM_SET_PRIVS_GOPTIONS(new_aip[dst],
1077
0
                   ACL_NO_RIGHTS, ACL_NO_RIGHTS);
1078
0
    num++;          /* set num to the size of new_acl */
1079
0
  }
1080
1081
0
  old_rights = ACLITEM_GET_RIGHTS(new_aip[dst]);
1082
0
  old_goptions = ACLITEM_GET_GOPTIONS(new_aip[dst]);
1083
1084
  /* apply the specified permissions change */
1085
0
  switch (modechg)
1086
0
  {
1087
0
    case ACL_MODECHG_ADD:
1088
0
      ACLITEM_SET_RIGHTS(new_aip[dst],
1089
0
                 old_rights | ACLITEM_GET_RIGHTS(*mod_aip));
1090
0
      break;
1091
0
    case ACL_MODECHG_DEL:
1092
0
      ACLITEM_SET_RIGHTS(new_aip[dst],
1093
0
                 old_rights & ~ACLITEM_GET_RIGHTS(*mod_aip));
1094
0
      break;
1095
0
    case ACL_MODECHG_EQL:
1096
0
      ACLITEM_SET_RIGHTS(new_aip[dst],
1097
0
                 ACLITEM_GET_RIGHTS(*mod_aip));
1098
0
      break;
1099
0
  }
1100
1101
0
  new_rights = ACLITEM_GET_RIGHTS(new_aip[dst]);
1102
0
  new_goptions = ACLITEM_GET_GOPTIONS(new_aip[dst]);
1103
1104
  /*
1105
   * If the adjusted entry has no permissions, delete it from the list.
1106
   */
1107
0
  if (new_rights == ACL_NO_RIGHTS)
1108
0
  {
1109
0
    memmove(new_aip + dst,
1110
0
        new_aip + dst + 1,
1111
0
        (num - dst - 1) * sizeof(AclItem));
1112
    /* Adjust array size to be 'num - 1' items */
1113
0
    ARR_DIMS(new_acl)[0] = num - 1;
1114
0
    SET_VARSIZE(new_acl, ACL_N_SIZE(num - 1));
1115
0
  }
1116
1117
  /*
1118
   * Remove abandoned privileges (cascading revoke).  Currently we can only
1119
   * handle this when the grantee is not PUBLIC.
1120
   */
1121
0
  if ((old_goptions & ~new_goptions) != 0)
1122
0
  {
1123
0
    Assert(mod_aip->ai_grantee != ACL_ID_PUBLIC);
1124
0
    new_acl = recursive_revoke(new_acl, mod_aip->ai_grantee,
1125
0
                   (old_goptions & ~new_goptions),
1126
0
                   ownerId, behavior);
1127
0
  }
1128
1129
0
  return new_acl;
1130
0
}
1131
1132
/*
1133
 * Update an ACL array to reflect a change of owner to the parent object
1134
 *
1135
 *  old_acl: the input ACL array (must not be NULL)
1136
 *  oldOwnerId: Oid of the old object owner
1137
 *  newOwnerId: Oid of the new object owner
1138
 *
1139
 * The result is a modified copy; the input object is not changed.
1140
 *
1141
 * NB: caller is responsible for having detoasted the input ACL, if needed.
1142
 *
1143
 * Note: the name of this function is a bit of a misnomer, since it will
1144
 * happily make the specified role substitution whether the old role is
1145
 * really the owner of the parent object or merely mentioned in its ACL.
1146
 * But the vast majority of callers use it in connection with ALTER OWNER
1147
 * operations, so we'll keep the name.
1148
 */
1149
Acl *
1150
aclnewowner(const Acl *old_acl, Oid oldOwnerId, Oid newOwnerId)
1151
0
{
1152
0
  Acl      *new_acl;
1153
0
  AclItem    *new_aip;
1154
0
  AclItem    *old_aip;
1155
0
  AclItem    *dst_aip;
1156
0
  AclItem    *src_aip;
1157
0
  AclItem    *targ_aip;
1158
0
  bool    newpresent = false;
1159
0
  int     dst,
1160
0
        src,
1161
0
        targ,
1162
0
        num;
1163
1164
0
  check_acl(old_acl);
1165
1166
  /*
1167
   * Make a copy of the given ACL, substituting new owner ID for old
1168
   * wherever it appears as either grantor or grantee.  Also note if the new
1169
   * owner ID is already present.
1170
   */
1171
0
  num = ACL_NUM(old_acl);
1172
0
  old_aip = ACL_DAT(old_acl);
1173
0
  new_acl = allocacl(num);
1174
0
  new_aip = ACL_DAT(new_acl);
1175
0
  memcpy(new_aip, old_aip, num * sizeof(AclItem));
1176
0
  for (dst = 0, dst_aip = new_aip; dst < num; dst++, dst_aip++)
1177
0
  {
1178
0
    if (dst_aip->ai_grantor == oldOwnerId)
1179
0
      dst_aip->ai_grantor = newOwnerId;
1180
0
    else if (dst_aip->ai_grantor == newOwnerId)
1181
0
      newpresent = true;
1182
0
    if (dst_aip->ai_grantee == oldOwnerId)
1183
0
      dst_aip->ai_grantee = newOwnerId;
1184
0
    else if (dst_aip->ai_grantee == newOwnerId)
1185
0
      newpresent = true;
1186
0
  }
1187
1188
  /*
1189
   * If the old ACL contained any references to the new owner, then we may
1190
   * now have generated an ACL containing duplicate entries.  Find them and
1191
   * merge them so that there are not duplicates.  (This is relatively
1192
   * expensive since we use a stupid O(N^2) algorithm, but it's unlikely to
1193
   * be the normal case.)
1194
   *
1195
   * To simplify deletion of duplicate entries, we temporarily leave them in
1196
   * the array but set their privilege masks to zero; when we reach such an
1197
   * entry it's just skipped.  (Thus, a side effect of this code will be to
1198
   * remove privilege-free entries, should there be any in the input.)  dst
1199
   * is the next output slot, targ is the currently considered input slot
1200
   * (always >= dst), and src scans entries to the right of targ looking for
1201
   * duplicates.  Once an entry has been emitted to dst it is known
1202
   * duplicate-free and need not be considered anymore.
1203
   */
1204
0
  if (newpresent)
1205
0
  {
1206
0
    dst = 0;
1207
0
    for (targ = 0, targ_aip = new_aip; targ < num; targ++, targ_aip++)
1208
0
    {
1209
      /* ignore if deleted in an earlier pass */
1210
0
      if (ACLITEM_GET_RIGHTS(*targ_aip) == ACL_NO_RIGHTS)
1211
0
        continue;
1212
      /* find and merge any duplicates */
1213
0
      for (src = targ + 1, src_aip = targ_aip + 1; src < num;
1214
0
         src++, src_aip++)
1215
0
      {
1216
0
        if (ACLITEM_GET_RIGHTS(*src_aip) == ACL_NO_RIGHTS)
1217
0
          continue;
1218
0
        if (aclitem_match(targ_aip, src_aip))
1219
0
        {
1220
0
          ACLITEM_SET_RIGHTS(*targ_aip,
1221
0
                     ACLITEM_GET_RIGHTS(*targ_aip) |
1222
0
                     ACLITEM_GET_RIGHTS(*src_aip));
1223
          /* mark the duplicate deleted */
1224
0
          ACLITEM_SET_RIGHTS(*src_aip, ACL_NO_RIGHTS);
1225
0
        }
1226
0
      }
1227
      /* and emit to output */
1228
0
      new_aip[dst] = *targ_aip;
1229
0
      dst++;
1230
0
    }
1231
    /* Adjust array size to be 'dst' items */
1232
0
    ARR_DIMS(new_acl)[0] = dst;
1233
0
    SET_VARSIZE(new_acl, ACL_N_SIZE(dst));
1234
0
  }
1235
1236
0
  return new_acl;
1237
0
}
1238
1239
1240
/*
1241
 * When granting grant options, we must disallow attempts to set up circular
1242
 * chains of grant options.  Suppose A (the object owner) grants B some
1243
 * privileges with grant option, and B re-grants them to C.  If C could
1244
 * grant the privileges to B as well, then A would be unable to effectively
1245
 * revoke the privileges from B, since recursive_revoke would consider that
1246
 * B still has 'em from C.
1247
 *
1248
 * We check for this by recursively deleting all grant options belonging to
1249
 * the target grantee, and then seeing if the would-be grantor still has the
1250
 * grant option or not.
1251
 */
1252
static void
1253
check_circularity(const Acl *old_acl, const AclItem *mod_aip,
1254
          Oid ownerId)
1255
0
{
1256
0
  Acl      *acl;
1257
0
  AclItem    *aip;
1258
0
  int     i,
1259
0
        num;
1260
0
  AclMode   own_privs;
1261
1262
0
  check_acl(old_acl);
1263
1264
  /*
1265
   * For now, grant options can only be granted to roles, not PUBLIC.
1266
   * Otherwise we'd have to work a bit harder here.
1267
   */
1268
0
  Assert(mod_aip->ai_grantee != ACL_ID_PUBLIC);
1269
1270
  /* The owner always has grant options, no need to check */
1271
0
  if (mod_aip->ai_grantor == ownerId)
1272
0
    return;
1273
1274
  /* Make a working copy */
1275
0
  acl = allocacl(ACL_NUM(old_acl));
1276
0
  memcpy(acl, old_acl, ACL_SIZE(old_acl));
1277
1278
  /* Zap all grant options of target grantee, plus what depends on 'em */
1279
0
cc_restart:
1280
0
  num = ACL_NUM(acl);
1281
0
  aip = ACL_DAT(acl);
1282
0
  for (i = 0; i < num; i++)
1283
0
  {
1284
0
    if (aip[i].ai_grantee == mod_aip->ai_grantee &&
1285
0
      ACLITEM_GET_GOPTIONS(aip[i]) != ACL_NO_RIGHTS)
1286
0
    {
1287
0
      Acl      *new_acl;
1288
1289
      /* We'll actually zap ordinary privs too, but no matter */
1290
0
      new_acl = aclupdate(acl, &aip[i], ACL_MODECHG_DEL,
1291
0
                ownerId, DROP_CASCADE);
1292
1293
0
      pfree(acl);
1294
0
      acl = new_acl;
1295
1296
0
      goto cc_restart;
1297
0
    }
1298
0
  }
1299
1300
  /* Now we can compute grantor's independently-derived privileges */
1301
0
  own_privs = aclmask(acl,
1302
0
            mod_aip->ai_grantor,
1303
0
            ownerId,
1304
0
            ACL_GRANT_OPTION_FOR(ACLITEM_GET_GOPTIONS(*mod_aip)),
1305
0
            ACLMASK_ALL);
1306
0
  own_privs = ACL_OPTION_TO_PRIVS(own_privs);
1307
1308
0
  if ((ACLITEM_GET_GOPTIONS(*mod_aip) & ~own_privs) != 0)
1309
0
    ereport(ERROR,
1310
0
        (errcode(ERRCODE_INVALID_GRANT_OPERATION),
1311
0
         errmsg("grant options cannot be granted back to your own grantor")));
1312
1313
0
  pfree(acl);
1314
0
}
1315
1316
1317
/*
1318
 * Ensure that no privilege is "abandoned".  A privilege is abandoned
1319
 * if the user that granted the privilege loses the grant option.  (So
1320
 * the chain through which it was granted is broken.)  Either the
1321
 * abandoned privileges are revoked as well, or an error message is
1322
 * printed, depending on the drop behavior option.
1323
 *
1324
 *  acl: the input ACL list
1325
 *  grantee: the user from whom some grant options have been revoked
1326
 *  revoke_privs: the grant options being revoked
1327
 *  ownerId: Oid of object owner
1328
 *  behavior: RESTRICT or CASCADE behavior for recursive removal
1329
 *
1330
 * The input Acl object is pfree'd if replaced.
1331
 */
1332
static Acl *
1333
recursive_revoke(Acl *acl,
1334
         Oid grantee,
1335
         AclMode revoke_privs,
1336
         Oid ownerId,
1337
         DropBehavior behavior)
1338
0
{
1339
0
  AclMode   still_has;
1340
0
  AclItem    *aip;
1341
0
  int     i,
1342
0
        num;
1343
1344
0
  check_acl(acl);
1345
1346
  /* The owner can never truly lose grant options, so short-circuit */
1347
0
  if (grantee == ownerId)
1348
0
    return acl;
1349
1350
  /* The grantee might still have some grant options via another grantor */
1351
0
  still_has = aclmask(acl, grantee, ownerId,
1352
0
            ACL_GRANT_OPTION_FOR(revoke_privs),
1353
0
            ACLMASK_ALL);
1354
0
  revoke_privs &= ~ACL_OPTION_TO_PRIVS(still_has);
1355
0
  if (revoke_privs == ACL_NO_RIGHTS)
1356
0
    return acl;
1357
1358
0
restart:
1359
0
  num = ACL_NUM(acl);
1360
0
  aip = ACL_DAT(acl);
1361
0
  for (i = 0; i < num; i++)
1362
0
  {
1363
0
    if (aip[i].ai_grantor == grantee
1364
0
      && (ACLITEM_GET_PRIVS(aip[i]) & revoke_privs) != 0)
1365
0
    {
1366
0
      AclItem   mod_acl;
1367
0
      Acl      *new_acl;
1368
1369
0
      if (behavior == DROP_RESTRICT)
1370
0
        ereport(ERROR,
1371
0
            (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
1372
0
             errmsg("dependent privileges exist"),
1373
0
             errhint("Use CASCADE to revoke them too.")));
1374
1375
0
      mod_acl.ai_grantor = grantee;
1376
0
      mod_acl.ai_grantee = aip[i].ai_grantee;
1377
0
      ACLITEM_SET_PRIVS_GOPTIONS(mod_acl,
1378
0
                     revoke_privs,
1379
0
                     revoke_privs);
1380
1381
0
      new_acl = aclupdate(acl, &mod_acl, ACL_MODECHG_DEL,
1382
0
                ownerId, behavior);
1383
1384
0
      pfree(acl);
1385
0
      acl = new_acl;
1386
1387
0
      goto restart;
1388
0
    }
1389
0
  }
1390
1391
0
  return acl;
1392
0
}
1393
1394
1395
/*
1396
 * aclmask --- compute bitmask of all privileges held by roleid.
1397
 *
1398
 * When 'how' = ACLMASK_ALL, this simply returns the privilege bits
1399
 * held by the given roleid according to the given ACL list, ANDed
1400
 * with 'mask'.  (The point of passing 'mask' is to let the routine
1401
 * exit early if all privileges of interest have been found.)
1402
 *
1403
 * When 'how' = ACLMASK_ANY, returns as soon as any bit in the mask
1404
 * is known true.  (This lets us exit soonest in cases where the
1405
 * caller is only going to test for zero or nonzero result.)
1406
 *
1407
 * Usage patterns:
1408
 *
1409
 * To see if any of a set of privileges are held:
1410
 *    if (aclmask(acl, roleid, ownerId, privs, ACLMASK_ANY) != 0)
1411
 *
1412
 * To see if all of a set of privileges are held:
1413
 *    if (aclmask(acl, roleid, ownerId, privs, ACLMASK_ALL) == privs)
1414
 *
1415
 * To determine exactly which of a set of privileges are held:
1416
 *    heldprivs = aclmask(acl, roleid, ownerId, privs, ACLMASK_ALL);
1417
 */
1418
AclMode
1419
aclmask(const Acl *acl, Oid roleid, Oid ownerId,
1420
    AclMode mask, AclMaskHow how)
1421
0
{
1422
0
  AclMode   result;
1423
0
  AclMode   remaining;
1424
0
  AclItem    *aidat;
1425
0
  int     i,
1426
0
        num;
1427
1428
  /*
1429
   * Null ACL should not happen, since caller should have inserted
1430
   * appropriate default
1431
   */
1432
0
  if (acl == NULL)
1433
0
    elog(ERROR, "null ACL");
1434
1435
0
  check_acl(acl);
1436
1437
  /* Quick exit for mask == 0 */
1438
0
  if (mask == 0)
1439
0
    return 0;
1440
1441
0
  result = 0;
1442
1443
  /* Owner always implicitly has all grant options */
1444
0
  if ((mask & ACLITEM_ALL_GOPTION_BITS) &&
1445
0
    has_privs_of_role(roleid, ownerId))
1446
0
  {
1447
0
    result = mask & ACLITEM_ALL_GOPTION_BITS;
1448
0
    if ((how == ACLMASK_ALL) ? (result == mask) : (result != 0))
1449
0
      return result;
1450
0
  }
1451
1452
0
  num = ACL_NUM(acl);
1453
0
  aidat = ACL_DAT(acl);
1454
1455
  /*
1456
   * Check privileges granted directly to roleid or to public
1457
   */
1458
0
  for (i = 0; i < num; i++)
1459
0
  {
1460
0
    AclItem    *aidata = &aidat[i];
1461
1462
0
    if (aidata->ai_grantee == ACL_ID_PUBLIC ||
1463
0
      aidata->ai_grantee == roleid)
1464
0
    {
1465
0
      result |= aidata->ai_privs & mask;
1466
0
      if ((how == ACLMASK_ALL) ? (result == mask) : (result != 0))
1467
0
        return result;
1468
0
    }
1469
0
  }
1470
1471
  /*
1472
   * Check privileges granted indirectly via role memberships. We do this in
1473
   * a separate pass to minimize expensive indirect membership tests.  In
1474
   * particular, it's worth testing whether a given ACL entry grants any
1475
   * privileges still of interest before we perform the has_privs_of_role
1476
   * test.
1477
   */
1478
0
  remaining = mask & ~result;
1479
0
  for (i = 0; i < num; i++)
1480
0
  {
1481
0
    AclItem    *aidata = &aidat[i];
1482
1483
0
    if (aidata->ai_grantee == ACL_ID_PUBLIC ||
1484
0
      aidata->ai_grantee == roleid)
1485
0
      continue;     /* already checked it */
1486
1487
0
    if ((aidata->ai_privs & remaining) &&
1488
0
      has_privs_of_role(roleid, aidata->ai_grantee))
1489
0
    {
1490
0
      result |= aidata->ai_privs & mask;
1491
0
      if ((how == ACLMASK_ALL) ? (result == mask) : (result != 0))
1492
0
        return result;
1493
0
      remaining = mask & ~result;
1494
0
    }
1495
0
  }
1496
1497
0
  return result;
1498
0
}
1499
1500
1501
/*
1502
 * aclmask_direct --- compute bitmask of all privileges held by roleid.
1503
 *
1504
 * This is exactly like aclmask() except that we consider only privileges
1505
 * held *directly* by roleid, not those inherited via role membership.
1506
 */
1507
static AclMode
1508
aclmask_direct(const Acl *acl, Oid roleid, Oid ownerId,
1509
         AclMode mask, AclMaskHow how)
1510
0
{
1511
0
  AclMode   result;
1512
0
  AclItem    *aidat;
1513
0
  int     i,
1514
0
        num;
1515
1516
  /*
1517
   * Null ACL should not happen, since caller should have inserted
1518
   * appropriate default
1519
   */
1520
0
  if (acl == NULL)
1521
0
    elog(ERROR, "null ACL");
1522
1523
0
  check_acl(acl);
1524
1525
  /* Quick exit for mask == 0 */
1526
0
  if (mask == 0)
1527
0
    return 0;
1528
1529
0
  result = 0;
1530
1531
  /* Owner always implicitly has all grant options */
1532
0
  if ((mask & ACLITEM_ALL_GOPTION_BITS) &&
1533
0
    roleid == ownerId)
1534
0
  {
1535
0
    result = mask & ACLITEM_ALL_GOPTION_BITS;
1536
0
    if ((how == ACLMASK_ALL) ? (result == mask) : (result != 0))
1537
0
      return result;
1538
0
  }
1539
1540
0
  num = ACL_NUM(acl);
1541
0
  aidat = ACL_DAT(acl);
1542
1543
  /*
1544
   * Check privileges granted directly to roleid (and not to public)
1545
   */
1546
0
  for (i = 0; i < num; i++)
1547
0
  {
1548
0
    AclItem    *aidata = &aidat[i];
1549
1550
0
    if (aidata->ai_grantee == roleid)
1551
0
    {
1552
0
      result |= aidata->ai_privs & mask;
1553
0
      if ((how == ACLMASK_ALL) ? (result == mask) : (result != 0))
1554
0
        return result;
1555
0
    }
1556
0
  }
1557
1558
0
  return result;
1559
0
}
1560
1561
1562
/*
1563
 * aclmembers
1564
 *    Find out all the roleids mentioned in an Acl.
1565
 *    Note that we do not distinguish grantors from grantees.
1566
 *
1567
 * *roleids is set to point to a palloc'd array containing distinct OIDs
1568
 * in sorted order.  The length of the array is the function result.
1569
 */
1570
int
1571
aclmembers(const Acl *acl, Oid **roleids)
1572
0
{
1573
0
  Oid      *list;
1574
0
  const AclItem *acldat;
1575
0
  int     i,
1576
0
        j;
1577
1578
0
  if (acl == NULL || ACL_NUM(acl) == 0)
1579
0
  {
1580
0
    *roleids = NULL;
1581
0
    return 0;
1582
0
  }
1583
1584
0
  check_acl(acl);
1585
1586
  /* Allocate the worst-case space requirement */
1587
0
  list = palloc(ACL_NUM(acl) * 2 * sizeof(Oid));
1588
0
  acldat = ACL_DAT(acl);
1589
1590
  /*
1591
   * Walk the ACL collecting mentioned RoleIds.
1592
   */
1593
0
  j = 0;
1594
0
  for (i = 0; i < ACL_NUM(acl); i++)
1595
0
  {
1596
0
    const AclItem *ai = &acldat[i];
1597
1598
0
    if (ai->ai_grantee != ACL_ID_PUBLIC)
1599
0
      list[j++] = ai->ai_grantee;
1600
    /* grantor is currently never PUBLIC, but let's check anyway */
1601
0
    if (ai->ai_grantor != ACL_ID_PUBLIC)
1602
0
      list[j++] = ai->ai_grantor;
1603
0
  }
1604
1605
  /* Sort the array */
1606
0
  qsort(list, j, sizeof(Oid), oid_cmp);
1607
1608
  /*
1609
   * We could repalloc the array down to minimum size, but it's hardly worth
1610
   * it since it's only transient memory.
1611
   */
1612
0
  *roleids = list;
1613
1614
  /* Remove duplicates from the array */
1615
0
  return qunique(list, j, sizeof(Oid), oid_cmp);
1616
0
}
1617
1618
1619
/*
1620
 * aclinsert (exported function)
1621
 */
1622
Datum
1623
aclinsert(PG_FUNCTION_ARGS)
1624
0
{
1625
0
  ereport(ERROR,
1626
0
      (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1627
0
       errmsg("aclinsert is no longer supported")));
1628
1629
0
  PG_RETURN_NULL();     /* keep compiler quiet */
1630
0
}
1631
1632
Datum
1633
aclremove(PG_FUNCTION_ARGS)
1634
0
{
1635
0
  ereport(ERROR,
1636
0
      (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1637
0
       errmsg("aclremove is no longer supported")));
1638
1639
0
  PG_RETURN_NULL();     /* keep compiler quiet */
1640
0
}
1641
1642
Datum
1643
aclcontains(PG_FUNCTION_ARGS)
1644
0
{
1645
0
  Acl      *acl = PG_GETARG_ACL_P(0);
1646
0
  AclItem    *aip = PG_GETARG_ACLITEM_P(1);
1647
0
  AclItem    *aidat;
1648
0
  int     i,
1649
0
        num;
1650
1651
0
  check_acl(acl);
1652
0
  num = ACL_NUM(acl);
1653
0
  aidat = ACL_DAT(acl);
1654
0
  for (i = 0; i < num; ++i)
1655
0
  {
1656
0
    if (aip->ai_grantee == aidat[i].ai_grantee &&
1657
0
      aip->ai_grantor == aidat[i].ai_grantor &&
1658
0
      (ACLITEM_GET_RIGHTS(*aip) & ACLITEM_GET_RIGHTS(aidat[i])) == ACLITEM_GET_RIGHTS(*aip))
1659
0
      PG_RETURN_BOOL(true);
1660
0
  }
1661
0
  PG_RETURN_BOOL(false);
1662
0
}
1663
1664
Datum
1665
makeaclitem(PG_FUNCTION_ARGS)
1666
0
{
1667
0
  Oid     grantee = PG_GETARG_OID(0);
1668
0
  Oid     grantor = PG_GETARG_OID(1);
1669
0
  text     *privtext = PG_GETARG_TEXT_PP(2);
1670
0
  bool    goption = PG_GETARG_BOOL(3);
1671
0
  AclItem    *result;
1672
0
  AclMode   priv;
1673
0
  static const priv_map any_priv_map[] = {
1674
0
    {"SELECT", ACL_SELECT},
1675
0
    {"INSERT", ACL_INSERT},
1676
0
    {"UPDATE", ACL_UPDATE},
1677
0
    {"DELETE", ACL_DELETE},
1678
0
    {"TRUNCATE", ACL_TRUNCATE},
1679
0
    {"REFERENCES", ACL_REFERENCES},
1680
0
    {"TRIGGER", ACL_TRIGGER},
1681
0
    {"EXECUTE", ACL_EXECUTE},
1682
0
    {"USAGE", ACL_USAGE},
1683
0
    {"CREATE", ACL_CREATE},
1684
0
    {"TEMP", ACL_CREATE_TEMP},
1685
0
    {"TEMPORARY", ACL_CREATE_TEMP},
1686
0
    {"CONNECT", ACL_CONNECT},
1687
0
    {"SET", ACL_SET},
1688
0
    {"ALTER SYSTEM", ACL_ALTER_SYSTEM},
1689
0
    {"MAINTAIN", ACL_MAINTAIN},
1690
0
    {NULL, 0}
1691
0
  };
1692
1693
0
  priv = convert_any_priv_string(privtext, any_priv_map);
1694
1695
0
  result = palloc_object(AclItem);
1696
1697
0
  result->ai_grantee = grantee;
1698
0
  result->ai_grantor = grantor;
1699
1700
0
  ACLITEM_SET_PRIVS_GOPTIONS(*result, priv,
1701
0
                 (goption ? priv : ACL_NO_RIGHTS));
1702
1703
0
  PG_RETURN_ACLITEM_P(result);
1704
0
}
1705
1706
1707
/*
1708
 * convert_any_priv_string: recognize privilege strings for has_foo_privilege
1709
 *
1710
 * We accept a comma-separated list of case-insensitive privilege names,
1711
 * producing a bitmask of the OR'd privilege bits.  We are liberal about
1712
 * whitespace between items, not so much about whitespace within items.
1713
 * The allowed privilege names are given as an array of priv_map structs,
1714
 * terminated by one with a NULL name pointer.
1715
 */
1716
static AclMode
1717
convert_any_priv_string(text *priv_type_text,
1718
            const priv_map *privileges)
1719
0
{
1720
0
  AclMode   result = 0;
1721
0
  char     *priv_type = text_to_cstring(priv_type_text);
1722
0
  char     *chunk;
1723
0
  char     *next_chunk;
1724
1725
  /* We rely on priv_type being a private, modifiable string */
1726
0
  for (chunk = priv_type; chunk; chunk = next_chunk)
1727
0
  {
1728
0
    int     chunk_len;
1729
0
    const priv_map *this_priv;
1730
1731
    /* Split string at commas */
1732
0
    next_chunk = strchr(chunk, ',');
1733
0
    if (next_chunk)
1734
0
      *next_chunk++ = '\0';
1735
1736
    /* Drop leading/trailing whitespace in this chunk */
1737
0
    while (*chunk && isspace((unsigned char) *chunk))
1738
0
      chunk++;
1739
0
    chunk_len = strlen(chunk);
1740
0
    while (chunk_len > 0 && isspace((unsigned char) chunk[chunk_len - 1]))
1741
0
      chunk_len--;
1742
0
    chunk[chunk_len] = '\0';
1743
1744
    /* Match to the privileges list */
1745
0
    for (this_priv = privileges; this_priv->name; this_priv++)
1746
0
    {
1747
0
      if (pg_strcasecmp(this_priv->name, chunk) == 0)
1748
0
      {
1749
0
        result |= this_priv->value;
1750
0
        break;
1751
0
      }
1752
0
    }
1753
0
    if (!this_priv->name)
1754
0
      ereport(ERROR,
1755
0
          (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1756
0
           errmsg("unrecognized privilege type: \"%s\"", chunk)));
1757
0
  }
1758
1759
0
  pfree(priv_type);
1760
0
  return result;
1761
0
}
1762
1763
1764
static const char *
1765
convert_aclright_to_string(int aclright)
1766
0
{
1767
0
  switch (aclright)
1768
0
  {
1769
0
    case ACL_INSERT:
1770
0
      return "INSERT";
1771
0
    case ACL_SELECT:
1772
0
      return "SELECT";
1773
0
    case ACL_UPDATE:
1774
0
      return "UPDATE";
1775
0
    case ACL_DELETE:
1776
0
      return "DELETE";
1777
0
    case ACL_TRUNCATE:
1778
0
      return "TRUNCATE";
1779
0
    case ACL_REFERENCES:
1780
0
      return "REFERENCES";
1781
0
    case ACL_TRIGGER:
1782
0
      return "TRIGGER";
1783
0
    case ACL_EXECUTE:
1784
0
      return "EXECUTE";
1785
0
    case ACL_USAGE:
1786
0
      return "USAGE";
1787
0
    case ACL_CREATE:
1788
0
      return "CREATE";
1789
0
    case ACL_CREATE_TEMP:
1790
0
      return "TEMPORARY";
1791
0
    case ACL_CONNECT:
1792
0
      return "CONNECT";
1793
0
    case ACL_SET:
1794
0
      return "SET";
1795
0
    case ACL_ALTER_SYSTEM:
1796
0
      return "ALTER SYSTEM";
1797
0
    case ACL_MAINTAIN:
1798
0
      return "MAINTAIN";
1799
0
    default:
1800
0
      elog(ERROR, "unrecognized aclright: %d", aclright);
1801
0
      return NULL;
1802
0
  }
1803
0
}
1804
1805
1806
/*----------
1807
 * Convert an aclitem[] to a table.
1808
 *
1809
 * Example:
1810
 *
1811
 * aclexplode('{=r/joe,foo=a*w/joe}'::aclitem[])
1812
 *
1813
 * returns the table
1814
 *
1815
 * {{ OID(joe), 0::OID,   'SELECT', false },
1816
 *  { OID(joe), OID(foo), 'INSERT', true },
1817
 *  { OID(joe), OID(foo), 'UPDATE', false }}
1818
 *----------
1819
 */
1820
Datum
1821
aclexplode(PG_FUNCTION_ARGS)
1822
0
{
1823
0
  Acl      *acl = PG_GETARG_ACL_P(0);
1824
0
  FuncCallContext *funcctx;
1825
0
  int      *idx;
1826
0
  AclItem    *aidat;
1827
1828
0
  if (SRF_IS_FIRSTCALL())
1829
0
  {
1830
0
    TupleDesc tupdesc;
1831
0
    MemoryContext oldcontext;
1832
1833
0
    check_acl(acl);
1834
1835
0
    funcctx = SRF_FIRSTCALL_INIT();
1836
0
    oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
1837
1838
    /*
1839
     * build tupdesc for result tuples (matches out parameters in pg_proc
1840
     * entry)
1841
     */
1842
0
    tupdesc = CreateTemplateTupleDesc(4);
1843
0
    TupleDescInitEntry(tupdesc, (AttrNumber) 1, "grantor",
1844
0
               OIDOID, -1, 0);
1845
0
    TupleDescInitEntry(tupdesc, (AttrNumber) 2, "grantee",
1846
0
               OIDOID, -1, 0);
1847
0
    TupleDescInitEntry(tupdesc, (AttrNumber) 3, "privilege_type",
1848
0
               TEXTOID, -1, 0);
1849
0
    TupleDescInitEntry(tupdesc, (AttrNumber) 4, "is_grantable",
1850
0
               BOOLOID, -1, 0);
1851
1852
0
    TupleDescFinalize(tupdesc);
1853
0
    funcctx->tuple_desc = BlessTupleDesc(tupdesc);
1854
1855
    /* allocate memory for user context */
1856
0
    idx = palloc_array(int, 2);
1857
0
    idx[0] = 0;       /* ACL array item index */
1858
0
    idx[1] = -1;      /* privilege type counter */
1859
0
    funcctx->user_fctx = idx;
1860
1861
0
    MemoryContextSwitchTo(oldcontext);
1862
0
  }
1863
1864
0
  funcctx = SRF_PERCALL_SETUP();
1865
0
  idx = (int *) funcctx->user_fctx;
1866
0
  aidat = ACL_DAT(acl);
1867
1868
  /* need test here in case acl has no items */
1869
0
  while (idx[0] < ACL_NUM(acl))
1870
0
  {
1871
0
    AclItem    *aidata;
1872
0
    AclMode   priv_bit;
1873
1874
0
    idx[1]++;
1875
0
    if (idx[1] == N_ACL_RIGHTS)
1876
0
    {
1877
0
      idx[1] = 0;
1878
0
      idx[0]++;
1879
0
      if (idx[0] >= ACL_NUM(acl)) /* done */
1880
0
        break;
1881
0
    }
1882
0
    aidata = &aidat[idx[0]];
1883
0
    priv_bit = UINT64CONST(1) << idx[1];
1884
1885
0
    if (ACLITEM_GET_PRIVS(*aidata) & priv_bit)
1886
0
    {
1887
0
      Datum   result;
1888
0
      Datum   values[4];
1889
0
      bool    nulls[4] = {0};
1890
0
      HeapTuple tuple;
1891
1892
0
      values[0] = ObjectIdGetDatum(aidata->ai_grantor);
1893
0
      values[1] = ObjectIdGetDatum(aidata->ai_grantee);
1894
0
      values[2] = CStringGetTextDatum(convert_aclright_to_string(priv_bit));
1895
0
      values[3] = BoolGetDatum((ACLITEM_GET_GOPTIONS(*aidata) & priv_bit) != 0);
1896
1897
0
      tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
1898
0
      result = HeapTupleGetDatum(tuple);
1899
1900
0
      SRF_RETURN_NEXT(funcctx, result);
1901
0
    }
1902
0
  }
1903
1904
0
  SRF_RETURN_DONE(funcctx);
1905
0
}
1906
1907
1908
/*
1909
 * has_table_privilege variants
1910
 *    These are all named "has_table_privilege" at the SQL level.
1911
 *    They take various combinations of relation name, relation OID,
1912
 *    user name, user OID, or implicit user = current_user.
1913
 *
1914
 *    The result is a boolean value: true if user has the indicated
1915
 *    privilege, false if not.  The variants that take a relation OID
1916
 *    return NULL if the OID doesn't exist (rather than failing, as
1917
 *    they did before Postgres 8.4).
1918
 */
1919
1920
/*
1921
 * has_table_privilege_name_name
1922
 *    Check user privileges on a table given
1923
 *    name username, text tablename, and text priv name.
1924
 */
1925
Datum
1926
has_table_privilege_name_name(PG_FUNCTION_ARGS)
1927
0
{
1928
0
  Name    rolename = PG_GETARG_NAME(0);
1929
0
  text     *tablename = PG_GETARG_TEXT_PP(1);
1930
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
1931
0
  Oid     roleid;
1932
0
  Oid     tableoid;
1933
0
  AclMode   mode;
1934
0
  AclResult aclresult;
1935
1936
0
  roleid = get_role_oid_or_public(NameStr(*rolename));
1937
0
  tableoid = convert_table_name(tablename);
1938
0
  mode = convert_table_priv_string(priv_type_text);
1939
1940
0
  aclresult = pg_class_aclcheck(tableoid, roleid, mode);
1941
1942
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1943
0
}
1944
1945
/*
1946
 * has_table_privilege_name
1947
 *    Check user privileges on a table given
1948
 *    text tablename and text priv name.
1949
 *    current_user is assumed
1950
 */
1951
Datum
1952
has_table_privilege_name(PG_FUNCTION_ARGS)
1953
0
{
1954
0
  text     *tablename = PG_GETARG_TEXT_PP(0);
1955
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(1);
1956
0
  Oid     roleid;
1957
0
  Oid     tableoid;
1958
0
  AclMode   mode;
1959
0
  AclResult aclresult;
1960
1961
0
  roleid = GetUserId();
1962
0
  tableoid = convert_table_name(tablename);
1963
0
  mode = convert_table_priv_string(priv_type_text);
1964
1965
0
  aclresult = pg_class_aclcheck(tableoid, roleid, mode);
1966
1967
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1968
0
}
1969
1970
/*
1971
 * has_table_privilege_name_id
1972
 *    Check user privileges on a table given
1973
 *    name usename, table oid, and text priv name.
1974
 */
1975
Datum
1976
has_table_privilege_name_id(PG_FUNCTION_ARGS)
1977
0
{
1978
0
  Name    username = PG_GETARG_NAME(0);
1979
0
  Oid     tableoid = PG_GETARG_OID(1);
1980
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
1981
0
  Oid     roleid;
1982
0
  AclMode   mode;
1983
0
  AclResult aclresult;
1984
0
  bool    is_missing = false;
1985
1986
0
  roleid = get_role_oid_or_public(NameStr(*username));
1987
0
  mode = convert_table_priv_string(priv_type_text);
1988
1989
0
  aclresult = pg_class_aclcheck_ext(tableoid, roleid, mode, &is_missing);
1990
1991
0
  if (is_missing)
1992
0
    PG_RETURN_NULL();
1993
1994
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1995
0
}
1996
1997
/*
1998
 * has_table_privilege_id
1999
 *    Check user privileges on a table given
2000
 *    table oid, and text priv name.
2001
 *    current_user is assumed
2002
 */
2003
Datum
2004
has_table_privilege_id(PG_FUNCTION_ARGS)
2005
0
{
2006
0
  Oid     tableoid = PG_GETARG_OID(0);
2007
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(1);
2008
0
  Oid     roleid;
2009
0
  AclMode   mode;
2010
0
  AclResult aclresult;
2011
0
  bool    is_missing = false;
2012
2013
0
  roleid = GetUserId();
2014
0
  mode = convert_table_priv_string(priv_type_text);
2015
2016
0
  aclresult = pg_class_aclcheck_ext(tableoid, roleid, mode, &is_missing);
2017
2018
0
  if (is_missing)
2019
0
    PG_RETURN_NULL();
2020
2021
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
2022
0
}
2023
2024
/*
2025
 * has_table_privilege_id_name
2026
 *    Check user privileges on a table given
2027
 *    roleid, text tablename, and text priv name.
2028
 */
2029
Datum
2030
has_table_privilege_id_name(PG_FUNCTION_ARGS)
2031
0
{
2032
0
  Oid     roleid = PG_GETARG_OID(0);
2033
0
  text     *tablename = PG_GETARG_TEXT_PP(1);
2034
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
2035
0
  Oid     tableoid;
2036
0
  AclMode   mode;
2037
0
  AclResult aclresult;
2038
2039
0
  tableoid = convert_table_name(tablename);
2040
0
  mode = convert_table_priv_string(priv_type_text);
2041
2042
0
  aclresult = pg_class_aclcheck(tableoid, roleid, mode);
2043
2044
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
2045
0
}
2046
2047
/*
2048
 * has_table_privilege_id_id
2049
 *    Check user privileges on a table given
2050
 *    roleid, table oid, and text priv name.
2051
 */
2052
Datum
2053
has_table_privilege_id_id(PG_FUNCTION_ARGS)
2054
0
{
2055
0
  Oid     roleid = PG_GETARG_OID(0);
2056
0
  Oid     tableoid = PG_GETARG_OID(1);
2057
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
2058
0
  AclMode   mode;
2059
0
  AclResult aclresult;
2060
0
  bool    is_missing = false;
2061
2062
0
  mode = convert_table_priv_string(priv_type_text);
2063
2064
0
  aclresult = pg_class_aclcheck_ext(tableoid, roleid, mode, &is_missing);
2065
2066
0
  if (is_missing)
2067
0
    PG_RETURN_NULL();
2068
2069
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
2070
0
}
2071
2072
/*
2073
 *    Support routines for has_table_privilege family.
2074
 */
2075
2076
/*
2077
 * Given a table name expressed as a string, look it up and return Oid
2078
 */
2079
static Oid
2080
convert_table_name(text *tablename)
2081
0
{
2082
0
  RangeVar   *relrv;
2083
2084
0
  relrv = makeRangeVarFromNameList(textToQualifiedNameList(tablename));
2085
2086
  /* We might not even have permissions on this relation; don't lock it. */
2087
0
  return RangeVarGetRelid(relrv, NoLock, false);
2088
0
}
2089
2090
/*
2091
 * convert_table_priv_string
2092
 *    Convert text string to AclMode value.
2093
 */
2094
static AclMode
2095
convert_table_priv_string(text *priv_type_text)
2096
0
{
2097
0
  static const priv_map table_priv_map[] = {
2098
0
    {"SELECT", ACL_SELECT},
2099
0
    {"SELECT WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_SELECT)},
2100
0
    {"INSERT", ACL_INSERT},
2101
0
    {"INSERT WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_INSERT)},
2102
0
    {"UPDATE", ACL_UPDATE},
2103
0
    {"UPDATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_UPDATE)},
2104
0
    {"DELETE", ACL_DELETE},
2105
0
    {"DELETE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_DELETE)},
2106
0
    {"TRUNCATE", ACL_TRUNCATE},
2107
0
    {"TRUNCATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_TRUNCATE)},
2108
0
    {"REFERENCES", ACL_REFERENCES},
2109
0
    {"REFERENCES WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_REFERENCES)},
2110
0
    {"TRIGGER", ACL_TRIGGER},
2111
0
    {"TRIGGER WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_TRIGGER)},
2112
0
    {"MAINTAIN", ACL_MAINTAIN},
2113
0
    {"MAINTAIN WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_MAINTAIN)},
2114
0
    {NULL, 0}
2115
0
  };
2116
2117
0
  return convert_any_priv_string(priv_type_text, table_priv_map);
2118
0
}
2119
2120
/*
2121
 * has_sequence_privilege variants
2122
 *    These are all named "has_sequence_privilege" at the SQL level.
2123
 *    They take various combinations of relation name, relation OID,
2124
 *    user name, user OID, or implicit user = current_user.
2125
 *
2126
 *    The result is a boolean value: true if user has the indicated
2127
 *    privilege, false if not.  The variants that take a relation OID
2128
 *    return NULL if the OID doesn't exist.
2129
 */
2130
2131
/*
2132
 * has_sequence_privilege_name_name
2133
 *    Check user privileges on a sequence given
2134
 *    name username, text sequencename, and text priv name.
2135
 */
2136
Datum
2137
has_sequence_privilege_name_name(PG_FUNCTION_ARGS)
2138
0
{
2139
0
  Name    rolename = PG_GETARG_NAME(0);
2140
0
  text     *sequencename = PG_GETARG_TEXT_PP(1);
2141
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
2142
0
  Oid     roleid;
2143
0
  Oid     sequenceoid;
2144
0
  AclMode   mode;
2145
0
  AclResult aclresult;
2146
2147
0
  roleid = get_role_oid_or_public(NameStr(*rolename));
2148
0
  mode = convert_sequence_priv_string(priv_type_text);
2149
0
  sequenceoid = convert_table_name(sequencename);
2150
0
  if (get_rel_relkind(sequenceoid) != RELKIND_SEQUENCE)
2151
0
    ereport(ERROR,
2152
0
        (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2153
0
         errmsg("\"%s\" is not a sequence",
2154
0
            text_to_cstring(sequencename))));
2155
2156
0
  aclresult = pg_class_aclcheck(sequenceoid, roleid, mode);
2157
2158
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
2159
0
}
2160
2161
/*
2162
 * has_sequence_privilege_name
2163
 *    Check user privileges on a sequence given
2164
 *    text sequencename and text priv name.
2165
 *    current_user is assumed
2166
 */
2167
Datum
2168
has_sequence_privilege_name(PG_FUNCTION_ARGS)
2169
0
{
2170
0
  text     *sequencename = PG_GETARG_TEXT_PP(0);
2171
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(1);
2172
0
  Oid     roleid;
2173
0
  Oid     sequenceoid;
2174
0
  AclMode   mode;
2175
0
  AclResult aclresult;
2176
2177
0
  roleid = GetUserId();
2178
0
  mode = convert_sequence_priv_string(priv_type_text);
2179
0
  sequenceoid = convert_table_name(sequencename);
2180
0
  if (get_rel_relkind(sequenceoid) != RELKIND_SEQUENCE)
2181
0
    ereport(ERROR,
2182
0
        (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2183
0
         errmsg("\"%s\" is not a sequence",
2184
0
            text_to_cstring(sequencename))));
2185
2186
0
  aclresult = pg_class_aclcheck(sequenceoid, roleid, mode);
2187
2188
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
2189
0
}
2190
2191
/*
2192
 * has_sequence_privilege_name_id
2193
 *    Check user privileges on a sequence given
2194
 *    name usename, sequence oid, and text priv name.
2195
 */
2196
Datum
2197
has_sequence_privilege_name_id(PG_FUNCTION_ARGS)
2198
0
{
2199
0
  Name    username = PG_GETARG_NAME(0);
2200
0
  Oid     sequenceoid = PG_GETARG_OID(1);
2201
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
2202
0
  Oid     roleid;
2203
0
  AclMode   mode;
2204
0
  AclResult aclresult;
2205
0
  char    relkind;
2206
0
  bool    is_missing = false;
2207
2208
0
  roleid = get_role_oid_or_public(NameStr(*username));
2209
0
  mode = convert_sequence_priv_string(priv_type_text);
2210
0
  relkind = get_rel_relkind(sequenceoid);
2211
0
  if (relkind == '\0')
2212
0
    PG_RETURN_NULL();
2213
0
  else if (relkind != RELKIND_SEQUENCE)
2214
0
    ereport(ERROR,
2215
0
        (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2216
0
         errmsg("\"%s\" is not a sequence",
2217
0
            get_rel_name(sequenceoid))));
2218
2219
0
  aclresult = pg_class_aclcheck_ext(sequenceoid, roleid, mode, &is_missing);
2220
2221
0
  if (is_missing)
2222
0
    PG_RETURN_NULL();
2223
2224
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
2225
0
}
2226
2227
/*
2228
 * has_sequence_privilege_id
2229
 *    Check user privileges on a sequence given
2230
 *    sequence oid, and text priv name.
2231
 *    current_user is assumed
2232
 */
2233
Datum
2234
has_sequence_privilege_id(PG_FUNCTION_ARGS)
2235
0
{
2236
0
  Oid     sequenceoid = PG_GETARG_OID(0);
2237
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(1);
2238
0
  Oid     roleid;
2239
0
  AclMode   mode;
2240
0
  AclResult aclresult;
2241
0
  char    relkind;
2242
0
  bool    is_missing = false;
2243
2244
0
  roleid = GetUserId();
2245
0
  mode = convert_sequence_priv_string(priv_type_text);
2246
0
  relkind = get_rel_relkind(sequenceoid);
2247
0
  if (relkind == '\0')
2248
0
    PG_RETURN_NULL();
2249
0
  else if (relkind != RELKIND_SEQUENCE)
2250
0
    ereport(ERROR,
2251
0
        (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2252
0
         errmsg("\"%s\" is not a sequence",
2253
0
            get_rel_name(sequenceoid))));
2254
2255
0
  aclresult = pg_class_aclcheck_ext(sequenceoid, roleid, mode, &is_missing);
2256
2257
0
  if (is_missing)
2258
0
    PG_RETURN_NULL();
2259
2260
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
2261
0
}
2262
2263
/*
2264
 * has_sequence_privilege_id_name
2265
 *    Check user privileges on a sequence given
2266
 *    roleid, text sequencename, and text priv name.
2267
 */
2268
Datum
2269
has_sequence_privilege_id_name(PG_FUNCTION_ARGS)
2270
0
{
2271
0
  Oid     roleid = PG_GETARG_OID(0);
2272
0
  text     *sequencename = PG_GETARG_TEXT_PP(1);
2273
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
2274
0
  Oid     sequenceoid;
2275
0
  AclMode   mode;
2276
0
  AclResult aclresult;
2277
2278
0
  mode = convert_sequence_priv_string(priv_type_text);
2279
0
  sequenceoid = convert_table_name(sequencename);
2280
0
  if (get_rel_relkind(sequenceoid) != RELKIND_SEQUENCE)
2281
0
    ereport(ERROR,
2282
0
        (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2283
0
         errmsg("\"%s\" is not a sequence",
2284
0
            text_to_cstring(sequencename))));
2285
2286
0
  aclresult = pg_class_aclcheck(sequenceoid, roleid, mode);
2287
2288
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
2289
0
}
2290
2291
/*
2292
 * has_sequence_privilege_id_id
2293
 *    Check user privileges on a sequence given
2294
 *    roleid, sequence oid, and text priv name.
2295
 */
2296
Datum
2297
has_sequence_privilege_id_id(PG_FUNCTION_ARGS)
2298
0
{
2299
0
  Oid     roleid = PG_GETARG_OID(0);
2300
0
  Oid     sequenceoid = PG_GETARG_OID(1);
2301
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
2302
0
  AclMode   mode;
2303
0
  AclResult aclresult;
2304
0
  char    relkind;
2305
0
  bool    is_missing = false;
2306
2307
0
  mode = convert_sequence_priv_string(priv_type_text);
2308
0
  relkind = get_rel_relkind(sequenceoid);
2309
0
  if (relkind == '\0')
2310
0
    PG_RETURN_NULL();
2311
0
  else if (relkind != RELKIND_SEQUENCE)
2312
0
    ereport(ERROR,
2313
0
        (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2314
0
         errmsg("\"%s\" is not a sequence",
2315
0
            get_rel_name(sequenceoid))));
2316
2317
0
  aclresult = pg_class_aclcheck_ext(sequenceoid, roleid, mode, &is_missing);
2318
2319
0
  if (is_missing)
2320
0
    PG_RETURN_NULL();
2321
2322
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
2323
0
}
2324
2325
/*
2326
 * convert_sequence_priv_string
2327
 *    Convert text string to AclMode value.
2328
 */
2329
static AclMode
2330
convert_sequence_priv_string(text *priv_type_text)
2331
0
{
2332
0
  static const priv_map sequence_priv_map[] = {
2333
0
    {"USAGE", ACL_USAGE},
2334
0
    {"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
2335
0
    {"SELECT", ACL_SELECT},
2336
0
    {"SELECT WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_SELECT)},
2337
0
    {"UPDATE", ACL_UPDATE},
2338
0
    {"UPDATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_UPDATE)},
2339
0
    {NULL, 0}
2340
0
  };
2341
2342
0
  return convert_any_priv_string(priv_type_text, sequence_priv_map);
2343
0
}
2344
2345
2346
/*
2347
 * has_any_column_privilege variants
2348
 *    These are all named "has_any_column_privilege" at the SQL level.
2349
 *    They take various combinations of relation name, relation OID,
2350
 *    user name, user OID, or implicit user = current_user.
2351
 *
2352
 *    The result is a boolean value: true if user has the indicated
2353
 *    privilege for any column of the table, false if not.  The variants
2354
 *    that take a relation OID return NULL if the OID doesn't exist.
2355
 */
2356
2357
/*
2358
 * has_any_column_privilege_name_name
2359
 *    Check user privileges on any column of a table given
2360
 *    name username, text tablename, and text priv name.
2361
 */
2362
Datum
2363
has_any_column_privilege_name_name(PG_FUNCTION_ARGS)
2364
0
{
2365
0
  Name    rolename = PG_GETARG_NAME(0);
2366
0
  text     *tablename = PG_GETARG_TEXT_PP(1);
2367
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
2368
0
  Oid     roleid;
2369
0
  Oid     tableoid;
2370
0
  AclMode   mode;
2371
0
  AclResult aclresult;
2372
2373
0
  roleid = get_role_oid_or_public(NameStr(*rolename));
2374
0
  tableoid = convert_table_name(tablename);
2375
0
  mode = convert_column_priv_string(priv_type_text);
2376
2377
  /* First check at table level, then examine each column if needed */
2378
0
  aclresult = pg_class_aclcheck(tableoid, roleid, mode);
2379
0
  if (aclresult != ACLCHECK_OK)
2380
0
    aclresult = pg_attribute_aclcheck_all(tableoid, roleid, mode,
2381
0
                        ACLMASK_ANY);
2382
2383
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
2384
0
}
2385
2386
/*
2387
 * has_any_column_privilege_name
2388
 *    Check user privileges on any column of a table given
2389
 *    text tablename and text priv name.
2390
 *    current_user is assumed
2391
 */
2392
Datum
2393
has_any_column_privilege_name(PG_FUNCTION_ARGS)
2394
0
{
2395
0
  text     *tablename = PG_GETARG_TEXT_PP(0);
2396
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(1);
2397
0
  Oid     roleid;
2398
0
  Oid     tableoid;
2399
0
  AclMode   mode;
2400
0
  AclResult aclresult;
2401
2402
0
  roleid = GetUserId();
2403
0
  tableoid = convert_table_name(tablename);
2404
0
  mode = convert_column_priv_string(priv_type_text);
2405
2406
  /* First check at table level, then examine each column if needed */
2407
0
  aclresult = pg_class_aclcheck(tableoid, roleid, mode);
2408
0
  if (aclresult != ACLCHECK_OK)
2409
0
    aclresult = pg_attribute_aclcheck_all(tableoid, roleid, mode,
2410
0
                        ACLMASK_ANY);
2411
2412
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
2413
0
}
2414
2415
/*
2416
 * has_any_column_privilege_name_id
2417
 *    Check user privileges on any column of a table given
2418
 *    name usename, table oid, and text priv name.
2419
 */
2420
Datum
2421
has_any_column_privilege_name_id(PG_FUNCTION_ARGS)
2422
0
{
2423
0
  Name    username = PG_GETARG_NAME(0);
2424
0
  Oid     tableoid = PG_GETARG_OID(1);
2425
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
2426
0
  Oid     roleid;
2427
0
  AclMode   mode;
2428
0
  AclResult aclresult;
2429
0
  bool    is_missing = false;
2430
2431
0
  roleid = get_role_oid_or_public(NameStr(*username));
2432
0
  mode = convert_column_priv_string(priv_type_text);
2433
2434
  /* First check at table level, then examine each column if needed */
2435
0
  aclresult = pg_class_aclcheck_ext(tableoid, roleid, mode, &is_missing);
2436
0
  if (aclresult != ACLCHECK_OK)
2437
0
  {
2438
0
    if (is_missing)
2439
0
      PG_RETURN_NULL();
2440
0
    aclresult = pg_attribute_aclcheck_all_ext(tableoid, roleid, mode,
2441
0
                          ACLMASK_ANY, &is_missing);
2442
0
    if (is_missing)
2443
0
      PG_RETURN_NULL();
2444
0
  }
2445
2446
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
2447
0
}
2448
2449
/*
2450
 * has_any_column_privilege_id
2451
 *    Check user privileges on any column of a table given
2452
 *    table oid, and text priv name.
2453
 *    current_user is assumed
2454
 */
2455
Datum
2456
has_any_column_privilege_id(PG_FUNCTION_ARGS)
2457
0
{
2458
0
  Oid     tableoid = PG_GETARG_OID(0);
2459
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(1);
2460
0
  Oid     roleid;
2461
0
  AclMode   mode;
2462
0
  AclResult aclresult;
2463
0
  bool    is_missing = false;
2464
2465
0
  roleid = GetUserId();
2466
0
  mode = convert_column_priv_string(priv_type_text);
2467
2468
  /* First check at table level, then examine each column if needed */
2469
0
  aclresult = pg_class_aclcheck_ext(tableoid, roleid, mode, &is_missing);
2470
0
  if (aclresult != ACLCHECK_OK)
2471
0
  {
2472
0
    if (is_missing)
2473
0
      PG_RETURN_NULL();
2474
0
    aclresult = pg_attribute_aclcheck_all_ext(tableoid, roleid, mode,
2475
0
                          ACLMASK_ANY, &is_missing);
2476
0
    if (is_missing)
2477
0
      PG_RETURN_NULL();
2478
0
  }
2479
2480
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
2481
0
}
2482
2483
/*
2484
 * has_any_column_privilege_id_name
2485
 *    Check user privileges on any column of a table given
2486
 *    roleid, text tablename, and text priv name.
2487
 */
2488
Datum
2489
has_any_column_privilege_id_name(PG_FUNCTION_ARGS)
2490
0
{
2491
0
  Oid     roleid = PG_GETARG_OID(0);
2492
0
  text     *tablename = PG_GETARG_TEXT_PP(1);
2493
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
2494
0
  Oid     tableoid;
2495
0
  AclMode   mode;
2496
0
  AclResult aclresult;
2497
2498
0
  tableoid = convert_table_name(tablename);
2499
0
  mode = convert_column_priv_string(priv_type_text);
2500
2501
  /* First check at table level, then examine each column if needed */
2502
0
  aclresult = pg_class_aclcheck(tableoid, roleid, mode);
2503
0
  if (aclresult != ACLCHECK_OK)
2504
0
    aclresult = pg_attribute_aclcheck_all(tableoid, roleid, mode,
2505
0
                        ACLMASK_ANY);
2506
2507
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
2508
0
}
2509
2510
/*
2511
 * has_any_column_privilege_id_id
2512
 *    Check user privileges on any column of a table given
2513
 *    roleid, table oid, and text priv name.
2514
 */
2515
Datum
2516
has_any_column_privilege_id_id(PG_FUNCTION_ARGS)
2517
0
{
2518
0
  Oid     roleid = PG_GETARG_OID(0);
2519
0
  Oid     tableoid = PG_GETARG_OID(1);
2520
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
2521
0
  AclMode   mode;
2522
0
  AclResult aclresult;
2523
0
  bool    is_missing = false;
2524
2525
0
  mode = convert_column_priv_string(priv_type_text);
2526
2527
  /* First check at table level, then examine each column if needed */
2528
0
  aclresult = pg_class_aclcheck_ext(tableoid, roleid, mode, &is_missing);
2529
0
  if (aclresult != ACLCHECK_OK)
2530
0
  {
2531
0
    if (is_missing)
2532
0
      PG_RETURN_NULL();
2533
0
    aclresult = pg_attribute_aclcheck_all_ext(tableoid, roleid, mode,
2534
0
                          ACLMASK_ANY, &is_missing);
2535
0
    if (is_missing)
2536
0
      PG_RETURN_NULL();
2537
0
  }
2538
2539
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
2540
0
}
2541
2542
2543
/*
2544
 * has_column_privilege variants
2545
 *    These are all named "has_column_privilege" at the SQL level.
2546
 *    They take various combinations of relation name, relation OID,
2547
 *    column name, column attnum, user name, user OID, or
2548
 *    implicit user = current_user.
2549
 *
2550
 *    The result is a boolean value: true if user has the indicated
2551
 *    privilege, false if not.  The variants that take a relation OID
2552
 *    return NULL (rather than throwing an error) if that relation OID
2553
 *    doesn't exist.  Likewise, the variants that take an integer attnum
2554
 *    return NULL (rather than throwing an error) if there is no such
2555
 *    pg_attribute entry.  All variants return NULL if an attisdropped
2556
 *    column is selected.  These rules are meant to avoid unnecessary
2557
 *    failures in queries that scan pg_attribute.
2558
 */
2559
2560
/*
2561
 * column_privilege_check: check column privileges, but don't throw an error
2562
 *    for dropped column or table
2563
 *
2564
 * Returns 1 if have the privilege, 0 if not, -1 if dropped column/table.
2565
 */
2566
static int
2567
column_privilege_check(Oid tableoid, AttrNumber attnum,
2568
             Oid roleid, AclMode mode)
2569
0
{
2570
0
  AclResult aclresult;
2571
0
  bool    is_missing = false;
2572
2573
  /*
2574
   * If convert_column_name failed, we can just return -1 immediately.
2575
   */
2576
0
  if (attnum == InvalidAttrNumber)
2577
0
    return -1;
2578
2579
  /*
2580
   * Check for column-level privileges first. This serves in part as a check
2581
   * on whether the column even exists, so we need to do it before checking
2582
   * table-level privilege.
2583
   */
2584
0
  aclresult = pg_attribute_aclcheck_ext(tableoid, attnum, roleid,
2585
0
                      mode, &is_missing);
2586
0
  if (aclresult == ACLCHECK_OK)
2587
0
    return 1;
2588
0
  else if (is_missing)
2589
0
    return -1;
2590
2591
  /* Next check if we have the privilege at the table level */
2592
0
  aclresult = pg_class_aclcheck_ext(tableoid, roleid, mode, &is_missing);
2593
0
  if (aclresult == ACLCHECK_OK)
2594
0
    return 1;
2595
0
  else if (is_missing)
2596
0
    return -1;
2597
0
  else
2598
0
    return 0;
2599
0
}
2600
2601
/*
2602
 * has_column_privilege_name_name_name
2603
 *    Check user privileges on a column given
2604
 *    name username, text tablename, text colname, and text priv name.
2605
 */
2606
Datum
2607
has_column_privilege_name_name_name(PG_FUNCTION_ARGS)
2608
0
{
2609
0
  Name    rolename = PG_GETARG_NAME(0);
2610
0
  text     *tablename = PG_GETARG_TEXT_PP(1);
2611
0
  text     *column = PG_GETARG_TEXT_PP(2);
2612
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(3);
2613
0
  Oid     roleid;
2614
0
  Oid     tableoid;
2615
0
  AttrNumber  colattnum;
2616
0
  AclMode   mode;
2617
0
  int     privresult;
2618
2619
0
  roleid = get_role_oid_or_public(NameStr(*rolename));
2620
0
  tableoid = convert_table_name(tablename);
2621
0
  colattnum = convert_column_name(tableoid, column);
2622
0
  mode = convert_column_priv_string(priv_type_text);
2623
2624
0
  privresult = column_privilege_check(tableoid, colattnum, roleid, mode);
2625
0
  if (privresult < 0)
2626
0
    PG_RETURN_NULL();
2627
0
  PG_RETURN_BOOL(privresult);
2628
0
}
2629
2630
/*
2631
 * has_column_privilege_name_name_attnum
2632
 *    Check user privileges on a column given
2633
 *    name username, text tablename, int attnum, and text priv name.
2634
 */
2635
Datum
2636
has_column_privilege_name_name_attnum(PG_FUNCTION_ARGS)
2637
0
{
2638
0
  Name    rolename = PG_GETARG_NAME(0);
2639
0
  text     *tablename = PG_GETARG_TEXT_PP(1);
2640
0
  AttrNumber  colattnum = PG_GETARG_INT16(2);
2641
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(3);
2642
0
  Oid     roleid;
2643
0
  Oid     tableoid;
2644
0
  AclMode   mode;
2645
0
  int     privresult;
2646
2647
0
  roleid = get_role_oid_or_public(NameStr(*rolename));
2648
0
  tableoid = convert_table_name(tablename);
2649
0
  mode = convert_column_priv_string(priv_type_text);
2650
2651
0
  privresult = column_privilege_check(tableoid, colattnum, roleid, mode);
2652
0
  if (privresult < 0)
2653
0
    PG_RETURN_NULL();
2654
0
  PG_RETURN_BOOL(privresult);
2655
0
}
2656
2657
/*
2658
 * has_column_privilege_name_id_name
2659
 *    Check user privileges on a column given
2660
 *    name username, table oid, text colname, and text priv name.
2661
 */
2662
Datum
2663
has_column_privilege_name_id_name(PG_FUNCTION_ARGS)
2664
0
{
2665
0
  Name    username = PG_GETARG_NAME(0);
2666
0
  Oid     tableoid = PG_GETARG_OID(1);
2667
0
  text     *column = PG_GETARG_TEXT_PP(2);
2668
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(3);
2669
0
  Oid     roleid;
2670
0
  AttrNumber  colattnum;
2671
0
  AclMode   mode;
2672
0
  int     privresult;
2673
2674
0
  roleid = get_role_oid_or_public(NameStr(*username));
2675
0
  colattnum = convert_column_name(tableoid, column);
2676
0
  mode = convert_column_priv_string(priv_type_text);
2677
2678
0
  privresult = column_privilege_check(tableoid, colattnum, roleid, mode);
2679
0
  if (privresult < 0)
2680
0
    PG_RETURN_NULL();
2681
0
  PG_RETURN_BOOL(privresult);
2682
0
}
2683
2684
/*
2685
 * has_column_privilege_name_id_attnum
2686
 *    Check user privileges on a column given
2687
 *    name username, table oid, int attnum, and text priv name.
2688
 */
2689
Datum
2690
has_column_privilege_name_id_attnum(PG_FUNCTION_ARGS)
2691
0
{
2692
0
  Name    username = PG_GETARG_NAME(0);
2693
0
  Oid     tableoid = PG_GETARG_OID(1);
2694
0
  AttrNumber  colattnum = PG_GETARG_INT16(2);
2695
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(3);
2696
0
  Oid     roleid;
2697
0
  AclMode   mode;
2698
0
  int     privresult;
2699
2700
0
  roleid = get_role_oid_or_public(NameStr(*username));
2701
0
  mode = convert_column_priv_string(priv_type_text);
2702
2703
0
  privresult = column_privilege_check(tableoid, colattnum, roleid, mode);
2704
0
  if (privresult < 0)
2705
0
    PG_RETURN_NULL();
2706
0
  PG_RETURN_BOOL(privresult);
2707
0
}
2708
2709
/*
2710
 * has_column_privilege_id_name_name
2711
 *    Check user privileges on a column given
2712
 *    oid roleid, text tablename, text colname, and text priv name.
2713
 */
2714
Datum
2715
has_column_privilege_id_name_name(PG_FUNCTION_ARGS)
2716
0
{
2717
0
  Oid     roleid = PG_GETARG_OID(0);
2718
0
  text     *tablename = PG_GETARG_TEXT_PP(1);
2719
0
  text     *column = PG_GETARG_TEXT_PP(2);
2720
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(3);
2721
0
  Oid     tableoid;
2722
0
  AttrNumber  colattnum;
2723
0
  AclMode   mode;
2724
0
  int     privresult;
2725
2726
0
  tableoid = convert_table_name(tablename);
2727
0
  colattnum = convert_column_name(tableoid, column);
2728
0
  mode = convert_column_priv_string(priv_type_text);
2729
2730
0
  privresult = column_privilege_check(tableoid, colattnum, roleid, mode);
2731
0
  if (privresult < 0)
2732
0
    PG_RETURN_NULL();
2733
0
  PG_RETURN_BOOL(privresult);
2734
0
}
2735
2736
/*
2737
 * has_column_privilege_id_name_attnum
2738
 *    Check user privileges on a column given
2739
 *    oid roleid, text tablename, int attnum, and text priv name.
2740
 */
2741
Datum
2742
has_column_privilege_id_name_attnum(PG_FUNCTION_ARGS)
2743
0
{
2744
0
  Oid     roleid = PG_GETARG_OID(0);
2745
0
  text     *tablename = PG_GETARG_TEXT_PP(1);
2746
0
  AttrNumber  colattnum = PG_GETARG_INT16(2);
2747
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(3);
2748
0
  Oid     tableoid;
2749
0
  AclMode   mode;
2750
0
  int     privresult;
2751
2752
0
  tableoid = convert_table_name(tablename);
2753
0
  mode = convert_column_priv_string(priv_type_text);
2754
2755
0
  privresult = column_privilege_check(tableoid, colattnum, roleid, mode);
2756
0
  if (privresult < 0)
2757
0
    PG_RETURN_NULL();
2758
0
  PG_RETURN_BOOL(privresult);
2759
0
}
2760
2761
/*
2762
 * has_column_privilege_id_id_name
2763
 *    Check user privileges on a column given
2764
 *    oid roleid, table oid, text colname, and text priv name.
2765
 */
2766
Datum
2767
has_column_privilege_id_id_name(PG_FUNCTION_ARGS)
2768
0
{
2769
0
  Oid     roleid = PG_GETARG_OID(0);
2770
0
  Oid     tableoid = PG_GETARG_OID(1);
2771
0
  text     *column = PG_GETARG_TEXT_PP(2);
2772
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(3);
2773
0
  AttrNumber  colattnum;
2774
0
  AclMode   mode;
2775
0
  int     privresult;
2776
2777
0
  colattnum = convert_column_name(tableoid, column);
2778
0
  mode = convert_column_priv_string(priv_type_text);
2779
2780
0
  privresult = column_privilege_check(tableoid, colattnum, roleid, mode);
2781
0
  if (privresult < 0)
2782
0
    PG_RETURN_NULL();
2783
0
  PG_RETURN_BOOL(privresult);
2784
0
}
2785
2786
/*
2787
 * has_column_privilege_id_id_attnum
2788
 *    Check user privileges on a column given
2789
 *    oid roleid, table oid, int attnum, and text priv name.
2790
 */
2791
Datum
2792
has_column_privilege_id_id_attnum(PG_FUNCTION_ARGS)
2793
0
{
2794
0
  Oid     roleid = PG_GETARG_OID(0);
2795
0
  Oid     tableoid = PG_GETARG_OID(1);
2796
0
  AttrNumber  colattnum = PG_GETARG_INT16(2);
2797
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(3);
2798
0
  AclMode   mode;
2799
0
  int     privresult;
2800
2801
0
  mode = convert_column_priv_string(priv_type_text);
2802
2803
0
  privresult = column_privilege_check(tableoid, colattnum, roleid, mode);
2804
0
  if (privresult < 0)
2805
0
    PG_RETURN_NULL();
2806
0
  PG_RETURN_BOOL(privresult);
2807
0
}
2808
2809
/*
2810
 * has_column_privilege_name_name
2811
 *    Check user privileges on a column given
2812
 *    text tablename, text colname, and text priv name.
2813
 *    current_user is assumed
2814
 */
2815
Datum
2816
has_column_privilege_name_name(PG_FUNCTION_ARGS)
2817
0
{
2818
0
  text     *tablename = PG_GETARG_TEXT_PP(0);
2819
0
  text     *column = PG_GETARG_TEXT_PP(1);
2820
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
2821
0
  Oid     roleid;
2822
0
  Oid     tableoid;
2823
0
  AttrNumber  colattnum;
2824
0
  AclMode   mode;
2825
0
  int     privresult;
2826
2827
0
  roleid = GetUserId();
2828
0
  tableoid = convert_table_name(tablename);
2829
0
  colattnum = convert_column_name(tableoid, column);
2830
0
  mode = convert_column_priv_string(priv_type_text);
2831
2832
0
  privresult = column_privilege_check(tableoid, colattnum, roleid, mode);
2833
0
  if (privresult < 0)
2834
0
    PG_RETURN_NULL();
2835
0
  PG_RETURN_BOOL(privresult);
2836
0
}
2837
2838
/*
2839
 * has_column_privilege_name_attnum
2840
 *    Check user privileges on a column given
2841
 *    text tablename, int attnum, and text priv name.
2842
 *    current_user is assumed
2843
 */
2844
Datum
2845
has_column_privilege_name_attnum(PG_FUNCTION_ARGS)
2846
0
{
2847
0
  text     *tablename = PG_GETARG_TEXT_PP(0);
2848
0
  AttrNumber  colattnum = PG_GETARG_INT16(1);
2849
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
2850
0
  Oid     roleid;
2851
0
  Oid     tableoid;
2852
0
  AclMode   mode;
2853
0
  int     privresult;
2854
2855
0
  roleid = GetUserId();
2856
0
  tableoid = convert_table_name(tablename);
2857
0
  mode = convert_column_priv_string(priv_type_text);
2858
2859
0
  privresult = column_privilege_check(tableoid, colattnum, roleid, mode);
2860
0
  if (privresult < 0)
2861
0
    PG_RETURN_NULL();
2862
0
  PG_RETURN_BOOL(privresult);
2863
0
}
2864
2865
/*
2866
 * has_column_privilege_id_name
2867
 *    Check user privileges on a column given
2868
 *    table oid, text colname, and text priv name.
2869
 *    current_user is assumed
2870
 */
2871
Datum
2872
has_column_privilege_id_name(PG_FUNCTION_ARGS)
2873
0
{
2874
0
  Oid     tableoid = PG_GETARG_OID(0);
2875
0
  text     *column = PG_GETARG_TEXT_PP(1);
2876
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
2877
0
  Oid     roleid;
2878
0
  AttrNumber  colattnum;
2879
0
  AclMode   mode;
2880
0
  int     privresult;
2881
2882
0
  roleid = GetUserId();
2883
0
  colattnum = convert_column_name(tableoid, column);
2884
0
  mode = convert_column_priv_string(priv_type_text);
2885
2886
0
  privresult = column_privilege_check(tableoid, colattnum, roleid, mode);
2887
0
  if (privresult < 0)
2888
0
    PG_RETURN_NULL();
2889
0
  PG_RETURN_BOOL(privresult);
2890
0
}
2891
2892
/*
2893
 * has_column_privilege_id_attnum
2894
 *    Check user privileges on a column given
2895
 *    table oid, int attnum, and text priv name.
2896
 *    current_user is assumed
2897
 */
2898
Datum
2899
has_column_privilege_id_attnum(PG_FUNCTION_ARGS)
2900
0
{
2901
0
  Oid     tableoid = PG_GETARG_OID(0);
2902
0
  AttrNumber  colattnum = PG_GETARG_INT16(1);
2903
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
2904
0
  Oid     roleid;
2905
0
  AclMode   mode;
2906
0
  int     privresult;
2907
2908
0
  roleid = GetUserId();
2909
0
  mode = convert_column_priv_string(priv_type_text);
2910
2911
0
  privresult = column_privilege_check(tableoid, colattnum, roleid, mode);
2912
0
  if (privresult < 0)
2913
0
    PG_RETURN_NULL();
2914
0
  PG_RETURN_BOOL(privresult);
2915
0
}
2916
2917
/*
2918
 *    Support routines for has_column_privilege family.
2919
 */
2920
2921
/*
2922
 * Given a table OID and a column name expressed as a string, look it up
2923
 * and return the column number.  Returns InvalidAttrNumber in cases
2924
 * where caller should return NULL instead of failing.
2925
 */
2926
static AttrNumber
2927
convert_column_name(Oid tableoid, text *column)
2928
0
{
2929
0
  char     *colname;
2930
0
  HeapTuple attTuple;
2931
0
  AttrNumber  attnum;
2932
2933
0
  colname = text_to_cstring(column);
2934
2935
  /*
2936
   * We don't use get_attnum() here because it will report that dropped
2937
   * columns don't exist.  We need to treat dropped columns differently from
2938
   * nonexistent columns.
2939
   */
2940
0
  attTuple = SearchSysCache2(ATTNAME,
2941
0
                 ObjectIdGetDatum(tableoid),
2942
0
                 CStringGetDatum(colname));
2943
0
  if (HeapTupleIsValid(attTuple))
2944
0
  {
2945
0
    Form_pg_attribute attributeForm;
2946
2947
0
    attributeForm = (Form_pg_attribute) GETSTRUCT(attTuple);
2948
    /* We want to return NULL for dropped columns */
2949
0
    if (attributeForm->attisdropped)
2950
0
      attnum = InvalidAttrNumber;
2951
0
    else
2952
0
      attnum = attributeForm->attnum;
2953
0
    ReleaseSysCache(attTuple);
2954
0
  }
2955
0
  else
2956
0
  {
2957
0
    char     *tablename = get_rel_name(tableoid);
2958
2959
    /*
2960
     * If the table OID is bogus, or it's just been dropped, we'll get
2961
     * NULL back.  In such cases we want has_column_privilege to return
2962
     * NULL too, so just return InvalidAttrNumber.
2963
     */
2964
0
    if (tablename != NULL)
2965
0
    {
2966
      /* tableoid exists, colname does not, so throw error */
2967
0
      ereport(ERROR,
2968
0
          (errcode(ERRCODE_UNDEFINED_COLUMN),
2969
0
           errmsg("column \"%s\" of relation \"%s\" does not exist",
2970
0
              colname, tablename)));
2971
0
    }
2972
    /* tableoid doesn't exist, so act like attisdropped case */
2973
0
    attnum = InvalidAttrNumber;
2974
0
  }
2975
2976
0
  pfree(colname);
2977
0
  return attnum;
2978
0
}
2979
2980
/*
2981
 * convert_column_priv_string
2982
 *    Convert text string to AclMode value.
2983
 */
2984
static AclMode
2985
convert_column_priv_string(text *priv_type_text)
2986
0
{
2987
0
  static const priv_map column_priv_map[] = {
2988
0
    {"SELECT", ACL_SELECT},
2989
0
    {"SELECT WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_SELECT)},
2990
0
    {"INSERT", ACL_INSERT},
2991
0
    {"INSERT WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_INSERT)},
2992
0
    {"UPDATE", ACL_UPDATE},
2993
0
    {"UPDATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_UPDATE)},
2994
0
    {"REFERENCES", ACL_REFERENCES},
2995
0
    {"REFERENCES WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_REFERENCES)},
2996
0
    {NULL, 0}
2997
0
  };
2998
2999
0
  return convert_any_priv_string(priv_type_text, column_priv_map);
3000
0
}
3001
3002
3003
/*
3004
 * has_database_privilege variants
3005
 *    These are all named "has_database_privilege" at the SQL level.
3006
 *    They take various combinations of database name, database OID,
3007
 *    user name, user OID, or implicit user = current_user.
3008
 *
3009
 *    The result is a boolean value: true if user has the indicated
3010
 *    privilege, false if not, or NULL if object doesn't exist.
3011
 */
3012
3013
/*
3014
 * has_database_privilege_name_name
3015
 *    Check user privileges on a database given
3016
 *    name username, text databasename, and text priv name.
3017
 */
3018
Datum
3019
has_database_privilege_name_name(PG_FUNCTION_ARGS)
3020
0
{
3021
0
  Name    username = PG_GETARG_NAME(0);
3022
0
  text     *databasename = PG_GETARG_TEXT_PP(1);
3023
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
3024
0
  Oid     roleid;
3025
0
  Oid     databaseoid;
3026
0
  AclMode   mode;
3027
0
  AclResult aclresult;
3028
3029
0
  roleid = get_role_oid_or_public(NameStr(*username));
3030
0
  databaseoid = convert_database_name(databasename);
3031
0
  mode = convert_database_priv_string(priv_type_text);
3032
3033
0
  aclresult = object_aclcheck(DatabaseRelationId, databaseoid, roleid, mode);
3034
3035
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
3036
0
}
3037
3038
/*
3039
 * has_database_privilege_name
3040
 *    Check user privileges on a database given
3041
 *    text databasename and text priv name.
3042
 *    current_user is assumed
3043
 */
3044
Datum
3045
has_database_privilege_name(PG_FUNCTION_ARGS)
3046
0
{
3047
0
  text     *databasename = PG_GETARG_TEXT_PP(0);
3048
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(1);
3049
0
  Oid     roleid;
3050
0
  Oid     databaseoid;
3051
0
  AclMode   mode;
3052
0
  AclResult aclresult;
3053
3054
0
  roleid = GetUserId();
3055
0
  databaseoid = convert_database_name(databasename);
3056
0
  mode = convert_database_priv_string(priv_type_text);
3057
3058
0
  aclresult = object_aclcheck(DatabaseRelationId, databaseoid, roleid, mode);
3059
3060
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
3061
0
}
3062
3063
/*
3064
 * has_database_privilege_name_id
3065
 *    Check user privileges on a database given
3066
 *    name usename, database oid, and text priv name.
3067
 */
3068
Datum
3069
has_database_privilege_name_id(PG_FUNCTION_ARGS)
3070
0
{
3071
0
  Name    username = PG_GETARG_NAME(0);
3072
0
  Oid     databaseoid = PG_GETARG_OID(1);
3073
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
3074
0
  Oid     roleid;
3075
0
  AclMode   mode;
3076
0
  AclResult aclresult;
3077
0
  bool    is_missing = false;
3078
3079
0
  roleid = get_role_oid_or_public(NameStr(*username));
3080
0
  mode = convert_database_priv_string(priv_type_text);
3081
3082
0
  aclresult = object_aclcheck_ext(DatabaseRelationId, databaseoid,
3083
0
                  roleid, mode,
3084
0
                  &is_missing);
3085
3086
0
  if (is_missing)
3087
0
    PG_RETURN_NULL();
3088
3089
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
3090
0
}
3091
3092
/*
3093
 * has_database_privilege_id
3094
 *    Check user privileges on a database given
3095
 *    database oid, and text priv name.
3096
 *    current_user is assumed
3097
 */
3098
Datum
3099
has_database_privilege_id(PG_FUNCTION_ARGS)
3100
0
{
3101
0
  Oid     databaseoid = PG_GETARG_OID(0);
3102
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(1);
3103
0
  Oid     roleid;
3104
0
  AclMode   mode;
3105
0
  AclResult aclresult;
3106
0
  bool    is_missing = false;
3107
3108
0
  roleid = GetUserId();
3109
0
  mode = convert_database_priv_string(priv_type_text);
3110
3111
0
  aclresult = object_aclcheck_ext(DatabaseRelationId, databaseoid,
3112
0
                  roleid, mode,
3113
0
                  &is_missing);
3114
3115
0
  if (is_missing)
3116
0
    PG_RETURN_NULL();
3117
3118
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
3119
0
}
3120
3121
/*
3122
 * has_database_privilege_id_name
3123
 *    Check user privileges on a database given
3124
 *    roleid, text databasename, and text priv name.
3125
 */
3126
Datum
3127
has_database_privilege_id_name(PG_FUNCTION_ARGS)
3128
0
{
3129
0
  Oid     roleid = PG_GETARG_OID(0);
3130
0
  text     *databasename = PG_GETARG_TEXT_PP(1);
3131
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
3132
0
  Oid     databaseoid;
3133
0
  AclMode   mode;
3134
0
  AclResult aclresult;
3135
3136
0
  databaseoid = convert_database_name(databasename);
3137
0
  mode = convert_database_priv_string(priv_type_text);
3138
3139
0
  aclresult = object_aclcheck(DatabaseRelationId, databaseoid, roleid, mode);
3140
3141
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
3142
0
}
3143
3144
/*
3145
 * has_database_privilege_id_id
3146
 *    Check user privileges on a database given
3147
 *    roleid, database oid, and text priv name.
3148
 */
3149
Datum
3150
has_database_privilege_id_id(PG_FUNCTION_ARGS)
3151
0
{
3152
0
  Oid     roleid = PG_GETARG_OID(0);
3153
0
  Oid     databaseoid = PG_GETARG_OID(1);
3154
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
3155
0
  AclMode   mode;
3156
0
  AclResult aclresult;
3157
0
  bool    is_missing = false;
3158
3159
0
  mode = convert_database_priv_string(priv_type_text);
3160
3161
0
  aclresult = object_aclcheck_ext(DatabaseRelationId, databaseoid,
3162
0
                  roleid, mode,
3163
0
                  &is_missing);
3164
3165
0
  if (is_missing)
3166
0
    PG_RETURN_NULL();
3167
3168
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
3169
0
}
3170
3171
/*
3172
 *    Support routines for has_database_privilege family.
3173
 */
3174
3175
/*
3176
 * Given a database name expressed as a string, look it up and return Oid
3177
 */
3178
static Oid
3179
convert_database_name(text *databasename)
3180
0
{
3181
0
  char     *dbname = text_to_cstring(databasename);
3182
3183
0
  return get_database_oid(dbname, false);
3184
0
}
3185
3186
/*
3187
 * convert_database_priv_string
3188
 *    Convert text string to AclMode value.
3189
 */
3190
static AclMode
3191
convert_database_priv_string(text *priv_type_text)
3192
0
{
3193
0
  static const priv_map database_priv_map[] = {
3194
0
    {"CREATE", ACL_CREATE},
3195
0
    {"CREATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
3196
0
    {"TEMPORARY", ACL_CREATE_TEMP},
3197
0
    {"TEMPORARY WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP)},
3198
0
    {"TEMP", ACL_CREATE_TEMP},
3199
0
    {"TEMP WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP)},
3200
0
    {"CONNECT", ACL_CONNECT},
3201
0
    {"CONNECT WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CONNECT)},
3202
0
    {NULL, 0}
3203
0
  };
3204
3205
0
  return convert_any_priv_string(priv_type_text, database_priv_map);
3206
0
}
3207
3208
3209
/*
3210
 * has_foreign_data_wrapper_privilege variants
3211
 *    These are all named "has_foreign_data_wrapper_privilege" at the SQL level.
3212
 *    They take various combinations of foreign-data wrapper name,
3213
 *    fdw OID, user name, user OID, or implicit user = current_user.
3214
 *
3215
 *    The result is a boolean value: true if user has the indicated
3216
 *    privilege, false if not.
3217
 */
3218
3219
/*
3220
 * has_foreign_data_wrapper_privilege_name_name
3221
 *    Check user privileges on a foreign-data wrapper given
3222
 *    name username, text fdwname, and text priv name.
3223
 */
3224
Datum
3225
has_foreign_data_wrapper_privilege_name_name(PG_FUNCTION_ARGS)
3226
0
{
3227
0
  Name    username = PG_GETARG_NAME(0);
3228
0
  text     *fdwname = PG_GETARG_TEXT_PP(1);
3229
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
3230
0
  Oid     roleid;
3231
0
  Oid     fdwid;
3232
0
  AclMode   mode;
3233
0
  AclResult aclresult;
3234
3235
0
  roleid = get_role_oid_or_public(NameStr(*username));
3236
0
  fdwid = convert_foreign_data_wrapper_name(fdwname);
3237
0
  mode = convert_foreign_data_wrapper_priv_string(priv_type_text);
3238
3239
0
  aclresult = object_aclcheck(ForeignDataWrapperRelationId, fdwid, roleid, mode);
3240
3241
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
3242
0
}
3243
3244
/*
3245
 * has_foreign_data_wrapper_privilege_name
3246
 *    Check user privileges on a foreign-data wrapper given
3247
 *    text fdwname and text priv name.
3248
 *    current_user is assumed
3249
 */
3250
Datum
3251
has_foreign_data_wrapper_privilege_name(PG_FUNCTION_ARGS)
3252
0
{
3253
0
  text     *fdwname = PG_GETARG_TEXT_PP(0);
3254
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(1);
3255
0
  Oid     roleid;
3256
0
  Oid     fdwid;
3257
0
  AclMode   mode;
3258
0
  AclResult aclresult;
3259
3260
0
  roleid = GetUserId();
3261
0
  fdwid = convert_foreign_data_wrapper_name(fdwname);
3262
0
  mode = convert_foreign_data_wrapper_priv_string(priv_type_text);
3263
3264
0
  aclresult = object_aclcheck(ForeignDataWrapperRelationId, fdwid, roleid, mode);
3265
3266
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
3267
0
}
3268
3269
/*
3270
 * has_foreign_data_wrapper_privilege_name_id
3271
 *    Check user privileges on a foreign-data wrapper given
3272
 *    name usename, foreign-data wrapper oid, and text priv name.
3273
 */
3274
Datum
3275
has_foreign_data_wrapper_privilege_name_id(PG_FUNCTION_ARGS)
3276
0
{
3277
0
  Name    username = PG_GETARG_NAME(0);
3278
0
  Oid     fdwid = PG_GETARG_OID(1);
3279
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
3280
0
  Oid     roleid;
3281
0
  AclMode   mode;
3282
0
  AclResult aclresult;
3283
0
  bool    is_missing = false;
3284
3285
0
  roleid = get_role_oid_or_public(NameStr(*username));
3286
0
  mode = convert_foreign_data_wrapper_priv_string(priv_type_text);
3287
3288
0
  aclresult = object_aclcheck_ext(ForeignDataWrapperRelationId, fdwid,
3289
0
                  roleid, mode,
3290
0
                  &is_missing);
3291
3292
0
  if (is_missing)
3293
0
    PG_RETURN_NULL();
3294
3295
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
3296
0
}
3297
3298
/*
3299
 * has_foreign_data_wrapper_privilege_id
3300
 *    Check user privileges on a foreign-data wrapper given
3301
 *    foreign-data wrapper oid, and text priv name.
3302
 *    current_user is assumed
3303
 */
3304
Datum
3305
has_foreign_data_wrapper_privilege_id(PG_FUNCTION_ARGS)
3306
0
{
3307
0
  Oid     fdwid = PG_GETARG_OID(0);
3308
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(1);
3309
0
  Oid     roleid;
3310
0
  AclMode   mode;
3311
0
  AclResult aclresult;
3312
0
  bool    is_missing = false;
3313
3314
0
  roleid = GetUserId();
3315
0
  mode = convert_foreign_data_wrapper_priv_string(priv_type_text);
3316
3317
0
  aclresult = object_aclcheck_ext(ForeignDataWrapperRelationId, fdwid,
3318
0
                  roleid, mode,
3319
0
                  &is_missing);
3320
3321
0
  if (is_missing)
3322
0
    PG_RETURN_NULL();
3323
3324
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
3325
0
}
3326
3327
/*
3328
 * has_foreign_data_wrapper_privilege_id_name
3329
 *    Check user privileges on a foreign-data wrapper given
3330
 *    roleid, text fdwname, and text priv name.
3331
 */
3332
Datum
3333
has_foreign_data_wrapper_privilege_id_name(PG_FUNCTION_ARGS)
3334
0
{
3335
0
  Oid     roleid = PG_GETARG_OID(0);
3336
0
  text     *fdwname = PG_GETARG_TEXT_PP(1);
3337
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
3338
0
  Oid     fdwid;
3339
0
  AclMode   mode;
3340
0
  AclResult aclresult;
3341
3342
0
  fdwid = convert_foreign_data_wrapper_name(fdwname);
3343
0
  mode = convert_foreign_data_wrapper_priv_string(priv_type_text);
3344
3345
0
  aclresult = object_aclcheck(ForeignDataWrapperRelationId, fdwid, roleid, mode);
3346
3347
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
3348
0
}
3349
3350
/*
3351
 * has_foreign_data_wrapper_privilege_id_id
3352
 *    Check user privileges on a foreign-data wrapper given
3353
 *    roleid, fdw oid, and text priv name.
3354
 */
3355
Datum
3356
has_foreign_data_wrapper_privilege_id_id(PG_FUNCTION_ARGS)
3357
0
{
3358
0
  Oid     roleid = PG_GETARG_OID(0);
3359
0
  Oid     fdwid = PG_GETARG_OID(1);
3360
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
3361
0
  AclMode   mode;
3362
0
  AclResult aclresult;
3363
0
  bool    is_missing = false;
3364
3365
0
  mode = convert_foreign_data_wrapper_priv_string(priv_type_text);
3366
3367
0
  aclresult = object_aclcheck_ext(ForeignDataWrapperRelationId, fdwid,
3368
0
                  roleid, mode,
3369
0
                  &is_missing);
3370
3371
0
  if (is_missing)
3372
0
    PG_RETURN_NULL();
3373
3374
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
3375
0
}
3376
3377
/*
3378
 *    Support routines for has_foreign_data_wrapper_privilege family.
3379
 */
3380
3381
/*
3382
 * Given a FDW name expressed as a string, look it up and return Oid
3383
 */
3384
static Oid
3385
convert_foreign_data_wrapper_name(text *fdwname)
3386
0
{
3387
0
  char     *fdwstr = text_to_cstring(fdwname);
3388
3389
0
  return get_foreign_data_wrapper_oid(fdwstr, false);
3390
0
}
3391
3392
/*
3393
 * convert_foreign_data_wrapper_priv_string
3394
 *    Convert text string to AclMode value.
3395
 */
3396
static AclMode
3397
convert_foreign_data_wrapper_priv_string(text *priv_type_text)
3398
0
{
3399
0
  static const priv_map foreign_data_wrapper_priv_map[] = {
3400
0
    {"USAGE", ACL_USAGE},
3401
0
    {"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
3402
0
    {NULL, 0}
3403
0
  };
3404
3405
0
  return convert_any_priv_string(priv_type_text, foreign_data_wrapper_priv_map);
3406
0
}
3407
3408
3409
/*
3410
 * has_function_privilege variants
3411
 *    These are all named "has_function_privilege" at the SQL level.
3412
 *    They take various combinations of function name, function OID,
3413
 *    user name, user OID, or implicit user = current_user.
3414
 *
3415
 *    The result is a boolean value: true if user has the indicated
3416
 *    privilege, false if not, or NULL if object doesn't exist.
3417
 */
3418
3419
/*
3420
 * has_function_privilege_name_name
3421
 *    Check user privileges on a function given
3422
 *    name username, text functionname, and text priv name.
3423
 */
3424
Datum
3425
has_function_privilege_name_name(PG_FUNCTION_ARGS)
3426
0
{
3427
0
  Name    username = PG_GETARG_NAME(0);
3428
0
  text     *functionname = PG_GETARG_TEXT_PP(1);
3429
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
3430
0
  Oid     roleid;
3431
0
  Oid     functionoid;
3432
0
  AclMode   mode;
3433
0
  AclResult aclresult;
3434
3435
0
  roleid = get_role_oid_or_public(NameStr(*username));
3436
0
  functionoid = convert_function_name(functionname);
3437
0
  mode = convert_function_priv_string(priv_type_text);
3438
3439
0
  aclresult = object_aclcheck(ProcedureRelationId, functionoid, roleid, mode);
3440
3441
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
3442
0
}
3443
3444
/*
3445
 * has_function_privilege_name
3446
 *    Check user privileges on a function given
3447
 *    text functionname and text priv name.
3448
 *    current_user is assumed
3449
 */
3450
Datum
3451
has_function_privilege_name(PG_FUNCTION_ARGS)
3452
0
{
3453
0
  text     *functionname = PG_GETARG_TEXT_PP(0);
3454
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(1);
3455
0
  Oid     roleid;
3456
0
  Oid     functionoid;
3457
0
  AclMode   mode;
3458
0
  AclResult aclresult;
3459
3460
0
  roleid = GetUserId();
3461
0
  functionoid = convert_function_name(functionname);
3462
0
  mode = convert_function_priv_string(priv_type_text);
3463
3464
0
  aclresult = object_aclcheck(ProcedureRelationId, functionoid, roleid, mode);
3465
3466
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
3467
0
}
3468
3469
/*
3470
 * has_function_privilege_name_id
3471
 *    Check user privileges on a function given
3472
 *    name usename, function oid, and text priv name.
3473
 */
3474
Datum
3475
has_function_privilege_name_id(PG_FUNCTION_ARGS)
3476
0
{
3477
0
  Name    username = PG_GETARG_NAME(0);
3478
0
  Oid     functionoid = PG_GETARG_OID(1);
3479
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
3480
0
  Oid     roleid;
3481
0
  AclMode   mode;
3482
0
  AclResult aclresult;
3483
0
  bool    is_missing = false;
3484
3485
0
  roleid = get_role_oid_or_public(NameStr(*username));
3486
0
  mode = convert_function_priv_string(priv_type_text);
3487
3488
0
  aclresult = object_aclcheck_ext(ProcedureRelationId, functionoid,
3489
0
                  roleid, mode,
3490
0
                  &is_missing);
3491
3492
0
  if (is_missing)
3493
0
    PG_RETURN_NULL();
3494
3495
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
3496
0
}
3497
3498
/*
3499
 * has_function_privilege_id
3500
 *    Check user privileges on a function given
3501
 *    function oid, and text priv name.
3502
 *    current_user is assumed
3503
 */
3504
Datum
3505
has_function_privilege_id(PG_FUNCTION_ARGS)
3506
0
{
3507
0
  Oid     functionoid = PG_GETARG_OID(0);
3508
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(1);
3509
0
  Oid     roleid;
3510
0
  AclMode   mode;
3511
0
  AclResult aclresult;
3512
0
  bool    is_missing = false;
3513
3514
0
  roleid = GetUserId();
3515
0
  mode = convert_function_priv_string(priv_type_text);
3516
3517
0
  aclresult = object_aclcheck_ext(ProcedureRelationId, functionoid,
3518
0
                  roleid, mode,
3519
0
                  &is_missing);
3520
3521
0
  if (is_missing)
3522
0
    PG_RETURN_NULL();
3523
3524
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
3525
0
}
3526
3527
/*
3528
 * has_function_privilege_id_name
3529
 *    Check user privileges on a function given
3530
 *    roleid, text functionname, and text priv name.
3531
 */
3532
Datum
3533
has_function_privilege_id_name(PG_FUNCTION_ARGS)
3534
0
{
3535
0
  Oid     roleid = PG_GETARG_OID(0);
3536
0
  text     *functionname = PG_GETARG_TEXT_PP(1);
3537
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
3538
0
  Oid     functionoid;
3539
0
  AclMode   mode;
3540
0
  AclResult aclresult;
3541
3542
0
  functionoid = convert_function_name(functionname);
3543
0
  mode = convert_function_priv_string(priv_type_text);
3544
3545
0
  aclresult = object_aclcheck(ProcedureRelationId, functionoid, roleid, mode);
3546
3547
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
3548
0
}
3549
3550
/*
3551
 * has_function_privilege_id_id
3552
 *    Check user privileges on a function given
3553
 *    roleid, function oid, and text priv name.
3554
 */
3555
Datum
3556
has_function_privilege_id_id(PG_FUNCTION_ARGS)
3557
0
{
3558
0
  Oid     roleid = PG_GETARG_OID(0);
3559
0
  Oid     functionoid = PG_GETARG_OID(1);
3560
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
3561
0
  AclMode   mode;
3562
0
  AclResult aclresult;
3563
0
  bool    is_missing = false;
3564
3565
0
  mode = convert_function_priv_string(priv_type_text);
3566
3567
0
  aclresult = object_aclcheck_ext(ProcedureRelationId, functionoid,
3568
0
                  roleid, mode,
3569
0
                  &is_missing);
3570
3571
0
  if (is_missing)
3572
0
    PG_RETURN_NULL();
3573
3574
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
3575
0
}
3576
3577
/*
3578
 *    Support routines for has_function_privilege family.
3579
 */
3580
3581
/*
3582
 * Given a function name expressed as a string, look it up and return Oid
3583
 */
3584
static Oid
3585
convert_function_name(text *functionname)
3586
0
{
3587
0
  char     *funcname = text_to_cstring(functionname);
3588
0
  Oid     oid;
3589
3590
0
  oid = DatumGetObjectId(DirectFunctionCall1(regprocedurein,
3591
0
                         CStringGetDatum(funcname)));
3592
3593
0
  if (!OidIsValid(oid))
3594
0
    ereport(ERROR,
3595
0
        (errcode(ERRCODE_UNDEFINED_FUNCTION),
3596
0
         errmsg("function \"%s\" does not exist", funcname)));
3597
3598
0
  return oid;
3599
0
}
3600
3601
/*
3602
 * convert_function_priv_string
3603
 *    Convert text string to AclMode value.
3604
 */
3605
static AclMode
3606
convert_function_priv_string(text *priv_type_text)
3607
0
{
3608
0
  static const priv_map function_priv_map[] = {
3609
0
    {"EXECUTE", ACL_EXECUTE},
3610
0
    {"EXECUTE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_EXECUTE)},
3611
0
    {NULL, 0}
3612
0
  };
3613
3614
0
  return convert_any_priv_string(priv_type_text, function_priv_map);
3615
0
}
3616
3617
3618
/*
3619
 * has_language_privilege variants
3620
 *    These are all named "has_language_privilege" at the SQL level.
3621
 *    They take various combinations of language name, language OID,
3622
 *    user name, user OID, or implicit user = current_user.
3623
 *
3624
 *    The result is a boolean value: true if user has the indicated
3625
 *    privilege, false if not, or NULL if object doesn't exist.
3626
 */
3627
3628
/*
3629
 * has_language_privilege_name_name
3630
 *    Check user privileges on a language given
3631
 *    name username, text languagename, and text priv name.
3632
 */
3633
Datum
3634
has_language_privilege_name_name(PG_FUNCTION_ARGS)
3635
0
{
3636
0
  Name    username = PG_GETARG_NAME(0);
3637
0
  text     *languagename = PG_GETARG_TEXT_PP(1);
3638
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
3639
0
  Oid     roleid;
3640
0
  Oid     languageoid;
3641
0
  AclMode   mode;
3642
0
  AclResult aclresult;
3643
3644
0
  roleid = get_role_oid_or_public(NameStr(*username));
3645
0
  languageoid = convert_language_name(languagename);
3646
0
  mode = convert_language_priv_string(priv_type_text);
3647
3648
0
  aclresult = object_aclcheck(LanguageRelationId, languageoid, roleid, mode);
3649
3650
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
3651
0
}
3652
3653
/*
3654
 * has_language_privilege_name
3655
 *    Check user privileges on a language given
3656
 *    text languagename and text priv name.
3657
 *    current_user is assumed
3658
 */
3659
Datum
3660
has_language_privilege_name(PG_FUNCTION_ARGS)
3661
0
{
3662
0
  text     *languagename = PG_GETARG_TEXT_PP(0);
3663
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(1);
3664
0
  Oid     roleid;
3665
0
  Oid     languageoid;
3666
0
  AclMode   mode;
3667
0
  AclResult aclresult;
3668
3669
0
  roleid = GetUserId();
3670
0
  languageoid = convert_language_name(languagename);
3671
0
  mode = convert_language_priv_string(priv_type_text);
3672
3673
0
  aclresult = object_aclcheck(LanguageRelationId, languageoid, roleid, mode);
3674
3675
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
3676
0
}
3677
3678
/*
3679
 * has_language_privilege_name_id
3680
 *    Check user privileges on a language given
3681
 *    name usename, language oid, and text priv name.
3682
 */
3683
Datum
3684
has_language_privilege_name_id(PG_FUNCTION_ARGS)
3685
0
{
3686
0
  Name    username = PG_GETARG_NAME(0);
3687
0
  Oid     languageoid = PG_GETARG_OID(1);
3688
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
3689
0
  Oid     roleid;
3690
0
  AclMode   mode;
3691
0
  AclResult aclresult;
3692
0
  bool    is_missing = false;
3693
3694
0
  roleid = get_role_oid_or_public(NameStr(*username));
3695
0
  mode = convert_language_priv_string(priv_type_text);
3696
3697
0
  aclresult = object_aclcheck_ext(LanguageRelationId, languageoid,
3698
0
                  roleid, mode,
3699
0
                  &is_missing);
3700
3701
0
  if (is_missing)
3702
0
    PG_RETURN_NULL();
3703
3704
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
3705
0
}
3706
3707
/*
3708
 * has_language_privilege_id
3709
 *    Check user privileges on a language given
3710
 *    language oid, and text priv name.
3711
 *    current_user is assumed
3712
 */
3713
Datum
3714
has_language_privilege_id(PG_FUNCTION_ARGS)
3715
0
{
3716
0
  Oid     languageoid = PG_GETARG_OID(0);
3717
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(1);
3718
0
  Oid     roleid;
3719
0
  AclMode   mode;
3720
0
  AclResult aclresult;
3721
0
  bool    is_missing = false;
3722
3723
0
  roleid = GetUserId();
3724
0
  mode = convert_language_priv_string(priv_type_text);
3725
3726
0
  aclresult = object_aclcheck_ext(LanguageRelationId, languageoid,
3727
0
                  roleid, mode,
3728
0
                  &is_missing);
3729
3730
0
  if (is_missing)
3731
0
    PG_RETURN_NULL();
3732
3733
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
3734
0
}
3735
3736
/*
3737
 * has_language_privilege_id_name
3738
 *    Check user privileges on a language given
3739
 *    roleid, text languagename, and text priv name.
3740
 */
3741
Datum
3742
has_language_privilege_id_name(PG_FUNCTION_ARGS)
3743
0
{
3744
0
  Oid     roleid = PG_GETARG_OID(0);
3745
0
  text     *languagename = PG_GETARG_TEXT_PP(1);
3746
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
3747
0
  Oid     languageoid;
3748
0
  AclMode   mode;
3749
0
  AclResult aclresult;
3750
3751
0
  languageoid = convert_language_name(languagename);
3752
0
  mode = convert_language_priv_string(priv_type_text);
3753
3754
0
  aclresult = object_aclcheck(LanguageRelationId, languageoid, roleid, mode);
3755
3756
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
3757
0
}
3758
3759
/*
3760
 * has_language_privilege_id_id
3761
 *    Check user privileges on a language given
3762
 *    roleid, language oid, and text priv name.
3763
 */
3764
Datum
3765
has_language_privilege_id_id(PG_FUNCTION_ARGS)
3766
0
{
3767
0
  Oid     roleid = PG_GETARG_OID(0);
3768
0
  Oid     languageoid = PG_GETARG_OID(1);
3769
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
3770
0
  AclMode   mode;
3771
0
  AclResult aclresult;
3772
0
  bool    is_missing = false;
3773
3774
0
  mode = convert_language_priv_string(priv_type_text);
3775
3776
0
  aclresult = object_aclcheck_ext(LanguageRelationId, languageoid,
3777
0
                  roleid, mode,
3778
0
                  &is_missing);
3779
3780
0
  if (is_missing)
3781
0
    PG_RETURN_NULL();
3782
3783
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
3784
0
}
3785
3786
/*
3787
 *    Support routines for has_language_privilege family.
3788
 */
3789
3790
/*
3791
 * Given a language name expressed as a string, look it up and return Oid
3792
 */
3793
static Oid
3794
convert_language_name(text *languagename)
3795
0
{
3796
0
  char     *langname = text_to_cstring(languagename);
3797
3798
0
  return get_language_oid(langname, false);
3799
0
}
3800
3801
/*
3802
 * convert_language_priv_string
3803
 *    Convert text string to AclMode value.
3804
 */
3805
static AclMode
3806
convert_language_priv_string(text *priv_type_text)
3807
0
{
3808
0
  static const priv_map language_priv_map[] = {
3809
0
    {"USAGE", ACL_USAGE},
3810
0
    {"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
3811
0
    {NULL, 0}
3812
0
  };
3813
3814
0
  return convert_any_priv_string(priv_type_text, language_priv_map);
3815
0
}
3816
3817
3818
/*
3819
 * has_schema_privilege variants
3820
 *    These are all named "has_schema_privilege" at the SQL level.
3821
 *    They take various combinations of schema name, schema OID,
3822
 *    user name, user OID, or implicit user = current_user.
3823
 *
3824
 *    The result is a boolean value: true if user has the indicated
3825
 *    privilege, false if not, or NULL if object doesn't exist.
3826
 */
3827
3828
/*
3829
 * has_schema_privilege_name_name
3830
 *    Check user privileges on a schema given
3831
 *    name username, text schemaname, and text priv name.
3832
 */
3833
Datum
3834
has_schema_privilege_name_name(PG_FUNCTION_ARGS)
3835
0
{
3836
0
  Name    username = PG_GETARG_NAME(0);
3837
0
  text     *schemaname = PG_GETARG_TEXT_PP(1);
3838
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
3839
0
  Oid     roleid;
3840
0
  Oid     schemaoid;
3841
0
  AclMode   mode;
3842
0
  AclResult aclresult;
3843
3844
0
  roleid = get_role_oid_or_public(NameStr(*username));
3845
0
  schemaoid = convert_schema_name(schemaname);
3846
0
  mode = convert_schema_priv_string(priv_type_text);
3847
3848
0
  aclresult = object_aclcheck(NamespaceRelationId, schemaoid, roleid, mode);
3849
3850
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
3851
0
}
3852
3853
/*
3854
 * has_schema_privilege_name
3855
 *    Check user privileges on a schema given
3856
 *    text schemaname and text priv name.
3857
 *    current_user is assumed
3858
 */
3859
Datum
3860
has_schema_privilege_name(PG_FUNCTION_ARGS)
3861
0
{
3862
0
  text     *schemaname = PG_GETARG_TEXT_PP(0);
3863
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(1);
3864
0
  Oid     roleid;
3865
0
  Oid     schemaoid;
3866
0
  AclMode   mode;
3867
0
  AclResult aclresult;
3868
3869
0
  roleid = GetUserId();
3870
0
  schemaoid = convert_schema_name(schemaname);
3871
0
  mode = convert_schema_priv_string(priv_type_text);
3872
3873
0
  aclresult = object_aclcheck(NamespaceRelationId, schemaoid, roleid, mode);
3874
3875
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
3876
0
}
3877
3878
/*
3879
 * has_schema_privilege_name_id
3880
 *    Check user privileges on a schema given
3881
 *    name usename, schema oid, and text priv name.
3882
 */
3883
Datum
3884
has_schema_privilege_name_id(PG_FUNCTION_ARGS)
3885
0
{
3886
0
  Name    username = PG_GETARG_NAME(0);
3887
0
  Oid     schemaoid = PG_GETARG_OID(1);
3888
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
3889
0
  Oid     roleid;
3890
0
  AclMode   mode;
3891
0
  AclResult aclresult;
3892
0
  bool    is_missing = false;
3893
3894
0
  roleid = get_role_oid_or_public(NameStr(*username));
3895
0
  mode = convert_schema_priv_string(priv_type_text);
3896
3897
0
  aclresult = object_aclcheck_ext(NamespaceRelationId, schemaoid,
3898
0
                  roleid, mode,
3899
0
                  &is_missing);
3900
3901
0
  if (is_missing)
3902
0
    PG_RETURN_NULL();
3903
3904
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
3905
0
}
3906
3907
/*
3908
 * has_schema_privilege_id
3909
 *    Check user privileges on a schema given
3910
 *    schema oid, and text priv name.
3911
 *    current_user is assumed
3912
 */
3913
Datum
3914
has_schema_privilege_id(PG_FUNCTION_ARGS)
3915
0
{
3916
0
  Oid     schemaoid = PG_GETARG_OID(0);
3917
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(1);
3918
0
  Oid     roleid;
3919
0
  AclMode   mode;
3920
0
  AclResult aclresult;
3921
0
  bool    is_missing = false;
3922
3923
0
  roleid = GetUserId();
3924
0
  mode = convert_schema_priv_string(priv_type_text);
3925
3926
0
  aclresult = object_aclcheck_ext(NamespaceRelationId, schemaoid,
3927
0
                  roleid, mode,
3928
0
                  &is_missing);
3929
3930
0
  if (is_missing)
3931
0
    PG_RETURN_NULL();
3932
3933
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
3934
0
}
3935
3936
/*
3937
 * has_schema_privilege_id_name
3938
 *    Check user privileges on a schema given
3939
 *    roleid, text schemaname, and text priv name.
3940
 */
3941
Datum
3942
has_schema_privilege_id_name(PG_FUNCTION_ARGS)
3943
0
{
3944
0
  Oid     roleid = PG_GETARG_OID(0);
3945
0
  text     *schemaname = PG_GETARG_TEXT_PP(1);
3946
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
3947
0
  Oid     schemaoid;
3948
0
  AclMode   mode;
3949
0
  AclResult aclresult;
3950
3951
0
  schemaoid = convert_schema_name(schemaname);
3952
0
  mode = convert_schema_priv_string(priv_type_text);
3953
3954
0
  aclresult = object_aclcheck(NamespaceRelationId, schemaoid, roleid, mode);
3955
3956
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
3957
0
}
3958
3959
/*
3960
 * has_schema_privilege_id_id
3961
 *    Check user privileges on a schema given
3962
 *    roleid, schema oid, and text priv name.
3963
 */
3964
Datum
3965
has_schema_privilege_id_id(PG_FUNCTION_ARGS)
3966
0
{
3967
0
  Oid     roleid = PG_GETARG_OID(0);
3968
0
  Oid     schemaoid = PG_GETARG_OID(1);
3969
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
3970
0
  AclMode   mode;
3971
0
  AclResult aclresult;
3972
0
  bool    is_missing = false;
3973
3974
0
  mode = convert_schema_priv_string(priv_type_text);
3975
3976
0
  aclresult = object_aclcheck_ext(NamespaceRelationId, schemaoid,
3977
0
                  roleid, mode,
3978
0
                  &is_missing);
3979
3980
0
  if (is_missing)
3981
0
    PG_RETURN_NULL();
3982
3983
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
3984
0
}
3985
3986
/*
3987
 *    Support routines for has_schema_privilege family.
3988
 */
3989
3990
/*
3991
 * Given a schema name expressed as a string, look it up and return Oid
3992
 */
3993
static Oid
3994
convert_schema_name(text *schemaname)
3995
0
{
3996
0
  char     *nspname = text_to_cstring(schemaname);
3997
3998
0
  return get_namespace_oid(nspname, false);
3999
0
}
4000
4001
/*
4002
 * convert_schema_priv_string
4003
 *    Convert text string to AclMode value.
4004
 */
4005
static AclMode
4006
convert_schema_priv_string(text *priv_type_text)
4007
0
{
4008
0
  static const priv_map schema_priv_map[] = {
4009
0
    {"CREATE", ACL_CREATE},
4010
0
    {"CREATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
4011
0
    {"USAGE", ACL_USAGE},
4012
0
    {"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
4013
0
    {NULL, 0}
4014
0
  };
4015
4016
0
  return convert_any_priv_string(priv_type_text, schema_priv_map);
4017
0
}
4018
4019
4020
/*
4021
 * has_server_privilege variants
4022
 *    These are all named "has_server_privilege" at the SQL level.
4023
 *    They take various combinations of foreign server name,
4024
 *    server OID, user name, user OID, or implicit user = current_user.
4025
 *
4026
 *    The result is a boolean value: true if user has the indicated
4027
 *    privilege, false if not.
4028
 */
4029
4030
/*
4031
 * has_server_privilege_name_name
4032
 *    Check user privileges on a foreign server given
4033
 *    name username, text servername, and text priv name.
4034
 */
4035
Datum
4036
has_server_privilege_name_name(PG_FUNCTION_ARGS)
4037
0
{
4038
0
  Name    username = PG_GETARG_NAME(0);
4039
0
  text     *servername = PG_GETARG_TEXT_PP(1);
4040
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
4041
0
  Oid     roleid;
4042
0
  Oid     serverid;
4043
0
  AclMode   mode;
4044
0
  AclResult aclresult;
4045
4046
0
  roleid = get_role_oid_or_public(NameStr(*username));
4047
0
  serverid = convert_server_name(servername);
4048
0
  mode = convert_server_priv_string(priv_type_text);
4049
4050
0
  aclresult = object_aclcheck(ForeignServerRelationId, serverid, roleid, mode);
4051
4052
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
4053
0
}
4054
4055
/*
4056
 * has_server_privilege_name
4057
 *    Check user privileges on a foreign server given
4058
 *    text servername and text priv name.
4059
 *    current_user is assumed
4060
 */
4061
Datum
4062
has_server_privilege_name(PG_FUNCTION_ARGS)
4063
0
{
4064
0
  text     *servername = PG_GETARG_TEXT_PP(0);
4065
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(1);
4066
0
  Oid     roleid;
4067
0
  Oid     serverid;
4068
0
  AclMode   mode;
4069
0
  AclResult aclresult;
4070
4071
0
  roleid = GetUserId();
4072
0
  serverid = convert_server_name(servername);
4073
0
  mode = convert_server_priv_string(priv_type_text);
4074
4075
0
  aclresult = object_aclcheck(ForeignServerRelationId, serverid, roleid, mode);
4076
4077
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
4078
0
}
4079
4080
/*
4081
 * has_server_privilege_name_id
4082
 *    Check user privileges on a foreign server given
4083
 *    name usename, foreign server oid, and text priv name.
4084
 */
4085
Datum
4086
has_server_privilege_name_id(PG_FUNCTION_ARGS)
4087
0
{
4088
0
  Name    username = PG_GETARG_NAME(0);
4089
0
  Oid     serverid = PG_GETARG_OID(1);
4090
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
4091
0
  Oid     roleid;
4092
0
  AclMode   mode;
4093
0
  AclResult aclresult;
4094
0
  bool    is_missing = false;
4095
4096
0
  roleid = get_role_oid_or_public(NameStr(*username));
4097
0
  mode = convert_server_priv_string(priv_type_text);
4098
4099
0
  aclresult = object_aclcheck_ext(ForeignServerRelationId, serverid,
4100
0
                  roleid, mode,
4101
0
                  &is_missing);
4102
4103
0
  if (is_missing)
4104
0
    PG_RETURN_NULL();
4105
4106
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
4107
0
}
4108
4109
/*
4110
 * has_server_privilege_id
4111
 *    Check user privileges on a foreign server given
4112
 *    server oid, and text priv name.
4113
 *    current_user is assumed
4114
 */
4115
Datum
4116
has_server_privilege_id(PG_FUNCTION_ARGS)
4117
0
{
4118
0
  Oid     serverid = PG_GETARG_OID(0);
4119
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(1);
4120
0
  Oid     roleid;
4121
0
  AclMode   mode;
4122
0
  AclResult aclresult;
4123
0
  bool    is_missing = false;
4124
4125
0
  roleid = GetUserId();
4126
0
  mode = convert_server_priv_string(priv_type_text);
4127
4128
0
  aclresult = object_aclcheck_ext(ForeignServerRelationId, serverid,
4129
0
                  roleid, mode,
4130
0
                  &is_missing);
4131
4132
0
  if (is_missing)
4133
0
    PG_RETURN_NULL();
4134
4135
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
4136
0
}
4137
4138
/*
4139
 * has_server_privilege_id_name
4140
 *    Check user privileges on a foreign server given
4141
 *    roleid, text servername, and text priv name.
4142
 */
4143
Datum
4144
has_server_privilege_id_name(PG_FUNCTION_ARGS)
4145
0
{
4146
0
  Oid     roleid = PG_GETARG_OID(0);
4147
0
  text     *servername = PG_GETARG_TEXT_PP(1);
4148
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
4149
0
  Oid     serverid;
4150
0
  AclMode   mode;
4151
0
  AclResult aclresult;
4152
4153
0
  serverid = convert_server_name(servername);
4154
0
  mode = convert_server_priv_string(priv_type_text);
4155
4156
0
  aclresult = object_aclcheck(ForeignServerRelationId, serverid, roleid, mode);
4157
4158
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
4159
0
}
4160
4161
/*
4162
 * has_server_privilege_id_id
4163
 *    Check user privileges on a foreign server given
4164
 *    roleid, server oid, and text priv name.
4165
 */
4166
Datum
4167
has_server_privilege_id_id(PG_FUNCTION_ARGS)
4168
0
{
4169
0
  Oid     roleid = PG_GETARG_OID(0);
4170
0
  Oid     serverid = PG_GETARG_OID(1);
4171
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
4172
0
  AclMode   mode;
4173
0
  AclResult aclresult;
4174
0
  bool    is_missing = false;
4175
4176
0
  mode = convert_server_priv_string(priv_type_text);
4177
4178
0
  aclresult = object_aclcheck_ext(ForeignServerRelationId, serverid,
4179
0
                  roleid, mode,
4180
0
                  &is_missing);
4181
4182
0
  if (is_missing)
4183
0
    PG_RETURN_NULL();
4184
4185
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
4186
0
}
4187
4188
/*
4189
 *    Support routines for has_server_privilege family.
4190
 */
4191
4192
/*
4193
 * Given a server name expressed as a string, look it up and return Oid
4194
 */
4195
static Oid
4196
convert_server_name(text *servername)
4197
0
{
4198
0
  char     *serverstr = text_to_cstring(servername);
4199
4200
0
  return get_foreign_server_oid(serverstr, false);
4201
0
}
4202
4203
/*
4204
 * convert_server_priv_string
4205
 *    Convert text string to AclMode value.
4206
 */
4207
static AclMode
4208
convert_server_priv_string(text *priv_type_text)
4209
0
{
4210
0
  static const priv_map server_priv_map[] = {
4211
0
    {"USAGE", ACL_USAGE},
4212
0
    {"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
4213
0
    {NULL, 0}
4214
0
  };
4215
4216
0
  return convert_any_priv_string(priv_type_text, server_priv_map);
4217
0
}
4218
4219
4220
/*
4221
 * has_tablespace_privilege variants
4222
 *    These are all named "has_tablespace_privilege" at the SQL level.
4223
 *    They take various combinations of tablespace name, tablespace OID,
4224
 *    user name, user OID, or implicit user = current_user.
4225
 *
4226
 *    The result is a boolean value: true if user has the indicated
4227
 *    privilege, false if not.
4228
 */
4229
4230
/*
4231
 * has_tablespace_privilege_name_name
4232
 *    Check user privileges on a tablespace given
4233
 *    name username, text tablespacename, and text priv name.
4234
 */
4235
Datum
4236
has_tablespace_privilege_name_name(PG_FUNCTION_ARGS)
4237
0
{
4238
0
  Name    username = PG_GETARG_NAME(0);
4239
0
  text     *tablespacename = PG_GETARG_TEXT_PP(1);
4240
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
4241
0
  Oid     roleid;
4242
0
  Oid     tablespaceoid;
4243
0
  AclMode   mode;
4244
0
  AclResult aclresult;
4245
4246
0
  roleid = get_role_oid_or_public(NameStr(*username));
4247
0
  tablespaceoid = convert_tablespace_name(tablespacename);
4248
0
  mode = convert_tablespace_priv_string(priv_type_text);
4249
4250
0
  aclresult = object_aclcheck(TableSpaceRelationId, tablespaceoid, roleid, mode);
4251
4252
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
4253
0
}
4254
4255
/*
4256
 * has_tablespace_privilege_name
4257
 *    Check user privileges on a tablespace given
4258
 *    text tablespacename and text priv name.
4259
 *    current_user is assumed
4260
 */
4261
Datum
4262
has_tablespace_privilege_name(PG_FUNCTION_ARGS)
4263
0
{
4264
0
  text     *tablespacename = PG_GETARG_TEXT_PP(0);
4265
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(1);
4266
0
  Oid     roleid;
4267
0
  Oid     tablespaceoid;
4268
0
  AclMode   mode;
4269
0
  AclResult aclresult;
4270
4271
0
  roleid = GetUserId();
4272
0
  tablespaceoid = convert_tablespace_name(tablespacename);
4273
0
  mode = convert_tablespace_priv_string(priv_type_text);
4274
4275
0
  aclresult = object_aclcheck(TableSpaceRelationId, tablespaceoid, roleid, mode);
4276
4277
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
4278
0
}
4279
4280
/*
4281
 * has_tablespace_privilege_name_id
4282
 *    Check user privileges on a tablespace given
4283
 *    name usename, tablespace oid, and text priv name.
4284
 */
4285
Datum
4286
has_tablespace_privilege_name_id(PG_FUNCTION_ARGS)
4287
0
{
4288
0
  Name    username = PG_GETARG_NAME(0);
4289
0
  Oid     tablespaceoid = PG_GETARG_OID(1);
4290
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
4291
0
  Oid     roleid;
4292
0
  AclMode   mode;
4293
0
  AclResult aclresult;
4294
0
  bool    is_missing = false;
4295
4296
0
  roleid = get_role_oid_or_public(NameStr(*username));
4297
0
  mode = convert_tablespace_priv_string(priv_type_text);
4298
4299
0
  aclresult = object_aclcheck_ext(TableSpaceRelationId, tablespaceoid,
4300
0
                  roleid, mode,
4301
0
                  &is_missing);
4302
4303
0
  if (is_missing)
4304
0
    PG_RETURN_NULL();
4305
4306
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
4307
0
}
4308
4309
/*
4310
 * has_tablespace_privilege_id
4311
 *    Check user privileges on a tablespace given
4312
 *    tablespace oid, and text priv name.
4313
 *    current_user is assumed
4314
 */
4315
Datum
4316
has_tablespace_privilege_id(PG_FUNCTION_ARGS)
4317
0
{
4318
0
  Oid     tablespaceoid = PG_GETARG_OID(0);
4319
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(1);
4320
0
  Oid     roleid;
4321
0
  AclMode   mode;
4322
0
  AclResult aclresult;
4323
0
  bool    is_missing = false;
4324
4325
0
  roleid = GetUserId();
4326
0
  mode = convert_tablespace_priv_string(priv_type_text);
4327
4328
0
  aclresult = object_aclcheck_ext(TableSpaceRelationId, tablespaceoid,
4329
0
                  roleid, mode,
4330
0
                  &is_missing);
4331
4332
0
  if (is_missing)
4333
0
    PG_RETURN_NULL();
4334
4335
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
4336
0
}
4337
4338
/*
4339
 * has_tablespace_privilege_id_name
4340
 *    Check user privileges on a tablespace given
4341
 *    roleid, text tablespacename, and text priv name.
4342
 */
4343
Datum
4344
has_tablespace_privilege_id_name(PG_FUNCTION_ARGS)
4345
0
{
4346
0
  Oid     roleid = PG_GETARG_OID(0);
4347
0
  text     *tablespacename = PG_GETARG_TEXT_PP(1);
4348
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
4349
0
  Oid     tablespaceoid;
4350
0
  AclMode   mode;
4351
0
  AclResult aclresult;
4352
4353
0
  tablespaceoid = convert_tablespace_name(tablespacename);
4354
0
  mode = convert_tablespace_priv_string(priv_type_text);
4355
4356
0
  aclresult = object_aclcheck(TableSpaceRelationId, tablespaceoid, roleid, mode);
4357
4358
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
4359
0
}
4360
4361
/*
4362
 * has_tablespace_privilege_id_id
4363
 *    Check user privileges on a tablespace given
4364
 *    roleid, tablespace oid, and text priv name.
4365
 */
4366
Datum
4367
has_tablespace_privilege_id_id(PG_FUNCTION_ARGS)
4368
0
{
4369
0
  Oid     roleid = PG_GETARG_OID(0);
4370
0
  Oid     tablespaceoid = PG_GETARG_OID(1);
4371
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
4372
0
  AclMode   mode;
4373
0
  AclResult aclresult;
4374
0
  bool    is_missing = false;
4375
4376
0
  mode = convert_tablespace_priv_string(priv_type_text);
4377
4378
0
  aclresult = object_aclcheck_ext(TableSpaceRelationId, tablespaceoid,
4379
0
                  roleid, mode,
4380
0
                  &is_missing);
4381
4382
0
  if (is_missing)
4383
0
    PG_RETURN_NULL();
4384
4385
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
4386
0
}
4387
4388
/*
4389
 *    Support routines for has_tablespace_privilege family.
4390
 */
4391
4392
/*
4393
 * Given a tablespace name expressed as a string, look it up and return Oid
4394
 */
4395
static Oid
4396
convert_tablespace_name(text *tablespacename)
4397
0
{
4398
0
  char     *spcname = text_to_cstring(tablespacename);
4399
4400
0
  return get_tablespace_oid(spcname, false);
4401
0
}
4402
4403
/*
4404
 * convert_tablespace_priv_string
4405
 *    Convert text string to AclMode value.
4406
 */
4407
static AclMode
4408
convert_tablespace_priv_string(text *priv_type_text)
4409
0
{
4410
0
  static const priv_map tablespace_priv_map[] = {
4411
0
    {"CREATE", ACL_CREATE},
4412
0
    {"CREATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
4413
0
    {NULL, 0}
4414
0
  };
4415
4416
0
  return convert_any_priv_string(priv_type_text, tablespace_priv_map);
4417
0
}
4418
4419
/*
4420
 * has_type_privilege variants
4421
 *    These are all named "has_type_privilege" at the SQL level.
4422
 *    They take various combinations of type name, type OID,
4423
 *    user name, user OID, or implicit user = current_user.
4424
 *
4425
 *    The result is a boolean value: true if user has the indicated
4426
 *    privilege, false if not, or NULL if object doesn't exist.
4427
 */
4428
4429
/*
4430
 * has_type_privilege_name_name
4431
 *    Check user privileges on a type given
4432
 *    name username, text typename, and text priv name.
4433
 */
4434
Datum
4435
has_type_privilege_name_name(PG_FUNCTION_ARGS)
4436
0
{
4437
0
  Name    username = PG_GETARG_NAME(0);
4438
0
  text     *typename = PG_GETARG_TEXT_PP(1);
4439
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
4440
0
  Oid     roleid;
4441
0
  Oid     typeoid;
4442
0
  AclMode   mode;
4443
0
  AclResult aclresult;
4444
4445
0
  roleid = get_role_oid_or_public(NameStr(*username));
4446
0
  typeoid = convert_type_name(typename);
4447
0
  mode = convert_type_priv_string(priv_type_text);
4448
4449
0
  aclresult = object_aclcheck(TypeRelationId, typeoid, roleid, mode);
4450
4451
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
4452
0
}
4453
4454
/*
4455
 * has_type_privilege_name
4456
 *    Check user privileges on a type given
4457
 *    text typename and text priv name.
4458
 *    current_user is assumed
4459
 */
4460
Datum
4461
has_type_privilege_name(PG_FUNCTION_ARGS)
4462
0
{
4463
0
  text     *typename = PG_GETARG_TEXT_PP(0);
4464
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(1);
4465
0
  Oid     roleid;
4466
0
  Oid     typeoid;
4467
0
  AclMode   mode;
4468
0
  AclResult aclresult;
4469
4470
0
  roleid = GetUserId();
4471
0
  typeoid = convert_type_name(typename);
4472
0
  mode = convert_type_priv_string(priv_type_text);
4473
4474
0
  aclresult = object_aclcheck(TypeRelationId, typeoid, roleid, mode);
4475
4476
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
4477
0
}
4478
4479
/*
4480
 * has_type_privilege_name_id
4481
 *    Check user privileges on a type given
4482
 *    name usename, type oid, and text priv name.
4483
 */
4484
Datum
4485
has_type_privilege_name_id(PG_FUNCTION_ARGS)
4486
0
{
4487
0
  Name    username = PG_GETARG_NAME(0);
4488
0
  Oid     typeoid = PG_GETARG_OID(1);
4489
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
4490
0
  Oid     roleid;
4491
0
  AclMode   mode;
4492
0
  AclResult aclresult;
4493
0
  bool    is_missing = false;
4494
4495
0
  roleid = get_role_oid_or_public(NameStr(*username));
4496
0
  mode = convert_type_priv_string(priv_type_text);
4497
4498
0
  aclresult = object_aclcheck_ext(TypeRelationId, typeoid,
4499
0
                  roleid, mode,
4500
0
                  &is_missing);
4501
4502
0
  if (is_missing)
4503
0
    PG_RETURN_NULL();
4504
4505
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
4506
0
}
4507
4508
/*
4509
 * has_type_privilege_id
4510
 *    Check user privileges on a type given
4511
 *    type oid, and text priv name.
4512
 *    current_user is assumed
4513
 */
4514
Datum
4515
has_type_privilege_id(PG_FUNCTION_ARGS)
4516
0
{
4517
0
  Oid     typeoid = PG_GETARG_OID(0);
4518
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(1);
4519
0
  Oid     roleid;
4520
0
  AclMode   mode;
4521
0
  AclResult aclresult;
4522
0
  bool    is_missing = false;
4523
4524
0
  roleid = GetUserId();
4525
0
  mode = convert_type_priv_string(priv_type_text);
4526
4527
0
  aclresult = object_aclcheck_ext(TypeRelationId, typeoid,
4528
0
                  roleid, mode,
4529
0
                  &is_missing);
4530
4531
0
  if (is_missing)
4532
0
    PG_RETURN_NULL();
4533
4534
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
4535
0
}
4536
4537
/*
4538
 * has_type_privilege_id_name
4539
 *    Check user privileges on a type given
4540
 *    roleid, text typename, and text priv name.
4541
 */
4542
Datum
4543
has_type_privilege_id_name(PG_FUNCTION_ARGS)
4544
0
{
4545
0
  Oid     roleid = PG_GETARG_OID(0);
4546
0
  text     *typename = PG_GETARG_TEXT_PP(1);
4547
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
4548
0
  Oid     typeoid;
4549
0
  AclMode   mode;
4550
0
  AclResult aclresult;
4551
4552
0
  typeoid = convert_type_name(typename);
4553
0
  mode = convert_type_priv_string(priv_type_text);
4554
4555
0
  aclresult = object_aclcheck(TypeRelationId, typeoid, roleid, mode);
4556
4557
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
4558
0
}
4559
4560
/*
4561
 * has_type_privilege_id_id
4562
 *    Check user privileges on a type given
4563
 *    roleid, type oid, and text priv name.
4564
 */
4565
Datum
4566
has_type_privilege_id_id(PG_FUNCTION_ARGS)
4567
0
{
4568
0
  Oid     roleid = PG_GETARG_OID(0);
4569
0
  Oid     typeoid = PG_GETARG_OID(1);
4570
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
4571
0
  AclMode   mode;
4572
0
  AclResult aclresult;
4573
0
  bool    is_missing = false;
4574
4575
0
  mode = convert_type_priv_string(priv_type_text);
4576
4577
0
  aclresult = object_aclcheck_ext(TypeRelationId, typeoid,
4578
0
                  roleid, mode,
4579
0
                  &is_missing);
4580
4581
0
  if (is_missing)
4582
0
    PG_RETURN_NULL();
4583
4584
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
4585
0
}
4586
4587
/*
4588
 *    Support routines for has_type_privilege family.
4589
 */
4590
4591
/*
4592
 * Given a type name expressed as a string, look it up and return Oid
4593
 */
4594
static Oid
4595
convert_type_name(text *typename)
4596
0
{
4597
0
  char     *typname = text_to_cstring(typename);
4598
0
  Oid     oid;
4599
4600
0
  oid = DatumGetObjectId(DirectFunctionCall1(regtypein,
4601
0
                         CStringGetDatum(typname)));
4602
4603
0
  if (!OidIsValid(oid))
4604
0
    ereport(ERROR,
4605
0
        (errcode(ERRCODE_UNDEFINED_OBJECT),
4606
0
         errmsg("type \"%s\" does not exist", typname)));
4607
4608
0
  return oid;
4609
0
}
4610
4611
/*
4612
 * convert_type_priv_string
4613
 *    Convert text string to AclMode value.
4614
 */
4615
static AclMode
4616
convert_type_priv_string(text *priv_type_text)
4617
0
{
4618
0
  static const priv_map type_priv_map[] = {
4619
0
    {"USAGE", ACL_USAGE},
4620
0
    {"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
4621
0
    {NULL, 0}
4622
0
  };
4623
4624
0
  return convert_any_priv_string(priv_type_text, type_priv_map);
4625
0
}
4626
4627
/*
4628
 * has_parameter_privilege variants
4629
 *    These are all named "has_parameter_privilege" at the SQL level.
4630
 *    They take various combinations of parameter name with
4631
 *    user name, user OID, or implicit user = current_user.
4632
 *
4633
 *    The result is a boolean value: true if user has been granted
4634
 *    the indicated privilege or false if not.
4635
 */
4636
4637
/*
4638
 * has_param_priv_byname
4639
 *
4640
 *    Helper function to check user privileges on a parameter given the
4641
 *    role by Oid, parameter by text name, and privileges as AclMode.
4642
 */
4643
static bool
4644
has_param_priv_byname(Oid roleid, const text *parameter, AclMode priv)
4645
0
{
4646
0
  char     *paramstr = text_to_cstring(parameter);
4647
4648
0
  return pg_parameter_aclcheck(paramstr, roleid, priv) == ACLCHECK_OK;
4649
0
}
4650
4651
/*
4652
 * has_parameter_privilege_name_name
4653
 *    Check user privileges on a parameter given name username, text
4654
 *    parameter, and text priv name.
4655
 */
4656
Datum
4657
has_parameter_privilege_name_name(PG_FUNCTION_ARGS)
4658
0
{
4659
0
  Name    username = PG_GETARG_NAME(0);
4660
0
  text     *parameter = PG_GETARG_TEXT_PP(1);
4661
0
  AclMode   priv = convert_parameter_priv_string(PG_GETARG_TEXT_PP(2));
4662
0
  Oid     roleid = get_role_oid_or_public(NameStr(*username));
4663
4664
0
  PG_RETURN_BOOL(has_param_priv_byname(roleid, parameter, priv));
4665
0
}
4666
4667
/*
4668
 * has_parameter_privilege_name
4669
 *    Check user privileges on a parameter given text parameter and text priv
4670
 *    name.  current_user is assumed
4671
 */
4672
Datum
4673
has_parameter_privilege_name(PG_FUNCTION_ARGS)
4674
0
{
4675
0
  text     *parameter = PG_GETARG_TEXT_PP(0);
4676
0
  AclMode   priv = convert_parameter_priv_string(PG_GETARG_TEXT_PP(1));
4677
4678
0
  PG_RETURN_BOOL(has_param_priv_byname(GetUserId(), parameter, priv));
4679
0
}
4680
4681
/*
4682
 * has_parameter_privilege_id_name
4683
 *    Check user privileges on a parameter given roleid, text parameter, and
4684
 *    text priv name.
4685
 */
4686
Datum
4687
has_parameter_privilege_id_name(PG_FUNCTION_ARGS)
4688
0
{
4689
0
  Oid     roleid = PG_GETARG_OID(0);
4690
0
  text     *parameter = PG_GETARG_TEXT_PP(1);
4691
0
  AclMode   priv = convert_parameter_priv_string(PG_GETARG_TEXT_PP(2));
4692
4693
0
  PG_RETURN_BOOL(has_param_priv_byname(roleid, parameter, priv));
4694
0
}
4695
4696
/*
4697
 *    Support routines for has_parameter_privilege family.
4698
 */
4699
4700
/*
4701
 * convert_parameter_priv_string
4702
 *    Convert text string to AclMode value.
4703
 */
4704
static AclMode
4705
convert_parameter_priv_string(text *priv_text)
4706
0
{
4707
0
  static const priv_map parameter_priv_map[] = {
4708
0
    {"SET", ACL_SET},
4709
0
    {"SET WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_SET)},
4710
0
    {"ALTER SYSTEM", ACL_ALTER_SYSTEM},
4711
0
    {"ALTER SYSTEM WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_ALTER_SYSTEM)},
4712
0
    {NULL, 0}
4713
0
  };
4714
4715
0
  return convert_any_priv_string(priv_text, parameter_priv_map);
4716
0
}
4717
4718
/*
4719
 * has_largeobject_privilege variants
4720
 *    These are all named "has_largeobject_privilege" at the SQL level.
4721
 *    They take various combinations of large object OID with
4722
 *    user name, user OID, or implicit user = current_user.
4723
 *
4724
 *    The result is a boolean value: true if user has the indicated
4725
 *    privilege, false if not, or NULL if object doesn't exist.
4726
 */
4727
4728
/*
4729
 * has_lo_priv_byid
4730
 *
4731
 *    Helper function to check user privileges on a large object given the
4732
 *    role by Oid, large object by Oid, and privileges as AclMode.
4733
 */
4734
static bool
4735
has_lo_priv_byid(Oid roleid, Oid lobjId, AclMode priv, bool *is_missing)
4736
0
{
4737
0
  Snapshot  snapshot = NULL;
4738
0
  AclResult aclresult;
4739
4740
0
  if (priv & ACL_UPDATE)
4741
0
    snapshot = NULL;
4742
0
  else
4743
0
    snapshot = GetActiveSnapshot();
4744
4745
0
  if (!LargeObjectExistsWithSnapshot(lobjId, snapshot))
4746
0
  {
4747
0
    Assert(is_missing != NULL);
4748
0
    *is_missing = true;
4749
0
    return false;
4750
0
  }
4751
4752
0
  if (lo_compat_privileges)
4753
0
    return true;
4754
4755
0
  aclresult = pg_largeobject_aclcheck_snapshot(lobjId,
4756
0
                         roleid,
4757
0
                         priv,
4758
0
                         snapshot);
4759
0
  return aclresult == ACLCHECK_OK;
4760
0
}
4761
4762
/*
4763
 * has_largeobject_privilege_name_id
4764
 *    Check user privileges on a large object given
4765
 *    name username, large object oid, and text priv name.
4766
 */
4767
Datum
4768
has_largeobject_privilege_name_id(PG_FUNCTION_ARGS)
4769
0
{
4770
0
  Name    username = PG_GETARG_NAME(0);
4771
0
  Oid     roleid = get_role_oid_or_public(NameStr(*username));
4772
0
  Oid     lobjId = PG_GETARG_OID(1);
4773
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
4774
0
  AclMode   mode;
4775
0
  bool    is_missing = false;
4776
0
  bool    result;
4777
4778
0
  mode = convert_largeobject_priv_string(priv_type_text);
4779
0
  result = has_lo_priv_byid(roleid, lobjId, mode, &is_missing);
4780
4781
0
  if (is_missing)
4782
0
    PG_RETURN_NULL();
4783
4784
0
  PG_RETURN_BOOL(result);
4785
0
}
4786
4787
/*
4788
 * has_largeobject_privilege_id
4789
 *    Check user privileges on a large object given
4790
 *    large object oid, and text priv name.
4791
 *    current_user is assumed
4792
 */
4793
Datum
4794
has_largeobject_privilege_id(PG_FUNCTION_ARGS)
4795
0
{
4796
0
  Oid     lobjId = PG_GETARG_OID(0);
4797
0
  Oid     roleid = GetUserId();
4798
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(1);
4799
0
  AclMode   mode;
4800
0
  bool    is_missing = false;
4801
0
  bool    result;
4802
4803
0
  mode = convert_largeobject_priv_string(priv_type_text);
4804
0
  result = has_lo_priv_byid(roleid, lobjId, mode, &is_missing);
4805
4806
0
  if (is_missing)
4807
0
    PG_RETURN_NULL();
4808
4809
0
  PG_RETURN_BOOL(result);
4810
0
}
4811
4812
/*
4813
 * has_largeobject_privilege_id_id
4814
 *    Check user privileges on a large object given
4815
 *    roleid, large object oid, and text priv name.
4816
 */
4817
Datum
4818
has_largeobject_privilege_id_id(PG_FUNCTION_ARGS)
4819
0
{
4820
0
  Oid     roleid = PG_GETARG_OID(0);
4821
0
  Oid     lobjId = PG_GETARG_OID(1);
4822
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
4823
0
  AclMode   mode;
4824
0
  bool    is_missing = false;
4825
0
  bool    result;
4826
4827
0
  mode = convert_largeobject_priv_string(priv_type_text);
4828
0
  result = has_lo_priv_byid(roleid, lobjId, mode, &is_missing);
4829
4830
0
  if (is_missing)
4831
0
    PG_RETURN_NULL();
4832
4833
0
  PG_RETURN_BOOL(result);
4834
0
}
4835
4836
/*
4837
 * convert_largeobject_priv_string
4838
 *    Convert text string to AclMode value.
4839
 */
4840
static AclMode
4841
convert_largeobject_priv_string(text *priv_type_text)
4842
0
{
4843
0
  static const priv_map largeobject_priv_map[] = {
4844
0
    {"SELECT", ACL_SELECT},
4845
0
    {"SELECT WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_SELECT)},
4846
0
    {"UPDATE", ACL_UPDATE},
4847
0
    {"UPDATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_UPDATE)},
4848
0
    {NULL, 0}
4849
0
  };
4850
4851
0
  return convert_any_priv_string(priv_type_text, largeobject_priv_map);
4852
0
}
4853
4854
/*
4855
 * pg_has_role variants
4856
 *    These are all named "pg_has_role" at the SQL level.
4857
 *    They take various combinations of role name, role OID,
4858
 *    user name, user OID, or implicit user = current_user.
4859
 *
4860
 *    The result is a boolean value: true if user has the indicated
4861
 *    privilege, false if not.
4862
 */
4863
4864
/*
4865
 * pg_has_role_name_name
4866
 *    Check user privileges on a role given
4867
 *    name username, name rolename, and text priv name.
4868
 */
4869
Datum
4870
pg_has_role_name_name(PG_FUNCTION_ARGS)
4871
0
{
4872
0
  Name    username = PG_GETARG_NAME(0);
4873
0
  Name    rolename = PG_GETARG_NAME(1);
4874
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
4875
0
  Oid     roleid;
4876
0
  Oid     roleoid;
4877
0
  AclMode   mode;
4878
0
  AclResult aclresult;
4879
4880
0
  roleid = get_role_oid(NameStr(*username), false);
4881
0
  roleoid = get_role_oid(NameStr(*rolename), false);
4882
0
  mode = convert_role_priv_string(priv_type_text);
4883
4884
0
  aclresult = pg_role_aclcheck(roleoid, roleid, mode);
4885
4886
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
4887
0
}
4888
4889
/*
4890
 * pg_has_role_name
4891
 *    Check user privileges on a role given
4892
 *    name rolename and text priv name.
4893
 *    current_user is assumed
4894
 */
4895
Datum
4896
pg_has_role_name(PG_FUNCTION_ARGS)
4897
0
{
4898
0
  Name    rolename = PG_GETARG_NAME(0);
4899
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(1);
4900
0
  Oid     roleid;
4901
0
  Oid     roleoid;
4902
0
  AclMode   mode;
4903
0
  AclResult aclresult;
4904
4905
0
  roleid = GetUserId();
4906
0
  roleoid = get_role_oid(NameStr(*rolename), false);
4907
0
  mode = convert_role_priv_string(priv_type_text);
4908
4909
0
  aclresult = pg_role_aclcheck(roleoid, roleid, mode);
4910
4911
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
4912
0
}
4913
4914
/*
4915
 * pg_has_role_name_id
4916
 *    Check user privileges on a role given
4917
 *    name usename, role oid, and text priv name.
4918
 */
4919
Datum
4920
pg_has_role_name_id(PG_FUNCTION_ARGS)
4921
0
{
4922
0
  Name    username = PG_GETARG_NAME(0);
4923
0
  Oid     roleoid = PG_GETARG_OID(1);
4924
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
4925
0
  Oid     roleid;
4926
0
  AclMode   mode;
4927
0
  AclResult aclresult;
4928
4929
0
  roleid = get_role_oid(NameStr(*username), false);
4930
0
  mode = convert_role_priv_string(priv_type_text);
4931
4932
0
  aclresult = pg_role_aclcheck(roleoid, roleid, mode);
4933
4934
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
4935
0
}
4936
4937
/*
4938
 * pg_has_role_id
4939
 *    Check user privileges on a role given
4940
 *    role oid, and text priv name.
4941
 *    current_user is assumed
4942
 */
4943
Datum
4944
pg_has_role_id(PG_FUNCTION_ARGS)
4945
0
{
4946
0
  Oid     roleoid = PG_GETARG_OID(0);
4947
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(1);
4948
0
  Oid     roleid;
4949
0
  AclMode   mode;
4950
0
  AclResult aclresult;
4951
4952
0
  roleid = GetUserId();
4953
0
  mode = convert_role_priv_string(priv_type_text);
4954
4955
0
  aclresult = pg_role_aclcheck(roleoid, roleid, mode);
4956
4957
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
4958
0
}
4959
4960
/*
4961
 * pg_has_role_id_name
4962
 *    Check user privileges on a role given
4963
 *    roleid, name rolename, and text priv name.
4964
 */
4965
Datum
4966
pg_has_role_id_name(PG_FUNCTION_ARGS)
4967
0
{
4968
0
  Oid     roleid = PG_GETARG_OID(0);
4969
0
  Name    rolename = PG_GETARG_NAME(1);
4970
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
4971
0
  Oid     roleoid;
4972
0
  AclMode   mode;
4973
0
  AclResult aclresult;
4974
4975
0
  roleoid = get_role_oid(NameStr(*rolename), false);
4976
0
  mode = convert_role_priv_string(priv_type_text);
4977
4978
0
  aclresult = pg_role_aclcheck(roleoid, roleid, mode);
4979
4980
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
4981
0
}
4982
4983
/*
4984
 * pg_has_role_id_id
4985
 *    Check user privileges on a role given
4986
 *    roleid, role oid, and text priv name.
4987
 */
4988
Datum
4989
pg_has_role_id_id(PG_FUNCTION_ARGS)
4990
0
{
4991
0
  Oid     roleid = PG_GETARG_OID(0);
4992
0
  Oid     roleoid = PG_GETARG_OID(1);
4993
0
  text     *priv_type_text = PG_GETARG_TEXT_PP(2);
4994
0
  AclMode   mode;
4995
0
  AclResult aclresult;
4996
4997
0
  mode = convert_role_priv_string(priv_type_text);
4998
4999
0
  aclresult = pg_role_aclcheck(roleoid, roleid, mode);
5000
5001
0
  PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
5002
0
}
5003
5004
/*
5005
 *    Support routines for pg_has_role family.
5006
 */
5007
5008
/*
5009
 * convert_role_priv_string
5010
 *    Convert text string to AclMode value.
5011
 *
5012
 * We use USAGE to denote whether the privileges of the role are accessible
5013
 * (has_privs_of_role), MEMBER to denote is_member, and MEMBER WITH GRANT
5014
 * (or ADMIN) OPTION to denote is_admin.  There is no ACL bit corresponding
5015
 * to MEMBER so we cheat and use ACL_CREATE for that.  This convention
5016
 * is shared only with pg_role_aclcheck, below.
5017
 */
5018
static AclMode
5019
convert_role_priv_string(text *priv_type_text)
5020
0
{
5021
0
  static const priv_map role_priv_map[] = {
5022
0
    {"USAGE", ACL_USAGE},
5023
0
    {"MEMBER", ACL_CREATE},
5024
0
    {"SET", ACL_SET},
5025
0
    {"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
5026
0
    {"USAGE WITH ADMIN OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
5027
0
    {"MEMBER WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
5028
0
    {"MEMBER WITH ADMIN OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
5029
0
    {"SET WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
5030
0
    {"SET WITH ADMIN OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
5031
0
    {NULL, 0}
5032
0
  };
5033
5034
0
  return convert_any_priv_string(priv_type_text, role_priv_map);
5035
0
}
5036
5037
/*
5038
 * pg_role_aclcheck
5039
 *    Quick-and-dirty support for pg_has_role
5040
 */
5041
static AclResult
5042
pg_role_aclcheck(Oid role_oid, Oid roleid, AclMode mode)
5043
0
{
5044
0
  if (mode & ACL_GRANT_OPTION_FOR(ACL_CREATE))
5045
0
  {
5046
0
    if (is_admin_of_role(roleid, role_oid))
5047
0
      return ACLCHECK_OK;
5048
0
  }
5049
0
  if (mode & ACL_CREATE)
5050
0
  {
5051
0
    if (is_member_of_role(roleid, role_oid))
5052
0
      return ACLCHECK_OK;
5053
0
  }
5054
0
  if (mode & ACL_USAGE)
5055
0
  {
5056
0
    if (has_privs_of_role(roleid, role_oid))
5057
0
      return ACLCHECK_OK;
5058
0
  }
5059
0
  if (mode & ACL_SET)
5060
0
  {
5061
0
    if (member_can_set_role(roleid, role_oid))
5062
0
      return ACLCHECK_OK;
5063
0
  }
5064
0
  return ACLCHECK_NO_PRIV;
5065
0
}
5066
5067
5068
/*
5069
 * initialization function (called by InitPostgres)
5070
 */
5071
void
5072
initialize_acl(void)
5073
0
{
5074
0
  if (!IsBootstrapProcessingMode())
5075
0
  {
5076
0
    cached_db_hash =
5077
0
      GetSysCacheHashValue1(DATABASEOID,
5078
0
                  ObjectIdGetDatum(MyDatabaseId));
5079
5080
    /*
5081
     * In normal mode, set a callback on any syscache invalidation of rows
5082
     * of pg_auth_members (for roles_is_member_of()) pg_database (for
5083
     * roles_is_member_of())
5084
     */
5085
0
    CacheRegisterSyscacheCallback(AUTHMEMROLEMEM,
5086
0
                    RoleMembershipCacheCallback,
5087
0
                    (Datum) 0);
5088
0
    CacheRegisterSyscacheCallback(AUTHOID,
5089
0
                    RoleMembershipCacheCallback,
5090
0
                    (Datum) 0);
5091
0
    CacheRegisterSyscacheCallback(DATABASEOID,
5092
0
                    RoleMembershipCacheCallback,
5093
0
                    (Datum) 0);
5094
0
  }
5095
0
}
5096
5097
/*
5098
 * RoleMembershipCacheCallback
5099
 *    Syscache inval callback function
5100
 */
5101
static void
5102
RoleMembershipCacheCallback(Datum arg, SysCacheIdentifier cacheid,
5103
              uint32 hashvalue)
5104
0
{
5105
0
  if (cacheid == DATABASEOID &&
5106
0
    hashvalue != cached_db_hash &&
5107
0
    hashvalue != 0)
5108
0
  {
5109
0
    return;         /* ignore pg_database changes for other DBs */
5110
0
  }
5111
5112
  /* Force membership caches to be recomputed on next use */
5113
0
  cached_role[ROLERECURSE_MEMBERS] = InvalidOid;
5114
0
  cached_role[ROLERECURSE_PRIVS] = InvalidOid;
5115
0
  cached_role[ROLERECURSE_SETROLE] = InvalidOid;
5116
0
}
5117
5118
/*
5119
 * A helper function for roles_is_member_of() that provides an optimized
5120
 * implementation of list_append_unique_oid() via a Bloom filter.  The caller
5121
 * (i.e., roles_is_member_of()) is responsible for freeing bf once it is done
5122
 * using this function.
5123
 */
5124
static inline List *
5125
roles_list_append(List *roles_list, bloom_filter **bf, Oid role)
5126
0
{
5127
0
  unsigned char *roleptr = (unsigned char *) &role;
5128
5129
  /*
5130
   * If there is a previously-created Bloom filter, use it to try to
5131
   * determine whether the role is missing from the list.  If it says yes,
5132
   * that's a hard fact and we can go ahead and add the role.  If it says
5133
   * no, that's only probabilistic and we'd better search the list.  Without
5134
   * a filter, we must always do an ordinary linear search through the
5135
   * existing list.
5136
   */
5137
0
  if ((*bf && bloom_lacks_element(*bf, roleptr, sizeof(Oid))) ||
5138
0
    !list_member_oid(roles_list, role))
5139
0
  {
5140
    /*
5141
     * If the list is large, we take on the overhead of creating and
5142
     * populating a Bloom filter to speed up future calls to this
5143
     * function.
5144
     */
5145
0
    if (*bf == NULL &&
5146
0
      list_length(roles_list) > ROLES_LIST_BLOOM_THRESHOLD)
5147
0
    {
5148
0
      *bf = bloom_create(ROLES_LIST_BLOOM_THRESHOLD * 10, work_mem, 0);
5149
0
      foreach_oid(roleid, roles_list)
5150
0
        bloom_add_element(*bf, (unsigned char *) &roleid, sizeof(Oid));
5151
0
    }
5152
5153
    /*
5154
     * Finally, add the role to the list and the Bloom filter, if it
5155
     * exists.
5156
     */
5157
0
    roles_list = lappend_oid(roles_list, role);
5158
0
    if (*bf)
5159
0
      bloom_add_element(*bf, roleptr, sizeof(Oid));
5160
0
  }
5161
5162
0
  return roles_list;
5163
0
}
5164
5165
/*
5166
 * Get a list of roles that the specified roleid is a member of
5167
 *
5168
 * Type ROLERECURSE_MEMBERS recurses through all grants; ROLERECURSE_PRIVS
5169
 * recurses only through inheritable grants; and ROLERECURSE_SETROLE recurses
5170
 * only through grants with set_option.
5171
 *
5172
 * Since indirect membership testing is relatively expensive, we cache
5173
 * a list of memberships.  Hence, the result is only guaranteed good until
5174
 * the next call of roles_is_member_of()!
5175
 *
5176
 * For the benefit of select_best_grantor, the result is defined to be
5177
 * in breadth-first order, ie, closer relationships earlier.
5178
 *
5179
 * If admin_of is not InvalidOid, this function sets *admin_role, either
5180
 * to the OID of the first role in the result list that directly possesses
5181
 * ADMIN OPTION on the role corresponding to admin_of, or to InvalidOid if
5182
 * there is no such role.
5183
 */
5184
static List *
5185
roles_is_member_of(Oid roleid, enum RoleRecurseType type,
5186
           Oid admin_of, Oid *admin_role)
5187
0
{
5188
0
  Oid     dba;
5189
0
  List     *roles_list;
5190
0
  ListCell   *l;
5191
0
  List     *new_cached_roles;
5192
0
  MemoryContext oldctx;
5193
0
  bloom_filter *bf = NULL;
5194
5195
0
  Assert(OidIsValid(admin_of) == (admin_role != NULL));
5196
0
  if (admin_role != NULL)
5197
0
    *admin_role = InvalidOid;
5198
5199
  /* If cache is valid and ADMIN OPTION not sought, just return the list */
5200
0
  if (cached_role[type] == roleid && !OidIsValid(admin_of) &&
5201
0
    OidIsValid(cached_role[type]))
5202
0
    return cached_roles[type];
5203
5204
  /*
5205
   * Role expansion happens in a non-database backend when guc.c checks
5206
   * ROLE_PG_READ_ALL_SETTINGS for a physical walsender SHOW command.  In
5207
   * that case, no role gets pg_database_owner.
5208
   */
5209
0
  if (!OidIsValid(MyDatabaseId))
5210
0
    dba = InvalidOid;
5211
0
  else
5212
0
  {
5213
0
    HeapTuple dbtup;
5214
5215
0
    dbtup = SearchSysCache1(DATABASEOID, ObjectIdGetDatum(MyDatabaseId));
5216
0
    if (!HeapTupleIsValid(dbtup))
5217
0
      elog(ERROR, "cache lookup failed for database %u", MyDatabaseId);
5218
0
    dba = ((Form_pg_database) GETSTRUCT(dbtup))->datdba;
5219
0
    ReleaseSysCache(dbtup);
5220
0
  }
5221
5222
  /*
5223
   * Find all the roles that roleid is a member of, including multi-level
5224
   * recursion.  The role itself will always be the first element of the
5225
   * resulting list.
5226
   *
5227
   * Each element of the list is scanned to see if it adds any indirect
5228
   * memberships.  We can use a single list as both the record of
5229
   * already-found memberships and the agenda of roles yet to be scanned.
5230
   * This is a bit tricky but works because the foreach() macro doesn't
5231
   * fetch the next list element until the bottom of the loop.
5232
   */
5233
0
  roles_list = list_make1_oid(roleid);
5234
5235
0
  foreach(l, roles_list)
5236
0
  {
5237
0
    Oid     memberid = lfirst_oid(l);
5238
0
    CatCList   *memlist;
5239
0
    int     i;
5240
5241
    /* Find roles that memberid is directly a member of */
5242
0
    memlist = SearchSysCacheList1(AUTHMEMMEMROLE,
5243
0
                    ObjectIdGetDatum(memberid));
5244
0
    for (i = 0; i < memlist->n_members; i++)
5245
0
    {
5246
0
      HeapTuple tup = &memlist->members[i]->tuple;
5247
0
      Form_pg_auth_members form = (Form_pg_auth_members) GETSTRUCT(tup);
5248
0
      Oid     otherid = form->roleid;
5249
5250
      /*
5251
       * While otherid==InvalidOid shouldn't appear in the catalog, the
5252
       * OidIsValid() avoids crashing if that arises.
5253
       */
5254
0
      if (otherid == admin_of && form->admin_option &&
5255
0
        OidIsValid(admin_of) && !OidIsValid(*admin_role))
5256
0
        *admin_role = memberid;
5257
5258
      /* If we're supposed to ignore non-heritable grants, do so. */
5259
0
      if (type == ROLERECURSE_PRIVS && !form->inherit_option)
5260
0
        continue;
5261
5262
      /* If we're supposed to ignore non-SET grants, do so. */
5263
0
      if (type == ROLERECURSE_SETROLE && !form->set_option)
5264
0
        continue;
5265
5266
      /*
5267
       * Even though there shouldn't be any loops in the membership
5268
       * graph, we must test for having already seen this role. It is
5269
       * legal for instance to have both A->B and A->C->B.
5270
       */
5271
0
      roles_list = roles_list_append(roles_list, &bf, otherid);
5272
0
    }
5273
0
    ReleaseSysCacheList(memlist);
5274
5275
    /* implement pg_database_owner implicit membership */
5276
0
    if (memberid == dba && OidIsValid(dba))
5277
0
      roles_list = roles_list_append(roles_list, &bf,
5278
0
                       ROLE_PG_DATABASE_OWNER);
5279
0
  }
5280
5281
  /*
5282
   * Free the Bloom filter created by roles_list_append(), if there is one.
5283
   */
5284
0
  if (bf)
5285
0
    bloom_free(bf);
5286
5287
  /*
5288
   * Copy the completed list into TopMemoryContext so it will persist.
5289
   */
5290
0
  oldctx = MemoryContextSwitchTo(TopMemoryContext);
5291
0
  new_cached_roles = list_copy(roles_list);
5292
0
  MemoryContextSwitchTo(oldctx);
5293
0
  list_free(roles_list);
5294
5295
  /*
5296
   * Now safe to assign to state variable
5297
   */
5298
0
  cached_role[type] = InvalidOid; /* just paranoia */
5299
0
  list_free(cached_roles[type]);
5300
0
  cached_roles[type] = new_cached_roles;
5301
0
  cached_role[type] = roleid;
5302
5303
  /* And now we can return the answer */
5304
0
  return cached_roles[type];
5305
0
}
5306
5307
5308
/*
5309
 * Does member have the privileges of role (directly or indirectly)?
5310
 *
5311
 * This is defined not to recurse through grants that are not inherited,
5312
 * and only inherited grants confer the associated privileges automatically.
5313
 *
5314
 * See also member_can_set_role, below.
5315
 */
5316
bool
5317
has_privs_of_role(Oid member, Oid role)
5318
0
{
5319
  /* Fast path for simple case */
5320
0
  if (member == role)
5321
0
    return true;
5322
5323
  /* Superusers have every privilege, so are part of every role */
5324
0
  if (superuser_arg(member))
5325
0
    return true;
5326
5327
  /*
5328
   * Find all the roles that member has the privileges of, including
5329
   * multi-level recursion, then see if target role is any one of them.
5330
   */
5331
0
  return list_member_oid(roles_is_member_of(member, ROLERECURSE_PRIVS,
5332
0
                        InvalidOid, NULL),
5333
0
               role);
5334
0
}
5335
5336
/*
5337
 * Can member use SET ROLE to this role?
5338
 *
5339
 * There must be a chain of grants from 'member' to 'role' each of which
5340
 * permits SET ROLE; that is, each of which has set_option = true.
5341
 *
5342
 * It doesn't matter whether the grants are inheritable. That's a separate
5343
 * question; see has_privs_of_role.
5344
 *
5345
 * This function should be used to determine whether the session user can
5346
 * use SET ROLE to become the target user. We also use it to determine whether
5347
 * the session user can change an existing object to be owned by the target
5348
 * user, or create new objects owned by the target user.
5349
 */
5350
bool
5351
member_can_set_role(Oid member, Oid role)
5352
0
{
5353
  /* Fast path for simple case */
5354
0
  if (member == role)
5355
0
    return true;
5356
5357
  /* Superusers have every privilege, so can always SET ROLE */
5358
0
  if (superuser_arg(member))
5359
0
    return true;
5360
5361
  /*
5362
   * Find all the roles that member can access via SET ROLE, including
5363
   * multi-level recursion, then see if target role is any one of them.
5364
   */
5365
0
  return list_member_oid(roles_is_member_of(member, ROLERECURSE_SETROLE,
5366
0
                        InvalidOid, NULL),
5367
0
               role);
5368
0
}
5369
5370
/*
5371
 * Permission violation error unless able to SET ROLE to target role.
5372
 */
5373
void
5374
check_can_set_role(Oid member, Oid role)
5375
0
{
5376
0
  if (!member_can_set_role(member, role))
5377
0
    ereport(ERROR,
5378
0
        (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
5379
0
         errmsg("must be able to SET ROLE \"%s\"",
5380
0
            GetUserNameFromId(role, false))));
5381
0
}
5382
5383
/*
5384
 * Is member a member of role (directly or indirectly)?
5385
 *
5386
 * This is defined to recurse through grants whether they are inherited or not.
5387
 *
5388
 * Do not use this for privilege checking, instead use has_privs_of_role().
5389
 * Don't use it for determining whether it's possible to SET ROLE to some
5390
 * other role; for that, use member_can_set_role(). And don't use it for
5391
 * determining whether it's OK to create an object owned by some other role:
5392
 * use member_can_set_role() for that, too.
5393
 *
5394
 * In short, calling this function is the wrong thing to do nearly everywhere.
5395
 */
5396
bool
5397
is_member_of_role(Oid member, Oid role)
5398
0
{
5399
  /* Fast path for simple case */
5400
0
  if (member == role)
5401
0
    return true;
5402
5403
  /* Superusers have every privilege, so are part of every role */
5404
0
  if (superuser_arg(member))
5405
0
    return true;
5406
5407
  /*
5408
   * Find all the roles that member is a member of, including multi-level
5409
   * recursion, then see if target role is any one of them.
5410
   */
5411
0
  return list_member_oid(roles_is_member_of(member, ROLERECURSE_MEMBERS,
5412
0
                        InvalidOid, NULL),
5413
0
               role);
5414
0
}
5415
5416
/*
5417
 * Is member a member of role, not considering superuserness?
5418
 *
5419
 * This is identical to is_member_of_role except we ignore superuser
5420
 * status.
5421
 *
5422
 * Do not use this for privilege checking, instead use has_privs_of_role()
5423
 */
5424
bool
5425
is_member_of_role_nosuper(Oid member, Oid role)
5426
0
{
5427
  /* Fast path for simple case */
5428
0
  if (member == role)
5429
0
    return true;
5430
5431
  /*
5432
   * Find all the roles that member is a member of, including multi-level
5433
   * recursion, then see if target role is any one of them.
5434
   */
5435
0
  return list_member_oid(roles_is_member_of(member, ROLERECURSE_MEMBERS,
5436
0
                        InvalidOid, NULL),
5437
0
               role);
5438
0
}
5439
5440
5441
/*
5442
 * Is member an admin of role?  That is, is member the role itself (subject to
5443
 * restrictions below), a member (directly or indirectly) WITH ADMIN OPTION,
5444
 * or a superuser?
5445
 */
5446
bool
5447
is_admin_of_role(Oid member, Oid role)
5448
0
{
5449
0
  Oid     admin_role;
5450
5451
0
  if (superuser_arg(member))
5452
0
    return true;
5453
5454
  /* By policy, a role cannot have WITH ADMIN OPTION on itself. */
5455
0
  if (member == role)
5456
0
    return false;
5457
5458
0
  (void) roles_is_member_of(member, ROLERECURSE_MEMBERS, role, &admin_role);
5459
0
  return OidIsValid(admin_role);
5460
0
}
5461
5462
/*
5463
 * Find a role whose privileges "member" inherits which has ADMIN OPTION
5464
 * on "role", ignoring super-userness.
5465
 *
5466
 * There might be more than one such role; prefer one which involves fewer
5467
 * hops. That is, if member has ADMIN OPTION, prefer that over all other
5468
 * options; if not, prefer a role from which member inherits more directly
5469
 * over more indirect inheritance.
5470
 */
5471
Oid
5472
select_best_admin(Oid member, Oid role)
5473
0
{
5474
0
  Oid     admin_role;
5475
5476
  /* By policy, a role cannot have WITH ADMIN OPTION on itself. */
5477
0
  if (member == role)
5478
0
    return InvalidOid;
5479
5480
0
  (void) roles_is_member_of(member, ROLERECURSE_PRIVS, role, &admin_role);
5481
0
  return admin_role;
5482
0
}
5483
5484
/*
5485
 * Select the effective grantor ID for a GRANT or REVOKE operation.
5486
 *
5487
 * If the GRANT/REVOKE has an explicit GRANTED BY clause, we always use
5488
 * exactly that role (which may result in granting/revoking no privileges).
5489
 * Otherwise, we seek a "best" grantor, starting with the current user.
5490
 *
5491
 * The grantor must always be either the object owner or some role that has
5492
 * been explicitly granted grant options.  This ensures that all granted
5493
 * privileges appear to flow from the object owner, and there are never
5494
 * multiple "original sources" of a privilege.  Therefore, if the would-be
5495
 * grantor is a member of a role that has the needed grant options, we have
5496
 * to do the grant as that role instead.
5497
 *
5498
 * It is possible that the would-be grantor is a member of several roles
5499
 * that have different subsets of the desired grant options, but no one
5500
 * role has 'em all.  In this case we pick a role with the largest number
5501
 * of desired options.  Ties are broken in favor of closer ancestors.
5502
 *
5503
 * grantedBy: the GRANTED BY clause of GRANT/REVOKE, or NULL if none
5504
 * privileges: the privileges to be granted/revoked
5505
 * acl: the ACL of the object in question
5506
 * ownerId: the role owning the object in question
5507
 * *grantorId: receives the OID of the role to do the grant as
5508
 * *grantOptions: receives grant options actually held by grantorId (maybe 0)
5509
 */
5510
void
5511
select_best_grantor(const RoleSpec *grantedBy, AclMode privileges,
5512
          const Acl *acl, Oid ownerId,
5513
          Oid *grantorId, AclMode *grantOptions)
5514
0
{
5515
0
  Oid     roleId = GetUserId();
5516
0
  AclMode   needed_goptions = ACL_GRANT_OPTION_FOR(privileges);
5517
0
  List     *roles_list;
5518
0
  int     nrights;
5519
0
  ListCell   *l;
5520
5521
  /*
5522
   * If we have GRANTED BY, resolve it and verify current user is allowed to
5523
   * specify that role.
5524
   */
5525
0
  if (grantedBy)
5526
0
  {
5527
0
    Oid     grantor = get_rolespec_oid(grantedBy, false);
5528
5529
0
    if (!has_privs_of_role(roleId, grantor))
5530
0
      ereport(ERROR,
5531
0
          (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
5532
0
           errmsg("must inherit privileges of role \"%s\"",
5533
0
              GetUserNameFromId(grantor, false))));
5534
    /* Use exactly that grantor, whether it has privileges or not */
5535
0
    *grantorId = grantor;
5536
0
    *grantOptions = aclmask_direct(acl, grantor, ownerId,
5537
0
                     needed_goptions, ACLMASK_ALL);
5538
0
    return;
5539
0
  }
5540
5541
  /*
5542
   * The object owner is always treated as having all grant options, so if
5543
   * roleId is the owner it's easy.  Also, if roleId is a superuser it's
5544
   * easy: superusers are implicitly members of every role, so they act as
5545
   * the object owner.
5546
   */
5547
0
  if (roleId == ownerId || superuser_arg(roleId))
5548
0
  {
5549
0
    *grantorId = ownerId;
5550
0
    *grantOptions = needed_goptions;
5551
0
    return;
5552
0
  }
5553
5554
  /*
5555
   * Otherwise we have to do a careful search to see if roleId has the
5556
   * privileges of any suitable role.  Note: we can hang onto the result of
5557
   * roles_is_member_of() throughout this loop, because aclmask_direct()
5558
   * doesn't query any role memberships.
5559
   */
5560
0
  roles_list = roles_is_member_of(roleId, ROLERECURSE_PRIVS,
5561
0
                  InvalidOid, NULL);
5562
5563
  /* initialize candidate result as default */
5564
0
  *grantorId = roleId;
5565
0
  *grantOptions = ACL_NO_RIGHTS;
5566
0
  nrights = 0;
5567
5568
0
  foreach(l, roles_list)
5569
0
  {
5570
0
    Oid     otherrole = lfirst_oid(l);
5571
0
    AclMode   otherprivs;
5572
5573
0
    otherprivs = aclmask_direct(acl, otherrole, ownerId,
5574
0
                  needed_goptions, ACLMASK_ALL);
5575
0
    if (otherprivs == needed_goptions)
5576
0
    {
5577
      /* Found a suitable grantor */
5578
0
      *grantorId = otherrole;
5579
0
      *grantOptions = otherprivs;
5580
0
      return;
5581
0
    }
5582
5583
    /*
5584
     * If it has just some of the needed privileges, remember best
5585
     * candidate.
5586
     */
5587
0
    if (otherprivs != ACL_NO_RIGHTS)
5588
0
    {
5589
0
      int     nnewrights = pg_popcount64(otherprivs);
5590
5591
0
      if (nnewrights > nrights)
5592
0
      {
5593
0
        *grantorId = otherrole;
5594
0
        *grantOptions = otherprivs;
5595
0
        nrights = nnewrights;
5596
0
      }
5597
0
    }
5598
0
  }
5599
0
}
5600
5601
/*
5602
 * get_role_oid - Given a role name, look up the role's OID.
5603
 *
5604
 * If missing_ok is false, throw an error if role name not found.  If
5605
 * true, just return InvalidOid.
5606
 */
5607
Oid
5608
get_role_oid(const char *rolname, bool missing_ok)
5609
0
{
5610
0
  Oid     oid;
5611
5612
0
  oid = GetSysCacheOid1(AUTHNAME, Anum_pg_authid_oid,
5613
0
              CStringGetDatum(rolname));
5614
0
  if (!OidIsValid(oid) && !missing_ok)
5615
0
    ereport(ERROR,
5616
0
        (errcode(ERRCODE_UNDEFINED_OBJECT),
5617
0
         errmsg("role \"%s\" does not exist", rolname)));
5618
0
  return oid;
5619
0
}
5620
5621
/*
5622
 * get_role_oid_or_public - As above, but return ACL_ID_PUBLIC if the
5623
 *    role name is "public".
5624
 */
5625
Oid
5626
get_role_oid_or_public(const char *rolname)
5627
0
{
5628
0
  if (strcmp(rolname, "public") == 0)
5629
0
    return ACL_ID_PUBLIC;
5630
5631
0
  return get_role_oid(rolname, false);
5632
0
}
5633
5634
/*
5635
 * Given a RoleSpec node, return the OID it corresponds to.  If missing_ok is
5636
 * true, return InvalidOid if the role does not exist.
5637
 *
5638
 * PUBLIC is always disallowed here.  Routines wanting to handle the PUBLIC
5639
 * case must check the case separately.
5640
 */
5641
Oid
5642
get_rolespec_oid(const RoleSpec *role, bool missing_ok)
5643
0
{
5644
0
  Oid     oid;
5645
5646
0
  switch (role->roletype)
5647
0
  {
5648
0
    case ROLESPEC_CSTRING:
5649
0
      Assert(role->rolename);
5650
0
      oid = get_role_oid(role->rolename, missing_ok);
5651
0
      break;
5652
5653
0
    case ROLESPEC_CURRENT_ROLE:
5654
0
    case ROLESPEC_CURRENT_USER:
5655
0
      oid = GetUserId();
5656
0
      break;
5657
5658
0
    case ROLESPEC_SESSION_USER:
5659
0
      oid = GetSessionUserId();
5660
0
      break;
5661
5662
0
    case ROLESPEC_PUBLIC:
5663
0
      ereport(ERROR,
5664
0
          (errcode(ERRCODE_UNDEFINED_OBJECT),
5665
0
           errmsg("role \"%s\" does not exist", "public")));
5666
0
      oid = InvalidOid; /* make compiler happy */
5667
0
      break;
5668
5669
0
    default:
5670
0
      elog(ERROR, "unexpected role type %d", role->roletype);
5671
0
  }
5672
5673
0
  return oid;
5674
0
}
5675
5676
/*
5677
 * Given a RoleSpec node, return the pg_authid HeapTuple it corresponds to.
5678
 * Caller must ReleaseSysCache when done with the result tuple.
5679
 */
5680
HeapTuple
5681
get_rolespec_tuple(const RoleSpec *role)
5682
0
{
5683
0
  HeapTuple tuple;
5684
5685
0
  switch (role->roletype)
5686
0
  {
5687
0
    case ROLESPEC_CSTRING:
5688
0
      Assert(role->rolename);
5689
0
      tuple = SearchSysCache1(AUTHNAME, CStringGetDatum(role->rolename));
5690
0
      if (!HeapTupleIsValid(tuple))
5691
0
        ereport(ERROR,
5692
0
            (errcode(ERRCODE_UNDEFINED_OBJECT),
5693
0
             errmsg("role \"%s\" does not exist", role->rolename)));
5694
0
      break;
5695
5696
0
    case ROLESPEC_CURRENT_ROLE:
5697
0
    case ROLESPEC_CURRENT_USER:
5698
0
      tuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(GetUserId()));
5699
0
      if (!HeapTupleIsValid(tuple))
5700
0
        elog(ERROR, "cache lookup failed for role %u", GetUserId());
5701
0
      break;
5702
5703
0
    case ROLESPEC_SESSION_USER:
5704
0
      tuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(GetSessionUserId()));
5705
0
      if (!HeapTupleIsValid(tuple))
5706
0
        elog(ERROR, "cache lookup failed for role %u", GetSessionUserId());
5707
0
      break;
5708
5709
0
    case ROLESPEC_PUBLIC:
5710
0
      ereport(ERROR,
5711
0
          (errcode(ERRCODE_UNDEFINED_OBJECT),
5712
0
           errmsg("role \"%s\" does not exist", "public")));
5713
0
      tuple = NULL;   /* make compiler happy */
5714
0
      break;
5715
5716
0
    default:
5717
0
      elog(ERROR, "unexpected role type %d", role->roletype);
5718
0
  }
5719
5720
0
  return tuple;
5721
0
}
5722
5723
/*
5724
 * Given a RoleSpec, returns a palloc'ed copy of the corresponding role's name.
5725
 */
5726
char *
5727
get_rolespec_name(const RoleSpec *role)
5728
0
{
5729
0
  HeapTuple tp;
5730
0
  Form_pg_authid authForm;
5731
0
  char     *rolename;
5732
5733
0
  tp = get_rolespec_tuple(role);
5734
0
  authForm = (Form_pg_authid) GETSTRUCT(tp);
5735
0
  rolename = pstrdup(NameStr(authForm->rolname));
5736
0
  ReleaseSysCache(tp);
5737
5738
0
  return rolename;
5739
0
}
5740
5741
/*
5742
 * Given a RoleSpec, throw an error if the name is reserved, using detail_msg,
5743
 * if provided (which must be already translated).
5744
 *
5745
 * If node is NULL, no error is thrown.  If detail_msg is NULL then no detail
5746
 * message is provided.
5747
 */
5748
void
5749
check_rolespec_name(const RoleSpec *role, const char *detail_msg)
5750
0
{
5751
0
  if (!role)
5752
0
    return;
5753
5754
0
  if (role->roletype != ROLESPEC_CSTRING)
5755
0
    return;
5756
5757
0
  if (IsReservedName(role->rolename))
5758
0
  {
5759
0
    if (detail_msg)
5760
0
      ereport(ERROR,
5761
0
          (errcode(ERRCODE_RESERVED_NAME),
5762
0
           errmsg("role name \"%s\" is reserved",
5763
0
              role->rolename),
5764
0
           errdetail_internal("%s", detail_msg)));
5765
0
    else
5766
0
      ereport(ERROR,
5767
0
          (errcode(ERRCODE_RESERVED_NAME),
5768
0
           errmsg("role name \"%s\" is reserved",
5769
0
              role->rolename)));
5770
0
  }
5771
0
}