]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
kdc: Use a consistent, stable time throughout the Heimdal KDC
authorAndrew Bartlett <abartlet@samba.org>
Tue, 28 May 2024 00:53:19 +0000 (12:53 +1200)
committerAndrew Bartlett <abartlet@samba.org>
Mon, 10 Jun 2024 04:27:30 +0000 (04:27 +0000)
The MIT KDC has a fallback to a consistent time per fetch call, and
both implementations then follow the time in each 'struct
samba_kdc_entry'.

Signed-off-by: Andrew Bartlett <abartlet@samba.org>
Reviewed-by: Jo Sutton <josutton@catalyst.net.nz>
source4/dsdb/gmsa/util.c
source4/kdc/db-glue.c
source4/kdc/kdc-heimdal.c
source4/kdc/mit_kdc_irpc.c
source4/kdc/mit_samba.c
source4/kdc/samba_kdc.h
source4/libnet/libnet_export_keytab.c

index bda64bdc77ab3c47ba0246c1cb89ed984e518e1a..ecd055fd259fa1bb796089e3ebeb35b64a267fbb 100644 (file)
@@ -1783,3 +1783,14 @@ bool dsdb_gmsa_current_time(struct ldb_context *ldb, NTTIME *current_time_out)
 
        return gmsa_current_time(current_time_out);
 }
+
+/* Set the current time.  Caller to supply valid unsigned long long talloc pointer and manage lifetime */
+bool dsdb_gmsa_set_current_time(struct ldb_context *ldb, unsigned long long *current_time_talloc)
+{
+       int ret = ldb_set_opaque(ldb, DSDB_GMSA_TIME_OPAQUE, current_time_talloc);
+       if (ret != LDB_SUCCESS) {
+
+               return false;
+       }
+       return true;
+}
index 01b19209cf54881b0724509f43cb28e13fe108dd..e1a62552afe9a57c5661b75e0d9665057a71d095 100644 (file)
@@ -1286,6 +1286,7 @@ static krb5_error_code samba_kdc_message2entry(krb5_context context,
                ret = ENOMEM;
                goto out;
        }
+       p->current_nttime = *kdc_db_ctx->current_nttime_ull;
 
        talloc_set_destructor(p, samba_kdc_entry_destructor);
 
