s4:smb2: Add lease break support for SMB2.1
authorZach Loafman <zachary.loafman@isilon.com>
Mon, 30 Mar 2009 22:57:57 +0000 (15:57 -0700)
committerTim Prouty <tprouty@samba.org>
Wed, 1 Apr 2009 15:30:25 +0000 (08:30 -0700)
Add the structures and marshalling for the lease break variants of the
oplock break / oplock break ack messages.

source4/libcli/raw/interfaces.h
source4/libcli/smb2/config.mk
source4/libcli/smb2/create.c
source4/libcli/smb2/lease_break.c [new file with mode: 0644]
source4/libcli/smb2/smb2.h
source4/libcli/smb2/transport.c

index bd93fa1695d4039950ad4d768b6323ddbc2f0d1a..3c0d186b877dbd6f3340526fe750b735df61f841 100644 (file)
@@ -56,13 +56,26 @@ struct smb2_handle {
 /*
   SMB2 lease structure (per MS-SMB2 2.2.13)
 */
+struct smb2_lease_key {
+       uint64_t data[2];
+};
+
 struct smb2_lease {
-       uint64_t lease_key[2];
+       struct smb2_lease_key lease_key;
        uint32_t lease_state;
        uint32_t lease_flags; /* should be 0 */
        uint64_t lease_duration; /* should be 0 */
 };
 
+struct smb2_lease_break {
+       struct smb2_lease current_lease;
+       uint32_t break_flags;
+       uint32_t new_lease_state;
+       uint32_t break_reason; /* should be 0 */
+       uint32_t access_mask_hint; /* should be 0 */
+       uint32_t share_mask_hint; /* should be 0 */
+};
+
 struct ntvfs_handle;
 
 /*
@@ -2006,6 +2019,14 @@ union smb_lock {
                        /* struct smb2_handle handle; */
                } in, out;
        } smb2_break;
+
+       /* SMB2 Lease Break Ack (same opcode as smb2_break) */
+       struct smb2_lease_break_ack {
+               struct {
+                       uint32_t reserved;
+                       struct smb2_lease lease;
+               } in, out;
+       } smb2_lease_break_ack;
 };
 
 
index 322bca141694d60395387659d693aaa1b523b05a..ddd45c965f236a252557daec41bd108eba32b826 100644 (file)
@@ -5,6 +5,7 @@ LIBCLI_SMB2_OBJ_FILES = $(addprefix $(libclisrcdir)/smb2/, \
        transport.o request.o negprot.o session.o tcon.o \
        create.o close.o connect.o getinfo.o write.o read.o \
        setinfo.o find.o ioctl.o logoff.o tdis.o flush.o \
-       lock.o notify.o cancel.o keepalive.o break.o util.o signing.o)
+       lock.o notify.o cancel.o keepalive.o break.o util.o signing.o \
+       lease_break.o)
 
 $(eval $(call proto_header_template,$(libclisrcdir)/smb2/smb2_proto.h,$(LIBCLI_SMB2_OBJ_FILES:.o=.c)))
