pgsql-ace-01-database-8.5devel-r2475.patch

text/x-patch

Filename: pgsql-ace-01-database-8.5devel-r2475.patch
Type: text/x-patch
Part: 0
Message: [PATCH] ACE Framework - Database, Schema

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: context
File+
src/backend/catalog/aclchk.c 63 5
src/backend/commands/comment.c 2 3
src/backend/commands/dbcommands.c 32 122
src/backend/commands/indexcmds.c 3 3
src/backend/Makefile 1 1
src/backend/security/ace/ace_database.c 285 0
src/backend/security/ace/ace_misc.c 23 0
src/backend/security/ace/Makefile 11 0
src/backend/security/Makefile 10 0
src/backend/utils/adt/dbsize.c 3 6
src/backend/utils/init/postinit.c 5 12
src/include/security/ace.h 39 0
diff -Nrpc base/src/backend/Makefile ace_database/src/backend/Makefile
*** base/src/backend/Makefile	Fri Sep 11 15:31:36 2009
--- ace_database/src/backend/Makefile	Sun Dec 13 14:27:22 2009
*************** include $(top_builddir)/src/Makefile.glo
*** 16,22 ****
  
  SUBDIRS = access bootstrap catalog parser commands executor foreign lib libpq \
  	main nodes optimizer port postmaster regex rewrite \
! 	storage tcop tsearch utils $(top_builddir)/src/timezone
  
  include $(srcdir)/common.mk
  
--- 16,22 ----
  
  SUBDIRS = access bootstrap catalog parser commands executor foreign lib libpq \
  	main nodes optimizer port postmaster regex rewrite \
! 	security storage tcop tsearch utils $(top_builddir)/src/timezone
  
  include $(srcdir)/common.mk
  
diff -Nrpc base/src/backend/catalog/aclchk.c ace_database/src/backend/catalog/aclchk.c
*** base/src/backend/catalog/aclchk.c	Fri Dec 11 14:58:31 2009
--- ace_database/src/backend/catalog/aclchk.c	Sun Dec 13 14:27:22 2009
***************
*** 46,51 ****
--- 46,52 ----
  #include "foreign/foreign.h"
  #include "miscadmin.h"
  #include "parser/parse_func.h"
+ #include "security/ace.h"
  #include "utils/acl.h"
  #include "utils/fmgroids.h"
  #include "utils/lsyscache.h"
*************** static void expand_all_col_privileges(Oi
*** 125,130 ****
--- 126,134 ----
  						  int num_col_privileges);
  static AclMode string_to_privilege(const char *privname);
  static const char *privilege_to_string(AclMode privilege);
