1
0
mirror of https://github.com/samba-team/samba.git synced 2025-01-11 05:18:09 +03:00

Jeremy requested that I get my NTLMSSP patch into CVS. He didn't request

the schannel code, but I've included that anyway. :-)

This patch revives the client-side NTLMSSP support for RPC named pipes
in Samba, and cleans up the client and server schannel code.  The use of the
new code is enabled by the 'sign', 'seal' and 'schannel' commands in
rpcclient.

The aim was to prove that our separate NTLMSSP client library actually
implements NTLMSSP signing and sealing as per Microsoft's NTLMv1 implementation,
in the hope that knowing this will assist us in correctly implementing
NTLMSSP signing for SMB packets.  (Still not yet functional)

This patch replaces the NTLMSSP implementation in rpc_client/cli_pipe.c with
calls to libsmb/ntlmssp.c.  In the process, we have gained the ability to
use the more secure NT password, and the ability to sign-only, instead of
having to seal the pipe connection.  (Previously we were limited to sealing,
and could only use the LM-password derived key).

Our new client-side NTLMSSP code also needed alteration to cope with our
comparatively simple server-side implementation.  A future step is to replace
it with calls to the same NTLMSSP library.

Also included in this patch is the schannel 'sign only' patch I submitted to
the team earlier.  While not enabled (and not functional, at this stage) the
work in this patch makes the code paths *much* easier to follow.  I have also
included similar hooks in rpccleint to allow the use of schannel on *any* pipe.

rpcclient now defaults to not using schannel (or any other extra per-pipe
authenticiation) for any connection.  The 'schannel' command enables schannel
for all pipes until disabled.

This code is also much more secure than the previous code, as changes to our
cli_pipe routines ensure that the authentication footer cannot be removed
by an attacker, and more error states are correctly handled.

(The same needs to be done to our server)

Andrew Bartlett
(This used to be commit 5472ddc9ea)
This commit is contained in:
Andrew Bartlett 2003-07-14 08:46:32 +00:00
parent 379367dd76
commit 456f51bcbe
16 changed files with 1290 additions and 830 deletions

View File

@ -131,25 +131,30 @@ struct cli_state {
* Only used in NT domain calls.
*/
int pipe_idx; /* Index (into list of known pipes)
of the pipe we're talking to,
if any */
uint16 nt_pipe_fnum; /* Pipe handle. */
/* Secure pipe parameters */
int pipe_auth_flags;
uint16 saved_netlogon_pipe_fnum; /* The "first" pipe to get
the session key for the
schannel. */
struct netsec_auth_struct auth_info;
NTLMSSP_CLIENT_STATE *ntlmssp_pipe_state;
unsigned char sess_key[16]; /* Current session key. */
unsigned char ntlmssp_hash[258]; /* ntlmssp data. */
uint32 ntlmssp_cli_flgs; /* ntlmssp client flags */
uint32 ntlmssp_srv_flgs; /* ntlmssp server flags */
uint32 ntlmssp_seq_num; /* ntlmssp sequence number */
DOM_CRED clnt_cred; /* Client credential. */
fstring mach_acct; /* MYNAME$. */
fstring srv_name_slash; /* \\remote server. */
fstring clnt_name_slash; /* \\local client. */
uint16 max_xmit_frag;
uint16 max_recv_frag;
uint32 ntlmssp_flags;
BOOL use_kerberos;
BOOL use_spnego;

View File

@ -129,5 +129,10 @@ typedef struct ntlmssp_client_state
/* ntlmv1 */
unsigned char ntlmssp_hash[258];
/* it turns out that we don't always get the
response in at the time we want to process it.
Store it here, until we need it */
DATA_BLOB stored_response;
} NTLMSSP_CLIENT_STATE;

View File

@ -51,17 +51,32 @@ enum RPC_PKT_TYPE
/* NTLMSSP signature version */
#define NTLMSSP_SIGN_VERSION 0x01
/* NTLMSSP auth type and level. */
/* NTLMSSP auth type */
#define NTLMSSP_AUTH_TYPE 0xa
#define NTLMSSP_AUTH_LEVEL 0x6
/* DCE-RPC standard identifiers to indicate
signing or sealing of an RPC pipe */
#define RPC_PIPE_AUTH_SIGN_LEVEL 0x5
#define RPC_PIPE_AUTH_SEAL_LEVEL 0x6
/* Netlogon schannel auth type and level */
#define NETSEC_AUTH_TYPE 0x44
#define NETSEC_AUTH_LEVEL 0x6
#define NETSEC_SIGNATURE { 0x77, 0x00, 0x7a, 0x00, 0xff, 0xff, 0x00, 0x00 }
#define RPC_AUTH_NETSEC_CHK_LEN 0x20
#define NETLOGON_NEG_SCHANNEL 0x40000000
enum netsec_direction
{
SENDER_IS_INITIATOR,
SENDER_IS_ACCEPTOR
};
/* Internal Flags to indicate what type of authentication on the pipe */
#define AUTH_PIPE_SIGN 0x0001
#define AUTH_PIPE_SEAL 0x0002
#define AUTH_PIPE_NTLMSSP 0x0004
#define AUTH_PIPE_NETSEC 0x0008
/* Maximum PDU fragment size. */
#define MAX_PDU_FRAG_LEN 0x1630
/* #define MAX_PDU_FRAG_LEN 0x10b8 this is what w2k sets */
@ -222,8 +237,8 @@ typedef struct rpc_auth_netsec_neg_info
typedef struct rpc_auth_netsec_chk_info
{
uint8 sig [8]; /* 77 00 7a 00 ff ff 00 00 */
uint8 data1[8];
uint8 data3[8]; /* verifier, seq num */
uint8 packet_digest[8]; /* checksum over the packet, MD5'ed with session key */
uint8 seq_num[8]; /* verifier, seq num */
uint8 data8[8]; /* random 8-byte nonce */
} RPC_AUTH_NETSEC_CHK;
@ -350,5 +365,4 @@ typedef struct rpc_auth_ntlmssp_chk_info
#define RPC_AUTH_NTLMSSP_CHK_LEN 16
#endif /* _DCE_RPC_H */

View File

