s4:dsdb: Implement DSDB_SEARCH_UPDATE_MANAGED_PASSWORDS search flag
authorJo Sutton <josutton@catalyst.net.nz>
Tue, 9 Apr 2024 04:24:43 +0000 (16:24 +1200)
committerJo Sutton <jsutton@samba.org>
Sun, 21 Apr 2024 22:10:36 +0000 (22:10 +0000)
View with ‘git show -b’.

Signed-off-by: Jo Sutton <josutton@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
source4/dsdb/common/util.c

index 7cdfca8da198129f40ff05379db99fd2b932ea64..d3fb3024db0dc5bf07ec58727591c1a2315df56b 100644 (file)
@@ -36,6 +36,8 @@
 #include "param/param.h"
 #include "librpc/gen_ndr/ndr_drsblobs.h"
 #include "dsdb/common/util.h"
+#include "dsdb/gmsa/gkdi.h"
+#include "dsdb/gmsa/util.h"
 #include "lib/socket/socket.h"
 #include "librpc/gen_ndr/irpc.h"
 #include "libds/common/flag_mapping.h"
@@ -5634,6 +5636,8 @@ int dsdb_search(struct ldb_context *ldb,
        va_list ap;
        char *expression = NULL;
        TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+       int tries;
+       const int max_tries = 5;
 
        /* cross-partitions searches with a basedn break multi-domain support */
        SMB_ASSERT(basedn == NULL || (dsdb_flags & DSDB_SEARCH_SEARCH_ALL_PARTITIONS) == 0);
@@ -5642,7 +5646,7 @@ int dsdb_search(struct ldb_context *ldb,
                return ldb_oom(ldb);
        }
 
-       res = talloc_zero(tmp_ctx, struct ldb_result);
+       res = talloc(tmp_ctx, struct ldb_result);
        if (!res) {
                talloc_free(tmp_ctx);
                return ldb_oom(ldb);
@@ -5659,70 +5663,111 @@ int dsdb_search(struct ldb_context *ldb,
                }
        }
 
-       ret = ldb_build_search_req(&req, ldb, tmp_ctx,
-                                  basedn,
-                                  scope,
-                                  expression,
-                                  attrs,
-                                  NULL,
-                                  res,
-                                  ldb_search_default_callback,
-                                  NULL);
-       if (ret != LDB_SUCCESS) {
-               talloc_free(tmp_ctx);
-               return ret;
-       }
-
-       ret = dsdb_request_add_controls(req, dsdb_flags);
-       if (ret != LDB_SUCCESS) {
-               talloc_free(tmp_ctx);
-               ldb_reset_err_string(ldb);
-               return ret;
-       }
+       for (tries = 0; tries < max_tries; ++tries) {
+               bool retry = true;
 
-       ret = ldb_request(ldb, req);
-       if (ret == LDB_SUCCESS) {
-               ret = ldb_wait(req->handle, LDB_WAIT_ALL);
-       }
+               *res = (struct ldb_result){};
 
-       if (ret != LDB_SUCCESS) {
-               DBG_INFO("%s flags=0x%08x %s %s -> %s (%s)\n",
-                        dsdb_search_scope_as_string(scope),
-                        dsdb_flags,
-                        basedn?ldb_dn_get_extended_linearized(tmp_ctx,
-                                                              basedn,
-                                                              1):"NULL",
-                        expression?expression:"NULL",
-                        ldb_errstring(ldb), ldb_strerror(ret));
-               talloc_free(tmp_ctx);
-               return ret;
-       }
+               ret = ldb_build_search_req(&req, ldb, tmp_ctx,
+                                          basedn,
+                                          scope,
+                                          expression,
+                                          attrs,
+                                          NULL,
+                                          res,
+                                          ldb_search_default_callback,
+                                          NULL);
+               if (ret != LDB_SUCCESS) {
+                       talloc_free(tmp_ctx);
+                       return ret;
+               }
 
-       if (dsdb_flags & DSDB_SEARCH_ONE_ONLY) {
-               if (res->count == 0) {
-                       DBG_INFO("%s SEARCH_ONE_ONLY flags=0x%08x %s %s -> %u results\n",
-                                dsdb_search_scope_as_string(scope),
-                                dsdb_flags,
-                                basedn?ldb_dn_get_extended_linearized(tmp_ctx,
-                                                                      basedn,
-                                                                      1):"NULL",
-                                expression?expression:"NULL", res->count);
+               ret = dsdb_request_add_controls(req, dsdb_flags);
+               if (ret != LDB_SUCCESS) {
                        talloc_free(tmp_ctx);
                        ldb_reset_err_string(ldb);
-                       return ldb_error(ldb, LDB_ERR_NO_SUCH_OBJECT, __func__);
+                       return ret;
+               }
+
+               ret = ldb_request(ldb, req);
+               if (ret == LDB_SUCCESS) {
+                       ret = ldb_wait(req->handle, LDB_WAIT_ALL);
                }
-               if (res->count != 1) {
-                       DBG_INFO("%s SEARCH_ONE_ONLY flags=0x%08x %s %s -> %u (expected 1) results\n",
+
+               if (ret != LDB_SUCCESS) {
+                       DBG_INFO("%s flags=0x%08x %s %s -> %s (%s)\n",
                                 dsdb_search_scope_as_string(scope),
                                 dsdb_flags,
                                 basedn?ldb_dn_get_extended_linearized(tmp_ctx,
                                                                       basedn,
                                                                       1):"NULL",
-                                expression?expression:"NULL", res->count);
+                                expression?expression:"NULL",
+                                ldb_errstring(ldb), ldb_strerror(ret));
                        talloc_free(tmp_ctx);
-                       ldb_reset_err_string(ldb);
-                       return LDB_ERR_CONSTRAINT_VIOLATION;
+                       return ret;
+               }
+
+               if (dsdb_flags & DSDB_SEARCH_ONE_ONLY) {
+                       if (res->count == 0) {
+                               DBG_INFO("%s SEARCH_ONE_ONLY flags=0x%08x %s %s -> %u results\n",
+                                        dsdb_search_scope_as_string(scope),
+                                        dsdb_flags,
+                                        basedn?ldb_dn_get_extended_linearized(tmp_ctx,
+                                                                              basedn,
+                                                                              1):"NULL",
+                                        expression?expression:"NULL", res->count);
+                               talloc_free(tmp_ctx);
+                               ldb_reset_err_string(ldb);
+                               return ldb_error(ldb, LDB_ERR_NO_SUCH_OBJECT, __func__);
+                       }
+                       if (res->count != 1) {
+                               DBG_INFO("%s SEARCH_ONE_ONLY flags=0x%08x %s %s -> %u (expected 1) results\n",
+                                        dsdb_search_scope_as_string(scope),
+                                        dsdb_flags,
+                                        basedn?ldb_dn_get_extended_linearized(tmp_ctx,
+                                                                              basedn,
+                                                                              1):"NULL",
+                                        expression?expression:"NULL", res->count);
+                               talloc_free(tmp_ctx);
+                               ldb_reset_err_string(ldb);
+                               return LDB_ERR_CONSTRAINT_VIOLATION;
+                       }
                }
+
+               if (!(dsdb_flags & DSDB_SEARCH_UPDATE_MANAGED_PASSWORDS)) {
+                       break;
+               }
+
+               /*
+                * If we’re searching for passwords, we must account for the
+                * possibility that one or more of the accounts are Group
+                * Managed Service Accounts with out‐of‐date keys. In such a
+                * case, we must derive the new password(s), update the keys,
+                * and perform the search again to get the updated results.
+                *
+                * The following attributes are necessary in order for this to
+                * work properly:
+                *
+                * • msDS-ManagedPasswordId
+                * • msDS-ManagedPasswordInterval
+                * • objectClass
+                * • objectSid
+                * • whenCreated
+                */
+
+               ret = dsdb_update_gmsa_keys(ldb, tmp_ctx, res, &retry);
+               if (ret) {
+                       talloc_free(tmp_ctx);
+                       return ret;
+               }
+               if (!retry) {
+                       break;
+               }
+       }
+       if (tries == max_tries) {
+               talloc_free(tmp_ctx);
+               ldb_reset_err_string(ldb);
+               return ldb_operr(ldb);
        }
 
        *_result = talloc_steal(mem_ctx, res);