+ static AclMode restrict_grant(bool is_grant, AclMode avail_goptions,
+ 							  bool all_privs, AclMode privileges,
+ 							  const char *objname);
  static AclMode restrict_and_check_grant(bool is_grant, AclMode avail_goptions,
  						 bool all_privs, AclMode privileges,
  						 Oid objectId, Oid grantorId,
*************** merge_acl_with_grant(Acl *old_acl, bool 
*** 221,230 ****
--- 225,281 ----
  	return new_acl;
  }
  
+ 
  /*
   * Restrict the privileges to what we can actually grant, and emit
   * the standards-mandated warning and error messages.
   */
+ static AclMode restrict_grant(bool is_grant, AclMode avail_goptions,
+ 							  bool all_privs, AclMode privileges,
+ 							  const char *objname)
+ {
+ 	AclMode		this_privileges;
+ 
+ 	/*
+ 	 * Restrict the operation to what we can actually grant or revoke, and
+ 	 * issue a warning if appropriate.	(For REVOKE this isn't quite what the
+ 	 * spec says to do: the spec seems to want a warning only if no privilege
+ 	 * bits actually change in the ACL. In practice that behavior seems much
+ 	 * too noisy, as well as inconsistent with the GRANT case.)
+ 	 */
+ 	this_privileges = privileges & ACL_OPTION_TO_PRIVS(avail_goptions);
+ 	if (is_grant)
+ 	{
+ 		if (this_privileges == 0)
+ 			ereport(WARNING,
+ 					(errcode(ERRCODE_WARNING_PRIVILEGE_NOT_GRANTED),
+ 				  errmsg("no privileges were granted for \"%s\"", objname)));
+ 		else if (!all_privs && this_privileges != privileges)
+ 			ereport(WARNING,
+ 					(errcode(ERRCODE_WARNING_PRIVILEGE_NOT_GRANTED),
+ 			 errmsg("not all privileges were granted for \"%s\"", objname)));
+ 	}
+ 	else
+ 	{
+ 		if (this_privileges == 0)
+ 			ereport(WARNING,
+ 					(errcode(ERRCODE_WARNING_PRIVILEGE_NOT_REVOKED),
+ 			  errmsg("no privileges could be revoked for \"%s\"", objname)));
+ 		else if (!all_privs && this_privileges != privileges)
+ 			ereport(WARNING,
+ 					(errcode(ERRCODE_WARNING_PRIVILEGE_NOT_REVOKED),
+ 					 errmsg("not all privileges could be revoked for \"%s\"", objname)));
+ 	}
+ 
+ 	return this_privileges;
+ }
+ 
+ /*
+  * Restrict the privileges to what we can actually grant, and emit
+  * the standards-mandated warning and error messages.
+  * (when ace_xxx_grant() is given for all the object classes, this
+  * function shall be removed.)
+  */
  static AclMode
  restrict_and_check_grant(bool is_grant, AclMode avail_goptions, bool all_privs,
  						 AclMode privileges, Oid objectId, Oid grantorId,
*************** ExecGrant_Database(InternalGrant *istmt)
*** 1965,1979 ****
  							&grantorId, &avail_goptions);
  
  		/*
  		 * Restrict the privileges to what we can actually grant, and emit the
  		 * standards-mandated warning and error messages.
  		 */
  		this_privileges =
! 			restrict_and_check_grant(istmt->is_grant, avail_goptions,
! 									 istmt->all_privs, istmt->privileges,
! 									 datId, grantorId, ACL_KIND_DATABASE,
! 									 NameStr(pg_database_tuple->datname),
! 									 0, NULL);
  
  		/*
  		 * Generate new ACL.
--- 2016,2037 ----
  							&grantorId, &avail_goptions);
  
  		/*
+ 		 * Permission check to grant/revoke permissions.
+ 		 *
+ 		 * If we found no grant options, consider whether to issue a hard
+ 		 * error. Per spec, having any privilege at all on the object will
+ 		 * get you by here.
+ 		 */
+ 		ace_database_grant(datId, grantorId, avail_goptions);
+ 
+ 		/*
  		 * Restrict the privileges to what we can actually grant, and emit the
  		 * standards-mandated warning and error messages.
  		 */
  		this_privileges =
! 			restrict_grant(istmt->is_grant, avail_goptions,
! 						   istmt->all_privs, istmt->privileges,
! 						   NameStr(pg_database_tuple->datname));
  
  		/*
  		 * Generate new ACL.
diff -Nrpc base/src/backend/commands/comment.c ace_database/src/backend/commands/comment.c
*** base/src/backend/commands/comment.c	Fri Dec 11 14:58:31 2009
--- ace_database/src/backend/commands/comment.c	Sun Dec 13 14:27:22 2009
***************
*** 49,54 ****
--- 49,55 ----
  #include "parser/parse_func.h"
  #include "parser/parse_oper.h"
  #include "parser/parse_type.h"
+ #include "security/ace.h"
  #include "utils/acl.h"
  #include "utils/builtins.h"
  #include "utils/fmgroids.h"
*************** CommentDatabase(List *qualname, char *co
*** 685,693 ****
  	}
  
  	/* Check object security */
! 	if (!pg_database_ownercheck(oid, GetUserId()))
! 		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE,
! 					   database);
  
  	/* Call CreateSharedComments() to create/drop the comments */
  	CreateSharedComments(oid, DatabaseRelationId, comment);
--- 686,692 ----
  	}
  
  	/* Check object security */
! 	ace_database_comment(oid);
  
  	/* Call CreateSharedComments() to create/drop the comments */
  	CreateSharedComments(oid, DatabaseRelationId, comment);
diff -Nrpc base/src/backend/commands/dbcommands.c ace_database/src/backend/commands/dbcommands.c
*** base/src/backend/commands/dbcommands.c	Thu Nov 12 23:28:17 2009
--- ace_database/src/backend/commands/dbcommands.c	Sun Dec 13 14:27:22 2009
***************
*** 42,47 ****
--- 42,48 ----
  #include "miscadmin.h"
  #include "pgstat.h"
  #include "postmaster/bgwriter.h"
+ #include "security/ace.h"
  #include "storage/bufmgr.h"
  #include "storage/fd.h"
  #include "storage/lmgr.h"
*************** static bool get_db_info(const char *name
*** 79,85 ****
  			int *encodingP, bool *dbIsTemplateP, bool *dbAllowConnP,
  			Oid *dbLastSysOidP, TransactionId *dbFrozenXidP,
  			Oid *dbTablespace, char **dbCollate, char **dbCtype);
- static bool have_createdb_privilege(void);
  static void remove_dbtablespaces(Oid db_id);
  static bool check_db_file_conflict(Oid db_id);
  static int	errdetail_busy_db(int notherbackends, int npreparedxacts);
--- 80,85 ----
*************** createdb(const CreatedbStmt *stmt)
*** 103,109 ****
  	Oid			src_lastsysoid;
  	TransactionId src_frozenxid;
  	Oid			src_deftablespace;
! 	volatile Oid dst_deftablespace;
  	Relation	pg_database_rel;
  	HeapTuple	tuple;
  	Datum		new_record[Natts_pg_database];
--- 103,109 ----
  	Oid			src_lastsysoid;
  	TransactionId src_frozenxid;
  	Oid			src_deftablespace;
! 	volatile Oid dst_deftablespace = InvalidOid;
  	Relation	pg_database_rel;
  	HeapTuple	tuple;
  	Datum		new_record[Natts_pg_database];
