mirror of
https://github.com/samba-team/samba.git
synced 2024-12-27 03:21:53 +03:00
Finally get NTLMv2 working on the client!
With big thanks to tpot for the ethereal disector, and for the base code behind this, we now fully support NTLMv2 as a client. In particular, we support it with direct domain logons (tested with ntlm_auth --diagnostics), with 'old style' session setups, and with NTLMSSP. In fact, for NTLMSSP we recycle one of the parts of the server's reply directly... (we might need to parse for unicode issues later). In particular, a Win2k domain controller now supplies us with a session key for this password, which means that doman joins, and non-spnego SMB signing are now supported with NTLMv2! Andrew Bartlett
This commit is contained in:
parent
dcc6d9e76c
commit
9f6a26769d
@ -257,14 +257,23 @@ static BOOL cli_session_setup_nt1(struct cli_state *cli, const char *user,
|
||||
if (passlen != 24) {
|
||||
if (lp_client_ntlmv2_auth()) {
|
||||
DATA_BLOB server_chal;
|
||||
|
||||
DATA_BLOB names_blob;
|
||||
server_chal = data_blob(cli->secblob.data, MIN(cli->secblob.length, 8));
|
||||
|
||||
if (!SMBNTLMv2encrypt(user, workgroup, pass, server_chal,
|
||||
&lm_response, &nt_response, NULL, &session_key)) {
|
||||
/* note that the 'workgroup' here is a best guess - we don't know
|
||||
the server's domain at this point. The 'server name' is also
|
||||
dodgy...
|
||||
*/
|
||||
names_blob = NTLMv2_generate_names_blob(cli->called.name, workgroup);
|
||||
|
||||
if (!SMBNTLMv2encrypt(user, workgroup, pass, &server_chal,
|
||||
&names_blob,
|
||||
&lm_response, &nt_response, &session_key)) {
|
||||
data_blob_free(&names_blob);
|
||||
data_blob_free(&server_chal);
|
||||
return False;
|
||||
}
|
||||
data_blob_free(&names_blob);
|
||||
data_blob_free(&server_chal);
|
||||
|
||||
} else {
|
||||
|
@ -487,9 +487,8 @@ static NTSTATUS ntlmssp_client_challenge(struct ntlmssp_client_state *ntlmssp_st
|
||||
}
|
||||
|
||||
SAFE_FREE(server_domain);
|
||||
data_blob_free(&struct_blob);
|
||||
|
||||
if (challenge_blob.length != 8) {
|
||||
data_blob_free(&struct_blob);
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
@ -500,9 +499,11 @@ static NTSTATUS ntlmssp_client_challenge(struct ntlmssp_client_state *ntlmssp_st
|
||||
|
||||
if (!SMBNTLMv2encrypt(ntlmssp_state->user,
|
||||
ntlmssp_state->domain,
|
||||
ntlmssp_state->password, challenge_blob,
|
||||
&lm_response, &nt_response, NULL, &session_key)) {
|
||||
ntlmssp_state->password, &challenge_blob,
|
||||
&struct_blob,
|
||||
&lm_response, &nt_response, &session_key)) {
|
||||
data_blob_free(&challenge_blob);
|
||||
data_blob_free(&struct_blob);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
} else {
|
||||
@ -522,6 +523,7 @@ static NTSTATUS ntlmssp_client_challenge(struct ntlmssp_client_state *ntlmssp_st
|
||||
session_key = data_blob(NULL, 16);
|
||||
SMBsesskeygen_ntv1(nt_hash, NULL, session_key.data);
|
||||
}
|
||||
data_blob_free(&struct_blob);
|
||||
|
||||
/* this generates the actual auth packet */
|
||||
if (!msrpc_gen(next_request, auth_gen_string,
|
||||
|
@ -249,21 +249,21 @@ BOOL make_oem_passwd_hash(char data[516], const char *passwd, uchar old_pw_hash[
|
||||
|
||||
/* Does the md5 encryption from the NT hash for NTLMv2. */
|
||||
void SMBOWFencrypt_ntv2(const uchar kr[16],
|
||||
const DATA_BLOB srv_chal,
|
||||
const DATA_BLOB cli_chal,
|
||||
const DATA_BLOB *srv_chal,
|
||||
const DATA_BLOB *cli_chal,
|
||||
uchar resp_buf[16])
|
||||
{
|
||||
HMACMD5Context ctx;
|
||||
|
||||
hmac_md5_init_limK_to_64(kr, 16, &ctx);
|
||||
hmac_md5_update(srv_chal.data, srv_chal.length, &ctx);
|
||||
hmac_md5_update(cli_chal.data, cli_chal.length, &ctx);
|
||||
hmac_md5_update(srv_chal->data, srv_chal->length, &ctx);
|
||||
hmac_md5_update(cli_chal->data, cli_chal->length, &ctx);
|
||||
hmac_md5_final(resp_buf, &ctx);
|
||||
|
||||
#ifdef DEBUG_PASSWORD
|
||||
DEBUG(100, ("SMBOWFencrypt_ntv2: srv_chal, cli_chal, resp_buf\n"));
|
||||
dump_data(100, srv_chal.data, srv_chal.length);
|
||||
dump_data(100, cli_chal.data, cli_chal.length);
|
||||
dump_data(100, srv_chal->data, srv_chal->length);
|
||||
dump_data(100, cli_chal->data, cli_chal->length);
|
||||
dump_data(100, resp_buf, 16);
|
||||
#endif
|
||||
}
|
||||
@ -294,36 +294,98 @@ void SMBsesskeygen_ntv1(const uchar kr[16],
|
||||
#endif
|
||||
}
|
||||
|
||||
static DATA_BLOB NTLMv2_generate_response(uchar ntlm_v2_hash[16],
|
||||
DATA_BLOB server_chal, size_t client_chal_length)
|
||||
DATA_BLOB NTLMv2_generate_names_blob(const char *hostname,
|
||||
const char *domain)
|
||||
{
|
||||
DATA_BLOB names_blob = data_blob(NULL, 0);
|
||||
|
||||
msrpc_gen(&names_blob, "aaa",
|
||||
True, NTLMSSP_NAME_TYPE_DOMAIN, domain,
|
||||
True, NTLMSSP_NAME_TYPE_SERVER, hostname,
|
||||
True, 0, "");
|
||||
return names_blob;
|
||||
}
|
||||
|
||||
static DATA_BLOB NTLMv2_generate_client_data(const DATA_BLOB *names_blob)
|
||||
{
|
||||
uchar client_chal[8];
|
||||
DATA_BLOB response = data_blob(NULL, 0);
|
||||
char long_date[8];
|
||||
|
||||
generate_random_buffer(client_chal, sizeof(client_chal), False);
|
||||
|
||||
put_long_date(long_date, time(NULL));
|
||||
|
||||
/* See http://www.ubiqx.org/cifs/SMB.html#SMB.8.5 */
|
||||
|
||||
msrpc_gen(&response, "ddbbdb",
|
||||
0x00000101, /* Header */
|
||||
0, /* 'Reserved' */
|
||||
long_date, 8, /* Timestamp */
|
||||
client_chal, 8, /* client challenge */
|
||||
0, /* Unknown */
|
||||
names_blob->data, names_blob->length); /* End of name list */
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
static DATA_BLOB NTLMv2_generate_response(const uchar ntlm_v2_hash[16],
|
||||
const DATA_BLOB *server_chal,
|
||||
const DATA_BLOB *names_blob)
|
||||
{
|
||||
uchar ntlmv2_response[16];
|
||||
DATA_BLOB ntlmv2_client_data;
|
||||
DATA_BLOB final_response;
|
||||
|
||||
/* NTLMv2 */
|
||||
/* generate some data to pass into the response function - including
|
||||
the hostname and domain name of the server */
|
||||
ntlmv2_client_data = NTLMv2_generate_client_data(names_blob);
|
||||
|
||||
/* We also get to specify some random data */
|
||||
ntlmv2_client_data = data_blob(NULL, client_chal_length);
|
||||
generate_random_buffer(ntlmv2_client_data.data, ntlmv2_client_data.length, False);
|
||||
|
||||
/* Given that data, and the challenge from the server, generate a response */
|
||||
SMBOWFencrypt_ntv2(ntlm_v2_hash, server_chal, ntlmv2_client_data, ntlmv2_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 */
|
||||
final_response = data_blob(NULL, ntlmv2_client_data.length + sizeof(ntlmv2_response));
|
||||
final_response = data_blob(NULL, sizeof(ntlmv2_response) + ntlmv2_client_data.length);
|
||||
|
||||
memcpy(final_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(final_response.data + sizeof(ntlmv2_response), ntlmv2_client_data.data, ntlmv2_client_data.length);
|
||||
|
||||
memcpy(final_response.data+sizeof(ntlmv2_response),
|
||||
ntlmv2_client_data.data, ntlmv2_client_data.length);
|
||||
|
||||
data_blob_free(&ntlmv2_client_data);
|
||||
|
||||
return final_response;
|
||||
}
|
||||
|
||||
static DATA_BLOB LMv2_generate_response(const uchar ntlm_v2_hash[16],
|
||||
const DATA_BLOB *server_chal)
|
||||
{
|
||||
uchar lmv2_response[16];
|
||||
DATA_BLOB lmv2_client_data = data_blob(NULL, 8);
|
||||
DATA_BLOB final_response = data_blob(NULL, 24);
|
||||
|
||||
/* LMv2 */
|
||||
/* client-supplied random data */
|
||||
generate_random_buffer(lmv2_client_data.data, lmv2_client_data.length, False);
|
||||
|
||||
/* Given that data, and the challenge from the server, generate a response */
|
||||
SMBOWFencrypt_ntv2(ntlm_v2_hash, server_chal, &lmv2_client_data, lmv2_response);
|
||||
memcpy(final_response.data, lmv2_response, sizeof(lmv2_response));
|
||||
|
||||
/* after the first 16 bytes is the random data we generated above,
|
||||
so the server can verify us with it */
|
||||
memcpy(final_response.data+sizeof(lmv2_response),
|
||||
lmv2_client_data.data, lmv2_client_data.length);
|
||||
|
||||
data_blob_free(&lmv2_client_data);
|
||||
|
||||
return final_response;
|
||||
}
|
||||
|
||||
BOOL SMBNTLMv2encrypt(const char *user, const char *domain, const char *password,
|
||||
const DATA_BLOB server_chal,
|
||||
const DATA_BLOB *server_chal,
|
||||
const DATA_BLOB *names_blob,
|
||||
DATA_BLOB *lm_response, DATA_BLOB *nt_response,
|
||||
DATA_BLOB *lm_session_key,
|
||||
DATA_BLOB *nt_session_key)
|
||||
{
|
||||
uchar nt_hash[16];
|
||||
@ -339,7 +401,8 @@ BOOL SMBNTLMv2encrypt(const char *user, const char *domain, const char *password
|
||||
}
|
||||
|
||||
if (nt_response) {
|
||||
*nt_response = NTLMv2_generate_response(ntlm_v2_hash, server_chal, 64 /* pick a number, > 8 */);
|
||||
*nt_response = NTLMv2_generate_response(ntlm_v2_hash, server_chal,
|
||||
names_blob);
|
||||
if (nt_session_key) {
|
||||
*nt_session_key = data_blob(NULL, 16);
|
||||
|
||||
@ -352,14 +415,7 @@ BOOL SMBNTLMv2encrypt(const char *user, const char *domain, const char *password
|
||||
/* LMv2 */
|
||||
|
||||
if (lm_response) {
|
||||
*lm_response = NTLMv2_generate_response(ntlm_v2_hash, server_chal, 8);
|
||||
if (lm_session_key) {
|
||||
*lm_session_key = data_blob(NULL, 16);
|
||||
|
||||
/* The NTLMv2 calculations also provide a session key, for signing etc later */
|
||||
/* use only the first 16 bytes of nt_response for session key */
|
||||
SMBsesskeygen_ntv2(ntlm_v2_hash, lm_response->data, lm_session_key->data);
|
||||
}
|
||||
*lm_response = LMv2_generate_response(ntlm_v2_hash, server_chal);
|
||||
}
|
||||
|
||||
return True;
|
||||
|
@ -832,26 +832,25 @@ static BOOL test_ntlmv2(void)
|
||||
NTSTATUS nt_status;
|
||||
uint32 flags = 0;
|
||||
DATA_BLOB ntlmv2_response = data_blob(NULL, 0);
|
||||
DATA_BLOB lmv2_response = data_blob(NULL, 0);
|
||||
DATA_BLOB nt_session_key = data_blob(NULL, 0);
|
||||
DATA_BLOB lm_session_key = data_blob(NULL, 0);
|
||||
DATA_BLOB names_blob = NTLMv2_generate_names_blob(get_winbind_netbios_name(), get_winbind_domain());
|
||||
|
||||
uchar lm_key[16];
|
||||
uchar nt_key[16];
|
||||
DATA_BLOB chall = get_challenge();
|
||||
char *error_string;
|
||||
|
||||
ZERO_STRUCT(lm_key);
|
||||
ZERO_STRUCT(nt_key);
|
||||
|
||||
flags |= WINBIND_PAM_LMKEY;
|
||||
flags |= WINBIND_PAM_NTKEY;
|
||||
|
||||
if (!SMBNTLMv2encrypt(opt_username, opt_domain, opt_password, chall,
|
||||
&lmv2_response, &ntlmv2_response,
|
||||
&lm_session_key, &nt_session_key)) {
|
||||
if (!SMBNTLMv2encrypt(opt_username, opt_domain, opt_password, &chall,
|
||||
&names_blob,
|
||||
NULL, &ntlmv2_response,
|
||||
&nt_session_key)) {
|
||||
data_blob_free(&names_blob);
|
||||
return False;
|
||||
}
|
||||
data_blob_free(&names_blob);
|
||||
|
||||
nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
|
||||
opt_workstation,
|
||||
@ -859,11 +858,10 @@ static BOOL test_ntlmv2(void)
|
||||
NULL,
|
||||
&ntlmv2_response,
|
||||
flags,
|
||||
lm_key,
|
||||
NULL,
|
||||
nt_key,
|
||||
&error_string);
|
||||
|
||||
data_blob_free(&lmv2_response);
|
||||
data_blob_free(&ntlmv2_response);
|
||||
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
@ -874,16 +872,6 @@ static BOOL test_ntlmv2(void)
|
||||
return False;
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (memcmp(lm_session_key.data, lm_key,
|
||||
sizeof(lm_key)) != 0) {
|
||||
DEBUG(1, ("LM Session Key does not match expectations!\n"));
|
||||
DEBUG(1, ("lm_key:\n"));
|
||||
dump_data(1, lm_key, 16);
|
||||
DEBUG(1, ("expected:\n"));
|
||||
dump_data(1, lm_session_key.data, lm_session_key.length);
|
||||
pass = False;
|
||||
}
|
||||
if (memcmp(nt_session_key.data, nt_key,
|
||||
sizeof(nt_key)) != 0) {
|
||||
DEBUG(1, ("NT Session Key does not match expectations!\n"));
|
||||
@ -893,7 +881,6 @@ static BOOL test_ntlmv2(void)
|
||||
dump_data(1, nt_session_key.data, nt_session_key.length);
|
||||
pass = False;
|
||||
}
|
||||
#endif
|
||||
return pass;
|
||||
}
|
||||
|
||||
@ -909,24 +896,24 @@ static BOOL test_lmv2_ntlmv2(void)
|
||||
DATA_BLOB ntlmv2_response = data_blob(NULL, 0);
|
||||
DATA_BLOB lmv2_response = data_blob(NULL, 0);
|
||||
DATA_BLOB nt_session_key = data_blob(NULL, 0);
|
||||
DATA_BLOB lm_session_key = data_blob(NULL, 0);
|
||||
DATA_BLOB names_blob = NTLMv2_generate_names_blob(get_winbind_netbios_name(), get_winbind_domain());
|
||||
|
||||
uchar lm_key[16];
|
||||
uchar nt_key[16];
|
||||
DATA_BLOB chall = get_challenge();
|
||||
char *error_string;
|
||||
|
||||
ZERO_STRUCT(nt_key);
|
||||
ZERO_STRUCT(lm_key);
|
||||
|
||||
flags |= WINBIND_PAM_LMKEY;
|
||||
flags |= WINBIND_PAM_NTKEY;
|
||||
|
||||
if (!SMBNTLMv2encrypt(opt_username, opt_domain, opt_password, chall,
|
||||
if (!SMBNTLMv2encrypt(opt_username, opt_domain, opt_password, &chall,
|
||||
&names_blob,
|
||||
&lmv2_response, &ntlmv2_response,
|
||||
&lm_session_key, &nt_session_key)) {
|
||||
&nt_session_key)) {
|
||||
data_blob_free(&names_blob);
|
||||
return False;
|
||||
}
|
||||
data_blob_free(&names_blob);
|
||||
|
||||
nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
|
||||
opt_workstation,
|
||||
@ -934,7 +921,7 @@ static BOOL test_lmv2_ntlmv2(void)
|
||||
&lmv2_response,
|
||||
&ntlmv2_response,
|
||||
flags,
|
||||
lm_key,
|
||||
NULL,
|
||||
nt_key,
|
||||
&error_string);
|
||||
|
||||
@ -949,16 +936,6 @@ static BOOL test_lmv2_ntlmv2(void)
|
||||
return False;
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (memcmp(lm_session_key.data, lm_key,
|
||||
sizeof(lm_key)) != 0) {
|
||||
DEBUG(1, ("LM Session Key does not match expectations!\n"));
|
||||
DEBUG(1, ("lm_key:\n"));
|
||||
dump_data(1, lm_key, 16);
|
||||
DEBUG(1, ("expected:\n"));
|
||||
dump_data(1, lm_session_key.data, lm_session_key.length);
|
||||
pass = False;
|
||||
}
|
||||
if (memcmp(nt_session_key.data, nt_key,
|
||||
sizeof(nt_key)) != 0) {
|
||||
DEBUG(1, ("NT Session Key does not match expectations!\n"));
|
||||
@ -968,7 +945,6 @@ static BOOL test_lmv2_ntlmv2(void)
|
||||
dump_data(1, nt_session_key.data, nt_session_key.length);
|
||||
pass = False;
|
||||
}
|
||||
#endif
|
||||
return pass;
|
||||
}
|
||||
|
||||
@ -981,25 +957,15 @@ static BOOL test_lmv2(void)
|
||||
BOOL pass = True;
|
||||
NTSTATUS nt_status;
|
||||
uint32 flags = 0;
|
||||
DATA_BLOB ntlmv2_response = data_blob(NULL, 0);
|
||||
DATA_BLOB lmv2_response = data_blob(NULL, 0);
|
||||
DATA_BLOB nt_session_key = data_blob(NULL, 0);
|
||||
DATA_BLOB lm_session_key = data_blob(NULL, 0);
|
||||
|
||||
uchar lm_key[16];
|
||||
uchar nt_key[16];
|
||||
DATA_BLOB chall = get_challenge();
|
||||
char *error_string;
|
||||
|
||||
ZERO_STRUCT(nt_key);
|
||||
ZERO_STRUCT(lm_key);
|
||||
|
||||
flags |= WINBIND_PAM_LMKEY;
|
||||
flags |= WINBIND_PAM_NTKEY;
|
||||
|
||||
if (!SMBNTLMv2encrypt(opt_username, opt_domain, opt_password, chall,
|
||||
&lmv2_response, &ntlmv2_response,
|
||||
&lm_session_key, &nt_session_key)) {
|
||||
if (!SMBNTLMv2encrypt(opt_username, opt_domain, opt_password, &chall,
|
||||
NULL,
|
||||
&lmv2_response, NULL,
|
||||
NULL)) {
|
||||
return False;
|
||||
}
|
||||
|
||||
@ -1009,12 +975,11 @@ static BOOL test_lmv2(void)
|
||||
&lmv2_response,
|
||||
NULL,
|
||||
flags,
|
||||
lm_key,
|
||||
nt_key,
|
||||
NULL,
|
||||
NULL,
|
||||
&error_string);
|
||||
|
||||
data_blob_free(&lmv2_response);
|
||||
data_blob_free(&ntlmv2_response);
|
||||
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
d_printf("%s (0x%x)\n",
|
||||
@ -1024,26 +989,6 @@ static BOOL test_lmv2(void)
|
||||
return False;
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (memcmp(lm_session_key.data, lm_key,
|
||||
sizeof(lm_key)) != 0) {
|
||||
DEBUG(1, ("LM Session Key does not match expectations!\n"));
|
||||
DEBUG(1, ("lm_key:\n"));
|
||||
dump_data(1, lm_key, 16);
|
||||
DEBUG(1, ("expected:\n"));
|
||||
dump_data(1, lm_session_key.data, lm_session_key.length);
|
||||
pass = False;
|
||||
}
|
||||
if (memcmp(nt_session_key.data, nt_key,
|
||||
sizeof(nt_key)) != 0) {
|
||||
DEBUG(1, ("NT Session Key does not match expectations!\n"));
|
||||
DEBUG(1, ("nt_key:\n"));
|
||||
dump_data(1, nt_key, 16);
|
||||
DEBUG(1, ("expected:\n"));
|
||||
dump_data(1, nt_session_key.data, nt_session_key.length);
|
||||
pass = False;
|
||||
}
|
||||
#endif
|
||||
return pass;
|
||||
}
|
||||
|
||||
@ -1145,24 +1090,24 @@ static BOOL test_ntlmv2_broken(BOOL break_lmv2)
|
||||
DATA_BLOB ntlmv2_response = data_blob(NULL, 0);
|
||||
DATA_BLOB lmv2_response = data_blob(NULL, 0);
|
||||
DATA_BLOB nt_session_key = data_blob(NULL, 0);
|
||||
DATA_BLOB lm_session_key = data_blob(NULL, 0);
|
||||
DATA_BLOB names_blob = NTLMv2_generate_names_blob(get_winbind_netbios_name(), get_winbind_domain());
|
||||
|
||||
uchar lm_key[16];
|
||||
uchar nt_key[16];
|
||||
DATA_BLOB chall = get_challenge();
|
||||
char *error_string;
|
||||
|
||||
ZERO_STRUCT(nt_key);
|
||||
ZERO_STRUCT(lm_key);
|
||||
|
||||
flags |= WINBIND_PAM_LMKEY;
|
||||
flags |= WINBIND_PAM_NTKEY;
|
||||
|
||||
if (!SMBNTLMv2encrypt(opt_username, opt_domain, opt_password, chall,
|
||||
|
||||
if (!SMBNTLMv2encrypt(opt_username, opt_domain, opt_password, &chall,
|
||||
&names_blob,
|
||||
&lmv2_response, &ntlmv2_response,
|
||||
&lm_session_key, &nt_session_key)) {
|
||||
&nt_session_key)) {
|
||||
data_blob_free(&names_blob);
|
||||
return False;
|
||||
}
|
||||
data_blob_free(&names_blob);
|
||||
|
||||
/* Heh - this should break the appropriate password hash nicely! */
|
||||
|
||||
@ -1177,7 +1122,7 @@ static BOOL test_ntlmv2_broken(BOOL break_lmv2)
|
||||
&lmv2_response,
|
||||
&ntlmv2_response,
|
||||
flags,
|
||||
lm_key,
|
||||
NULL,
|
||||
nt_key,
|
||||
&error_string);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user