index 344be60f6e233b066e505c5cb9ba326168ff91a9..363210bd03811b51f60ccef8be67396f6e33e89a 100644 (file)
@@ -315,7 +315,7 @@ struct smb2_request *smb2_create_send(struct smb2_tree *tree, struct smb2_create
        if (io->in.lease_request) {
                uint8_t data[32];
 
-               memcpy(&data[0], io->in.lease_request->lease_key, 16);
+               memcpy(&data[0], &io->in.lease_request->lease_key, 16);
                SIVAL(data, 16, io->in.lease_request->lease_state);
                SIVAL(data, 20, io->in.lease_request->lease_flags);
                SBVAL(data, 24, io->in.lease_request->lease_duration);
@@ -427,7 +427,7 @@ NTSTATUS smb2_create_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx, struct
                        }
 
                        data = io->out.blobs.blobs[i].data.data;
-                       memcpy(io->out.lease_response.lease_key, data, 16);
+                       memcpy(&io->out.lease_response.lease_key, data, 16);
                        io->out.lease_response.lease_state = IVAL(data, 16);
                        io->out.lease_response.lease_flags = IVAL(data, 20);
                        io->out.lease_response.lease_duration = BVAL(data, 24);
diff --git a/source4/libcli/smb2/lease_break.c b/source4/libcli/smb2/lease_break.c
new file mode 100644 (file)
index 0000000..c238f1d
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+   Unix SMB/CIFS implementation.
+
+   SMB2 client oplock break handling
+
+   Copyright (C) Zachary Loafman 2009
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/*
+  Send a Lease Break Acknowledgement
+*/
+struct smb2_request *smb2_lease_break_ack_send(struct smb2_tree *tree,
+                                               struct smb2_lease_break_ack *io)
+{
+       struct smb2_request *req;
+
+       req = smb2_request_init_tree(tree, SMB2_OP_BREAK, 0x24, false, 0);
+       if (req == NULL) return NULL;
+
+       SIVAL(req->out.body, 0x02, io->in.reserved);
+       SIVAL(req->out.body, 0x04, io->in.lease.lease_flags);
+       memcpy(req->out.body+0x8, &io->in.lease.lease_key,
+           sizeof(struct smb2_lease_key));
+       SIVAL(req->out.body, 0x18, io->in.lease.lease_state);
+       SBVAL(req->out.body, 0x1C, io->in.lease.lease_duration);
+
+       smb2_transport_send(req);
+
+       return req;
+}
+
+
+/*
+  Receive a Lease Break Response
+*/
+NTSTATUS smb2_lease_break_ack_recv(struct smb2_request *req,
+                                   struct smb2_lease_break_ack *io)
+{
+       if (!smb2_request_receive(req) ||
+           !smb2_request_is_ok(req)) {
+               return smb2_request_destroy(req);
+       }
+
+       SMB2_CHECK_PACKET_RECV(req, 0x24, false);
+
+       io->out.reserved                = IVAL(req->in.body, 0x02);
+       io->out.lease.lease_flags       = IVAL(req->in.body, 0x04);
+       memcpy(&io->out.lease.lease_key, req->in.body+0x8,
+           sizeof(struct smb2_lease_key));
+       io->out.lease.lease_state       = IVAL(req->in.body, 0x18);
+       io->out.lease.lease_duration    = IVAL(req->in.body, 0x1C);
+
+       return smb2_request_destroy(req);
+}
+
+/*
+  sync flush request
+*/
+NTSTATUS smb2_lease_break_ack(struct smb2_tree *tree,
+                              struct smb2_lease_break_ack *io)
+{
+       struct smb2_request *req = smb2_lease_break_ack_send(tree, io);
+       return smb2_lease_break_ack_recv(req, io);
+}
index fd961ce5f3988241d82749be9edfb70967e76feb..3044623ae8d3a4e9a1668946f25dc8638afb5981 100644 (file)
@@ -26,6 +26,7 @@
 #include "libcli/raw/libcliraw.h"
 
 struct smb2_handle;
+struct smb2_lease_break;
 
 /*
   information returned from the negotiate process
@@ -73,6 +74,15 @@ struct smb2_transport {
                void *private_data;
        } oplock;
 
+       struct {
+               /* a lease break request handler */
+               bool (*handler)(struct smb2_transport *transport,
+                               const struct smb2_lease_break *lease_break,
+                               void *private_data);
+               /* private data passed to the oplock handler */
+               void *private_data;
+       } lease;
+
        struct smbcli_options options;
 
        bool signing_required;
