From: Andrew Bartlett Date: Tue, 28 May 2024 00:53:19 +0000 (+1200) Subject: kdc: Use a consistent, stable time throughout the Heimdal KDC X-Git-Tag: tdb-1.4.11~404 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1dcd8be8f06066f8aed7765c4e92259ce1371991;p=thirdparty%2Fsamba.git kdc: Use a consistent, stable time throughout the Heimdal KDC 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 Reviewed-by: Jo Sutton --- diff --git a/source4/dsdb/gmsa/util.c b/source4/dsdb/gmsa/util.c index bda64bdc77a..ecd055fd259 100644 --- a/source4/dsdb/gmsa/util.c +++ b/source4/dsdb/gmsa/util.c @@ -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; +} diff --git a/source4/kdc/db-glue.c b/source4/kdc/db-glue.c index 01b19209cf5..e1a62552afe 100644 --- a/source4/kdc/db-glue.c +++ b/source4/kdc/db-glue.c @@ -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) { diff --git a/source4/kdc/kdc-heimdal.c b/source4/kdc/kdc-heimdal.c index 241c167d3e9..978de7a8caf 100644 --- a/source4/kdc/kdc-heimdal.c +++ b/source4/kdc/kdc-heimdal.c @@ -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], diff --git a/source4/kdc/mit_kdc_irpc.c b/source4/kdc/mit_kdc_irpc.c index d2c39412081..470aaa24f3d 100644 --- a/source4/kdc/mit_kdc_irpc.c +++ b/source4/kdc/mit_kdc_irpc.c @@ -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, diff --git a/source4/kdc/mit_samba.c b/source4/kdc/mit_samba.c index 2593cbfcd4b..4af02fa00d0 100644 --- a/source4/kdc/mit_samba.c +++ b/source4/kdc/mit_samba.c @@ -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); } diff --git a/source4/kdc/samba_kdc.h b/source4/kdc/samba_kdc.h index 67009b963bc..3c5116092b9 100644 --- a/source4/kdc/samba_kdc.h +++ b/source4/kdc/samba_kdc.h @@ -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; diff --git a/source4/libnet/libnet_export_keytab.c b/source4/libnet/libnet_export_keytab.c index fbe94068f58..4a5f9268a17 100644 --- a/source4/libnet/libnet_export_keytab.c +++ b/source4/libnet/libnet_export_keytab.c @@ -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(¤t_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)) {