Print this page
NEX-16824 SMB client connection setup rework
NEX-17232 SMB client reconnect failures
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
and: (improve debug)
SUP-513 Unable to join AD domain (fix lint)
SUP-513 Unable to join AD domain (with NtlmMinSeverSec set in the registry)
 Implement "Extended Session Security" and "Key Exchange" in NTLMSSP
re #12739 rb4271 AD join with lmauth_level=2 fails
re #12394 rb3934 Even NULL sessions should use SPNEGO

@@ -19,11 +19,11 @@
  * CDDL HEADER END
  */
 
 /*
  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
  */
 
 /*
  * NT Lan Manager Security Support Provider (NTLMSSP)
  *

@@ -65,17 +65,18 @@
 #include "ssp.h"
 #include "ntlm.h"
 #include "ntlmssp.h"
 
 /* A shorter alias for a crazy long name from [MS-NLMP] */
-#define NTLMSSP_NEGOTIATE_NTLM2 \
+#define NTLMSSP_NEGOTIATE_ESS \
         NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY
 
 typedef struct ntlmssp_state {
         uint32_t ss_flags;
         char *ss_target_name;   /* Primary domain or server name */
         struct mbuf *ss_target_info;
+        uchar_t ss_ssnkey[NTLM_HASH_SZ];
         uchar_t ss_kxkey[NTLM_HASH_SZ];
 } ntlmssp_state_t;
 
 /*
  * So called "security buffer".

@@ -88,12 +89,11 @@
 };
 #define ID_SZ 8
 static const char ntlmssp_id[ID_SZ] = "NTLMSSP";
 
 static int
-ntlm_rand_ssn_key(struct smb_ctx *ctx,
-        ntlmssp_state_t *ssp_st, struct mbdata *ek_mbp);
+ntlm_rand_ssn_key(ntlmssp_state_t *ssp_st, struct mbdata *ek_mbp);
 
 /*
  * Get a "security buffer" (header part)
  */
 static int

@@ -247,20 +247,18 @@
             NTLMSSP_REQUEST_TARGET |
             NTLMSSP_NEGOTIATE_SIGN |
             NTLMSSP_NEGOTIATE_SEAL |
             /* NTLMSSP_NEGOTIATE_LM_KEY (never) */
             NTLMSSP_NEGOTIATE_NTLM |
-            /* NTLMSSP_NEGOTIATE_ALWAYS_SIGN (set below) */
-            NTLMSSP_NEGOTIATE_NTLM2 |
+            NTLMSSP_NEGOTIATE_ALWAYS_SIGN |
+            NTLMSSP_NEGOTIATE_ESS |
             NTLMSSP_NEGOTIATE_128 |
             NTLMSSP_NEGOTIATE_KEY_EXCH |
             NTLMSSP_NEGOTIATE_56;
 
-        if (ctx->ct_vcflags & SMBV_WILL_SIGN) {
-                ssp_st->ss_flags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN;
-                ctx->ct_hflags2 |= SMB_FLAGS2_SECURITY_SIGNATURE;
-        }
+        if ((ctx->ct_vopt & SMBVOPT_SIGNING_ENABLED) == 0)
+                ssp_st->ss_flags &= ~NTLMSSP_NEGOTIATE_ALWAYS_SIGN;
 
         bcopy(ntlmssp_id, &hdr.h_id, ID_SZ);
         hdr.h_type = NTLMSSP_MSGTYPE_NEGOTIATE;
         hdr.h_flags = ssp_st->ss_flags;
 

