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,29 **** * CDDL HEADER END */ /* * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. ! * Copyright 2013 Nexenta Systems, Inc. All rights reserved. */ /* * NT Lan Manager Security Support Provider (NTLMSSP) * --- 19,29 ---- * CDDL HEADER END */ /* * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. ! * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ /* * NT Lan Manager Security Support Provider (NTLMSSP) *
*** 65,81 **** #include "ssp.h" #include "ntlm.h" #include "ntlmssp.h" /* A shorter alias for a crazy long name from [MS-NLMP] */ ! #define NTLMSSP_NEGOTIATE_NTLM2 \ 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_kxkey[NTLM_HASH_SZ]; } ntlmssp_state_t; /* * So called "security buffer". --- 65,82 ---- #include "ssp.h" #include "ntlm.h" #include "ntlmssp.h" /* A shorter alias for a crazy long name from [MS-NLMP] */ ! #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,99 **** }; #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); /* * Get a "security buffer" (header part) */ static int --- 89,99 ---- }; #define ID_SZ 8 static const char ntlmssp_id[ID_SZ] = "NTLMSSP"; static int ! ntlm_rand_ssn_key(ntlmssp_state_t *ssp_st, struct mbdata *ek_mbp); /* * Get a "security buffer" (header part) */ static int
*** 247,266 **** 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_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; ! } bcopy(ntlmssp_id, &hdr.h_id, ID_SZ); hdr.h_type = NTLMSSP_MSGTYPE_NEGOTIATE; hdr.h_flags = ssp_st->ss_flags; --- 247,264 ---- NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_SIGN | NTLMSSP_NEGOTIATE_SEAL | /* NTLMSSP_NEGOTIATE_LM_KEY (never) */ NTLMSSP_NEGOTIATE_NTLM | ! NTLMSSP_NEGOTIATE_ALWAYS_SIGN | ! NTLMSSP_NEGOTIATE_ESS | NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_KEY_EXCH | NTLMSSP_NEGOTIATE_56; ! 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,513 **** */ 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. */ ! hdr.h_flags |= NTLMSSP_NEGOTIATE_NULL_SESSION; ! ssp_st->ss_flags &= ~( ! NTLMSSP_NEGOTIATE_NTLM2 | ! NTLMSSP_NEGOTIATE_KEY_EXCH); err = 0; } 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); 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) { /* * Doing NTLM ("v1x") which is NTLM with * "Extended Session Security" */ err = ntlm_put_v1x_responses(ctx, ! &lm_mbc, &nt_mbc); if (err) goto out; ! /* Compute the "Key exchange key". */ ! ntlm2_kxkey(ctx, &lm_mbc, ssp_st->ss_kxkey); } else { /* * Doing plain old NTLM (and LM if enabled) */ err = ntlm_put_v1_responses(ctx, ! &lm_mbc, &nt_mbc); 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); } /* ! * Compute the "Exported Session Key" and (possibly) ! * the "Encrypted Random Sesion Key". ! * [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); if (err) goto out; } else { /* ExportedSessionKey is the KeyExchangeKey */ ! memcpy(ctx->ct_ssn_key, 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 */ --- 443,518 ---- */ 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), 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 */ ! 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, ssp_st->ss_ssnkey); if (err) goto out; ! /* 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, ssp_st->ss_ssnkey); if (err) goto out; ! /* ! * "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, ssp_st->ss_ssnkey); if (err) goto out; ! /* KeyExchangeKey = SessionBaseKey (v1) */ ! memcpy(ssp_st->ss_kxkey, ssp_st->ss_ssnkey, NTLM_HASH_SZ); } /* ! * 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(ssp_st, &ek_mbc); if (err) goto out; } else { /* ExportedSessionKey is the KeyExchangeKey */ ! 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,598 **** * 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; --- 593,602 ----
*** 601,652 **** 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); /* 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); return (err); } /* * ntlmssp_final * * Called after successful authentication. ! * Setup the MAC key for signing. */ int ntlmssp_final(struct ssp_ctx *sp) { struct smb_ctx *ctx = sp->smb_ctx; int err = 0; /* ! * MAC_key is just the session key, but ! * Only on the first successful auth. */ ! 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; 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; ! } out: return (err); } --- 605,651 ---- 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(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, ! ssp_st->ss_ssnkey, NTLM_HASH_SZ); return (err); } /* * ntlmssp_final * * Called after successful authentication. ! * 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; /* ! * Update/save the session key. */ ! 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; } ! ctx->ct_ssnkey_len = NTLM_HASH_SZ; ! memcpy(ctx->ct_ssnkey_buf, ssp_st->ss_ssnkey, NTLM_HASH_SZ); out: return (err); }
*** 726,742 **** */ int ntlmssp_init_client(struct ssp_ctx *sp) { ntlmssp_state_t *ssp_st; ! if ((sp->smb_ctx->ct_authflags & (SMB_AT_NTLM2 | SMB_AT_NTLM1 | SMB_AT_ANON)) == 0) { DPRINT("No NTLM authflags"); return (EINVAL); } ssp_st = calloc(1, sizeof (*ssp_st)); if (ssp_st == NULL) return (ENOMEM); sp->sp_nexttok = ntlmssp_next_token; --- 725,745 ---- */ int ntlmssp_init_client(struct ssp_ctx *sp) { ntlmssp_state_t *ssp_st; + smb_ctx_t *ctx = sp->smb_ctx; ! 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;