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