Coverage Report

Created: 2025-06-15 06:31

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