]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
dsdb: Add routine to check the DB vs lp functional levels
authorAndrew Bartlett <abartlet@samba.org>
Wed, 31 May 2023 02:33:08 +0000 (14:33 +1200)
committerAndrew Bartlett <abartlet@samba.org>
Wed, 14 Jun 2023 22:57:34 +0000 (22:57 +0000)
This will be called at server startup (as well as from Python tests)

Signed-off-by: Andrew Bartlett <abartlet@samba.org>
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
source4/dsdb/common/util.c
source4/dsdb/pydsdb.c

index 38ad7eaf1fd861362fc96d632746d00848a2be6e..050ec42ea8c53d1074ec9ed0584533ead21ad20a 100644 (file)
@@ -3994,6 +3994,123 @@ int dsdb_dc_functional_level(struct ldb_context *ldb)
        return *dcFunctionality;
 }
 
+int dsdb_check_and_update_fl(struct ldb_context *ldb_ctx, struct loadparm_context *lp_ctx)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       int ret;
+
+       int db_dc_functional_level;
+       int db_domain_functional_level;
+       int db_forest_functional_level;
+       int lp_dc_functional_level = lpcfg_ad_dc_functional_level(lp_ctx);
+       bool am_rodc;
+       struct ldb_message *msg = NULL;
+       struct ldb_dn *dc_ntds_settings_dn = NULL;
+
+
+       db_dc_functional_level = dsdb_dc_functional_level(ldb_ctx);
+       db_domain_functional_level = dsdb_functional_level(ldb_ctx);
+       db_forest_functional_level = dsdb_forest_functional_level(ldb_ctx);
+
+       if (lp_dc_functional_level < db_domain_functional_level) {
+               DBG_ERR("Refusing to start as smb.conf 'ad dc functional level' maps to %d, "
+                       "which is less than the domain functional level of %d\n",
+                       lp_dc_functional_level, db_domain_functional_level);
+               TALLOC_FREE(frame);
+               return LDB_ERR_CONSTRAINT_VIOLATION;
+       }
+
+       if (lp_dc_functional_level < db_forest_functional_level) {
+               DBG_ERR("Refusing to start as smb.conf 'ad dc functional level' maps to %d, "
+                       "which is less than the forest functional level of %d\n",
+                       lp_dc_functional_level, db_forest_functional_level);
+               TALLOC_FREE(frame);
+               return LDB_ERR_CONSTRAINT_VIOLATION;
+       }
+
+       /* Check if we need to update the DB */
+       if (db_dc_functional_level == lp_dc_functional_level) {
+               TALLOC_FREE(frame);
+               return LDB_SUCCESS;
+       }
+
+       /* Confirm we are not an RODC before we try a modify */
+       ret = samdb_rodc(ldb_ctx, &am_rodc);
+       if (ret != LDB_SUCCESS) {
+               DBG_ERR("Failed to determine if this server is an RODC\n");
+               TALLOC_FREE(frame);
+               return ret;
+       }
+
+       if (am_rodc) {
+               DBG_WARNING("Unable to update DC's msDS-Behavior-Version "
+                           "to correct value (%d from %d) as we are an RODC\n",
+                           db_forest_functional_level, lp_dc_functional_level);
+               TALLOC_FREE(frame);
+               return LDB_SUCCESS;
+       }
+
+       dc_ntds_settings_dn = samdb_ntds_settings_dn(ldb_ctx, frame);
+
+       if (dc_ntds_settings_dn == NULL) {
+               DBG_ERR("Failed to find own NTDS Settings DN\n");
+               TALLOC_FREE(frame);
+               return LDB_ERR_NO_SUCH_OBJECT;
+       }
+
+       /* Now update our msDS-Behavior-Version */
+
+       msg = ldb_msg_new(frame);
+       if (msg == NULL) {
+               DBG_ERR("Failed to allocate message to update msDS-Behavior-Version\n");
+               TALLOC_FREE(frame);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       msg->dn = dc_ntds_settings_dn;
+
+       ret = samdb_msg_add_int(ldb_ctx, frame, msg, "msDS-Behavior-Version", lp_dc_functional_level);
+       if (ret != LDB_SUCCESS) {
+               DBG_ERR("Failed to set new msDS-Behavior-Version on message\n");
+               TALLOC_FREE(frame);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       ret = dsdb_replace(ldb_ctx, msg, 0);
+       if (ret != LDB_SUCCESS) {
+               DBG_ERR("Failed to update DB with new msDS-Behavior-Version on %s: %s\n",
+                       ldb_dn_get_linearized(dc_ntds_settings_dn),
+                       ldb_errstring(ldb_ctx));
+               TALLOC_FREE(frame);
+               return ret;
+       }
+
+       /*
+        * We have to update the opaque because this particular ldb_context
+        * will not re-read the DB
+        */
+       {
+               int *val = talloc(ldb_ctx, int);
+               if (!val) {
+                       TALLOC_FREE(frame);
+                       return LDB_ERR_OPERATIONS_ERROR;
+               }
+               *val = lp_dc_functional_level;
+               ret = ldb_set_opaque(ldb_ctx,
+                                    "domainControllerFunctionality", val);
+               if (ret != LDB_SUCCESS) {
+                       DBG_ERR("Failed to re-set domainControllerFunctionality opaque\n");
+                       TALLOC_FREE(val);
+                       TALLOC_FREE(frame);
+                       return ret;
+               }
+       }
+
+       TALLOC_FREE(frame);
+       return LDB_SUCCESS;
+}
+
+
 /*
   set a GUID in an extended DN structure
  */
index 1f2fa7c4be7f71acd46d282c7191a56a7411a4ae..22506423d492bd36948bc12448a77582731f12a8 100644 (file)
@@ -1426,6 +1426,38 @@ static PyObject *py_dsdb_user_account_control_flag_bit_to_string(PyObject *self,
        return PyUnicode_FromString(str);
 }
 
+static PyObject *py_dsdb_check_and_update_fl(PyObject *self, PyObject *args)
+{
+       TALLOC_CTX *frame = NULL;
+
+       PyObject *py_ldb = NULL, *py_lp = NULL;
+       struct ldb_context *ldb = NULL;
+       struct loadparm_context *lp_ctx = NULL;
+
+       int ret;
+
+       if (!PyArg_ParseTuple(args, "OO", &py_ldb, &py_lp)) {
+               return NULL;
+       }
+
+       PyErr_LDB_OR_RAISE(py_ldb, ldb);
+
+       frame = talloc_stackframe();
+
+       lp_ctx = lpcfg_from_py_object(frame, py_lp);
+       if (lp_ctx == NULL) {
+               TALLOC_FREE(frame);
+               return NULL;
+       }
+
+       ret = dsdb_check_and_update_fl(ldb, lp_ctx);
+       TALLOC_FREE(frame);
+
+       PyErr_LDB_ERROR_IS_ERR_RAISE(py_ldb_get_exception(), ret, ldb);
+
+       Py_RETURN_NONE;
+}
+
 static PyMethodDef py_dsdb_methods[] = {
        { "_samdb_server_site_name", (PyCFunction)py_samdb_server_site_name,
                METH_VARARGS, "Get the server site name as a string"},
@@ -1512,6 +1544,12 @@ static PyMethodDef py_dsdb_methods[] = {
                METH_VARARGS,
                "user_account_control_flag_bit_to_string(bit)"
                 " -> string name" },
+       { "check_and_update_fl",
+               (PyCFunction)py_dsdb_check_and_update_fl,
+               METH_VARARGS,
+               "check_and_update_fl(ldb, lp) -> None\n"
+         "Hook to run in testing the code run on samba server startup "
+         "to validate and update DC functional levels"},
        {0}
 };