From 1eb19821403fdcd8ed5de9f7ceecb9d407124adf Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 19 Mar 2013 09:02:52 +0100 Subject: [PATCH 01/16] libcli/smb: add SMB_CAP_LEGACY_CLIENT_MASK define Older Samba releases (<= 3.6.x) expect the client to send CAP_LARGE_READX in order to let the client use large reads. Signed-off-by: Stefan Metzmacher --- libcli/smb/smb_constants.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/libcli/smb/smb_constants.h b/libcli/smb/smb_constants.h index f1ecbe9..6dd7b28 100644 --- a/libcli/smb/smb_constants.h +++ b/libcli/smb/smb_constants.h @@ -253,6 +253,16 @@ enum smb_signing_setting { #define SMB_CAP_CLIENT_MASK ( \ CAP_DYNAMIC_REAUTH | \ 0) +/* + * Older Samba releases (<= 3.6.x) + * expect the client to send CAP_LARGE_READX + * in order to let the client use large reads. + */ +#define SMB_CAP_LEGACY_CLIENT_MASK ( \ + SMB_CAP_CLIENT_MASK | \ + CAP_LARGE_READX | \ + CAP_LARGE_WRITEX | \ + 0) /* Client-side offline caching policy types */ enum csc_policy { -- 1.7.9.5 From fbd72ccf6cf41cf04ef0f845f9c29012b67305b9 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 19 Mar 2013 09:03:38 +0100 Subject: [PATCH 02/16] s3:libsmb: make use of SMB_CAP_LEGACY_CLIENT_MASK instead of SMB_CAP_CLIENT_MASK This should allow smbclient to use large reads against older Samba versions (<= 3.6.x). Signed-off-by: Stefan Metzmacher --- source3/libsmb/cliconnect.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/source3/libsmb/cliconnect.c b/source3/libsmb/cliconnect.c index 52d8e0c..872900c 100644 --- a/source3/libsmb/cliconnect.c +++ b/source3/libsmb/cliconnect.c @@ -341,9 +341,13 @@ static uint32_t cli_session_setup_capabilities(struct cli_state *cli, * - client only flags * - flags used in both directions * - * We do not echo the server only flags. + * We do not echo the server only flags, except some legacy flags. + * + * SMB_CAP_LEGACY_CLIENT_MASK contains CAP_LARGE_READX and + * CAP_LARGE_WRITEX in order to allow us to do large reads + * against old Samba releases (<= 3.6.x). */ - client_capabilities &= (SMB_CAP_BOTH_MASK | SMB_CAP_CLIENT_MASK); + client_capabilities &= (SMB_CAP_BOTH_MASK | SMB_CAP_LEGACY_CLIENT_MASK); /* * Session Setup specific flags CAP_DYNAMIC_REAUTH -- 1.7.9.5 From ece10408e6b69762f663fd8eaaa0cbc2899ee236 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 18 Mar 2013 15:02:55 +0100 Subject: [PATCH 03/16] libcli/smb: defer failing for missing NEGOTIATE_SECURITY_SIGNATURES_ENABLED Windows servers take a look at the FLAGS2_SMB_SECURITY_SIGNATURES_REQUIRED flag during a session setup and turn on signing if the client requires it. Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison --- libcli/smb/smbXcli_base.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/libcli/smb/smbXcli_base.c b/libcli/smb/smbXcli_base.c index 4c60a05..cd995e9 100644 --- a/libcli/smb/smbXcli_base.c +++ b/libcli/smb/smbXcli_base.c @@ -4046,6 +4046,15 @@ static void smbXcli_negprot_smb1_done(struct tevent_req *subreq) if (server_security_mode & NEGOTIATE_SECURITY_SIGNATURES_ENABLED) { server_signing = "supported"; server_allowed = true; + } else if (conn->mandatory_signing) { + /* + * We have mandatory signing as client + * lets assume the server will look at our + * FLAGS2_SMB_SECURITY_SIGNATURES_REQUIRED + * flag in the session setup + */ + server_signing = "not announced"; + server_allowed = true; } if (server_security_mode & NEGOTIATE_SECURITY_SIGNATURES_REQUIRED) { server_signing = "required"; -- 1.7.9.5 From 9ab515fb0695c8b20b280661ec915407c45730f2 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 13 Mar 2013 15:23:52 -0700 Subject: [PATCH 04/16] libcli/smb: smb1cli_inbuf_parse_chain() and smb1cli_conn_dispatch_incoming() should use smb_len_tcp. They have to cope with large READX call replies that have a length greater than smb_len_nbt() can handle. Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher --- libcli/smb/smbXcli_base.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libcli/smb/smbXcli_base.c b/libcli/smb/smbXcli_base.c index cd995e9..5a5828a 100644 --- a/libcli/smb/smbXcli_base.c +++ b/libcli/smb/smbXcli_base.c @@ -1618,7 +1618,7 @@ static NTSTATUS smb1cli_inbuf_parse_chain(uint8_t *buf, TALLOC_CTX *mem_ctx, NTSTATUS status; size_t min_size = MIN_SMB_SIZE; - buflen = smb_len_nbt(buf); + buflen = smb_len_tcp(buf); taken = 0; hdr = buf + NBT_HDR_SIZE; @@ -1845,7 +1845,7 @@ static NTSTATUS smb1cli_conn_dispatch_incoming(struct smbXcli_conn *conn, uint16_t mid; bool oplock_break; uint8_t *inhdr = inbuf + NBT_HDR_SIZE; - size_t len = smb_len_nbt(inbuf); + size_t len = smb_len_tcp(inbuf); struct iovec *iov = NULL; int num_iov = 0; struct tevent_req **chain = NULL; -- 1.7.9.5 From 28ae0cbb6ad8830ee029ca09e9b3c13c703cf230 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 18 Mar 2013 09:33:00 +0100 Subject: [PATCH 05/16] s3:libsmb: let cli_read_andx_create() accept any length It's up to the server to decide the allowed length. Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison --- source3/libsmb/clireadwrite.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/source3/libsmb/clireadwrite.c b/source3/libsmb/clireadwrite.c index 75c1683..47e7f1b 100644 --- a/source3/libsmb/clireadwrite.c +++ b/source3/libsmb/clireadwrite.c @@ -132,13 +132,6 @@ struct tevent_req *cli_read_andx_create(TALLOC_CTX *mem_ctx, struct cli_read_andx_state *state; uint8_t wct = 10; - if (size > cli_read_max_bufsize(cli)) { - DEBUG(0, ("cli_read_andx_send got size=%d, can only handle " - "size=%d\n", (int)size, - (int)cli_read_max_bufsize(cli))); - return NULL; - } - req = tevent_req_create(mem_ctx, &state, struct cli_read_andx_state); if (req == NULL) { return NULL; -- 1.7.9.5 From 08b027bcec37e6241bdbf26e7c87f9cd5518a158 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 18 Mar 2013 10:10:25 +0100 Subject: [PATCH 06/16] s3:smbd: keep global_client_caps and max_send from the first successful session setup Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison --- source3/smbd/sesssetup.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/source3/smbd/sesssetup.c b/source3/smbd/sesssetup.c index 890189c..4728759 100644 --- a/source3/smbd/sesssetup.c +++ b/source3/smbd/sesssetup.c @@ -132,11 +132,12 @@ static void reply_sesssetup_and_X_spnego(struct smb_request *req) uint16_t action = 0; NTTIME now = timeval_to_nttime(&req->request_time); struct smbXsrv_session *session = NULL; + uint16_t smb_bufsize = SVAL(req->vwv+2, 0); uint32_t client_caps = IVAL(req->vwv+10, 0); DEBUG(3,("Doing spnego session setup\n")); - if (global_client_caps == 0) { + if (!sconn->smb1.sessions.done_sesssetup) { global_client_caps = client_caps; if (!(global_client_caps & CAP_STATUS32)) { @@ -377,6 +378,12 @@ static void reply_sesssetup_and_X_spnego(struct smb_request *req) return; } + if (!sconn->smb1.sessions.done_sesssetup) { + sconn->smb1.sessions.max_send = + MIN(sconn->smb1.sessions.max_send,smb_bufsize); + } + sconn->smb1.sessions.done_sesssetup = true; + /* current_user_info is changed on new vuid */ reload_services(sconn, conn_snum_used, true); } else if (NT_STATUS_IS_OK(status)) { @@ -560,7 +567,7 @@ static void setup_new_vc_session(struct smbd_server_connection *sconn) void reply_sesssetup_and_X(struct smb_request *req) { uint64_t sess_vuid; - int smb_bufsize; + uint16_t smb_bufsize; DATA_BLOB lm_resp; DATA_BLOB nt_resp; DATA_BLOB plaintext_password; @@ -671,8 +678,7 @@ void reply_sesssetup_and_X(struct smb_request *req) const uint8_t *save_p = req->buf; uint16 byte_count; - - if(global_client_caps == 0) { + if (!sconn->smb1.sessions.done_sesssetup) { global_client_caps = IVAL(req->vwv+11, 0); if (!(global_client_caps & CAP_STATUS32)) { -- 1.7.9.5 From ed949c1d01df3ed631cb2ce3a8a665d7401cd01f Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 19 Mar 2013 13:24:46 +0100 Subject: [PATCH 07/16] s3:smbd: remove silly (SMB_OFF_T_BITS == 64) checks configure will abort if sizeof(off_t) is not 8. Signed-off-by: Stefan Metzmacher --- source3/smbd/negprot.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/source3/smbd/negprot.c b/source3/smbd/negprot.c index e33350c..cfe0983 100644 --- a/source3/smbd/negprot.c +++ b/source3/smbd/negprot.c @@ -284,11 +284,10 @@ static void reply_nt1(struct smb_request *req, uint16 choice) capabilities |= CAP_UNIX; } - if (lp_large_readwrite() && (SMB_OFF_T_BITS == 64)) + if (lp_large_readwrite()) capabilities |= CAP_LARGE_READX|CAP_LARGE_WRITEX|CAP_W2K_SMBS; - if (SMB_OFF_T_BITS == 64) - capabilities |= CAP_LARGE_FILES; + capabilities |= CAP_LARGE_FILES; if (lp_readraw() && lp_writeraw()) capabilities |= CAP_RAW_MODE; -- 1.7.9.5 From 0ed4bf9c16f9a3eefeb40dbb6c3cce2e49f9a346 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 18 Mar 2013 12:36:30 +0100 Subject: [PATCH 08/16] s3:smbd: add some const to req_is_in_chain() Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison --- source3/smbd/process.c | 2 +- source3/smbd/proto.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source3/smbd/process.c b/source3/smbd/process.c index 1640cca..fcb970d 100644 --- a/source3/smbd/process.c +++ b/source3/smbd/process.c @@ -3631,7 +3631,7 @@ void smbd_process(struct tevent_context *ev_ctx, exit_server_cleanly(NULL); } -bool req_is_in_chain(struct smb_request *req) +bool req_is_in_chain(const struct smb_request *req) { if (req->vwv != (const uint16_t *)(req->inbuf+smb_vwv)) { /* diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h index bd22dd3..7e13049 100644 --- a/source3/smbd/proto.h +++ b/source3/smbd/proto.h @@ -801,7 +801,7 @@ bool smb1_parse_chain(TALLOC_CTX *mem_ctx, const uint8_t *buf, struct smbd_server_connection *sconn, bool encrypted, uint32_t seqnum, struct smb_request ***reqs, unsigned *num_reqs); -bool req_is_in_chain(struct smb_request *req); +bool req_is_in_chain(const struct smb_request *req); void smbd_process(struct tevent_context *ev_ctx, struct messaging_context *msg_ctx, int sock_fd, -- 1.7.9.5 From aaf5a731180a67979d5dfe68d12aaed7eddb3215 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Mon, 18 Mar 2013 15:05:24 -0700 Subject: [PATCH 09/16] s3:smbd: Fix off-by 4 error in wrap protection code in create_outbuf() Subtract 4 from smb_size (39) here as the length of the SMB reply following the 4 byte type+length field can be up to 0xFFFFFF bytes. Signed-off-by: Jeremy Allison Signed-off-by: Stefan Metzmacher Reviewed-by: Stefan Metzmacher --- source3/smbd/process.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/source3/smbd/process.c b/source3/smbd/process.c index fcb970d..1ebda79 100644 --- a/source3/smbd/process.c +++ b/source3/smbd/process.c @@ -1270,11 +1270,13 @@ static bool create_outbuf(TALLOC_CTX *mem_ctx, struct smb_request *req, const char *inbuf, char **outbuf, uint8_t num_words, uint32_t num_bytes) { + size_t smb_len = MIN_SMB_SIZE + VWV(num_words) + num_bytes; + /* - * Protect against integer wrap - */ - if ((num_bytes > 0xffffff) - || ((num_bytes + smb_size + num_words*2) > 0xffffff)) { + * Protect against integer wrap. + * The SMB layer reply can be up to 0xFFFFFF bytes. + */ + if ((num_bytes > 0xffffff) || (smb_len > 0xffffff)) { char *msg; if (asprintf(&msg, "num_bytes too large: %u", (unsigned)num_bytes) == -1) { @@ -1283,8 +1285,11 @@ static bool create_outbuf(TALLOC_CTX *mem_ctx, struct smb_request *req, smb_panic(msg); } + /* + * Here we include the NBT header for now. + */ *outbuf = talloc_array(mem_ctx, char, - smb_size + num_words*2 + num_bytes); + NBT_HDR_SIZE + smb_len); if (*outbuf == NULL) { return false; } @@ -1296,7 +1301,7 @@ static bool create_outbuf(TALLOC_CTX *mem_ctx, struct smb_request *req, * himself */ if (num_words != 0) { - memset(*outbuf + smb_vwv0, 0, num_words*2); + memset(*outbuf + (NBT_HDR_SIZE + HDR_VWV), 0, VWV(num_words)); } return true; -- 1.7.9.5 From f225a4ae293ddb2b6ccabe5eb34ff25a3af498c8 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 15 Mar 2013 11:53:04 -0700 Subject: [PATCH 10/16] s3:smbd: Remove server_will_accept_large_read() and erroneous comment. We're going to replace this with a function that calculates the max PDU to return on a read and supports short reads. Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher --- source3/smbd/reply.c | 29 +---------------------------- 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/source3/smbd/reply.c b/source3/smbd/reply.c index 2c31f15..edcfb77 100644 --- a/source3/smbd/reply.c +++ b/source3/smbd/reply.c @@ -3848,26 +3848,6 @@ nosendfile_read: } /**************************************************************************** - MacOSX clients send large reads without telling us they are going to do that. - Bug #9572 - File corruption during SMB1 read by Mac OSX 10.8.2 clients - Allow this if we are talking to a Samba client, or if we told the client - we supported this. -****************************************************************************/ - -static bool server_will_accept_large_read(void) -{ - /* Samba client ? No problem. */ - if (get_remote_arch() == RA_SAMBA) { - return true; - } - /* Need UNIX extensions. */ - if (!lp_unix_extensions()) { - return false; - } - return true; -} - -/**************************************************************************** Reply to a read and X. ****************************************************************************/ @@ -3913,14 +3893,7 @@ void reply_read_and_X(struct smb_request *req) } upper_size = SVAL(req->vwv+7, 0); - if ((upper_size != 0) && server_will_accept_large_read()) { - /* - * This is Samba only behavior (up to Samba 3.6)! - * - * Windows 2008 R2 ignores the upper_size, - * so we do unless unix extentions are active - * or "smbclient" is talking to us. - */ + if (upper_size != 0) { smb_maxcnt |= (upper_size<<16); if (upper_size > 1) { /* Can't do this on a chained packet. */ -- 1.7.9.5 From e2f34ef8d12d06a56fc2d7fcd288af4824c08810 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 15 Mar 2013 11:57:48 -0700 Subject: [PATCH 11/16] s3:smbd: Add functions calc_max_read_pdu()/calc_read_size() to work out the length we should return. LARGE_READX test shows it's always safe to return a short read. Windows does so. Do the calculations to return what will fit in a read depending on what the client negotiated. Signed-off-by: Jeremy Allison Signed-off-by: Stefan Metzmacher Reviewed-by: Stefan Metzmacher --- source3/smbd/reply.c | 111 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 86 insertions(+), 25 deletions(-) diff --git a/source3/smbd/reply.c b/source3/smbd/reply.c index edcfb77..8b500c5 100644 --- a/source3/smbd/reply.c +++ b/source3/smbd/reply.c @@ -3848,6 +3848,84 @@ nosendfile_read: } /**************************************************************************** + Work out how much space we have for a read return. +****************************************************************************/ + +static size_t calc_max_read_pdu(const struct smb_request *req) +{ + if (req->sconn->conn->protocol < PROTOCOL_NT1) { + return req->sconn->smb1.sessions.max_send; + } + + if (!lp_large_readwrite()) { + return req->sconn->smb1.sessions.max_send; + } + + if (req_is_in_chain(req)) { + return req->sconn->smb1.sessions.max_send; + } + + if (req->encrypted) { + /* + * Don't take encrypted traffic up to the + * limit. There are padding considerations + * that make that tricky. + */ + return req->sconn->smb1.sessions.max_send; + } + + if (srv_is_signing_active(req->sconn)) { + return 0x1FFFF; + } + + if (!lp_unix_extensions()) { + return 0x1FFFF; + } + + /* + * We can do ultra-large POSIX reads. + */ + return 0xFFFFFF; +} + +/**************************************************************************** + Calculate how big a read can be. Copes with all clients. It's always + safe to return a short read - Windows does this. +****************************************************************************/ + +static size_t calc_read_size(const struct smb_request *req, + size_t upper_size, + size_t lower_size) +{ + size_t max_pdu = calc_max_read_pdu(req); + size_t total_size = 0; + size_t hdr_len = MIN_SMB_SIZE + VWV(12); + size_t max_len = max_pdu - hdr_len; + + /* + * Windows explicitly ignores upper size of 0xFFFF. + * See [MS-SMB].pdf <26> Section 2.2.4.2.1: + * We must do the same as these will never fit even in + * an extended size NetBIOS packet. + */ + if (upper_size == 0xFFFF) { + upper_size = 0; + } + + if (req->sconn->conn->protocol < PROTOCOL_NT1) { + upper_size = 0; + } + + total_size = ((upper_size<<16) | lower_size); + + /* + * LARGE_READX test shows it's always safe to return + * a short read. Windows does so. + */ + return MIN(total_size, max_len); +} + +/**************************************************************************** Reply to a read and X. ****************************************************************************/ @@ -3893,31 +3971,14 @@ void reply_read_and_X(struct smb_request *req) } upper_size = SVAL(req->vwv+7, 0); - if (upper_size != 0) { - smb_maxcnt |= (upper_size<<16); - if (upper_size > 1) { - /* Can't do this on a chained packet. */ - if ((CVAL(req->vwv+0, 0) != 0xFF)) { - reply_nterror(req, NT_STATUS_NOT_SUPPORTED); - END_PROFILE(SMBreadX); - return; - } - /* We currently don't do this on signed or sealed data. */ - if (srv_is_signing_active(req->sconn) || - is_encrypted_packet(req->sconn, req->inbuf)) { - reply_nterror(req, NT_STATUS_NOT_SUPPORTED); - END_PROFILE(SMBreadX); - return; - } - /* Is there room in the reply for this data ? */ - if (smb_maxcnt > (0xFFFFFF - (smb_size -4 + 12*2))) { - reply_nterror(req, - NT_STATUS_INVALID_PARAMETER); - END_PROFILE(SMBreadX); - return; - } - big_readX = True; - } + smb_maxcnt = calc_read_size(req, upper_size, smb_maxcnt); + if (smb_maxcnt > (0x1FFFF - (MIN_SMB_SIZE + VWV(12)))) { + /* + * This is a heuristic to avoid keeping large + * outgoing buffers around over long-lived aio + * requests. + */ + big_readX = True; } if (req->wct == 12) { -- 1.7.9.5 From b9f40456e65772a09d3a343a8a578eab4e00b9de Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 18 Mar 2013 19:50:38 +0100 Subject: [PATCH 12/16] s4:smb_server: fix large read_andx requests Signed-off-by: Stefan Metzmacher --- source4/smb_server/smb/reply.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/source4/smb_server/smb/reply.c b/source4/smb_server/smb/reply.c index bae6b2c..7ce5f5d 100644 --- a/source4/smb_server/smb/reply.c +++ b/source4/smb_server/smb/reply.c @@ -848,6 +848,7 @@ static void reply_read_and_X_send(struct ntvfs_request *ntvfs) void smbsrv_reply_read_and_X(struct smbsrv_request *req) { union smb_read *io; + uint16_t high_part = 0; /* parse request */ if (req->in.wct != 12) { @@ -869,13 +870,18 @@ void smbsrv_reply_read_and_X(struct smbsrv_request *req) io->readx.in.read_for_execute = false; } - if (req->smb_conn->negotiate.client_caps & CAP_LARGE_READX) { - uint32_t high_part = IVAL(req->in.vwv, VWV(7)); - if (high_part == 1) { - io->readx.in.maxcnt |= high_part << 16; - } + if (req->smb_conn->negotiate.protocol == PROTOCOL_NT1) { + high_part = SVAL(req->in.vwv, VWV(7)); } - + if (high_part != UINT16_MAX) { + io->readx.in.maxcnt |= high_part << 16; + } + + /* + * Windows truncates the length to 0x10000 + */ + io->readx.in.maxcnt = MIN(io->readx.in.maxcnt, 0x10000); + /* the 64 bit variant */ if (req->in.wct == 12) { uint32_t offset_high = IVAL(req->in.vwv, VWV(10)); -- 1.7.9.5 From c6c1f62ca5c496ce8737d4c10dd3d47435f9d49f Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 13 Mar 2013 15:43:21 -0700 Subject: [PATCH 13/16] s3:torture: Add new LARGE_READX test to investigate large SMBreadX behavior. Signed-off-by: Jeremy Allison Signed-off-by: Stefan Metzmacher Reviewed-by: Stefan Metzmacher --- source3/torture/torture.c | 338 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 337 insertions(+), 1 deletion(-) diff --git a/source3/torture/torture.c b/source3/torture/torture.c index 93b9cfd..3c6db30 100644 --- a/source3/torture/torture.c +++ b/source3/torture/torture.c @@ -65,7 +65,7 @@ static fstring multishare_conn_fname; static bool use_multishare_conn = False; static bool do_encrypt; static const char *local_path = NULL; -static int signing_state = SMB_SIGNING_DEFAULT; +static enum smb_signing_setting signing_state = SMB_SIGNING_DEFAULT; char *test_filename; bool torture_showall = False; @@ -7239,6 +7239,341 @@ static bool run_windows_write(int dummy) return ret; } +static size_t calc_expected_return(struct cli_state *cli, size_t len_requested) +{ + size_t max_pdu = 0x1FFFF; + + if (cli->server_posix_capabilities & CIFS_UNIX_LARGE_READ_CAP) { + max_pdu = 0xFFFFFF; + } + + if (smb1cli_conn_signing_is_active(cli->conn)) { + max_pdu = 0x1FFFF; + } + + if (smb1cli_conn_encryption_on(cli->conn)) { + max_pdu = CLI_BUFFER_SIZE; + } + + if ((len_requested & 0xFFFF0000) == 0xFFFF0000) { + len_requested &= 0xFFFF; + } + + return MIN(len_requested, max_pdu - (MIN_SMB_SIZE + VWV(12))); +} + +static bool check_read_call(struct cli_state *cli, + uint16_t fnum, + uint8_t *buf, + size_t len_requested) +{ + NTSTATUS status; + struct tevent_req *subreq = NULL; + ssize_t len_read = 0; + size_t len_expected = 0; + struct tevent_context *ev = NULL; + + ev = samba_tevent_context_init(talloc_tos()); + if (ev == NULL) { + return false; + } + + subreq = cli_read_andx_send(talloc_tos(), + ev, + cli, + fnum, + 0, + len_requested); + + if (!tevent_req_poll_ntstatus(subreq, ev, &status)) { + return false; + } + + status = cli_read_andx_recv(subreq, &len_read, &buf); + if (!NT_STATUS_IS_OK(status)) { + d_printf("cli_read_andx_recv failed: %s\n", nt_errstr(status)); + return false; + } + + TALLOC_FREE(subreq); + TALLOC_FREE(ev); + + len_expected = calc_expected_return(cli, len_requested); + + if (len_expected > 0x10000 && len_read == 0x10000) { + /* Windows servers only return a max of 0x10000, + doesn't matter if you set CAP_LARGE_READX in + the client sessionsetupX call or not. */ + d_printf("Windows server - returned 0x10000 on a read of 0x%x\n", + (unsigned int)len_requested); + } else if (len_read != len_expected) { + d_printf("read of 0x%x failed: got 0x%x, expected 0x%x\n", + (unsigned int)len_requested, + (unsigned int)len_read, + (unsigned int)len_expected); + return false; + } else { + d_printf("Correct read reply.\n"); + } + + return true; +} + +/* Test large readX variants. */ +static bool large_readx_tests(struct cli_state *cli, + uint16_t fnum, + uint8_t *buf) +{ + /* A read of 0xFFFF0001 should *always* return 1 byte. */ + if (check_read_call(cli, fnum, buf, 0xFFFF0001) == false) { + return false; + } + /* A read of 0x10000 should return 0x10000 bytes. */ + if (check_read_call(cli, fnum, buf, 0x10000) == false) { + return false; + } + /* A read of 0x10000 should return 0x10001 bytes. */ + if (check_read_call(cli, fnum, buf, 0x10001) == false) { + return false; + } + /* A read of 0x1FFFF - (MIN_SMB_SIZE + VWV(12) should return + the requested number of bytes. */ + if (check_read_call(cli, fnum, buf, 0x1FFFF - (MIN_SMB_SIZE + VWV(12))) == false) { + return false; + } + /* A read of 1MB should return 1MB bytes (on Samba). */ + if (check_read_call(cli, fnum, buf, 0x100000) == false) { + return false; + } + + if (check_read_call(cli, fnum, buf, 0x20001) == false) { + return false; + } + if (check_read_call(cli, fnum, buf, 0x22000001) == false) { + return false; + } + if (check_read_call(cli, fnum, buf, 0xFFFE0001) == false) { + return false; + } + return true; +} + +static bool run_large_readx(int dummy) +{ + uint8_t *buf = NULL; + struct cli_state *cli1 = NULL; + struct cli_state *cli2 = NULL; + bool correct = false; + const char *fname = "\\large_readx.dat"; + NTSTATUS status; + uint16_t fnum1 = UINT16_MAX; + uint32_t normal_caps = 0; + size_t file_size = 20*1024*1024; + TALLOC_CTX *frame = talloc_stackframe(); + size_t i; + struct { + const char *name; + enum smb_signing_setting signing_setting; + enum protocol_types protocol; + } runs[] = { + { + .name = "NT1", + .signing_setting = SMB_SIGNING_IF_REQUIRED, + .protocol = PROTOCOL_NT1, + },{ + .name = "NT1 - SIGNING_REQUIRED", + .signing_setting = SMB_SIGNING_REQUIRED, + .protocol = PROTOCOL_NT1, + }, + }; + + printf("starting large_readx test\n"); + + if (!torture_open_connection(&cli1, 0)) { + goto out; + } + + normal_caps = smb1cli_conn_capabilities(cli1->conn); + + if (!(normal_caps & CAP_LARGE_READX)) { + d_printf("Server doesn't have CAP_LARGE_READX 0x%x\n", + (unsigned int)normal_caps); + goto out; + } + + /* Create a file of size 4MB. */ + status = cli_ntcreate(cli1, fname, 0, GENERIC_ALL_ACCESS, + FILE_ATTRIBUTE_NORMAL, 0, FILE_OVERWRITE_IF, + 0, 0, &fnum1); + + if (!NT_STATUS_IS_OK(status)) { + d_printf("open %s failed: %s\n", fname, nt_errstr(status)); + goto out; + } + + /* Write file_size bytes. */ + buf = talloc_zero_array(frame, uint8_t, file_size); + if (buf == NULL) { + goto out; + } + + status = cli_writeall(cli1, + fnum1, + 0, + buf, + 0, + file_size, + NULL); + if (!NT_STATUS_IS_OK(status)) { + d_printf("cli_writeall failed: %s\n", nt_errstr(status)); + goto out; + } + + status = cli_close(cli1, fnum1); + if (!NT_STATUS_IS_OK(status)) { + d_printf("cli_close failed: %s\n", nt_errstr(status)); + goto out; + } + + fnum1 = UINT16_MAX; + + for (i=0; i < ARRAY_SIZE(runs); i++) { + enum smb_signing_setting saved_signing_setting = signing_state; + uint16_t fnum2 = -1; + + if (do_encrypt && + (runs[i].signing_setting == SMB_SIGNING_REQUIRED)) + { + d_printf("skip[%u] - %s\n", (unsigned)i, runs[i].name); + continue; + } + + d_printf("run[%u] - %s\n", (unsigned)i, runs[i].name); + + signing_state = runs[i].signing_setting; + cli2 = open_nbt_connection(); + signing_state = saved_signing_setting; + if (cli2 == NULL) { + goto out; + } + + status = smbXcli_negprot(cli2->conn, + cli2->timeout, + runs[i].protocol, + runs[i].protocol); + if (!NT_STATUS_IS_OK(status)) { + goto out; + } + + status = cli_session_setup(cli2, + username, + password, + strlen(password)+1, + password, + strlen(password)+1, + workgroup); + if (!NT_STATUS_IS_OK(status)) { + goto out; + } + + status = cli_tree_connect(cli2, + share, + "?????", + password, + strlen(password)+1); + if (!NT_STATUS_IS_OK(status)) { + goto out; + } + + cli_set_timeout(cli2, 120000); /* set a really long timeout (2 minutes) */ + + normal_caps = smb1cli_conn_capabilities(cli2->conn); + + if (!(normal_caps & CAP_LARGE_READX)) { + d_printf("Server doesn't have CAP_LARGE_READX 0x%x\n", + (unsigned int)normal_caps); + goto out; + } + + if (do_encrypt) { + if (force_cli_encryption(cli2, share) == false) { + goto out; + } + } else if (SERVER_HAS_UNIX_CIFS(cli2)) { + uint16_t major, minor; + uint32_t caplow, caphigh; + + status = cli_unix_extensions_version(cli2, + &major, &minor, + &caplow, &caphigh); + if (!NT_STATUS_IS_OK(status)) { + goto out; + } + } + + status = cli_ntcreate(cli2, fname, 0, FILE_READ_DATA, + FILE_ATTRIBUTE_NORMAL, 0, FILE_OPEN, + 0, 0, &fnum2); + if (!NT_STATUS_IS_OK(status)) { + d_printf("Second open %s failed: %s\n", fname, nt_errstr(status)); + goto out; + } + + /* All reads must return less than file_size bytes. */ + if (!large_readx_tests(cli2, fnum2, buf)) { + goto out; + } + + status = cli_close(cli2, fnum2); + if (!NT_STATUS_IS_OK(status)) { + d_printf("cli_close failed: %s\n", nt_errstr(status)); + goto out; + } + fnum2 = -1; + + if (!torture_close_connection(cli2)) { + goto out; + } + cli2 = NULL; + } + + correct = true; + printf("Success on large_readx test\n"); + + out: + + if (cli2) { + if (!torture_close_connection(cli2)) { + correct = false; + } + } + + if (cli1) { + if (fnum1 != UINT16_MAX) { + status = cli_close(cli1, fnum1); + if (!NT_STATUS_IS_OK(status)) { + d_printf("cli_close failed: %s\n", nt_errstr(status)); + } + fnum1 = UINT16_MAX; + } + + status = cli_unlink(cli1, fname, + FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN); + if (!NT_STATUS_IS_OK(status)) { + printf("unlink failed (%s)\n", nt_errstr(status)); + } + + if (!torture_close_connection(cli1)) { + correct = false; + } + } + + TALLOC_FREE(frame); + + printf("finished large_readx test\n"); + return correct; +} + static bool run_cli_echo(int dummy) { struct cli_state *cli; @@ -9153,6 +9488,7 @@ static struct { { "CHAIN2", run_chain2, 0}, { "CHAIN3", run_chain3, 0}, { "WINDOWS-WRITE", run_windows_write, 0}, + { "LARGE_READX", run_large_readx, 0}, { "NTTRANS-CREATE", run_nttrans_create, 0}, { "NTTRANS-FSCTL", run_nttrans_fsctl, 0}, { "CLI_ECHO", run_cli_echo, 0}, -- 1.7.9.5 From e1e856dc66462a20c47508cedc8a5b69cc710afd Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 13 Mar 2013 15:45:12 -0700 Subject: [PATCH 14/16] s3:selftest: Add LARGE_READX test into our make test infrastructure. Tested against non-encrypted and encrypted connections. Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher --- source3/selftest/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source3/selftest/tests.py b/source3/selftest/tests.py index 30253ca..1c123f5 100755 --- a/source3/selftest/tests.py +++ b/source3/selftest/tests.py @@ -54,7 +54,7 @@ plantestsuite("samba3.blackbox.registry.upgrade", "s3dc:local", [os.path.join(sa tests = ["FDPASS", "LOCK1", "LOCK2", "LOCK3", "LOCK4", "LOCK5", "LOCK6", "LOCK7", "LOCK9", "UNLINK", "BROWSE", "ATTR", "TRANS2", "TORTURE", "OPLOCK1", "OPLOCK2", "OPLOCK4", "STREAMERROR", - "DIR", "DIR1", "DIR-CREATETIME", "TCON", "TCONDEV", "RW1", "RW2", "RW3", "RW-SIGNING", + "DIR", "DIR1", "DIR-CREATETIME", "TCON", "TCONDEV", "RW1", "RW2", "RW3", "LARGE_READX", "RW-SIGNING", "OPEN", "XCOPY", "RENAME", "DELETE", "DELETE-LN", "PROPERTIES", "W2K", "TCON2", "IOCTL", "CHKPATH", "FDSESS", "CHAIN1", "CHAIN2", "CHAIN3", -- 1.7.9.5 From 042a0c2262b9cce4f272d567600c693e1061514a Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 19 Mar 2013 17:11:03 +0100 Subject: [PATCH 15/16] s4:torture: raw.read fix large reads against windows Signed-off-by: Stefan Metzmacher --- source4/torture/raw/read.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/source4/torture/raw/read.c b/source4/torture/raw/read.c index 6586177..5a5ffee 100644 --- a/source4/torture/raw/read.c +++ b/source4/torture/raw/read.c @@ -529,7 +529,7 @@ static bool test_readx(struct torture_context *tctx, struct smbcli_state *cli) printf("SAMBA: large read extension\n"); CHECK_VALUE(io.readx.out.nread, 80000); } else { - CHECK_VALUE(io.readx.out.nread, 0); + CHECK_VALUE(io.readx.out.nread, 0x10000); } CHECK_BUFFER(buf, seed, io.readx.out.nread); @@ -569,13 +569,7 @@ static bool test_readx(struct torture_context *tctx, struct smbcli_state *cli) io.readx.in.maxcnt = 0x10000; status = smb_raw_read(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_OK); - if (torture_setting_bool(tctx, "samba3", false) || - torture_setting_bool(tctx, "samba4", false)) { - printf("SAMBA: large read extension\n"); - CHECK_VALUE(io.readx.out.nread, 0x10000); - } else { - CHECK_VALUE(io.readx.out.nread, 0); - } + CHECK_VALUE(io.readx.out.nread, 0x10000); io.readx.in.maxcnt = 0x10001; status = smb_raw_read(cli->tree, &io); @@ -585,7 +579,7 @@ static bool test_readx(struct torture_context *tctx, struct smbcli_state *cli) printf("SAMBA: large read extension\n"); CHECK_VALUE(io.readx.out.nread, 0x10001); } else { - CHECK_VALUE(io.readx.out.nread, 0); + CHECK_VALUE(io.readx.out.nread, 0x10000); } } else { printf("Server does not support the CAP_LARGE_READX extension\n"); -- 1.7.9.5 From b2e83f9c36c5bb9c5860fe5cd19c2de8c5fbeff6 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 20 Mar 2013 08:49:20 +0100 Subject: [PATCH 16/16] s4:torture: let raw.read accept larger reads than 0x10000 Signed-off-by: Stefan Metzmacher --- source4/torture/raw/read.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/source4/torture/raw/read.c b/source4/torture/raw/read.c index 5a5ffee..59089bf 100644 --- a/source4/torture/raw/read.c +++ b/source4/torture/raw/read.c @@ -524,8 +524,7 @@ static bool test_readx(struct torture_context *tctx, struct smbcli_state *cli) CHECK_STATUS(status, NT_STATUS_OK); CHECK_VALUE(io.readx.out.remaining, 0xFFFF); CHECK_VALUE(io.readx.out.compaction_mode, 0); - if (torture_setting_bool(tctx, "samba3", false) || - torture_setting_bool(tctx, "samba4", false)) { + if (io.readx.out.nread == io.readx.in.maxcnt) { printf("SAMBA: large read extension\n"); CHECK_VALUE(io.readx.out.nread, 80000); } else { @@ -574,8 +573,7 @@ static bool test_readx(struct torture_context *tctx, struct smbcli_state *cli) io.readx.in.maxcnt = 0x10001; status = smb_raw_read(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_OK); - if (torture_setting_bool(tctx, "samba3", false) || - torture_setting_bool(tctx, "samba4", false)) { + if (io.readx.out.nread == io.readx.in.maxcnt) { printf("SAMBA: large read extension\n"); CHECK_VALUE(io.readx.out.nread, 0x10001); } else { -- 1.7.9.5