summaryrefslogtreecommitdiff
path: root/source3
diff options
context:
space:
mode:
Diffstat (limited to 'source3')
-rw-r--r--source3/include/smb.h5
-rw-r--r--source3/smbd/pipes.c4
-rw-r--r--source3/smbd/process.c153
-rw-r--r--source3/smbd/proto.h1
4 files changed, 103 insertions, 60 deletions
diff --git a/source3/include/smb.h b/source3/include/smb.h
index 8e9dd5162a..382926c07e 100644
--- a/source3/include/smb.h
+++ b/source3/include/smb.h
@@ -495,6 +495,11 @@ struct smb_request {
* under privilege.
*/
struct privilege_paths *priv_paths;
+
+ /*
+ * Request list for chained requests, we're part of it.
+ */
+ struct smb_request **chain;
};
/* Defines for the sent_oplock_break field above. */
diff --git a/source3/smbd/pipes.c b/source3/smbd/pipes.c
index c21b635bfb..c3a5cb3c03 100644
--- a/source3/smbd/pipes.c
+++ b/source3/smbd/pipes.c
@@ -367,7 +367,7 @@ static void pipe_write_andx_done(struct tevent_req *subreq)
* We must free here as the ownership of req was
* moved to the connection struct in reply_pipe_write_and_X().
*/
- TALLOC_FREE(req);
+ smb_request_done(req);
}
/****************************************************************************
@@ -493,5 +493,5 @@ static void pipe_read_andx_done(struct tevent_req *subreq)
* We must free here as the ownership of req was
* moved to the connection struct in reply_pipe_read_and_X().
*/
- TALLOC_FREE(req);
+ smb_request_done(req);
}
diff --git a/source3/smbd/process.c b/source3/smbd/process.c
index 90d3166993..8b15ac88f9 100644
--- a/source3/smbd/process.c
+++ b/source3/smbd/process.c
@@ -540,6 +540,7 @@ static bool init_smb_request(struct smb_request *req,
req->chain_fsp = NULL;
req->smb2req = NULL;
req->priv_paths = NULL;
+ req->chain = NULL;
smb_init_perfcount_data(&req->pcd);
/* Ensure we have at least wct words and 2 bytes of bcc. */
@@ -1562,79 +1563,110 @@ static void construct_reply_chain(struct smbd_server_connection *sconn,
bool encrypted,
struct smb_perfcount_data *deferred_pcd)
{
- struct connection_struct *conn = NULL;
struct smb_request **reqs = NULL;
- struct smb_request *req, *first_req, *last_req;
- unsigned i, num_reqs;
- NTSTATUS status;
+ struct smb_request *req;
+ unsigned num_reqs;
bool ok;
ok = smb1_parse_chain(talloc_tos(), (uint8_t *)inbuf, sconn, encrypted,
seqnum, &reqs, &num_reqs);
if (!ok) {
- status = NT_STATUS_INVALID_PARAMETER;
- goto error;
+ char errbuf[smb_size];
+ error_packet(errbuf, 0, 0, NT_STATUS_INVALID_PARAMETER,
+ __LINE__, __FILE__);
+ if (!srv_send_smb(sconn, errbuf, true, seqnum, encrypted,
+ NULL)) {
+ exit_server_cleanly("construct_reply_chain: "
+ "srv_send_smb failed.");
+ }
+ return;
}
- for (i=0; i<num_reqs; i++) {
- req = reqs[i];
+ req = reqs[0];
+ req->inbuf = (uint8_t *)talloc_move(reqs, &inbuf);
- if (i > 0) {
- /*
- * Get the session and tree ids and chain fsp
- * from the previous request into the current
- * one
- */
- struct smb_request *prev_req = reqs[i-1];
- const uint8_t *prev_outbuf = prev_req->outbuf;
+ req->conn = switch_message(req->cmd, req);
- req->vuid = SVAL(prev_outbuf, smb_uid);
- req->tid = SVAL(prev_outbuf, smb_tid);
- req->conn = conn_find(req->sconn, req->tid);
- req->chain_fsp = prev_req->chain_fsp;
- }
- req->inbuf = (uint8_t *)inbuf;
- conn = switch_message(req->cmd, req);
+ if (req->outbuf == NULL) {
+ /*
+ * Request has suspended itself, will come
+ * back here.
+ */
+ return;
+ }
+ smb_request_done(req);
+}
- if (req->outbuf == NULL) {
- if (open_was_deferred(req->sconn, req->mid)) {
- TALLOC_FREE(reqs);
- return;
- }
- status = NT_STATUS_INTERNAL_ERROR;
- goto error;
- }
- if (IVAL(req->outbuf, smb_rcls) != 0) {
- break;
- }
+/*
+ * To be called from an async SMB handler that is potentially chained
+ * when it is finished for shipping.
+ */
+
+void smb_request_done(struct smb_request *req)
+{
+ struct smb_request **reqs = NULL;
+ struct smb_request *first_req;
+ size_t i, num_reqs, next_index;
+ NTSTATUS status;
+
+ if (req->chain == NULL) {
+ first_req = req;
+ goto shipit;
}
- first_req = reqs[0];
- last_req = req;
+ reqs = req->chain;
+ num_reqs = talloc_array_length(reqs);
- if (i == 0) {
+ for (i=0; i<num_reqs; i++) {
+ if (reqs[i] == req) {
+ break;
+ }
+ }
+ if (i == num_reqs) {
/*
- * The first request already gave an error, no need to
- * do any splicing
+ * Invalid chain, should not happen
*/
- goto shipit;
+ status = NT_STATUS_INTERNAL_ERROR;
+ goto error;
+ }
+ next_index = i+1;
+
+ while ((next_index < num_reqs) && (IVAL(req->outbuf, smb_rcls) == 0)) {
+ struct smb_request *next = reqs[next_index];
+
+ next->vuid = SVAL(req->outbuf, smb_uid);
+ next->tid = SVAL(req->outbuf, smb_tid);
+ next->conn = conn_find(req->sconn, req->tid);
+ next->chain_fsp = req->chain_fsp;
+ next->inbuf = (uint8_t *)req->inbuf;
+
+ req = next;
+ req->conn = switch_message(req->cmd, req);
+
+ if (req->outbuf == NULL) {
+ /*
+ * Request has suspended itself, will come
+ * back here.
+ */
+ return;
+ }
+ next_index += 1;
}
+ first_req = reqs[0];
+
for (i=1; i<next_index; i++) {
- req = reqs[i];
+ bool ok;
- ok = smb_splice_chain(&first_req->outbuf, req->outbuf);
+ ok = smb_splice_chain(&first_req->outbuf, reqs[i]->outbuf);
if (!ok) {
status = NT_STATUS_INTERNAL_ERROR;
goto error;
}
- if (req == last_req) {
- break;
- }
}
- SSVAL(first_req->outbuf, smb_uid, SVAL(last_req->outbuf, smb_uid));
- SSVAL(first_req->outbuf, smb_tid, SVAL(last_req->outbuf, smb_tid));
+ SSVAL(first_req->outbuf, smb_uid, SVAL(req->outbuf, smb_uid));
+ SSVAL(first_req->outbuf, smb_tid, SVAL(req->outbuf, smb_tid));
/*
* This scary statement intends to set the
@@ -1643,42 +1675,43 @@ static void construct_reply_chain(struct smbd_server_connection *sconn,
*/
SSVAL(first_req->outbuf, smb_flg2,
(SVAL(first_req->outbuf, smb_flg2) & ~FLAGS2_32_BIT_ERROR_CODES)
- |(SVAL(last_req->outbuf, smb_flg2) & FLAGS2_32_BIT_ERROR_CODES));
+ |(SVAL(req->outbuf, smb_flg2) & FLAGS2_32_BIT_ERROR_CODES));
/*
* Transfer the error codes from the subrequest to the main one
*/
- SSVAL(first_req->outbuf, smb_rcls, SVAL(last_req->outbuf, smb_rcls));
- SSVAL(first_req->outbuf, smb_err, SVAL(last_req->outbuf, smb_err));
+ SSVAL(first_req->outbuf, smb_rcls, SVAL(req->outbuf, smb_rcls));
+ SSVAL(first_req->outbuf, smb_err, SVAL(req->outbuf, smb_err));
- goto shipit;
+ _smb_setlen_large(
+ first_req->outbuf, talloc_get_size(first_req->outbuf) - 4);
shipit:
- _smb_setlen_large(first_req->outbuf,
- talloc_get_size(first_req->outbuf) - 4);
-
if (!srv_send_smb(first_req->sconn,
(char *)first_req->outbuf,
true, first_req->seqnum+1,
- IS_CONN_ENCRYPTED(conn)||first_req->encrypted,
+ IS_CONN_ENCRYPTED(req->conn)||first_req->encrypted,
&first_req->pcd)) {
exit_server_cleanly("construct_reply_chain: srv_send_smb "
"failed.");
}
- TALLOC_FREE(reqs);
+ TALLOC_FREE(req); /* non-chained case */
+ TALLOC_FREE(reqs); /* chained case */
return;
error:
{
char errbuf[smb_size];
error_packet(errbuf, 0, 0, status, __LINE__, __FILE__);
- if (!srv_send_smb(sconn, errbuf, true, seqnum, encrypted,
+ if (!srv_send_smb(req->sconn, errbuf, true,
+ req->seqnum+1, req->encrypted,
NULL)) {
exit_server_cleanly("construct_reply_chain: "
"srv_send_smb failed.");
}
}
- TALLOC_FREE(reqs);
+ TALLOC_FREE(req); /* non-chained case */
+ TALLOC_FREE(reqs); /* chained case */
}
/****************************************************************************
@@ -2220,6 +2253,7 @@ bool smb1_parse_chain(TALLOC_CTX *mem_ctx, const uint8_t *buf,
struct smb_request ***reqs, unsigned *num_reqs)
{
struct smb1_parse_chain_state state;
+ unsigned i;
state.mem_ctx = mem_ctx;
state.buf = buf;
@@ -2233,6 +2267,9 @@ bool smb1_parse_chain(TALLOC_CTX *mem_ctx, const uint8_t *buf,
TALLOC_FREE(state.reqs);
return false;
}
+ for (i=0; i<state.num_reqs; i++) {
+ state.reqs[i]->chain = state.reqs;
+ }
*reqs = state.reqs;
*num_reqs = state.num_reqs;
return true;
diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h
index a467202ff4..f9eabce0a3 100644
--- a/source3/smbd/proto.h
+++ b/source3/smbd/proto.h
@@ -780,6 +780,7 @@ bool push_deferred_open_message_smb(struct smb_request *req,
size_t priv_len);
NTSTATUS allow_new_trans(struct trans_state *list, uint64_t mid);
void reply_outbuf(struct smb_request *req, uint8 num_words, uint32 num_bytes);
+void smb_request_done(struct smb_request *req);
const char *smb_fn_name(int type);
void add_to_common_flags2(uint32 v);
void remove_from_common_flags2(uint32 v);