diff options
Diffstat (limited to 'source3')
-rw-r--r-- | source3/include/smb.h | 5 | ||||
-rw-r--r-- | source3/smbd/pipes.c | 4 | ||||
-rw-r--r-- | source3/smbd/process.c | 153 | ||||
-rw-r--r-- | source3/smbd/proto.h | 1 |
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); |