⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 acl.c

📁 PostgreSQL 8.1.4的源码 适用于Linux下的开源数据库系统
💻 C
📖 第 1 页 / 共 5 页
字号:
/*------------------------------------------------------------------------- * * acl.c *	  Basic access control list data structures manipulation routines. * * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION *	  $PostgreSQL: pgsql/src/backend/utils/adt/acl.c,v 1.127 2005/11/04 17:25:15 tgl Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include <ctype.h>#include "catalog/namespace.h"#include "catalog/pg_authid.h"#include "catalog/pg_auth_members.h"#include "catalog/pg_type.h"#include "commands/dbcommands.h"#include "commands/tablespace.h"#include "miscadmin.h"#include "utils/acl.h"#include "utils/builtins.h"#include "utils/catcache.h"#include "utils/inval.h"#include "utils/lsyscache.h"#include "utils/memutils.h"#include "utils/syscache.h"/* * We frequently need to test whether a given role is a member of some other * role.  In most of these tests the "given role" is the same, namely the * active current user.  So we can optimize it by keeping a cached list of * all the roles the "given role" is a member of, directly or indirectly. * The cache is flushed whenever we detect a change in pg_auth_members. * * There are actually two caches, one computed under "has_privs" rules * (do not recurse where rolinherit isn't true) and one computed under * "is_member" rules (recurse regardless of rolinherit). * * Possibly this mechanism should be generalized to allow caching membership * info for multiple roles? * * The has_privs cache is: * cached_privs_role is the role OID the cache is for. * cached_privs_roles is an OID list of roles that cached_privs_role *		has the privileges of (always including itself). * The cache is valid if cached_privs_role is not InvalidOid. * * The is_member cache is similarly: * cached_member_role is the role OID the cache is for. * cached_membership_roles is an OID list of roles that cached_member_role *		is a member of (always including itself). * The cache is valid if cached_member_role is not InvalidOid. */static Oid	cached_privs_role = InvalidOid;static List *cached_privs_roles = NIL;static Oid	cached_member_role = InvalidOid;static List *cached_membership_roles = NIL;static const char *getid(const char *s, char *n);static void putid(char *p, const char *s);static Acl *allocacl(int n);static const char *aclparse(const char *s, AclItem *aip);static bool aclitem_match(const AclItem *a1, const AclItem *a2);static void check_circularity(const Acl *old_acl, const AclItem *mod_aip,				  Oid ownerId);static Acl *recursive_revoke(Acl *acl, Oid grantee, AclMode revoke_privs,				 Oid ownerId, DropBehavior behavior);static int	oidComparator(const void *arg1, const void *arg2);static AclMode convert_priv_string(text *priv_type_text);static Oid	convert_table_name(text *tablename);static AclMode convert_table_priv_string(text *priv_type_text);static Oid	convert_database_name(text *databasename);static AclMode convert_database_priv_string(text *priv_type_text);static Oid	convert_function_name(text *functionname);static AclMode convert_function_priv_string(text *priv_type_text);static Oid	convert_language_name(text *languagename);static AclMode convert_language_priv_string(text *priv_type_text);static Oid	convert_schema_name(text *schemaname);static AclMode convert_schema_priv_string(text *priv_type_text);static Oid	convert_tablespace_name(text *tablespacename);static AclMode convert_tablespace_priv_string(text *priv_type_text);static AclMode convert_role_priv_string(text *priv_type_text);static AclResult pg_role_aclcheck(Oid role_oid, Oid roleid, AclMode mode);static void RoleMembershipCacheCallback(Datum arg, Oid relid);/* * getid *		Consumes the first alphanumeric string (identifier) found in string *		's', ignoring any leading white space.	If it finds a double quote *		it returns the word inside the quotes. * * RETURNS: *		the string position in 's' that points to the next non-space character *		in 's', after any quotes.  Also: *		- loads the identifier into 'n'.  (If no identifier is found, 'n' *		  contains an empty string.)  'n' must be NAMEDATALEN bytes. */static const char *getid(const char *s, char *n){	int			len = 0;	bool		in_quotes = false;	Assert(s && n);	while (isspace((unsigned char) *s))		s++;	/* This code had better match what putid() does, below */	for (;		 *s != '\0' &&		 (isalnum((unsigned char) *s) ||		  *s == '_' ||		  *s == '"' ||		  in_quotes);		 s++)	{		if (*s == '"')		{			/* safe to look at next char (could be '\0' though) */			if (*(s + 1) != '"')			{				in_quotes = !in_quotes;				continue;			}			/* it's an escaped double quote; skip the escaping char */			s++;		}		/* Add the character to the string */		if (len >= NAMEDATALEN - 1)			ereport(ERROR,					(errcode(ERRCODE_NAME_TOO_LONG),					 errmsg("identifier too long"),					 errdetail("Identifier must be less than %d characters.",							   NAMEDATALEN)));		n[len++] = *s;	}	n[len] = '\0';	while (isspace((unsigned char) *s))		s++;	return s;}/* * Write a role name at *p, adding double quotes if needed. * There must be at least (2*NAMEDATALEN)+2 bytes available at *p. * This needs to be kept in sync with copyAclUserName in pg_dump/dumputils.c */static voidputid(char *p, const char *s){	const char *src;	bool		safe = true;	for (src = s; *src; src++)	{		/* This test had better match what getid() does, above */		if (!isalnum((unsigned char) *src) && *src != '_')		{			safe = false;			break;		}	}	if (!safe)		*p++ = '"';	for (src = s; *src; src++)	{		/* A double quote character in a username is encoded as "" */		if (*src == '"')			*p++ = '"';		*p++ = *src;	}	if (!safe)		*p++ = '"';	*p = '\0';}/* * aclparse *		Consumes and parses an ACL specification of the form: *				[group|user] [A-Za-z0-9]*=[rwaR]* *		from string 's', ignoring any leading white space or white space *		between the optional id type keyword (group|user) and the actual *		ACL specification. * *		The group|user decoration is unnecessary in the roles world, *		but we still accept it for backward compatibility. * *		This routine is called by the parser as well as aclitemin(), hence *		the added generality. * * RETURNS: *		the string position in 's' immediately following the ACL *		specification.	Also: *		- loads the structure pointed to by 'aip' with the appropriate *		  UID/GID, id type identifier and mode type values. */static const char *aclparse(const char *s, AclItem *aip){	AclMode		privs,				goption,				read;	char		name[NAMEDATALEN];	char		name2[NAMEDATALEN];	Assert(s && aip);#ifdef ACLDEBUG	elog(LOG, "aclparse: input = \"%s\"", s);#endif	s = getid(s, name);	if (*s != '=')	{		/* we just read a keyword, not a name */		if (strcmp(name, "group") != 0 && strcmp(name, "user") != 0)			ereport(ERROR,					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),					 errmsg("unrecognized key word: \"%s\"", name),					 errhint("ACL key word must be \"group\" or \"user\".")));		s = getid(s, name);		/* move s to the name beyond the keyword */		if (name[0] == '\0')			ereport(ERROR,					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),					 errmsg("missing name"),					 errhint("A name must follow the \"group\" or \"user\" key word.")));	}	if (*s != '=')		ereport(ERROR,				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),				 errmsg("missing \"=\" sign")));	privs = goption = ACL_NO_RIGHTS;	for (++s, read = 0; isalpha((unsigned char) *s) || *s == '*'; s++)	{		switch (*s)		{			case '*':				goption |= read;				break;			case ACL_INSERT_CHR:				read = ACL_INSERT;				break;			case ACL_SELECT_CHR:				read = ACL_SELECT;				break;			case ACL_UPDATE_CHR:				read = ACL_UPDATE;				break;			case ACL_DELETE_CHR:				read = ACL_DELETE;				break;			case ACL_RULE_CHR:				read = ACL_RULE;				break;			case ACL_REFERENCES_CHR:				read = ACL_REFERENCES;				break;			case ACL_TRIGGER_CHR:				read = ACL_TRIGGER;				break;			case ACL_EXECUTE_CHR:				read = ACL_EXECUTE;				break;			case ACL_USAGE_CHR:				read = ACL_USAGE;				break;			case ACL_CREATE_CHR:				read = ACL_CREATE;				break;			case ACL_CREATE_TEMP_CHR:				read = ACL_CREATE_TEMP;				break;			default:				ereport(ERROR,						(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),					  errmsg("invalid mode character: must be one of \"%s\"",							 ACL_ALL_RIGHTS_STR)));		}		privs |= read;	}	if (name[0] == '\0')		aip->ai_grantee = ACL_ID_PUBLIC;	else		aip->ai_grantee = get_roleid_checked(name);	/*	 * XXX Allow a degree of backward compatibility by defaulting the grantor	 * to the superuser.	 */	if (*s == '/')	{		s = getid(s + 1, name2);		if (name2[0] == '\0')			ereport(ERROR,					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),					 errmsg("a name must follow the \"/\" sign")));		aip->ai_grantor = get_roleid_checked(name2);	}	else	{		aip->ai_grantor = BOOTSTRAP_SUPERUSERID;		ereport(WARNING,				(errcode(ERRCODE_INVALID_GRANTOR),				 errmsg("defaulting grantor to user ID %u",						BOOTSTRAP_SUPERUSERID)));	}	ACLITEM_SET_PRIVS_GOPTIONS(*aip, privs, goption);#ifdef ACLDEBUG	elog(LOG, "aclparse: correctly read [%u %x %x]",		 aip->ai_grantee, privs, goption);#endif	return s;}/* * allocacl *		Allocates storage for a new Acl with 'n' entries. * * RETURNS: *		the new Acl */static Acl *allocacl(int n){	Acl		   *new_acl;	Size		size;	if (n < 0)		elog(ERROR, "invalid size: %d", n);	size = ACL_N_SIZE(n);	new_acl = (Acl *) palloc0(size);	new_acl->size = size;	new_acl->ndim = 1;	new_acl->flags = 0;	new_acl->elemtype = ACLITEMOID;	ARR_LBOUND(new_acl)[0] = 1;	ARR_DIMS(new_acl)[0] = n;	return new_acl;}/* * aclitemin *		Allocates storage for, and fills in, a new AclItem given a string *		's' that contains an ACL specification.  See aclparse for details. * * RETURNS: *		the new AclItem */Datumaclitemin(PG_FUNCTION_ARGS){	const char *s = PG_GETARG_CSTRING(0);	AclItem    *aip;	aip = (AclItem *) palloc(sizeof(AclItem));	s = aclparse(s, aip);	while (isspace((unsigned char) *s))		++s;	if (*s)		ereport(ERROR,				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),			   errmsg("extra garbage at the end of the ACL specification")));	PG_RETURN_ACLITEM_P(aip);}/* * aclitemout *		Allocates storage for, and fills in, a new null-delimited string *		containing a formatted ACL specification.  See aclparse for details. * * RETURNS: *		the new string */Datumaclitemout(PG_FUNCTION_ARGS){	AclItem    *aip = PG_GETARG_ACLITEM_P(0);	char	   *p;	char	   *out;	HeapTuple	htup;	unsigned	i;	out = palloc(strlen("=/") +				 2 * N_ACL_RIGHTS +				 2 * (2 * NAMEDATALEN + 2) +				 1);	p = out;	*p = '\0';	if (aip->ai_grantee != ACL_ID_PUBLIC)	{		htup = SearchSysCache(AUTHOID,							  ObjectIdGetDatum(aip->ai_grantee),							  0, 0, 0);		if (HeapTupleIsValid(htup))		{			putid(p, NameStr(((Form_pg_authid) GETSTRUCT(htup))->rolname));			ReleaseSysCache(htup);		}		else		{			/* Generate numeric OID if we don't find an entry */			sprintf(p, "%u", aip->ai_grantee);		}	}	while (*p)		++p;	*p++ = '=';	for (i = 0; i < N_ACL_RIGHTS; ++i)	{		if (ACLITEM_GET_PRIVS(*aip) & (1 << i))			*p++ = ACL_ALL_RIGHTS_STR[i];		if (ACLITEM_GET_GOPTIONS(*aip) & (1 << i))			*p++ = '*';	}	*p++ = '/';	*p = '\0';	htup = SearchSysCache(AUTHOID,						  ObjectIdGetDatum(aip->ai_grantor),						  0, 0, 0);	if (HeapTupleIsValid(htup))	{		putid(p, NameStr(((Form_pg_authid) GETSTRUCT(htup))->rolname));		ReleaseSysCache(htup);	}	else	{		/* Generate numeric OID if we don't find an entry */		sprintf(p, "%u", aip->ai_grantor);	}	PG_RETURN_CSTRING(out);}/* * aclitem_match *		Two AclItems are considered to match iff they have the same *		grantee and grantor; the privileges are ignored. */static boolaclitem_match(const AclItem *a1, const AclItem *a2){	return a1->ai_grantee == a2->ai_grantee &&		a1->ai_grantor == a2->ai_grantor;}/* * aclitem equality operator */Datumaclitem_eq(PG_FUNCTION_ARGS){	AclItem    *a1 = PG_GETARG_ACLITEM_P(0);	AclItem    *a2 = PG_GETARG_ACLITEM_P(1);	bool		result;	result = a1->ai_privs == a2->ai_privs &&		a1->ai_grantee == a2->ai_grantee &&		a1->ai_grantor == a2->ai_grantor;	PG_RETURN_BOOL(result);}/* * aclitem hash function * * We make aclitems hashable not so much because anyone is likely to hash * them, as because we want array equality to work on aclitem arrays, and * with the typcache mechanism we must have a hash or btree opclass. */Datumhash_aclitem(PG_FUNCTION_ARGS){	AclItem    *a = PG_GETARG_ACLITEM_P(0);	/* not very bright, but avoids any issue of padding in struct */	PG_RETURN_UINT32((uint32) (a->ai_privs + a->ai_grantee + a->ai_grantor));}/* * acldefault()  --- create an ACL describing default access permissions * * Change this routine if you want to alter the default access policy for * newly-created objects (or any object with a NULL acl entry).

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -