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