smbd: remove dead code
[samba.git] / source3 / smbd / smb2_ioctl_network_fs.c
index 8341f2b327d6e8d87e641dfd81092ee4719f931b..8154fa3f92ed2ea7986bdf0642b1422743b2724a 100644 (file)
 #include "../libcli/smb/smb_common.h"
 #include "../libcli/security/security.h"
 #include "../lib/util/tevent_ntstatus.h"
-#include "../lib/ccan/build_assert/build_assert.h"
 #include "include/ntioctl.h"
 #include "../librpc/ndr/libndr.h"
 #include "librpc/gen_ndr/ndr_ioctl.h"
 #include "smb2_ioctl_private.h"
+#include "../lib/tsocket/tsocket.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_SMB2
 
-#define COPYCHUNK_MAX_CHUNKS   256             /* 2k8r2 & win8 = 256 */
-#define COPYCHUNK_MAX_CHUNK_LEN        1048576         /* 2k8r2 & win8 = 1048576 */
-#define COPYCHUNK_MAX_TOTAL_LEN        16777216        /* 2k8r2 & win8 = 16777216 */
 static void copychunk_pack_limits(struct srv_copychunk_rsp *cc_rsp)
 {
        cc_rsp->chunks_written = COPYCHUNK_MAX_CHUNKS;
@@ -46,16 +46,31 @@ static NTSTATUS copychunk_check_limits(struct srv_copychunk_copy *cc_copy)
        uint32_t i;
        uint32_t total_len = 0;
 
+       /*
+        * [MS-SMB2] 3.3.5.15.6 Handling a Server-Side Data Copy Request
+        * Send and invalid parameter response if:
+        * - The ChunkCount value is greater than
+        *   ServerSideCopyMaxNumberofChunks
+        */
        if (cc_copy->chunk_count > COPYCHUNK_MAX_CHUNKS) {
                return NT_STATUS_INVALID_PARAMETER;
        }
 
        for (i = 0; i < cc_copy->chunk_count; i++) {
-               if (cc_copy->chunks[i].length > COPYCHUNK_MAX_CHUNK_LEN) {
+               /*
+                * - The Length value in a single chunk is greater than
+                *   ServerSideCopyMaxChunkSize or equal to zero.
+                */
+               if ((cc_copy->chunks[i].length == 0)
+                || (cc_copy->chunks[i].length > COPYCHUNK_MAX_CHUNK_LEN)) {
                        return NT_STATUS_INVALID_PARAMETER;
                }
                total_len += cc_copy->chunks[i].length;
        }
+       /*
+        * - Sum of Lengths in all chunks is greater than
+        *   ServerSideCopyMaxDataSize
+        */
        if (total_len > COPYCHUNK_MAX_TOTAL_LEN) {
                return NT_STATUS_INVALID_PARAMETER;
        }
@@ -63,106 +78,17 @@ static NTSTATUS copychunk_check_limits(struct srv_copychunk_copy *cc_copy)
        return NT_STATUS_OK;
 }
 
-static void copychunk_unlock_all(struct files_struct *src_fsp,
-                                struct files_struct *dst_fsp,
-                                struct lock_struct *rd_locks,
-                                struct lock_struct *wr_locks,
-                                uint32_t num_locks)
-{
-
-       uint32_t i;
-
-       for (i = 0; i < num_locks; i++) {
-               SMB_VFS_STRICT_UNLOCK(src_fsp->conn, src_fsp, &rd_locks[i]);
-               SMB_VFS_STRICT_UNLOCK(dst_fsp->conn, dst_fsp, &wr_locks[i]);
-       }
-}
-
-/* request read and write locks for each chunk */
-static NTSTATUS copychunk_lock_all(TALLOC_CTX *mem_ctx,
-                                  struct srv_copychunk_copy *cc_copy,
-                                  struct files_struct *src_fsp,
-                                  struct files_struct *dst_fsp,
-                                  struct lock_struct **rd_locks,
-                                  struct lock_struct **wr_locks,
-                                  uint32_t *num_locks)
-{
-       NTSTATUS status;
-       uint32_t i;
-       struct lock_struct *rlocks;
-       struct lock_struct *wlocks;
-
-       rlocks = talloc_array(mem_ctx, struct lock_struct,
-                             cc_copy->chunk_count);
-       if (rlocks == NULL) {
-               status = NT_STATUS_NO_MEMORY;
-               goto err_out;
-       }
-
-       wlocks = talloc_array(mem_ctx, struct lock_struct,
-                             cc_copy->chunk_count);
-       if (wlocks == NULL) {
-               status = NT_STATUS_NO_MEMORY;
-               goto err_rlocks_free;
-       }
-
-       for (i = 0; i < cc_copy->chunk_count; i++) {
-               init_strict_lock_struct(src_fsp,
-                                       src_fsp->op->global->open_persistent_id,
-                                       cc_copy->chunks[i].source_off,
-                                       cc_copy->chunks[i].length,
-                                       READ_LOCK,
-                                       &rlocks[i]);
-               init_strict_lock_struct(dst_fsp,
-                                       dst_fsp->op->global->open_persistent_id,
-                                       cc_copy->chunks[i].target_off,
-                                       cc_copy->chunks[i].length,
-                                       WRITE_LOCK,
-                                       &wlocks[i]);
-
-               if (!SMB_VFS_STRICT_LOCK(src_fsp->conn, src_fsp, &rlocks[i])) {
-                       status = NT_STATUS_FILE_LOCK_CONFLICT;
-                       goto err_unlock;
-               }
-               if (!SMB_VFS_STRICT_LOCK(dst_fsp->conn, dst_fsp, &wlocks[i])) {
-                       /* unlock last rlock, otherwise missed by cleanup */
-                       SMB_VFS_STRICT_UNLOCK(src_fsp->conn, src_fsp,
-                                             &rlocks[i]);
-                       status = NT_STATUS_FILE_LOCK_CONFLICT;
-                       goto err_unlock;
-               }
-       }
-
-       *rd_locks = rlocks;
-       *wr_locks = wlocks;
-       *num_locks = i;
-
-       return NT_STATUS_OK;
-
-err_unlock:
-       if (i > 0) {
-               /* cleanup all locks successfully issued so far */
-               copychunk_unlock_all(src_fsp, dst_fsp, rlocks, wlocks, i);
-       }
-       talloc_free(wlocks);
-err_rlocks_free:
-       talloc_free(rlocks);
-err_out:
-       return status;
-}
-
 struct fsctl_srv_copychunk_state {
+       struct tevent_context *ev;
        struct connection_struct *conn;
-       uint32_t dispatch_count;
-       uint32_t recv_count;
-       uint32_t bad_recv_count;
+       struct srv_copychunk_copy cc_copy;
+       uint32_t current_chunk;
        NTSTATUS status;
        off_t total_written;
+       uint32_t ctl_code;
+       DATA_BLOB token;
        struct files_struct *src_fsp;
        struct files_struct *dst_fsp;
-       struct lock_struct *wlocks;
-       struct lock_struct *rlocks;
-       uint32_t num_locks;
        enum {
                COPYCHUNK_OUT_EMPTY = 0,
                COPYCHUNK_OUT_LIMITS,
@@ -171,178 +97,164 @@ struct fsctl_srv_copychunk_state {
 };
 static void fsctl_srv_copychunk_vfs_done(struct tevent_req *subreq);
 
+static NTSTATUS fsctl_srv_copychunk_loop(struct tevent_req *req);
+
 static struct tevent_req *fsctl_srv_copychunk_send(TALLOC_CTX *mem_ctx,
                                                   struct tevent_context *ev,
+                                                  uint32_t ctl_code,
                                                   struct files_struct *dst_fsp,
                                                   DATA_BLOB *in_input,
+                                                  size_t in_max_output,
                                                   struct smbd_smb2_request *smb2req)
 {
-       struct tevent_req *req;
-       struct srv_copychunk_copy cc_copy;
+       struct tevent_req *req = NULL;
+       struct fsctl_srv_copychunk_state *state = NULL;
        enum ndr_err_code ndr_ret;
-       uint64_t src_persistent_h;
-       uint64_t src_volatile_h;
-       int i;
-       struct srv_copychunk *chunk;
-       struct fsctl_srv_copychunk_state *state;
+       NTSTATUS status;
+
+       /* handler for both copy-chunk variants */
+       SMB_ASSERT((ctl_code == FSCTL_SRV_COPYCHUNK)
+               || (ctl_code == FSCTL_SRV_COPYCHUNK_WRITE));
 
        req = tevent_req_create(mem_ctx, &state,
                                struct fsctl_srv_copychunk_state);
        if (req == NULL) {
                return NULL;
        }
-       state->conn = dst_fsp->conn;
-       ndr_ret = ndr_pull_struct_blob(in_input, mem_ctx, &cc_copy,
-                       (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_copy);
-       if (ndr_ret != NDR_ERR_SUCCESS) {
-               DEBUG(0, ("failed to unmarshall copy chunk req\n"));
+       *state = (struct fsctl_srv_copychunk_state) {
+               .conn = dst_fsp->conn,
+               .ev = ev,
+               .ctl_code = ctl_code,
+               .dst_fsp = dst_fsp,
+       };
+
+       if (in_max_output < sizeof(struct srv_copychunk_rsp)) {
+               DEBUG(3, ("max output %d not large enough to hold copy chunk "
+                         "response %lu\n", (int)in_max_output,
+                         (unsigned long)sizeof(struct srv_copychunk_rsp)));
                state->status = NT_STATUS_INVALID_PARAMETER;
                tevent_req_nterror(req, state->status);
                return tevent_req_post(req, ev);
        }
 
-       /* persistent/volatile keys sent as the resume key */
-       src_persistent_h = BVAL(cc_copy.source_key, 0);
-       src_volatile_h = BVAL(cc_copy.source_key, 8);
-       state->src_fsp = file_fsp_get(smb2req, src_persistent_h, src_volatile_h);
-       if (state->src_fsp == NULL) {
-               DEBUG(3, ("invalid resume key in copy chunk req\n"));
-               state->status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
+       ndr_ret = ndr_pull_struct_blob(in_input, mem_ctx, &state->cc_copy,
+                       (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_copy);
+       if (ndr_ret != NDR_ERR_SUCCESS) {
+               DEBUG(0, ("failed to unmarshall copy chunk req\n"));
+               state->status = NT_STATUS_INVALID_PARAMETER;
                tevent_req_nterror(req, state->status);
                return tevent_req_post(req, ev);
        }
 
-       state->dst_fsp = dst_fsp;
-       /*
-        * [MS-SMB2] 3.3.5.15.6 Handling a Server-Side Data Copy Request
-        * If Open.GrantedAccess of the destination file does not
-        * include FILE_WRITE_DATA, then the request MUST be failed with
-        * STATUS_ACCESS_DENIED. If Open.GrantedAccess of the
-        * destination file does not include FILE_READ_DATA access and
-        * the CtlCode is FSCTL_SRV_COPYCHUNK, then the request MUST be
-        * failed with STATUS_ACCESS_DENIED.
-        */
-       if (!CHECK_WRITE(state->dst_fsp)) {
-               state->status = NT_STATUS_ACCESS_DENIED;
-               tevent_req_nterror(req, state->status);
-               return tevent_req_post(req, ev);
-       }
-       if (!CHECK_READ(state->dst_fsp, smb2req->smb1req)) {
-               state->status = NT_STATUS_ACCESS_DENIED;
-               tevent_req_nterror(req, state->status);
-               return tevent_req_post(req, ev);
-       }
-       if (!CHECK_READ(state->src_fsp, smb2req->smb1req)) {
-               state->status = NT_STATUS_ACCESS_DENIED;
-               tevent_req_nterror(req, state->status);
-               return tevent_req_post(req, ev);
-       }
+       state->token = data_blob_const(state->cc_copy.source_key,
+                                      sizeof(state->cc_copy.source_key));
 
-       state->status = copychunk_check_limits(&cc_copy);
-       if (tevent_req_nterror(req, state->status)) {
+       state->status = copychunk_check_limits(&state->cc_copy);
+       if (!NT_STATUS_IS_OK(state->status)) {
                DEBUG(3, ("copy chunk req exceeds limits\n"));
                state->out_data = COPYCHUNK_OUT_LIMITS;
+               tevent_req_nterror(req, state->status);
                return tevent_req_post(req, ev);
        }
 
        /* any errors from here onwards should carry copychunk response data */
        state->out_data = COPYCHUNK_OUT_RSP;
 
-       state->status = copychunk_lock_all(state,
-                                          &cc_copy,
-                                          state->src_fsp,
-                                          state->dst_fsp,
-                                          &state->rlocks,
-                                          &state->wlocks,
-                                          &state->num_locks);
-       if (tevent_req_nterror(req, state->status)) {
+       status = fsctl_srv_copychunk_loop(req);
+       if (tevent_req_nterror(req, status)) {
                return tevent_req_post(req, ev);
        }
 
-       for (i = 0; i < cc_copy.chunk_count; i++) {
-               struct tevent_req *vfs_subreq;
-               chunk = &cc_copy.chunks[i];
-               vfs_subreq = SMB_VFS_COPY_CHUNK_SEND(dst_fsp->conn,
-                                                    state, ev,
-                                                    state->src_fsp,
-                                                    chunk->source_off,
-                                                    state->dst_fsp,
-                                                    chunk->target_off,
-                                                    chunk->length);
-               if (vfs_subreq == NULL) {
-                       DEBUG(0, ("VFS copy chunk send failed\n"));
-                       state->status = NT_STATUS_NO_MEMORY;
-                       if (state->dispatch_count == 0) {
-                               /* nothing dispatched, return immediately */
-                               copychunk_unlock_all(state->src_fsp,
-                                                    state->dst_fsp,
-                                                    state->rlocks,
-                                                    state->wlocks,
-                                                    state->num_locks);
-                               tevent_req_nterror(req, state->status);
-                               return tevent_req_post(req, ev);
-                       } else {
-                               /*
-                                * wait for dispatched to complete before
-                                * returning error, locks held.
-                                */
-                               break;
-                       }
-               }
-               tevent_req_set_callback(vfs_subreq,
-                                       fsctl_srv_copychunk_vfs_done, req);
-               state->dispatch_count++;
+       return req;
+}
+
+static NTSTATUS fsctl_srv_copychunk_loop(struct tevent_req *req)
+{
+       struct fsctl_srv_copychunk_state *state = tevent_req_data(
+               req, struct fsctl_srv_copychunk_state);
+       struct tevent_req *subreq = NULL;
+       uint32_t length = 0;
+       off_t source_off = 0;
+       off_t target_off = 0;
+
+       /*
+        * chunk_count can be 0 which must either just do nothing returning
+        * success saying number of copied chunks is 0 (verified against
+        * Windows).
+        *
+        * Or it can be a special macOS copyfile request, so we send this into
+        * the VFS, vfs_fruit if loaded implements the macOS copyile semantics.
+        */
+       if (state->cc_copy.chunk_count > 0) {
+               struct srv_copychunk *chunk = NULL;
+
+               chunk = &state->cc_copy.chunks[state->current_chunk];
+               length = chunk->length;
+               source_off = chunk->source_off;
+               target_off = chunk->target_off;
+       }
+
+       subreq = SMB_VFS_OFFLOAD_WRITE_SEND(state->dst_fsp->conn,
+                                        state,
+                                        state->ev,
+                                        state->ctl_code,
+                                        &state->token,
+                                        source_off,
+                                        state->dst_fsp,
+                                        target_off,
+                                        length);
+       if (tevent_req_nomem(subreq, req)) {
+               return NT_STATUS_NO_MEMORY;
        }
+       tevent_req_set_callback(subreq, fsctl_srv_copychunk_vfs_done, req);
 
-       /* hold locks until all dispatched requests are completed */
-       return req;
+       return NT_STATUS_OK;
 }
 
 static void fsctl_srv_copychunk_vfs_done(struct tevent_req *subreq)
 {
        struct tevent_req *req = tevent_req_callback_data(
                subreq, struct tevent_req);
-       struct fsctl_srv_copychunk_state *state = tevent_req_data(req,
-                                       struct fsctl_srv_copychunk_state);
+       struct fsctl_srv_copychunk_state *state = tevent_req_data(
+               req, struct fsctl_srv_copychunk_state);
        off_t chunk_nwritten;
        NTSTATUS status;
 
-       state->recv_count++;
-       status = SMB_VFS_COPY_CHUNK_RECV(state->conn, subreq,
+       status = SMB_VFS_OFFLOAD_WRITE_RECV(state->conn, subreq,
                                         &chunk_nwritten);
        TALLOC_FREE(subreq);
-       if (NT_STATUS_IS_OK(status)) {
-               DEBUG(10, ("good copy chunk recv %d of %d\n",
-                          state->recv_count,
-                          state->dispatch_count));
-               state->total_written += chunk_nwritten;
-       } else {
-               DEBUG(0, ("bad status in copy chunk recv %d of %d: %s\n",
-                         state->recv_count,
-                         state->dispatch_count,
-                         nt_errstr(status)));
-               state->bad_recv_count++;
-               /* may overwrite previous failed status */
-               state->status = status;
-       }
-
-       if (state->recv_count != state->dispatch_count) {
+       if (!NT_STATUS_IS_OK(status)) {
+               DBG_ERR("copy chunk failed [%s] chunk [%u] of [%u]\n",
+                       nt_errstr(status),
+                       (unsigned int)state->current_chunk,
+                       (unsigned int)state->cc_copy.chunk_count);
+               tevent_req_nterror(req, status);
+               return;
+       }
+
+       DBG_DEBUG("good copy chunk [%u] of [%u]\n",
+                 (unsigned int)state->current_chunk,
+                 (unsigned int)state->cc_copy.chunk_count);
+       state->total_written += chunk_nwritten;
+
+       if (state->cc_copy.chunk_count == 0) {
                /*
-                * Wait for all VFS copy_chunk requests to complete, even
-                * if an error is received for a specific chunk.
+                * This must not produce an error but just return a chunk count
+                * of 0 in the response.
                 */
+               tevent_req_done(req);
                return;
        }
 
-       /* all VFS copy_chunk requests done */
-       copychunk_unlock_all(state->src_fsp,
-                            state->dst_fsp,
-                            state->rlocks,
-                            state->wlocks,
-                            state->num_locks);
-
-       if (!tevent_req_nterror(req, state->status)) {
+       state->current_chunk++;
+       if (state->current_chunk == state->cc_copy.chunk_count) {
                tevent_req_done(req);
+               return;
+       }
+
+       status = fsctl_srv_copychunk_loop(req);
+       if (tevent_req_nterror(req, status)) {
+               return;
        }
 }
 
@@ -364,7 +276,7 @@ static NTSTATUS fsctl_srv_copychunk_recv(struct tevent_req *req,
                *pack_rsp = true;
                break;
        case COPYCHUNK_OUT_RSP:
-               cc_rsp->chunks_written = state->recv_count - state->bad_recv_count;
+               cc_rsp->chunks_written = state->current_chunk;
                cc_rsp->chunk_bytes_written = 0;
                cc_rsp->total_bytes_written = state->total_written;
                *pack_rsp = true;
@@ -373,12 +285,122 @@ static NTSTATUS fsctl_srv_copychunk_recv(struct tevent_req *req,
                assert(1);
                break;
        }
-       status = state->status;
-       tevent_req_received(req);
-
+       status = tevent_req_simple_recv_ntstatus(req);
        return status;
 }
 
+static NTSTATUS fsctl_network_iface_info(TALLOC_CTX *mem_ctx,
+                                        struct tevent_context *ev,
+                                        struct smbXsrv_connection *xconn,
+                                        DATA_BLOB *in_input,
+                                        uint32_t in_max_output,
+                                        DATA_BLOB *out_output)
+{
+       struct fsctl_net_iface_info *array = NULL;
+       struct fsctl_net_iface_info *first = NULL;
+       struct fsctl_net_iface_info *last = NULL;
+       size_t i;
+       size_t num_ifaces = iface_count();
+       enum ndr_err_code ndr_err;
+
+       if (in_input->length != 0) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       *out_output = data_blob_null;
+
+       array = talloc_zero_array(mem_ctx,
+                                 struct fsctl_net_iface_info,
+                                 num_ifaces);
+       if (array == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       for (i=0; i < num_ifaces; i++) {
+               struct fsctl_net_iface_info *cur = &array[i];
+               const struct interface *iface = get_interface(i);
+               const struct sockaddr_storage *ifss = &iface->ip;
+               const void *ifptr = ifss;
+               const struct sockaddr *ifsa = (const struct sockaddr *)ifptr;
+               struct tsocket_address *a = NULL;
+               char *addr;
+               bool ok;
+               int ret;
+
+               ret = tsocket_address_bsd_from_sockaddr(array,
+                                       ifsa, sizeof(struct sockaddr_storage),
+                                       &a);
+               if (ret != 0) {
+                       return map_nt_error_from_unix_common(errno);
+               }
+
+               ok = tsocket_address_is_inet(a, "ip");
+               if (!ok) {
+                       continue;
+               }
+
+               addr = tsocket_address_inet_addr_string(a, array);
+               if (addr == NULL) {
+                       TALLOC_FREE(array);
+                       return NT_STATUS_NO_MEMORY;
+               }
+
+               cur->ifindex = iface->if_index;
+               if (cur->ifindex == 0) {
+                       /*
+                        * Did not get interface index from kernel,
+                        * nor from the config. ==> Apply a common
+                        * default value for these cases.
+                        */
+                       cur->ifindex = UINT32_MAX;
+               }
+               cur->capability = iface->capability;
+               cur->linkspeed = iface->linkspeed;
+               if (cur->linkspeed == 0) {
+                       DBG_DEBUG("Link speed 0 on interface [%s] - skipping "
+                                 "address [%s].\n", iface->name, addr);
+                       continue;
+               }
+
+               ok = tsocket_address_is_inet(a, "ipv4");
+               if (ok) {
+                       cur->sockaddr.family = FSCTL_NET_IFACE_AF_INET;
+                       cur->sockaddr.saddr.saddr_in.ipv4 = addr;
+               }
+               ok = tsocket_address_is_inet(a, "ipv6");
+               if (ok) {
+                       cur->sockaddr.family = FSCTL_NET_IFACE_AF_INET6;
+                       cur->sockaddr.saddr.saddr_in6.ipv6 = addr;
+               }
+
+               if (first == NULL) {
+                       first = cur;
+               }
+               if (last != NULL) {
+                       last->next = cur;
+               }
+               last = cur;
+       }
+
+       if (first == NULL) {
+               TALLOC_FREE(array);
+               return NT_STATUS_OK;
+       }
+
+       if (DEBUGLEVEL >= 10) {
+               NDR_PRINT_DEBUG(fsctl_net_iface_info, first);
+       }
+
+       ndr_err = ndr_push_struct_blob(out_output, mem_ctx, first,
+                       (ndr_push_flags_fn_t)ndr_push_fsctl_net_iface_info);
+       TALLOC_FREE(array);
+       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+               return ndr_map_error2ntstatus(ndr_err);
+       }
+
+       return NT_STATUS_OK;
+}
+
 static NTSTATUS fsctl_validate_neg_info(TALLOC_CTX *mem_ctx,
                                        struct tevent_context *ev,
                                        struct smbXsrv_connection *conn,
@@ -392,9 +414,27 @@ static NTSTATUS fsctl_validate_neg_info(TALLOC_CTX *mem_ctx,
        struct GUID in_guid;
        uint16_t in_security_mode;
        uint16_t in_num_dialects;
-       uint16_t i;
+       uint16_t dialect;
        DATA_BLOB out_guid_blob;
        NTSTATUS status;
+       enum protocol_types protocol = PROTOCOL_NONE;
+
+       if (lp_server_max_protocol() <= PROTOCOL_SMB2_02) {
+               /*
+                * With SMB 2.02 we didn't get the
+                * capabitities, client guid, security mode
+                * and dialects the client would have offered.
+                *
+                * So we behave compatible with a true
+                * SMB 2.02 server and return NT_STATUS_FILE_CLOSED.
+                *
+                * As SMB >= 2.10 offers the two phase SMB2 Negotiate
+                * we keep supporting FSCTL_VALIDATE_NEGOTIATE_INFO
+                * starting with SMB 2.10, while Windows only supports
+                * it starting with SMB > 2.10.
+                */
+               return NT_STATUS_FILE_CLOSED;
+       }
 
        if (in_input->length < 0x18) {
                return NT_STATUS_INVALID_PARAMETER;
@@ -418,21 +458,26 @@ static NTSTATUS fsctl_validate_neg_info(TALLOC_CTX *mem_ctx,
                return status;
        }
 
-       if (in_num_dialects != conn->smb2.client.num_dialects) {
+       /*
+        * From: [MS-SMB2]
+        * 3.3.5.15.12 Handling a Validate Negotiate Info Request
+        *
+        * The server MUST determine the greatest common dialect
+        * between the dialects it implements and the Dialects array
+        * of the VALIDATE_NEGOTIATE_INFO request. If no dialect is
+        * matched, or if the value is not equal to Connection.Dialect,
+        * the server MUST terminate the transport connection
+        * and free the Connection object.
+        */
+       protocol = smbd_smb2_protocol_dialect_match(in_input->data + 0x18,
+                                                   in_num_dialects,
+                                                   &dialect);
+       if (conn->protocol != protocol) {
                *disconnect = true;
                return NT_STATUS_ACCESS_DENIED;
        }
 
-       for (i=0; i < in_num_dialects; i++) {
-               uint16_t v = SVAL(in_input->data, 0x18 + i*2);
-
-               if (conn->smb2.client.dialects[i] != v) {
-                       *disconnect = true;
-                       return NT_STATUS_ACCESS_DENIED;
-               }
-       }
-
-       if (GUID_compare(&in_guid, &conn->smb2.client.guid) != 0) {
+       if (!GUID_equal(&in_guid, &conn->smb2.client.guid)) {
                *disconnect = true;
                return NT_STATUS_ACCESS_DENIED;
        }
@@ -460,48 +505,14 @@ static NTSTATUS fsctl_validate_neg_info(TALLOC_CTX *mem_ctx,
 
        SIVAL(out_output->data, 0x00, conn->smb2.server.capabilities);
        memcpy(out_output->data+0x04, out_guid_blob.data, 16);
-       SIVAL(out_output->data, 0x14, conn->smb2.server.security_mode);
-       SIVAL(out_output->data, 0x16, conn->smb2.server.dialect);
-
-       return NT_STATUS_OK;
-}
-
-static NTSTATUS fsctl_srv_req_resume_key(TALLOC_CTX *mem_ctx,
-                                        struct tevent_context *ev,
-                                        struct files_struct *fsp,
-                                        uint32_t in_max_output,
-                                        DATA_BLOB *out_output)
-{
-       struct req_resume_key_rsp rkey_rsp;
-       enum ndr_err_code ndr_ret;
-       DATA_BLOB output;
-
-       if (fsp == NULL) {
-               return NT_STATUS_FILE_CLOSED;
-       }
-
-       ZERO_STRUCT(rkey_rsp);
-       /* combine persistent and volatile handles for the resume key */
-       SBVAL(rkey_rsp.resume_key, 0, fsp->op->global->open_persistent_id);
-       SBVAL(rkey_rsp.resume_key, 8, fsp->op->global->open_volatile_id);
-
-       ndr_ret = ndr_push_struct_blob(&output, mem_ctx, &rkey_rsp,
-                       (ndr_push_flags_fn_t)ndr_push_req_resume_key_rsp);
-       if (ndr_ret != NDR_ERR_SUCCESS) {
-               return NT_STATUS_INTERNAL_ERROR;
-       }
-
-       if (in_max_output < output.length) {
-               DEBUG(1, ("max output %u too small for resume key rsp %ld\n",
-                         in_max_output, (long int)output.length));
-               return NT_STATUS_INVALID_PARAMETER;
-       }
-       *out_output = output;
+       SSVAL(out_output->data, 0x14, conn->smb2.server.security_mode);
+       SSVAL(out_output->data, 0x16, conn->smb2.server.dialect);
 
        return NT_STATUS_OK;
 }
 
 static void smb2_ioctl_network_fs_copychunk_done(struct tevent_req *subreq);
+static void smb2_ioctl_network_fs_offload_read_done(struct tevent_req *subreq);
 
 struct tevent_req *smb2_ioctl_network_fs(uint32_t ctl_code,
                                         struct tevent_context *ev,
@@ -512,9 +523,20 @@ struct tevent_req *smb2_ioctl_network_fs(uint32_t ctl_code,
        NTSTATUS status;
 
        switch (ctl_code) {
+       /*
+        * [MS-SMB2] 2.2.31
+        * FSCTL_SRV_COPYCHUNK is issued when a handle has
+        * FILE_READ_DATA and FILE_WRITE_DATA access to the file;
+        * FSCTL_SRV_COPYCHUNK_WRITE is issued when a handle only has
+        * FILE_WRITE_DATA access.
+        */
+       case FSCTL_SRV_COPYCHUNK_WRITE: /* FALL THROUGH */
        case FSCTL_SRV_COPYCHUNK:
-               subreq = fsctl_srv_copychunk_send(state, ev, state->fsp,
+               subreq = fsctl_srv_copychunk_send(state, ev,
+                                                 ctl_code,
+                                                 state->fsp,
                                                  &state->in_input,
+                                                 state->in_max_output,
                                                  state->smb2req);
                if (tevent_req_nomem(subreq, req)) {
                        return tevent_req_post(req, ev);
@@ -524,9 +546,32 @@ struct tevent_req *smb2_ioctl_network_fs(uint32_t ctl_code,
                                        req);
                return req;
                break;
+       case FSCTL_QUERY_NETWORK_INTERFACE_INFO:
+               if (!state->smbreq->xconn->client->server_multi_channel_enabled)
+               {
+                       if (IS_IPC(state->smbreq->conn)) {
+                               status = NT_STATUS_FS_DRIVER_REQUIRED;
+                       } else {
+                               status = NT_STATUS_INVALID_DEVICE_REQUEST;
+                       }
+
+                       tevent_req_nterror(req, status);
+                       return tevent_req_post(req, ev);
+               }
+
+               status = fsctl_network_iface_info(state, ev,
+                                                 state->smbreq->xconn,
+                                                 &state->in_input,
+                                                 state->in_max_output,
+                                                 &state->out_output);
+               if (!tevent_req_nterror(req, status)) {
+                       tevent_req_done(req);
+               }
+               return tevent_req_post(req, ev);
+               break;
        case FSCTL_VALIDATE_NEGOTIATE_INFO:
                status = fsctl_validate_neg_info(state, ev,
-                                                state->smbreq->sconn->conn,
+                                                state->smbreq->xconn,
                                                 &state->in_input,
                                                 state->in_max_output,
                                                 &state->out_output,
@@ -537,14 +582,18 @@ struct tevent_req *smb2_ioctl_network_fs(uint32_t ctl_code,
                return tevent_req_post(req, ev);
                break;
        case FSCTL_SRV_REQUEST_RESUME_KEY:
-               status = fsctl_srv_req_resume_key(state, ev, state->fsp,
-                                                 state->in_max_output,
-                                                 &state->out_output);
-               if (!tevent_req_nterror(req, status)) {
-                       tevent_req_done(req);
+               subreq = SMB_VFS_OFFLOAD_READ_SEND(state,
+                                                  ev,
+                                                  state->fsp,
+                                                  FSCTL_SRV_REQUEST_RESUME_KEY,
+                                                  0, 0, 0);
+               if (tevent_req_nomem(subreq, req)) {
+                       return tevent_req_post(req, ev);
                }
-               return tevent_req_post(req, ev);
-               break;
+               tevent_req_set_callback(
+                       subreq, smb2_ioctl_network_fs_offload_read_done, req);
+               return req;
+
        default: {
                uint8_t *out_data = NULL;
                uint32_t out_data_len = 0;
@@ -614,3 +663,42 @@ static void smb2_ioctl_network_fs_copychunk_done(struct tevent_req *subreq)
                tevent_req_done(req);
        }
 }
+
+static void smb2_ioctl_network_fs_offload_read_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct smbd_smb2_ioctl_state *state = tevent_req_data(
+               req, struct smbd_smb2_ioctl_state);
+       struct req_resume_key_rsp rkey_rsp;
+       enum ndr_err_code ndr_ret;
+       DATA_BLOB token;
+       NTSTATUS status;
+
+       status = SMB_VFS_OFFLOAD_READ_RECV(subreq,
+                                          state->fsp->conn,
+                                          state,
+                                          &token);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+
+       if (token.length != sizeof(rkey_rsp.resume_key)) {
+               tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+               return;
+       }
+
+       ZERO_STRUCT(rkey_rsp);
+       memcpy(rkey_rsp.resume_key, token.data, token.length);
+
+       ndr_ret = ndr_push_struct_blob(&state->out_output, state, &rkey_rsp,
+                       (ndr_push_flags_fn_t)ndr_push_req_resume_key_rsp);
+       if (ndr_ret != NDR_ERR_SUCCESS) {
+               tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+               return;
+       }
+
+       tevent_req_done(req);
+       return;
+}