*************** createdb(const CreatedbStmt *stmt)
*** 250,255 ****
--- 250,267 ----
  					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
  					 errmsg("invalid connection limit: %d", dbconnlimit)));
  	}
+ 	if (dtablespacename && dtablespacename->arg)
+ 	{
+ 		char	   *tablespacename;
+ 
+ 		tablespacename = strVal(dtablespacename->arg);
+ 		dst_deftablespace = get_tablespace_oid(tablespacename);
+ 		if (!OidIsValid(dst_deftablespace))
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_UNDEFINED_OBJECT),
+ 					 errmsg("tablespace \"%s\" does not exist",
+ 							tablespacename)));
+ 	}
  
  	/* obtain OID of proposed owner */
  	if (dbowner)
*************** createdb(const CreatedbStmt *stmt)
*** 258,277 ****
  		datdba = GetUserId();
  
  	/*
- 	 * To create a database, must have createdb privilege and must be able to
- 	 * become the target role (this does not imply that the target role itself
- 	 * must have createdb privilege).  The latter provision guards against
- 	 * "giveaway" attacks.	Note that a superuser will always have both of
- 	 * these privileges a fortiori.
- 	 */
- 	if (!have_createdb_privilege())
- 		ereport(ERROR,
- 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- 				 errmsg("permission denied to create database")));
- 
- 	check_is_member_of_role(GetUserId(), datdba);
- 
- 	/*
  	 * Lookup database (template) to be cloned, and obtain share lock on it.
  	 * ShareLock allows two CREATE DATABASEs to work from the same template
  	 * concurrently, while ensuring no one is busy dropping it in parallel
--- 270,275 ----
*************** createdb(const CreatedbStmt *stmt)
*** 293,310 ****
  				 errmsg("template database \"%s\" does not exist",
  						dbtemplate)));
  
! 	/*
! 	 * Permission check: to copy a DB that's not marked datistemplate, you
! 	 * must be superuser or the owner thereof.
! 	 */
! 	if (!src_istemplate)
! 	{
! 		if (!pg_database_ownercheck(src_dboid, GetUserId()))
! 			ereport(ERROR,
! 					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 					 errmsg("permission denied to copy database \"%s\"",
! 							dbtemplate)));
! 	}
  
  	/* If encoding or locales are defaulted, use source's setting */
  	if (encoding < 0)
--- 291,299 ----
  				 errmsg("template database \"%s\" does not exist",
  						dbtemplate)));
  
! 	/* Permission check to create a new database */
! 	ace_database_create(dbname, src_dboid, src_istemplate,
! 						datdba, dst_deftablespace);
  
  	/* If encoding or locales are defaulted, use source's setting */
  	if (encoding < 0)
*************** createdb(const CreatedbStmt *stmt)
*** 421,445 ****
  	}
  
  	/* Resolve default tablespace for new database */
! 	if (dtablespacename && dtablespacename->arg)
  	{
- 		char	   *tablespacename;
- 		AclResult	aclresult;
- 
- 		tablespacename = strVal(dtablespacename->arg);
- 		dst_deftablespace = get_tablespace_oid(tablespacename);
- 		if (!OidIsValid(dst_deftablespace))
- 			ereport(ERROR,
- 					(errcode(ERRCODE_UNDEFINED_OBJECT),
- 					 errmsg("tablespace \"%s\" does not exist",
- 							tablespacename)));
- 		/* check permissions */
- 		aclresult = pg_tablespace_aclcheck(dst_deftablespace, GetUserId(),
- 										   ACL_CREATE);
- 		if (aclresult != ACLCHECK_OK)
- 			aclcheck_error(aclresult, ACL_KIND_TABLESPACE,
- 						   tablespacename);
- 
  		/* pg_global must never be the default tablespace */
  		if (dst_deftablespace == GLOBALTABLESPACE_OID)
  			ereport(ERROR,
--- 410,417 ----
  	}
  
  	/* Resolve default tablespace for new database */
! 	if (OidIsValid(dst_deftablespace))
  	{
  		/* pg_global must never be the default tablespace */
  		if (dst_deftablespace == GLOBALTABLESPACE_OID)
  			ereport(ERROR,
*************** createdb(const CreatedbStmt *stmt)
*** 471,477 ****
  				ereport(ERROR,
  						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  						 errmsg("cannot assign new default tablespace \"%s\"",
! 								tablespacename),
  						 errdetail("There is a conflict because database \"%s\" already has some tables in this tablespace.",
  								   dbtemplate)));
  			pfree(srcpath);
--- 443,449 ----
  				ereport(ERROR,
  						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  						 errmsg("cannot assign new default tablespace \"%s\"",
! 								get_tablespace_name(dst_deftablespace)),
  						 errdetail("There is a conflict because database \"%s\" already has some tables in this tablespace.",
  								   dbtemplate)));
  			pfree(srcpath);
*************** dropdb(const char *dbname, bool missing_
*** 771,779 ****
  	/*
  	 * Permission checks
  	 */
! 	if (!pg_database_ownercheck(db_id, GetUserId()))
! 		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE,
! 					   dbname);
  
  	/*
  	 * Disallow dropping a DB that is marked istemplate.  This is just to
--- 743,749 ----
  	/*
  	 * Permission checks
  	 */
! 	ace_database_drop(db_id, false);
  
  	/*
  	 * Disallow dropping a DB that is marked istemplate.  This is just to
*************** RenameDatabase(const char *oldname, cons
*** 904,919 ****
  				(errcode(ERRCODE_UNDEFINED_DATABASE),
  				 errmsg("database \"%s\" does not exist", oldname)));
  
! 	/* must be owner */
! 	if (!pg_database_ownercheck(db_id, GetUserId()))
! 		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE,
! 					   oldname);
! 
! 	/* must have createdb rights */
! 	if (!have_createdb_privilege())
! 		ereport(ERROR,
! 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 				 errmsg("permission denied to rename database")));
  
  	/*
  	 * Make sure the new name doesn't exist.  See notes for same error in
--- 874,881 ----
  				(errcode(ERRCODE_UNDEFINED_DATABASE),
  				 errmsg("database \"%s\" does not exist", oldname)));
  
! 	/* Permission check to rename the database */
! 	ace_database_alter(db_id, newname, InvalidOid, InvalidOid);
  
  	/*
  	 * Make sure the new name doesn't exist.  See notes for same error in
*************** movedb(const char *dbname, const char *t
*** 984,990 ****
  	bool		new_record_repl[Natts_pg_database];
  	ScanKeyData scankey;
  	SysScanDesc sysscan;
- 	AclResult	aclresult;
  	char	   *src_dbpath;
  	char	   *dst_dbpath;
  	DIR		   *dstdir;
--- 946,951 ----
*************** movedb(const char *dbname, const char *t
*** 1015,1027 ****
  							   AccessExclusiveLock);
  
  	/*
- 	 * Permission checks
- 	 */
- 	if (!pg_database_ownercheck(db_id, GetUserId()))
- 		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE,
- 					   dbname);
- 
- 	/*
  	 * Obviously can't move the tables of my own database
  	 */
  	if (db_id == MyDatabaseId)
