From 21d8547cca56ab5219349a64136879ab8c729f60 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Tue, 20 Nov 2012 20:58:24 -0800 Subject: [PATCH 1/9] Ensure we don't do an SMB2 aio write if RECVFILE is active. Part of bug #9412 - SMB2 server doesn't support recvfile. 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 b0b90c0..2d64c55 100644 --- a/source3/smbd/aio.c +++ b/source3/smbd/aio.c @@ -502,6 +502,11 @@ NTSTATUS schedule_aio_smb2_write(connection_struct *conn, size_t min_aio_write_size = lp_aio_write_size(SNUM(conn)); int ret; + if (smbreq->unread_bytes) { + /* Can't do async with recvfile. */ + return NT_STATUS_RETRY; + } + /* Ensure aio is initialized. */ if (!initialize_async_io_handler()) { return NT_STATUS_RETRY; -- 1.7.10.4 From b7d502d97e0b14c884e87e5b90b0b283bfbe86d7 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Tue, 20 Nov 2012 21:02:37 -0800 Subject: [PATCH 2/9] If we already have an smb1req attached to the struct smbd_smb2_request, don't recreate it. Part of bug #9412 - SMB2 server doesn't support recvfile. 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 360a73a..311e575 100644 --- a/source3/smbd/smb2_glue.c +++ b/source3/smbd/smb2_glue.c @@ -31,9 +31,13 @@ struct smb_request *smbd_smb2_fake_smb_request(struct smbd_smb2_request *req) inhdr = (const uint8_t *)req->in.vector[i+0].iov_base; - 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->vuid = req->session->compat_vuser->vuid; -- 1.7.10.4 From ac057320a2c6bdd4e6b37db4e72d30a0644fb83e Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Tue, 20 Nov 2012 21:12:05 -0800 Subject: [PATCH 3/9] 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. Part of bug #9412 - SMB2 server doesn't support recvfile. 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 f515f19..24dcd3d 100644 --- a/source3/smbd/globals.h +++ b/source3/smbd/globals.h @@ -276,6 +276,7 @@ NTSTATUS smbd_smb2_request_check_session(struct smbd_smb2_request *req); NTSTATUS smbd_smb2_request_check_tcon(struct smbd_smb2_request *req); 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_sizes(struct smbd_smb2_request *req, diff --git a/source3/smbd/smb2_glue.c b/source3/smbd/smb2_glue.c index 311e575..e64949d 100644 --- a/source3/smbd/smb2_glue.c +++ b/source3/smbd/smb2_glue.c @@ -61,6 +61,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 && req->smb1req->unread_bytes) { + return req->smb1req->unread_bytes; + } + return 0; +} + +/********************************************************* Called from file_free() to remove any chained fsp pointers. *********************************************************/ -- 1.7.10.4 From 204522ab2e73208821db47a86eec9f39f431e50e Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Tue, 20 Nov 2012 21:21:20 -0800 Subject: [PATCH 4/9] Allow smbd_smb2_request_process_write() to cope with RECVFILE. Cope with unread_bytes in the socket buffer. Part of bug #9412 - SMB2 server doesn't support recvfile. Signed-off-by: Jeremy Allison --- source3/smbd/smb2_write.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/source3/smbd/smb2_write.c b/source3/smbd/smb2_write.c index 5aeed8c7..6fb0a14 100644 --- a/source3/smbd/smb2_write.c +++ b/source3/smbd/smb2_write.c @@ -53,6 +53,7 @@ NTSTATUS smbd_smb2_request_process_write(struct smbd_smb2_request *req) struct files_struct *in_fsp; uint32_t in_flags; struct tevent_req *subreq; + size_t unread_bytes; status = smbd_smb2_request_verify_sizes(req, 0x31); if (!NT_STATUS_IS_OK(status)) { @@ -74,7 +75,17 @@ NTSTATUS smbd_smb2_request_process_write(struct smbd_smb2_request *req) return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } - if (in_data_length > req->in.vector[i+2].iov_len) { + unread_bytes = smbd_smb2_unread_bytes(req); + if (unread_bytes) { + /* + * RECVFILE code path. Ensure we have the + * correct number of bytes left in the socket + * buffers. + */ + if (in_data_length != unread_bytes) { + return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); + } + } else if (in_data_length > req->in.vector[i+2].iov_len) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } -- 1.7.10.4 From e7df4d1e051c6f5b6e8e9d938379b3fbc61d713c Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Tue, 20 Nov 2012 21:24:08 -0800 Subject: [PATCH 5/9] Allow smbd_smb2_request_verify_sizes() to cope with unread bytes in the socket buffer (when RECVFILE is turned on). Part of bug #9412 - SMB2 server doesn't support recvfile. 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 a0e390e..3281b39 100644 --- a/source3/smbd/smb2_server.c +++ b/source3/smbd/smb2_server.c @@ -1241,6 +1241,11 @@ NTSTATUS smbd_smb2_request_verify_sizes(struct smbd_smb2_request *req, case SMB2_OP_GETINFO: min_dyn_size = 0; break; + case SMB2_OP_WRITE: + if (req->smb1req && req->smb1req->unread_bytes) { + min_dyn_size = 0; + } + break; } /* -- 1.7.10.4 From a18990f635fcbbf82db30f8aa0853c04948f2098 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Tue, 20 Nov 2012 21:26:01 -0800 Subject: [PATCH 6/9] Allow smbd_smb2_request_error_ex() to cope with unread bytes on error. Drain the socket if a RECVFILE write failed. Part of bug #9412 - SMB2 server doesn't support recvfile. 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 3281b39..c669f50 100644 --- a/source3/smbd/smb2_server.c +++ b/source3/smbd/smb2_server.c @@ -2039,6 +2039,16 @@ NTSTATUS smbd_smb2_request_error_ex(struct smbd_smb2_request *req, i, nt_errstr(status), info ? " +info" : "", location)); + if (req->smb1req && req->smb1req->unread_bytes) { + /* Recvfile error. Drain incoming socket. */ + if (drain_socket(req->sconn->sock, + req->smb1req->unread_bytes) != + req->smb1req->unread_bytes) + { + smb_panic("Failed to drain SMB2 socket\n"); + } + } + body.data = outhdr + SMB2_HDR_BODY; body.length = 8; SSVAL(body.data, 0, 9); -- 1.7.10.4 From ea4f1a4973b98751fee0dfba0980f47050682ca5 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Tue, 20 Nov 2012 21:28:02 -0800 Subject: [PATCH 7/9] Move the initialization of body vector to just after its creation. Part of bug #9412 - SMB2 server doesn't support recvfile. Signed-off-by: Jeremy Allison --- source3/smbd/smb2_server.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c index c669f50..e0c81e9 100644 --- a/source3/smbd/smb2_server.c +++ b/source3/smbd/smb2_server.c @@ -2417,13 +2417,14 @@ static int smbd_smb2_request_next_vector(struct tstream_context *stream, return -1; } + req->in.vector[idx].iov_base = (void *)body; + req->in.vector[idx].iov_len = body_size; + dyn = talloc_array(req->in.vector, uint8_t, dyn_size); if (dyn == NULL) { return -1; } - req->in.vector[idx].iov_base = (void *)body; - req->in.vector[idx].iov_len = body_size; req->in.vector[idx+1].iov_base = (void *)dyn; req->in.vector[idx+1].iov_len = dyn_size; -- 1.7.10.4 From 997c6ee90a081d50c10ec924d08f3faa4f268b28 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Tue, 20 Nov 2012 21:35:07 -0800 Subject: [PATCH 8/9] Add is_smb2_recvfile_write(). This is the guts of the SMB2_WRITE RECVFILE detection code. Part of bug #9412 - SMB2 server doesn't support recvfile. Signed-off-by: Jeremy Allison --- source3/smbd/smb2_server.c | 96 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c index e0c81e9..93ac03d 100644 --- a/source3/smbd/smb2_server.c +++ b/source3/smbd/smb2_server.c @@ -2175,6 +2175,102 @@ struct smbd_smb2_request_read_state { struct smbd_smb2_request *smb2_req; }; +static bool is_smb2_recvfile_write(struct smbd_smb2_request_read_state *state, + int idx, + size_t dyn_size) +{ + const uint8_t *inhdr; + int min_recv_size = lp_min_receive_file_size(); + struct smbd_smb2_request *req = state->smb2_req; + uint16_t opcode; + uint32_t in_flags; + uint32_t in_tid; + uint64_t in_session_id; + void *p; + struct smbd_smb2_tcon *tcon; + struct smbd_smb2_session *session; + + if (min_recv_size == 0) { + /* recvfile not turned on. Nothing to do here. */ + return false; + } + + if (req->do_signing) { + /* Can't do recvfile on signed connection. */ + return false; + } + + if (req->in.vector[idx-1].iov_len != SMB2_HDR_BODY) { + /* Bad header. Let the caller deal with it. */ + return false; + } + + inhdr = (const uint8_t *)req->in.vector[idx-1].iov_base; + + opcode = SVAL(inhdr, SMB2_HDR_OPCODE); + if (opcode != SMB2_OP_WRITE) { + /* Only do recvfile in SMB2_WRITE. */ + return false; + } + + /* Check that body size == 0x30 - this is a SMB2_WRITE body */ + if (req->in.vector[idx].iov_len != 0x30) { + /* Incorrect body size. Caller handle. */ + return false; + } + + if (dyn_size == 0 || dyn_size < min_recv_size) { + /* Bad write, or too small for us. Caller handle. */ + return false; + } + + in_flags = IVAL(inhdr, SMB2_HDR_FLAGS); + if (in_flags & SMB2_HDR_FLAG_CHAINED) { + /* Can't do recvfile on chained requests. */ + return false; + } + + /* + * We have to look up the session and tree id's, + * as we can't do recvfile on IPC or print connections. + */ + + in_session_id = BVAL(inhdr, SMB2_HDR_SESSION_ID); + + /* Lookup an existing session */ + p = idr_find(req->sconn->smb2.sessions.idtree, in_session_id); + if (p == NULL) { + /* Bad session id. Caller handle. */ + return false; + } + session = talloc_get_type_abort(p, struct smbd_smb2_session); + if (!NT_STATUS_IS_OK(session->status)) { + /* Session is invalid. Caller handle. */ + return false; + } + + in_tid = IVAL(inhdr, SMB2_HDR_TID); + + /* Lookup an existing tree id to make sure it's a file share. */ + p = idr_find(session->tcons.idtree, in_tid); + if (p == NULL) { + /* Bad tid. Caller handle. */ + return false; + } + tcon = talloc_get_type_abort(p, struct smbd_smb2_tcon); + + if (IS_IPC(tcon->compat_conn) || IS_PRINT(tcon->compat_conn)) { + return false; + } + + /* + * OK, this is a recvfile we can handle on + * a file share. + */ + + return true; +} + static int smbd_smb2_request_next_vector(struct tstream_context *stream, void *private_data, TALLOC_CTX *mem_ctx, -- 1.7.10.4 From 1bb58dda689656d0ad45171aa2aeb9a4d15c670b Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Tue, 20 Nov 2012 22:17:22 -0800 Subject: [PATCH 9/9] Plumb the new recvfile detection into the main SMB2 server path. Part of bug #9412 - SMB2 server doesn't support recvfile. Signed-off-by: Jeremy Allison --- source3/smbd/smb2_server.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c index 93ac03d..363ddef 100644 --- a/source3/smbd/smb2_server.c +++ b/source3/smbd/smb2_server.c @@ -2516,6 +2516,37 @@ static int smbd_smb2_request_next_vector(struct tstream_context *stream, req->in.vector[idx].iov_base = (void *)body; req->in.vector[idx].iov_len = body_size; + /* + * Might this be an SMB2_WRITE with recvfile turned on ? + * If so, adjust the lengths. + */ + if (!invalid && (next_command_ofs == 0)) { + if (is_smb2_recvfile_write(state, idx, dyn_size)) { + /* + * Adjust the sizes to ignore + * the write data left in the TCP + * buffers, and allocate a + * struct smb_request to hold + * the unread_bytes variable. + * That's how we know we're doing + * recvfile in the SMB2_WRITE code. + */ + req->smb1req = talloc_zero(req, + struct smb_request); + if (!req->smb1req) { + return -1; + } + + /* + * We don't adjust state->missing + * here as it's already been decremented + * for dyn_size. + */ + req->smb1req->unread_bytes = dyn_size; + dyn_size = 0; + } + } + dyn = talloc_array(req->in.vector, uint8_t, dyn_size); if (dyn == NULL) { return -1; -- 1.7.10.4