libcli: smb: Add smb2cli_tcon_set_id().
[sfrench/samba-autobuild/.git] / libcli / smb / smbXcli_base.c
index f8d4df3c14ffee2f217060043ed4c55e095d29a9..cc8978975e10b031f2c8a6c0afd7e17c75ce8bea 100644 (file)
@@ -34,6 +34,9 @@
 #include "librpc/ndr/libndr.h"
 #include "libcli/smb/smb2_negotiate_context.h"
 #include "lib/crypto/sha512.h"
+#include "lib/crypto/aes.h"
+#include "lib/crypto/aes_ccm_128.h"
+#include "lib/crypto/aes_gcm_128.h"
 
 struct smbXcli_conn;
 struct smbXcli_req;
@@ -41,8 +44,7 @@ struct smbXcli_session;
 struct smbXcli_tcon;
 
 struct smbXcli_conn {
-       int read_fd;
-       int write_fd;
+       int sock_fd;
        struct sockaddr_storage local_ss;
        struct sockaddr_storage remote_ss;
        const char *remote_name;
@@ -50,6 +52,7 @@ struct smbXcli_conn {
        struct tevent_queue *outgoing;
        struct tevent_req **pending;
        struct tevent_req *read_smb_req;
+       struct tevent_req *suicide_req;
 
        enum protocol_types min_protocol;
        enum protocol_types max_protocol;
@@ -130,6 +133,11 @@ struct smbXcli_conn {
                uint16_t cur_credits;
                uint16_t max_credits;
 
+               uint32_t cc_chunk_len;
+               uint32_t cc_max_chunks;
+
+               uint8_t io_priority;
+
                uint8_t preauth_sha512[64];
        } smb2;
 
@@ -145,6 +153,8 @@ struct smb2cli_session {
        bool should_encrypt;
        DATA_BLOB encryption_key;
        DATA_BLOB decryption_key;
+       uint64_t nonce_high_random;
+       uint64_t nonce_high_max;
        uint64_t nonce_high;
        uint64_t nonce_low;
        uint16_t channel_sequence;
@@ -157,6 +167,7 @@ struct smbXcli_session {
 
        struct {
                uint16_t session_id;
+               uint16_t action;
                DATA_BLOB application_key;
                bool protected_key;
        } smb1;
@@ -212,6 +223,8 @@ struct smbXcli_req_state {
 
        uint8_t *inbuf;
 
+       struct tevent_req *write_req;
+
        struct {
                /* Space for the header including the wct */
                uint8_t hdr[HDR_VWV];
@@ -274,6 +287,9 @@ struct smbXcli_req_state {
                bool signing_skipped;
                bool notify_async;
                bool got_async;
+               uint16_t cancel_flags;
+               uint64_t cancel_mid;
+               uint64_t cancel_aid;
        } smb2;
 };
 
@@ -315,18 +331,13 @@ struct smbXcli_conn *smbXcli_conn_create(TALLOC_CTX *mem_ctx,
                return NULL;
        }
 
-       conn->read_fd = fd;
-       conn->write_fd = dup(fd);
-       if (conn->write_fd == -1) {
-               goto error;
-       }
+       conn->sock_fd = fd;
 
        conn->remote_name = talloc_strdup(conn, remote_name);
        if (conn->remote_name == NULL) {
                goto error;
        }
 
-
        ss = (void *)&conn->local_ss;
        sa = (struct sockaddr *)ss;
        sa_length = sizeof(conn->local_ss);
@@ -366,6 +377,13 @@ struct smbXcli_conn *smbXcli_conn_create(TALLOC_CTX *mem_ctx,
                conn->desire_signing = false;
                conn->mandatory_signing = false;
                break;
+       case SMB_SIGNING_DESIRED:
+               /* if the server desires it */
+               conn->allow_signing = true;
+               conn->desire_signing = true;
+               conn->mandatory_signing = false;
+               break;
+       case SMB_SIGNING_IPC_DEFAULT:
        case SMB_SIGNING_REQUIRED:
                /* always */
                conn->allow_signing = true;
@@ -402,14 +420,19 @@ struct smbXcli_conn *smbXcli_conn_create(TALLOC_CTX *mem_ctx,
 
        conn->smb2.cur_credits = 1;
        conn->smb2.max_credits = 0;
+       conn->smb2.io_priority = 1;
+
+       /*
+        * Samba and Windows servers accept a maximum of 16 MiB with a maximum
+        * chunk length of 1 MiB.
+        */
+       conn->smb2.cc_chunk_len = 1024 * 1024;
+       conn->smb2.cc_max_chunks = 16;
 
        talloc_set_destructor(conn, smbXcli_conn_destructor);
        return conn;
 
  error:
-       if (conn->write_fd != -1) {
-               close(conn->write_fd);
-       }
        TALLOC_FREE(conn);
        return NULL;
 }
@@ -420,7 +443,7 @@ bool smbXcli_conn_is_connected(struct smbXcli_conn *conn)
                return false;
        }
 
-       if (conn->read_fd == -1) {
+       if (conn->sock_fd == -1) {
                return false;
        }
 
@@ -445,9 +468,26 @@ bool smbXcli_conn_use_unicode(struct smbXcli_conn *conn)
        return false;
 }
 
+/*
+ * [MS-SMB] 2.2.2.3.5 - SMB1 support for passing through
+ * query/set commands to the file system
+ */
+bool smbXcli_conn_support_passthrough(struct smbXcli_conn *conn)
+{
+       if (conn->protocol >= PROTOCOL_SMB2_02) {
+               return true;
+       }
+
+       if (conn->smb1.capabilities & CAP_W2K_SMBS) {
+               return true;
+       }
+
+       return false;
+}
+
 void smbXcli_conn_set_sockopt(struct smbXcli_conn *conn, const char *options)
 {
-       set_socket_options(conn->read_fd, options);
+       set_socket_options(conn->sock_fd, options);
 }
 
 const struct sockaddr_storage *smbXcli_conn_local_sockaddr(struct smbXcli_conn *conn)
@@ -508,8 +548,11 @@ struct smbXcli_conn_samba_suicide_state {
        struct smbXcli_conn *conn;
        struct iovec iov;
        uint8_t buf[9];
+       struct tevent_req *write_req;
 };
 
+static void smbXcli_conn_samba_suicide_cleanup(struct tevent_req *req,
+                                              enum tevent_req_state req_state);
 static void smbXcli_conn_samba_suicide_done(struct tevent_req *subreq);
 
 struct tevent_req *smbXcli_conn_samba_suicide_send(TALLOC_CTX *mem_ctx,
@@ -530,18 +573,53 @@ struct tevent_req *smbXcli_conn_samba_suicide_send(TALLOC_CTX *mem_ctx,
        SCVAL(state->buf, 8, exitcode);
        _smb_setlen_nbt(state->buf, sizeof(state->buf)-4);
 
+       if (conn->suicide_req != NULL) {
+               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+               return tevent_req_post(req, ev);
+       }
+
        state->iov.iov_base = state->buf;
        state->iov.iov_len = sizeof(state->buf);
 
-       subreq = writev_send(state, ev, conn->outgoing, conn->write_fd,
+       subreq = writev_send(state, ev, conn->outgoing, conn->sock_fd,
                             false, &state->iov, 1);
        if (tevent_req_nomem(subreq, req)) {
                return tevent_req_post(req, ev);
        }
        tevent_req_set_callback(subreq, smbXcli_conn_samba_suicide_done, req);
+       state->write_req = subreq;
+
+       tevent_req_set_cleanup_fn(req, smbXcli_conn_samba_suicide_cleanup);
+
+       /*
+        * We need to use tevent_req_defer_callback()
+        * in order to allow smbXcli_conn_disconnect()
+        * to do a safe cleanup.
+        */
+       tevent_req_defer_callback(req, ev);
+       conn->suicide_req = req;
+
        return req;
 }
 
+static void smbXcli_conn_samba_suicide_cleanup(struct tevent_req *req,
+                                              enum tevent_req_state req_state)
+{
+       struct smbXcli_conn_samba_suicide_state *state = tevent_req_data(
+               req, struct smbXcli_conn_samba_suicide_state);
+
+       TALLOC_FREE(state->write_req);
+
+       if (state->conn == NULL) {
+               return;
+       }
+
+       if (state->conn->suicide_req == req) {
+               state->conn->suicide_req = NULL;
+       }
+       state->conn = NULL;
+}
+
 static void smbXcli_conn_samba_suicide_done(struct tevent_req *subreq)
 {
        struct tevent_req *req = tevent_req_callback_data(
@@ -551,9 +629,12 @@ static void smbXcli_conn_samba_suicide_done(struct tevent_req *subreq)
        ssize_t nwritten;
        int err;
 
+       state->write_req = NULL;
+
        nwritten = writev_recv(subreq, &err);
        TALLOC_FREE(subreq);
        if (nwritten == -1) {
+               /* here, we need to notify all pending requests */
                NTSTATUS status = map_nt_error_from_unix_common(err);
                smbXcli_conn_disconnect(state->conn, status);
                return;
@@ -590,9 +671,8 @@ NTSTATUS smbXcli_conn_samba_suicide(struct smbXcli_conn *conn,
        if (req == NULL) {
                goto fail;
        }
-       ok = tevent_req_poll(req, ev);
+       ok = tevent_req_poll_ntstatus(req, ev, &status);
        if (!ok) {
-               status = map_nt_error_from_unix_common(errno);
                goto fail;
        }
        status = smbXcli_conn_samba_suicide_recv(req);
@@ -613,14 +693,9 @@ uint32_t smb1cli_conn_max_xmit(struct smbXcli_conn *conn)
 
 bool smb1cli_conn_req_possible(struct smbXcli_conn *conn)
 {
-       size_t pending;
+       size_t pending = talloc_array_length(conn->pending);
        uint16_t possible = conn->smb1.server.max_mux;
 
-       pending = tevent_queue_length(conn->outgoing);
-       if (pending >= possible) {
-               return false;
-       }
-       pending += talloc_array_length(conn->pending);
        if (pending >= possible) {
                return false;
        }
@@ -781,6 +856,70 @@ static uint16_t smb1cli_alloc_mid(struct smbXcli_conn *conn)
        }
 }
 
+static NTSTATUS smbXcli_req_cancel_write_req(struct tevent_req *req)
+{
+       struct smbXcli_req_state *state =
+               tevent_req_data(req,
+               struct smbXcli_req_state);
+       struct smbXcli_conn *conn = state->conn;
+       size_t num_pending = talloc_array_length(conn->pending);
+       ssize_t ret;
+       int err;
+       bool ok;
+
+       if (state->write_req == NULL) {
+               return NT_STATUS_OK;
+       }
+
+       /*
+        * Check if it's possible to cancel the request.
+        * If the result is true it's not to late.
+        * See writev_cancel().
+        */
+       ok = tevent_req_cancel(state->write_req);
+       if (ok) {
+               TALLOC_FREE(state->write_req);
+
+               if (conn->protocol >= PROTOCOL_SMB2_02) {
+                       /*
+                        * SMB2 has a sane signing state.
+                        */
+                       return NT_STATUS_OK;
+               }
+
+               if (num_pending > 1) {
+                       /*
+                        * We have more pending requests following us.  This
+                        * means the signing state will be broken for them.
+                        *
+                        * As a solution we could add the requests directly to
+                        * our outgoing queue and do the signing in the trigger
+                        * function and then use writev_send() without passing a
+                        * queue.  That way we'll only sign packets we're most
+                        * likely send to the wire.
+                        */
+                       return NT_STATUS_REQUEST_OUT_OF_SEQUENCE;
+               }
+
+               /*
+                * If we're the only request that's
+                * pending, we're able to recover the signing
+                * state.
+                */
+               smb_signing_cancel_reply(conn->smb1.signing,
+                                        state->smb1.one_way_seqnum);
+               return NT_STATUS_OK;
+       }
+
+       ret = writev_recv(state->write_req, &err);
+       TALLOC_FREE(state->write_req);
+       if (ret == -1) {
+               return map_nt_error_from_unix_common(err);
+       }
+
+       return NT_STATUS_OK;
+}
+
 void smbXcli_req_unset_pending(struct tevent_req *req)
 {
        struct smbXcli_req_state *state =
@@ -789,12 +928,23 @@ void smbXcli_req_unset_pending(struct tevent_req *req)
        struct smbXcli_conn *conn = state->conn;
        size_t num_pending = talloc_array_length(conn->pending);
        size_t i;
+       NTSTATUS cancel_status;
+
+       cancel_status = smbXcli_req_cancel_write_req(req);
 
        if (state->smb1.mid != 0) {
                /*
                 * This is a [nt]trans[2] request which waits
                 * for more than one reply.
                 */
+               if (!NT_STATUS_IS_OK(cancel_status)) {
+                       /*
+                        * If the write_req cancel didn't work
+                        * we can't use the connection anymore.
+                        */
+                       smbXcli_conn_disconnect(conn, cancel_status);
+                       return;
+               }
                return;
        }
 
@@ -806,8 +956,18 @@ void smbXcli_req_unset_pending(struct tevent_req *req)
                 * conn->pending. So if nothing is pending anymore, we need to
                 * delete the socket read fde.
                 */
+               /* TODO: smbXcli_conn_cancel_read_req */
                TALLOC_FREE(conn->pending);
                conn->read_smb_req = NULL;
+
+               if (!NT_STATUS_IS_OK(cancel_status)) {
+                       /*
+                        * If the write_req cancel didn't work
+                        * we can't use the connection anymore.
+                        */
+                       smbXcli_conn_disconnect(conn, cancel_status);
+                       return;
+               }
                return;
        }
 
@@ -822,6 +982,15 @@ void smbXcli_req_unset_pending(struct tevent_req *req)
                 * right thing nevertheless, the point of this routine is to
                 * remove ourselves from conn->pending.
                 */
+
+               if (!NT_STATUS_IS_OK(cancel_status)) {
+                       /*
+                        * If the write_req cancel didn't work
+                        * we can't use the connection anymore.
+                        */
+                       smbXcli_conn_disconnect(conn, cancel_status);
+                       return;
+               }
                return;
        }
 
@@ -838,6 +1007,15 @@ void smbXcli_req_unset_pending(struct tevent_req *req)
         */
        conn->pending = talloc_realloc(NULL, conn->pending, struct tevent_req *,
                                       num_pending - 1);
+
+       if (!NT_STATUS_IS_OK(cancel_status)) {
+               /*
+                * If the write_req cancel didn't work
+                * we can't use the connection anymore.
+                */
+               smbXcli_conn_disconnect(conn, cancel_status);
+               return;
+       }
        return;
 }
 
@@ -847,17 +1025,31 @@ static void smbXcli_req_cleanup(struct tevent_req *req,
        struct smbXcli_req_state *state =
                tevent_req_data(req,
                struct smbXcli_req_state);
+       struct smbXcli_conn *conn = state->conn;
+       NTSTATUS cancel_status;
 
        switch (req_state) {
        case TEVENT_REQ_RECEIVED:
                /*
                 * Make sure we really remove it from
                 * the pending array on destruction.
+                *
+                * smbXcli_req_unset_pending() calls
+                * smbXcli_req_cancel_write_req() internal
                 */
                state->smb1.mid = 0;
                smbXcli_req_unset_pending(req);
                return;
        default:
+               cancel_status = smbXcli_req_cancel_write_req(req);
+               if (!NT_STATUS_IS_OK(cancel_status)) {
+                       /*
+                        * If the write_req cancel didn't work
+                        * we can't use the connection anymore.
+                        */
+                       smbXcli_conn_disconnect(conn, cancel_status);
+                       return;
+               }
                return;
        }
 }
@@ -966,7 +1158,7 @@ static bool smbXcli_conn_receive_next(struct smbXcli_conn *conn)
         */
        conn->read_smb_req = read_smb_send(conn->pending,
                                           state->ev,
-                                          conn->read_fd);
+                                          conn->sock_fd);
        if (conn->read_smb_req == NULL) {
                return false;
        }
@@ -977,17 +1169,11 @@ static bool smbXcli_conn_receive_next(struct smbXcli_conn *conn)
 void smbXcli_conn_disconnect(struct smbXcli_conn *conn, NTSTATUS status)
 {
        struct smbXcli_session *session;
+       int sock_fd = conn->sock_fd;
 
        tevent_queue_stop(conn->outgoing);
 
-       if (conn->read_fd != -1) {
-               close(conn->read_fd);
-       }
-       if (conn->write_fd != -1) {
-               close(conn->write_fd);
-       }
-       conn->read_fd = -1;
-       conn->write_fd = -1;
+       conn->sock_fd = -1;
 
        session = conn->sessions;
        if (talloc_array_length(conn->pending) == 0) {
@@ -1001,6 +1187,17 @@ void smbXcli_conn_disconnect(struct smbXcli_conn *conn, NTSTATUS status)
                smb2cli_session_increment_channel_sequence(session);
        }
 
+       if (conn->suicide_req != NULL) {
+               /*
+                * smbXcli_conn_samba_suicide_send()
+                * used tevent_req_defer_callback() already.
+                */
+               if (!NT_STATUS_IS_OK(status)) {
+                       tevent_req_nterror(conn->suicide_req, status);
+               }
+               conn->suicide_req = NULL;
+       }
+
        /*
         * Cancel all pending requests. We do not do a for-loop walking
         * conn->pending because that array changes in
@@ -1017,6 +1214,8 @@ void smbXcli_conn_disconnect(struct smbXcli_conn *conn, NTSTATUS status)
                state = tevent_req_data(req, struct smbXcli_req_state);
 
                if (state->smb1.chained_requests == NULL) {
+                       bool in_progress;
+
                        /*
                         * We're dead. No point waiting for trans2
                         * replies.
@@ -1030,6 +1229,14 @@ void smbXcli_conn_disconnect(struct smbXcli_conn *conn, NTSTATUS status)
                                continue;
                        }
 
+                       in_progress = tevent_req_is_in_progress(req);
+                       if (!in_progress) {
+                               /*
+                                * already finished
+                                */
+                               continue;
+                       }
+
                        /*
                         * we need to defer the callback, because we may notify
                         * more then one caller.
@@ -1043,6 +1250,8 @@ void smbXcli_conn_disconnect(struct smbXcli_conn *conn, NTSTATUS status)
                num_chained = talloc_array_length(chain);
 
                for (i=0; i<num_chained; i++) {
+                       bool in_progress;
+
                        req = chain[i];
                        state = tevent_req_data(req, struct smbXcli_req_state);
 
@@ -1059,6 +1268,14 @@ void smbXcli_conn_disconnect(struct smbXcli_conn *conn, NTSTATUS status)
                                continue;
                        }
 
+                       in_progress = tevent_req_is_in_progress(req);
+                       if (!in_progress) {
+                               /*
+                                * already finished
+                                */
+                               continue;
+                       }
+
                        /*
                         * we need to defer the callback, because we may notify
                         * more than one caller.
@@ -1068,6 +1285,10 @@ void smbXcli_conn_disconnect(struct smbXcli_conn *conn, NTSTATUS status)
                }
                TALLOC_FREE(chain);
        }
+
+       if (sock_fd != -1) {
+               close(sock_fd);
+       }
 }
 
 /*
@@ -1122,27 +1343,6 @@ static size_t smbXcli_iov_len(const struct iovec *iov, int count)
        return ret;
 }
 
-static uint8_t *smbXcli_iov_concat(TALLOC_CTX *mem_ctx,
-                                  const struct iovec *iov,
-                                  int count)
-{
-       size_t len = smbXcli_iov_len(iov, count);
-       size_t copied;
-       uint8_t *buf;
-       int i;
-
-       buf = talloc_array(mem_ctx, uint8_t, len);
-       if (buf == NULL) {
-               return NULL;
-       }
-       copied = 0;
-       for (i=0; i<count; i++) {
-               memcpy(buf+copied, iov[i].iov_base, iov[i].iov_len);
-               copied += iov[i].iov_len;
-       }
-       return buf;
-}
-
 static void smb1cli_req_flags(enum protocol_types protocol,
                              uint32_t smb1_capabilities,
                              uint8_t smb_command,
@@ -1265,6 +1465,7 @@ struct tevent_req *smb1cli_req_create(TALLOC_CTX *mem_ctx,
        uint16_t flags2 = 0;
        uint16_t uid = 0;
        uint16_t tid = 0;
+       ssize_t num_bytes;
 
        if (iov_count > MAX_SMB_IOV) {
                /*
@@ -1336,7 +1537,17 @@ struct tevent_req *smb1cli_req_create(TALLOC_CTX *mem_ctx,
 
        state->smb1.vwv = vwv;
 
-       SSVAL(state->smb1.bytecount_buf, 0, smbXcli_iov_len(bytes_iov, iov_count));
+       num_bytes = iov_buflen(bytes_iov, iov_count);
+       if (num_bytes == -1) {
+               /*
+                * I'd love to add a check for num_bytes<=UINT16_MAX here, but
+                * the smbclient->samba connections can lie and transfer more.
+                */
+               TALLOC_FREE(req);
+               return NULL;
+       }
+
+       SSVAL(state->smb1.bytecount_buf, 0, num_bytes);
 
        state->smb1.iov[0].iov_base = (void *)state->length_hdr;
        state->smb1.iov[0].iov_len  = sizeof(state->length_hdr);
@@ -1414,7 +1625,7 @@ static NTSTATUS smb1cli_conn_signv(struct smbXcli_conn *conn,
 
        frame = talloc_stackframe();
 
-       buf = smbXcli_iov_concat(frame, &iov[1], iov_count - 1);
+       buf = iov_concat(frame, &iov[1], iov_count - 1);
        if (buf == NULL) {
                return NT_STATUS_NO_MEMORY;
        }
@@ -1443,6 +1654,7 @@ static NTSTATUS smb1cli_req_writev_submit(struct tevent_req *req,
        NTSTATUS status;
        uint8_t cmd;
        uint16_t mid;
+       ssize_t nbtlen;
 
        if (!smbXcli_conn_is_connected(state->conn)) {
                return NT_STATUS_CONNECTION_DISCONNECTED;
@@ -1483,7 +1695,12 @@ static NTSTATUS smb1cli_req_writev_submit(struct tevent_req *req,
        }
        SSVAL(iov[1].iov_base, HDR_MID, mid);
 
-       _smb_setlen_nbt(iov[0].iov_base, smbXcli_iov_len(&iov[1], iov_count-1));
+       nbtlen = iov_buflen(&iov[1], iov_count-1);
+       if ((nbtlen == -1) || (nbtlen > 0x1FFFF)) {
+               return NT_STATUS_INVALID_PARAMETER_MIX;
+       }
+
+       _smb_setlen_nbt(iov[0].iov_base, nbtlen);
 
        status = smb1cli_conn_signv(state->conn, iov, iov_count,
                                    &state->smb1.seqnum,
@@ -1500,7 +1717,7 @@ static NTSTATUS smb1cli_req_writev_submit(struct tevent_req *req,
        if (common_encryption_on(state->conn->smb1.trans_enc)) {
                char *buf, *enc_buf;
 
-               buf = (char *)smbXcli_iov_concat(talloc_tos(), iov, iov_count);
+               buf = (char *)iov_concat(talloc_tos(), iov, iov_count);
                if (buf == NULL) {
                        return NT_STATUS_NO_MEMORY;
                }
@@ -1527,14 +1744,20 @@ static NTSTATUS smb1cli_req_writev_submit(struct tevent_req *req,
                state->conn->dispatch_incoming = smb1cli_conn_dispatch_incoming;
        }
 
+       if (!smbXcli_req_set_pending(req)) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
        tevent_req_set_cancel_fn(req, smbXcli_req_cancel);
 
        subreq = writev_send(state, state->ev, state->conn->outgoing,
-                            state->conn->write_fd, false, iov, iov_count);
+                            state->conn->sock_fd, false, iov, iov_count);
        if (subreq == NULL) {
                return NT_STATUS_NO_MEMORY;
        }
        tevent_req_set_callback(subreq, smb1cli_req_writev_done, req);
+       state->write_req = subreq;
+
        return NT_STATUS_OK;
 }
 
@@ -1591,9 +1814,12 @@ static void smb1cli_req_writev_done(struct tevent_req *subreq)
        ssize_t nwritten;
        int err;
 
+       state->write_req = NULL;
+
        nwritten = writev_recv(subreq, &err);
        TALLOC_FREE(subreq);
        if (nwritten == -1) {
+               /* here, we need to notify all pending requests */
                NTSTATUS status = map_nt_error_from_unix_common(err);
                smbXcli_conn_disconnect(state->conn, status);
                return;
@@ -1604,11 +1830,6 @@ static void smb1cli_req_writev_done(struct tevent_req *subreq)
                tevent_req_done(req);
                return;
        }
-
-       if (!smbXcli_req_set_pending(req)) {
-               tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
-               return;
-       }
 }
 
 static void smbXcli_conn_received(struct tevent_req *subreq)
@@ -2349,7 +2570,7 @@ NTSTATUS smb1cli_req_chain_submit(struct tevent_req **reqs, int num_reqs)
        struct iovec *iov = NULL;
        struct iovec *this_iov;
        NTSTATUS status;
-       size_t nbt_len;
+       ssize_t nbt_len;
 
        if (num_reqs == 1) {
                return smb1cli_req_writev_submit(reqs[0], first_state,
@@ -2471,8 +2692,8 @@ NTSTATUS smb1cli_req_chain_submit(struct tevent_req **reqs, int num_reqs)
                chain_padding = next_padding;
        }
 
-       nbt_len = smbXcli_iov_len(&iov[1], iovlen-1);
-       if (nbt_len > first_state->conn->smb1.max_xmit) {
+       nbt_len = iov_buflen(&iov[1], iovlen-1);
+       if ((nbt_len == -1) || (nbt_len > first_state->conn->smb1.max_xmit)) {
                TALLOC_FREE(iov);
                TALLOC_FREE(first_state->smb1.chained_requests);
                return NT_STATUS_INVALID_PARAMETER_MIX;
@@ -2556,6 +2777,48 @@ void smb2cli_conn_set_max_credits(struct smbXcli_conn *conn,
        conn->smb2.max_credits = max_credits;
 }
 
+uint16_t smb2cli_conn_get_cur_credits(struct smbXcli_conn *conn)
+{
+       return conn->smb2.cur_credits;
+}
+
+uint8_t smb2cli_conn_get_io_priority(struct smbXcli_conn *conn)
+{
+       if (conn->protocol < PROTOCOL_SMB3_11) {
+               return 0;
+       }
+
+       return conn->smb2.io_priority;
+}
+
+void smb2cli_conn_set_io_priority(struct smbXcli_conn *conn,
+                                 uint8_t io_priority)
+{
+       conn->smb2.io_priority = io_priority;
+}
+
+uint32_t smb2cli_conn_cc_chunk_len(struct smbXcli_conn *conn)
+{
+       return conn->smb2.cc_chunk_len;
+}
+
+void smb2cli_conn_set_cc_chunk_len(struct smbXcli_conn *conn,
+                                   uint32_t chunk_len)
+{
+       conn->smb2.cc_chunk_len = chunk_len;
+}
+
+uint32_t smb2cli_conn_cc_max_chunks(struct smbXcli_conn *conn)
+{
+       return conn->smb2.cc_max_chunks;
+}
+
+void smb2cli_conn_set_cc_max_chunks(struct smbXcli_conn *conn,
+                                   uint32_t max_chunks)
+{
+       conn->smb2.cc_max_chunks = max_chunks;
+}
+
 static void smb2cli_req_cancel_done(struct tevent_req *subreq);
 
 static bool smb2cli_req_cancel(struct tevent_req *req)
@@ -2563,9 +2826,6 @@ static bool smb2cli_req_cancel(struct tevent_req *req)
        struct smbXcli_req_state *state =
                tevent_req_data(req,
                struct smbXcli_req_state);
-       uint32_t flags = IVAL(state->smb2.hdr, SMB2_HDR_FLAGS);
-       uint64_t mid = BVAL(state->smb2.hdr, SMB2_HDR_MESSAGE_ID);
-       uint64_t aid = BVAL(state->smb2.hdr, SMB2_HDR_ASYNC_ID);
        struct smbXcli_tcon *tcon = state->tcon;
        struct smbXcli_session *session = state->session;
        uint8_t *fixed = state->smb2.pad;
@@ -2580,7 +2840,7 @@ static bool smb2cli_req_cancel(struct tevent_req *req)
        subreq = smb2cli_req_create(state, state->ev,
                                    state->conn,
                                    SMB2_OP_CANCEL,
-                                   flags, 0,
+                                   0, 0, /* flags */
                                    0, /* timeout */
                                    tcon, session,
                                    fixed, fixed_len,
@@ -2590,19 +2850,9 @@ static bool smb2cli_req_cancel(struct tevent_req *req)
        }
        substate = tevent_req_data(subreq, struct smbXcli_req_state);
 
-       /*
-        * clear everything but the SMB2_HDR_FLAG_ASYNC flag
-        * e.g. if SMB2_HDR_FLAG_CHAINED is set we get INVALID_PARAMETER back
-        */
-       flags &= SMB2_HDR_FLAG_ASYNC;
-
-       if (flags & SMB2_HDR_FLAG_ASYNC) {
-               mid = 0;
-       }
-
-       SIVAL(substate->smb2.hdr, SMB2_HDR_FLAGS, flags);
-       SBVAL(substate->smb2.hdr, SMB2_HDR_MESSAGE_ID, mid);
-       SBVAL(substate->smb2.hdr, SMB2_HDR_ASYNC_ID, aid);
+       SIVAL(substate->smb2.hdr, SMB2_HDR_FLAGS, state->smb2.cancel_flags);
+       SBVAL(substate->smb2.hdr, SMB2_HDR_MESSAGE_ID, state->smb2.cancel_mid);
+       SBVAL(substate->smb2.hdr, SMB2_HDR_ASYNC_ID, state->smb2.cancel_aid);
 
        status = smb2cli_req_compound_submit(&subreq, 1);
        if (!NT_STATUS_IS_OK(status)) {
@@ -2666,6 +2916,10 @@ struct tevent_req *smb2cli_req_create(TALLOC_CTX *mem_ctx,
                use_replay_flag = true;
        }
 
+       if (smbXcli_conn_protocol(conn) >= PROTOCOL_SMB3_11) {
+               flags |= SMB2_PRIORITY_VALUE_TO_MASK(conn->smb2.io_priority);
+       }
+
        if (session) {
                uid = session->smb2->session_id;
 
@@ -2801,6 +3055,8 @@ NTSTATUS smb2cli_req_compound_submit(struct tevent_req **reqs,
        int tf_iov = -1;
        const DATA_BLOB *encryption_key = NULL;
        uint64_t encryption_session_id = 0;
+       uint64_t nonce_high = UINT64_MAX;
+       uint64_t nonce_low = UINT64_MAX;
 
        /*
         * 1 for the nbt length, optional TRANSFORM
@@ -2851,6 +3107,31 @@ NTSTATUS smb2cli_req_compound_submit(struct tevent_req **reqs,
 
                encryption_session_id = state->session->smb2->session_id;
 
+               state->session->smb2->nonce_low += 1;
+               if (state->session->smb2->nonce_low == 0) {
+                       state->session->smb2->nonce_high += 1;
+                       state->session->smb2->nonce_low += 1;
+               }
+
+               /*
+                * CCM and GCM algorithms must never have their
+                * nonce wrap, or the security of the whole
+                * communication and the keys is destroyed.
+                * We must drop the connection once we have
+                * transfered too much data.
+                *
+                * NOTE: We assume nonces greater than 8 bytes.
+                */
+               if (state->session->smb2->nonce_high >=
+                   state->session->smb2->nonce_high_max)
+               {
+                       return NT_STATUS_ENCRYPTION_FAILED;
+               }
+
+               nonce_high = state->session->smb2->nonce_high_random;
+               nonce_high += state->session->smb2->nonce_high;
+               nonce_low = state->session->smb2->nonce_low;
+
                tf_iov = num_iov;
                iov[num_iov].iov_base = state->smb2.transform;
                iov[num_iov].iov_len  = sizeof(state->smb2.transform);
@@ -2858,18 +3139,12 @@ NTSTATUS smb2cli_req_compound_submit(struct tevent_req **reqs,
 
                SBVAL(state->smb2.transform, SMB2_TF_PROTOCOL_ID, SMB2_TF_MAGIC);
                SBVAL(state->smb2.transform, SMB2_TF_NONCE,
-                     state->session->smb2->nonce_low);
+                     nonce_low);
                SBVAL(state->smb2.transform, SMB2_TF_NONCE+8,
-                     state->session->smb2->nonce_high);
+                     nonce_high);
                SBVAL(state->smb2.transform, SMB2_TF_SESSION_ID,
                      encryption_session_id);
 
-               state->session->smb2->nonce_low += 1;
-               if (state->session->smb2->nonce_low == 0) {
-                       state->session->smb2->nonce_high += 1;
-                       state->session->smb2->nonce_low += 1;
-               }
-
                nbt_len += SMB2_TF_HDR_SIZE;
                break;
        }
@@ -2947,6 +3222,10 @@ NTSTATUS smb2cli_req_compound_submit(struct tevent_req **reqs,
                SSVAL(state->smb2.hdr, SMB2_HDR_CREDIT, credits);
                SBVAL(state->smb2.hdr, SMB2_HDR_MESSAGE_ID, mid);
 
+               state->smb2.cancel_flags = 0;
+               state->smb2.cancel_mid = mid;
+               state->smb2.cancel_aid = 0;
+
 skip_credits:
                if (state->session && encryption_key == NULL) {
                        /*
@@ -3071,11 +3350,13 @@ skip_credits:
        }
 
        subreq = writev_send(state, state->ev, state->conn->outgoing,
-                            state->conn->write_fd, false, iov, num_iov);
+                            state->conn->sock_fd, false, iov, num_iov);
        if (subreq == NULL) {
                return NT_STATUS_NO_MEMORY;
        }
        tevent_req_set_callback(subreq, smb2cli_req_writev_done, reqs[0]);
+       state->write_req = subreq;
+
        return NT_STATUS_OK;
 }
 
@@ -3137,6 +3418,8 @@ static void smb2cli_req_writev_done(struct tevent_req *subreq)
        ssize_t nwritten;
        int err;
 
+       state->write_req = NULL;
+
        nwritten = writev_recv(subreq, &err);
        TALLOC_FREE(subreq);
        if (nwritten == -1) {
@@ -3147,6 +3430,21 @@ static void smb2cli_req_writev_done(struct tevent_req *subreq)
        }
 }
 
+static struct smbXcli_session* smbXcli_session_by_uid(struct smbXcli_conn *conn,
+                                                    uint64_t uid)
+{
+       struct smbXcli_session *s = conn->sessions;
+
+       for (; s; s = s->next) {
+               if (s->smb2->session_id != uid) {
+                       continue;
+               }
+               break;
+       }
+
+       return s;
+}
+
 static NTSTATUS smb2cli_inbuf_parse_compound(struct smbXcli_conn *conn,
                                             uint8_t *buf,
                                             size_t buflen,
@@ -3214,14 +3512,7 @@ static NTSTATUS smb2cli_inbuf_parse_compound(struct smbXcli_conn *conn,
                                goto inval;
                        }
 
-                       s = conn->sessions;
-                       for (; s; s = s->next) {
-                               if (s->smb2->session_id != uid) {
-                                       continue;
-                               }
-                               break;
-                       }
-
+                       s = smbXcli_session_by_uid(conn, uid);
                        if (s == NULL) {
                                DEBUG(10, ("unknown session_id %llu\n",
                                           (unsigned long long)uid));
@@ -3343,8 +3634,8 @@ static NTSTATUS smb2cli_conn_dispatch_incoming(struct smbXcli_conn *conn,
 {
        struct tevent_req *req;
        struct smbXcli_req_state *state = NULL;
-       struct iovec *iov;
-       int i, num_iov;
+       struct iovec *iov = NULL;
+       int i, num_iov = 0;
        NTSTATUS status;
        bool defer = true;
        struct smbXcli_session *last_session = NULL;
@@ -3413,9 +3704,9 @@ static NTSTATUS smb2cli_conn_dispatch_incoming(struct smbXcli_conn *conn,
                         * even if the SMB2_HDR_FLAG_SIGNED flag
                         * is set.
                         */
-                       req_flags |= SMB2_HDR_FLAG_ASYNC;
-                       SBVAL(state->smb2.hdr, SMB2_HDR_FLAGS, req_flags);
-                       SBVAL(state->smb2.hdr, SMB2_HDR_ASYNC_ID, async_id);
+                       state->smb2.cancel_flags = SMB2_HDR_FLAG_ASYNC;
+                       state->smb2.cancel_mid = 0;
+                       state->smb2.cancel_aid = async_id;
 
                        if (state->smb2.notify_async) {
                                tevent_req_defer_callback(req, state->ev);
@@ -3440,17 +3731,8 @@ static NTSTATUS smb2cli_conn_dispatch_incoming(struct smbXcli_conn *conn,
                        uint64_t uid = BVAL(inhdr, SMB2_HDR_SESSION_ID);
 
                        if (session == NULL) {
-                               struct smbXcli_session *s;
-
-                               s = state->conn->sessions;
-                               for (; s; s = s->next) {
-                                       if (s->smb2->session_id != uid) {
-                                               continue;
-                                       }
-
-                                       session = s;
-                                       break;
-                               }
+                               session = smbXcli_session_by_uid(state->conn,
+                                                                uid);
                        }
 
                        if (session == NULL) {
@@ -3569,13 +3851,9 @@ static NTSTATUS smb2cli_conn_dispatch_incoming(struct smbXcli_conn *conn,
                                }
                        }
                        if (signing_key) {
-                               int cmp;
-                               static const uint8_t zeros[16];
-
-                               cmp = memcmp(inhdr+SMB2_HDR_SIGNATURE,
-                                            zeros,
-                                            16);
-                               if (cmp == 0) {
+                               bool zero;
+                               zero = all_zero(inhdr+SMB2_HDR_SIGNATURE, 16);
+                               if (zero) {
                                        state->smb2.signing_skipped = true;
                                        signing_key = NULL;
                                }
@@ -3787,6 +4065,7 @@ static const struct {
        {PROTOCOL_SMB3_00,      SMB3_DIALECT_REVISION_300},
        {PROTOCOL_SMB3_02,      SMB3_DIALECT_REVISION_302},
        {PROTOCOL_SMB3_10,      SMB3_DIALECT_REVISION_310},
+       {PROTOCOL_SMB3_11,      SMB3_DIALECT_REVISION_311},
 };
 
 struct smbXcli_negprot_state {
@@ -3813,7 +4092,8 @@ struct tevent_req *smbXcli_negprot_send(TALLOC_CTX *mem_ctx,
                                        struct smbXcli_conn *conn,
                                        uint32_t timeout_msec,
                                        enum protocol_types min_protocol,
-                                       enum protocol_types max_protocol)
+                                       enum protocol_types max_protocol,
+                                       uint16_t max_credits)
 {
        struct tevent_req *req, *subreq;
        struct smbXcli_negprot_state *state;
@@ -3846,6 +4126,10 @@ struct tevent_req *smbXcli_negprot_send(TALLOC_CTX *mem_ctx,
        conn->max_protocol = max_protocol;
        conn->protocol = PROTOCOL_NONE;
 
+       if (max_protocol >= PROTOCOL_SMB2_02) {
+               conn->smb2.max_credits = max_credits;
+       }
+
        if ((min_protocol < PROTOCOL_SMB2_02) &&
            (max_protocol < PROTOCOL_SMB2_02)) {
                /*
@@ -3868,16 +4152,6 @@ struct tevent_req *smbXcli_negprot_send(TALLOC_CTX *mem_ctx,
                 */
                conn->dispatch_incoming = smb2cli_conn_dispatch_incoming;
 
-               /*
-                * As we're starting with an SMB2 negprot, emulate Windows
-                * and ask for 31 credits in the initial SMB2 negprot.
-                * If we don't and leave requested credits at
-                * zero, MacOSX servers return zero credits on
-                * the negprot reply and we fail to connect.
-                */
-               smb2cli_conn_set_max_credits(conn,
-                       WINDOWS_CLIENT_PURE_SMB2_NEGPROT_INITIAL_CREDIT_ASK);
-
                subreq = smbXcli_negprot_smb2_subreq(state);
                if (tevent_req_nomem(subreq, req)) {
                        return tevent_req_post(req, ev);
@@ -4449,8 +4723,12 @@ static struct tevent_req *smbXcli_negprot_smb2_subreq(struct smbXcli_negprot_sta
                }
 
                SSVAL(p, 0, 2); /* ChiperCount */
-               SSVAL(p, 2, SMB2_ENCRYPTION_AES128_GCM);
-               SSVAL(p, 4, SMB2_ENCRYPTION_AES128_CCM);
+               /*
+                * For now we preferr CCM because our implementation
+                * is faster than GCM, see bug #11451.
+                */
+               SSVAL(p, 2, SMB2_ENCRYPTION_AES128_CCM);
+               SSVAL(p, 4, SMB2_ENCRYPTION_AES128_GCM);
 
                b = data_blob_const(p, 6);
                status = smb2_negotiate_context_add(state, &c,
@@ -4854,13 +5132,13 @@ NTSTATUS smbXcli_negprot(struct smbXcli_conn *conn,
                goto fail;
        }
        req = smbXcli_negprot_send(frame, ev, conn, timeout_msec,
-                                  min_protocol, max_protocol);
+                                  min_protocol, max_protocol,
+                                  WINDOWS_CLIENT_PURE_SMB2_NEGPROT_INITIAL_CREDIT_ASK);
        if (req == NULL) {
                goto fail;
        }
-       ok = tevent_req_poll(req, ev);
+       ok = tevent_req_poll_ntstatus(req, ev, &status);
        if (!ok) {
-               status = map_nt_error_from_unix_common(errno);
                goto fail;
        }
        status = smbXcli_negprot_recv(req);
@@ -5039,6 +5317,21 @@ static void smb2cli_validate_negotiate_info_done(struct tevent_req *subreq)
                tevent_req_done(req);
                return;
        }
+       if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
+               /*
+                * The response was signed, but not supported
+                *
+                * This might be returned by older Windows versions or by
+                * NetApp SMB server implementations.
+                *
+                * See
+                *
+                * https://blogs.msdn.microsoft.com/openspecification/2012/06/28/smb3-secure-dialect-negotiation/
+                *
+                */
+               tevent_req_done(req);
+               return;
+       }
        if (tevent_req_nterror(req, status)) {
                return;
        }
@@ -5113,7 +5406,7 @@ struct smbXcli_session *smbXcli_session_create(TALLOC_CTX *mem_ctx,
        }
        talloc_set_destructor(session, smbXcli_session_destructor);
 
-       DLIST_ADD_END(conn->sessions, session, struct smbXcli_session *);
+       DLIST_ADD_END(conn->sessions, session);
        session->conn = conn;
 
        memcpy(session->smb2_channel.preauth_sha512,
@@ -5143,16 +5436,48 @@ struct smbXcli_session *smbXcli_session_copy(TALLOC_CTX *mem_ctx,
        session->smb2_channel = src->smb2_channel;
        session->disconnect_expired = src->disconnect_expired;
 
-       DLIST_ADD_END(src->conn->sessions, session, struct smbXcli_session *);
+       DLIST_ADD_END(src->conn->sessions, session);
        talloc_set_destructor(session, smbXcli_session_destructor);
 
        return session;
 }
 
+bool smbXcli_session_is_guest(struct smbXcli_session *session)
+{
+       if (session == NULL) {
+               return false;
+       }
+
+       if (session->conn == NULL) {
+               return false;
+       }
+
+       if (session->conn->mandatory_signing) {
+               return false;
+       }
+
+       if (session->conn->protocol >= PROTOCOL_SMB2_02) {
+               if (session->smb2->session_flags & SMB2_SESSION_FLAG_IS_GUEST) {
+                       return true;
+               }
+               return false;
+       }
+
+       if (session->smb1.action & SMB_SETUP_GUEST) {
+               return true;
+       }
+
+       return false;
+}
+
 bool smbXcli_session_is_authenticated(struct smbXcli_session *session)
 {
        const DATA_BLOB *application_key;
 
+       if (session == NULL) {
+               return false;
+       }
+
        if (session->conn == NULL) {
                return false;
        }
@@ -5220,6 +5545,12 @@ void smb1cli_session_set_id(struct smbXcli_session *session,
        session->smb1.session_id = session_id;
 }
 
+void smb1cli_session_set_action(struct smbXcli_session *session,
+                               uint16_t action)
+{
+       session->smb1.action = action;
+}
+
 NTSTATUS smb1cli_session_set_session_key(struct smbXcli_session *session,
                                         const DATA_BLOB _session_key)
 {
@@ -5294,6 +5625,9 @@ uint8_t smb2cli_session_security_mode(struct smbXcli_session *session)
        if (conn->mandatory_signing) {
                security_mode |= SMB2_NEGOTIATE_SIGNING_REQUIRED;
        }
+       if (session->smb2->should_sign) {
+               security_mode |= SMB2_NEGOTIATE_SIGNING_REQUIRED;
+       }
 
        return security_mode;
 }
@@ -5332,6 +5666,11 @@ uint16_t smb2cli_session_reset_channel_sequence(struct smbXcli_session *session,
        return prev_cs;
 }
 
+uint16_t smb2cli_session_current_channel_sequence(struct smbXcli_session *session)
+{
+       return session->smb2->channel_sequence;
+}
+
 void smb2cli_session_start_replay(struct smbXcli_session *session)
 {
        session->smb2->replay_active = true;
@@ -5376,7 +5715,7 @@ NTSTATUS smb2cli_session_set_session_key(struct smbXcli_session *session,
                                         const struct iovec *recv_iov)
 {
        struct smbXcli_conn *conn = session->conn;
-       uint16_t no_sign_flags;
+       uint16_t no_sign_flags = 0;
        uint8_t session_key[16];
        bool check_signature = true;
        uint32_t hdr_flags;
@@ -5391,6 +5730,7 @@ NTSTATUS smb2cli_session_set_session_key(struct smbXcli_session *session,
                struct _derivation decryption;
                struct _derivation application;
        } derivation = { };
+       size_t nonce_size = 0;
 
        if (conn == NULL) {
                return NT_STATUS_INVALID_PARAMETER_MIX;
@@ -5400,7 +5740,18 @@ NTSTATUS smb2cli_session_set_session_key(struct smbXcli_session *session,
                return NT_STATUS_INVALID_PARAMETER_MIX;
        }
 
-       no_sign_flags = SMB2_SESSION_FLAG_IS_GUEST | SMB2_SESSION_FLAG_IS_NULL;
+       if (!conn->mandatory_signing) {
+               /*
+                * only allow guest sessions without
+                * mandatory signing.
+                *
+                * If we try an authentication with username != ""
+                * and the server let us in without verifying the
+                * password we don't have a negotiated session key
+                * for signing.
+                */
+               no_sign_flags = SMB2_SESSION_FLAG_IS_GUEST;
+       }
 
        if (session->smb2->session_flags & no_sign_flags) {
                session->smb2->should_sign = false;
@@ -5583,9 +5934,31 @@ NTSTATUS smb2cli_session_set_session_key(struct smbXcli_session *session,
                session->smb2->should_encrypt = false;
        }
 
-       generate_random_buffer((uint8_t *)&session->smb2->nonce_high,
-                              sizeof(session->smb2->nonce_high));
-       session->smb2->nonce_low = 1;
+       /*
+        * CCM and GCM algorithms must never have their
+        * nonce wrap, or the security of the whole
+        * communication and the keys is destroyed.
+        * We must drop the connection once we have
+        * transfered too much data.
+        *
+        * NOTE: We assume nonces greater than 8 bytes.
+        */
+       generate_random_buffer((uint8_t *)&session->smb2->nonce_high_random,
+                              sizeof(session->smb2->nonce_high_random));
+       switch (conn->smb2.server.cipher) {
+       case SMB2_ENCRYPTION_AES128_CCM:
+               nonce_size = AES_CCM_128_NONCE_SIZE;
+               break;
+       case SMB2_ENCRYPTION_AES128_GCM:
+               nonce_size = AES_GCM_128_IV_SIZE;
+               break;
+       default:
+               nonce_size = 0;
+               break;
+       }
+       session->smb2->nonce_high_max = SMB2_NONCE_HIGH_MAX(nonce_size);
+       session->smb2->nonce_high = 0;
+       session->smb2->nonce_low = 0;
 
        return NT_STATUS_OK;
 }
@@ -5616,7 +5989,7 @@ NTSTATUS smb2cli_session_create_channel(TALLOC_CTX *mem_ctx,
        }
 
        talloc_set_destructor(session2, smbXcli_session_destructor);
-       DLIST_ADD_END(conn->sessions, session2, struct smbXcli_session *);
+       DLIST_ADD_END(conn->sessions, session2);
        session2->conn = conn;
 
        memcpy(session2->smb2_channel.preauth_sha512,
@@ -5702,6 +6075,14 @@ NTSTATUS smb2cli_session_set_channel_key(struct smbXcli_session *session,
 
 NTSTATUS smb2cli_session_encryption_on(struct smbXcli_session *session)
 {
+       if (!session->smb2->should_sign) {
+               /*
+                * We need required signing on the session
+                * in order to prevent man in the middle attacks.
+                */
+               return NT_STATUS_INVALID_PARAMETER_MIX;
+       }
+
        if (session->smb2->should_encrypt) {
                return NT_STATUS_OK;
        }
@@ -5733,6 +6114,38 @@ struct smbXcli_tcon *smbXcli_tcon_create(TALLOC_CTX *mem_ctx)
        return tcon;
 }
 
+/*
+ * Return a deep structure copy of a struct smbXcli_tcon *
+ */
+
+struct smbXcli_tcon *smbXcli_tcon_copy(TALLOC_CTX *mem_ctx,
+                               const struct smbXcli_tcon *tcon_in)
+{
+       struct smbXcli_tcon *tcon;
+
+       tcon = talloc_memdup(mem_ctx, tcon_in, sizeof(struct smbXcli_tcon));
+       if (tcon == NULL) {
+               return NULL;
+       }
+
+       /* Deal with the SMB1 strings. */
+       if (tcon_in->smb1.service != NULL) {
+               tcon->smb1.service = talloc_strdup(tcon, tcon_in->smb1.service);
+               if (tcon->smb1.service == NULL) {
+                       TALLOC_FREE(tcon);
+                       return NULL;
+               }
+       }
+       if (tcon->smb1.fs_type != NULL) {
+               tcon->smb1.fs_type = talloc_strdup(tcon, tcon_in->smb1.fs_type);
+               if (tcon->smb1.fs_type == NULL) {
+                       TALLOC_FREE(tcon);
+                       return NULL;
+               }
+       }
+       return tcon;
+}
+
 void smbXcli_tcon_set_fs_attributes(struct smbXcli_tcon *tcon,
                                    uint32_t fs_attributes)
 {
@@ -5811,11 +6224,21 @@ uint32_t smb2cli_tcon_current_id(struct smbXcli_tcon *tcon)
        return tcon->smb2.tcon_id;
 }
 
+void smb2cli_tcon_set_id(struct smbXcli_tcon *tcon, uint32_t tcon_id)
+{
+       tcon->smb2.tcon_id = tcon_id;
+}
+
 uint32_t smb2cli_tcon_capabilities(struct smbXcli_tcon *tcon)
 {
        return tcon->smb2.capabilities;
 }
 
+uint32_t smb2cli_tcon_flags(struct smbXcli_tcon *tcon)
+{
+       return tcon->smb2.flags;
+}
+
 void smb2cli_tcon_set_values(struct smbXcli_tcon *tcon,
                             struct smbXcli_session *session,
                             uint32_t tcon_id,
@@ -5872,3 +6295,13 @@ bool smb2cli_tcon_is_encryption_on(struct smbXcli_tcon *tcon)
 {
        return tcon->smb2.should_encrypt;
 }
+
+void smb2cli_conn_set_mid(struct smbXcli_conn *conn, uint64_t mid)
+{
+       conn->smb2.mid = mid;
+}
+
+uint64_t smb2cli_conn_get_mid(struct smbXcli_conn *conn)
+{
+       return conn->smb2.mid;
+}