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