s4:dsdb: Implement msDS-ManagedPassword attribute
authorJo Sutton <josutton@catalyst.net.nz>
Tue, 13 Feb 2024 02:45:21 +0000 (15:45 +1300)
committerAndrew Bartlett <abartlet@samba.org>
Tue, 16 Apr 2024 05:02:30 +0000 (05:02 +0000)
Signed-off-by: Jo Sutton <josutton@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
Autobuild-User(master): Andrew Bartlett <abartlet@samba.org>
Autobuild-Date(master): Tue Apr 16 05:02:30 UTC 2024 on atb-devel-224

selftest/knownfail.d/gmsa
selftest/knownfail.d/user_getpassword_gmsa
source4/dsdb/samdb/ldb_modules/managed_pwd.c [new file with mode: 0644]
source4/dsdb/samdb/ldb_modules/managed_pwd.h [new file with mode: 0644]
source4/dsdb/samdb/ldb_modules/operational.c
source4/dsdb/samdb/ldb_modules/wscript_build_server
source4/dsdb/samdb/samdb.h

index 31b0c869db784e23efb55681eb5d2c1f3421049a..7a126d6cc22cc0c293a3eef668aacbb4fd0dfdc9 100644 (file)
@@ -1,10 +1 @@
 ^samba.tests.dckeytab.samba.tests.dckeytab.DCKeytabTests.test_export_keytab_gmsa
-^samba.tests.blackbox.gmsa.samba.tests.blackbox.gmsa.GMSABlackboxTest.test_gmsa_password_access
-^samba.tests.krb5.gmsa_tests.samba.tests.krb5.gmsa_tests.GmsaTests.test_retrieved_password\(ad_dc:local\)$
-^samba.tests.krb5.gmsa_tests.samba.tests.krb5.gmsa_tests.GmsaTests.test_retrieved_password_allowed\(ad_dc:local\)$
-^samba.tests.krb5.gmsa_tests.samba.tests.krb5.gmsa_tests.GmsaTests.test_retrieved_password_during_clock_skew_window_when_current_key_is_expired\(ad_dc:local\)$
-^samba.tests.krb5.gmsa_tests.samba.tests.krb5.gmsa_tests.GmsaTests.test_retrieved_password_during_clock_skew_window_when_current_key_is_valid\(ad_dc:local\)$
-^samba.tests.krb5.gmsa_tests.samba.tests.krb5.gmsa_tests.GmsaTests.test_retrieved_password_during_clock_skew_window_when_next_key_is_expired\(ad_dc:local\)$
-^samba.tests.krb5.gmsa_tests.samba.tests.krb5.gmsa_tests.GmsaTests.test_retrieved_password_when_current_key_is_expired\(ad_dc:local\)$
-^samba.tests.krb5.gmsa_tests.samba.tests.krb5.gmsa_tests.GmsaTests.test_retrieved_password_when_current_key_is_valid\(ad_dc:local\)$
-^samba.tests.krb5.gmsa_tests.samba.tests.krb5.gmsa_tests.GmsaTests.test_retrieved_password_when_next_key_is_expired\(ad_dc:local\)$
index dd28e61d9b5fc84451a897fa2a054b18dd6ccfdd..a9d4afe9ea8ed9123dfcc84c2103b0ae730a34c2 100644 (file)
@@ -1,6 +1,3 @@
-^samba.tests.samba_tool.user_getpassword_gmsa.samba.tests.samba_tool.user_getpassword_gmsa.GMSAPasswordTest.test_getpassword\(ad_dc_default\)
-^samba.tests.samba_tool.user_getpassword_gmsa.samba.tests.samba_tool.user_getpassword_gmsa.GMSAPasswordTest.test_querytime\(ad_dc_default\)
-^samba.tests.samba_tool.user_getpassword_gmsa.samba.tests.samba_tool.user_getpassword_gmsa.GMSAPasswordTest.test_querytime_unixtime\(ad_dc_default\)
 ^samba.tests.samba_tool.user_getpassword_gmsa.samba.tests.samba_tool.user_getpassword_gmsa.GMSAPasswordTest.test_unicode_pwd\(ad_dc_default\)
 ^samba.tests.samba_tool.user_getpassword_gmsa.samba.tests.samba_tool.user_getpassword_gmsa.GMSAPasswordTest.test_utf16_password\(ad_dc_default\)
 ^samba.tests.samba_tool.user_getpassword_gmsa.samba.tests.samba_tool.user_getpassword_gmsa.GMSAPasswordTest.test_utf8_password\(ad_dc_default\)