--- 976,981 ----
*************** movedb(const char *dbname, const char *t
*** 1041,1051 ****
  	/*
  	 * Permission checks
  	 */
! 	aclresult = pg_tablespace_aclcheck(dst_tblspcoid, GetUserId(),
! 									   ACL_CREATE);
! 	if (aclresult != ACLCHECK_OK)
! 		aclcheck_error(aclresult, ACL_KIND_TABLESPACE,
! 					   tblspcname);
  
  	/*
  	 * pg_global must never be the default tablespace
--- 995,1001 ----
  	/*
  	 * Permission checks
  	 */
! 	ace_database_alter(db_id, NULL, dst_tblspcoid, InvalidOid);
  
  	/*
  	 * pg_global must never be the default tablespace
*************** AlterDatabase(AlterDatabaseStmt *stmt, b
*** 1367,1375 ****
  				(errcode(ERRCODE_UNDEFINED_DATABASE),
  				 errmsg("database \"%s\" does not exist", stmt->dbname)));
  
! 	if (!pg_database_ownercheck(HeapTupleGetOid(tuple), GetUserId()))
! 		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE,
! 					   stmt->dbname);
  
  	/*
  	 * Build an updated tuple, perusing the information just obtained
--- 1317,1326 ----
  				(errcode(ERRCODE_UNDEFINED_DATABASE),
  				 errmsg("database \"%s\" does not exist", stmt->dbname)));
  
! 	/*
! 	 * Permission checks
! 	 */
! 	ace_database_alter(HeapTupleGetOid(tuple), NULL, InvalidOid, InvalidOid);
  
  	/*
  	 * Build an updated tuple, perusing the information just obtained
*************** AlterDatabaseSet(AlterDatabaseSetStmt *s
*** 1417,1425 ****
  	 */
  	shdepLockAndCheckObject(DatabaseRelationId, datid);
  
! 	if (!pg_database_ownercheck(datid, GetUserId()))
!   		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE,
!   					   stmt->dbname);
  
  	AlterSetting(datid, InvalidOid, stmt->setstmt);
    
--- 1368,1375 ----
  	 */
  	shdepLockAndCheckObject(DatabaseRelationId, datid);
  
! 	/* Permission checks */
! 	ace_database_alter(datid, NULL, InvalidOid, InvalidOid);
  
  	AlterSetting(datid, InvalidOid, stmt->setstmt);
    