@ -1911,6 +1911,17 @@ void dump_data(int level, const char *buf1,int len)
}
}
void dump_data_pw(const char *msg, const uchar * data, size_t len)
{
#ifdef DEBUG_PASSWORD
DEBUG(11, ("%s", msg));
if (data != NULL && len > 0)
{
dump_data(11, data, len);
}
#endif
}
char *tab_depth(int depth)
{
static pstring spaces;

View File

@ -540,6 +540,12 @@ static BOOL cli_session_setup_ntlmssp(struct cli_state *cli, const char *user,
ntlmssp_state->use_ntlmv2 = lp_client_ntlmv2_auth();
if (cli->sign_info.negotiated_smb_signing
|| cli->sign_info.mandetory_signing) {
ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SIGN;
ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN;
}
do {
nt_status = ntlmssp_client_update(ntlmssp_state,
blob_in, &blob_out);

View File

@ -203,12 +203,9 @@ void cli_init_creds(struct cli_state *cli, const struct ntuser_creds *usr)
fstrcpy(cli->domain , usr->domain);
fstrcpy(cli->user_name, usr->user_name);
memcpy(&cli->pwd, &usr->pwd, sizeof(usr->pwd));
cli->ntlmssp_flags = usr->ntlmssp_flags;
cli->ntlmssp_cli_flgs = usr != NULL ? usr->ntlmssp_flags : 0;
DEBUG(10,("cli_init_creds: user %s domain %s flgs: %x\nntlmssp_cli_flgs:%x\n",
cli->user_name, cli->domain,
cli->ntlmssp_flags,cli->ntlmssp_cli_flgs));
DEBUG(10,("cli_init_creds: user %s domain %s\n",
cli->user_name, cli->domain));
}
/****************************************************************************
@ -287,6 +284,8 @@ struct cli_state *cli_initialise(struct cli_state *cli)
cli->initialised = 1;
cli->allocated = alloced_cli;
cli->pipe_idx = -1;
return cli;
/* Clean up after malloc() error */
@ -302,18 +301,51 @@ struct cli_state *cli_initialise(struct cli_state *cli)
return NULL;
}
/****************************************************************************
close the session
****************************************************************************/
void cli_nt_session_close(struct cli_state *cli)
{
if (cli->ntlmssp_pipe_state) {
ntlmssp_client_end(&cli->ntlmssp_pipe_state);
}
cli_close(cli, cli->nt_pipe_fnum);
cli->nt_pipe_fnum = 0;
cli->pipe_idx = -1;
}
/****************************************************************************
close the NETLOGON session holding the session key for NETSEC
****************************************************************************/
void cli_nt_netlogon_netsec_session_close(struct cli_state *cli)
{
if (cli->saved_netlogon_pipe_fnum != 0) {
cli_close(cli, cli->saved_netlogon_pipe_fnum);
cli->saved_netlogon_pipe_fnum = 0;
}
}
/****************************************************************************
Close a client connection and free the memory without destroying cli itself.
****************************************************************************/
void cli_close_connection(struct cli_state *cli)
{
cli_nt_session_close(cli);
cli_nt_netlogon_netsec_session_close(cli);
SAFE_FREE(cli->outbuf);
SAFE_FREE(cli->inbuf);
cli_free_signing_context(cli);
data_blob_free(&cli->secblob);
if (cli->ntlmssp_pipe_state)
ntlmssp_client_end(&cli->ntlmssp_pipe_state);
if (cli->mem_ctx) {
talloc_destroy(cli->mem_ctx);
cli->mem_ctx = NULL;

View File

@ -409,6 +409,10 @@ static NTSTATUS ntlmssp_client_initial(struct ntlmssp_client_state *ntlmssp_stat
ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_UNICODE;
}
if (ntlmssp_state->use_ntlmv2) {
ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_NTLM2;
}
/* generate the ntlmssp negotiate packet */
msrpc_gen(next_request, "CddAA",
"NTLMSSP",
@ -435,7 +439,7 @@ static NTSTATUS ntlmssp_client_challenge(struct ntlmssp_client_state *ntlmssp_st
uint32 chal_flags, ntlmssp_command, unkn1, unkn2;
DATA_BLOB server_domain_blob;
DATA_BLOB challenge_blob;
DATA_BLOB struct_blob;
DATA_BLOB struct_blob = data_blob(NULL, 0);
char *server_domain;
const char *chal_parse_string;
const char *auth_gen_string;
@ -443,28 +447,48 @@ static NTSTATUS ntlmssp_client_challenge(struct ntlmssp_client_state *ntlmssp_st
DATA_BLOB nt_response = data_blob(NULL, 0);
DATA_BLOB session_key = data_blob(NULL, 0);
uint8 datagram_sess_key[16];
size_t datagram_sess_key_len;
#if 0 /* until we know what flag to tigger it on */
generate_random_buffer(datagram_sess_key, sizeof(datagram_sess_key), False);
datagram_sess_key_len = sizeof(datagram_sess_key);
#else
ZERO_STRUCT(datagram_sess_key);
datagram_sess_key_len = 0;
#endif
if (!msrpc_parse(&reply, "CdBd",
"NTLMSSP",
&ntlmssp_command,
&server_domain_blob,
&chal_flags)) {
DEBUG(0, ("Failed to parse the NTLMSSP Challenge\n"));
DEBUG(1, ("Failed to parse the NTLMSSP Challenge: (#1)\n"));
dump_data(2, reply.data, reply.length);
return NT_STATUS_INVALID_PARAMETER;
}
data_blob_free(&server_domain_blob);
DEBUG(3, ("Got challenge flags:\n"));
debug_ntlmssp_flags(chal_flags);
if (chal_flags & NTLMSSP_NEGOTIATE_UNICODE) {
chal_parse_string = "CdUdbddB";
if (chal_flags & NTLMSSP_CHAL_TARGET_INFO) {
chal_parse_string = "CdUdbddB";
} else {
chal_parse_string = "CdUdbdd";
}
auth_gen_string = "CdBBUUUBd";
ntlmssp_state->unicode = True;
ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_UNICODE;
ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_OEM;
} else if (chal_flags & NTLMSSP_NEGOTIATE_OEM) {
chal_parse_string = "CdAdbddB";
if (chal_flags & NTLMSSP_CHAL_TARGET_INFO) {
chal_parse_string = "CdAdbddB";
} else {
chal_parse_string = "CdAdbdd";
}
auth_gen_string = "CdBBAAABd";
ntlmssp_state->unicode = False;
ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_UNICODE;
@ -473,6 +497,25 @@ static NTSTATUS ntlmssp_client_challenge(struct ntlmssp_client_state *ntlmssp_st
return NT_STATUS_INVALID_PARAMETER;
}
if (chal_flags & NTLMSSP_NEGOTIATE_LM_KEY && lp_client_lanman_auth()) {
/* server forcing us to use LM */
ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_LM_KEY;
ntlmssp_state->use_ntlmv2 = False;
} else {
ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY;
}
if (!(chal_flags & NTLMSSP_NEGOTIATE_NTLM2)) {
ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2;
}
if (!(chal_flags & NTLMSSP_NEGOTIATE_128)) {
ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_128;
}
DEBUG(3, ("NTLMSSP: Set final flags:\n"));
debug_ntlmssp_flags(ntlmssp_state->neg_flags);
if (!msrpc_parse(&reply, chal_parse_string,
"NTLMSSP",
&ntlmssp_command,
@ -481,7 +524,8 @@ static NTSTATUS ntlmssp_client_challenge(struct ntlmssp_client_state *ntlmssp_st
&challenge_blob, 8,
&unkn1, &unkn2,
&struct_blob)) {
DEBUG(0, ("Failed to parse the NTLMSSP Challenge\n"));
DEBUG(1, ("Failed to parse the NTLMSSP Challenge: (#2)\n"));
dump_data(2, reply.data, reply.length);
return NT_STATUS_INVALID_PARAMETER;
}
@ -493,6 +537,11 @@ static NTSTATUS ntlmssp_client_challenge(struct ntlmssp_client_state *ntlmssp_st
if (ntlmssp_state->use_ntlmv2) {
if (!struct_blob.length) {
/* be lazy, match win2k - we can't do NTLMv2 without it */
return NT_STATUS_INVALID_PARAMETER;
}
/* TODO: if the remote server is standalone, then we should replace 'domain'
with the server name as supplied above */
@ -506,10 +555,12 @@ static NTSTATUS ntlmssp_client_challenge(struct ntlmssp_client_state *ntlmssp_st
return NT_STATUS_NO_MEMORY;
}
} else {
uchar lm_hash[16];
uchar nt_hash[16];
E_deshash(ntlmssp_state->password, lm_hash);
E_md4hash(ntlmssp_state->password, nt_hash);
/* non encrypted password supplied. Ignore ntpass. */
/* lanman auth is insecure, it may be disabled */
if (lp_client_lanman_auth()) {
lm_response = data_blob(NULL, 24);
SMBencrypt(ntlmssp_state->password,challenge_blob.data,
@ -519,8 +570,15 @@ static NTSTATUS ntlmssp_client_challenge(struct ntlmssp_client_state *ntlmssp_st
nt_response = data_blob(NULL, 24);
SMBNTencrypt(ntlmssp_state->password,challenge_blob.data,
nt_response.data);
session_key = data_blob(NULL, 16);
SMBsesskeygen_ntv1(nt_hash, NULL, session_key.data);
if ((ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_LM_KEY)
&& lp_client_lanman_auth()) {
SMBsesskeygen_lmv1(lm_hash, lm_response.data,
session_key.data);
} else {
SMBsesskeygen_ntv1(nt_hash, NULL, session_key.data);
}
}
data_blob_free(&struct_blob);
@ -533,7 +591,7 @@ static NTSTATUS ntlmssp_client_challenge(struct ntlmssp_client_state *ntlmssp_st
ntlmssp_state->domain,
ntlmssp_state->user,
ntlmssp_state->get_global_myname(),
datagram_sess_key, 16,
datagram_sess_key, datagram_sess_key_len,
ntlmssp_state->neg_flags)) {
data_blob_free(&lm_response);
@ -575,6 +633,8 @@ NTSTATUS ntlmssp_client_start(NTLMSSP_CLIENT_STATE **ntlmssp_state)
(*ntlmssp_state)->unicode = True;
(*ntlmssp_state)->use_ntlmv2 = lp_client_ntlmv2_auth();
(*ntlmssp_state)->neg_flags =
NTLMSSP_NEGOTIATE_128 |
NTLMSSP_NEGOTIATE_NTLM |
@ -596,6 +656,7 @@ NTSTATUS ntlmssp_client_end(NTLMSSP_CLIENT_STATE **ntlmssp_state)
data_blob_free(&(*ntlmssp_state)->lm_resp);
data_blob_free(&(*ntlmssp_state)->nt_resp);
data_blob_free(&(*ntlmssp_state)->session_key);
data_blob_free(&(*ntlmssp_state)->stored_response);
talloc_destroy(mem_ctx);
}
@ -606,12 +667,18 @@ NTSTATUS ntlmssp_client_end(NTLMSSP_CLIENT_STATE **ntlmssp_state)
NTSTATUS ntlmssp_client_update(NTLMSSP_CLIENT_STATE *ntlmssp_state,
DATA_BLOB reply, DATA_BLOB *next_request)
{
NTSTATUS nt_status = NT_STATUS_INVALID_PARAMETER;
uint32 ntlmssp_command;
*next_request = data_blob(NULL, 0);
if (!reply.length) {
return ntlmssp_client_initial(ntlmssp_state, reply, next_request);
}
/* If there is a cached reply, use it - otherwise this is the first packet */
if (!ntlmssp_state->stored_response.length) {
return ntlmssp_client_initial(ntlmssp_state, reply, next_request);
}
reply = ntlmssp_state->stored_response;
}
if (!msrpc_parse(&reply, "Cd",
"NTLMSSP",
@ -620,9 +687,12 @@ NTSTATUS ntlmssp_client_update(NTLMSSP_CLIENT_STATE *ntlmssp_state,
}
if (ntlmssp_command == NTLMSSP_CHALLENGE) {
return ntlmssp_client_challenge(ntlmssp_state, reply, next_request);
nt_status = ntlmssp_client_challenge(ntlmssp_state, reply, next_request);
}
return NT_STATUS_INVALID_PARAMETER;
if (ntlmssp_state->stored_response.length) {
data_blob_free(&ntlmssp_state->stored_response);
}
return nt_status;
}
NTSTATUS ntlmssp_set_username(NTLMSSP_CLIENT_STATE *ntlmssp_state, const char *user)
@ -651,3 +721,16 @@ NTSTATUS ntlmssp_set_domain(NTLMSSP_CLIENT_STATE *ntlmssp_state, const char *dom
}
return NT_STATUS_OK;
}
/**
* Store a DATA_BLOB containing an NTLMSSP response, for use later.
* This 'keeps' the data blob - the caller must *not* free it.
*/
NTSTATUS ntlmssp_client_store_response(NTLMSSP_CLIENT_STATE *ntlmssp_state,
DATA_BLOB response)
{
data_blob_free(&ntlmssp_state->stored_response);
ntlmssp_state->stored_response = response;
return NT_STATUS_OK;
}

View File

@ -220,23 +220,27 @@ BOOL msrpc_parse(const DATA_BLOB *blob,
len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
ptr = IVAL(blob->data, head_ofs); head_ofs += 4;
/* make sure its in the right format - be strict */
if (len1 != len2 || ptr + len1 > blob->length) {
return False;
}
if (len1 & 1) {
/* if odd length and unicode */
return False;
}
ps = va_arg(ap, char **);
if (0 < len1) {
pull_string(NULL, p, blob->data + ptr, sizeof(p),
len1,
STR_UNICODE|STR_NOALIGN);
(*ps) = smb_xstrdup(p);
if (len1 == 0 && len2 == 0) {
*ps = smb_xstrdup("");
} else {
(*ps) = smb_xstrdup("");
/* make sure its in the right format - be strict */
if (len1 != len2 || ptr + len1 > blob->length) {
return False;
}
if (len1 & 1) {
/* if odd length and unicode */
return False;
}
if (0 < len1) {
pull_string(NULL, p, blob->data + ptr, sizeof(p),
len1,
STR_UNICODE|STR_NOALIGN);
(*ps) = smb_xstrdup(p);
} else {
(*ps) = smb_xstrdup("");
}
}
break;
case 'A':
@ -245,19 +249,23 @@ BOOL msrpc_parse(const DATA_BLOB *blob,
len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
ptr = IVAL(blob->data, head_ofs); head_ofs += 4;
/* make sure its in the right format - be strict */
if (len1 != len2 || ptr + len1 > blob->length) {
return False;
}
ps = va_arg(ap, char **);
if (0 < len1) {
pull_string(NULL, p, blob->data + ptr, sizeof(p),
len1,
STR_ASCII|STR_NOALIGN);
(*ps) = smb_xstrdup(p);
/* make sure its in the right format - be strict */
if (len1 == 0 && len2 == 0) {
*ps = smb_xstrdup("");
} else {
(*ps) = smb_xstrdup("");
if (len1 != len2 || ptr + len1 > blob->length) {
return False;
}
if (0 < len1) {
pull_string(NULL, p, blob->data + ptr, sizeof(p),
len1,
STR_ASCII|STR_NOALIGN);
(*ps) = smb_xstrdup(p);
} else {
(*ps) = smb_xstrdup("");
}
}
break;
case 'B':
@ -265,12 +273,17 @@ BOOL msrpc_parse(const DATA_BLOB *blob,
len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
ptr = IVAL(blob->data, head_ofs); head_ofs += 4;
/* make sure its in the right format - be strict */
if (len1 != len2 || ptr + len1 > blob->length) {
return False;
}
b = (DATA_BLOB *)va_arg(ap, void *);
*b = data_blob(blob->data + ptr, len1);
if (len1 == 0 && len2 == 0) {
*b = data_blob(NULL, 0);
} else {
/* make sure its in the right format - be strict */
if (len1 != len2 || ptr + len1 > blob->length) {
return False;
}
*b = data_blob(blob->data + ptr, len1);
}
break;
case 'b':
b = (DATA_BLOB *)va_arg(ap, void *);

View File

@ -79,13 +79,18 @@ static void calc_hash(unsigned char *hash, const char *k2, int k2l)
}
static void calc_ntlmv2_hash(unsigned char hash[16], char digest[16],
const char encrypted_response[16],
DATA_BLOB session_key,
const char *constant)
{
struct MD5Context ctx3;
/* NOTE: This code is currently complate fantasy - it's
got more in common with reality than the previous code
(the LM session key is not the right thing to use) but
it still needs work */
MD5Init(&ctx3);
MD5Update(&ctx3, encrypted_response, 5);
MD5Update(&ctx3, session_key.data, session_key.length);
MD5Update(&ctx3, constant, strlen(constant));
MD5Final(digest, &ctx3);
@ -113,25 +118,28 @@ static NTSTATUS ntlmssp_make_packet_signiture(NTLMSSP_CLIENT_STATE *ntlmssp_stat
hmac_md5_update(data, length, &ctx);
hmac_md5_final(digest, &ctx);
if (!msrpc_gen(sig, "Bd", digest, sizeof(digest), ntlmssp_state->ntlmssp_seq_num)) {
if (!msrpc_gen(sig, "dBd", NTLMSSP_SIGN_VERSION, digest, 8 /* only copy first 8 bytes */
, ntlmssp_state->ntlmssp_seq_num)) {
return NT_STATUS_NO_MEMORY;
}
switch (direction) {
case NTLMSSP_SEND:
NTLMSSPcalc_ap(ntlmssp_state->cli_sign_hash, sig->data, sig->length);
NTLMSSPcalc_ap(ntlmssp_state->cli_sign_hash, sig->data+4, sig->length-4);
break;
case NTLMSSP_RECEIVE:
NTLMSSPcalc_ap(ntlmssp_state->srv_sign_hash, sig->data, sig->length);
NTLMSSPcalc_ap(ntlmssp_state->srv_sign_hash, sig->data+4, sig->length-4);
break;
}
} else {
uint32 crc;
crc = crc32_calc_buffer(data, length);
if (!msrpc_gen(sig, "ddd", 0, crc, ntlmssp_state->ntlmssp_seq_num)) {
if (!msrpc_gen(sig, "dddd", NTLMSSP_SIGN_VERSION, 0, crc, ntlmssp_state->ntlmssp_seq_num)) {
return NT_STATUS_NO_MEMORY;
}
NTLMSSPcalc_ap(ntlmssp_state->ntlmssp_hash, sig->data, sig->length);
dump_data_pw("ntlmssp hash:\n", ntlmssp_state->ntlmssp_hash,
sizeof(ntlmssp_state->ntlmssp_hash));
NTLMSSPcalc_ap(ntlmssp_state->ntlmssp_hash, sig->data+4, sig->length-4);
}
return NT_STATUS_OK;
}
@ -140,8 +148,11 @@ NTSTATUS ntlmssp_client_sign_packet(NTLMSSP_CLIENT_STATE *ntlmssp_state,
const uchar *data, size_t length,
DATA_BLOB *sig)
{
NTSTATUS nt_status = ntlmssp_make_packet_signiture(ntlmssp_state, data, length, NTLMSSP_SEND, sig);
/* increment counter on send */
ntlmssp_state->ntlmssp_seq_num++;
return ntlmssp_make_packet_signiture(ntlmssp_state, data, length, NTLMSSP_SEND, sig);
return nt_status;
}
/**
@ -151,8 +162,8 @@ NTSTATUS ntlmssp_client_sign_packet(NTLMSSP_CLIENT_STATE *ntlmssp_state,
*/
NTSTATUS ntlmssp_client_check_packet(NTLMSSP_CLIENT_STATE *ntlmssp_state,
const uchar *data, size_t length,
const DATA_BLOB *sig)
const uchar *data, size_t length,
const DATA_BLOB *sig)
{
DATA_BLOB local_sig;
NTSTATUS nt_status;
@ -170,9 +181,7 @@ NTSTATUS ntlmssp_client_check_packet(NTLMSSP_CLIENT_STATE *ntlmssp_state,
return nt_status;
}
if (memcmp(sig->data, local_sig.data, MIN(sig->length, local_sig.length)) == 0) {
return NT_STATUS_OK;
} else {
if (memcmp(sig->data+sig->length - 8, local_sig.data+local_sig.length - 8, 8) != 0) {
DEBUG(5, ("BAD SIG: wanted signature of\n"));
dump_data(5, local_sig.data, local_sig.length);
@ -182,6 +191,97 @@ NTSTATUS ntlmssp_client_check_packet(NTLMSSP_CLIENT_STATE *ntlmssp_state,
DEBUG(0, ("NTLMSSP packet check failed due to invalid signiture!\n"));
return NT_STATUS_ACCESS_DENIED;
}
/* increment counter on recieive */
ntlmssp_state->ntlmssp_seq_num++;
return NT_STATUS_OK;
}
/**
* Seal data with the NTLMSSP algorithm
*
*/
NTSTATUS ntlmssp_client_seal_packet(NTLMSSP_CLIENT_STATE *ntlmssp_state,
uchar *data, size_t length,
DATA_BLOB *sig)
{
DEBUG(10,("ntlmssp_client_seal_data: seal\n"));
dump_data_pw("ntlmssp clear data\n", data, length);
if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
HMACMD5Context ctx;
char seq_num[4];
uchar digest[16];
SIVAL(seq_num, 0, ntlmssp_state->ntlmssp_seq_num);
hmac_md5_init_limK_to_64(ntlmssp_state->cli_sign_const, 16, &ctx);
hmac_md5_update(seq_num, 4, &ctx);
hmac_md5_update(data, length, &ctx);
hmac_md5_final(digest, &ctx);
if (!msrpc_gen(sig, "dBd", NTLMSSP_SIGN_VERSION, digest, 8 /* only copy first 8 bytes */
, ntlmssp_state->ntlmssp_seq_num)) {
return NT_STATUS_NO_MEMORY;
}
dump_data_pw("ntlmssp client sealing hash:\n",
ntlmssp_state->cli_seal_hash,
sizeof(ntlmssp_state->cli_seal_hash));
NTLMSSPcalc_ap(ntlmssp_state->cli_seal_hash, data, length);
dump_data_pw("ntlmssp client signing hash:\n",
ntlmssp_state->cli_sign_hash,
sizeof(ntlmssp_state->cli_sign_hash));
NTLMSSPcalc_ap(ntlmssp_state->cli_sign_hash, sig->data+4, sig->length-4);
} else {
uint32 crc;
crc = crc32_calc_buffer(data, length);
if (!msrpc_gen(sig, "dddd", NTLMSSP_SIGN_VERSION, 0, crc, ntlmssp_state->ntlmssp_seq_num)) {
return NT_STATUS_NO_MEMORY;
}
/* The order of these two operations matters - we must first seal the packet,
then seal the sequence number - this is becouse the ntlmssp_hash is not
constant, but is is rather updated with each iteration */
dump_data_pw("ntlmssp hash:\n", ntlmssp_state->ntlmssp_hash,
sizeof(ntlmssp_state->ntlmssp_hash));
NTLMSSPcalc_ap(ntlmssp_state->ntlmssp_hash, data, length);
dump_data_pw("ntlmssp hash:\n", ntlmssp_state->ntlmssp_hash,
sizeof(ntlmssp_state->ntlmssp_hash));
NTLMSSPcalc_ap(ntlmssp_state->ntlmssp_hash, sig->data+4, sig->length-4);
}
dump_data_pw("ntlmssp sealed data\n", data, length);
/* increment counter on send */
ntlmssp_state->ntlmssp_seq_num++;
return NT_STATUS_OK;
}
/**
* Unseal data with the NTLMSSP algorithm
*
*/
NTSTATUS ntlmssp_client_unseal_packet(NTLMSSP_CLIENT_STATE *ntlmssp_state,
uchar *data, size_t length,
DATA_BLOB *sig)
{
DEBUG(10,("ntlmssp_client_unseal_data: seal\n"));
dump_data_pw("ntlmssp sealed data\n", data, length);
if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
NTLMSSPcalc_ap(ntlmssp_state->srv_seal_hash, data, length);
} else {
dump_data_pw("ntlmssp hash:\n", ntlmssp_state->ntlmssp_hash,
sizeof(ntlmssp_state->ntlmssp_hash));
NTLMSSPcalc_ap(ntlmssp_state->ntlmssp_hash, data, length);
}
dump_data_pw("ntlmssp clear data\n", data, length);
return ntlmssp_client_check_packet(ntlmssp_state, data, length, sig);
}
/**
@ -190,37 +290,69 @@ NTSTATUS ntlmssp_client_check_packet(NTLMSSP_CLIENT_STATE *ntlmssp_state,
NTSTATUS ntlmssp_client_sign_init(NTLMSSP_CLIENT_STATE *ntlmssp_state)
{
unsigned char p24[24];
unsigned char lm_hash[16];
ZERO_STRUCT(p24);
DEBUG(3, ("NTLMSSP Sign/Seal - Initialising with flags:\n"));
debug_ntlmssp_flags(ntlmssp_state->neg_flags);
if (!ntlmssp_state->lm_resp.data) {
/* can't sign or check signitures yet */
return NT_STATUS_UNSUCCESSFUL;
}
E_deshash(ntlmssp_state->password, lm_hash);
NTLMSSPOWFencrypt(lm_hash, ntlmssp_state->lm_resp.data, p24);
if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2)
{
calc_ntlmv2_hash(ntlmssp_state->cli_sign_hash, ntlmssp_state->cli_sign_const, p24, CLI_SIGN);
calc_ntlmv2_hash(ntlmssp_state->cli_seal_hash, ntlmssp_state->cli_seal_const, p24, CLI_SEAL);
calc_ntlmv2_hash(ntlmssp_state->srv_sign_hash, ntlmssp_state->srv_sign_const, p24, SRV_SIGN);
calc_ntlmv2_hash(ntlmssp_state->srv_seal_hash, ntlmssp_state->srv_seal_const, p24, SRV_SEAL);
}
else
{
char k2[8];
memcpy(k2, p24, 5);
k2[5] = 0xe5;
k2[6] = 0x38;
k2[7] = 0xb0;
calc_ntlmv2_hash(ntlmssp_state->cli_sign_hash,
ntlmssp_state->cli_sign_const,
ntlmssp_state->session_key, CLI_SIGN);
dump_data_pw("NTLMSSP client sign hash:\n",
ntlmssp_state->cli_sign_hash,
sizeof(ntlmssp_state->cli_sign_hash));
calc_ntlmv2_hash(ntlmssp_state->cli_seal_hash,
ntlmssp_state->cli_seal_const,
ntlmssp_state->session_key, CLI_SEAL);
dump_data_pw("NTLMSSP client sesl hash:\n",
ntlmssp_state->cli_seal_hash,
sizeof(ntlmssp_state->cli_seal_hash));
calc_ntlmv2_hash(ntlmssp_state->srv_sign_hash,
ntlmssp_state->srv_sign_const,
ntlmssp_state->session_key, SRV_SIGN);
dump_data_pw("NTLMSSP server sign hash:\n",
ntlmssp_state->srv_sign_hash,
sizeof(ntlmssp_state->srv_sign_hash));
calc_ntlmv2_hash(ntlmssp_state->srv_seal_hash,
ntlmssp_state->srv_seal_const,
ntlmssp_state->session_key, SRV_SEAL);
dump_data_pw("NTLMSSP server seal hash:\n",
ntlmssp_state->cli_sign_hash,
sizeof(ntlmssp_state->cli_sign_hash));
}
else if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) {
if (!ntlmssp_state->session_key.data || ntlmssp_state->session_key.length < 8) {
/* can't sign or check signitures yet */
DEBUG(5, ("NTLMSSP Sign/Seal - cannot use LM KEY yet\n"));
return NT_STATUS_UNSUCCESSFUL;
}
calc_hash(ntlmssp_state->ntlmssp_hash, k2, 8);
DEBUG(5, ("NTLMSSP Sign/Seal - using LM KEY\n"));
calc_hash(ntlmssp_state->ntlmssp_hash, ntlmssp_state->session_key.data, 8);
dump_data_pw("NTLMSSP hash:\n", ntlmssp_state->ntlmssp_hash,
sizeof(ntlmssp_state->ntlmssp_hash));
} else {
if (!ntlmssp_state->session_key.data || ntlmssp_state->session_key.length < 16) {
/* can't sign or check signitures yet */
DEBUG(5, ("NTLMSSP Sign/Seal - cannot use NT KEY yet\n"));
return NT_STATUS_UNSUCCESSFUL;
}
DEBUG(5, ("NTLMSSP Sign/Seal - using NT KEY\n"));
calc_hash(ntlmssp_state->ntlmssp_hash, ntlmssp_state->session_key.data, 16);
dump_data_pw("NTLMSSP hash:\n", ntlmssp_state->ntlmssp_hash,
sizeof(ntlmssp_state->ntlmssp_hash));
}
ntlmssp_state->ntlmssp_seq_num = 0;
ZERO_STRUCT(lm_hash);
return NT_STATUS_OK;
}

View File

@ -43,15 +43,10 @@ static void pwd_init(struct pwd_info *pwd)
static void pwd_make_lm_nt_16(struct pwd_info *pwd, const char *clr)
{
pstring dos_passwd;
pwd_init(pwd);
push_ascii_pstring(dos_passwd, clr);
nt_lm_owf_gen(dos_passwd, pwd->smb_nt_pwd, pwd->smb_lm_pwd);
nt_lm_owf_gen(clr, pwd->smb_nt_pwd, pwd->smb_lm_pwd);
pwd->null_pwd = False;
pwd->cleartext = False;
pwd->crypted = False;
}
@ -61,12 +56,9 @@ static void pwd_make_lm_nt_16(struct pwd_info *pwd, const char *clr)
void pwd_set_cleartext(struct pwd_info *pwd, const char *clr)
{
pwd_init(pwd);
push_ascii_fstring(pwd->password, clr);
pwd->cleartext = True;
pwd->null_pwd = False;
pwd->crypted = False;
pwd_make_lm_nt_16(pwd, clr);
fstrcpy(pwd->password, clr);
pwd->cleartext = True;
}
/****************************************************************************

View File

@ -271,6 +271,8 @@ void SMBOWFencrypt_ntv2(const uchar kr[16],
void SMBsesskeygen_ntv2(const uchar kr[16],
const uchar * nt_resp, uint8 sess_key[16])
{
/* a very nice, 128 bit, variable session key */
HMACMD5Context ctx;
hmac_md5_init_limK_to_64(kr, 16, &ctx);
@ -286,6 +288,9 @@ void SMBsesskeygen_ntv2(const uchar kr[16],
void SMBsesskeygen_ntv1(const uchar kr[16],
const uchar * nt_resp, uint8 sess_key[16])
{
/* yes, this session key does not change - yes, this
is a problem - but it is 128 bits */
mdfour((unsigned char *)sess_key, kr, 16);
#ifdef DEBUG_PASSWORD
@ -294,6 +299,32 @@ void SMBsesskeygen_ntv1(const uchar kr[16],
#endif
}
void SMBsesskeygen_lmv1(const uchar lm_hash[16],
const uchar lm_resp[24], /* only uses 8 */
uint8 sess_key[16])
{
/* Calculate the LM session key (effective length 40 bits,
but changes with each session) */
uchar p24[24];
uchar partial_lm_hash[16];
memcpy(partial_lm_hash, lm_hash, 8);
memset(partial_lm_hash + 8, 0xbd, 8);
SMBOWFencrypt(lm_hash, lm_resp, p24);
memcpy(sess_key, p24, 16);
sess_key[5] = 0xe5;
sess_key[6] = 0x38;
sess_key[7] = 0xb0;
#ifdef DEBUG_PASSWORD
DEBUG(100, ("SMBsesskeygen_lmv1:\n"));
dump_data(100, sess_key, 16);
#endif
}
DATA_BLOB NTLMv2_generate_names_blob(const char *hostname,
const char *domain)
{

File diff suppressed because it is too large Load Diff

View File

@ -1378,141 +1378,221 @@ static void netsechash(uchar * key, uchar * data, int data_len)
}
}
void dump_data_pw(const char *msg, const uchar * data, size_t len)
/*******************************************************************
Create a digest over the entire packet (including the data), and
MD5 it with the session key.
********************************************************************/
static void netsec_digest(struct netsec_auth_struct *a,
int auth_flags,
RPC_AUTH_NETSEC_CHK * verf,
char *data, size_t data_len,
uchar digest_final[16])
{
#ifdef DEBUG_PASSWORD
DEBUG(11, ("%s", msg));
if (data != NULL && len > 0)
{
dump_data(11, data, len);
uchar whole_packet_digest[16];
static uchar zeros[4];
struct MD5Context ctx3;
/* verfiy the signature on the packet by MD5 over various bits */
MD5Init(&ctx3);
/* use our sequence number, which ensures the packet is not
out of order */
MD5Update(&ctx3, zeros, sizeof(zeros));
MD5Update(&ctx3, verf->sig, sizeof(verf->sig));
if (auth_flags & AUTH_PIPE_SEAL) {
MD5Update(&ctx3, verf->data8, sizeof(verf->data8));
}
#endif
MD5Update(&ctx3, data, data_len);
MD5Final(whole_packet_digest, &ctx3);
dump_data_pw("whole_packet_digest:\n", whole_packet_digest, sizeof(whole_packet_digest));
/* MD5 this result and the session key, to prove that
only a valid client could had produced this */
hmac_md5(a->sess_key, whole_packet_digest, sizeof(whole_packet_digest), digest_final);
}
void netsec_encode(struct netsec_auth_struct *a,
RPC_AUTH_NETSEC_CHK * verf, char *data, size_t data_len)
/*******************************************************************
Calculate the key with which to encode the data payload
********************************************************************/
static void netsec_get_sealing_key(struct netsec_auth_struct *a,
RPC_AUTH_NETSEC_CHK *verf,
uchar sealing_key[16])
{
uchar dataN[4];
uchar digest1[16];
struct MD5Context ctx3;
static uchar zeros[4];
uchar digest2[16];
uchar sess_kf0[16];
int i;
SIVAL(dataN, 0, 0);
for (i = 0; i < sizeof(sess_kf0); i++) {
sess_kf0[i] = a->sess_key[i] ^ 0xf0;
}
dump_data_pw("sess_kf0:\n", sess_kf0, sizeof(sess_kf0));
/* MD5 of sess_kf0 and the high bytes of the sequence number */
hmac_md5(sess_kf0, zeros, 0x4, digest2);
dump_data_pw("digest2:\n", digest2, sizeof(digest2));
/* MD5 of the above result, plus 8 bytes of sequence number */
hmac_md5(digest2, verf->seq_num, sizeof(verf->seq_num), sealing_key);
dump_data_pw("sealing_key:\n", sealing_key, 16);
}
/*******************************************************************
Encode or Decode the sequence number (which is symmetric)
********************************************************************/
static void netsec_deal_with_seq_num(struct netsec_auth_struct *a,
RPC_AUTH_NETSEC_CHK *verf)
{
static uchar zeros[4];
uchar sequence_key[16];
uchar digest1[16];
hmac_md5(a->sess_key, zeros, sizeof(zeros), digest1);
dump_data_pw("(sequence key) digest1:\n", digest1, sizeof(digest1));
hmac_md5(digest1, verf->packet_digest, 8, sequence_key);
dump_data_pw("sequence_key:\n", sequence_key, sizeof(sequence_key));
dump_data_pw("seq_num (before):\n", verf->seq_num, sizeof(verf->seq_num));
netsechash(sequence_key, verf->seq_num, 8);
dump_data_pw("seq_num (after):\n", verf->seq_num, sizeof(verf->seq_num));
}
/*******************************************************************
Encode a blob of data using the netsec (schannel) alogrithm, also produceing
a checksum over the original data. We currently only support
signing and sealing togeather - the signing-only code is close, but not
quite compatible with what MS does.
********************************************************************/
void netsec_encode(struct netsec_auth_struct *a, int auth_flags,
enum netsec_direction direction,
RPC_AUTH_NETSEC_CHK * verf, char *data, size_t data_len)
{
uchar digest_final[16];
DEBUG(10,("SCHANNEL: netsec_encode seq_num=%d data_len=%d\n", a->seq_num, data_len));
dump_data_pw("a->sess_key:\n", a->sess_key, sizeof(a->sess_key));
dump_data_pw("a->seq_num :\n", dataN, sizeof(dataN));
MD5Init(&ctx3);
MD5Update(&ctx3, dataN, 0x4);
MD5Update(&ctx3, verf->sig, 8);
RSIVAL(verf->seq_num, 0, a->seq_num);
MD5Update(&ctx3, verf->data8, 8);
dump_data_pw("verf->data8:\n", verf->data8, sizeof(verf->data8));
dump_data_pw("sess_kf0:\n", sess_kf0, sizeof(sess_kf0));
hmac_md5(sess_kf0, dataN, 0x4, digest1);
dump_data_pw("digest1 (ebp-8):\n", digest1, sizeof(digest1));
hmac_md5(digest1, verf->data3, 8, digest1);
dump_data_pw("netsechashkey:\n", digest1, sizeof(digest1));
netsechash(digest1, verf->data8, 8);
dump_data_pw("verf->data8:\n", verf->data8, sizeof(verf->data8));
dump_data_pw("data :\n", data, data_len);
MD5Update(&ctx3, data, data_len);
{
char digest_tmp[16];
char digest2[16];
MD5Final(digest_tmp, &ctx3);
hmac_md5(a->sess_key, digest_tmp, 16, digest2);
dump_data_pw("digest_tmp:\n", digest_tmp, sizeof(digest_tmp));
dump_data_pw("digest:\n", digest2, sizeof(digest2));
memcpy(verf->data1, digest2, sizeof(verf->data1));
switch (direction) {
case SENDER_IS_INITIATOR:
SIVAL(verf->seq_num, 4, 0x80);
break;
case SENDER_IS_ACCEPTOR:
SIVAL(verf->seq_num, 4, 0x0);
break;
}
netsechash(digest1, data, data_len);
dump_data_pw("data:\n", data, data_len);
dump_data_pw("verf->seq_num:\n", verf->seq_num, sizeof(verf->seq_num));
hmac_md5(a->sess_key, dataN, 0x4, digest1);
dump_data_pw("ctx:\n", digest1, sizeof(digest1));
/* produce a digest of the packet to prove it's legit (before we seal it) */
netsec_digest(a, auth_flags, verf, data, data_len, digest_final);
memcpy(verf->packet_digest, digest_final, sizeof(verf->packet_digest));
hmac_md5(digest1, verf->data1, 8, digest1);
if (auth_flags & AUTH_PIPE_SEAL) {
uchar sealing_key[16];
dump_data_pw("netsechashkey:\n", digest1, sizeof(digest1));
/* get the key to encode the data with */
netsec_get_sealing_key(a, verf, sealing_key);
dump_data_pw("verf->data3:\n", verf->data3, sizeof(verf->data3));
netsechash(digest1, verf->data3, 8);
dump_data_pw("verf->data3:\n", verf->data3, sizeof(verf->data3));
/* encode the verification data */
dump_data_pw("verf->data8:\n", verf->data8, sizeof(verf->data8));
netsechash(sealing_key, verf->data8, 8);
dump_data_pw("verf->data8_enc:\n", verf->data8, sizeof(verf->data8));
/* encode the packet payload */
dump_data_pw("data:\n", data, data_len);
netsechash(sealing_key, data, data_len);
dump_data_pw("data_enc:\n", data, data_len);
}
/* encode the sequence number (key based on packet digest) */
/* needs to be done after the sealing, as the original version
is used in the sealing stuff... */
netsec_deal_with_seq_num(a, verf);
return;
}
BOOL netsec_decode(struct netsec_auth_struct *a,
/*******************************************************************
Decode a blob of data using the netsec (schannel) alogrithm, also verifiying
a checksum over the original data. We currently can verify signed messages,
as well as decode sealed messages
********************************************************************/
BOOL netsec_decode(struct netsec_auth_struct *a, int auth_flags,
enum netsec_direction direction,
RPC_AUTH_NETSEC_CHK * verf, char *data, size_t data_len)
{
uchar dataN[4];
uchar digest1[16];
struct MD5Context ctx3;
uchar sess_kf0[16];
int i;
uchar digest_final[16];
SIVAL(dataN, 0, 0);
/* Create the expected sequence number for comparison */
uchar seq_num[8];
RSIVAL(seq_num, 0, a->seq_num);
for (i = 0; i < sizeof(sess_kf0); i++) {
sess_kf0[i] = a->sess_key[i] ^ 0xf0;
switch (direction) {
case SENDER_IS_INITIATOR:
SIVAL(seq_num, 4, 0x80);
break;
case SENDER_IS_ACCEPTOR:
SIVAL(seq_num, 4, 0x0);
break;
}
DEBUG(10,("SCHANNEL: netsec_decode seq_num=%d data_len=%d\n", a->seq_num, data_len));
dump_data_pw("a->sess_key:\n", a->sess_key, sizeof(a->sess_key));
dump_data_pw("a->seq_num :\n", dataN, sizeof(dataN));
hmac_md5(a->sess_key, dataN, 0x4, digest1);
dump_data_pw("ctx:\n", digest1, sizeof(digest1));
hmac_md5(digest1, verf->data1, 8, digest1);
dump_data_pw("seq_num:\n", seq_num, sizeof(seq_num));
dump_data_pw("netsechashkey:\n", digest1, sizeof(digest1));
dump_data_pw("verf->data3:\n", verf->data3, sizeof(verf->data3));
netsechash(digest1, verf->data3, 8);
dump_data_pw("verf->data3_dec:\n", verf->data3, sizeof(verf->data3));
/* extract the sequence number (key based on supplied packet digest) */
/* needs to be done before the sealing, as the original version
is used in the sealing stuff... */
netsec_deal_with_seq_num(a, verf);
MD5Init(&ctx3);
MD5Update(&ctx3, dataN, 0x4);
MD5Update(&ctx3, verf->sig, 8);
dump_data_pw("sess_kf0:\n", sess_kf0, sizeof(sess_kf0));
hmac_md5(sess_kf0, dataN, 0x4, digest1);
dump_data_pw("digest1 (ebp-8):\n", digest1, sizeof(digest1));
hmac_md5(digest1, verf->data3, 8, digest1);
dump_data_pw("netsechashkey:\n", digest1, sizeof(digest1));
dump_data_pw("verf->data8:\n", verf->data8, sizeof(verf->data8));
netsechash(digest1, verf->data8, 8);
dump_data_pw("verf->data8_dec:\n", verf->data8, sizeof(verf->data8));
MD5Update(&ctx3, verf->data8, 8);
dump_data_pw("data :\n", data, data_len);
netsechash(digest1, data, data_len);
dump_data_pw("datadec:\n", data, data_len);
MD5Update(&ctx3, data, data_len);
{
uchar digest_tmp[16];
MD5Final(digest_tmp, &ctx3);
hmac_md5(a->sess_key, digest_tmp, 16, digest1);
dump_data_pw("digest_tmp:\n", digest_tmp, sizeof(digest_tmp));
if (memcmp(verf->seq_num, seq_num, sizeof(seq_num))) {
/* don't even bother with the below if the sequence number is out */
/* The sequence number is MD5'ed with a key based on the whole-packet
digest, as supplied by the client. We check that it's a valid
checksum after the decode, below
*/
return False;
}
dump_data_pw("digest:\n", digest1, sizeof(digest1));
dump_data_pw("verf->data1:\n", verf->data1, sizeof(verf->data1));
if (auth_flags & AUTH_PIPE_SEAL) {
uchar sealing_key[16];
/* get the key to extract the data with */
netsec_get_sealing_key(a, verf, sealing_key);
return memcmp(digest1, verf->data1, sizeof(verf->data1)) == 0;
/* extract the verification data */
dump_data_pw("verf->data8:\n", verf->data8,
sizeof(verf->data8));
netsechash(sealing_key, verf->data8, 8);
dump_data_pw("verf->data8_dec:\n", verf->data8,
sizeof(verf->data8));
/* extract the packet payload */
dump_data_pw("data :\n", data, data_len);
netsechash(sealing_key, data, data_len);
dump_data_pw("datadec:\n", data, data_len);
}
/* digest includes 'data' after unsealing */
netsec_digest(a, auth_flags, verf, data, data_len, digest_final);
dump_data_pw("Calculated digest:\n", digest_final,
sizeof(digest_final));
dump_data_pw("verf->packet_digest:\n", verf->packet_digest,
sizeof(verf->packet_digest));
/* compare - if the client got the same result as us, then
it must know the session key */
return (memcmp(digest_final, verf->packet_digest,
sizeof(verf->packet_digest)) == 0);
}

View File

@ -602,15 +602,6 @@ BOOL smb_io_rpc_hdr_autha(const char *desc, RPC_HDR_AUTHA *rai, prs_struct *ps,
return True;
}
/*******************************************************************
Checks an RPC_HDR_AUTH structure.
********************************************************************/
BOOL rpc_hdr_auth_chk(RPC_HDR_AUTH *rai)
{
return (rai->auth_type == NTLMSSP_AUTH_TYPE && rai->auth_level == NTLMSSP_AUTH_LEVEL);
}
/*******************************************************************
Inits an RPC_HDR_AUTH structure.
********************************************************************/
@ -1088,9 +1079,10 @@ BOOL rpc_auth_ntlmssp_chk(RPC_AUTH_NTLMSSP_CHK *chk, uint32 crc32, uint32 seq_nu
chk->seq_num != seq_num)
{
DEBUG(5,("verify failed - crc %x ver %x seq %d\n",
crc32, NTLMSSP_SIGN_VERSION, seq_num));
chk->crc32, chk->ver, chk->seq_num));
DEBUG(5,("verify expect - crc %x ver %x seq %d\n",
chk->crc32, chk->ver, chk->seq_num));
crc32, NTLMSSP_SIGN_VERSION, seq_num));
return False;
}
return True;
@ -1182,15 +1174,15 @@ creates an RPC_AUTH_NETSEC_CHK structure.
********************************************************************/
BOOL init_rpc_auth_netsec_chk(RPC_AUTH_NETSEC_CHK * chk,
const uchar sig[8],
const uchar data1[8],
const uchar data3[8], const uchar data8[8])
const uchar packet_digest[8],
const uchar seq_num[8], const uchar data8[8])
{
if (chk == NULL)
return False;
memcpy(chk->sig, sig, sizeof(chk->sig));
memcpy(chk->data1, data1, sizeof(chk->data1));
memcpy(chk->data3, data3, sizeof(chk->data3));
memcpy(chk->packet_digest, packet_digest, sizeof(chk->packet_digest));
memcpy(chk->seq_num, seq_num, sizeof(chk->seq_num));
memcpy(chk->data8, data8, sizeof(chk->data8));
return True;
@ -1209,8 +1201,8 @@ BOOL smb_io_rpc_auth_netsec_chk(const char *desc, RPC_AUTH_NETSEC_CHK * chk,
depth++;
prs_uint8s(False, "sig ", ps, depth, chk->sig, sizeof(chk->sig));
prs_uint8s(False, "data3", ps, depth, chk->data3, sizeof(chk->data3));
prs_uint8s(False, "data1", ps, depth, chk->data1, sizeof(chk->data1));
prs_uint8s(False, "seq_num", ps, depth, chk->seq_num, sizeof(chk->seq_num));
prs_uint8s(False, "packet_digest", ps, depth, chk->packet_digest, sizeof(chk->packet_digest));
prs_uint8s(False, "data8", ps, depth, chk->data8, sizeof(chk->data8));
return True;

View File

@ -227,7 +227,7 @@ BOOL create_next_pdu(pipes_struct *p)
if (auth_seal || auth_verify) {
RPC_HDR_AUTH auth_info;
init_rpc_hdr_auth(&auth_info, NTLMSSP_AUTH_TYPE, NTLMSSP_AUTH_LEVEL,
init_rpc_hdr_auth(&auth_info, NTLMSSP_AUTH_TYPE, RPC_PIPE_AUTH_SEAL_LEVEL,
(auth_verify ? RPC_HDR_AUTH_LEN : 0), (auth_verify ? 1 : 0));
if(!smb_io_rpc_hdr_auth("hdr_auth", &auth_info, &outgoing_pdu, 0)) {
DEBUG(0,("create_next_pdu: failed to marshall RPC_HDR_AUTH.\n"));
@ -263,11 +263,9 @@ BOOL create_next_pdu(pipes_struct *p)
prs_struct rverf;
prs_struct rauth;
uchar sign[8];
data = prs_data_p(&outgoing_pdu) + data_pos;
init_rpc_hdr_auth(&auth_info, NETSEC_AUTH_TYPE, NETSEC_AUTH_LEVEL,
init_rpc_hdr_auth(&auth_info, NETSEC_AUTH_TYPE, RPC_PIPE_AUTH_SEAL_LEVEL,
RPC_HDR_AUTH_LEN, 1);
if(!smb_io_rpc_hdr_auth("hdr_auth", &auth_info, &outgoing_pdu, 0)) {
@ -284,12 +282,12 @@ BOOL create_next_pdu(pipes_struct *p)
p->netsec_auth.seq_num));
}
RSIVAL(sign, 0, p->netsec_auth.seq_num);
SIVAL(sign, 4, 0);
init_rpc_auth_netsec_chk(&verf, netsec_sig, nullbytes, nullbytes, nullbytes);
init_rpc_auth_netsec_chk(&verf, netsec_sig, nullbytes, sign, nullbytes);
netsec_encode(&p->netsec_auth, &verf, data, data_len);
netsec_encode(&p->netsec_auth,
AUTH_PIPE_NETSEC|AUTH_PIPE_SIGN|AUTH_PIPE_SEAL,
SENDER_IS_ACCEPTOR,
&verf, data, data_len);
smb_io_rpc_auth_netsec_chk("", &verf, &outgoing_pdu, 0);
@ -458,6 +456,10 @@ failed authentication on named pipe %s.\n", domain, user_name, wks, p->name ));
p->ntlmssp_hash[256] = 0;
p->ntlmssp_hash[257] = 0;
}
dump_data_pw("NTLMSSP hash (v1)\n", p->ntlmssp_hash,
sizeof(p->ntlmssp_hash));
/* NTLMSSPhash(p->ntlmssp_hash, p24); */
p->ntlmssp_seq_num = 0;
@ -546,7 +548,7 @@ BOOL api_pipe_bind_auth_resp(pipes_struct *p, prs_struct *rpc_in_p)
return False;
}
if (autha_info.auth_type != NTLMSSP_AUTH_TYPE || autha_info.auth_level != NTLMSSP_AUTH_LEVEL) {
if (autha_info.auth_type != NTLMSSP_AUTH_TYPE || autha_info.auth_level != RPC_PIPE_AUTH_SEAL_LEVEL) {
DEBUG(0,("api_pipe_bind_auth_resp: incorrect auth type (%d) or level (%d).\n",
(int)autha_info.auth_type, (int)autha_info.auth_level ));
return False;
@ -1070,7 +1072,7 @@ BOOL api_pipe_bind_req(pipes_struct *p, prs_struct *rpc_in_p)
/*** Authentication info ***/
init_rpc_hdr_auth(&auth_info, NTLMSSP_AUTH_TYPE, NTLMSSP_AUTH_LEVEL, RPC_HDR_AUTH_LEN, 1);
init_rpc_hdr_auth(&auth_info, NTLMSSP_AUTH_TYPE, RPC_PIPE_AUTH_SEAL_LEVEL, RPC_HDR_AUTH_LEN, 1);
if(!smb_io_rpc_hdr_auth("", &auth_info, &out_auth, 0)) {
DEBUG(0,("api_pipe_bind_req: marshalling of RPC_HDR_AUTH failed.\n"));
goto err_exit;
@ -1105,7 +1107,7 @@ BOOL api_pipe_bind_req(pipes_struct *p, prs_struct *rpc_in_p)
re-used from the auth2 the client did before. */
p->dc = last_dcinfo;
init_rpc_hdr_auth(&auth_info, NETSEC_AUTH_TYPE, NETSEC_AUTH_LEVEL, RPC_HDR_AUTH_LEN, 1);
init_rpc_hdr_auth(&auth_info, NETSEC_AUTH_TYPE, RPC_PIPE_AUTH_SEAL_LEVEL, RPC_HDR_AUTH_LEN, 1);
if(!smb_io_rpc_hdr_auth("", &auth_info, &out_auth, 0)) {
DEBUG(0,("api_pipe_bind_req: marshalling of RPC_HDR_AUTH failed.\n"));
goto err_exit;
@ -1226,7 +1228,14 @@ BOOL api_pipe_auth_process(pipes_struct *p, prs_struct *rpc_in)
* has already been consumed.
*/
char *data = prs_data_p(rpc_in) + RPC_HDR_REQ_LEN;
dump_data_pw("NTLMSSP hash (v1)\n", p->ntlmssp_hash,
sizeof(p->ntlmssp_hash));
dump_data_pw("Incoming RPC PDU (NTLMSSP sealed)\n",
data, data_len);
NTLMSSPcalc_p(p, (uchar*)data, data_len);
dump_data_pw("Incoming RPC PDU (NTLMSSP unsealed)\n",
data, data_len);
crc32 = crc32_calc_buffer(data, data_len);
}
@ -1335,7 +1344,7 @@ BOOL api_pipe_netsec_process(pipes_struct *p, prs_struct *rpc_in)
}
if ((auth_info.auth_type != NETSEC_AUTH_TYPE) ||
(auth_info.auth_level != NETSEC_AUTH_LEVEL)) {
(auth_info.auth_level != RPC_PIPE_AUTH_SEAL_LEVEL)) {
DEBUG(0,("Invalid auth info %d or level %d on schannel\n",
auth_info.auth_type, auth_info.auth_level));
return False;
@ -1346,7 +1355,10 @@ BOOL api_pipe_netsec_process(pipes_struct *p, prs_struct *rpc_in)
return False;
}
if (!netsec_decode(&p->netsec_auth, &netsec_chk,
if (!netsec_decode(&p->netsec_auth,
AUTH_PIPE_NETSEC|AUTH_PIPE_SIGN|AUTH_PIPE_SEAL,
SENDER_IS_INITIATOR,
&netsec_chk,
prs_data_p(rpc_in)+old_offset, data_len)) {
DEBUG(0,("failed to decode PDU\n"));
return False;

View File

@ -319,6 +319,119 @@ static NTSTATUS cmd_quit(struct cli_state *cli, TALLOC_CTX *mem_ctx,
return NT_STATUS_OK; /* NOTREACHED */
}
static NTSTATUS cmd_sign(struct cli_state *cli, TALLOC_CTX *mem_ctx,
int argc, const char **argv)
{
if (cli->pipe_auth_flags == (AUTH_PIPE_NTLMSSP|AUTH_PIPE_SIGN)) {
return NT_STATUS_OK;
} else {
/* still have session, just need to use it again */
cli->pipe_auth_flags = AUTH_PIPE_NTLMSSP;
cli->pipe_auth_flags |= AUTH_PIPE_SIGN;
if (cli->nt_pipe_fnum != 0)
cli_nt_session_close(cli);
}
return NT_STATUS_OK;
}
static NTSTATUS cmd_seal(struct cli_state *cli, TALLOC_CTX *mem_ctx,
int argc, const char **argv)
{
if (cli->pipe_auth_flags == (AUTH_PIPE_NTLMSSP|AUTH_PIPE_SIGN|AUTH_PIPE_SEAL)) {
return NT_STATUS_OK;
} else {
/* still have session, just need to use it again */
cli->pipe_auth_flags = AUTH_PIPE_NTLMSSP;
cli->pipe_auth_flags |= AUTH_PIPE_SIGN;
cli->pipe_auth_flags |= AUTH_PIPE_SEAL;
if (cli->nt_pipe_fnum != 0)
cli_nt_session_close(cli);
}
return NT_STATUS_OK;
}
static NTSTATUS cmd_none(struct cli_state *cli, TALLOC_CTX *mem_ctx,
int argc, const char **argv)
{
if (cli->pipe_auth_flags == 0) {
return NT_STATUS_OK;
} else {
/* still have session, just need to use it again */
cli->pipe_auth_flags = 0;
if (cli->nt_pipe_fnum != 0)
cli_nt_session_close(cli);
}
cli->pipe_auth_flags = 0;
return NT_STATUS_OK;
}
static NTSTATUS cmd_schannel(struct cli_state *cli, TALLOC_CTX *mem_ctx,
int argc, const char **argv)
{
uchar trust_password[16];
uint32 sec_channel_type;
uint32 neg_flags = 0x000001ff;
NTSTATUS result;
static uchar zeros[16];
/* Cleanup */
if ((memcmp(cli->auth_info.sess_key, zeros, sizeof(cli->auth_info.sess_key)) != 0)
&& (cli->saved_netlogon_pipe_fnum != 0)) {
if (cli->pipe_auth_flags == (AUTH_PIPE_NETSEC|AUTH_PIPE_SIGN|AUTH_PIPE_SEAL)) {
return NT_STATUS_OK;
} else {
/* still have session, just need to use it again */
cli->pipe_auth_flags = AUTH_PIPE_NETSEC;
cli->pipe_auth_flags |= AUTH_PIPE_SIGN;
cli->pipe_auth_flags |= AUTH_PIPE_SEAL;
if (cli->nt_pipe_fnum != 0)
cli_nt_session_close(cli);
}
}
if (cli->nt_pipe_fnum != 0)
cli_nt_session_close(cli);
cli->pipe_auth_flags = 0;
if (!secrets_fetch_trust_account_password(lp_workgroup(),
trust_password,
NULL, &sec_channel_type)) {
return NT_STATUS_UNSUCCESSFUL;
}
if (!cli_nt_session_open(cli, PI_NETLOGON)) {
DEBUG(0, ("Could not initialise %s\n",
get_pipe_name_from_index(PI_NETLOGON)));
return NT_STATUS_UNSUCCESSFUL;
}
neg_flags |= NETLOGON_NEG_SCHANNEL;
result = cli_nt_setup_creds(cli, sec_channel_type, trust_password,
&neg_flags, 2);
if (!NT_STATUS_IS_OK(result)) {
ZERO_STRUCT(cli->auth_info.sess_key);
cli->pipe_auth_flags = 0;
return result;
}
memcpy(cli->auth_info.sess_key, cli->sess_key,
sizeof(cli->auth_info.sess_key));
cli->saved_netlogon_pipe_fnum = cli->nt_pipe_fnum;
cli->pipe_auth_flags = AUTH_PIPE_NETSEC;
cli->pipe_auth_flags |= AUTH_PIPE_SIGN;
cli->pipe_auth_flags |= AUTH_PIPE_SEAL;
return NT_STATUS_OK;
}
/* Built in rpcclient commands */
static struct cmd_set rpcclient_commands[] = {
@ -331,6 +444,10 @@ static struct cmd_set rpcclient_commands[] = {
{ "list", RPC_RTYPE_NTSTATUS, cmd_listcommands, NULL, -1, "List available commands on <pipe>", "pipe" },
{ "exit", RPC_RTYPE_NTSTATUS, cmd_quit, NULL, -1, "Exit program", "" },
{ "quit", RPC_RTYPE_NTSTATUS, cmd_quit, NULL, -1, "Exit program", "" },
{ "sign", RPC_RTYPE_NTSTATUS, cmd_sign, NULL, -1, "Force RPC pipe connections to be signed", "" },
{ "seal", RPC_RTYPE_NTSTATUS, cmd_seal, NULL, -1, "Force RPC pipe connections to be sealed", "" },
{ "schannel", RPC_RTYPE_NTSTATUS, cmd_schannel, NULL, -1, "Force RPC pipe connections to be sealed with 'schannel' (NETSEC). Assumes valid machine account to this domain controller.", "" },
{ "none", RPC_RTYPE_NTSTATUS, cmd_none, NULL, -1, "Force RPC pipe connections to have no special properties", "" },
{ NULL }
};
@ -392,8 +509,9 @@ static NTSTATUS do_cmd(struct cli_state *cli,
struct cmd_set *cmd_entry,
int argc, char **argv)
{
NTSTATUS ntresult;
WERROR wresult;
NTSTATUS ntresult;
WERROR wresult;
uchar trust_password[16];
TALLOC_CTX *mem_ctx;
@ -401,12 +519,16 @@ static NTSTATUS do_cmd(struct cli_state *cli,
if (!(mem_ctx = talloc_init("do_cmd"))) {
DEBUG(0, ("talloc_init() failed\n"));
return NT_STATUS_UNSUCCESSFUL;
return NT_STATUS_NO_MEMORY;
}
/* Open pipe */
if (cmd_entry->pipe_idx != -1) {
if (cmd_entry->pipe_idx != -1
&& cmd_entry->pipe_idx != cli->pipe_idx) {
if (cli->nt_pipe_fnum != 0)
cli_nt_session_close(cli);
if (!cli_nt_session_open(cli, cmd_entry->pipe_idx)) {
DEBUG(0, ("Could not initialise %s\n",
get_pipe_name_from_index(cmd_entry->pipe_idx)));
@ -414,21 +536,25 @@ static NTSTATUS do_cmd(struct cli_state *cli,
}
}
if (cmd_entry->pipe_idx == PI_NETLOGON) {
uchar trust_password[16];
if ((cmd_entry->pipe_idx == PI_NETLOGON) && !(cli->pipe_auth_flags & AUTH_PIPE_NETSEC)) {
uint32 neg_flags = 0x000001ff;
uint32 sec_channel_type;
if (!secrets_fetch_trust_account_password(lp_workgroup(),
trust_password,
NULL, &sec_channel_type)) {
return NT_STATUS_UNSUCCESSFUL;
}
if (!NT_STATUS_IS_OK(cli_nt_establish_netlogon(cli, sec_channel_type,
trust_password))) {
DEBUG(0, ("Could not initialise NETLOGON pipe\n"));
return NT_STATUS_UNSUCCESSFUL;
ntresult = cli_nt_setup_creds(cli, sec_channel_type,
trust_password,
&neg_flags, 2);
if (!NT_STATUS_IS_OK(ntresult)) {
ZERO_STRUCT(cli->auth_info.sess_key);
printf("nt_setup_creds failed with %s\n", nt_errstr(ntresult));
return ntresult;
}
}
/* Run command */
@ -450,9 +576,6 @@ static NTSTATUS do_cmd(struct cli_state *cli,
/* Cleanup */
if (cmd_entry->pipe_idx != -1)
cli_nt_session_close(cli);
talloc_destroy(mem_ctx);
return ntresult;