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