*************** AlterDatabaseOwner(const char *dbname, O
*** 1474,1500 ****
  		bool		isNull;
  		HeapTuple	newtuple;
  
! 		/* Otherwise, must be owner of the existing object */
! 		if (!pg_database_ownercheck(HeapTupleGetOid(tuple), GetUserId()))
! 			aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE,
! 						   dbname);
! 
! 		/* Must be able to become new owner */
! 		check_is_member_of_role(GetUserId(), newOwnerId);
! 
! 		/*
! 		 * must have createdb rights
! 		 *
! 		 * NOTE: This is different from other alter-owner checks in that the
! 		 * current user is checked for createdb privileges instead of the
! 		 * destination owner.  This is consistent with the CREATE case for
! 		 * databases.  Because superusers will always have this right, we need
! 		 * no special case for them.
! 		 */
! 		if (!have_createdb_privilege())
! 			ereport(ERROR,
! 					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 				   errmsg("permission denied to change owner of database")));
  
  		memset(repl_null, false, sizeof(repl_null));
  		memset(repl_repl, false, sizeof(repl_repl));
--- 1424,1432 ----
  		bool		isNull;
  		HeapTuple	newtuple;
  
! 		/* Permission checks */
! 		ace_database_alter(HeapTupleGetOid(tuple),
! 						   NULL, InvalidOid, newOwnerId);
  
  		memset(repl_null, false, sizeof(repl_null));
  		memset(repl_repl, false, sizeof(repl_repl));
*************** get_db_info(const char *name, LOCKMODE l
*** 1664,1691 ****
  	return result;
  }
  
