1
0
mirror of https://github.com/samba-team/samba.git synced 2025-02-14 01:57:53 +03:00

(only for HEAD at the moment).

Add NTLMv2 support to our client, used when so configured ('client use NTLMv2 =
yes') and only when 'client use spengo = no'.  (A new option to allow the
client and server ends to chose spnego seperatly).

NTLMv2 signing doesn't yet work, and NTLMv2 is not done for NTLMSSP yet.

Also some parinoia checks in our input parsing.

Andrew Bartlett
This commit is contained in:
Andrew Bartlett -
parent 9cd29f2bac
commit 85e9c060ea
7 changed files with 200 additions and 112 deletions

View File

@ -62,7 +62,7 @@ typedef struct smb_sign_info {
BOOL negotiated_smb_signing;
BOOL temp_smb_signing;
size_t mac_key_len;
uint8 mac_key[44];
uint8 mac_key[64];
uint32 send_seq_num;
uint32 reply_seq_num;
BOOL allow_smb_signing;

View File

@ -2,7 +2,7 @@
Unix SMB/CIFS implementation.
client connect/disconnect routines
Copyright (C) Andrew Tridgell 1994-1998
Copyright (C) Andrew Barteltt 2001-2002
Copyright (C) Andrew Barteltt 2001-2003
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -45,7 +45,7 @@ static const struct {
****************************************************************************/
static BOOL cli_session_setup_lanman2(struct cli_state *cli, const char *user,
const char *pass, int passlen, const char *workgroup)
const char *pass, size_t passlen, const char *workgroup)
{
fstring pword;
char *p;
@ -228,7 +228,7 @@ static BOOL cli_session_setup_plaintext(struct cli_state *cli, const char *user,
return True;
}
static void set_signing_on_cli (struct cli_state *cli, const char* pass, uint8 response[24])
static void set_signing_on_cli (struct cli_state *cli, uint8 user_session_key[16], DATA_BLOB response)
{
uint8 zero_sig[8];
ZERO_STRUCT(zero_sig);
@ -242,7 +242,7 @@ static void set_signing_on_cli (struct cli_state *cli, const char* pass, uint8 r
DEBUG(3, ("smb signing enabled!\n"));
cli->sign_info.use_smb_signing = True;
cli_calculate_mac_key(cli, pass, response);
cli_calculate_mac_key(cli, user_session_key, response);
} else {
DEBUG(5, ("smb signing NOT enabled!\n"));
}
@ -265,25 +265,90 @@ static void set_temp_signing_on_cli(struct cli_state *cli)
****************************************************************************/
static BOOL cli_session_setup_nt1(struct cli_state *cli, const char *user,
const char *pass, int passlen,
const char *ntpass, int ntpasslen,
const char *pass, size_t passlen,
const char *ntpass, size_t ntpasslen,
const char *workgroup)
{
uint32 capabilities = cli_session_setup_capabilities(cli);
uchar pword[24];
uchar ntpword[24];
DATA_BLOB lm_response = data_blob(NULL, 0);
DATA_BLOB nt_response = data_blob(NULL, 0);
uchar user_session_key[16];
char *p;
BOOL have_plaintext = False;
if (passlen > sizeof(pword) || ntpasslen > sizeof(ntpword))
return False;
if (passlen != 24) {
/* non encrypted password supplied. Ignore ntpass. */
passlen = 24;
ntpasslen = 24;
SMBencrypt(pass,cli->secblob.data,pword);
SMBNTencrypt(pass,cli->secblob.data,ntpword);
uchar nt_hash[16];
E_md4hash(pass, nt_hash);
if (lp_client_ntlmv2_auth()) {
uchar ntlm_v2_hash[16];
uchar ntlmv2_response[16];
uchar lmv2_response[16];
DATA_BLOB ntlmv2_client_data;
DATA_BLOB lmv2_client_data;
DATA_BLOB server_chal;
/* We don't use the NT# directly. Instead we use it mashed up with
the username and domain.
This prevents username swapping during the auth exchange
*/
if (!ntv2_owf_gen(nt_hash, user, workgroup, ntlm_v2_hash)) {
return False;
}
server_chal = data_blob(cli->secblob.data, MIN(cli->secblob.length, 8));
/* NTLMv2 */
/* We also get to specify some random data */
ntlmv2_client_data = data_blob(NULL, 20);
generate_random_buffer(ntlmv2_client_data.data, ntlmv2_client_data.length, False);
memset(ntlmv2_client_data.data, 'A', ntlmv2_client_data.length);
/* Given that data, and the challenge from the server, generate a response */
SMBOWFencrypt_ntv2(ntlm_v2_hash, server_chal, ntlmv2_client_data, ntlmv2_response);
/* put it into nt_response, for the code below to put into the packet */
nt_response = data_blob(NULL, ntlmv2_client_data.length + sizeof(ntlmv2_response));
memcpy(nt_response.data, ntlmv2_response, sizeof(ntlmv2_response));
/* after the first 16 bytes is the random data we generated above, so the server can verify us with it */
memcpy(nt_response.data + sizeof(ntlmv2_response), ntlmv2_client_data.data, ntlmv2_client_data.length);
data_blob_free(&ntlmv2_client_data);
/* LMv2 */
/* We also get to specify some random data, but only 8 bytes (24 byte total response) */
lmv2_client_data = data_blob(NULL, 8);
generate_random_buffer(lmv2_client_data.data, lmv2_client_data.length, False);
memset(lmv2_client_data.data, 'B', lmv2_client_data.length);
/* Calculate response */
SMBOWFencrypt_ntv2(ntlm_v2_hash, server_chal, lmv2_client_data, lmv2_response);
/* Calculate response */
lm_response = data_blob(NULL, lmv2_client_data.length + sizeof(lmv2_response));
memcpy(lm_response.data, lmv2_response, sizeof(lmv2_response));
/* after the first 16 bytes is the 8 bytes of random data we made above */
memcpy(lm_response.data + sizeof(lmv2_response), lmv2_client_data.data, lmv2_client_data.length);
data_blob_free(&lmv2_client_data);
data_blob_free(&server_chal);
/* The NTLMv2 calculations also provide a session key, for signing etc later */
SMBsesskeygen_ntv2(ntlm_v2_hash, ntlmv2_response, user_session_key);
} else {
/* non encrypted password supplied. Ignore ntpass. */
if (lp_client_lanman_auth()) {
lm_response = data_blob(NULL, 24);
SMBencrypt(pass,cli->secblob.data,lm_response.data);
}
nt_response = data_blob(NULL, 24);
SMBNTencrypt(pass,cli->secblob.data,nt_response.data);
SMBsesskeygen_ntv1(nt_hash, NULL, user_session_key);
}
have_plaintext = True;
set_temp_signing_on_cli(cli);
@ -291,11 +356,9 @@ static BOOL cli_session_setup_nt1(struct cli_state *cli, const char *user,
/* pre-encrypted password supplied. Only used for
security=server, can't do
signing becouse we don't have oringial key */
memcpy(pword, pass, 24);
if (ntpasslen == 24)
memcpy(ntpword, ntpass, 24);
else
ZERO_STRUCT(ntpword);
lm_response = data_blob(pass, passlen);
nt_response = data_blob(ntpass, ntpasslen);
}
/* send a session setup command */
@ -310,28 +373,35 @@ static BOOL cli_session_setup_nt1(struct cli_state *cli, const char *user,
SSVAL(cli->outbuf,smb_vwv3,2);
SSVAL(cli->outbuf,smb_vwv4,cli->pid);
SIVAL(cli->outbuf,smb_vwv5,cli->sesskey);
SSVAL(cli->outbuf,smb_vwv7,passlen);
SSVAL(cli->outbuf,smb_vwv8,ntpasslen);
SSVAL(cli->outbuf,smb_vwv7,lm_response.length);
SSVAL(cli->outbuf,smb_vwv8,nt_response.length);
SIVAL(cli->outbuf,smb_vwv11,capabilities);
p = smb_buf(cli->outbuf);
memcpy(p,pword,passlen); p += passlen;
memcpy(p,ntpword,ntpasslen); p += ntpasslen;
if (lm_response.length) {
memcpy(p,lm_response.data, lm_response.length); p += lm_response.length;
}
if (nt_response.length) {
memcpy(p,nt_response.data, nt_response.length); p += nt_response.length;
}
p += clistr_push(cli, p, user, -1, STR_TERMINATE);
p += clistr_push(cli, p, workgroup, -1, STR_TERMINATE);
p += clistr_push(cli, p, "Unix", -1, STR_TERMINATE);
p += clistr_push(cli, p, "Samba", -1, STR_TERMINATE);
cli_setup_bcc(cli, p);
if (!cli_send_smb(cli))
return False;
if (!cli_receive_smb(cli))
if (!cli_send_smb(cli) || !cli_receive_smb(cli)) {
data_blob_free(&lm_response);
data_blob_free(&nt_response);
return False;
}
show_msg(cli->inbuf);
if (cli_is_error(cli))
if (cli_is_error(cli)) {
data_blob_free(&lm_response);
data_blob_free(&nt_response);
return False;
}
/* use the returned vuid from now on */
cli->vuid = SVAL(cli->inbuf,smb_uid);
@ -345,9 +415,11 @@ static BOOL cli_session_setup_nt1(struct cli_state *cli, const char *user,
if (have_plaintext) {
/* Have plaintext orginal */
set_signing_on_cli(cli, pass, ntpword);
set_signing_on_cli(cli, user_session_key, nt_response);
}
data_blob_free(&lm_response);
data_blob_free(&nt_response);
return True;
}
@ -529,6 +601,10 @@ static BOOL cli_session_setup_ntlmssp(struct cli_state *cli, const char *user,
return False;
}
if (challenge_blob.length < 8) {
return False;
}
DEBUG(10, ("Challenge:\n"));
dump_data(10, challenge_blob.data, 8);

View File

@ -251,8 +251,8 @@ struct cli_state *cli_initialise(struct cli_state *cli)
cli->outbuf = (char *)malloc(cli->bufsize);
cli->inbuf = (char *)malloc(cli->bufsize);
cli->oplock_handler = cli_oplock_ack;
if (lp_use_spnego())
cli->use_spnego = True;
cli->use_spnego = lp_client_use_spnego();
cli->capabilities = CAP_UNICODE | CAP_STATUS32;

View File

@ -709,7 +709,8 @@ BOOL msrpc_parse(DATA_BLOB *blob,
case 'b':
b = (DATA_BLOB *)va_arg(ap, void *);
len1 = va_arg(ap, unsigned);
*b = data_blob(blob->data + head_ofs, len1);
*b = data_blob(blob->data + head_ofs,
MIN(len1, blob->length - head_ofs));
head_ofs += len1;
break;
case 'd':

View File

@ -37,71 +37,7 @@ static const uint8 *get_challenge(struct ntlmssp_state *ntlmssp_state)
return chal;
}
NTSTATUS ntlmssp_server_start(NTLMSSP_STATE **ntlmssp_state)
{
TALLOC_CTX *mem_ctx;
mem_ctx = talloc_init("NTLMSSP context");
*ntlmssp_state = talloc_zero(mem_ctx, sizeof(**ntlmssp_state));
if (!*ntlmssp_state) {
DEBUG(0,("ntlmssp_start: talloc failed!\n"));
talloc_destroy(mem_ctx);
return NT_STATUS_NO_MEMORY;
}
ZERO_STRUCTP(*ntlmssp_state);
(*ntlmssp_state)->mem_ctx = mem_ctx;
(*ntlmssp_state)->get_challenge = get_challenge;
(*ntlmssp_state)->get_global_myname = global_myname;
(*ntlmssp_state)->get_domain = lp_workgroup;
(*ntlmssp_state)->server_role = ROLE_DOMAIN_MEMBER; /* a good default */
return NT_STATUS_OK;
}
NTSTATUS ntlmssp_server_end(NTLMSSP_STATE **ntlmssp_state)
{
TALLOC_CTX *mem_ctx = (*ntlmssp_state)->mem_ctx;
data_blob_free(&(*ntlmssp_state)->chal);
data_blob_free(&(*ntlmssp_state)->lm_resp);
data_blob_free(&(*ntlmssp_state)->nt_resp);
SAFE_FREE((*ntlmssp_state)->user);
SAFE_FREE((*ntlmssp_state)->domain);
SAFE_FREE((*ntlmssp_state)->workstation);
talloc_destroy(mem_ctx);
*ntlmssp_state = NULL;
return NT_STATUS_OK;
}
NTSTATUS ntlmssp_server_update(NTLMSSP_STATE *ntlmssp_state,
DATA_BLOB request, DATA_BLOB *reply)
{
uint32 ntlmssp_command;
*reply = data_blob(NULL, 0);
if (!msrpc_parse(&request, "Cd",
"NTLMSSP",
&ntlmssp_command)) {
return NT_STATUS_LOGON_FAILURE;
}
if (ntlmssp_command == NTLMSSP_NEGOTIATE) {
return ntlmssp_negotiate(ntlmssp_state, request, reply);
} else if (ntlmssp_command == NTLMSSP_AUTH) {
return ntlmssp_auth(ntlmssp_state, request, reply);
} else {
return NT_STATUS_LOGON_FAILURE;
}
}
static const char *ntlmssp_target_name(NTLMSSP_STATE *ntlmssp_state,
static const char *ntlmssp_target_name(struct ntlmssp_state *ntlmssp_state,
uint32 neg_flags, uint32 *chal_flags)
{
if (neg_flags & NTLMSSP_REQUEST_TARGET) {
@ -119,8 +55,8 @@ static const char *ntlmssp_target_name(NTLMSSP_STATE *ntlmssp_state,
}
}
NTSTATUS ntlmssp_negotiate(NTLMSSP_STATE *ntlmssp_state,
DATA_BLOB request, DATA_BLOB *reply)
static NTSTATUS ntlmssp_server_negotiate(struct ntlmssp_state *ntlmssp_state,
DATA_BLOB request, DATA_BLOB *reply)
{
DATA_BLOB struct_blob;
fstring dnsname, dnsdomname;
@ -222,8 +158,8 @@ NTSTATUS ntlmssp_negotiate(NTLMSSP_STATE *ntlmssp_state,
return NT_STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS ntlmssp_auth(NTLMSSP_STATE *ntlmssp_state,
DATA_BLOB request, DATA_BLOB *reply)
static NTSTATUS ntlmssp_server_auth(struct ntlmssp_state *ntlmssp_state,
DATA_BLOB request, DATA_BLOB *reply)
{
DATA_BLOB sess_key;
uint32 ntlmssp_command, neg_flags;
@ -279,3 +215,68 @@ NTSTATUS ntlmssp_auth(NTLMSSP_STATE *ntlmssp_state,
return nt_status;
}
NTSTATUS ntlmssp_server_start(NTLMSSP_STATE **ntlmssp_state)
{
TALLOC_CTX *mem_ctx;
mem_ctx = talloc_init("NTLMSSP context");
*ntlmssp_state = talloc_zero(mem_ctx, sizeof(**ntlmssp_state));
if (!*ntlmssp_state) {
DEBUG(0,("ntlmssp_start: talloc failed!\n"));
talloc_destroy(mem_ctx);
return NT_STATUS_NO_MEMORY;
}
ZERO_STRUCTP(*ntlmssp_state);
(*ntlmssp_state)->mem_ctx = mem_ctx;
(*ntlmssp_state)->get_challenge = get_challenge;
(*ntlmssp_state)->get_global_myname = global_myname;
(*ntlmssp_state)->get_domain = lp_workgroup;
(*ntlmssp_state)->server_role = ROLE_DOMAIN_MEMBER; /* a good default */
return NT_STATUS_OK;
}
NTSTATUS ntlmssp_server_end(NTLMSSP_STATE **ntlmssp_state)
{
TALLOC_CTX *mem_ctx = (*ntlmssp_state)->mem_ctx;
data_blob_free(&(*ntlmssp_state)->chal);
data_blob_free(&(*ntlmssp_state)->lm_resp);
data_blob_free(&(*ntlmssp_state)->nt_resp);
SAFE_FREE((*ntlmssp_state)->user);
SAFE_FREE((*ntlmssp_state)->domain);
SAFE_FREE((*ntlmssp_state)->workstation);
talloc_destroy(mem_ctx);
*ntlmssp_state = NULL;
return NT_STATUS_OK;
}
NTSTATUS ntlmssp_server_update(NTLMSSP_STATE *ntlmssp_state,
DATA_BLOB request, DATA_BLOB *reply)
{
uint32 ntlmssp_command;
*reply = data_blob(NULL, 0);
if (!msrpc_parse(&request, "Cd",
"NTLMSSP",
&ntlmssp_command)) {
return NT_STATUS_LOGON_FAILURE;
}
if (ntlmssp_command == NTLMSSP_NEGOTIATE) {
return ntlmssp_server_negotiate(ntlmssp_state, request, reply);
} else if (ntlmssp_command == NTLMSSP_AUTH) {
return ntlmssp_server_auth(ntlmssp_state, request, reply);
} else {
return NT_STATUS_LOGON_FAILURE;
}
}

View File

@ -363,19 +363,17 @@ BOOL decode_pw_buffer(char in_buffer[516], char *new_pwrd,
SMB signing - setup the MAC key.
************************************************************/
void cli_calculate_mac_key(struct cli_state *cli, const char *plain_passwd, const uchar resp[24])
void cli_calculate_mac_key(struct cli_state *cli, const uchar user_session_key[16], const DATA_BLOB response)
{
uchar nt_hash[16];
E_md4hash(plain_passwd, nt_hash);
mdfour(&cli->sign_info.mac_key[0], nt_hash, sizeof(nt_hash));
memcpy(&cli->sign_info.mac_key[16],resp,24);
cli->sign_info.mac_key_len = 40;
memcpy(&cli->sign_info.mac_key[0], user_session_key, 16);
memcpy(&cli->sign_info.mac_key[16],response.data, MIN(response.length, 40 - 16));
cli->sign_info.mac_key_len = MIN(response.length + 16, 40);
cli->sign_info.use_smb_signing = True;
/* These calls are INCONPATIBLE with SMB signing */
cli->readbraw_supported = False;
cli->writebraw_supported = False;
cli->writebraw_supported = False;
/* Reset the sequence number in case we had a previous (aborted) attempt */
cli->sign_info.send_seq_num = 2;

View File

@ -264,6 +264,10 @@ typedef struct
BOOL bAllowTrustedDomains;
BOOL bLanmanAuth;
BOOL bNTLMAuth;
BOOL bUseSpnego;
BOOL bClientLanManAuth;
BOOL bClientNTLMv2Auth;
BOOL bClientUseSpnego;
BOOL bDebugHiresTimestamp;
BOOL bDebugPid;
BOOL bDebugUid;
@ -272,7 +276,6 @@ typedef struct
BOOL bUnicode;
BOOL bUseMmap;
BOOL bHostnameLookups;
BOOL bUseSpnego;
BOOL bUnixExtensions;
BOOL bDisableNetbios;
BOOL bKernelChangeNotify;
@ -772,6 +775,8 @@ static struct parm_struct parm_table[] = {
{"restrict anonymous", P_INTEGER, P_GLOBAL, &Globals.restrict_anonymous, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
{"lanman auth", P_BOOL, P_GLOBAL, &Globals.bLanmanAuth, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
{"ntlm auth", P_BOOL, P_GLOBAL, &Globals.bNTLMAuth, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
{"client NTLMv2 auth", P_BOOL, P_GLOBAL, &Globals.bClientNTLMv2Auth, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
{"client lanman auth", P_BOOL, P_GLOBAL, &Globals.bClientLanManAuth, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
{"username", P_STRING, P_LOCAL, &sDefault.szUsername, NULL, NULL, FLAG_GLOBAL | FLAG_SHARE},
{"user", P_STRING, P_LOCAL, &sDefault.szUsername, NULL, NULL, FLAG_HIDE},
@ -864,6 +869,7 @@ static struct parm_struct parm_table[] = {
{"unix extensions", P_BOOL, P_GLOBAL, &Globals.bUnixExtensions, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
{"use spnego", P_BOOL, P_GLOBAL, &Globals.bUseSpnego, NULL, NULL, FLAG_DEVELOPER},
{"client signing", P_BOOL, P_GLOBAL, &Globals.client_signing, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
{"client use spnego", P_BOOL, P_GLOBAL, &Globals.bClientUseSpnego, NULL, NULL, FLAG_DEVELOPER},
{"Tuning Options", P_SEP, P_SEPARATOR},
@ -1375,8 +1381,10 @@ static void init_globals(void)
Globals.bNTStatusSupport = True; /* Use NT status by default. */
Globals.bStatCache = True; /* use stat cache by default */
Globals.restrict_anonymous = 0;
Globals.bClientLanManAuth = True; /* Do use the LanMan hash if it is available */
Globals.bLanmanAuth = True; /* Do use the LanMan hash if it is available */
Globals.bNTLMAuth = True; /* Do use NTLMv1 if it is available (otherwise NTLMv2) */
Globals.map_to_guest = 0; /* By Default, "Never" */
Globals.min_passwd_length = MINPASSWDLENGTH; /* By Default, 5. */
Globals.oplock_break_wait_time = 0; /* By Default, 0 msecs. */
@ -1453,6 +1461,7 @@ static void init_globals(void)
Globals.name_cache_timeout = 660; /* In seconds */
Globals.bUseSpnego = True;
Globals.bClientUseSpnego = True;
string_set(&Globals.smb_ports, SMB_PORTS);
}
@ -1677,12 +1686,15 @@ FN_GLOBAL_BOOL(lp_allow_trusted_domains, &Globals.bAllowTrustedDomains)
FN_GLOBAL_INTEGER(lp_restrict_anonymous, &Globals.restrict_anonymous)
FN_GLOBAL_BOOL(lp_lanman_auth, &Globals.bLanmanAuth)
FN_GLOBAL_BOOL(lp_ntlm_auth, &Globals.bNTLMAuth)
FN_GLOBAL_BOOL(lp_client_lanman_auth, &Globals.bClientLanManAuth)
FN_GLOBAL_BOOL(lp_client_ntlmv2_auth, &Globals.bClientNTLMv2Auth)
FN_GLOBAL_BOOL(lp_host_msdfs, &Globals.bHostMSDfs)
FN_GLOBAL_BOOL(lp_kernel_oplocks, &Globals.bKernelOplocks)
FN_GLOBAL_BOOL(lp_enhanced_browsing, &Globals.enhanced_browsing)
FN_GLOBAL_BOOL(lp_use_mmap, &Globals.bUseMmap)
FN_GLOBAL_BOOL(lp_unix_extensions, &Globals.bUnixExtensions)
FN_GLOBAL_BOOL(lp_use_spnego, &Globals.bUseSpnego)
FN_GLOBAL_BOOL(lp_client_use_spnego, &Globals.bClientUseSpnego)
FN_GLOBAL_BOOL(lp_hostname_lookups, &Globals.bHostnameLookups)
FN_GLOBAL_BOOL(lp_kernel_change_notify, &Globals.bKernelChangeNotify)
FN_GLOBAL_INTEGER(lp_os_level, &Globals.os_level)