drepl: schema repl race condition fix
authorAaron Haslett <aaronhaslett@catalyst.net.nz>
Wed, 21 Nov 2018 00:55:53 +0000 (13:55 +1300)
committerAndrew Bartlett <abartlet@samba.org>
Mon, 17 Dec 2018 00:24:15 +0000 (01:24 +0100)
Adds final schema consistency check before committing changes.
Aborts if corruption found.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12889
Signed-off-by: Aaron Haslett <aaronhaslett@catalyst.net.nz>
Reviewed-by: Gary Lockyer <gary@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
source4/dsdb/repl/replicated_objects.c
source4/dsdb/samdb/ldb_modules/schema_load.c
source4/dsdb/samdb/samdb.h

index fd567e9..372fb2d 100644 (file)
@@ -914,8 +914,10 @@ WERROR dsdb_replicated_objects_commit(struct ldb_context *ldb,
        }
        talloc_free(ext_res);
 
-       /* Save our updated prefixMap */
+       /* Save our updated prefixMap and check the schema is good. */
        if (working_schema) {
+               struct ldb_result *ext_res_2;
+
                werr = dsdb_write_prefixes_from_schema_to_ldb(working_schema,
                                                              ldb,
                                                              working_schema);
@@ -924,7 +926,9 @@ WERROR dsdb_replicated_objects_commit(struct ldb_context *ldb,
                        if (used_global_schema) { 
                                dsdb_set_global_schema(ldb);
                        } else if (cur_schema ) {
-                               dsdb_reference_schema(ldb, cur_schema, SCHEMA_MEMORY_ONLY);
+                               dsdb_reference_schema(ldb,
+                                                     cur_schema,
+                                                     SCHEMA_MEMORY_ONLY);
                        }
                        DEBUG(0,("Failed to save updated prefixMap: %s\n",
                                 win_errstr(werr)));
@@ -932,6 +936,33 @@ WERROR dsdb_replicated_objects_commit(struct ldb_context *ldb,
                        TALLOC_FREE(tmp_ctx);
                        return werr;
                }
+
+               /*
+                * Use dsdb_schema_from_db through dsdb extended to check we
+                * can load the schema currently sitting in the transaction.
+                * We need this check because someone might have written to
+                * the schema or prefixMap before we started the transaction,
+                * which may have caused corruption.
+                */
+               ret = ldb_extended(ldb, DSDB_EXTENDED_SCHEMA_LOAD,
+                                  NULL, &ext_res_2);
+
+               if (ret != LDB_SUCCESS) {
+                       if (used_global_schema) {
+                               dsdb_set_global_schema(ldb);
+                       } else if (cur_schema) {
+                               dsdb_reference_schema(ldb, cur_schema, SCHEMA_MEMORY_ONLY);
+                       }
+                       DEBUG(0,("Corrupt schema write attempt detected, "
+                                "aborting schema modification operation.\n"
+                                "This probably happened due to bad timing of "
+                                "another schema edit: %s (%s)\n",
+                                ldb_errstring(ldb),
+                                ldb_strerror(ret)));
+                       ldb_transaction_cancel(ldb);
+                       TALLOC_FREE(tmp_ctx);
+                       return WERR_FOOBAR;
+               }
        }
 
        ret = ldb_transaction_prepare_commit(ldb);
index d9396c9..473a2e0 100644 (file)
@@ -552,26 +552,35 @@ static int schema_load_extended(struct ldb_module *module, struct ldb_request *r
        struct dsdb_schema *schema;
        int ret;
 
-       if (strcmp(req->op.extended.oid, DSDB_EXTENDED_SCHEMA_UPDATE_NOW_OID) != 0) {
-               return ldb_next_request(module, req);
-       }
-       /* Force a refresh */
-       schema = dsdb_get_schema(ldb, NULL);
+       if (strcmp(req->op.extended.oid, DSDB_EXTENDED_SCHEMA_LOAD) == 0) {
 
-       ret = dsdb_schema_set_indices_and_attributes(ldb,
-                                                    schema,
-                                                    SCHEMA_WRITE);
-
-       if (ret != LDB_SUCCESS) {
-               ldb_asprintf_errstring(ldb, "Failed to write new "
-                                      "@INDEXLIST and @ATTRIBUTES "
-                                      "records for updated schema: %s",
-                                      ldb_errstring(ldb));
+               ret = dsdb_schema_from_db(module, req, 0, &schema);
+               if (ret == LDB_SUCCESS) {
+                       return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
+               }
                return ret;
+
+       } else if (strcmp(req->op.extended.oid, DSDB_EXTENDED_SCHEMA_UPDATE_NOW_OID) == 0) {
+               /* Force a refresh */
+               schema = dsdb_get_schema(ldb, NULL);
+
+               ret = dsdb_schema_set_indices_and_attributes(ldb,
+                                                            schema,
+                                                            SCHEMA_WRITE);
+
+               if (ret != LDB_SUCCESS) {
+                       ldb_asprintf_errstring(ldb, "Failed to write new "
+                                              "@INDEXLIST and @ATTRIBUTES "
+                                              "records for updated schema: %s",
+                                              ldb_errstring(ldb));
+                       return ret;
+               }
+
+               return ldb_next_request(module, req);
+       } else {
+               /* Pass to next module, the partition one should finish the chain */
+               return ldb_next_request(module, req);
        }
-       
-       /* Pass to next module, the partition one should finish the chain */
-       return ldb_next_request(module, req);
 }
 
 static int schema_read_lock(struct ldb_module *module)
index e1b0e4a..0835ff6 100644 (file)
@@ -272,6 +272,8 @@ struct dsdb_create_partition_exop {
  */
 #define DSDB_EXTENDED_SCHEMA_UPDATE_NOW_OID "1.3.6.1.4.1.7165.4.4.2"
 
+#define DSDB_EXTENDED_SCHEMA_LOAD "1.3.6.1.4.1.7165.4.4.10"
+
 #define DSDB_EXTENDED_SCHEMA_UPGRADE_IN_PROGRESS_OID "1.3.6.1.4.1.7165.4.4.6"
 
 #define DSDB_OPENLDAP_DEREFERENCE_CONTROL "1.3.6.1.4.1.4203.666.5.16"