From 033ced60ac734161686bd3da685f2d7b056e17c8 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 16 Sep 2009 02:03:46 +0200 Subject: libcli/auth: rewrite schannel sign/seal code to be more generic This prepares support for HMAC-SHA256/AES. metze --- libcli/auth/schannel_sign.c | 297 ++++++++++++++++++++++---------------------- 1 file changed, 148 insertions(+), 149 deletions(-) (limited to 'libcli/auth/schannel_sign.c') diff --git a/libcli/auth/schannel_sign.c b/libcli/auth/schannel_sign.c index e60b4107bb..0672f67197 100644 --- a/libcli/auth/schannel_sign.c +++ b/libcli/auth/schannel_sign.c @@ -24,58 +24,89 @@ #include "../libcli/auth/schannel.h" #include "../lib/crypto/crypto.h" -#define NETSEC_SIGN_SIGNATURE { 0x77, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00 } -#define NETSEC_SEAL_SIGNATURE { 0x77, 0x00, 0x7a, 0x00, 0xff, 0xff, 0x00, 0x00 } +static void netsec_offset_and_sizes(struct schannel_state *state, + bool do_seal, + uint32_t *_min_sig_size, + uint32_t *_used_sig_size, + uint32_t *_checksum_length, + uint32_t *_confounder_ofs) +{ + uint32_t min_sig_size = 24; + uint32_t used_sig_size = 32; + uint32_t checksum_length = 8; + uint32_t confounder_ofs = 24; + + if (do_seal) { + min_sig_size += 8; + } + + if (_min_sig_size) { + *_min_sig_size = min_sig_size; + } + + if (_used_sig_size) { + *_used_sig_size = used_sig_size; + } + + if (_checksum_length) { + *_checksum_length = checksum_length; + } + + if (_confounder_ofs) { + *_confounder_ofs = confounder_ofs; + } +} /******************************************************************* Encode or Decode the sequence number (which is symmetric) ********************************************************************/ -static void netsec_deal_with_seq_num(struct schannel_state *state, - const uint8_t packet_digest[8], - uint8_t seq_num[8]) +static void netsec_do_seq_num(struct schannel_state *state, + const uint8_t *checksum, + uint32_t checksum_length, + uint8_t seq_num[8]) { static const uint8_t zeros[4]; uint8_t sequence_key[16]; uint8_t digest1[16]; hmac_md5(state->creds->session_key, zeros, sizeof(zeros), digest1); - hmac_md5(digest1, packet_digest, 8, sequence_key); + hmac_md5(digest1, checksum, checksum_length, sequence_key); arcfour_crypt(seq_num, sequence_key, 8); state->seq_num++; } - -/******************************************************************* - Calculate the key with which to encode the data payload - ********************************************************************/ -static void netsec_get_sealing_key(const uint8_t session_key[16], - const uint8_t seq_num[8], - uint8_t sealing_key[16]) +static void netsec_do_seal(struct schannel_state *state, + const uint8_t seq_num[8], + uint8_t confounder[8], + uint8_t *data, uint32_t length) { + uint8_t sealing_key[16]; static const uint8_t zeros[4]; uint8_t digest2[16]; uint8_t sess_kf0[16]; int i; for (i = 0; i < 16; i++) { - sess_kf0[i] = session_key[i] ^ 0xf0; + sess_kf0[i] = state->creds->session_key[i] ^ 0xf0; } hmac_md5(sess_kf0, zeros, 4, digest2); hmac_md5(digest2, seq_num, 8, sealing_key); -} + arcfour_crypt(confounder, sealing_key, 8); + arcfour_crypt(data, sealing_key, length); +} /******************************************************************* Create a digest over the entire packet (including the data), and MD5 it with the session key. ********************************************************************/ -static void schannel_digest(const uint8_t sess_key[16], - const uint8_t netsec_sig[8], - const uint8_t *confounder, - const uint8_t *data, size_t data_len, - uint8_t digest_final[16]) +static void netsec_do_sign(struct schannel_state *state, + const uint8_t *confounder, + const uint8_t *data, size_t data_len, + uint8_t header[8], + uint8_t *checksum) { uint8_t packet_digest[16]; static const uint8_t zeros[4]; @@ -83,187 +114,155 @@ static void schannel_digest(const uint8_t sess_key[16], MD5Init(&ctx); MD5Update(&ctx, zeros, 4); - MD5Update(&ctx, netsec_sig, 8); if (confounder) { + SSVAL(header, 0, NL_SIGN_HMAC_MD5); + SSVAL(header, 2, NL_SEAL_RC4); + SSVAL(header, 4, 0xFFFF); + SSVAL(header, 6, 0x0000); + + MD5Update(&ctx, header, 8); MD5Update(&ctx, confounder, 8); + } else { + SSVAL(header, 0, NL_SIGN_HMAC_MD5); + SSVAL(header, 2, NL_SEAL_NONE); + SSVAL(header, 4, 0xFFFF); + SSVAL(header, 6, 0x0000); + + MD5Update(&ctx, header, 8); } MD5Update(&ctx, data, data_len); MD5Final(packet_digest, &ctx); - hmac_md5(sess_key, packet_digest, sizeof(packet_digest), digest_final); + hmac_md5(state->creds->session_key, + packet_digest, sizeof(packet_digest), + checksum); } - -/* - unseal a packet -*/ -NTSTATUS schannel_unseal_packet(struct schannel_state *state, +NTSTATUS netsec_incoming_packet(struct schannel_state *state, TALLOC_CTX *mem_ctx, + bool do_unseal, uint8_t *data, size_t length, const DATA_BLOB *sig) { - uint8_t digest_final[16]; - uint8_t confounder[8]; + uint32_t min_sig_size = 0; + uint8_t header[8]; + uint8_t checksum[32]; + uint32_t checksum_length = sizeof(checksum_length); + uint8_t _confounder[8]; + uint8_t *confounder = NULL; + uint32_t confounder_ofs = 0; uint8_t seq_num[8]; - uint8_t sealing_key[16]; - static const uint8_t netsec_sig[8] = NETSEC_SEAL_SIGNATURE; + int ret; + + netsec_offset_and_sizes(state, + do_unseal, + &min_sig_size, + NULL, + &checksum_length, + &confounder_ofs); - if (sig->length != 32) { + if (sig->length < min_sig_size) { return NT_STATUS_ACCESS_DENIED; } - memcpy(confounder, sig->data+24, 8); + if (do_unseal) { + confounder = _confounder; + memcpy(confounder, sig->data+confounder_ofs, 8); + } else { + confounder = NULL; + } RSIVAL(seq_num, 0, state->seq_num); SIVAL(seq_num, 4, state->initiator?0:0x80); - netsec_get_sealing_key(state->creds->session_key, seq_num, sealing_key); - arcfour_crypt(confounder, sealing_key, 8); - arcfour_crypt(data, sealing_key, length); - - schannel_digest(state->creds->session_key, - netsec_sig, confounder, - data, length, digest_final); - - if (memcmp(digest_final, sig->data+16, 8) != 0) { - dump_data_pw("calc digest:", digest_final, 8); - dump_data_pw("wire digest:", sig->data+16, 8); - return NT_STATUS_ACCESS_DENIED; + if (do_unseal) { + netsec_do_seal(state, seq_num, + confounder, + data, length); } - netsec_deal_with_seq_num(state, digest_final, seq_num); - - if (memcmp(seq_num, sig->data+8, 8) != 0) { - dump_data_pw("calc seq num:", seq_num, 8); - dump_data_pw("wire seq num:", sig->data+8, 8); - return NT_STATUS_ACCESS_DENIED; - } - - return NT_STATUS_OK; -} - -/* - check the signature on a packet -*/ -NTSTATUS schannel_check_packet(struct schannel_state *state, - TALLOC_CTX *mem_ctx, - const uint8_t *data, size_t length, - const DATA_BLOB *sig) -{ - uint8_t digest_final[16]; - uint8_t seq_num[8]; - static const uint8_t netsec_sig[8] = NETSEC_SIGN_SIGNATURE; + netsec_do_sign(state, confounder, + data, length, + header, checksum); - /* w2k sends just 24 bytes and skip the confounder */ - if (sig->length != 32 && sig->length != 24) { + ret = memcmp(checksum, sig->data+16, checksum_length); + if (ret != 0) { + dump_data_pw("calc digest:", checksum, checksum_length); + dump_data_pw("wire digest:", sig->data+16, checksum_length); return NT_STATUS_ACCESS_DENIED; } - RSIVAL(seq_num, 0, state->seq_num); - SIVAL(seq_num, 4, state->initiator?0:0x80); - - dump_data_pw("seq_num:\n", seq_num, 8); - dump_data_pw("sess_key:\n", state->creds->session_key, 16); + netsec_do_seq_num(state, checksum, checksum_length, seq_num); - schannel_digest(state->creds->session_key, - netsec_sig, NULL, - data, length, digest_final); - - netsec_deal_with_seq_num(state, digest_final, seq_num); - - if (memcmp(seq_num, sig->data+8, 8) != 0) { + ret = memcmp(seq_num, sig->data+8, 8); + if (ret != 0) { dump_data_pw("calc seq num:", seq_num, 8); dump_data_pw("wire seq num:", sig->data+8, 8); return NT_STATUS_ACCESS_DENIED; } - if (memcmp(digest_final, sig->data+16, 8) != 0) { - dump_data_pw("calc digest:", digest_final, 8); - dump_data_pw("wire digest:", sig->data+16, 8); - return NT_STATUS_ACCESS_DENIED; - } - return NT_STATUS_OK; } - -/* - seal a packet -*/ -NTSTATUS schannel_seal_packet(struct schannel_state *state, - TALLOC_CTX *mem_ctx, - uint8_t *data, size_t length, - DATA_BLOB *sig) +NTSTATUS netsec_outgoing_packet(struct schannel_state *state, + TALLOC_CTX *mem_ctx, + bool do_seal, + uint8_t *data, size_t length, + DATA_BLOB *sig) { - uint8_t digest_final[16]; - uint8_t confounder[8]; + uint32_t min_sig_size = 0; + uint32_t used_sig_size = 0; + uint8_t header[8]; + uint8_t checksum[32]; + uint32_t checksum_length = sizeof(checksum_length); + uint8_t _confounder[8]; + uint8_t *confounder = NULL; + uint32_t confounder_ofs = 0; uint8_t seq_num[8]; - uint8_t sealing_key[16]; - static const uint8_t netsec_sig[8] = NETSEC_SEAL_SIGNATURE; - generate_random_buffer(confounder, 8); + netsec_offset_and_sizes(state, + do_seal, + &min_sig_size, + &used_sig_size, + &checksum_length, + &confounder_ofs); RSIVAL(seq_num, 0, state->seq_num); SIVAL(seq_num, 4, state->initiator?0x80:0); - schannel_digest(state->creds->session_key, - netsec_sig, confounder, - data, length, digest_final); - - netsec_get_sealing_key(state->creds->session_key, seq_num, sealing_key); - arcfour_crypt(confounder, sealing_key, 8); - arcfour_crypt(data, sealing_key, length); - - netsec_deal_with_seq_num(state, digest_final, seq_num); - - (*sig) = data_blob_talloc(mem_ctx, NULL, 32); - - memcpy(sig->data, netsec_sig, 8); - memcpy(sig->data+8, seq_num, 8); - memcpy(sig->data+16, digest_final, 8); - memcpy(sig->data+24, confounder, 8); - - dump_data_pw("signature:", sig->data+ 0, 8); - dump_data_pw("seq_num :", sig->data+ 8, 8); - dump_data_pw("digest :", sig->data+16, 8); - dump_data_pw("confound :", sig->data+24, 8); - - return NT_STATUS_OK; -} - - -/* - sign a packet -*/ -NTSTATUS schannel_sign_packet(struct schannel_state *state, - TALLOC_CTX *mem_ctx, - const uint8_t *data, size_t length, - DATA_BLOB *sig) -{ - uint8_t digest_final[16]; - uint8_t seq_num[8]; - static const uint8_t netsec_sig[8] = NETSEC_SIGN_SIGNATURE; + if (do_seal) { + confounder = _confounder; + generate_random_buffer(confounder, 8); + } else { + confounder = NULL; + } - RSIVAL(seq_num, 0, state->seq_num); - SIVAL(seq_num, 4, state->initiator?0x80:0); + netsec_do_sign(state, confounder, + data, length, + header, checksum); - schannel_digest(state->creds->session_key, - netsec_sig, NULL, - data, length, digest_final); + if (do_seal) { + netsec_do_seal(state, seq_num, + confounder, + data, length); + } - netsec_deal_with_seq_num(state, digest_final, seq_num); + netsec_do_seq_num(state, checksum, checksum_length, seq_num); - (*sig) = data_blob_talloc(mem_ctx, NULL, 32); + (*sig) = data_blob_talloc_zero(mem_ctx, used_sig_size); - memcpy(sig->data, netsec_sig, 8); + memcpy(sig->data, header, 8); memcpy(sig->data+8, seq_num, 8); - memcpy(sig->data+16, digest_final, 8); - memset(sig->data+24, 0, 8); + memcpy(sig->data+16, checksum, checksum_length); + + if (confounder) { + memcpy(sig->data+confounder_ofs, confounder, 8); + } dump_data_pw("signature:", sig->data+ 0, 8); dump_data_pw("seq_num :", sig->data+ 8, 8); - dump_data_pw("digest :", sig->data+16, 8); - dump_data_pw("confound :", sig->data+24, 8); + dump_data_pw("digest :", sig->data+16, checksum_length); + dump_data_pw("confound :", sig->data+confounder_ofs, 8); return NT_STATUS_OK; } -- cgit