Update per-column ACLs, not only per-table ACL, when changing table owner.
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 21 Dec 2011 23:23:11 +0000 (18:23 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 21 Dec 2011 23:23:11 +0000 (18:23 -0500)
We forgot to modify column ACLs, so privileges were still shown as having
been granted by the old owner.  This meant that neither the new owner nor
a superuser could revoke the now-untraceable-to-table-owner permissions.
Per bug #6350 from Marc Balmer.

This has been wrong since column ACLs were added, so back-patch to 8.4.

src/backend/commands/tablecmds.c

index 65a28bfb9a72f7945cdef0283f13976b8dbb81bb..8473c9e7f4452d274016bbb3c8962bbd1a275e19 100644 (file)
@@ -357,6 +357,8 @@ static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMOD
 static void ATPostAlterTypeParse(Oid oldId, char *cmd,
                     List **wqueue, LOCKMODE lockmode, bool rewrite);
 static void TryReuseIndex(Oid oldId, IndexStmt *stmt);
+static void change_owner_fix_column_acls(Oid relationOid,
+                            Oid oldOwnerId, Oid newOwnerId);
 static void change_owner_recurse_to_sequences(Oid relationOid,
                                  Oid newOwnerId, LOCKMODE lockmode);
 static void ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode);
@@ -8008,6 +8010,14 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lock
 
        heap_freetuple(newtuple);
 
+       /*
+        * We must similarly update any per-column ACLs to reflect the new
+        * owner; for neatness reasons that's split out as a subroutine.
+        */
+       change_owner_fix_column_acls(relationOid,
+                                    tuple_class->relowner,
+                                    newOwnerId);
+
        /*
         * Update owner dependency reference, if any.  A composite type has
         * none, because it's tracked for the pg_type entry instead of here;
@@ -8064,6 +8074,71 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lock
    relation_close(target_rel, NoLock);
 }
 
+/*
+ * change_owner_fix_column_acls
+ *
+ * Helper function for ATExecChangeOwner.  Scan the columns of the table
+ * and fix any non-null column ACLs to reflect the new owner.
+ */
+static void
+change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
+{
+   Relation    attRelation;
+   SysScanDesc scan;
+   ScanKeyData key[1];
+   HeapTuple   attributeTuple;
+
+   attRelation = heap_open(AttributeRelationId, RowExclusiveLock);
+   ScanKeyInit(&key[0],
+               Anum_pg_attribute_attrelid,
+               BTEqualStrategyNumber, F_OIDEQ,
+               ObjectIdGetDatum(relationOid));
+   scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
+                             true, SnapshotNow, 1, key);
+   while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
+   {
+       Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
+       Datum       repl_val[Natts_pg_attribute];
+       bool        repl_null[Natts_pg_attribute];
+       bool        repl_repl[Natts_pg_attribute];
+       Acl        *newAcl;
+       Datum       aclDatum;
+       bool        isNull;
+       HeapTuple   newtuple;
+
+       /* Ignore dropped columns */
+       if (att->attisdropped)
+           continue;
+
+       aclDatum = heap_getattr(attributeTuple,
+                               Anum_pg_attribute_attacl,
+                               RelationGetDescr(attRelation),
+                               &isNull);
+       /* Null ACLs do not require changes */
+       if (isNull)
+           continue;
+
+       memset(repl_null, false, sizeof(repl_null));
+       memset(repl_repl, false, sizeof(repl_repl));
+
+       newAcl = aclnewowner(DatumGetAclP(aclDatum),
+                            oldOwnerId, newOwnerId);
+       repl_repl[Anum_pg_attribute_attacl - 1] = true;
+       repl_val[Anum_pg_attribute_attacl - 1] = PointerGetDatum(newAcl);
+
+       newtuple = heap_modify_tuple(attributeTuple,
+                                    RelationGetDescr(attRelation),
+                                    repl_val, repl_null, repl_repl);
+
+       simple_heap_update(attRelation, &newtuple->t_self, newtuple);
+       CatalogUpdateIndexes(attRelation, newtuple);
+
+       heap_freetuple(newtuple);
+   }
+   systable_endscan(scan);
+   heap_close(attRelation, RowExclusiveLock);
+}
+
 /*
  * change_owner_recurse_to_sequences
  *