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