@@ -2180,8 +2181,8 @@ static krb5_error_code samba_kdc_trust_message2entry(krb5_context context,
        enum ndr_err_code ndr_err;
        int ret;
        unsigned int i;
-       struct timeval tv;
-       NTTIME an_hour_ago;
+       NTTIME now = *kdc_db_ctx->current_nttime_ull;
+       NTTIME an_hour_ago, an_hour;
        bool prefer_current = false;
        bool force_rc4 = lpcfg_kdc_force_enable_rc4_weak_session_keys(lp_ctx);
        uint32_t supported_enctypes = ENC_RC4_HMAC_MD5;
@@ -2308,6 +2309,7 @@ static krb5_error_code samba_kdc_trust_message2entry(krb5_context context,
        p->kdc_db_ctx = kdc_db_ctx;
        p->realm_dn = realm_dn;
        p->supported_enctypes = pa_supported_enctypes;
+       p->current_nttime = *kdc_db_ctx->current_nttime_ull;
 
        talloc_set_destructor(p, samba_kdc_entry_destructor);
 
@@ -2350,11 +2352,18 @@ static krb5_error_code samba_kdc_trust_message2entry(krb5_context context,
        /*
         * Windows prefers the previous key for one hour.
         */
-       tv = timeval_current();
-       if (tv.tv_sec > 3600) {
-               tv.tv_sec -= 3600;
+
+       an_hour = INT64_C(1000) * 1000 * 10 * 3600;
+
+       /*
+        * While a 'now' value of 0 is implausible, avoid this being a
+        * silly value in that case
+        */
+       if (now > an_hour) {
+               an_hour_ago = now - an_hour;
+       } else {
+               an_hour_ago = now;
        }
-       an_hour_ago = timeval_to_nttime(&tv);
 
        /* first work out the current kvno */
        current_kvno = 0;
@@ -4009,6 +4018,8 @@ NTSTATUS samba_kdc_setup_db_ctx(TALLOC_CTX *mem_ctx, struct samba_kdc_base_conte
        int ldb_ret;
        struct ldb_message *msg = NULL;
        struct samba_kdc_db_context *kdc_db_ctx = NULL;
+       bool time_ok;
+
        /* The idea here is very simple.  Using Kerberos to
         * authenticate the KDC to the LDAP server is highly likely to
         * be circular.
@@ -4025,6 +4036,9 @@ NTSTATUS samba_kdc_setup_db_ctx(TALLOC_CTX *mem_ctx, struct samba_kdc_base_conte
        kdc_db_ctx->lp_ctx = base_ctx->lp_ctx;
        kdc_db_ctx->msg_ctx = base_ctx->msg_ctx;
 
+       /* Copy over the pointer that will be updated with the time */
+       kdc_db_ctx->current_nttime_ull = base_ctx->current_nttime_ull;
+
        /* get default kdc policy */
        lpcfg_default_kdc_policy(mem_ctx,
                                 base_ctx->lp_ctx,
@@ -4065,6 +4079,16 @@ NTSTATUS samba_kdc_setup_db_ctx(TALLOC_CTX *mem_ctx, struct samba_kdc_base_conte
                }
        }
 
+       /*
+        * Set the current time pointer, which will be updated before
+        * each packet (Heimdal) or fetch call (MIT)
+        */
+       time_ok = dsdb_gmsa_set_current_time(kdc_db_ctx->samdb, kdc_db_ctx->current_nttime_ull);
+       if (!time_ok) {
+               talloc_free(kdc_db_ctx);
+               return NT_STATUS_INTERNAL_ERROR;
+       }
+
        /* Find out our own krbtgt kvno */
        ldb_ret = samdb_rodc(kdc_db_ctx->samdb, &kdc_db_ctx->rodc);
        if (ldb_ret != LDB_SUCCESS) {
index 241c167d3e9f29b79b488e6da0be5a7f288e8306..978de7a8caf0d0b4d44b054b9d5e61a74af26d33 100644 (file)
@@ -65,10 +65,15 @@ static kdc_code kdc_process(struct kdc_server *kdc,
        struct sockaddr_storage ss;
        krb5_data k5_reply;
        krb5_kdc_configuration *kdc_config = kdc->private_data;
+       struct timespec now_timespec = timespec_current();
+       struct timeval now_timeval = convert_timespec_to_timeval(now_timespec);
 
        krb5_data_zero(&k5_reply);
 
-       krb5_kdc_update_time(NULL);
+       krb5_kdc_update_time(&now_timeval);
+
+       /* This sets the time into the DSDB opaque */
+       *kdc->base_ctx->current_nttime_ull = full_timespec_to_nt_time(&now_timespec);
 
        ret = tsocket_address_bsd_sockaddr(peer_addr, (struct sockaddr *) &ss,
                                sizeof(struct sockaddr_storage));
@@ -449,6 +454,12 @@ static void kdc_post_fork(struct task_server *task, struct process_details *pd)
        kdc->base_ctx->lp_ctx = task->lp_ctx;
        kdc->base_ctx->msg_ctx = task->msg_ctx;
 
+       kdc->base_ctx->current_nttime_ull = talloc_zero(kdc, unsigned long long);
+       if (kdc->base_ctx->current_nttime_ull == NULL) {
+               task_server_terminate(task, "kdc: out of memory creating time variable", true);
+               return;
+       }
+
        status = hdb_samba4_create_kdc(kdc->base_ctx,
                                       kdc->smb_krb5_context->krb5_context,
                                       &kdc_config->db[0],
index d2c39412081e9b6ea728985a362a2b615c1c9d15..470aaa24f3dc444fb685eecf0eafcde4719b9942 100644 (file)
@@ -36,6 +36,7 @@
 #include "db-glue.h"
 #include "sdb.h"
 #include "mit_kdc_irpc.h"
+#include "lib/crypto/gmsa.h"
 
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_KERBEROS
@@ -62,6 +63,15 @@ static NTSTATUS netr_samlogon_generic_logon(struct irpc_message *msg,
        struct sdb_keys skeys;
        unsigned int i;
        const uint8_t *d = NULL;
+       NTTIME now;
+       bool time_ok;
+
+       time_ok = gmsa_current_time(&now);
+       if (!time_ok) {
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       *mki_ctx->db_ctx->current_nttime_ull = now;
 
        /* There is no reply to this request */
        r->out.generic_reply = data_blob(NULL, 0);
@@ -174,6 +184,11 @@ NTSTATUS samba_setup_mit_kdc_irpc(struct task_server *task)
        base_ctx.ev_ctx = task->event_ctx;
        base_ctx.lp_ctx = task->lp_ctx;
 
+       base_ctx.current_nttime_ull = talloc_zero(mki_ctx, unsigned long long);
+       if (base_ctx.current_nttime_ull == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
        /* db-glue.h */
        status = samba_kdc_setup_db_ctx(mki_ctx,
                                        &base_ctx,
index 2593cbfcd4b11e09dedd0b18c5fc25045f8ff54d..4af02fa00d0530edd09921ccc187f67f0ad651d8 100644 (file)
@@ -114,6 +114,12 @@ krb5_error_code mit_samba_context_init(struct mit_samba_context **_ctx)
                lpcfg_load_default(base_ctx.lp_ctx);
        }
 
+       base_ctx.current_nttime_ull = talloc_zero(ctx, unsigned long long);
+       if (base_ctx.current_nttime_ull == NULL) {
+               ret = ENOMEM;
+               goto done;
+       }
+
        status = samba_kdc_setup_db_ctx(ctx, &base_ctx, &ctx->db_ctx);
        if (!NT_STATUS_IS_OK(status)) {
                ret = EINVAL;
@@ -201,6 +207,15 @@ krb5_error_code mit_samba_get_principal(struct mit_samba_context *ctx,
        krb5_error_code ret;
        uint32_t sflags = 0;
        krb5_principal referral_principal = NULL;
+       NTTIME now;
+       bool time_ok;
+
+       time_ok = gmsa_current_time(&now);
+       if (!time_ok) {
+               return EINVAL;
+       }
+
+       *ctx->db_ctx->current_nttime_ull = now;
 
        kentry = calloc(1, sizeof(krb5_db_entry));
        if (kentry == NULL) {
@@ -343,6 +358,16 @@ krb5_error_code mit_samba_get_firstkey(struct mit_samba_context *ctx,
        krb5_db_entry *kentry;
        krb5_error_code ret;
 
+       NTTIME now;
+       bool time_ok;
+
+       time_ok = gmsa_current_time(&now);
+       if (!time_ok) {
+               return EINVAL;
+       }
+
+       *ctx->db_ctx->current_nttime_ull = now;
+
        kentry = malloc(sizeof(krb5_db_entry));
        if (kentry == NULL) {
                return ENOMEM;
@@ -381,6 +406,8 @@ krb5_error_code mit_samba_get_nextkey(struct mit_samba_context *ctx,
        krb5_db_entry *kentry;
        krb5_error_code ret;
 
+       /* Not updating time, keep the same for the whole operation */
+
        kentry = malloc(sizeof(krb5_db_entry));
        if (kentry == NULL) {
                return ENOMEM;
@@ -450,6 +477,9 @@ krb5_error_code mit_samba_get_pac(struct mit_samba_context *smb_ctx,
        skdc_entry = talloc_get_type_abort(client->e_data,
                                           struct samba_kdc_entry);
 
+       /* This sets the time into the DSDB opaque */
+       *smb_ctx->db_ctx->current_nttime_ull = skdc_entry->current_nttime;
+
        if (server == NULL) {
                return EINVAL;
        }
@@ -650,6 +680,9 @@ krb5_error_code mit_samba_update_pac(struct mit_samba_context *ctx,
                talloc_get_type_abort(krbtgt->e_data,
                                      struct samba_kdc_entry);
 
+       /* This sets the time into the DSDB opaque */
+       *ctx->db_ctx->current_nttime_ull = krbtgt_skdc_entry->current_nttime;
+
        if (server == NULL) {
                code = EINVAL;
                goto done;
@@ -788,6 +821,9 @@ krb5_error_code mit_samba_check_client_access(struct mit_samba_context *ctx,
 
        skdc_entry = talloc_get_type(client->e_data, struct samba_kdc_entry);
 
+       /* This sets the time into the DSDB opaque */
+       *ctx->db_ctx->current_nttime_ull = skdc_entry->current_nttime;
+
        nt_status = samba_kdc_check_client_access(skdc_entry,
                                                  client_name,
                                                  netbios_name,
@@ -814,6 +850,9 @@ krb5_error_code mit_samba_check_s4u2proxy(struct mit_samba_context *ctx,
                talloc_get_type_abort(server->e_data, struct samba_kdc_entry);
        krb5_error_code code;
 
+       /* This sets the time into the DSDB opaque */
+       *ctx->db_ctx->current_nttime_ull = server_skdc_entry->current_nttime;
+
        code = samba_kdc_check_s4u2proxy(ctx->context,
                                         ctx->db_ctx,
                                         server_skdc_entry,
@@ -835,6 +874,9 @@ krb5_error_code mit_samba_check_allowed_to_delegate_from(
        TALLOC_CTX *mem_ctx = NULL;
        krb5_error_code code;
 
+       /* This sets the time into the DSDB opaque */
+       *ctx->db_ctx->current_nttime_ull = proxy_skdc_entry->current_nttime;
+
        mem_ctx = talloc_new(NULL);
        if (mem_ctx == NULL) {
                return ENOMEM;
@@ -951,6 +993,9 @@ krb5_error_code mit_samba_kpasswd_change_password(struct mit_samba_context *ctx,
                talloc_get_type_abort(db_entry->e_data, struct samba_kdc_entry);
        krb5_error_code code = 0;
 
+       /* This sets the time into the DSDB opaque */
+       *ctx->db_ctx->current_nttime_ull = p->current_nttime;
+
 #ifdef DEBUG_PASSWORD
        DBG_WARNING("mit_samba_kpasswd_change_password called with: %s\n", pwd);
 #endif
@@ -1035,6 +1080,9 @@ void mit_samba_zero_bad_password_count(krb5_db_entry *db_entry)
                talloc_get_type_abort(db_entry->e_data, struct samba_kdc_entry);
        struct ldb_dn *domain_dn;
 
+       /* This sets the time into the DSDB opaque */
+       *p->kdc_db_ctx->current_nttime_ull = p->current_nttime;
+
        domain_dn = ldb_get_default_basedn(p->kdc_db_ctx->samdb);
 
        authsam_logon_success_accounting(p->kdc_db_ctx->samdb,
@@ -1051,6 +1099,9 @@ void mit_samba_update_bad_password_count(krb5_db_entry *db_entry)
        struct samba_kdc_entry *p =
                talloc_get_type_abort(db_entry->e_data, struct samba_kdc_entry);
 
+       /* This sets the time into the DSDB opaque */
+       *p->kdc_db_ctx->current_nttime_ull = p->current_nttime;
+
        authsam_update_bad_pwd_count(p->kdc_db_ctx->samdb,
                                     p->msg,
                                     ldb_get_default_basedn(p->kdc_db_ctx->samdb));
@@ -1061,5 +1112,8 @@ bool mit_samba_princ_needs_pac(krb5_db_entry *db_entry)
        struct samba_kdc_entry *skdc_entry =
                talloc_get_type_abort(db_entry->e_data, struct samba_kdc_entry);
 
+       /* This sets the time into the DSDB opaque */
+       *skdc_entry->kdc_db_ctx->current_nttime_ull = skdc_entry->current_nttime;
+
        return samba_princ_needs_pac(skdc_entry);
 }
index 67009b963bc51c48cd40317edd5003dc3f312d5b..3c5116092b9c9ff2ca7b32b93050f79721fdb837 100644 (file)
@@ -39,6 +39,13 @@ struct samba_kdc_base_context {
        struct loadparm_context *lp_ctx;
        struct imessaging_context *msg_ctx;
        struct ldb_context *samdb;
+
+       /*
+        * If we are under Heimdal, this will be updated at each
+        * packet to be the same time as the KDC process uses and will
+        * be set into dsdb_gmsa_set_current_time() (otherwise NULL)
+        */
+       unsigned long long *current_nttime_ull;
 };
 
 struct samba_kdc_seq;
@@ -53,6 +60,10 @@ struct samba_kdc_db_context {
        unsigned int my_krbtgt_number;
        struct ldb_dn *krbtgt_dn;
        struct samba_kdc_policy policy;
+       /*
+        * Copied from the base_context when this is created
+        */
+       unsigned long long *current_nttime_ull;
 };
 
 struct samba_kdc_entry {
@@ -76,6 +87,7 @@ struct samba_kdc_entry {
        bool claims_from_pac_are_initialized : 1;
        bool claims_from_db_are_initialized : 1;
        bool group_managed_service_account : 1;
+       NTTIME current_nttime;
 };
 
 extern struct hdb_method hdb_samba4_interface;
index fbe94068f58629893c6ace5aea1fe5804ddd7dac..4a5f9268a175419b638e7fb503edb720bfbe5686 100644 (file)
@@ -29,6 +29,7 @@
 #include "libnet/libnet_export_keytab.h"
 #include "kdc/db-glue.h"
 #include "kdc/sdb.h"
+#include "dsdb/gmsa/util.h"
 
 static NTSTATUS sdb_kt_copy(TALLOC_CTX *mem_ctx,
                            struct smb_krb5_context *smb_krb5_context,
@@ -371,6 +372,37 @@ NTSTATUS libnet_export_keytab(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, s
        base_ctx->ev_ctx = ctx->event_ctx;
        base_ctx->lp_ctx = ctx->lp_ctx;
        base_ctx->samdb = r->in.samdb;
+       if (base_ctx->samdb != NULL) {
+               base_ctx->current_nttime_ull = talloc_get_type(
+                       ldb_get_opaque(base_ctx->samdb, DSDB_GMSA_TIME_OPAQUE), unsigned long long);
+       }
+
+       /*
+        * If the caller hasn't set a fixed time, or a samdb, set up
+        * the pointer for the opaque and set to the current time
+        */
+       if (base_ctx->current_nttime_ull == NULL) {
+               bool time_ok;
+               NTTIME current_nttime;
+
+               base_ctx->current_nttime_ull = talloc_zero(base_ctx, unsigned long long);
+               if (base_ctx->current_nttime_ull == NULL) {
+                       r->out.error_string = NULL;
+                       return NT_STATUS_NO_MEMORY;
+               }
+
+               time_ok = gmsa_current_time(&current_nttime);
+
+               if (!time_ok) {
+                       /* This is really quite unlikely */
+                       r->out.error_string
+                               = talloc_asprintf(mem_ctx,
+                                                 "Failed to get current time to check "
+                                                 "time-dependent keys against for export");
+                       return NT_STATUS_UNSUCCESSFUL;
+               }
+               *base_ctx->current_nttime_ull = current_nttime;
+       }
 
        status = samba_kdc_setup_db_ctx(mem_ctx, base_ctx, &db_ctx);
        if (!NT_STATUS_IS_OK(status)) {