- /* Check if current user has createdb privileges */
- static bool
- have_createdb_privilege(void)
- {
- 	bool		result = false;
- 	HeapTuple	utup;
- 
- 	/* Superusers can always do everything */
- 	if (superuser())
- 		return true;
- 
- 	utup = SearchSysCache(AUTHOID,
- 						  ObjectIdGetDatum(GetUserId()),
- 						  0, 0, 0);
- 	if (HeapTupleIsValid(utup))
- 	{
- 		result = ((Form_pg_authid) GETSTRUCT(utup))->rolcreatedb;
- 		ReleaseSysCache(utup);
- 	}
- 	return result;
- }
- 
  /*
   * Remove tablespace directories
   *
--- 1596,1601 ----
diff -Nrpc base/src/backend/commands/indexcmds.c ace_database/src/backend/commands/indexcmds.c
*** base/src/backend/commands/indexcmds.c	Fri Dec 11 14:58:31 2009
--- ace_database/src/backend/commands/indexcmds.c	Sun Dec 13 14:27:22 2009
***************
*** 39,44 ****
--- 39,45 ----
  #include "parser/parse_func.h"
  #include "parser/parse_oper.h"
  #include "parser/parsetree.h"
+ #include "security/ace.h"
  #include "storage/lmgr.h"
  #include "storage/proc.h"
  #include "storage/procarray.h"
*************** ReindexDatabase(const char *databaseName
*** 1534,1542 ****
  				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  				 errmsg("can only reindex the currently open database")));
  
! 	if (!pg_database_ownercheck(MyDatabaseId, GetUserId()))
! 		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE,
! 					   databaseName);
  
  	/*
  	 * Create a memory context that will survive forced transaction commits we
--- 1535,1542 ----
  				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  				 errmsg("can only reindex the currently open database")));
  
! 	/* Permission checks */
! 	ace_database_reindex(MyDatabaseId);
  
  	/*
  	 * Create a memory context that will survive forced transaction commits we
diff -Nrpc base/src/backend/security/Makefile ace_database/src/backend/security/Makefile
*** base/src/backend/security/Makefile	Thu Jan  1 09:00:00 1970
--- ace_database/src/backend/security/Makefile	Sun Dec 13 14:27:22 2009
***************
*** 0 ****
--- 1,10 ----
+ #
+ # Makefile for security features
+ #
+ subdir = src/backend/security
+ top_builddir = ../../..
+ include $(top_builddir)/src/Makefile.global
+ 
+ SUBDIRS = ace
+ 
+ include $(top_srcdir)/src/backend/common.mk
diff -Nrpc base/src/backend/security/ace/Makefile ace_database/src/backend/security/ace/Makefile
*** base/src/backend/security/ace/Makefile	Thu Jan  1 09:00:00 1970
--- ace_database/src/backend/security/ace/Makefile	Sun Dec 13 14:27:22 2009
***************
*** 0 ****
--- 1,11 ----
+ #
+ # Makefile for ACE framework
+ #
+ subdir = src/backend/security/ace
+ top_builddir = ../../../..
+ include $(top_builddir)/src/Makefile.global
+ 
+ OBJS = ace_misc.o ace_database.o
+ 
+ include $(top_srcdir)/src/backend/common.mk
+ 
diff -Nrpc base/src/backend/security/ace/ace_database.c ace_database/src/backend/security/ace/ace_database.c
*** base/src/backend/security/ace/ace_database.c	Thu Jan  1 09:00:00 1970
--- ace_database/src/backend/security/ace/ace_database.c	Sun Dec 13 14:27:22 2009
***************
*** 0 ****
--- 1,285 ----
+ /*
+  * ace_database.c
+  *
+  * security hooks related to database object class.
+  *
+  * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  */
+ #include "postgres.h"
+ 
+ #include "catalog/pg_authid.h"
+ #include "catalog/pg_database.h"
+ #include "commands/dbcommands.h"
+ #include "commands/tablespace.h"
+ #include "miscadmin.h"
+ #include "security/ace.h"
+ #include "utils/syscache.h"
+ 
+ /* Check if current user has createdb privileges */
+ static bool
+ have_createdb_privilege(void)
+ {
+ 	bool        result = false;
+ 	HeapTuple   utup;
+ 
+ 	/* Superusers can always do everything */
+ 	if (superuser())
+ 		return true;
+ 
+ 	utup = SearchSysCache(AUTHOID,
+ 						  ObjectIdGetDatum(GetUserId()),
+ 						  0, 0, 0);
+ 	if (HeapTupleIsValid(utup))
+ 	{
+ 		result = ((Form_pg_authid) GETSTRUCT(utup))->rolcreatedb;
+ 		ReleaseSysCache(utup);
+ 	}
+ 	return result;
+ }
+ 
+ /*
+  * ace_database_create
+  *
+  * It enables security providers to apply permission checks to create
+  * a new database.
+  *
+  * datName : Name of the new database
+  * srcDatOid : OID of the source database
+  * srcIsTemplate : True, if the source database is marked as template
+  * datOwner : OID of the new database owner
+  * datTblspc : OID of the default tablespace of the new database,
+  *             if explicitly given. Otherwise, InvalidOid.
+  */
+ void
+ ace_database_create(const char *datName, Oid srcDatOid, bool srcIsTemplate,
+ 					Oid datOwner, Oid datTblspc)
+ {
+ 	AclResult	aclresult;
+ 
+ 	/*
+ 	 * To create a database, must have createdb privilege and must be able to
+ 	 * become the target role (this does not imply that the target role itself
+ 	 * must have createdb privilege).  The latter provision guards against
+ 	 * "giveaway" attacks.	Note that a superuser will always have both of
+ 	 * these privileges a fortiori.
+ 	 */
+ 	if (!have_createdb_privilege())
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ 				 errmsg("permission denied to create database")));
+ 
+ 	check_is_member_of_role(GetUserId(), datOwner);
+ 
+ 	/*
+ 	 * Permission check: to copy a DB that's not marked datistemplate, you
+ 	 * must be superuser or the owner thereof.
+ 	 */
+ 	if (!srcIsTemplate)
+ 	{
+ 		if (!pg_database_ownercheck(srcDatOid, GetUserId()))
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ 					 errmsg("permission denied to copy database \"%s\"",
+ 							get_database_name(srcDatOid))));
+ 	}
+ 
+ 	/*
+ 	 * Check permissions to use a certain tablespace as the default
+ 	 * tablespace in the new database
+ 	 */
+ 	if (OidIsValid(datTblspc))
+ 	{
+ 		aclresult = pg_tablespace_aclcheck(datTblspc, GetUserId(),
+ 										   ACL_CREATE);
+ 		if (aclresult != ACLCHECK_OK)
+ 			aclcheck_error(aclresult, ACL_KIND_TABLESPACE,
+ 						   get_tablespace_name(datTblspc));
+ 	}
+ }
+ 
+ /*
+  * ace_database_alter
+  *
+  * It enables security provides to check permission to alter properties
+  * of a certain database.
+  *
+  * datOid : OID of the database to be altered.
+  * newName : New name of the database, if given. Or, NULL.
+  * newTblspc : OID of the new defatult tablespace, if given. Or, InvalidOid.
+  * newOwner : OID of the new database owner, if given. Or, InvalidOid.
+  */
+ void
+ ace_database_alter(Oid datOid, const char *newName,
+ 				   Oid newTblspc, Oid newOwner)
+ {
+ 	AclResult	aclresult;
+ 
+ 	/* Must be owner for all the ALTER DATABASE options */
+ 	if (!pg_database_ownercheck(datOid, GetUserId()))
+ 		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE,
+ 					   get_database_name(datOid));
+ 
+ 	/* ALTER DATABASE ... RENAME TO */
+ 	if (newName)
+ 	{
+ 		/* Must have createdb right for renaming */
+ 		if (!have_createdb_privilege())
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ 					 errmsg("permission denied to rename database")));
+ 	}
+ 
+ 	/* ALTER DATABASE ... SET TABLESPACE */
+ 	if (OidIsValid(newTblspc))
+ 	{
+ 		/* Must have ACL_CREATE for the new default tablespace */
+ 		aclresult = pg_tablespace_aclcheck(newTblspc, GetUserId(),
+ 										   ACL_CREATE);
+ 		if (aclresult != ACLCHECK_OK)
+ 			aclcheck_error(aclresult, ACL_KIND_TABLESPACE,
+ 						   get_tablespace_name(newTblspc));
+ 	}
+ 
+ 	/* ALTER DATABASE ... OWNER TO */
+ 	if (OidIsValid(newOwner))
+ 	{
+ 		/* Must be able to become new owner */
+ 		check_is_member_of_role(GetUserId(), newOwner);
+ 
+ 		/*
+ 		 * must have createdb rights
+ 		 *
+ 		 * NOTE: This is different from other alter-owner checks in that the
+ 		 * current user is checked for createdb privileges instead of the
+ 		 * destination owner.  This is consistent with the CREATE case for
+ 		 * databases.  Because superusers will always have this right, we need
+ 		 * no special case for them.
+ 		 */
+ 		if (!have_createdb_privilege())
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ 					 errmsg("permission denied to change owner of database")));
+ 	}
+ }
+ 
+ /*
+  * ace_database_drop
+  *
+  * It enables security providers to check permission to drop a certain
+  * database.
+  *
+  * datOid : OID of the database to be dropped
+  * cascade : True, if cascaded deletion. Currently, it should never happen.
+  */
+ void
+ ace_database_drop(Oid datOid, bool cascade)
+ {
+ 	if (!cascade &&
+ 		!pg_database_ownercheck(datOid, GetUserId()))
+ 		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE,
+ 					   get_database_name(datOid));
+ }
+ 
+ /*
+  * ace_database_grant
+  *
+  * It enables security provides to check permission to grant/revoke
+  * privileges in the default PG model.
+  *
+  * datOid : OID of the database to be granted/revoked
+  * grantor : OID of the grantor role
+  * goptions : Available AclMask available to grant others
+  */
+ void
+ ace_database_grant(Oid datOid, Oid grantor, AclMode goptions)
+ {
+ 	/*
+ 	 * If we found no grant options, consider whether to issue a hard
+ 	 * error. Per spec, having any privilege at all on the object will
+ 	 * get you by here.
+ 	 */
+ 	if (goptions == ACL_NO_RIGHTS)
+ 	{
+ 		AclMode		whole_mask = ACL_ALL_RIGHTS_DATABASE;
+ 
+ 		if (pg_database_aclmask(datOid, grantor,
+ 								whole_mask | ACL_GRANT_OPTION_FOR(whole_mask),
+ 								ACLMASK_ANY) == ACL_NO_RIGHTS)
+ 			aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_DATABASE,
+ 						   get_database_name(datOid));
+ 	}
+ }
+ 
+ /*
+  * ace_database_comment
+  *
+  * It enables security provides to check permission to comment on
+  * the given database.
+  *
+  * datOid : OID of the database to be commented
+  */
+ void
+ ace_database_comment(Oid datOid)
+ {
+ 	if (!pg_database_ownercheck(datOid, GetUserId()))
+ 		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE,
+ 					   get_database_name(datOid));
+ }
+ 
+ /*
+  * ace_database_connect
+  *
+  * It enables security providers to check permission to connect to
+  * the given database on CheckMyDatabase()
+  *
+  * datOid : OID of the database to be connected
+  */
+ void
+ ace_database_connect(Oid datOid)
+ {
+ 	if (pg_database_aclcheck(datOid, GetUserId(),
+ 							 ACL_CONNECT) != ACLCHECK_OK)
+ 		ereport(FATAL,
+ 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ 				 errmsg("permission denied for database \"%s\"",
+ 						get_database_name(datOid)),
+ 				 errdetail("User does not have CONNECT privilege.")));
+ }
+ 
+ /*
+  * ace_database_reindex
+  *
+  * It enables security providers to check permissions to reindex
+  * all the tables withing the database
+  *
+  * datOid : OID of the database to be reindexed
+  */
+ void
+ ace_database_reindex(Oid datOid)
+ {
+ 	/* Must have ownership of the database */
+ 	if (!pg_database_ownercheck(datOid, GetUserId()))
+ 		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE,
+ 					   get_database_name(datOid));
+ }
+ 
+ /*
+  * ace_database_calculate_size
+  *
+  * It enables security providers to check permission to calculate
+  * a certain database size.
+  *
+  * datOid : OID of the database to be referenced
+  */
+ void
+ ace_database_calculate_size(Oid datOid)
+ {
+ 	AclResult	aclresult;
+ 
+ 	/* User must have connect privilege for target database */
+ 	aclresult = pg_database_aclcheck(datOid, GetUserId(), ACL_CONNECT);
+ 	if (aclresult != ACLCHECK_OK)
+ 		aclcheck_error(aclresult, ACL_KIND_DATABASE,
+ 					   get_database_name(datOid));
+ }
diff -Nrpc base/src/backend/security/ace/ace_misc.c ace_database/src/backend/security/ace/ace_misc.c
*** base/src/backend/security/ace/ace_misc.c	Thu Jan  1 09:00:00 1970
--- ace_database/src/backend/security/ace/ace_misc.c	Sun Dec 13 14:27:22 2009
***************
*** 0 ****
--- 1,23 ----
+ /*
+  * ace_misc.c
+  *
+  * miscellaneous security hook routines
+  *
+  * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  */
+ #include "postgres.h"
+ 
+ #include "security/ace.h"
+ 
+ /*
+  * ace_provider_initialize
+  *
+  * This hook allows security providers to initialize itself.
+  */
+ void
+ ace_provider_initialize(void)
+ {
+ 	/* initialize the default PG privs */
+ 	initialize_acl();
+ }
diff -Nrpc base/src/backend/utils/adt/dbsize.c ace_database/src/backend/utils/adt/dbsize.c
*** base/src/backend/utils/adt/dbsize.c	Thu Jun 18 10:20:52 2009
--- ace_database/src/backend/utils/adt/dbsize.c	Sun Dec 13 14:27:22 2009
***************
*** 21,26 ****
--- 21,27 ----
  #include "commands/dbcommands.h"
  #include "commands/tablespace.h"
  #include "miscadmin.h"
