From bcf2e1694ec85c750cc48cf33f3f283a9cb17162 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Mon, 18 Mar 2013 12:00:25 -0700 Subject: [PATCH 01/10] Ensure we don't do an SMB2 aio write if RECVFILE is active. Signed-off-by: Jeremy Allison --- source3/smbd/aio.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/source3/smbd/aio.c b/source3/smbd/aio.c index 3f553eb..e8be408 100644 --- a/source3/smbd/aio.c +++ b/source3/smbd/aio.c @@ -861,6 +861,11 @@ NTSTATUS schedule_aio_smb2_write(connection_struct *conn, return NT_STATUS_RETRY; } + if (smbreq->unread_bytes) { + /* Can't do async with recvfile. */ + return NT_STATUS_RETRY; + } + if (!(aio_ex = create_aio_extra(smbreq->smb2req, fsp, 0))) { return NT_STATUS_NO_MEMORY; } -- 1.8.1.3 From cd73967baa6d8f71687adb9a0d08289861d1d422 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Tue, 19 Mar 2013 12:16:32 -0700 Subject: [PATCH 02/10] If we already have an smb1req attached to the struct smbd_smb2_request, don't recreate it. Signed-off-by: Jeremy Allison --- source3/smbd/smb2_glue.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/source3/smbd/smb2_glue.c b/source3/smbd/smb2_glue.c index 1b2b4dd..9fc1e49 100644 --- a/source3/smbd/smb2_glue.c +++ b/source3/smbd/smb2_glue.c @@ -28,9 +28,13 @@ struct smb_request *smbd_smb2_fake_smb_request(struct smbd_smb2_request *req) struct smb_request *smbreq; const uint8_t *inhdr = SMBD_SMB2_IN_HDR_PTR(req); - smbreq = talloc_zero(req, struct smb_request); - if (smbreq == NULL) { - return NULL; + if (req->smb1req) { + smbreq = req->smb1req; + } else { + smbreq = talloc_zero(req, struct smb_request); + if (smbreq == NULL) { + return NULL; + } } smbreq->request_time = req->request_time; -- 1.8.1.3 From d9c96c3a4a7ff913a4d529c0dfb7c201781fe825 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Tue, 19 Mar 2013 12:24:17 -0700 Subject: [PATCH 03/10] Add function smbd_smb2_unread_bytes(). Returns number of bytes left to read for recvfile. Will be used in SMB_2_WRITE_FILE code path. Signed-off-by: Jeremy Allison --- source3/smbd/globals.h | 1 + source3/smbd/smb2_glue.c | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/source3/smbd/globals.h b/source3/smbd/globals.h index 3461bc5..a0a04bb 100644 --- a/source3/smbd/globals.h +++ b/source3/smbd/globals.h @@ -247,6 +247,7 @@ NTSTATUS smbd_smb2_request_pending_queue(struct smbd_smb2_request *req, uint32_t defer_time); struct smb_request *smbd_smb2_fake_smb_request(struct smbd_smb2_request *req); +size_t smbd_smb2_unread_bytes(struct smbd_smb2_request *req); void remove_smb2_chained_fsp(files_struct *fsp); NTSTATUS smbd_smb2_request_verify_creditcharge(struct smbd_smb2_request *req, diff --git a/source3/smbd/smb2_glue.c b/source3/smbd/smb2_glue.c index 9fc1e49..54135b5 100644 --- a/source3/smbd/smb2_glue.c +++ b/source3/smbd/smb2_glue.c @@ -59,6 +59,18 @@ struct smb_request *smbd_smb2_fake_smb_request(struct smbd_smb2_request *req) } /********************************************************* + Are there unread bytes for recvfile ? +*********************************************************/ + +size_t smbd_smb2_unread_bytes(struct smbd_smb2_request *req) +{ + if (req->smb1req) { + return req->smb1req->unread_bytes; + } + return 0; +} + +/********************************************************* Called from file_free() to remove any chained fsp pointers. *********************************************************/ -- 1.8.1.3 From 63f41a70fa26664381f9333e85af30b3115a1eaa Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Tue, 19 Mar 2013 12:36:52 -0700 Subject: [PATCH 04/10] Allow smbd_smb2_request_error_ex() to cope with unread bytes on error. Drain the socket if a RECVFILE write failed. Signed-off-by: Jeremy Allison --- source3/smbd/smb2_server.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c index eb7059e..7eb62fc 100644 --- a/source3/smbd/smb2_server.c +++ b/source3/smbd/smb2_server.c @@ -2621,11 +2621,21 @@ NTSTATUS smbd_smb2_request_error_ex(struct smbd_smb2_request *req, { DATA_BLOB body; uint8_t *outhdr = SMBD_SMB2_OUT_HDR_PTR(req); + size_t unread_bytes = smbd_smb2_unread_bytes(req); DEBUG(10,("smbd_smb2_request_error_ex: idx[%d] status[%s] |%s| at %s\n", req->current_idx, nt_errstr(status), info ? " +info" : "", location)); + if (unread_bytes) { + /* Recvfile error. Drain incoming socket. */ + size_t ret = drain_socket(req->sconn->sock, unread_bytes); + if (ret != unread_bytes) { + smbd_server_connection_terminate(req->sconn, + "Failed to drain SMB2 socket\n"); + } + } + body.data = outhdr + SMB2_HDR_BODY; body.length = 8; SSVAL(body.data, 0, 9); -- 1.8.1.3 From ca2fe9d6591fabf641ea75e1af17b2da227e0467 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Mon, 1 Apr 2013 13:12:55 -0700 Subject: [PATCH 05/10] Add utility function get_min_receive_file_size(). Signed-off-by: Jeremy Allison --- source3/smbd/smb2_server.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c index 7eb62fc..1c622a8 100644 --- a/source3/smbd/smb2_server.c +++ b/source3/smbd/smb2_server.c @@ -2842,6 +2842,17 @@ static int smbd_smb2_request_next_vector(struct tstream_context *stream, size_t *_count); static void smbd_smb2_request_read_done(struct tevent_req *subreq); +static size_t get_min_receive_file_size(struct smbd_smb2_request *smb2_req) +{ + if (smb2_req->do_signing) { + return 0; + } + if (smb2_req->do_encryption) { + return 0; + } + return (size_t)lp_min_receive_file_size(); +} + static struct tevent_req *smbd_smb2_request_read_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct smbd_server_connection *sconn) -- 1.8.1.3 From 22d3924a279dbbda9f7ffc8d6298d9fee5404038 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Mon, 1 Apr 2013 13:14:13 -0700 Subject: [PATCH 06/10] Add macro SMBD_SMB2_SHORT_RECEIVEFILE_WRITE_LEN. This is the 'short' length we'll read in the SMB2_WRITE receivefile code path. Signed-off-by: Jeremy Allison --- source3/smbd/globals.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source3/smbd/globals.h b/source3/smbd/globals.h index a0a04bb..f075a6d 100644 --- a/source3/smbd/globals.h +++ b/source3/smbd/globals.h @@ -546,6 +546,8 @@ struct smbd_smb2_request { #define SMBD_SMB2_OUT_DYN_PTR(req) (uint8_t *)(SMBD_SMB2_OUT_DYN_IOV(req)->iov_base) #define SMBD_SMB2_OUT_DYN_LEN(req) (SMBD_SMB2_OUT_DYN_IOV(req)->iov_len) +#define SMBD_SMB2_SHORT_RECEIVEFILE_WRITE_LEN (SMB2_HDR_BODY + 0x30) + struct { /* * vector[0] TRANSPORT HEADER (empty) -- 1.8.1.3 From e35cfa8a336f66c928c5b33757b8a1ff9b4f4bcf Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Mon, 1 Apr 2013 13:17:09 -0700 Subject: [PATCH 07/10] Add extra fields into struct smbd_smb2_request_read_state to support receivefile. Initialize min_recv_size with the size that will trigger the receivefile write path. Signed-off-by: Jeremy Allison --- source3/smbd/smb2_server.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c index 1c622a8..e1671a7 100644 --- a/source3/smbd/smb2_server.c +++ b/source3/smbd/smb2_server.c @@ -2831,6 +2831,8 @@ struct smbd_smb2_request_read_state { uint8_t nbt[NBT_HDR_SIZE]; bool done; } hdr; + bool doing_receivefile; + size_t min_recv_size; size_t pktlen; uint8_t *pktbuf; }; @@ -2874,6 +2876,7 @@ static struct tevent_req *smbd_smb2_request_read_send(TALLOC_CTX *mem_ctx, return tevent_req_post(req, ev); } state->smb2_req->sconn = sconn; + state->min_recv_size = get_min_receive_file_size(state->smb2_req); subreq = tstream_readv_pdu_queue_send(state->smb2_req, state->ev, -- 1.8.1.3 From 47c53ca35fbf73bde4e8ec333274159a589044a6 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Mon, 1 Apr 2013 13:19:01 -0700 Subject: [PATCH 08/10] Add stub static function that will turn on/off receivefile code path. Signed-off-by: Jeremy Allison --- source3/smbd/smb2_server.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c index e1671a7..a983dcd 100644 --- a/source3/smbd/smb2_server.c +++ b/source3/smbd/smb2_server.c @@ -2892,6 +2892,11 @@ static struct tevent_req *smbd_smb2_request_read_send(TALLOC_CTX *mem_ctx, return req; } +static bool is_smb2_recvfile_write(struct smbd_smb2_request_read_state *state) +{ + return false; +} + static int smbd_smb2_request_next_vector(struct tstream_context *stream, void *private_data, TALLOC_CTX *mem_ctx, -- 1.8.1.3 From 1f8a516bb625f2ea71aacc519982faa2c6e43052 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Mon, 1 Apr 2013 13:24:07 -0700 Subject: [PATCH 09/10] The guts of the receivefile code changes. If an incoming PDU might qualify, only read SMBD_SMB2_SHORT_RECEIVEFILE_WRITE_LEN = (SMB2_HEADER + SMB2_WRITE_BODY_LEN) bytes rather than the whole PDU. Next time we're called, use is_smb2_recvfile_write() to decide if this is an SMB2_WRITE that fit the receivefile criteria, otherwise just read the rest of the PDU. If we did do a short receivefile read, set up the smb2_req->smb1req->unread_bytes value to show what bytes remain in the TCP buffers. Signed-off-by: Jeremy Allison --- source3/smbd/smb2_server.c | 64 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 59 insertions(+), 5 deletions(-) diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c index a983dcd..4fc2bc7 100644 --- a/source3/smbd/smb2_server.c +++ b/source3/smbd/smb2_server.c @@ -2906,12 +2906,37 @@ static int smbd_smb2_request_next_vector(struct tstream_context *stream, struct smbd_smb2_request_read_state *state = talloc_get_type_abort(private_data, struct smbd_smb2_request_read_state); - struct iovec *vector; + struct iovec *vector = NULL; + size_t min_recvfile_size = UINT32_MAX; if (state->pktlen > 0) { - /* if there're no remaining bytes, we're done */ - *_vector = NULL; - *_count = 0; + if (state->doing_receivefile && !is_smb2_recvfile_write(state)) { + /* + * Not a possible receivefile write. + * Read the rest of the data. + */ + state->doing_receivefile = false; + vector = talloc_array(mem_ctx, struct iovec, 1); + if (vector == NULL) { + return -1; + } + vector[0].iov_base = (void *)(state->pktbuf + + SMBD_SMB2_SHORT_RECEIVEFILE_WRITE_LEN); + vector[0].iov_len = (state->pktlen - + SMBD_SMB2_SHORT_RECEIVEFILE_WRITE_LEN); + *_vector = vector; + *_count = 1; + } else { + /* + * Either this is a receivefile write so we've + * done a short read, or if not we have all the data. + * Either way, we're done and the caller will handle + * and short read case by looking at the + * state->doing_receivefile value. + */ + *_vector = NULL; + *_count = 0; + } return 0; } @@ -2957,7 +2982,26 @@ static int smbd_smb2_request_next_vector(struct tstream_context *stream, } vector[0].iov_base = (void *)state->pktbuf; - vector[0].iov_len = state->pktlen; + + if (state->min_recv_size != 0) { + min_recvfile_size = SMBD_SMB2_SHORT_RECEIVEFILE_WRITE_LEN; + min_recvfile_size += state->min_recv_size; + } + + if (state->pktlen > min_recvfile_size) { + /* + * Might be a receivefile write. Read the SMB2 HEADER + + * SMB2_WRITE header first. Set 'doing_receivefile' + * as we're *attempting* receivefile write. If this + * turns out not to be a SMB2_WRITE request or otherwise + * not suitable then we'll just read the rest of the data + * the next time this function is called. + */ + vector[0].iov_len = SMBD_SMB2_SHORT_RECEIVEFILE_WRITE_LEN; + state->doing_receivefile = true; + } else { + vector[0].iov_len = state->pktlen; + } *_vector = vector; *_count = 1; @@ -3020,6 +3064,16 @@ static void smbd_smb2_request_read_done(struct tevent_req *subreq) return; } + if (state->doing_receivefile) { + state->smb2_req->smb1req = talloc_zero(state->smb2_req, + struct smb_request); + if (tevent_req_nomem(state->smb2_req->smb1req, req)) { + return; + } + state->smb2_req->smb1req->unread_bytes = + state->pktlen - SMBD_SMB2_SHORT_RECEIVEFILE_WRITE_LEN; + } + state->smb2_req->current_idx = 1; tevent_req_done(req); -- 1.8.1.3 From 2023019409a2a3ade628b0bde9d5419b335d885f Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Mon, 1 Apr 2013 11:16:01 -0700 Subject: [PATCH 10/10] Add the internals of is_smb2_recvfile_write. This turns on the real receivefile detection, and completes the receivefile code path changes. Signed-off-by: Jeremy Allison --- source3/smbd/smb2_server.c | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c index 4fc2bc7..f1fcb7e 100644 --- a/source3/smbd/smb2_server.c +++ b/source3/smbd/smb2_server.c @@ -2894,7 +2894,43 @@ static struct tevent_req *smbd_smb2_request_read_send(TALLOC_CTX *mem_ctx, static bool is_smb2_recvfile_write(struct smbd_smb2_request_read_state *state) { - return false; + uint32_t flags; + + if (IVAL(state->pktbuf, 0) == SMB2_TF_MAGIC) { + /* Transform header. Cannot recvfile. */ + return false; + } + if (IVAL(state->pktbuf, 0) != SMB2_MAGIC) { + /* Not SMB2. Normal error path will cope. */ + return false; + } + if (SVAL(state->pktbuf, 4) != SMB2_HDR_BODY) { + /* Not SMB2. Normal error path will cope. */ + return false; + } + if (SVAL(state->pktbuf, SMB2_HDR_OPCODE) != SMB2_OP_WRITE) { + /* Needs to be a WRITE. */ + return false; + } + if (IVAL(state->pktbuf, SMB2_HDR_NEXT_COMMAND) != 0) { + /* Chained. Cannot recvfile. */ + return false; + } + flags = IVAL(state->pktbuf, SMB2_HDR_FLAGS); + if (flags & SMB2_HDR_FLAG_CHAINED) { + /* Chained. Cannot recvfile. */ + return false; + } + if (flags & SMB2_HDR_FLAG_SIGNED) { + /* Signed. Cannot recvfile. */ + return false; + } + + DEBUG(10,("Doing recvfile write len = %u\n", + (unsigned int)(state->pktlen - + SMBD_SMB2_SHORT_RECEIVEFILE_WRITE_LEN))); + + return true; } static int smbd_smb2_request_next_vector(struct tstream_context *stream, -- 1.8.1.3