diff --git a/source4/dsdb/samdb/ldb_modules/managed_pwd.c b/source4/dsdb/samdb/ldb_modules/managed_pwd.c
new file mode 100644 (file)
index 0000000..cb3fffc
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+   Unix SMB/CIFS implementation.
+   msDS-ManagedPassword attribute for Group Managed Service Accounts
+
+   Copyright (C) Catalyst.Net Ltd 2024
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include <talloc.h>
+#include <ldb.h>
+#include <ldb_module.h>
+#include <ldb_errors.h>
+#include <ldb_private.h>
+#include "lib/crypto/gmsa.h"
+#include "lib/util/time.h"
+#include "librpc/gen_ndr/ndr_gkdi.h"
+#include "librpc/gen_ndr/ndr_gmsa.h"
+#include "dsdb/gmsa/util.h"
+#include "dsdb/samdb/ldb_modules/managed_pwd.h"
+#include "dsdb/samdb/ldb_modules/util.h"
+#include "dsdb/samdb/samdb.h"
+
+#undef strcasecmp
+
+static int gmsa_managed_password(struct ldb_context *const ldb,
+                                struct ldb_message *msg,
+                                struct ldb_request *req,
+                                struct ldb_reply *ares)
+{
+       TALLOC_CTX *tmp_ctx = NULL;
+       const struct dsdb_encrypted_connection_state *conn_state = NULL;
+       int ret = LDB_SUCCESS;
+       NTSTATUS status = NT_STATUS_OK;
+       NTTIME current_time;
+       struct gmsa_update *gmsa_update = NULL;
+       struct gmsa_return_pwd return_pwd;
+       bool ok;
+
+       /*
+        * Prevent viewing msDS-ManagedPassword over an insecure connection. The
+        * opaque is added in the ldap backend init.
+        */
+       conn_state = ldb_get_opaque(
+               ldb, DSDB_OPAQUE_ENCRYPTED_CONNECTION_STATE_NAME);
+       if (conn_state != NULL && !conn_state->using_encrypted_connection) {
+               ret = dsdb_werror(ldb,
+                                 LDB_ERR_OPERATIONS_ERROR,
+                                 WERR_DS_CONFIDENTIALITY_REQUIRED,
+                                 "Viewing msDS-ManagedPassword requires an "
+                                 "encrypted connection");
+               goto out;
+       }
+
+       {
+               /* Is the account a Group Managed Service Account? */
+               const bool is_gmsa = dsdb_account_is_gmsa(ldb, msg);
+               if (!is_gmsa) {
+                       /* It’s not a GMSA — we’re done here. */
+                       ret = LDB_SUCCESS;
+                       goto out;
+               }
+       }
+
+       {
+               bool am_rodc = true;
+
+               /* Are we operating as an RODC? */
+               ret = samdb_rodc(ldb, &am_rodc);
+               if (ret != LDB_SUCCESS) {
+                       DBG_WARNING("unable to tell if we are an RODC\n");
+                       goto out;
+               }
+
+               if (am_rodc) {
+                       /* TODO: forward the request to a writable DC. */
+                       ret = ldb_error(
+                               ldb,
+                               LDB_ERR_OPERATIONS_ERROR,
+                               "msDS-ManagedPassword may only be viewed on a "
+                               "writeable DC, not an RODC");
+                       goto out;
+               }
+       }
+
+       tmp_ctx = talloc_new(msg);
+       if (tmp_ctx == NULL) {
+               ret = ldb_oom(ldb);
+               goto out;
+       }
+
+       {
+               struct dom_sid account_sid;
+               bool allowed_to_view = false;
+
+               ret = samdb_result_dom_sid_buf(msg, "objectSid", &account_sid);
+               if (ret) {
+                       goto out;
+               }
+
+               ret = gmsa_allowed_to_view_managed_password(
+                       tmp_ctx, ldb, msg, &account_sid, &allowed_to_view);
+               if (ret) {
+                       goto out;
+               }
+
+               if (!allowed_to_view) {
+                       /* Sorry, you can’t view the password. */
+                       goto out;
+               }
+       }
+
+       ok = dsdb_gmsa_current_time(ldb, &current_time);
+       if (!ok) {
+               ret = ldb_operr(ldb);
+               goto out;
+       }
+
+       ret = gmsa_recalculate_managed_pwd(
+               tmp_ctx, ldb, msg, current_time, &gmsa_update, &return_pwd);
+       if (ret) {
+               goto out;
+       }
+
+       SMB_ASSERT(return_pwd.new_pwd != NULL);
+
+       {
+               DATA_BLOB packed_blob = {};
+
+               status = gmsa_pack_managed_pwd(
+                       tmp_ctx,
+                       return_pwd.new_pwd->buf,
+                       return_pwd.prev_pwd != NULL ? return_pwd.prev_pwd->buf
+                                                   : NULL,
+                       return_pwd.query_interval,
+                       return_pwd.unchanged_interval,
+                       &packed_blob);
+               if (!NT_STATUS_IS_OK(status)) {
+                       ret = ldb_operr(ldb);
+                       goto out;
+               }
+
+               ret = ldb_msg_add_steal_value(msg,
+                                             "msDS-ManagedPassword",
+                                             &packed_blob);
+               if (ret) {
+                       goto out;
+               }
+       }
+
+out:
+       TALLOC_FREE(tmp_ctx);
+       return ret;
+}
+
+int constructed_msds_managed_password(struct ldb_module *module,
+                                     struct ldb_message *msg,
+                                     enum ldb_scope scope,
+                                     struct ldb_request *parent,
+                                     struct ldb_reply *ares)
+{
+       return gmsa_managed_password(ldb_module_get_ctx(module),
+                                    msg,
+                                    parent,
+                                    ares);
+}
diff --git a/source4/dsdb/samdb/ldb_modules/managed_pwd.h b/source4/dsdb/samdb/ldb_modules/managed_pwd.h
new file mode 100644 (file)
index 0000000..4750ea5
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+   Unix SMB/CIFS implementation.
+   msDS-ManagedPassword attribute for Group Managed Service Accounts
+
+   Copyright (C) Catalyst.Net Ltd 2024
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+#ifndef DSDB_SAMDB_LDB_MODULES_MANAGED_PWD_H
+#define DSDB_SAMDB_LDB_MODULES_MANAGED_PWD_H
+
+#include <ldb.h>
+
+struct ldb_module;
+int constructed_msds_managed_password(struct ldb_module *module,
+                                     struct ldb_message *msg,
+                                     enum ldb_scope scope,
+                                     struct ldb_request *parent,
+                                     struct ldb_reply *ares);
+
+#endif /* DSDB_SAMDB_LDB_MODULES_MANAGED_PWD_H */
index 9b56de2ae5b99ef058d7e7ac86e15426a8a8dfdd..4e9ece53643410783537ae865e9eabe8ee05ea68 100644 (file)
@@ -71,6 +71,7 @@
 #include "librpc/gen_ndr/ndr_misc.h"
 #include "librpc/gen_ndr/ndr_drsblobs.h"
 #include "dsdb/samdb/samdb.h"
