]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
s4:kdc: Merge current and previous gMSA keys during period when both are valid
authorJo Sutton <josutton@catalyst.net.nz>
Mon, 15 Apr 2024 02:46:47 +0000 (14:46 +1200)
committerAndrew Bartlett <abartlet@samba.org>
Wed, 22 May 2024 20:33:36 +0000 (20:33 +0000)
Signed-off-by: Jo Sutton <josutton@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
selftest/knownfail.d/gmsa
source4/dsdb/gmsa/util.c
source4/dsdb/gmsa/util.h
source4/kdc/db-glue.c

index e8701a5db0c3730c2ade146b04d82669ca213309..d0a058e6bace5bf461500803e32b69d6506df54f 100644 (file)
@@ -1,4 +1,3 @@
-^samba\.tests\.krb5\.gmsa_tests\.samba\.tests\.krb5\.gmsa_tests\.GmsaTests\.test_gmsa_keys_when_previous_password_is_acceptable\(ad_dc:local\)$
 # The unencrypted simple bind fails because the ad_dc environment sets ‘ldap
 # server require strong auth = yes’.
 ^samba\.tests\.krb5\.gmsa_tests\.samba\.tests\.krb5\.gmsa_tests\.GmsaTests\.test_retrieving_password_after_unencrypted_simple_bind\(ad_dc:local\)$
index 08e4045a1717e9562e16e04a65a09721ae22ad07..b0a0691ae1c7769d74c00c629c513c73adffcc3f 100644 (file)
@@ -1108,6 +1108,31 @@ static bool samdb_result_gkdi_rollover_interval(const struct ldb_message *msg,
                                      rollover_interval_out);
 }
 
+bool samdb_gmsa_key_is_recent(const struct ldb_message *msg,
+                             const NTTIME current_time)
+{
+       const struct KeyEnvelopeId *pwd_id = NULL;
+       struct KeyEnvelopeId pwd_id_buf;
+       NTTIME key_start_time;
+       bool ok;
+
+       pwd_id = gmsa_get_managed_pwd_id(msg, &pwd_id_buf);
+       if (pwd_id == NULL) {
+               return false;
+       }
+
+       ok = gkdi_get_key_start_time(pwd_id->gkid, &key_start_time);
+       if (!ok) {
+               return false;
+       }
+
+       if (current_time < key_start_time) {
+               return false;
+       }
+
+       return current_time - key_start_time < gkdi_max_clock_skew;
+}
+
 /*
  * Recalculate the managed password of an account. The account referred to by
  * ‘msg’ should be a Group Managed Service Account.
index 2fc1ee0d5711f24a9b56374c965feb141504decf..0db77a521d454be5c0d466c4dd42fc357963b601 100644 (file)
@@ -99,6 +99,9 @@ struct gmsa_return_pwd {
        NTTIME unchanged_interval;
 };
 
+bool samdb_gmsa_key_is_recent(const struct ldb_message *msg,
+                             const NTTIME current_time);
+
 /*
  * Recalculate the managed password of an account. The account referred to by
  * ‘msg’ should be a Group Managed Service Account.
index 6bc55e767eddacace6a9a0008cfff18613736a2a..43b8a1c1863a64883f01ae7a6a6361fd2d553f0e 100644 (file)
 #include "librpc/gen_ndr/ndr_security.h"
 #include "auth/auth.h"
 #include "auth/auth_sam.h"
+#include "dsdb/gmsa/util.h"
 #include "dsdb/samdb/samdb.h"
 #include "dsdb/common/util.h"
 #include "librpc/gen_ndr/ndr_drsblobs.h"
 #include "param/param.h"
 #include "param/secrets.h"
+#include "lib/crypto/gkdi.h"
 #include "../lib/crypto/md4.h"
+#include "lib/util/memory.h"
 #include "system/kerberos.h"
 #include "auth/kerberos/kerberos.h"
 #include "kdc/authn_policy_util.h"
@@ -575,6 +578,40 @@ fail:
        return ret;
 }
 
+static krb5_error_code samba_kdc_merge_keys(struct sdb_keys *keys,
+                                           struct sdb_keys *old_keys)
+{
+       unsigned num_keys;
+       unsigned num_old_keys;
+       unsigned total_keys;
+       unsigned j;
+       struct sdb_key *skeys = NULL;
+
+       if (keys == NULL || old_keys == NULL) {
+               return EINVAL;
+       }
+
+       num_keys = keys->len;
+       num_old_keys = old_keys->len;
+       total_keys = num_keys + num_old_keys;
+
+       skeys = realloc(keys->val, total_keys * sizeof keys->val[0]);
+       if (skeys == NULL) {
+               return ENOMEM;
+       }
+       keys->val = skeys;
+
+       for (j = 0; j < num_old_keys; ++j) {
+               keys->val[num_keys + j] = old_keys->val[j];
+       }
+       keys->len = total_keys;
+
+       old_keys->len = 0;
+       SAFE_FREE(old_keys->val);
+
+       return 0;
+}
+
 krb5_error_code samba_kdc_message2entry_keys(krb5_context context,
                                             TALLOC_CTX *mem_ctx,
                                             struct ldb_context *ldb,
@@ -855,6 +892,44 @@ krb5_error_code samba_kdc_message2entry_keys(krb5_context context,
                if (ret != 0) {
                        goto out;
                }
+
+               if (keys.skeys != NULL && !exporting_keytab) {
+                       bool is_gmsa;
+
+                       is_gmsa = dsdb_account_is_gmsa(ldb, msg);
+                       if (is_gmsa) {
+                               NTTIME current_time;
+                               bool gmsa_key_is_recent;
+                               bool ok;
+
+                               ok = dsdb_gmsa_current_time(ldb, &current_time);
+                               if (!ok) {
+                                       ret = EINVAL;
+                                       goto out;
+                               }
+
+                               gmsa_key_is_recent = samdb_gmsa_key_is_recent(
+                                       msg, current_time);
+                               if (gmsa_key_is_recent) {
+                                       /*
+                                        * As the current gMSA keys are less
+                                        * than five minutes old, the previous
+                                        * set of keys remains valid. The
+                                        * Heimdal KDC will try each of the
+                                        * current keys when decrypting a
+                                        * client’s PA‐DATA, so by merging the
+                                        * old set into the current set we can
+                                        * cause both sets to be considered for
+                                        * decryption.
+                                        */
+                                       ret = samba_kdc_merge_keys(
+                                               keys.skeys, old_keys.skeys);
+                                       if (ret) {
+                                               goto out;
+                                       }
+                               }
+                       }
+               }
        }
 
        if (older_keys.skeys != NULL) {