@@ -445,69 +443,76 @@
          */
         if (ctx->ct_authflags & SMB_AT_ANON) {
                 /*
                  * We're setting up a NULL session, meaning
                  * the lm_mbc, nt_mbc parts remain empty.
-                 * Let's add the "anon" flag (hint).
-                 * As there is no session key, disable the
-                 * fancy session key stuff.
+                 * Let's add the "anon" flag (hint), and
+                 * as we have no OWF hashes, we can't use
+                 * "extended session security" (_ESS).
+                 * The SessionBaseKey is all zeros, so
+                 * the KeyExchangeKey is too.  Otherwise
+                 * this is like NTLMv2/LMv2
                  */
-                hdr.h_flags |= NTLMSSP_NEGOTIATE_NULL_SESSION;
-                ssp_st->ss_flags &= ~(
-                    NTLMSSP_NEGOTIATE_NTLM2 |
-                    NTLMSSP_NEGOTIATE_KEY_EXCH);
+                ssp_st->ss_flags |= NTLMSSP_NEGOTIATE_NULL_SESSION;
+                ssp_st->ss_flags &= ~NTLMSSP_NEGOTIATE_ESS;
+                hdr.h_flags = ssp_st->ss_flags;
                 err = 0;
+                /* KeyExchangeKey = SessionBaseKey = (zeros) */
+                memset(ssp_st->ss_ssnkey, 0, NTLM_HASH_SZ);
+                memset(ssp_st->ss_kxkey, 0, NTLM_HASH_SZ);
         } else if (ctx->ct_authflags & SMB_AT_NTLM2) {
                 /*
                  * Doing NTLMv2/LMv2
                  */
                 err = ntlm_build_target_info(ctx,
                     ssp_st->ss_target_info, &ti_mbc);
                 if (err)
                         goto out;
                 err = ntlm_put_v2_responses(ctx, &ti_mbc,
-                    &lm_mbc, &nt_mbc);
+                    &lm_mbc, &nt_mbc, ssp_st->ss_ssnkey);
                 if (err)
                         goto out;
-                /* The "key exg. key" is the session base key */
-                memcpy(ssp_st->ss_kxkey, ctx->ct_ssn_key, NTLM_HASH_SZ);
-
-        } else if (ssp_st->ss_flags & NTLMSSP_NEGOTIATE_NTLM2) {
+                /* KeyExchangeKey = SessionBaseKey (v2) */
+                memcpy(ssp_st->ss_kxkey, ssp_st->ss_ssnkey, NTLM_HASH_SZ);
+        } else if (ssp_st->ss_flags & NTLMSSP_NEGOTIATE_ESS) {
                 /*
                  * Doing NTLM ("v1x") which is NTLM with
                  * "Extended Session Security"
                  */
                 err = ntlm_put_v1x_responses(ctx,
-                    &lm_mbc, &nt_mbc);
+                    &lm_mbc, &nt_mbc, ssp_st->ss_ssnkey);
                 if (err)
                         goto out;
-                /* Compute the "Key exchange key". */
-                ntlm2_kxkey(ctx, &lm_mbc, ssp_st->ss_kxkey);
+                /*
+                 * "v1x computes the KeyExchangeKey from both the
+                 * server and client nonce and (v1) SessionBaseKey.
+                 */
+                ntlm2_kxkey(ctx, &lm_mbc, ssp_st->ss_ssnkey,
+                    ssp_st->ss_kxkey);
         } else {
                 /*
                  * Doing plain old NTLM (and LM if enabled)
                  */
                 err = ntlm_put_v1_responses(ctx,
-                    &lm_mbc, &nt_mbc);
+                    &lm_mbc, &nt_mbc, ssp_st->ss_ssnkey);
                 if (err)
                         goto out;
-                /* The "key exg. key" is the session base key */
-                memcpy(ssp_st->ss_kxkey, ctx->ct_ssn_key, NTLM_HASH_SZ);
+                /* KeyExchangeKey = SessionBaseKey (v1) */
+                memcpy(ssp_st->ss_kxkey, ssp_st->ss_ssnkey, NTLM_HASH_SZ);
         }
 
         /*
-         * Compute the "Exported Session Key" and (possibly)
-         * the "Encrypted Random Sesion Key".
-         * [MS-NLMP 3.1.5.1.2]
+         * Compute the "ExportedSessionKey" and (possibly) the
+         * "EncryptedRandomSesionKey". [MS-NLMP 3.1.5.1.2]
          */
         if (ssp_st->ss_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) {
-                err = ntlm_rand_ssn_key(ctx, ssp_st, &ek_mbc);
+                err = ntlm_rand_ssn_key(ssp_st, &ek_mbc);
                 if (err)
                         goto out;
         } else {
                 /* ExportedSessionKey is the KeyExchangeKey */
-                memcpy(ctx->ct_ssn_key, ssp_st->ss_kxkey, NTLM_HASH_SZ);
+                memcpy(ssp_st->ss_ssnkey, ssp_st->ss_kxkey, NTLM_HASH_SZ);
                 /* EncryptedRandomSessionKey remains NULL */
         }
 
         err = mb_put_sb_data(&mb2, &hdr.h_lm_resp, lm_mbc.mb_top);
         lm_mbc.mb_top = NULL; /* consumed */

