From f1d34a430d227e685e2fe983b14c74136d9c8a8e Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 11 Feb 2020 16:07:05 +0100 Subject: [PATCH] auth/ntlmssp: implement channel binding support BUG: https://bugzilla.samba.org/show_bug.cgi?id=15621 Signed-off-by: Stefan Metzmacher Reviewed-by: Andrew Bartlett --- auth/ntlmssp/ntlmssp_client.c | 13 +++--- auth/ntlmssp/ntlmssp_private.h | 2 + auth/ntlmssp/ntlmssp_server.c | 47 ++++++++++++++++++++ auth/ntlmssp/ntlmssp_util.c | 98 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 154 insertions(+), 6 deletions(-) diff --git a/auth/ntlmssp/ntlmssp_client.c b/auth/ntlmssp/ntlmssp_client.c index 337aeed9229..d8dc1d2940b 100644 --- a/auth/ntlmssp/ntlmssp_client.c +++ b/auth/ntlmssp/ntlmssp_client.c @@ -599,6 +599,8 @@ NTSTATUS ntlmssp_client_challenge(struct gensec_security *gensec_security, SingleHost->Value.AvSingleHost.remaining = data_blob_null; } + if (!(gensec_security->want_features & GENSEC_FEATURE_CB_OPTIONAL) + || gensec_security->channel_bindings != NULL) { struct AV_PAIR *ChannelBindings = NULL; @@ -607,13 +609,12 @@ NTSTATUS ntlmssp_client_challenge(struct gensec_security *gensec_security, count++; *eol = *ChannelBindings; - /* - * gensec doesn't support channel bindings yet, - * but we want to match Windows on the wire - */ ChannelBindings->AvId = MsvChannelBindings; - memset(ChannelBindings->Value.ChannelBindings, 0, - sizeof(ChannelBindings->Value.ChannelBindings)); + nt_status = ntlmssp_hash_channel_bindings(gensec_security, + ChannelBindings->Value.ChannelBindings); + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } } service = gensec_get_target_service(gensec_security); diff --git a/auth/ntlmssp/ntlmssp_private.h b/auth/ntlmssp/ntlmssp_private.h index 4d84e3347b6..7b939b80ae2 100644 --- a/auth/ntlmssp/ntlmssp_private.h +++ b/auth/ntlmssp/ntlmssp_private.h @@ -56,6 +56,8 @@ void debug_ntlmssp_flags(uint32_t neg_flags); NTSTATUS ntlmssp_handle_neg_flags(struct ntlmssp_state *ntlmssp_state, uint32_t neg_flags, const char *name); const DATA_BLOB ntlmssp_version_blob(void); +NTSTATUS ntlmssp_hash_channel_bindings(struct gensec_security *gensec_security, + uint8_t cb_hash[16]); /* The following definitions come from auth/ntlmssp_server.c */ diff --git a/auth/ntlmssp/ntlmssp_server.c b/auth/ntlmssp/ntlmssp_server.c index 64b96283eb2..1e49379a8ed 100644 --- a/auth/ntlmssp/ntlmssp_server.c +++ b/auth/ntlmssp/ntlmssp_server.c @@ -386,6 +386,9 @@ static NTSTATUS ntlmssp_server_preauth(struct gensec_security *gensec_security, DATA_BLOB version_blob = data_blob_null; const unsigned int mic_len = NTLMSSP_MIC_SIZE; DATA_BLOB mic_blob = data_blob_null; + const uint8_t zero_channel_bindings[16] = { 0, }; + const uint8_t *client_channel_bindings = zero_channel_bindings; + uint8_t server_channel_bindings[16] = { 0, }; const char *parse_string; bool ok; struct timeval endtime; @@ -523,6 +526,7 @@ static NTSTATUS ntlmssp_server_preauth(struct gensec_security *gensec_security, uint32_t i = 0; uint32_t count = 0; const struct AV_PAIR *flags = NULL; + const struct AV_PAIR *cb = NULL; const struct AV_PAIR *eol = NULL; uint32_t av_flags = 0; @@ -598,6 +602,12 @@ static NTSTATUS ntlmssp_server_preauth(struct gensec_security *gensec_security, ntlmssp_state->new_spnego = true; } + cb = ndr_ntlmssp_find_av(&v2_resp.Challenge.AvPairs, + MsvChannelBindings); + if (cb != NULL) { + client_channel_bindings = cb->Value.ChannelBindings; + } + count = ntlmssp_state->server.av_pair_list.count; if (v2_resp.Challenge.AvPairs.count < count) { return NT_STATUS_INVALID_PARAMETER; @@ -700,6 +710,43 @@ static NTSTATUS ntlmssp_server_preauth(struct gensec_security *gensec_security, } } + if (gensec_security->channel_bindings != NULL) { + nt_status = ntlmssp_hash_channel_bindings(gensec_security, + server_channel_bindings); + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + + ok = mem_equal_const_time(client_channel_bindings, + server_channel_bindings, + 16); + if (!ok && gensec_security->want_features & GENSEC_FEATURE_CB_OPTIONAL) { + /* + * Unlike kerberos, explicit 16 zeros in + * MsvChannelBindings are not enough to + * pass the optional check. + * + * So we only let it through without explicit + * MsvChannelBindings. + */ + ok = (client_channel_bindings == zero_channel_bindings); + } + if (!ok) { + DBG_WARNING("Invalid channel bindings for " + "user=[%s] domain=[%s] workstation=[%s]\n", + ntlmssp_state->user, + ntlmssp_state->domain, + ntlmssp_state->client.netbios_name); + dump_data(DBGLVL_WARNING, + client_channel_bindings, + 16); + dump_data(DBGLVL_WARNING, + server_channel_bindings, + 16); + return NT_STATUS_BAD_BINDINGS; + } + } + nttime_to_timeval(&endtime, ntlmssp_state->server.challenge_endtime); expired = timeval_expired(&endtime); if (expired) { diff --git a/auth/ntlmssp/ntlmssp_util.c b/auth/ntlmssp/ntlmssp_util.c index 6f3b474fd71..b8dc84e1652 100644 --- a/auth/ntlmssp/ntlmssp_util.c +++ b/auth/ntlmssp/ntlmssp_util.c @@ -22,9 +22,15 @@ */ #include "includes.h" +#include "auth/gensec/gensec.h" +#include "auth/gensec/gensec_internal.h" #include "../auth/ntlmssp/ntlmssp.h" #include "../auth/ntlmssp/ntlmssp_private.h" +#include "lib/crypto/gnutls_helpers.h" +#include +#include + #undef DBGC_CLASS #define DBGC_CLASS DBGC_AUTH @@ -218,3 +224,95 @@ const DATA_BLOB ntlmssp_version_blob(void) return data_blob_const(version_buffer, ARRAY_SIZE(version_buffer)); } + +NTSTATUS ntlmssp_hash_channel_bindings(struct gensec_security *gensec_security, + uint8_t cb_hash[16]) +{ + const struct gensec_channel_bindings *cb = + gensec_security->channel_bindings; + gnutls_hash_hd_t hash_hnd = NULL; + uint8_t uint32buf[4]; + int rc; + + if (cb == NULL) { + memset(cb_hash, 0, 16); + return NT_STATUS_OK; + } + + GNUTLS_FIPS140_SET_LAX_MODE(); + rc = gnutls_hash_init(&hash_hnd, GNUTLS_DIG_MD5); + if (rc < 0) { + GNUTLS_FIPS140_SET_STRICT_MODE(); + return gnutls_error_to_ntstatus(rc, NT_STATUS_HMAC_NOT_SUPPORTED); + } + + SIVAL(uint32buf, 0, cb->initiator_addrtype); + rc = gnutls_hash(hash_hnd, uint32buf, sizeof(uint32buf)); + if (rc < 0) { + gnutls_hash_deinit(hash_hnd, NULL); + GNUTLS_FIPS140_SET_STRICT_MODE(); + return gnutls_error_to_ntstatus(rc, NT_STATUS_HMAC_NOT_SUPPORTED); + } + SIVAL(uint32buf, 0, cb->initiator_address.length); + rc = gnutls_hash(hash_hnd, uint32buf, sizeof(uint32buf)); + if (rc < 0) { + gnutls_hash_deinit(hash_hnd, NULL); + GNUTLS_FIPS140_SET_STRICT_MODE(); + return gnutls_error_to_ntstatus(rc, NT_STATUS_HMAC_NOT_SUPPORTED); + } + if (cb->initiator_address.length > 0) { + rc = gnutls_hash(hash_hnd, + cb->initiator_address.data, + cb->initiator_address.length); + if (rc < 0) { + gnutls_hash_deinit(hash_hnd, NULL); + GNUTLS_FIPS140_SET_STRICT_MODE(); + return gnutls_error_to_ntstatus(rc, NT_STATUS_HMAC_NOT_SUPPORTED); + } + } + SIVAL(uint32buf, 0, cb->acceptor_addrtype); + rc = gnutls_hash(hash_hnd, uint32buf, sizeof(uint32buf)); + if (rc < 0) { + gnutls_hash_deinit(hash_hnd, NULL); + GNUTLS_FIPS140_SET_STRICT_MODE(); + return gnutls_error_to_ntstatus(rc, NT_STATUS_HMAC_NOT_SUPPORTED); + } + SIVAL(uint32buf, 0, cb->acceptor_address.length); + rc = gnutls_hash(hash_hnd, uint32buf, sizeof(uint32buf)); + if (rc < 0) { + gnutls_hash_deinit(hash_hnd, NULL); + GNUTLS_FIPS140_SET_STRICT_MODE(); + return gnutls_error_to_ntstatus(rc, NT_STATUS_HMAC_NOT_SUPPORTED); + } + if (cb->acceptor_address.length > 0) { + rc = gnutls_hash(hash_hnd, + cb->acceptor_address.data, + cb->acceptor_address.length); + if (rc < 0) { + gnutls_hash_deinit(hash_hnd, NULL); + GNUTLS_FIPS140_SET_STRICT_MODE(); + return gnutls_error_to_ntstatus(rc, NT_STATUS_HMAC_NOT_SUPPORTED); + } + } + SIVAL(uint32buf, 0, cb->application_data.length); + rc = gnutls_hash(hash_hnd, uint32buf, sizeof(uint32buf)); + if (rc < 0) { + gnutls_hash_deinit(hash_hnd, NULL); + GNUTLS_FIPS140_SET_STRICT_MODE(); + return gnutls_error_to_ntstatus(rc, NT_STATUS_HMAC_NOT_SUPPORTED); + } + if (cb->application_data.length > 0) { + rc = gnutls_hash(hash_hnd, + cb->application_data.data, + cb->application_data.length); + if (rc < 0) { + gnutls_hash_deinit(hash_hnd, NULL); + GNUTLS_FIPS140_SET_STRICT_MODE(); + return gnutls_error_to_ntstatus(rc, NT_STATUS_HMAC_NOT_SUPPORTED); + } + } + + gnutls_hash_deinit(hash_hnd, cb_hash); + GNUTLS_FIPS140_SET_STRICT_MODE(); + return NT_STATUS_OK; +} -- 2.11.4.GIT