@@ -271,6 +281,9 @@ struct smb2_request {
 #define SMB2_LEASE_HANDLE                                0x02
 #define SMB2_LEASE_WRITE                                 0x04
 
+/* SMB2 lease break flags */
+#define SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED        0x01
+
 /* SMB2 impersonation levels */
 #define SMB2_IMPERSONATION_ANONYMOUS                     0x00
 #define SMB2_IMPERSONATION_IDENTIFICATION                0x01
index e112544c62159354d308343a6738e6033b9350f3..6a87d124d9a8322adbfa1fce8aa23560eb3868db 100644 (file)
@@ -144,24 +144,39 @@ static NTSTATUS smb2_handle_oplock_break(struct smb2_transport *transport,
                                         const DATA_BLOB *blob)
 {
        uint8_t *hdr;
-       uint16_t opcode;
+       uint8_t *body;
+       uint16_t len, bloblen;
+       bool lease;
 
        hdr = blob->data+NBT_HDR_SIZE;
+       body = hdr+SMB2_HDR_BODY;
+       bloblen = blob->length - SMB2_HDR_BODY;
 
-       if (blob->length < (SMB2_MIN_SIZE+0x18)) {
+       if (bloblen < 2) {
                DEBUG(1,("Discarding smb2 oplock reply of size %u\n",
-                        (unsigned)blob->length));
+                       (unsigned)blob->length));
                return NT_STATUS_INVALID_NETWORK_RESPONSE;
        }
 
-       opcode  = SVAL(hdr, SMB2_HDR_OPCODE);
+       len = CVAL(body, 0x00);
+       if (len > bloblen) {
+               DEBUG(1,("Discarding smb2 oplock reply,"
+                       "packet claims %u byte body, only %u bytes seen\n",
+                       len, bloblen));
+               return NT_STATUS_INVALID_NETWORK_RESPONSE;
+       }
 
-       if (opcode != SMB2_OP_BREAK) {
+       if (len == 24) {
+               lease = false;
+       } else if (len == 44) {
+               lease = true;
+       } else {
+               DEBUG(1,("Discarding smb2 oplock reply of invalid size %u\n",
+                       (unsigned)blob->length));
                return NT_STATUS_INVALID_NETWORK_RESPONSE;
        }
 
-       if (transport->oplock.handler) {
-               uint8_t *body = hdr+SMB2_HDR_BODY;
+       if (!lease && transport->oplock.handler) {
                struct smb2_handle h;
                uint8_t level;
 
@@ -170,8 +185,24 @@ static NTSTATUS smb2_handle_oplock_break(struct smb2_transport *transport,
 
                transport->oplock.handler(transport, &h, level,
                                          transport->oplock.private_data);
+       } else if (lease && transport->lease.handler) {
+               struct smb2_lease_break lb;
+
+               ZERO_STRUCT(lb);
+               lb.break_flags =                SVAL(body, 0x4);
+               memcpy(&lb.current_lease.lease_key, body+0x8,
+                   sizeof(struct smb2_lease_key));
+               lb.current_lease.lease_state =  SVAL(body, 0x18);
+               lb.new_lease_state =            SVAL(body, 0x1C);
+               lb.break_reason =               SVAL(body, 0x20);
+               lb.access_mask_hint =           SVAL(body, 0x24);
+               lb.share_mask_hint =            SVAL(body, 0x28);
+
+               transport->lease.handler(transport, &lb,
+                   transport->lease.private_data);
        } else {
-               DEBUG(5,("Got SMB2 oplock break with no handler\n"));
+               DEBUG(5,("Got SMB2 %s break with no handler\n",
+                       lease ? "lease" : "oplock"));
        }
 
        return NT_STATUS_OK;
@@ -193,6 +224,7 @@ static NTSTATUS smb2_transport_finish_recv(void *private_data, DATA_BLOB blob)
        uint16_t buffer_code;
        uint32_t dynamic_size;
        uint32_t i;
+       uint16_t opcode;
        NTSTATUS status;
 
        buffer = blob.data;
@@ -207,9 +239,16 @@ static NTSTATUS smb2_transport_finish_recv(void *private_data, DATA_BLOB blob)
 
        flags   = IVAL(hdr, SMB2_HDR_FLAGS);
        seqnum  = BVAL(hdr, SMB2_HDR_MESSAGE_ID);
+       opcode  = SVAL(hdr, SMB2_HDR_OPCODE);
 
        /* see MS-SMB2 3.2.5.19 */
        if (seqnum == UINT64_MAX) {
+               if (opcode != SMB2_OP_BREAK) {
+                       DEBUG(1,("Discarding packet with invalid seqnum, "
+                               "opcode %u\n", opcode));
+                       return NT_STATUS_INVALID_NETWORK_RESPONSE;
+               }
+
                return smb2_handle_oplock_break(transport, &blob);
        }