+#include "dsdb/samdb/ldb_modules/managed_pwd.h"
 #include "dsdb/samdb/ldb_modules/util.h"
 
 #include "auth/auth.h"
@@ -1411,6 +1412,17 @@ static const char *resultant_pso_computed_attrs[] =
        NULL
 };
 
+static const char *managed_password_computed_attrs[] = {
+       "msDS-GroupMSAMembership",
+       "msDS-ManagedPasswordId",
+       "msDS-ManagedPasswordInterval",
+       "msDS-ManagedPasswordPreviousId",
+       "objectClass",
+       "objectSid",
+       "whenCreated",
+       NULL,
+};
+
 /*
   a list of attribute names that are hidden, but can be searched for
   using another (non-hidden) name to produce the correct result
@@ -1433,7 +1445,11 @@ static const struct op_attributes_replace search_sub[] = {
        { "msDS-UserPasswordExpiryTimeComputed", "userAccountControl", user_password_expiry_time_computed_attrs,
          construct_msds_user_password_expiry_time_computed },
        { "msDS-ResultantPSO", "objectClass", resultant_pso_computed_attrs,
-         construct_resultant_pso }
+         construct_resultant_pso },
+       {"msDS-ManagedPassword",
+        NULL,
+        managed_password_computed_attrs,
+        constructed_msds_managed_password},
 };
 
 
index 7a63f43726b0f8aff4882b6f9678fa99f5f29241..9c1eb12a7c2e212bd6af212d89eaab60ee3070ba 100644 (file)
@@ -360,12 +360,12 @@ bld.SAMBA_MODULE('ldb_instancetype',
 
 
 bld.SAMBA_MODULE('ldb_operational',
-       source='operational.c',
+       source='operational.c managed_pwd.c',
        subsystem='ldb',
        init_function='ldb_operational_module_init',
        module_init_name='ldb_init_module',
        internal_module=False,
-       deps='talloc samba-util samdb-common DSDB_MODULE_HELPERS samdb'
+       deps='talloc samba-util samdb-common DSDB_MODULE_HELPERS samdb gkdi gmsa NDR_GKDI NDR_GMSA'
        )
 
 
index e3558c127093b87712256c47fb0106a3fe91882f..64135ef704f0247a50de363a8ef7fc5a423923a7 100644 (file)
@@ -31,11 +31,9 @@ struct loadparm_context;
 struct tevent_context;
 struct tsocket_address;
 struct dsdb_trust_routing_table;
-struct gmsa_update_pwd;
 struct gmsa_update_pwd_part;
 struct gmsa_update;
 struct gmsa_return_pwd;
-struct RootKey;
 struct KeyEnvelope;
 
 enum dsdb_password_checked {
@@ -44,10 +42,13 @@ enum dsdb_password_checked {
        DSDB_PASSWORD_CHECKED_AND_CORRECT
 };
 
+#include "lib/util/data_blob.h"
 #include "librpc/gen_ndr/security.h"
 #include <ldb.h>
 #include "lib/ldb-samba/ldif_handlers.h"
+#include "lib/util/time.h"
 #include "librpc/gen_ndr/samr.h"
+#include "libcli/util/werror.h"
 #include "librpc/gen_ndr/drsuapi.h"
 #include "librpc/gen_ndr/drsblobs.h"
 #include "dsdb/schema/schema.h"