@@ -588,11 +593,10 @@
  * With "key exchange", we replace the ExportedSessionKey
  * with random data and send that (encrypted) to the peer.
  */
 static int
 ntlm_rand_ssn_key(
-        struct smb_ctx *ctx,
         ntlmssp_state_t *ssp_st,
         struct mbdata *ek_mbp)
 {
 
         uchar_t *encr_ssn_key;

@@ -601,52 +605,47 @@
         if ((err = mb_init(ek_mbp)) != 0)
                 return (err);
         encr_ssn_key = mb_reserve(ek_mbp, NTLM_HASH_SZ);
 
         /* Set "ExportedSessionKey to NONCE(16) */
-        (void) smb_get_urandom(ctx->ct_ssn_key, NTLM_HASH_SZ);
+        (void) smb_get_urandom(ssp_st->ss_ssnkey, NTLM_HASH_SZ);
 
         /* Set "EncryptedRandomSessionKey" to RC4(...) */
         err = smb_encrypt_RC4(encr_ssn_key, NTLM_HASH_SZ,
             ssp_st->ss_kxkey, NTLM_HASH_SZ,
-            ctx->ct_ssn_key, NTLM_HASH_SZ);
+            ssp_st->ss_ssnkey, NTLM_HASH_SZ);
 
         return (err);
 }
 
 /*
  * ntlmssp_final
  *
  * Called after successful authentication.
- * Setup the MAC key for signing.
+ * Save the session key.
  */
 int
 ntlmssp_final(struct ssp_ctx *sp)
 {
         struct smb_ctx *ctx = sp->smb_ctx;
+        ntlmssp_state_t *ssp_st = sp->sp_private;
         int err = 0;
 
         /*
-         * MAC_key is just the session key, but
-         * Only on the first successful auth.
+         * Update/save the session key.
          */
-        if ((ctx->ct_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE) &&
-            (ctx->ct_mackey == NULL)) {
-                ctx->ct_mackeylen = NTLM_HASH_SZ;
-                ctx->ct_mackey = malloc(ctx->ct_mackeylen);
-                if (ctx->ct_mackey == NULL) {
-                        ctx->ct_mackeylen = 0;
+        if (ctx->ct_ssnkey_buf != NULL) {
+                free(ctx->ct_ssnkey_buf);
+                ctx->ct_ssnkey_buf = NULL;
+        }
+        ctx->ct_ssnkey_buf = malloc(NTLM_HASH_SZ);
+        if (ctx->ct_ssnkey_buf == NULL) {
                         err = ENOMEM;
                         goto out;
                 }
-                memcpy(ctx->ct_mackey, ctx->ct_ssn_key, NTLM_HASH_SZ);
-                /*
-                 * Apparently, the server used seq. no. zero
-                 * for our previous message, so next is two.
-                 */
-                ctx->ct_mac_seqno = 2;
-        }
+        ctx->ct_ssnkey_len = NTLM_HASH_SZ;
+        memcpy(ctx->ct_ssnkey_buf, ssp_st->ss_ssnkey, NTLM_HASH_SZ);
 
 out:
         return (err);
 }
 

@@ -726,17 +725,21 @@
  */
 int
 ntlmssp_init_client(struct ssp_ctx *sp)
 {
         ntlmssp_state_t *ssp_st;
+        smb_ctx_t *ctx = sp->smb_ctx;
 
-        if ((sp->smb_ctx->ct_authflags &
+        if ((ctx->ct_authflags &
             (SMB_AT_NTLM2 | SMB_AT_NTLM1 | SMB_AT_ANON)) == 0) {
                 DPRINT("No NTLM authflags");
                 return (EINVAL);
         }
 
+        /* Get the client nonce. */
+        (void) smb_get_urandom(ctx->ct_clnonce, NTLM_CHAL_SZ);
+
         ssp_st = calloc(1, sizeof (*ssp_st));
         if (ssp_st == NULL)
                 return (ENOMEM);
 
         sp->sp_nexttok = ntlmssp_next_token;