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