+ #include "security/ace.h"
  #include "storage/fd.h"
  #include "utils/acl.h"
  #include "utils/builtins.h"
*************** calculate_database_size(Oid dbOid)
*** 79,91 ****
  	struct dirent *direntry;
  	char		dirpath[MAXPGPATH];
  	char		pathname[MAXPGPATH];
- 	AclResult	aclresult;
  
! 	/* User must have connect privilege for target database */
! 	aclresult = pg_database_aclcheck(dbOid, GetUserId(), ACL_CONNECT);
! 	if (aclresult != ACLCHECK_OK)
! 		aclcheck_error(aclresult, ACL_KIND_DATABASE,
! 					   get_database_name(dbOid));
  
  	/* Shared storage in pg_global is not counted */
  
--- 80,88 ----
  	struct dirent *direntry;
  	char		dirpath[MAXPGPATH];
  	char		pathname[MAXPGPATH];
  
! 	/* Permission checks */
! 	ace_database_calculate_size(dbOid);
  
  	/* Shared storage in pg_global is not counted */
  
diff -Nrpc base/src/backend/utils/init/postinit.c ace_database/src/backend/utils/init/postinit.c
*** base/src/backend/utils/init/postinit.c	Tue Oct 13 08:36:15 2009
--- ace_database/src/backend/utils/init/postinit.c	Sun Dec 13 14:27:22 2009
***************
*** 36,41 ****
--- 36,42 ----
  #include "pgstat.h"
  #include "postmaster/autovacuum.h"
  #include "postmaster/postmaster.h"
