diff options
author | Andrew Bartlett <abartlet@samba.org> | 2003-07-14 08:46:32 +0000 |
---|---|---|
committer | Andrew Bartlett <abartlet@samba.org> | 2003-07-14 08:46:32 +0000 |
commit | 456f51bcbe04ccbb37a27b6e115a851cc134adcd (patch) | |
tree | a68c7158940ad47fa856f894c6634a26669ef692 /source3/rpc_parse | |
parent | 379367dd7607514c17bc8ea8aa60212b1c6070a7 (diff) | |
download | samba-456f51bcbe04ccbb37a27b6e115a851cc134adcd.tar.gz samba-456f51bcbe04ccbb37a27b6e115a851cc134adcd.tar.bz2 samba-456f51bcbe04ccbb37a27b6e115a851cc134adcd.zip |
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 5472ddc9eaf4e79c5b2e1c8ee8c7f190dc285f19)
Diffstat (limited to 'source3/rpc_parse')
-rw-r--r-- | source3/rpc_parse/parse_prs.c | 270 | ||||
-rw-r--r-- | source3/rpc_parse/parse_rpc.c | 26 |
2 files changed, 184 insertions, 112 deletions
diff --git a/source3/rpc_parse/parse_prs.c b/source3/rpc_parse/parse_prs.c index 88150c718b..efd4914c66 100644 --- a/source3/rpc_parse/parse_prs.c +++ b/source3/rpc_parse/parse_prs.c @@ -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); +} - 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)); +/******************************************************************* + 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]; - MD5Init(&ctx3); - MD5Update(&ctx3, dataN, 0x4); - MD5Update(&ctx3, verf->sig, 8); + hmac_md5(a->sess_key, zeros, sizeof(zeros), digest1); + dump_data_pw("(sequence key) digest1:\n", digest1, sizeof(digest1)); - MD5Update(&ctx3, verf->data8, 8); + hmac_md5(digest1, verf->packet_digest, 8, sequence_key); - dump_data_pw("verf->data8:\n", verf->data8, sizeof(verf->data8)); - dump_data_pw("sess_kf0:\n", sess_kf0, sizeof(sess_kf0)); + dump_data_pw("sequence_key:\n", sequence_key, sizeof(sequence_key)); - 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("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)); +} - dump_data_pw("verf->data8:\n", verf->data8, sizeof(verf->data8)); - dump_data_pw("data :\n", data, data_len); - MD5Update(&ctx3, data, data_len); +/******************************************************************* + 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]; - { - 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)); + 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)); + + RSIVAL(verf->seq_num, 0, a->seq_num); + + 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; - - SIVAL(dataN, 0, 0); - - for (i = 0; i < sizeof(sess_kf0); i++) { - sess_kf0[i] = a->sess_key[i] ^ 0xf0; + uchar digest_final[16]; + + /* Create the expected sequence number for comparison */ + uchar seq_num[8]; + RSIVAL(seq_num, 0, a->seq_num); + + 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("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)); - - 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("seq_num:\n", seq_num, sizeof(seq_num)); - 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); + /* 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); - dump_data_pw("data :\n", data, data_len); - netsechash(digest1, data, data_len); - dump_data_pw("datadec:\n", data, data_len); + 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; + } - 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 (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); + + /* 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); } - dump_data_pw("digest:\n", digest1, sizeof(digest1)); - dump_data_pw("verf->data1:\n", verf->data1, sizeof(verf->data1)); + /* digest includes 'data' after unsealing */ + netsec_digest(a, auth_flags, verf, data, data_len, digest_final); - return memcmp(digest1, verf->data1, sizeof(verf->data1)) == 0; + 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); } diff --git a/source3/rpc_parse/parse_rpc.c b/source3/rpc_parse/parse_rpc.c index be3a04e31c..34ba62caa9 100644 --- a/source3/rpc_parse/parse_rpc.c +++ b/source3/rpc_parse/parse_rpc.c @@ -603,15 +603,6 @@ BOOL smb_io_rpc_hdr_autha(const char *desc, RPC_HDR_AUTHA *rai, prs_struct *ps, } /******************************************************************* - 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; |