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;