summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libcli/smb/smbXcli_base.c149
1 files changed, 144 insertions, 5 deletions
diff --git a/libcli/smb/smbXcli_base.c b/libcli/smb/smbXcli_base.c
index 2cb5d449d9..e2ff017465 100644
--- a/libcli/smb/smbXcli_base.c
+++ b/libcli/smb/smbXcli_base.c
@@ -139,6 +139,7 @@ struct smbXcli_session {
struct smbXcli_req_state {
struct tevent_context *ev;
struct smbXcli_conn *conn;
+ struct smbXcli_session *session; /* maybe NULL */
uint8_t length_hdr[4];
@@ -188,6 +189,8 @@ struct smbXcli_req_state {
struct iovec *recv_iov;
uint16_t credit_charge;
+
+ bool signing_skipped;
} smb2;
};
@@ -2101,6 +2104,7 @@ NTSTATUS smb2cli_req_compound_submit(struct tevent_req **reqs,
nbt_len = 0;
for (i=0; i<num_reqs; i++) {
+ int hdr_iov;
size_t reqlen;
bool ret;
uint64_t avail;
@@ -2160,6 +2164,7 @@ NTSTATUS smb2cli_req_compound_submit(struct tevent_req **reqs,
SSVAL(state->smb2.hdr, SMB2_HDR_CREDIT, credits);
SBVAL(state->smb2.hdr, SMB2_HDR_MESSAGE_ID, mid);
+ hdr_iov = num_iov;
iov[num_iov].iov_base = state->smb2.hdr;
iov[num_iov].iov_len = sizeof(state->smb2.hdr);
num_iov += 1;
@@ -2190,16 +2195,22 @@ NTSTATUS smb2cli_req_compound_submit(struct tevent_req **reqs,
}
nbt_len += reqlen;
+ if (state->session && state->session->smb2.should_sign) {
+ NTSTATUS status;
+
+ status = smb2_signing_sign_pdu(state->session->smb2.signing_key,
+ &iov[hdr_iov], num_iov - hdr_iov);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
ret = smbXcli_req_set_pending(reqs[i]);
if (!ret) {
return NT_STATUS_NO_MEMORY;
}
}
- /*
- * TODO: Do signing here
- */
-
state = tevent_req_data(reqs[0], struct smbXcli_req_state);
_smb_setlen_tcp(state->length_hdr, nbt_len);
iov[0].iov_base = state->length_hdr;
@@ -2413,6 +2424,7 @@ static NTSTATUS smb2cli_conn_dispatch_incoming(struct smbXcli_conn *conn,
int i, num_iov;
NTSTATUS status;
bool defer = true;
+ struct smbXcli_session *last_session = NULL;
status = smb2cli_inbuf_parse_compound(inbuf, tmp_mem,
&iov, &num_iov);
@@ -2428,8 +2440,11 @@ static NTSTATUS smb2cli_conn_dispatch_incoming(struct smbXcli_conn *conn,
uint32_t flags = IVAL(inhdr, SMB2_HDR_FLAGS);
uint64_t mid = BVAL(inhdr, SMB2_HDR_MESSAGE_ID);
uint16_t req_opcode;
+ uint32_t req_flags;
uint16_t credits = SVAL(inhdr, SMB2_HDR_CREDIT);
uint32_t new_credits;
+ struct smbXcli_session *session = NULL;
+ const DATA_BLOB *signing_key = NULL;
new_credits = conn->smb2.cur_credits;
new_credits += credits;
@@ -2448,6 +2463,7 @@ static NTSTATUS smb2cli_conn_dispatch_incoming(struct smbXcli_conn *conn,
if (opcode != req_opcode) {
return NT_STATUS_INVALID_NETWORK_RESPONSE;
}
+ req_flags = SVAL(state->smb2.hdr, SMB2_HDR_FLAGS);
if (!(flags & SMB2_HDR_FLAG_REDIRECT)) {
return NT_STATUS_INVALID_NETWORK_RESPONSE;
@@ -2456,15 +2472,129 @@ static NTSTATUS smb2cli_conn_dispatch_incoming(struct smbXcli_conn *conn,
status = NT_STATUS(IVAL(inhdr, SMB2_HDR_STATUS));
if ((flags & SMB2_HDR_FLAG_ASYNC) &&
NT_STATUS_EQUAL(status, STATUS_PENDING)) {
- uint32_t req_flags = IVAL(state->smb2.hdr, SMB2_HDR_FLAGS);
uint64_t async_id = BVAL(inhdr, SMB2_HDR_ASYNC_ID);
+ /*
+ * async interim responses are not signed,
+ * even if the SMB2_HDR_FLAG_SIGNED flag
+ * is set.
+ */
req_flags |= SMB2_HDR_FLAG_ASYNC;
SBVAL(state->smb2.hdr, SMB2_HDR_FLAGS, req_flags);
SBVAL(state->smb2.hdr, SMB2_HDR_ASYNC_ID, async_id);
continue;
}
+ session = state->session;
+ if (req_flags & SMB2_HDR_FLAG_CHAINED) {
+ session = last_session;
+ }
+ last_session = session;
+
+ if (session && session->smb2.should_sign) {
+ if (!(flags & SMB2_HDR_FLAG_SIGNED)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ }
+
+ if (flags & SMB2_HDR_FLAG_SIGNED) {
+ uint64_t uid = BVAL(inhdr, SMB2_HDR_SESSION_ID);
+
+ if (session == NULL) {
+ struct smbXcli_session *s;
+
+ s = state->conn->sessions;
+ for (; s; s = s->next) {
+ if (s->smb2.session_id != uid) {
+ continue;
+ }
+
+ session = s;
+ break;
+ }
+ }
+
+ if (session == NULL) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ last_session = session;
+ signing_key = &session->smb2.signing_key;
+ }
+
+ if ((opcode == SMB2_OP_SESSSETUP) &&
+ NT_STATUS_IS_OK(status)) {
+ /*
+ * the caller has to check the signing
+ * as only the caller knows the correct
+ * session key
+ */
+ signing_key = NULL;
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_USER_SESSION_DELETED)) {
+ /*
+ * if the server returns NT_STATUS_USER_SESSION_DELETED
+ * the response is not signed and we should
+ * propagate the NT_STATUS_USER_SESSION_DELETED
+ * status to the caller.
+ */
+ if (signing_key) {
+ signing_key = NULL;
+ }
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_NAME_DELETED) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_FILE_CLOSED) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
+ /*
+ * if the server returns
+ * NT_STATUS_NETWORK_NAME_DELETED
+ * NT_STATUS_FILE_CLOSED
+ * NT_STATUS_INVALID_PARAMETER
+ * the response might not be signed
+ * as this happens before the signing checks.
+ *
+ * If server echos the signature (or all zeros)
+ * we should report the status from the server
+ * to the caller.
+ */
+ if (signing_key) {
+ int cmp;
+
+ cmp = memcmp(inhdr+SMB2_HDR_SIGNATURE,
+ state->smb2.hdr+SMB2_HDR_SIGNATURE,
+ 16);
+ if (cmp == 0) {
+ state->smb2.signing_skipped = true;
+ signing_key = NULL;
+ }
+ }
+ if (signing_key) {
+ int cmp;
+ static const uint8_t zeros[16];
+
+ cmp = memcmp(inhdr+SMB2_HDR_SIGNATURE,
+ zeros,
+ 16);
+ if (cmp == 0) {
+ state->smb2.signing_skipped = true;
+ signing_key = NULL;
+ }
+ }
+ }
+
+ if (signing_key) {
+ status = smb2_signing_check_pdu(*signing_key, cur, 3);
+ if (!NT_STATUS_IS_OK(status)) {
+ /*
+ * If the signing check fails, we disconnect
+ * the connection.
+ */
+ return status;
+ }
+ }
+
smbXcli_req_unset_pending(req);
/*
@@ -2565,6 +2695,15 @@ NTSTATUS smb2cli_req_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
return status;
}
+ if (state->smb2.signing_skipped) {
+ if (num_expected > 0) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ if (!NT_STATUS_IS_ERR(status)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ }
+
if (!found_size) {
return NT_STATUS_INVALID_NETWORK_RESPONSE;
}