auth/ntlmssp: implement channel binding support
authorStefan Metzmacher <metze@samba.org>
Tue, 11 Feb 2020 15:07:05 +0000 (16:07 +0100)
committerAndrew Bartlett <abartlet@samba.org>
Tue, 23 Apr 2024 23:50:34 +0000 (23:50 +0000)
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15621

Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
auth/ntlmssp/ntlmssp_client.c
auth/ntlmssp/ntlmssp_private.h
auth/ntlmssp/ntlmssp_server.c
auth/ntlmssp/ntlmssp_util.c

index 337aeed922990459eeee6b24b44f8c9ec23a0b40..d8dc1d2940bfc37232069767c33be40bf931d609 100644 (file)
@@ -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);
index 4d84e3347b6c8608912f3dc3f1ae1bc466fd7177..7b939b80ae2117393774c20fda2cb9dab321b9ce 100644 (file)
@@ -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  */
 
index 64b96283eb2fe2de03b591e2f22a6618daabdb96..1e49379a8ed6668e2e57a386dd51e49d643d326d 100644 (file)
@@ -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) {
index 6f3b474fd713d2be213acc8c93201d9dd4b3de0e..b8dc84e1652b129358ce93f3069d5ef3cf40cd49 100644 (file)
 */
 
 #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 <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+
 #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;
+}