DATA_BLOB application_key;
DATA_BLOB signing_key;
bool should_sign;
+ bool should_encrypt;
+ DATA_BLOB encryption_key;
+ DATA_BLOB decryption_key;
+ uint64_t channel_nonce;
+ uint64_t channel_next;
DATA_BLOB channel_signing_key;
} smb2;
};
const uint8_t *dyn;
uint32_t dyn_len;
- uint8_t hdr[64];
+ uint8_t hdr[SMB2_HDR_BODY];
uint8_t pad[7]; /* padding space for compounding */
- /* always an array of 3 talloc elements */
+ /*
+ * always an array of 3 talloc elements
+ * (without a SMB2_TRANSFORM header!)
+ *
+ * HDR, BODY, DYN
+ */
struct iovec *recv_iov;
uint16_t credit_charge;
+ bool should_sign;
+
bool signing_skipped;
bool notify_async;
bool got_async;
if (session) {
uid = session->smb2.session_id;
+
+ state->smb2.should_sign = session->smb2.should_sign;
+
+ if (cmd == SMB2_OP_SESSSETUP &&
+ session->smb2.signing_key.length != 0) {
+ state->smb2.should_sign = true;
+ }
}
state->smb2.recv_iov = talloc_zero_array(state, struct iovec, 3);
skip_credits:
if (state->session) {
- bool should_sign = state->session->smb2.should_sign;
-
- if (opcode == SMB2_OP_SESSSETUP &&
- state->session->smb2.signing_key.length != 0) {
- should_sign = true;
- }
-
/*
* We prefer the channel signing key if it is
* already there.
*/
- if (should_sign) {
+ if (state->smb2.should_sign) {
signing_key = &state->session->smb2.channel_signing_key;
}
}
while (taken < buflen) {
+ uint8_t *tf = NULL;
+ size_t tf_len = 0;
size_t len = buflen - taken;
uint8_t *hdr = first_hdr + taken;
struct iovec *cur;
uint16_t body_size;
struct iovec *iov_tmp;
+ if (len < 4) {
+ DEBUG(10, ("%d bytes left, expected at least %d\n",
+ (int)len, 4));
+ goto inval;
+ }
+ if (IVAL(hdr, 0) == SMB2_TF_MAGIC) {
+ struct smbXcli_session *s;
+ uint64_t uid;
+ struct iovec tf_iov[2];
+ NTSTATUS status;
+
+ if (len < SMB2_TF_HDR_SIZE) {
+ DEBUG(10, ("%d bytes left, expected at least %d\n",
+ (int)len, SMB2_TF_HDR_SIZE));
+ goto inval;
+ }
+ tf = hdr;
+ tf_len = SMB2_TF_HDR_SIZE;
+ taken += tf_len;
+
+ hdr = first_hdr + taken;
+ len = IVAL(tf, SMB2_TF_MSG_SIZE);
+ uid = BVAL(tf, SMB2_TF_SESSION_ID);
+
+ s = conn->sessions;
+ for (; s; s = s->next) {
+ if (s->smb2.session_id != uid) {
+ continue;
+ }
+ break;
+ }
+
+ if (s == NULL) {
+ DEBUG(10, ("unknown session_id %llu\n",
+ (unsigned long long)uid));
+ goto inval;
+ }
+
+ tf_iov[0].iov_base = (void *)tf;
+ tf_iov[0].iov_len = tf_len;
+ tf_iov[1].iov_base = (void *)hdr;
+ tf_iov[1].iov_len = len;
+
+ status = smb2_signing_decrypt_pdu(s->smb2.decryption_key,
+ conn->protocol,
+ tf_iov, 2);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(iov);
+ return status;
+ }
+ }
+
/*
* We need the header plus the body length field
*/
if (next_command_ofs > full_size) {
goto inval;
}
+ if (tf && next_command_ofs < len) {
+ goto inval;
+ }
full_size = next_command_ofs;
}
if (body_size < 2) {
}
iov_tmp = talloc_realloc(mem_ctx, iov, struct iovec,
- num_iov + 3);
+ num_iov + 4);
if (iov_tmp == NULL) {
TALLOC_FREE(iov);
return NT_STATUS_NO_MEMORY;
}
iov = iov_tmp;
cur = &iov[num_iov];
- num_iov += 3;
+ num_iov += 4;
- cur[0].iov_base = hdr;
- cur[0].iov_len = SMB2_HDR_BODY;
- cur[1].iov_base = hdr + SMB2_HDR_BODY;
- cur[1].iov_len = body_size;
- cur[2].iov_base = hdr + SMB2_HDR_BODY + body_size;
- cur[2].iov_len = full_size - (SMB2_HDR_BODY + body_size);
+ cur[0].iov_base = tf;
+ cur[0].iov_len = tf_len;
+ cur[1].iov_base = hdr;
+ cur[1].iov_len = SMB2_HDR_BODY;
+ cur[2].iov_base = hdr + SMB2_HDR_BODY;
+ cur[2].iov_len = body_size;
+ cur[3].iov_base = hdr + SMB2_HDR_BODY + body_size;
+ cur[3].iov_len = full_size - (SMB2_HDR_BODY + body_size);
taken += full_size;
}
return status;
}
- for (i=0; i<num_iov; i+=3) {
+ for (i=0; i<num_iov; i+=4) {
uint8_t *inbuf_ref = NULL;
struct iovec *cur = &iov[i];
- uint8_t *inhdr = (uint8_t *)cur[0].iov_base;
+ uint8_t *inhdr = (uint8_t *)cur[1].iov_base;
uint16_t opcode = SVAL(inhdr, SMB2_HDR_OPCODE);
uint32_t flags = IVAL(inhdr, SMB2_HDR_FLAGS);
uint64_t mid = BVAL(inhdr, SMB2_HDR_MESSAGE_ID);
uint32_t new_credits;
struct smbXcli_session *session = NULL;
const DATA_BLOB *signing_key = NULL;
- bool should_sign = false;
new_credits = conn->smb2.cur_credits;
new_credits += credits;
}
last_session = session;
- if (session) {
- should_sign = session->smb2.should_sign;
- if (opcode == SMB2_OP_SESSSETUP &&
- session->smb2.signing_key.length != 0) {
- should_sign = true;
- }
- }
-
- if (should_sign) {
+ if (state->smb2.should_sign) {
if (!(flags & SMB2_HDR_FLAG_SIGNED)) {
return NT_STATUS_ACCESS_DENIED;
}
if (signing_key) {
status = smb2_signing_check_pdu(*signing_key,
state->conn->protocol,
- cur, 3);
+ &cur[1], 3);
if (!NT_STATUS_IS_OK(status)) {
/*
* If the signing check fails, we disconnect
* There might be more than one response
* we need to defer the notifications
*/
- if ((num_iov == 4) && (talloc_array_length(conn->pending) == 0)) {
+ if ((num_iov == 5) && (talloc_array_length(conn->pending) == 0)) {
defer = false;
}
}
/* copy the related buffers */
- state->smb2.recv_iov[0] = cur[0];
- state->smb2.recv_iov[1] = cur[1];
- state->smb2.recv_iov[2] = cur[2];
+ state->smb2.recv_iov[0] = cur[1];
+ state->smb2.recv_iov[1] = cur[2];
+ state->smb2.recv_iov[2] = cur[3];
tevent_req_done(req);
}
session->smb2.signing_key.data);
}
+ session->smb2.encryption_key = data_blob_dup_talloc(session,
+ session->smb2.signing_key);
+ if (session->smb2.encryption_key.data == NULL) {
+ ZERO_STRUCT(session_key);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ 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 ");
+
+ smb2_key_derivation(session_key, sizeof(session_key),
+ label.data, label.length,
+ context.data, context.length,
+ session->smb2.encryption_key.data);
+ }
+
+ session->smb2.decryption_key = data_blob_dup_talloc(session,
+ session->smb2.signing_key);
+ if (session->smb2.decryption_key.data == NULL) {
+ ZERO_STRUCT(session_key);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ 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");
+
+ smb2_key_derivation(session_key, sizeof(session_key),
+ label.data, label.length,
+ context.data, context.length,
+ session->smb2.decryption_key.data);
+ }
+
session->smb2.application_key = data_blob_dup_talloc(session,
session->smb2.signing_key);
if (session->smb2.application_key.data == NULL) {
}
session->smb2.should_sign = false;
+ session->smb2.should_encrypt = false;
if (conn->desire_signing) {
session->smb2.should_sign = true;
session->smb2.should_sign = true;
}
+ generate_random_buffer((uint8_t *)&session->smb2.channel_nonce,
+ sizeof(session->smb2.channel_nonce));
+ session->smb2.channel_next = 1;
+
return NT_STATUS_OK;
}
return NT_STATUS_INVALID_PARAMETER_MIX;
}
+ if (session1->smb2.channel_next == 0) {
+ return NT_STATUS_INVALID_PARAMETER_MIX;
+ }
+
if (conn == NULL) {
return NT_STATUS_INVALID_PARAMETER_MIX;
}
}
session2->smb2.should_sign = session1->smb2.should_sign;
+ session2->smb2.should_encrypt = session1->smb2.should_encrypt;
+
+ session2->smb2.channel_nonce = session1->smb2.channel_nonce;
+ session2->smb2.channel_nonce += session1->smb2.channel_next;
+ session1->smb2.channel_next++;
+
+ session2->smb2.encryption_key = data_blob_dup_talloc(session2,
+ session1->smb2.encryption_key);
+ if (session2->smb2.encryption_key.data == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ session2->smb2.decryption_key = data_blob_dup_talloc(session2,
+ session1->smb2.decryption_key);
+ if (session2->smb2.decryption_key.data == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
talloc_set_destructor(session2, smbXcli_session_destructor);
DLIST_ADD_END(conn->sessions, session2, struct smbXcli_session *);