From c290ece1f6bf1b8b6c11672eab692f418d738071 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 24 Sep 2014 22:58:49 +0200 Subject: [PATCH] libcli/smb: implement SMB 3.10 session setup Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison --- libcli/smb/smb2cli_session.c | 19 ++++- libcli/smb/smbXcli_base.c | 155 ++++++++++++++++++++++++++++++----- libcli/smb/smbXcli_base.h | 2 + 3 files changed, 155 insertions(+), 21 deletions(-) diff --git a/libcli/smb/smb2cli_session.c b/libcli/smb/smb2cli_session.c index 4418a0d68fa..65a604a585a 100644 --- a/libcli/smb/smb2cli_session.c +++ b/libcli/smb/smb2cli_session.c @@ -120,6 +120,7 @@ static void smb2cli_session_setup_done(struct tevent_req *subreq) tevent_req_data(req, struct smb2cli_session_setup_state); NTSTATUS status; + NTSTATUS preauth_status; uint64_t current_session_id; uint64_t session_id; uint16_t session_flags; @@ -127,6 +128,7 @@ static void smb2cli_session_setup_done(struct tevent_req *subreq) uint16_t security_buffer_offset; uint16_t security_buffer_length; uint8_t *security_buffer_data = NULL; + struct iovec sent_iov[3]; const uint8_t *hdr; const uint8_t *body; static const struct smb2cli_req_expected_response expected[] = { @@ -142,13 +144,28 @@ static void smb2cli_session_setup_done(struct tevent_req *subreq) status = smb2cli_req_recv(subreq, state, &state->recv_iov, expected, ARRAY_SIZE(expected)); - TALLOC_FREE(subreq); if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + TALLOC_FREE(subreq); tevent_req_nterror(req, status); return; } + smb2cli_req_get_sent_iov(subreq, sent_iov); + preauth_status = smb2cli_session_update_preauth(state->session, sent_iov); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, preauth_status)) { + return; + } + + if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + preauth_status = smb2cli_session_update_preauth(state->session, + state->recv_iov); + if (tevent_req_nterror(req, preauth_status)) { + return; + } + } + hdr = (const uint8_t *)state->recv_iov[0].iov_base; body = (const uint8_t *)state->recv_iov[1].iov_base; diff --git a/libcli/smb/smbXcli_base.c b/libcli/smb/smbXcli_base.c index cb16bed2af8..ad405a2f8e9 100644 --- a/libcli/smb/smbXcli_base.c +++ b/libcli/smb/smbXcli_base.c @@ -164,6 +164,7 @@ struct smbXcli_session { struct { DATA_BLOB signing_key; + uint8_t preauth_sha512[64]; } smb2_channel; /* @@ -5112,6 +5113,10 @@ struct smbXcli_session *smbXcli_session_create(TALLOC_CTX *mem_ctx, DLIST_ADD_END(conn->sessions, session, struct smbXcli_session *); session->conn = conn; + memcpy(session->smb2_channel.preauth_sha512, + conn->smb2.preauth_sha512, + sizeof(session->smb2_channel.preauth_sha512)); + return session; } @@ -5334,6 +5339,35 @@ void smb2cli_session_stop_replay(struct smbXcli_session *session) session->smb2->replay_active = false; } +NTSTATUS smb2cli_session_update_preauth(struct smbXcli_session *session, + const struct iovec *iov) +{ + struct hc_sha512state sctx; + size_t i; + + if (session->conn == NULL) { + return NT_STATUS_INTERNAL_ERROR; + } + + if (session->conn->protocol < PROTOCOL_SMB3_10) { + return NT_STATUS_OK; + } + + if (session->smb2_channel.signing_key.length != 0) { + return NT_STATUS_OK; + } + + SHA512_Init(&sctx); + SHA512_Update(&sctx, session->smb2_channel.preauth_sha512, + sizeof(session->smb2_channel.preauth_sha512)); + for (i = 0; i < 3; i++) { + SHA512_Update(&sctx, iov[i].iov_base, iov[i].iov_len); + } + SHA512_Final(session->smb2_channel.preauth_sha512, &sctx); + + return NT_STATUS_OK; +} + NTSTATUS smb2cli_session_set_session_key(struct smbXcli_session *session, const DATA_BLOB _session_key, const struct iovec *recv_iov) @@ -5344,6 +5378,16 @@ NTSTATUS smb2cli_session_set_session_key(struct smbXcli_session *session, bool check_signature = true; uint32_t hdr_flags; NTSTATUS status; + struct _derivation { + DATA_BLOB label; + DATA_BLOB context; + }; + struct { + struct _derivation signing; + struct _derivation encryption; + struct _derivation decryption; + struct _derivation application; + } derivation = { }; if (conn == NULL) { return NT_STATUS_INVALID_PARAMETER_MIX; @@ -5364,6 +5408,49 @@ NTSTATUS smb2cli_session_set_session_key(struct smbXcli_session *session, return NT_STATUS_INVALID_PARAMETER_MIX; } + if (conn->protocol >= PROTOCOL_SMB3_10) { + struct _derivation *d; + DATA_BLOB p; + + p = data_blob_const(session->smb2_channel.preauth_sha512, + sizeof(session->smb2_channel.preauth_sha512)); + + d = &derivation.signing; + d->label = data_blob_string_const_null("SMBSigningKey"); + d->context = p; + + d = &derivation.encryption; + d->label = data_blob_string_const_null("SMBC2SCipherKey"); + d->context = p; + + d = &derivation.decryption; + d->label = data_blob_string_const_null("SMBS2CCipherKey"); + d->context = p; + + d = &derivation.application; + d->label = data_blob_string_const_null("SMBAppKey"); + d->context = p; + + } else if (conn->protocol >= PROTOCOL_SMB2_24) { + struct _derivation *d; + + d = &derivation.signing; + d->label = data_blob_string_const_null("SMB2AESCMAC"); + d->context = data_blob_string_const_null("SmbSign"); + + d = &derivation.encryption; + d->label = data_blob_string_const_null("SMB2AESCCM"); + d->context = data_blob_string_const_null("ServerIn "); + + d = &derivation.decryption; + d->label = data_blob_string_const_null("SMB2AESCCM"); + d->context = data_blob_string_const_null("ServerOut"); + + d = &derivation.application; + d->label = data_blob_string_const_null("SMB2APP"); + d->context = data_blob_string_const_null("SmbRpc"); + } + ZERO_STRUCT(session_key); memcpy(session_key, _session_key.data, MIN(_session_key.length, sizeof(session_key))); @@ -5377,12 +5464,11 @@ NTSTATUS smb2cli_session_set_session_key(struct smbXcli_session *session, } if (conn->protocol >= PROTOCOL_SMB2_24) { - const DATA_BLOB label = data_blob_string_const_null("SMB2AESCMAC"); - const DATA_BLOB context = data_blob_string_const_null("SmbSign"); + struct _derivation *d = &derivation.signing; smb2_key_derivation(session_key, sizeof(session_key), - label.data, label.length, - context.data, context.length, + d->label.data, d->label.length, + d->context.data, d->context.length, session->smb2->signing_key.data); } @@ -5394,12 +5480,11 @@ NTSTATUS smb2cli_session_set_session_key(struct smbXcli_session *session, } if (conn->protocol >= PROTOCOL_SMB2_24) { - const DATA_BLOB label = data_blob_string_const_null("SMB2AESCCM"); - const DATA_BLOB context = data_blob_string_const_null("ServerIn "); + struct _derivation *d = &derivation.encryption; smb2_key_derivation(session_key, sizeof(session_key), - label.data, label.length, - context.data, context.length, + d->label.data, d->label.length, + d->context.data, d->context.length, session->smb2->encryption_key.data); } @@ -5411,12 +5496,11 @@ NTSTATUS smb2cli_session_set_session_key(struct smbXcli_session *session, } if (conn->protocol >= PROTOCOL_SMB2_24) { - const DATA_BLOB label = data_blob_string_const_null("SMB2AESCCM"); - const DATA_BLOB context = data_blob_string_const_null("ServerOut"); + struct _derivation *d = &derivation.decryption; smb2_key_derivation(session_key, sizeof(session_key), - label.data, label.length, - context.data, context.length, + d->label.data, d->label.length, + d->context.data, d->context.length, session->smb2->decryption_key.data); } @@ -5428,12 +5512,11 @@ NTSTATUS smb2cli_session_set_session_key(struct smbXcli_session *session, } if (conn->protocol >= PROTOCOL_SMB2_24) { - const DATA_BLOB label = data_blob_string_const_null("SMB2APP"); - const DATA_BLOB context = data_blob_string_const_null("SmbRpc"); + struct _derivation *d = &derivation.application; smb2_key_derivation(session_key, sizeof(session_key), - label.data, label.length, - context.data, context.length, + d->label.data, d->label.length, + d->context.data, d->context.length, session->smb2->application_key.data); } ZERO_STRUCT(session_key); @@ -5461,6 +5544,10 @@ NTSTATUS smb2cli_session_set_session_key(struct smbXcli_session *session, check_signature = true; } + if (conn->protocol >= PROTOCOL_SMB3_10) { + check_signature = true; + } + if (check_signature) { status = smb2_signing_check_pdu(session->smb2_channel.signing_key, session->conn->protocol, @@ -5529,6 +5616,10 @@ NTSTATUS smb2cli_session_create_channel(TALLOC_CTX *mem_ctx, DLIST_ADD_END(conn->sessions, session2, struct smbXcli_session *); session2->conn = conn; + memcpy(session2->smb2_channel.preauth_sha512, + conn->smb2.preauth_sha512, + sizeof(session2->smb2_channel.preauth_sha512)); + *_session2 = session2; return NT_STATUS_OK; } @@ -5540,6 +5631,13 @@ NTSTATUS smb2cli_session_set_channel_key(struct smbXcli_session *session, struct smbXcli_conn *conn = session->conn; uint8_t channel_key[16]; NTSTATUS status; + struct _derivation { + DATA_BLOB label; + DATA_BLOB context; + }; + struct { + struct _derivation signing; + } derivation = { }; if (conn == NULL) { return NT_STATUS_INVALID_PARAMETER_MIX; @@ -5549,6 +5647,24 @@ NTSTATUS smb2cli_session_set_channel_key(struct smbXcli_session *session, return NT_STATUS_INVALID_PARAMETER_MIX; } + if (conn->protocol >= PROTOCOL_SMB3_10) { + struct _derivation *d; + DATA_BLOB p; + + p = data_blob_const(session->smb2_channel.preauth_sha512, + sizeof(session->smb2_channel.preauth_sha512)); + + d = &derivation.signing; + d->label = data_blob_string_const_null("SMBSigningKey"); + d->context = p; + } else if (conn->protocol >= PROTOCOL_SMB2_24) { + struct _derivation *d; + + d = &derivation.signing; + d->label = data_blob_string_const_null("SMB2AESCMAC"); + d->context = data_blob_string_const_null("SmbSign"); + } + ZERO_STRUCT(channel_key); memcpy(channel_key, _channel_key.data, MIN(_channel_key.length, sizeof(channel_key))); @@ -5562,12 +5678,11 @@ NTSTATUS smb2cli_session_set_channel_key(struct smbXcli_session *session, } if (conn->protocol >= PROTOCOL_SMB2_24) { - const DATA_BLOB label = data_blob_string_const_null("SMB2AESCMAC"); - const DATA_BLOB context = data_blob_string_const_null("SmbSign"); + struct _derivation *d = &derivation.signing; smb2_key_derivation(channel_key, sizeof(channel_key), - label.data, label.length, - context.data, context.length, + d->label.data, d->label.length, + d->context.data, d->context.length, session->smb2_channel.signing_key.data); } ZERO_STRUCT(channel_key); diff --git a/libcli/smb/smbXcli_base.h b/libcli/smb/smbXcli_base.h index 4d00e269691..2e13b57ef8f 100644 --- a/libcli/smb/smbXcli_base.h +++ b/libcli/smb/smbXcli_base.h @@ -403,6 +403,8 @@ uint16_t smb2cli_session_reset_channel_sequence(struct smbXcli_session *session, uint16_t channel_sequence); void smb2cli_session_start_replay(struct smbXcli_session *session); void smb2cli_session_stop_replay(struct smbXcli_session *session); +NTSTATUS smb2cli_session_update_preauth(struct smbXcli_session *session, + const struct iovec *iov); NTSTATUS smb2cli_session_set_session_key(struct smbXcli_session *session, const DATA_BLOB session_key, const struct iovec *recv_iov);