From bf62b6642c77e14142cdb724dc99dd3f8bfd89ac Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 14 May 2007 18:02:49 +0000 Subject: r22866: handle incoming chained smb2 requests in our server code to let the windows explorer in longhorn beta3 work. metze (This used to be commit 2390c9f24daccec917608cac0870890cdc73cb1c) --- source4/libcli/smb2/request.c | 24 +++---- source4/libcli/smb2/smb2.h | 2 +- source4/smb_server/smb2/fileio.c | 6 +- source4/smb_server/smb2/negprot.c | 24 +++---- source4/smb_server/smb2/receive.c | 117 +++++++++++++++++++++++++++++----- source4/smb_server/smb2/smb2_server.h | 7 ++ source4/smb_server/smb2/tcon.c | 11 ++++ 7 files changed, 148 insertions(+), 43 deletions(-) (limited to 'source4') diff --git a/source4/libcli/smb2/request.c b/source4/libcli/smb2/request.c index 87c4dd78e1..ef024d53f8 100644 --- a/source4/libcli/smb2/request.c +++ b/source4/libcli/smb2/request.c @@ -80,18 +80,18 @@ struct smb2_request *smb2_request_init(struct smb2_transport *transport, uint16_ req->out.body_size = body_fixed_size; req->out.dynamic = (body_dynamic_size ? req->out.body + body_fixed_size : NULL); - SIVAL(req->out.hdr, 0, SMB2_MAGIC); - SSVAL(req->out.hdr, SMB2_HDR_LENGTH, SMB2_HDR_BODY); - SSVAL(req->out.hdr, SMB2_HDR_PAD1, 0); - SIVAL(req->out.hdr, SMB2_HDR_STATUS, 0); - SSVAL(req->out.hdr, SMB2_HDR_OPCODE, opcode); - SSVAL(req->out.hdr, SMB2_HDR_UNKNOWN1,0); - SIVAL(req->out.hdr, SMB2_HDR_FLAGS, 0); - SIVAL(req->out.hdr, SMB2_HDR_UNKNOWN2,0); - SBVAL(req->out.hdr, SMB2_HDR_SEQNUM, req->seqnum); - SIVAL(req->out.hdr, SMB2_HDR_PID, 0); - SIVAL(req->out.hdr, SMB2_HDR_TID, 0); - SBVAL(req->out.hdr, SMB2_HDR_UID, 0); + SIVAL(req->out.hdr, 0, SMB2_MAGIC); + SSVAL(req->out.hdr, SMB2_HDR_LENGTH, SMB2_HDR_BODY); + SSVAL(req->out.hdr, SMB2_HDR_PAD1, 0); + SIVAL(req->out.hdr, SMB2_HDR_STATUS, 0); + SSVAL(req->out.hdr, SMB2_HDR_OPCODE, opcode); + SSVAL(req->out.hdr, SMB2_HDR_UNKNOWN1, 0); + SIVAL(req->out.hdr, SMB2_HDR_FLAGS, 0); + SIVAL(req->out.hdr, SMB2_HDR_CHAIN_OFFSET, 0); + SBVAL(req->out.hdr, SMB2_HDR_SEQNUM, req->seqnum); + SIVAL(req->out.hdr, SMB2_HDR_PID, 0); + SIVAL(req->out.hdr, SMB2_HDR_TID, 0); + SBVAL(req->out.hdr, SMB2_HDR_UID, 0); memset(req->out.hdr+SMB2_HDR_SIG, 0, 16); /* set the length of the fixed body part and +1 if there's a dynamic part also */ diff --git a/source4/libcli/smb2/smb2.h b/source4/libcli/smb2/smb2.h index 586acaccf6..4db7c126d8 100644 --- a/source4/libcli/smb2/smb2.h +++ b/source4/libcli/smb2/smb2.h @@ -164,7 +164,7 @@ struct smb2_request { #define SMB2_HDR_OPCODE 0x0c #define SMB2_HDR_UNKNOWN1 0x0e #define SMB2_HDR_FLAGS 0x10 -#define SMB2_HDR_UNKNOWN2 0x14 +#define SMB2_HDR_CHAIN_OFFSET 0x14 #define SMB2_HDR_SEQNUM 0x18 #define SMB2_HDR_PID 0x20 #define SMB2_HDR_TID 0x24 diff --git a/source4/smb_server/smb2/fileio.c b/source4/smb_server/smb2/fileio.c index 8e420458be..e399bfb901 100644 --- a/source4/smb_server/smb2/fileio.c +++ b/source4/smb_server/smb2/fileio.c @@ -44,9 +44,13 @@ static void smb2srv_create_send(struct ntvfs_request *ntvfs) SBVAL(req->out.body, 0x30, io->smb2.out.size); SIVAL(req->out.body, 0x38, io->smb2.out.file_attr); SIVAL(req->out.body, 0x3C, io->smb2.out._pad); - smb2srv_push_handle(req->out.body, 0x40,io->smb2.out.file.ntvfs); + smb2srv_push_handle(req->out.body, 0x40, io->smb2.out.file.ntvfs); SMB2SRV_CHECK(smb2_push_o32s32_blob(&req->out, 0x50, io->smb2.out.blob)); + /* also setup the chained file handle */ + req->chained_file_handle = req->_chained_file_handle; + smb2srv_push_handle(req->chained_file_handle, 0, io->smb2.out.file.ntvfs); + smb2srv_send_reply(req); } diff --git a/source4/smb_server/smb2/negprot.c b/source4/smb_server/smb2/negprot.c index fe65917275..c666b3d761 100644 --- a/source4/smb_server/smb2/negprot.c +++ b/source4/smb_server/smb2/negprot.c @@ -204,18 +204,18 @@ void smb2srv_reply_smb_negprot(struct smbsrv_request *smb_req) req->in.body_size = body_fixed_size; req->in.dynamic = NULL; - SIVAL(req->in.hdr, 0, SMB2_MAGIC); - SSVAL(req->in.hdr, SMB2_HDR_LENGTH, SMB2_HDR_BODY); - SSVAL(req->in.hdr, SMB2_HDR_PAD1, 0); - SIVAL(req->in.hdr, SMB2_HDR_STATUS, 0); - SSVAL(req->in.hdr, SMB2_HDR_OPCODE, SMB2_OP_NEGPROT); - SSVAL(req->in.hdr, SMB2_HDR_UNKNOWN1,0); - SIVAL(req->in.hdr, SMB2_HDR_FLAGS, 0); - SIVAL(req->in.hdr, SMB2_HDR_UNKNOWN2,0); - SBVAL(req->in.hdr, SMB2_HDR_SEQNUM, 0); - SIVAL(req->in.hdr, SMB2_HDR_PID, 0); - SIVAL(req->in.hdr, SMB2_HDR_TID, 0); - SBVAL(req->in.hdr, SMB2_HDR_UID, 0); + SIVAL(req->in.hdr, 0, SMB2_MAGIC); + SSVAL(req->in.hdr, SMB2_HDR_LENGTH, SMB2_HDR_BODY); + SSVAL(req->in.hdr, SMB2_HDR_PAD1, 0); + SIVAL(req->in.hdr, SMB2_HDR_STATUS, 0); + SSVAL(req->in.hdr, SMB2_HDR_OPCODE, SMB2_OP_NEGPROT); + SSVAL(req->in.hdr, SMB2_HDR_UNKNOWN1, 0); + SIVAL(req->in.hdr, SMB2_HDR_FLAGS, 0); + SIVAL(req->in.hdr, SMB2_HDR_CHAIN_OFFSET, 0); + SBVAL(req->in.hdr, SMB2_HDR_SEQNUM, 0); + SIVAL(req->in.hdr, SMB2_HDR_PID, 0); + SIVAL(req->in.hdr, SMB2_HDR_TID, 0); + SBVAL(req->in.hdr, SMB2_HDR_UID, 0); memset(req->in.hdr+SMB2_HDR_SIG, 0, 16); /* this seems to be a bug, they use 0x24 but the length is 0x26 */ diff --git a/source4/smb_server/smb2/receive.c b/source4/smb_server/smb2/receive.c index f2a34bdcbf..bae3486014 100644 --- a/source4/smb_server/smb2/receive.c +++ b/source4/smb_server/smb2/receive.c @@ -91,18 +91,18 @@ NTSTATUS smb2srv_setup_reply(struct smb2srv_request *req, uint16_t body_fixed_si req->out.body_size = body_fixed_size; req->out.dynamic = (body_dynamic_size ? req->out.body + body_fixed_size : NULL); - SIVAL(req->out.hdr, 0, SMB2_MAGIC); - SSVAL(req->out.hdr, SMB2_HDR_LENGTH, SMB2_HDR_BODY); - SSVAL(req->out.hdr, SMB2_HDR_PAD1, 0); - SIVAL(req->out.hdr, SMB2_HDR_STATUS, NT_STATUS_V(req->status)); - SSVAL(req->out.hdr, SMB2_HDR_OPCODE, SVAL(req->in.hdr, SMB2_HDR_OPCODE)); - SSVAL(req->out.hdr, SMB2_HDR_UNKNOWN1,0x0001); - SIVAL(req->out.hdr, SMB2_HDR_FLAGS, flags); - SIVAL(req->out.hdr, SMB2_HDR_UNKNOWN2,0); - SBVAL(req->out.hdr, SMB2_HDR_SEQNUM, req->seqnum); - SIVAL(req->out.hdr, SMB2_HDR_PID, pid); - SIVAL(req->out.hdr, SMB2_HDR_TID, tid); - SBVAL(req->out.hdr, SMB2_HDR_UID, BVAL(req->in.hdr, SMB2_HDR_UID)); + SIVAL(req->out.hdr, 0, SMB2_MAGIC); + SSVAL(req->out.hdr, SMB2_HDR_LENGTH, SMB2_HDR_BODY); + SSVAL(req->out.hdr, SMB2_HDR_PAD1, 0); + SIVAL(req->out.hdr, SMB2_HDR_STATUS, NT_STATUS_V(req->status)); + SSVAL(req->out.hdr, SMB2_HDR_OPCODE, SVAL(req->in.hdr, SMB2_HDR_OPCODE)); + SSVAL(req->out.hdr, SMB2_HDR_UNKNOWN1, 0x0001); + SIVAL(req->out.hdr, SMB2_HDR_FLAGS, flags); + SIVAL(req->out.hdr, SMB2_HDR_CHAIN_OFFSET, 0); + SBVAL(req->out.hdr, SMB2_HDR_SEQNUM, req->seqnum); + SIVAL(req->out.hdr, SMB2_HDR_PID, pid); + SIVAL(req->out.hdr, SMB2_HDR_TID, tid); + SBVAL(req->out.hdr, SMB2_HDR_UID, BVAL(req->in.hdr, SMB2_HDR_UID)); memset(req->out.hdr+SMB2_HDR_SIG, 0, 16); /* set the length of the fixed body part and +1 if there's a dynamic part also */ @@ -120,6 +120,85 @@ NTSTATUS smb2srv_setup_reply(struct smb2srv_request *req, uint16_t body_fixed_si return NT_STATUS_OK; } +static NTSTATUS smb2srv_reply(struct smb2srv_request *req); + +static void smb2srv_chain_reply(struct smb2srv_request *p_req) +{ + NTSTATUS status; + struct smb2srv_request *req; + uint32_t chain_offset; + uint32_t protocol_version; + uint16_t buffer_code; + uint32_t dynamic_size; + + chain_offset = p_req->chain_offset; + p_req->chain_offset = 0; + + if (p_req->in.size < (NBT_HDR_SIZE + chain_offset + SMB2_MIN_SIZE)) { + DEBUG(2,("Invalid SMB2 chained packet at offset 0x%X\n", + chain_offset)); + smbsrv_terminate_connection(p_req->smb_conn, "Invalid SMB2 chained packet"); + return; + } + + protocol_version = IVAL(p_req->in.buffer, NBT_HDR_SIZE + chain_offset); + if (protocol_version != SMB2_MAGIC) { + DEBUG(2,("Invalid SMB chained packet: protocol prefix: 0x%08X\n", + protocol_version)); + smbsrv_terminate_connection(p_req->smb_conn, "NON-SMB2 chained packet"); + return; + } + + req = smb2srv_init_request(p_req->smb_conn); + if (!req) { + smbsrv_terminate_connection(p_req->smb_conn, "SMB2 chained packet - no memory"); + return; + } + + req->in.buffer = talloc_steal(req, p_req->in.buffer); + req->in.size = p_req->in.size; + req->request_time = p_req->request_time; + req->in.allocated = req->in.size; + + req->in.hdr = req->in.buffer+ NBT_HDR_SIZE + chain_offset; + req->in.body = req->in.hdr + SMB2_HDR_BODY; + req->in.body_size = req->in.size - (NBT_HDR_SIZE+ chain_offset + SMB2_HDR_BODY); + req->in.dynamic = NULL; + + buffer_code = SVAL(req->in.body, 0); + req->in.body_fixed = (buffer_code & ~1); + dynamic_size = req->in.body_size - req->in.body_fixed; + + if (dynamic_size != 0 && (buffer_code & 1)) { + req->in.dynamic = req->in.body + req->in.body_fixed; + if (smb2_oob(&req->in, req->in.dynamic, dynamic_size)) { + DEBUG(1,("SMB2 chained request invalid dynamic size 0x%x\n", + dynamic_size)); + smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER); + return; + } + } + + if (p_req->chained_file_handle) { + memcpy(req->_chained_file_handle, + p_req->_chained_file_handle, + sizeof(req->_chained_file_handle)); + req->chained_file_handle = req->_chained_file_handle; + } + + /* + * TODO: - make sure the length field is 64 + * - make sure it's a request + */ + + status = smb2srv_reply(req); + if (!NT_STATUS_IS_OK(status)) { + smbsrv_terminate_connection(req->smb_conn, nt_errstr(status)); + talloc_free(req); + return; + } +} + void smb2srv_send_reply(struct smb2srv_request *req) { DATA_BLOB blob; @@ -140,6 +219,10 @@ void smb2srv_send_reply(struct smb2srv_request *req) if (!NT_STATUS_IS_OK(status)) { smbsrv_terminate_connection(req->smb_conn, nt_errstr(status)); } + if (req->chain_offset) { + smb2srv_chain_reply(req); + return; + } talloc_free(req); } @@ -174,10 +257,11 @@ static NTSTATUS smb2srv_reply(struct smb2srv_request *req) uint32_t tid; uint64_t uid; - opcode = SVAL(req->in.hdr, SMB2_HDR_OPCODE); - req->seqnum = BVAL(req->in.hdr, SMB2_HDR_SEQNUM); - tid = IVAL(req->in.hdr, SMB2_HDR_TID); - uid = BVAL(req->in.hdr, SMB2_HDR_UID); + opcode = SVAL(req->in.hdr, SMB2_HDR_OPCODE); + req->chain_offset = IVAL(req->in.hdr, SMB2_HDR_CHAIN_OFFSET); + req->seqnum = BVAL(req->in.hdr, SMB2_HDR_SEQNUM); + tid = IVAL(req->in.hdr, SMB2_HDR_TID); + uid = BVAL(req->in.hdr, SMB2_HDR_UID); req->session = smbsrv_session_find(req->smb_conn, uid, req->request_time); req->tcon = smbsrv_smb2_tcon_find(req->session, tid, req->request_time); @@ -311,7 +395,6 @@ NTSTATUS smbsrv_recv_smb2_request(void *private, DATA_BLOB blob) } protocol_version = IVAL(blob.data, NBT_HDR_SIZE); - if (protocol_version != SMB2_MAGIC) { DEBUG(2,("Invalid SMB packet: protocol prefix: 0x%08X\n", protocol_version)); diff --git a/source4/smb_server/smb2/smb2_server.h b/source4/smb_server/smb2/smb2_server.h index 353300681f..909a4228df 100644 --- a/source4/smb_server/smb2/smb2_server.h +++ b/source4/smb_server/smb2/smb2_server.h @@ -56,6 +56,13 @@ struct smb2srv_request { /* the id that can be used to cancel the request */ uint32_t pending_id; + /* the offset to the next SMB2 Header for chained requests */ + uint32_t chain_offset; + + /* chained file handle */ + uint8_t _chained_file_handle[16]; + uint8_t *chained_file_handle; + struct smb2_request_buffer in; struct smb2_request_buffer out; }; diff --git a/source4/smb_server/smb2/tcon.c b/source4/smb_server/smb2/tcon.c index a98110ab85..023ca9b0a4 100644 --- a/source4/smb_server/smb2/tcon.c +++ b/source4/smb_server/smb2/tcon.c @@ -45,6 +45,17 @@ struct ntvfs_handle *smb2srv_pull_handle(struct smb2srv_request *req, const uint uint32_t tid; uint32_t pad; + /* + * if there're chained requests used the cached handle + * + * TODO: check if this also correct when the given handle + * isn't all 0xFF. + */ + if (req->chained_file_handle) { + base = req->chained_file_handle; + offset = 0; + } + hid = BVAL(base, offset); tid = IVAL(base, offset + 8); pad = IVAL(base, offset + 12); -- cgit