aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2005-07-14 21:46:30 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2005-07-14 21:46:30 +0000
commitaa1110624c08298393dfce996f7b21809d98d3fd (patch)
treeb698817242cc44d7ad14cbd68cb0b96e751e4e13
parentbd157821649ae203b9c8ce8daf4ec027a0003351 (diff)
downloadpostgresql-aa1110624c08298393dfce996f7b21809d98d3fd.tar.gz
postgresql-aa1110624c08298393dfce996f7b21809d98d3fd.zip
Adjust permissions checking for ALTER OWNER commands: instead of
requiring superuserness always, allow an owner to reassign ownership to any role he is a member of, if that role would have the right to create a similar object. These three requirements essentially state that the would-be alterer has enough privilege to DROP the existing object and then re-CREATE it as the new role; so we might as well let him do it in one step. The ALTER TABLESPACE case is a bit squirrely, but the whole concept of non-superuser tablespace owners is pretty dubious anyway. Stephen Frost, code review by Tom Lane.
-rw-r--r--src/backend/commands/aggregatecmds.c22
-rw-r--r--src/backend/commands/conversioncmds.c25
-rw-r--r--src/backend/commands/dbcommands.c53
-rw-r--r--src/backend/commands/functioncmds.c22
-rw-r--r--src/backend/commands/opclasscmds.c22
-rw-r--r--src/backend/commands/operatorcmds.c22
-rw-r--r--src/backend/commands/schemacmds.c80
-rw-r--r--src/backend/commands/tablecmds.c23
-rw-r--r--src/backend/commands/tablespace.c24
-rw-r--r--src/backend/commands/typecmds.c22
-rw-r--r--src/backend/utils/adt/acl.c20
-rw-r--r--src/include/utils/acl.h3
12 files changed, 229 insertions, 109 deletions
diff --git a/src/backend/commands/aggregatecmds.c b/src/backend/commands/aggregatecmds.c
index 6335757981e..e96f328d190 100644
--- a/src/backend/commands/aggregatecmds.c
+++ b/src/backend/commands/aggregatecmds.c
@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/aggregatecmds.c,v 1.27 2005/06/28 05:08:53 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/aggregatecmds.c,v 1.28 2005/07/14 21:46:29 tgl Exp $
*
* DESCRIPTION
* The "DefineFoo" routines take the parse tree and pick out the
@@ -302,6 +302,7 @@ AlterAggregateOwner(List *name, TypeName *basetype, Oid newOwnerId)
HeapTuple tup;
Form_pg_proc procForm;
Relation rel;
+ AclResult aclresult;
/*
* if a basetype is passed in, then attempt to find an aggregate for
@@ -331,11 +332,20 @@ AlterAggregateOwner(List *name, TypeName *basetype, Oid newOwnerId)
*/
if (procForm->proowner != newOwnerId)
{
- /* Otherwise, must be superuser to change object ownership */
- if (!superuser())
- ereport(ERROR,
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- errmsg("must be superuser to change owner")));
+ /* Otherwise, must be owner of the existing object */
+ if (!pg_proc_ownercheck(procOid, GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
+ NameListToString(name));
+
+ /* Must be able to become new owner */
+ check_is_member_of_role(GetUserId(), newOwnerId);
+
+ /* New owner must have CREATE privilege on namespace */
+ aclresult = pg_namespace_aclcheck(procForm->pronamespace, newOwnerId,
+ ACL_CREATE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
+ get_namespace_name(procForm->pronamespace));
/*
* Modify the owner --- okay to scribble on tup because it's a
diff --git a/src/backend/commands/conversioncmds.c b/src/backend/commands/conversioncmds.c
index 9cc72ec7327..81e889aa4ee 100644
--- a/src/backend/commands/conversioncmds.c
+++ b/src/backend/commands/conversioncmds.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/conversioncmds.c,v 1.20 2005/07/07 20:39:58 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/conversioncmds.c,v 1.21 2005/07/14 21:46:29 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -152,8 +152,7 @@ RenameConversion(List *name, const char *newname)
newname, get_namespace_name(namespaceOid))));
/* must be owner */
- if (!superuser() &&
- ((Form_pg_conversion) GETSTRUCT(tup))->conowner != GetUserId())
+ if (!pg_conversion_ownercheck(conversionOid,GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION,
NameListToString(name));
@@ -182,6 +181,7 @@ AlterConversionOwner(List *name, Oid newOwnerId)
HeapTuple tup;
Relation rel;
Form_pg_conversion convForm;
+ AclResult aclresult;
rel = heap_open(ConversionRelationId, RowExclusiveLock);
@@ -206,11 +206,20 @@ AlterConversionOwner(List *name, Oid newOwnerId)
*/
if (convForm->conowner != newOwnerId)
{
- /* Otherwise, must be superuser to change object ownership */
- if (!superuser())
- ereport(ERROR,
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- errmsg("must be superuser to change owner")));
+ /* Otherwise, must be owner of the existing object */
+ if (!pg_conversion_ownercheck(HeapTupleGetOid(tup),GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION,
+ NameListToString(name));
+
+ /* Must be able to become new owner */
+ check_is_member_of_role(GetUserId(), newOwnerId);
+
+ /* New owner must have CREATE privilege on namespace */
+ aclresult = pg_namespace_aclcheck(convForm->connamespace, newOwnerId,
+ ACL_CREATE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
+ get_namespace_name(convForm->connamespace));
/*
* Modify the owner --- okay to scribble on tup because it's a
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index 7f6791abfd3..4e23def8361 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -15,7 +15,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.166 2005/07/08 04:12:24 neilc Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.167 2005/07/14 21:46:29 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -193,23 +193,19 @@ createdb(const CreatedbStmt *stmt)
else
datdba = GetUserId();
- if (is_member_of_role(GetUserId(), datdba))
- {
- /* creating database for self: createdb is required */
- if (!have_createdb_privilege())
- ereport(ERROR,
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- errmsg("permission denied to create database")));
- }
- else
- {
- /* creating database for someone else: must be superuser */
- /* note that the someone else need not have any permissions */
- if (!superuser())
- ereport(ERROR,
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- errmsg("must be superuser to create database for another user")));
- }
+ /*
+ * 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);
/*
* Check for db name conflict. There is a race condition here, since
@@ -930,11 +926,26 @@ AlterDatabaseOwner(const char *dbname, Oid newOwnerId)
bool isNull;
HeapTuple newtuple;
- /* must be superuser to change ownership */
- if (!superuser())
+ /* 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.
+ */
+ if (!have_createdb_privilege())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- errmsg("must be superuser to change owner")));
+ errmsg("permission denied to change owner of database")));
memset(repl_null, ' ', sizeof(repl_null));
memset(repl_repl, ' ', sizeof(repl_repl));
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 8867538f8eb..7e205890c90 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.63 2005/07/07 20:39:58 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.64 2005/07/14 21:46:29 tgl Exp $
*
* DESCRIPTION
* These routines take the parse tree and pick out the
@@ -859,6 +859,7 @@ AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId)
HeapTuple tup;
Form_pg_proc procForm;
Relation rel;
+ AclResult aclresult;
rel = heap_open(ProcedureRelationId, RowExclusiveLock);
@@ -892,11 +893,20 @@ AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId)
bool isNull;
HeapTuple newtuple;
- /* Otherwise, must be superuser to change object ownership */
- if (!superuser())
- ereport(ERROR,
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- errmsg("must be superuser to change owner")));
+ /* Otherwise, must be owner of the existing object */
+ if (!pg_proc_ownercheck(procOid,GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
+ NameListToString(name));
+
+ /* Must be able to become new owner */
+ check_is_member_of_role(GetUserId(), newOwnerId);
+
+ /* New owner must have CREATE privilege on namespace */
+ aclresult = pg_namespace_aclcheck(procForm->pronamespace, newOwnerId,
+ ACL_CREATE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
+ get_namespace_name(procForm->pronamespace));
memset(repl_null, ' ', sizeof(repl_null));
memset(repl_repl, ' ', sizeof(repl_repl));
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
index a0d0abd553d..c9d11da70eb 100644
--- a/src/backend/commands/opclasscmds.c
+++ b/src/backend/commands/opclasscmds.c
@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/opclasscmds.c,v 1.34 2005/07/07 20:39:58 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/opclasscmds.c,v 1.35 2005/07/14 21:46:29 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -892,6 +892,7 @@ AlterOpClassOwner(List *name, const char *access_method, Oid newOwnerId)
char *opcname;
HeapTuple tup;
Relation rel;
+ AclResult aclresult;
Form_pg_opclass opcForm;
amOid = GetSysCacheOid(AMNAME,
@@ -950,11 +951,20 @@ AlterOpClassOwner(List *name, const char *access_method, Oid newOwnerId)
*/
if (opcForm->opcowner != newOwnerId)
{
- /* Otherwise, must be superuser to change object ownership */
- if (!superuser())
- ereport(ERROR,
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- errmsg("must be superuser to change owner")));
+ /* Otherwise, must be owner of the existing object */
+ if (!pg_opclass_ownercheck(HeapTupleGetOid(tup),GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPCLASS,
+ NameListToString(name));
+
+ /* Must be able to become new owner */
+ check_is_member_of_role(GetUserId(), newOwnerId);
+
+ /* New owner must have CREATE privilege on namespace */
+ aclresult = pg_namespace_aclcheck(namespaceOid, newOwnerId,
+ ACL_CREATE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
+ get_namespace_name(namespaceOid));
/*
* Modify the owner --- okay to scribble on tup because it's a
diff --git a/src/backend/commands/operatorcmds.c b/src/backend/commands/operatorcmds.c
index ae93e9230ee..d36ac6acd0b 100644
--- a/src/backend/commands/operatorcmds.c
+++ b/src/backend/commands/operatorcmds.c
@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/operatorcmds.c,v 1.23 2005/07/07 20:39:58 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/operatorcmds.c,v 1.24 2005/07/14 21:46:29 tgl Exp $
*
* DESCRIPTION
* The "DefineFoo" routines take the parse tree and pick out the
@@ -274,6 +274,7 @@ AlterOperatorOwner(List *name, TypeName *typeName1, TypeName *typeName2,
Oid operOid;
HeapTuple tup;
Relation rel;
+ AclResult aclresult;
Form_pg_operator oprForm;
rel = heap_open(OperatorRelationId, RowExclusiveLock);
@@ -295,11 +296,20 @@ AlterOperatorOwner(List *name, TypeName *typeName1, TypeName *typeName2,
*/
if (oprForm->oprowner != newOwnerId)
{
- /* Otherwise, must be superuser to change object ownership */
- if (!superuser())
- ereport(ERROR,
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- errmsg("must be superuser to change owner")));
+ /* Otherwise, must be owner of the existing object */
+ if (!pg_oper_ownercheck(operOid,GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER,
+ NameListToString(name));
+
+ /* Must be able to become new owner */
+ check_is_member_of_role(GetUserId(), newOwnerId);
+
+ /* New owner must have CREATE privilege on namespace */
+ aclresult = pg_namespace_aclcheck(oprForm->oprnamespace, newOwnerId,
+ ACL_CREATE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
+ get_namespace_name(oprForm->oprnamespace));
/*
* Modify the owner --- okay to scribble on tup because it's a
diff --git a/src/backend/commands/schemacmds.c b/src/backend/commands/schemacmds.c
index c4c20ddeb39..65a6edeabc7 100644
--- a/src/backend/commands/schemacmds.c
+++ b/src/backend/commands/schemacmds.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/schemacmds.c,v 1.32 2005/07/07 20:39:58 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/schemacmds.c,v 1.33 2005/07/14 21:46:29 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -49,54 +49,45 @@ CreateSchemaCommand(CreateSchemaStmt *stmt)
saved_uid = GetUserId();
/*
- * Figure out user identities.
+ * Who is supposed to own the new schema?
*/
-
- if (!authId)
- {
- owner_uid = saved_uid;
- }
- else if (superuser())
- {
+ if (authId)
owner_uid = get_roleid_checked(authId);
-
- /*
- * Set the current user to the requested authorization so that
- * objects created in the statement have the requested owner.
- * (This will revert to session user on error or at the end of
- * this routine.)
- */
- SetUserId(owner_uid);
- }
else
- {
- const char *owner_name;
-
- /* not superuser */
owner_uid = saved_uid;
- owner_name = GetUserNameFromId(owner_uid);
- if (strcmp(authId, owner_name) != 0)
- ereport(ERROR,
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- errmsg("permission denied"),
- errdetail("\"%s\" is not a superuser, so cannot create a schema for \"%s\"",
- owner_name, authId)));
- }
/*
- * Permissions checks.
+ * To create a schema, must have schema-create privilege on the current
+ * database and must be able to become the target role (this does not
+ * imply that the target role itself must have create-schema privilege).
+ * The latter provision guards against "giveaway" attacks. Note that
+ * a superuser will always have both of these privileges a fortiori.
*/
aclresult = pg_database_aclcheck(MyDatabaseId, saved_uid, ACL_CREATE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, ACL_KIND_DATABASE,
get_database_name(MyDatabaseId));
+ check_is_member_of_role(saved_uid, owner_uid);
+
+ /* Additional check to protect reserved schema names */
if (!allowSystemTableMods && IsReservedName(schemaName))
ereport(ERROR,
(errcode(ERRCODE_RESERVED_NAME),
errmsg("unacceptable schema name \"%s\"", schemaName),
errdetail("The prefix \"pg_\" is reserved for system schemas.")));
+ /*
+ * If the requested authorization is different from the current user,
+ * temporarily set the current user so that the object(s) will be
+ * created with the correct ownership.
+ *
+ * (The setting will revert to session user on error or at the end of
+ * this routine.)
+ */
+ if (saved_uid != owner_uid)
+ SetUserId(owner_uid);
+
/* Create the schema's namespace */
namespaceId = NamespaceCreate(schemaName, owner_uid);
@@ -308,12 +299,29 @@ AlterSchemaOwner(const char *name, Oid newOwnerId)
Datum aclDatum;
bool isNull;
HeapTuple newtuple;
+ AclResult aclresult;
+
+ /* Otherwise, must be owner of the existing object */
+ if (!pg_namespace_ownercheck(HeapTupleGetOid(tup),GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE,
+ name);
+
+ /* Must be able to become new owner */
+ check_is_member_of_role(GetUserId(),newOwnerId);
- /* Otherwise, must be superuser to change object ownership */
- if (!superuser())
- ereport(ERROR,
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- errmsg("must be superuser to change owner")));
+ /*
+ * must have create-schema rights
+ *
+ * NOTE: This is different from other alter-owner checks in
+ * that the current user is checked for create privileges
+ * instead of the destination owner. This is consistent
+ * with the CREATE case for schemas.
+ */
+ aclresult = pg_database_aclcheck(MyDatabaseId, GetUserId(),
+ ACL_CREATE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, ACL_KIND_DATABASE,
+ get_database_name(MyDatabaseId));
memset(repl_null, ' ', sizeof(repl_null));
memset(repl_repl, ' ', sizeof(repl_repl));
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 2b50a402fc8..c06c272cbdd 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.163 2005/07/07 20:39:58 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.164 2005/07/14 21:46:29 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -5286,12 +5286,23 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId)
Datum aclDatum;
bool isNull;
HeapTuple newtuple;
+ Oid namespaceOid = tuple_class->relnamespace;
+ AclResult aclresult;
- /* Otherwise, check that we are the superuser */
- if (!superuser())
- ereport(ERROR,
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- errmsg("must be superuser to change owner")));
+ /* Otherwise, must be owner of the existing object */
+ if (!pg_class_ownercheck(relationOid,GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
+ RelationGetRelationName(target_rel));
+
+ /* Must be able to become new owner */
+ check_is_member_of_role(GetUserId(), newOwnerId);
+
+ /* New owner must have CREATE privilege on namespace */
+ aclresult = pg_namespace_aclcheck(namespaceOid, newOwnerId,
+ ACL_CREATE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
+ get_namespace_name(namespaceOid));
memset(repl_null, ' ', sizeof(repl_null));
memset(repl_repl, ' ', sizeof(repl_repl));
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c
index a2cce14335c..06c939c2d5c 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -37,7 +37,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/tablespace.c,v 1.25 2005/07/07 20:39:58 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/tablespace.c,v 1.26 2005/07/14 21:46:29 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -788,11 +788,23 @@ AlterTableSpaceOwner(const char *name, Oid newOwnerId)
bool isNull;
HeapTuple newtuple;
- /* Otherwise, must be superuser to change object ownership */
- if (!superuser())
- ereport(ERROR,
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- errmsg("must be superuser to change owner")));
+ /* Otherwise, must be owner of the existing object */
+ if (!pg_tablespace_ownercheck(HeapTupleGetOid(tup), GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TABLESPACE,
+ name);
+
+ /* Must be able to become new owner */
+ check_is_member_of_role(GetUserId(), newOwnerId);
+
+ /*
+ * Normally we would also check for create permissions here,
+ * but there are none for tablespaces so we follow what rename
+ * tablespace does and omit the create permissions check.
+ *
+ * NOTE: Only superusers may create tablespaces to begin with and
+ * so initially only a superuser would be able to change its
+ * ownership anyway.
+ */
memset(repl_null, ' ', sizeof(repl_null));
memset(repl_repl, ' ', sizeof(repl_repl));
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 7406e15376a..0979e8aeec2 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.75 2005/07/10 21:13:58 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.76 2005/07/14 21:46:29 tgl Exp $
*
* DESCRIPTION
* The "DefineFoo" routines take the parse tree and pick out the
@@ -2025,6 +2025,7 @@ AlterTypeOwner(List *names, Oid newOwnerId)
Relation rel;
HeapTuple tup;
Form_pg_type typTup;
+ AclResult aclresult;
/* Make a TypeName so we can use standard type lookup machinery */
typename = makeNode(TypeName);
@@ -2067,11 +2068,20 @@ AlterTypeOwner(List *names, Oid newOwnerId)
*/
if (typTup->typowner != newOwnerId)
{
- /* Otherwise, must be superuser to change object ownership */
- if (!superuser())
- ereport(ERROR,
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- errmsg("must be superuser to change owner")));
+ /* Otherwise, must be owner of the existing object */
+ if (!pg_type_ownercheck(HeapTupleGetOid(tup),GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
+ TypeNameToString(typename));
+
+ /* Must be able to become new owner */
+ check_is_member_of_role(GetUserId(), newOwnerId);
+
+ /* New owner must have CREATE privilege on namespace */
+ aclresult = pg_namespace_aclcheck(typTup->typnamespace, newOwnerId,
+ ACL_CREATE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
+ get_namespace_name(typTup->typnamespace));
/*
* Modify the owner --- okay to scribble on typTup because it's a
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index a416def8155..3fedb33a8a5 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/adt/acl.c,v 1.118 2005/07/07 20:39:58 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/acl.c,v 1.119 2005/07/14 21:46:30 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -2541,6 +2541,10 @@ is_member_of_role(Oid member, Oid role)
if (member == role)
return true;
+ /* Superusers have every privilege, so are part of every role */
+ if (superuser_arg(member))
+ return true;
+
/* If cache is already valid, just use the list */
if (OidIsValid(cached_role) && cached_role == member)
return list_member_oid(cached_memberships, role);
@@ -2604,6 +2608,20 @@ is_member_of_role(Oid member, Oid role)
return list_member_oid(cached_memberships, role);
}
+/*
+ * check_is_member_of_role
+ * is_member_of_role with a standard permission-violation error if not
+ */
+void
+check_is_member_of_role(Oid member, Oid role)
+{
+ if (!is_member_of_role(member, role))
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be member of role \"%s\"",
+ GetUserNameFromId(role))));
+}
+
/*
* Is member an admin of role (directly or indirectly)? That is, is it
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index 7defb4e1e28..d3ef0031985 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/utils/acl.h,v 1.81 2005/07/07 20:40:00 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/utils/acl.h,v 1.82 2005/07/14 21:46:30 tgl Exp $
*
* NOTES
* An ACL array is simply an array of AclItems, representing the union
@@ -212,6 +212,7 @@ extern int aclmembers(const Acl *acl, Oid **roleids);
extern bool is_member_of_role(Oid member, Oid role);
extern bool is_admin_of_role(Oid member, Oid role);
+extern void check_is_member_of_role(Oid member, Oid role);
extern void initialize_acl(void);