schema: Allow schemaUpdateNow to refresh schema during a transaction
authorGarming Sam <garming@catalyst.net.nz>
Fri, 18 Aug 2017 01:59:30 +0000 (13:59 +1200)
committerAndrew Bartlett <abartlet@samba.org>
Thu, 14 Dec 2017 07:20:15 +0000 (08:20 +0100)
When we upgrade a schema from 2008R2 to 2012R2, we want to apply all the
changes in a single transaction - if we can't apply all the updates then
we don't want to be left with a schema halfway in between the two.

However, as we apply each LDIF update, we also want to refresh the
schema. There are 2 reasons for this:
1. The adprep .LDIF files provided by Microsoft have some writes to
schemaUpdateNow in them.
2. Microsoft uses attribute OIDs in their adprep .LDIF files, which
Samba doesn't handle so well. However, we can replace the OIDs with the
attribute's ldapDisplayName and they work fine. But to do this, we need
to query the schema to map the OID to attribute name. And to query the
schema successfully, the schema needs to be refreshed after the new
attribute object has been added.

Basically this patch avoids bailing out during the dsdb_schema_refresh()
if we are writing schemaUpdateNow as part of a larger transaction.

Pair-programmed-with: Garming Sam <garming@catalyst.net.nz>

Signed-off-by: Tim Beale <timbeale@catalyst.net.nz>
Signed-off-by: Garming Sam <garming@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
source4/dsdb/samdb/ldb_modules/rootdse.c
source4/dsdb/samdb/ldb_modules/schema_load.c

index d1a4409b602fb6c00c6ae8744bdf1866042c9b79..0d486218c0ba5d10c8ac0f42ac66fa72a6f39621 100644 (file)
@@ -1306,12 +1306,28 @@ static int rootdse_schemaupdatenow(struct ldb_module *module, struct ldb_request
                return ldb_next_request(module, req);
        }
 
+       /*
+        * schemaUpdateNow has been requested. Allow this to refresh the schema
+        * even if we're currently in the middle of a transaction
+        */
+       ret = ldb_set_opaque(ldb, "dsdb_schema_refresh_expected", (void *)1);
+       if (ret != LDB_SUCCESS) {
+               return ldb_operr(ldb);
+       }
+
        ret = ldb_extended(ldb, DSDB_EXTENDED_SCHEMA_UPDATE_NOW_OID, schema_dn, &ext_res);
        if (ret != LDB_SUCCESS) {
+               ldb_set_opaque(ldb, "dsdb_schema_refresh_expected", (void *)0);
                return ldb_operr(ldb);
        }
 
        talloc_free(ext_res);
+
+       ret = ldb_set_opaque(ldb, "dsdb_schema_refresh_expected", (void *)0);
+       if (ret != LDB_SUCCESS) {
+               return ldb_operr(ldb);
+       }
+
        return ldb_module_done(req, NULL, NULL, ret);
 }
 
index 4013cdf6364ccd5a1d3682a54f702b69c42dad14..0bbd5fc60f03fa9839b663712f5e52728e1ba82f 100644 (file)
@@ -193,9 +193,17 @@ static struct dsdb_schema *dsdb_schema_refresh(struct ldb_module *module, struct
                return schema;
        }
 
-       /* We don't allow a schema reload during a transaction - nobody else can modify our schema behind our backs */
        if (private_data->in_transaction) {
-               return schema;
+
+               /*
+                * If the refresh is not an expected part of a larger
+                * transaction, then we don't allow a schema reload during a
+                * transaction. This stops others from modifying our schema
+                * behind our backs
+                */
+               if (ldb_get_opaque(ldb, "dsdb_schema_refresh_expected") != (void *)1) {
+                       return schema;
+               }
        }
 
        SMB_ASSERT(ev == ldb_get_event_context(ldb));