+ #include "security/ace.h"
  #include "storage/bufmgr.h"
  #include "storage/fd.h"
  #include "storage/ipc.h"
*************** CheckMyDatabase(const char *name, bool a
*** 276,292 ****
  					name)));
  
  		/*
! 		 * Check privilege to connect to the database.	(The am_superuser test
! 		 * is redundant, but since we have the flag, might as well check it
! 		 * and save a few cycles.)
  		 */
! 		if (!am_superuser &&
! 			pg_database_aclcheck(MyDatabaseId, GetUserId(),
! 								 ACL_CONNECT) != ACLCHECK_OK)
! 			ereport(FATAL,
! 					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 					 errmsg("permission denied for database \"%s\"", name),
! 					 errdetail("User does not have CONNECT privilege.")));
  
  		/*
  		 * Check connection limit for this database.
--- 277,285 ----
  					name)));
  
  		/*
! 		 * Check privilege to connect to the database.
  		 */
! 		ace_database_connect(MyDatabaseId);
  
  		/*
  		 * Check connection limit for this database.
*************** InitPostgres(const char *in_dbname, Oid 
*** 715,722 ****
  		am_superuser = superuser();
  	}
  
! 	/* set up ACL framework (so CheckMyDatabase can check permissions) */
! 	initialize_acl();
  
  	/* Process pg_db_role_setting options */
  	process_settings(MyDatabaseId, GetSessionUserId());
--- 708,715 ----
  		am_superuser = superuser();
  	}
  
! 	/* Set up ACE security providers */
! 	ace_provider_initialize();
  
  	/* Process pg_db_role_setting options */
  	process_settings(MyDatabaseId, GetSessionUserId());
diff -Nrpc base/src/include/security/ace.h ace_database/src/include/security/ace.h
*** base/src/include/security/ace.h	Thu Jan  1 09:00:00 1970
--- ace_database/src/include/security/ace.h	Sun Dec 13 14:27:22 2009
***************
*** 0 ****
--- 1,39 ----
+ /*
+  * security/ace.h
+  *
+  * Header file of the ACE framework
+  *
+  * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  */
+ #ifndef SECURITY_ACE_H
+ #define SECURITY_ACE_H
+ 
+ #include "utils/acl.h"
+ 
+ /* ace_misc.c */
+ extern void
+ ace_provider_initialize(void);
+ 
+ /* ace_database.c */
+ extern void
+ ace_database_create(const char *datName,
+ 					Oid srcDatOid, bool srcIsTemplate,
+                     Oid datOwner, Oid datTblspc);
+ extern void
+ ace_database_alter(Oid datOid, const char *newName,
+                    Oid newTblspc, Oid newOwner);
+ extern void
+ ace_database_drop(Oid datOid, bool cascade);
+ extern void
+ ace_database_grant(Oid datOid, Oid grantor, AclMode goptions);
+ extern void
+ ace_database_comment(Oid datOid);
+ extern void
+ ace_database_connect(Oid datOid);
+ extern void
+ ace_database_reindex(Oid datOid);
+ extern void
+ ace_database_calculate_size(Oid datOid);
+ 
+ #endif	/* SECURITY_ACE_H */