libcli:smb: Fix size types
[samba.git] / libcli / smb / smbXcli_base.c
index cccb61b507b7462fc54b32c591be257451407622..fbf24416093e6cf0b6ca2299d6dbd7701f6065f7 100644 (file)
 #include "../lib/util/tevent_unix.h"
 #include "lib/util/util_net.h"
 #include "lib/util/dlinklist.h"
+#include "lib/util/iov_buf.h"
 #include "../libcli/smb/smb_common.h"
 #include "../libcli/smb/smb_seal.h"
 #include "../libcli/smb/smb_signing.h"
 #include "../libcli/smb/read_smb.h"
 #include "smbXcli_base.h"
 #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;
@@ -38,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;
@@ -47,7 +52,10 @@ 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;
        enum protocol_types protocol;
        bool allow_signing;
        bool desire_signing;
@@ -118,11 +126,21 @@ struct smbXcli_conn {
                        NTTIME system_time;
                        NTTIME start_time;
                        DATA_BLOB gss_blob;
+                       uint16_t cipher;
                } server;
 
                uint64_t mid;
                uint16_t cur_credits;
                uint16_t max_credits;
+
+               uint32_t cc_chunk_len;
+               uint32_t cc_max_chunks;
+
+               uint8_t io_priority;
+
+               bool force_channel_sequence;
+
+               uint8_t preauth_sha512[64];
        } smb2;
 
        struct smbXcli_session *sessions;
@@ -137,9 +155,12 @@ 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;
+       bool replay_active;
 };
 
 struct smbXcli_session {
@@ -148,6 +169,7 @@ struct smbXcli_session {
 
        struct {
                uint16_t session_id;
+               uint16_t action;
                DATA_BLOB application_key;
                bool protected_key;
        } smb1;
@@ -156,6 +178,7 @@ struct smbXcli_session {
 
        struct {
                DATA_BLOB signing_key;
+               uint8_t preauth_sha512[64];
        } smb2_channel;
 
        /*
@@ -185,6 +208,7 @@ struct smbXcli_tcon {
                uint32_t flags;
                uint32_t capabilities;
                uint32_t maximal_access;
+               bool should_sign;
                bool should_encrypt;
        } smb2;
 };
@@ -201,6 +225,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];
@@ -263,6 +289,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;
 };
 
@@ -304,18 +333,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);
@@ -337,6 +361,8 @@ struct smbXcli_conn *smbXcli_conn_create(TALLOC_CTX *mem_ctx,
        }
        conn->pending = NULL;
 
+       conn->min_protocol = PROTOCOL_NONE;
+       conn->max_protocol = PROTOCOL_NONE;
        conn->protocol = PROTOCOL_NONE;
 
        switch (signing_state) {
@@ -353,6 +379,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;
@@ -389,14 +422,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;
 }
@@ -407,7 +445,7 @@ bool smbXcli_conn_is_connected(struct smbXcli_conn *conn)
                return false;
        }
 
-       if (conn->read_fd == -1) {
+       if (conn->sock_fd == -1) {
                return false;
        }
 
@@ -432,9 +470,31 @@ bool smbXcli_conn_use_unicode(struct smbXcli_conn *conn)
        return false;
 }
 
+bool smbXcli_conn_signing_mandatory(struct smbXcli_conn *conn)
+{
+       return conn->mandatory_signing;
+}
+
+/*
+ * [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)
@@ -491,12 +551,26 @@ const struct GUID *smbXcli_conn_server_guid(struct smbXcli_conn *conn)
        return &conn->smb1.server.guid;
 }
 
+bool smbXcli_conn_get_force_channel_sequence(struct smbXcli_conn *conn)
+{
+       return conn->smb2.force_channel_sequence;
+}
+
+void smbXcli_conn_set_force_channel_sequence(struct smbXcli_conn *conn,
+                                            bool v)
+{
+       conn->smb2.force_channel_sequence = v;
+}
+
 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,
@@ -517,18 +591,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(
@@ -538,9 +647,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;
@@ -577,9 +689,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);
@@ -600,14 +711,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;
        }
@@ -768,6 +874,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 too 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 =
@@ -776,12 +946,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;
        }
 
@@ -793,8 +974,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;
        }
 
@@ -809,6 +1000,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;
        }
 
@@ -825,6 +1025,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;
 }
 
@@ -834,17 +1043,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;
        }
 }
@@ -953,7 +1176,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;
        }
@@ -964,17 +1187,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) {
@@ -988,6 +1205,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
@@ -1004,6 +1232,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.
@@ -1017,6 +1247,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.
@@ -1030,6 +1268,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);
 
@@ -1046,6 +1286,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.
@@ -1055,6 +1303,10 @@ void smbXcli_conn_disconnect(struct smbXcli_conn *conn, NTSTATUS status)
                }
                TALLOC_FREE(chain);
        }
+
+       if (sock_fd != -1) {
+               close(sock_fd);
+       }
 }
 
 /*
@@ -1103,33 +1355,10 @@ void smb1cli_req_set_seqnum(struct tevent_req *req, uint32_t seqnum)
 
 static size_t smbXcli_iov_len(const struct iovec *iov, int count)
 {
-       size_t result = 0;
-       int i;
-       for (i=0; i<count; i++) {
-               result += iov[i].iov_len;
-       }
-       return result;
-}
-
-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;
+       ssize_t ret = iov_buflen(iov, count);
 
-       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;
+       /* Ignore the overflow case for now ... */
+       return ret;
 }
 
 static void smb1cli_req_flags(enum protocol_types protocol,
@@ -1254,6 +1483,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) {
                /*
@@ -1325,7 +1555,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);
@@ -1403,7 +1643,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;
        }
@@ -1432,12 +1672,16 @@ 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;
        }
 
        if (state->conn->protocol > PROTOCOL_NT1) {
+               DBG_ERR("called for dialect[%s] server[%s]\n",
+                       smb_protocol_types_string(state->conn->protocol),
+                       smbXcli_conn_remote_name(state->conn));
                return NT_STATUS_REVISION_MISMATCH;
        }
 
@@ -1472,7 +1716,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,
@@ -1489,7 +1738,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;
                }
@@ -1516,14 +1765,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;
 }
 
@@ -1580,9 +1835,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;
@@ -1593,11 +1851,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)
@@ -1661,7 +1914,7 @@ static NTSTATUS smb1cli_inbuf_parse_chain(uint8_t *buf, TALLOC_CTX *mem_ctx,
                                          struct iovec **piov, int *pnum_iov)
 {
        struct iovec *iov;
-       int num_iov;
+       size_t num_iov;
        size_t buflen;
        size_t taken;
        size_t remaining;
@@ -2338,7 +2591,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,
@@ -2460,8 +2713,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;
@@ -2545,6 +2798,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)
@@ -2552,9 +2847,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;
@@ -2569,7 +2861,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,
@@ -2579,19 +2871,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)) {
@@ -2630,8 +2912,9 @@ struct tevent_req *smb2cli_req_create(TALLOC_CTX *mem_ctx,
        uint32_t flags = 0;
        uint32_t tid = 0;
        uint64_t uid = 0;
-       bool use_channel_sequence = false;
+       bool use_channel_sequence = conn->smb2.force_channel_sequence;
        uint16_t channel_sequence = 0;
+       bool use_replay_flag = false;
 
        req = tevent_req_create(mem_ctx, &state,
                                struct smbXcli_req_state);
@@ -2650,6 +2933,14 @@ struct tevent_req *smb2cli_req_create(TALLOC_CTX *mem_ctx,
                use_channel_sequence = true;
        }
 
+       if (smbXcli_conn_protocol(conn) >= PROTOCOL_SMB3_00) {
+               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;
 
@@ -2657,11 +2948,20 @@ struct tevent_req *smb2cli_req_create(TALLOC_CTX *mem_ctx,
                        channel_sequence = session->smb2->channel_sequence;
                }
 
+               if (use_replay_flag && session->smb2->replay_active) {
+                       additional_flags |= SMB2_HDR_FLAG_REPLAY_OPERATION;
+               }
+
                state->smb2.should_sign = session->smb2->should_sign;
                state->smb2.should_encrypt = session->smb2->should_encrypt;
 
                if (cmd == SMB2_OP_SESSSETUP &&
-                   session->smb2->signing_key.length != 0) {
+                   session->smb2_channel.signing_key.length == 0 &&
+                   session->smb2->signing_key.length != 0)
+               {
+                       /*
+                        * a session bind needs to be signed
+                        */
                        state->smb2.should_sign = true;
                }
 
@@ -2669,11 +2969,24 @@ struct tevent_req *smb2cli_req_create(TALLOC_CTX *mem_ctx,
                    session->smb2_channel.signing_key.length == 0) {
                        state->smb2.should_encrypt = false;
                }
+
+               if (additional_flags & SMB2_HDR_FLAG_SIGNED) {
+                       if (session->smb2_channel.signing_key.length == 0) {
+                               tevent_req_nterror(req, NT_STATUS_NO_USER_SESSION_KEY);
+                               return req;
+                       }
+
+                       additional_flags &= ~SMB2_HDR_FLAG_SIGNED;
+                       state->smb2.should_sign = true;
+               }
        }
 
        if (tcon) {
                tid = tcon->smb2.tcon_id;
 
+               if (tcon->smb2.should_sign) {
+                       state->smb2.should_sign = true;
+               }
                if (tcon->smb2.should_encrypt) {
                        state->smb2.should_encrypt = true;
                }
@@ -2763,6 +3076,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
@@ -2813,6 +3128,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);
@@ -2820,18 +3160,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;
        }
@@ -2909,6 +3243,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) {
                        /*
@@ -3021,7 +3359,7 @@ skip_credits:
                }
 
                status = smb2_signing_encrypt_pdu(*encryption_key,
-                                       state->conn->protocol,
+                                       state->conn->smb2.server.cipher,
                                        &iov[tf_iov], num_iov - tf_iov);
                if (!NT_STATUS_IS_OK(status)) {
                        return status;
@@ -3033,11 +3371,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;
 }
 
@@ -3099,6 +3439,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) {
@@ -3109,16 +3451,32 @@ static void smb2cli_req_writev_done(struct tevent_req *subreq)
        }
 }
 
-static NTSTATUS smb2cli_inbuf_parse_compound(struct smbXcli_conn *conn,
-                                            uint8_t *buf,
-                                            size_t buflen,
-                                            TALLOC_CTX *mem_ctx,
-                                            struct iovec **piov, int *pnum_iov)
+static struct smbXcli_session* smbXcli_session_by_uid(struct smbXcli_conn *conn,
+                                                    uint64_t uid)
 {
-       struct iovec *iov;
-       int num_iov = 0;
-       size_t taken = 0;
-       uint8_t *first_hdr = buf;
+       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,
+                                            TALLOC_CTX *mem_ctx,
+                                            struct iovec **piov,
+                                            size_t *pnum_iov)
+{
+       struct iovec *iov;
+       int num_iov = 0;
+       size_t taken = 0;
+       uint8_t *first_hdr = buf;
        size_t verified_buflen = 0;
        uint8_t *tf = NULL;
        size_t tf_len = 0;
@@ -3176,14 +3534,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));
@@ -3196,7 +3547,7 @@ static NTSTATUS smb2cli_inbuf_parse_compound(struct smbXcli_conn *conn,
                        tf_iov[1].iov_len = enc_len;
 
                        status = smb2_signing_decrypt_pdu(s->smb2->decryption_key,
-                                                         conn->protocol,
+                                                         conn->smb2.server.cipher,
                                                          tf_iov, 2);
                        if (!NT_STATUS_IS_OK(status)) {
                                TALLOC_FREE(iov);
@@ -3305,8 +3656,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;
+       size_t i, num_iov = 0;
        NTSTATUS status;
        bool defer = true;
        struct smbXcli_session *last_session = NULL;
@@ -3375,9 +3726,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);
@@ -3402,17 +3753,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) {
@@ -3531,13 +3873,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;
                                }
@@ -3692,6 +4030,34 @@ NTSTATUS smb2cli_req_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
        return status;
 }
 
+NTSTATUS smb2cli_req_get_sent_iov(struct tevent_req *req,
+                                 struct iovec *sent_iov)
+{
+       struct smbXcli_req_state *state =
+               tevent_req_data(req,
+               struct smbXcli_req_state);
+
+       if (tevent_req_is_in_progress(req)) {
+               return STATUS_PENDING;
+       }
+
+       sent_iov[0].iov_base = state->smb2.hdr;
+       sent_iov[0].iov_len  = sizeof(state->smb2.hdr);
+
+       sent_iov[1].iov_base = discard_const(state->smb2.fixed);
+       sent_iov[1].iov_len  = state->smb2.fixed_len;
+
+       if (state->smb2.dyn != NULL) {
+               sent_iov[2].iov_base = discard_const(state->smb2.dyn);
+               sent_iov[2].iov_len  = state->smb2.dyn_len;
+       } else {
+               sent_iov[2].iov_base = NULL;
+               sent_iov[2].iov_len  = 0;
+       }
+
+       return NT_STATUS_OK;
+}
+
 static const struct {
        enum protocol_types proto;
        const char *smb1_name;
@@ -3720,18 +4086,17 @@ static const struct {
        {PROTOCOL_SMB2_24,      SMB2_DIALECT_REVISION_224},
        {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 {
        struct smbXcli_conn *conn;
        struct tevent_context *ev;
        uint32_t timeout_msec;
-       enum protocol_types min_protocol;
-       enum protocol_types max_protocol;
 
        struct {
                uint8_t fixed[36];
-               uint8_t dyn[ARRAY_SIZE(smb2cli_prots)*2];
        } smb2;
 };
 
@@ -3749,7 +4114,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;
@@ -3762,8 +4128,6 @@ struct tevent_req *smbXcli_negprot_send(TALLOC_CTX *mem_ctx,
        state->conn = conn;
        state->ev = ev;
        state->timeout_msec = timeout_msec;
-       state->min_protocol = min_protocol;
-       state->max_protocol = max_protocol;
 
        if (min_protocol == PROTOCOL_NONE) {
                tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
@@ -3780,6 +4144,14 @@ struct tevent_req *smbXcli_negprot_send(TALLOC_CTX *mem_ctx,
                return tevent_req_post(req, ev);
        }
 
+       conn->min_protocol = min_protocol;
+       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)) {
                /*
@@ -3859,11 +4231,11 @@ static struct tevent_req *smbXcli_negprot_smb1_subreq(struct smbXcli_negprot_sta
                uint8_t c = 2;
                bool ok;
 
-               if (smb1cli_prots[i].proto < state->min_protocol) {
+               if (smb1cli_prots[i].proto < state->conn->min_protocol) {
                        continue;
                }
 
-               if (smb1cli_prots[i].proto > state->max_protocol) {
+               if (smb1cli_prots[i].proto > state->conn->max_protocol) {
                        continue;
                }
 
@@ -3884,7 +4256,7 @@ static struct tevent_req *smbXcli_negprot_smb1_subreq(struct smbXcli_negprot_sta
                }
        }
 
-       smb1cli_req_flags(state->max_protocol,
+       smb1cli_req_flags(state->conn->max_protocol,
                          state->conn->smb1.client.capabilities,
                          SMBnegprot,
                          0, 0, &flags,
@@ -3979,11 +4351,11 @@ static void smbXcli_negprot_smb1_done(struct tevent_req *subreq)
        protnum = SVAL(vwv, 0);
 
        for (i=0; i < ARRAY_SIZE(smb1cli_prots); i++) {
-               if (smb1cli_prots[i].proto < state->min_protocol) {
+               if (smb1cli_prots[i].proto < state->conn->min_protocol) {
                        continue;
                }
 
-               if (smb1cli_prots[i].proto > state->max_protocol) {
+               if (smb1cli_prots[i].proto > state->conn->max_protocol) {
                        continue;
                }
 
@@ -4291,23 +4663,38 @@ static void smbXcli_negprot_smb1_done(struct tevent_req *subreq)
        tevent_req_done(req);
 }
 
+static size_t smbXcli_padding_helper(uint32_t offset, size_t n)
+{
+       if ((offset & (n-1)) == 0) return 0;
+       return n - (offset & (n-1));
+}
+
 static struct tevent_req *smbXcli_negprot_smb2_subreq(struct smbXcli_negprot_state *state)
 {
        size_t i;
        uint8_t *buf;
        uint16_t dialect_count = 0;
+       DATA_BLOB dyn = data_blob_null;
 
-       buf = state->smb2.dyn;
        for (i=0; i < ARRAY_SIZE(smb2cli_prots); i++) {
-               if (smb2cli_prots[i].proto < state->min_protocol) {
+               bool ok;
+               uint8_t val[2];
+
+               if (smb2cli_prots[i].proto < state->conn->min_protocol) {
                        continue;
                }
 
-               if (smb2cli_prots[i].proto > state->max_protocol) {
+               if (smb2cli_prots[i].proto > state->conn->max_protocol) {
                        continue;
                }
 
-               SSVAL(buf, dialect_count*2, smb2cli_prots[i].smb2_dialect);
+               SSVAL(val, 0, smb2cli_prots[i].smb2_dialect);
+
+               ok = data_blob_append(state, &dyn, val, sizeof(val));
+               if (!ok) {
+                       return NULL;
+               }
+
                dialect_count++;
        }
 
@@ -4316,12 +4703,12 @@ static struct tevent_req *smbXcli_negprot_smb2_subreq(struct smbXcli_negprot_sta
        SSVAL(buf, 2, dialect_count);
        SSVAL(buf, 4, state->conn->smb2.client.security_mode);
        SSVAL(buf, 6, 0);       /* Reserved */
-       if (state->max_protocol >= PROTOCOL_SMB2_22) {
+       if (state->conn->max_protocol >= PROTOCOL_SMB2_22) {
                SIVAL(buf, 8, state->conn->smb2.client.capabilities);
        } else {
                SIVAL(buf, 8, 0);       /* Capabilities */
        }
-       if (state->max_protocol >= PROTOCOL_SMB2_10) {
+       if (state->conn->max_protocol >= PROTOCOL_SMB2_10) {
                NTSTATUS status;
                DATA_BLOB blob;
 
@@ -4334,7 +4721,69 @@ static struct tevent_req *smbXcli_negprot_smb2_subreq(struct smbXcli_negprot_sta
        } else {
                memset(buf+12, 0, 16);  /* ClientGuid */
        }
-       SBVAL(buf, 28, 0);      /* ClientStartTime */
+
+       if (state->conn->max_protocol >= PROTOCOL_SMB3_10) {
+               NTSTATUS status;
+               struct smb2_negotiate_contexts c = { .num_contexts = 0, };
+               uint32_t offset;
+               DATA_BLOB b;
+               uint8_t p[38];
+               const uint8_t zeros[8] = {0, };
+               size_t pad;
+               bool ok;
+
+               SSVAL(p, 0,  1); /* HashAlgorithmCount */
+               SSVAL(p, 2, 32); /* SaltLength */
+               SSVAL(p, 4, SMB2_PREAUTH_INTEGRITY_SHA512);
+               generate_random_buffer(p + 6, 32);
+
+               b = data_blob_const(p, 38);
+               status = smb2_negotiate_context_add(state, &c,
+                                       SMB2_PREAUTH_INTEGRITY_CAPABILITIES, b);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return NULL;
+               }
+
+               SSVAL(p, 0, 2); /* ChiperCount */
+               /*
+                * 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,
+                                       SMB2_ENCRYPTION_CAPABILITIES, b);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return NULL;
+               }
+
+               status = smb2_negotiate_context_push(state, &b, c);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return NULL;
+               }
+
+               offset = SMB2_HDR_BODY + sizeof(state->smb2.fixed) + dyn.length;
+               pad = smbXcli_padding_helper(offset, 8);
+
+               ok = data_blob_append(state, &dyn, zeros, pad);
+               if (!ok) {
+                       return NULL;
+               }
+               offset += pad;
+
+               ok = data_blob_append(state, &dyn, b.data, b.length);
+               if (!ok) {
+                       return NULL;
+               }
+
+               SIVAL(buf, 28, offset);   /* NegotiateContextOffset */
+               SSVAL(buf, 32, c.num_contexts); /* NegotiateContextCount */
+               SSVAL(buf, 34, 0);        /* Reserved */
+       } else {
+               SBVAL(buf, 28, 0);      /* Reserved/ClientStartTime */
+       }
 
        return smb2cli_req_send(state, state->ev,
                                state->conn, SMB2_OP_NEGPROT,
@@ -4342,7 +4791,7 @@ static struct tevent_req *smbXcli_negprot_smb2_subreq(struct smbXcli_negprot_sta
                                state->timeout_msec,
                                NULL, NULL, /* tcon, session */
                                state->smb2.fixed, sizeof(state->smb2.fixed),
-                               state->smb2.dyn, dialect_count*2,
+                               dyn.data, dyn.length,
                                UINT16_MAX); /* max_dyn_len */
 }
 
@@ -4362,6 +4811,20 @@ static void smbXcli_negprot_smb2_done(struct tevent_req *subreq)
        uint8_t *body;
        size_t i;
        uint16_t dialect_revision;
+       struct smb2_negotiate_contexts c = { .num_contexts = 0, };
+       uint32_t negotiate_context_offset = 0;
+       uint16_t negotiate_context_count = 0;
+       DATA_BLOB negotiate_context_blob = data_blob_null;
+       size_t avail;
+       size_t ctx_ofs;
+       size_t needed;
+       struct smb2_negotiate_context *preauth = NULL;
+       uint16_t hash_count;
+       uint16_t salt_length;
+       uint16_t hash_selected;
+       struct hc_sha512state sctx;
+       struct smb2_negotiate_context *cipher = NULL;
+       struct iovec sent_iov[3];
        static const struct smb2cli_req_expected_response expected[] = {
        {
                .status = NT_STATUS_OK,
@@ -4371,7 +4834,6 @@ static void smbXcli_negprot_smb2_done(struct tevent_req *subreq)
 
        status = smb2cli_req_recv(subreq, state, &iov,
                                  expected, ARRAY_SIZE(expected));
-       TALLOC_FREE(subreq);
        if (tevent_req_nterror(req, status)) {
                return;
        }
@@ -4381,11 +4843,11 @@ static void smbXcli_negprot_smb2_done(struct tevent_req *subreq)
        dialect_revision = SVAL(body, 4);
 
        for (i=0; i < ARRAY_SIZE(smb2cli_prots); i++) {
-               if (smb2cli_prots[i].proto < state->min_protocol) {
+               if (smb2cli_prots[i].proto < state->conn->min_protocol) {
                        continue;
                }
 
-               if (smb2cli_prots[i].proto > state->max_protocol) {
+               if (smb2cli_prots[i].proto > state->conn->max_protocol) {
                        continue;
                }
 
@@ -4398,7 +4860,9 @@ static void smbXcli_negprot_smb2_done(struct tevent_req *subreq)
        }
 
        if (conn->protocol == PROTOCOL_NONE) {
-               if (state->min_protocol >= PROTOCOL_SMB2_02) {
+               TALLOC_FREE(subreq);
+
+               if (state->conn->min_protocol >= PROTOCOL_SMB2_02) {
                        tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
                        return;
                }
@@ -4409,7 +4873,7 @@ static void smbXcli_negprot_smb2_done(struct tevent_req *subreq)
                }
 
                /* make sure we do not loop forever */
-               state->min_protocol = PROTOCOL_SMB2_02;
+               state->conn->min_protocol = PROTOCOL_SMB2_02;
 
                /*
                 * send a SMB2 negprot, in order to negotiate
@@ -4424,6 +4888,9 @@ static void smbXcli_negprot_smb2_done(struct tevent_req *subreq)
        }
 
        conn->smb2.server.security_mode = SVAL(body, 2);
+       if (conn->protocol >= PROTOCOL_SMB3_10) {
+               negotiate_context_count = SVAL(body, 6);
+       }
 
        blob = data_blob_const(body + 8, 16);
        status = GUID_from_data_blob(&blob, &conn->smb2.server.guid);
@@ -4458,6 +4925,154 @@ static void smbXcli_negprot_smb2_done(struct tevent_req *subreq)
                return;
        }
 
+       if (conn->protocol < PROTOCOL_SMB3_10) {
+               TALLOC_FREE(subreq);
+
+               if (conn->smb2.server.capabilities & SMB2_CAP_ENCRYPTION) {
+                       conn->smb2.server.cipher = SMB2_ENCRYPTION_AES128_CCM;
+               }
+               tevent_req_done(req);
+               return;
+       }
+
+       /*
+        * Here we are now at SMB3_11, so encryption should be
+        * negotiated via context, not capabilities.
+        */
+
+       if (conn->smb2.server.capabilities & SMB2_CAP_ENCRYPTION) {
+               /*
+                * Server set SMB2_CAP_ENCRYPTION capability,
+                * but *SHOULD* not, not *MUST* not. Just mask it off.
+                * NetApp seems to do this:
+                * BUG: https://bugzilla.samba.org/show_bug.cgi?id=13009
+                */
+               conn->smb2.server.capabilities &= ~SMB2_CAP_ENCRYPTION;
+       }
+
+       negotiate_context_offset = IVAL(body, 60);
+       if (negotiate_context_offset < security_offset) {
+               tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+               return;
+       }
+
+       ctx_ofs = negotiate_context_offset - security_offset;
+       if (ctx_ofs > iov[2].iov_len) {
+               tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+               return;
+       }
+       avail = iov[2].iov_len - security_length;
+       needed = iov[2].iov_len - ctx_ofs;
+       if (needed > avail) {
+               tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+               return;
+       }
+
+       negotiate_context_blob.data = (uint8_t *)iov[2].iov_base;
+       negotiate_context_blob.length = iov[2].iov_len;
+
+       negotiate_context_blob.data += ctx_ofs;
+       negotiate_context_blob.length -= ctx_ofs;
+
+       status = smb2_negotiate_context_parse(state, negotiate_context_blob, &c);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+
+       if (negotiate_context_count != c.num_contexts) {
+               tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+               return;
+       }
+
+       preauth = smb2_negotiate_context_find(&c,
+                                       SMB2_PREAUTH_INTEGRITY_CAPABILITIES);
+       if (preauth == NULL) {
+               tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+               return;
+       }
+
+       if (preauth->data.length < 6) {
+               tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+               return;
+       }
+
+       hash_count = SVAL(preauth->data.data, 0);
+       salt_length = SVAL(preauth->data.data, 2);
+       hash_selected = SVAL(preauth->data.data, 4);
+
+       if (hash_count != 1) {
+               tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+               return;
+       }
+
+       if (preauth->data.length != (6 + salt_length)) {
+               tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+               return;
+       }
+
+       if (hash_selected != SMB2_PREAUTH_INTEGRITY_SHA512) {
+               tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+               return;
+       }
+
+       cipher = smb2_negotiate_context_find(&c, SMB2_ENCRYPTION_CAPABILITIES);
+       if (cipher != NULL) {
+               uint16_t cipher_count;
+
+               if (cipher->data.length < 2) {
+                       tevent_req_nterror(req,
+                                       NT_STATUS_INVALID_NETWORK_RESPONSE);
+                       return;
+               }
+
+               cipher_count = SVAL(cipher->data.data, 0);
+
+               if (cipher_count > 1) {
+                       tevent_req_nterror(req,
+                                       NT_STATUS_INVALID_NETWORK_RESPONSE);
+                       return;
+               }
+
+               if (cipher->data.length != (2 + 2 * cipher_count)) {
+                       tevent_req_nterror(req,
+                                       NT_STATUS_INVALID_NETWORK_RESPONSE);
+                       return;
+               }
+
+               if (cipher_count == 1) {
+                       uint16_t cipher_selected;
+
+                       cipher_selected = SVAL(cipher->data.data, 2);
+
+                       switch (cipher_selected) {
+                       case SMB2_ENCRYPTION_AES128_GCM:
+                       case SMB2_ENCRYPTION_AES128_CCM:
+                               conn->smb2.server.cipher = cipher_selected;
+                               break;
+                       }
+               }
+       }
+
+       /* First we hash the request */
+       smb2cli_req_get_sent_iov(subreq, sent_iov);
+       samba_SHA512_Init(&sctx);
+       samba_SHA512_Update(&sctx, conn->smb2.preauth_sha512,
+                     sizeof(conn->smb2.preauth_sha512));
+       for (i = 0; i < 3; i++) {
+               samba_SHA512_Update(&sctx, sent_iov[i].iov_base, sent_iov[i].iov_len);
+       }
+       samba_SHA512_Final(conn->smb2.preauth_sha512, &sctx);
+       TALLOC_FREE(subreq);
+
+       /* And now we hash the response */
+       samba_SHA512_Init(&sctx);
+       samba_SHA512_Update(&sctx, conn->smb2.preauth_sha512,
+                     sizeof(conn->smb2.preauth_sha512));
+       for (i = 0; i < 3; i++) {
+               samba_SHA512_Update(&sctx, iov[i].iov_base, iov[i].iov_len);
+       }
+       samba_SHA512_Final(conn->smb2.preauth_sha512, &sctx);
+
        tevent_req_done(req);
 }
 
@@ -4548,13 +5163,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);
@@ -4563,6 +5178,239 @@ NTSTATUS smbXcli_negprot(struct smbXcli_conn *conn,
        return status;
 }
 
+struct smb2cli_validate_negotiate_info_state {
+       struct smbXcli_conn *conn;
+       DATA_BLOB in_input_buffer;
+       DATA_BLOB in_output_buffer;
+       DATA_BLOB out_input_buffer;
+       DATA_BLOB out_output_buffer;
+       uint16_t dialect;
+};
+
+static void smb2cli_validate_negotiate_info_done(struct tevent_req *subreq);
+
+struct tevent_req *smb2cli_validate_negotiate_info_send(TALLOC_CTX *mem_ctx,
+                                               struct tevent_context *ev,
+                                               struct smbXcli_conn *conn,
+                                               uint32_t timeout_msec,
+                                               struct smbXcli_session *session,
+                                               struct smbXcli_tcon *tcon)
+{
+       struct tevent_req *req;
+       struct smb2cli_validate_negotiate_info_state *state;
+       uint8_t *buf;
+       uint16_t dialect_count = 0;
+       struct tevent_req *subreq;
+       bool _save_should_sign;
+       size_t i;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct smb2cli_validate_negotiate_info_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       state->conn = conn;
+
+       state->in_input_buffer = data_blob_talloc_zero(state,
+                                       4 + 16 + 1 + 1 + 2);
+       if (tevent_req_nomem(state->in_input_buffer.data, req)) {
+               return tevent_req_post(req, ev);
+       }
+       buf = state->in_input_buffer.data;
+
+       if (state->conn->max_protocol >= PROTOCOL_SMB2_22) {
+               SIVAL(buf, 0, conn->smb2.client.capabilities);
+       } else {
+               SIVAL(buf, 0, 0); /* Capabilities */
+       }
+       if (state->conn->max_protocol >= PROTOCOL_SMB2_10) {
+               NTSTATUS status;
+               DATA_BLOB blob;
+
+               status = GUID_to_ndr_blob(&conn->smb2.client.guid,
+                                         state, &blob);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return NULL;
+               }
+               memcpy(buf+4, blob.data, 16); /* ClientGuid */
+       } else {
+               memset(buf+4, 0, 16);   /* ClientGuid */
+       }
+       if (state->conn->min_protocol >= PROTOCOL_SMB2_02) {
+               SCVAL(buf, 20, conn->smb2.client.security_mode);
+       } else {
+               SCVAL(buf, 20, 0);
+       }
+       SCVAL(buf, 21, 0); /* reserved */
+
+       for (i=0; i < ARRAY_SIZE(smb2cli_prots); i++) {
+               bool ok;
+               size_t ofs;
+
+               if (smb2cli_prots[i].proto < state->conn->min_protocol) {
+                       continue;
+               }
+
+               if (smb2cli_prots[i].proto > state->conn->max_protocol) {
+                       continue;
+               }
+
+               if (smb2cli_prots[i].proto == state->conn->protocol) {
+                       state->dialect = smb2cli_prots[i].smb2_dialect;
+               }
+
+               ofs = state->in_input_buffer.length;
+               ok = data_blob_realloc(state, &state->in_input_buffer,
+                                      ofs + 2);
+               if (!ok) {
+                       tevent_req_oom(req);
+                       return tevent_req_post(req, ev);
+               }
+
+               buf = state->in_input_buffer.data;
+               SSVAL(buf, ofs, smb2cli_prots[i].smb2_dialect);
+
+               dialect_count++;
+       }
+       buf = state->in_input_buffer.data;
+       SSVAL(buf, 22, dialect_count);
+
+       _save_should_sign = smb2cli_tcon_is_signing_on(tcon);
+       smb2cli_tcon_should_sign(tcon, true);
+       subreq = smb2cli_ioctl_send(state, ev, conn,
+                                   timeout_msec, session, tcon,
+                                   UINT64_MAX, /* in_fid_persistent */
+                                   UINT64_MAX, /* in_fid_volatile */
+                                   FSCTL_VALIDATE_NEGOTIATE_INFO,
+                                   0, /* in_max_input_length */
+                                   &state->in_input_buffer,
+                                   24, /* in_max_output_length */
+                                   &state->in_output_buffer,
+                                   SMB2_IOCTL_FLAG_IS_FSCTL);
+       smb2cli_tcon_should_sign(tcon, _save_should_sign);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq,
+                               smb2cli_validate_negotiate_info_done,
+                               req);
+
+       return req;
+}
+
+static void smb2cli_validate_negotiate_info_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req =
+               tevent_req_callback_data(subreq,
+               struct tevent_req);
+       struct smb2cli_validate_negotiate_info_state *state =
+               tevent_req_data(req,
+               struct smb2cli_validate_negotiate_info_state);
+       NTSTATUS status;
+       const uint8_t *buf;
+       uint32_t capabilities;
+       DATA_BLOB guid_blob;
+       struct GUID server_guid;
+       uint16_t security_mode;
+       uint16_t dialect;
+
+       status = smb2cli_ioctl_recv(subreq, state,
+                                   &state->out_input_buffer,
+                                   &state->out_output_buffer);
+       TALLOC_FREE(subreq);
+       if (NT_STATUS_EQUAL(status, NT_STATUS_FILE_CLOSED)) {
+               /*
+                * The response was signed, but not supported
+                *
+                * Older Windows and Samba releases return
+                * NT_STATUS_FILE_CLOSED.
+                */
+               tevent_req_done(req);
+               return;
+       }
+       if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_DEVICE_REQUEST)) {
+               /*
+                * The response was signed, but not supported
+                *
+                * This is returned by the NTVFS based Samba 4.x file server
+                * for file shares.
+                */
+               tevent_req_done(req);
+               return;
+       }
+       if (NT_STATUS_EQUAL(status, NT_STATUS_FS_DRIVER_REQUIRED)) {
+               /*
+                * The response was signed, but not supported
+                *
+                * This is returned by the NTVFS based Samba 4.x file server
+                * for ipc shares.
+                */
+               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;
+       }
+
+       if (state->out_output_buffer.length != 24) {
+               tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+               return;
+       }
+
+       buf = state->out_output_buffer.data;
+
+       capabilities = IVAL(buf, 0);
+       guid_blob = data_blob_const(buf + 4, 16);
+       status = GUID_from_data_blob(&guid_blob, &server_guid);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+       security_mode = CVAL(buf, 20);
+       dialect = SVAL(buf, 22);
+
+       if (capabilities != state->conn->smb2.server.capabilities) {
+               tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
+               return;
+       }
+
+       if (!GUID_equal(&server_guid, &state->conn->smb2.server.guid)) {
+               tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
+               return;
+       }
+
+       if (security_mode != state->conn->smb2.server.security_mode) {
+               tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
+               return;
+       }
+
+       if (dialect != state->dialect) {
+               tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
+               return;
+       }
+
+       tevent_req_done(req);
+}
+
+NTSTATUS smb2cli_validate_negotiate_info_recv(struct tevent_req *req)
+{
+       return tevent_req_simple_recv_ntstatus(req);
+}
+
 static int smbXcli_session_destructor(struct smbXcli_session *session)
 {
        if (session->conn == NULL) {
@@ -4589,9 +5437,13 @@ 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,
+              conn->smb2.preauth_sha512,
+              sizeof(session->smb2_channel.preauth_sha512));
+
        return session;
 }
 
@@ -4615,12 +5467,68 @@ 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;
+       }
+
+       /*
+        * If we have an application key we had a session key negotiated
+        * at auth time.
+        */
+       if (session->conn->protocol >= PROTOCOL_SMB2_02) {
+               application_key = &session->smb2->application_key;
+       } else {
+               application_key = &session->smb1.application_key;
+       }
+
+       if (application_key->length == 0) {
+               return false;
+       }
+
+       return true;
+}
 
 NTSTATUS smbXcli_session_application_key(struct smbXcli_session *session,
                                         TALLOC_CTX *mem_ctx,
@@ -4668,6 +5576,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)
 {
@@ -4742,6 +5656,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;
 }
@@ -4769,16 +5686,82 @@ void smb2cli_session_increment_channel_sequence(struct smbXcli_session *session)
        session->smb2->channel_sequence += 1;
 }
 
+uint16_t smb2cli_session_reset_channel_sequence(struct smbXcli_session *session,
+                                               uint16_t channel_sequence)
+{
+       uint16_t prev_cs;
+
+       prev_cs = session->smb2->channel_sequence;
+       session->smb2->channel_sequence = channel_sequence;
+
+       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;
+}
+
+void smb2cli_session_stop_replay(struct smbXcli_session *session)
+{
+       session->smb2->replay_active = false;
+}
+
+NTSTATUS smb2cli_session_update_preauth(struct smbXcli_session *session,
+                                       const struct iovec *iov)
+{
+       struct hc_sha512state sctx;
+       size_t i;
+
+       if (session->conn == NULL) {
+               return NT_STATUS_INTERNAL_ERROR;
+       }
+
+       if (session->conn->protocol < PROTOCOL_SMB3_10) {
+               return NT_STATUS_OK;
+       }
+
+       if (session->smb2_channel.signing_key.length != 0) {
+               return NT_STATUS_OK;
+       }
+
+       samba_SHA512_Init(&sctx);
+       samba_SHA512_Update(&sctx, session->smb2_channel.preauth_sha512,
+                     sizeof(session->smb2_channel.preauth_sha512));
+       for (i = 0; i < 3; i++) {
+               samba_SHA512_Update(&sctx, iov[i].iov_base, iov[i].iov_len);
+       }
+       samba_SHA512_Final(session->smb2_channel.preauth_sha512, &sctx);
+
+       return NT_STATUS_OK;
+}
+
 NTSTATUS smb2cli_session_set_session_key(struct smbXcli_session *session,
                                         const DATA_BLOB _session_key,
                                         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;
        NTSTATUS status;
+       struct _derivation {
+               DATA_BLOB label;
+               DATA_BLOB context;
+       };
+       struct {
+               struct _derivation signing;
+               struct _derivation encryption;
+               struct _derivation decryption;
+               struct _derivation application;
+       } derivation = { };
+       size_t nonce_size = 0;
 
        if (conn == NULL) {
                return NT_STATUS_INVALID_PARAMETER_MIX;
@@ -4788,7 +5771,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;
@@ -4799,6 +5793,49 @@ NTSTATUS smb2cli_session_set_session_key(struct smbXcli_session *session,
                return NT_STATUS_INVALID_PARAMETER_MIX;
        }
 
+       if (conn->protocol >= PROTOCOL_SMB3_10) {
+               struct _derivation *d;
+               DATA_BLOB p;
+
+               p = data_blob_const(session->smb2_channel.preauth_sha512,
+                               sizeof(session->smb2_channel.preauth_sha512));
+
+               d = &derivation.signing;
+               d->label = data_blob_string_const_null("SMBSigningKey");
+               d->context = p;
+
+               d = &derivation.encryption;
+               d->label = data_blob_string_const_null("SMBC2SCipherKey");
+               d->context = p;
+
+               d = &derivation.decryption;
+               d->label = data_blob_string_const_null("SMBS2CCipherKey");
+               d->context = p;
+
+               d = &derivation.application;
+               d->label = data_blob_string_const_null("SMBAppKey");
+               d->context = p;
+
+       } else if (conn->protocol >= PROTOCOL_SMB2_24) {
+               struct _derivation *d;
+
+               d = &derivation.signing;
+               d->label = data_blob_string_const_null("SMB2AESCMAC");
+               d->context = data_blob_string_const_null("SmbSign");
+
+               d = &derivation.encryption;
+               d->label = data_blob_string_const_null("SMB2AESCCM");
+               d->context = data_blob_string_const_null("ServerIn ");
+
+               d = &derivation.decryption;
+               d->label = data_blob_string_const_null("SMB2AESCCM");
+               d->context = data_blob_string_const_null("ServerOut");
+
+               d = &derivation.application;
+               d->label = data_blob_string_const_null("SMB2APP");
+               d->context = data_blob_string_const_null("SmbRpc");
+       }
+
        ZERO_STRUCT(session_key);
        memcpy(session_key, _session_key.data,
               MIN(_session_key.length, sizeof(session_key)));
@@ -4812,12 +5849,11 @@ NTSTATUS smb2cli_session_set_session_key(struct smbXcli_session *session,
        }
 
        if (conn->protocol >= PROTOCOL_SMB2_24) {
-               const DATA_BLOB label = data_blob_string_const_null("SMB2AESCMAC");
-               const DATA_BLOB context = data_blob_string_const_null("SmbSign");
+               struct _derivation *d = &derivation.signing;
 
                smb2_key_derivation(session_key, sizeof(session_key),
-                                   label.data, label.length,
-                                   context.data, context.length,
+                                   d->label.data, d->label.length,
+                                   d->context.data, d->context.length,
                                    session->smb2->signing_key.data);
        }
 
@@ -4829,12 +5865,11 @@ NTSTATUS smb2cli_session_set_session_key(struct smbXcli_session *session,
        }
 
        if (conn->protocol >= PROTOCOL_SMB2_24) {
-               const DATA_BLOB label = data_blob_string_const_null("SMB2AESCCM");
-               const DATA_BLOB context = data_blob_string_const_null("ServerIn ");
+               struct _derivation *d = &derivation.encryption;
 
                smb2_key_derivation(session_key, sizeof(session_key),
-                                   label.data, label.length,
-                                   context.data, context.length,
+                                   d->label.data, d->label.length,
+                                   d->context.data, d->context.length,
                                    session->smb2->encryption_key.data);
        }
 
@@ -4846,12 +5881,11 @@ NTSTATUS smb2cli_session_set_session_key(struct smbXcli_session *session,
        }
 
        if (conn->protocol >= PROTOCOL_SMB2_24) {
-               const DATA_BLOB label = data_blob_string_const_null("SMB2AESCCM");
-               const DATA_BLOB context = data_blob_string_const_null("ServerOut");
+               struct _derivation *d = &derivation.decryption;
 
                smb2_key_derivation(session_key, sizeof(session_key),
-                                   label.data, label.length,
-                                   context.data, context.length,
+                                   d->label.data, d->label.length,
+                                   d->context.data, d->context.length,
                                    session->smb2->decryption_key.data);
        }
 
@@ -4863,12 +5897,11 @@ NTSTATUS smb2cli_session_set_session_key(struct smbXcli_session *session,
        }
 
        if (conn->protocol >= PROTOCOL_SMB2_24) {
-               const DATA_BLOB label = data_blob_string_const_null("SMB2APP");
-               const DATA_BLOB context = data_blob_string_const_null("SmbRpc");
+               struct _derivation *d = &derivation.application;
 
                smb2_key_derivation(session_key, sizeof(session_key),
-                                   label.data, label.length,
-                                   context.data, context.length,
+                                   d->label.data, d->label.length,
+                                   d->context.data, d->context.length,
                                    session->smb2->application_key.data);
        }
        ZERO_STRUCT(session_key);
@@ -4896,6 +5929,10 @@ NTSTATUS smb2cli_session_set_session_key(struct smbXcli_session *session,
                check_signature = true;
        }
 
+       if (conn->protocol >= PROTOCOL_SMB3_10) {
+               check_signature = true;
+       }
+
        if (check_signature) {
                status = smb2_signing_check_pdu(session->smb2_channel.signing_key,
                                                session->conn->protocol,
@@ -4924,13 +5961,35 @@ NTSTATUS smb2cli_session_set_session_key(struct smbXcli_session *session,
                session->smb2->should_encrypt = false;
        }
 
-       if (!(conn->smb2.server.capabilities & SMB2_CAP_ENCRYPTION)) {
+       if (conn->smb2.server.cipher == 0) {
                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;
 }
@@ -4961,9 +6020,13 @@ 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,
+              conn->smb2.preauth_sha512,
+              sizeof(session2->smb2_channel.preauth_sha512));
+
        *_session2 = session2;
        return NT_STATUS_OK;
 }
@@ -4975,6 +6038,13 @@ NTSTATUS smb2cli_session_set_channel_key(struct smbXcli_session *session,
        struct smbXcli_conn *conn = session->conn;
        uint8_t channel_key[16];
        NTSTATUS status;
+       struct _derivation {
+               DATA_BLOB label;
+               DATA_BLOB context;
+       };
+       struct {
+               struct _derivation signing;
+       } derivation = { };
 
        if (conn == NULL) {
                return NT_STATUS_INVALID_PARAMETER_MIX;
@@ -4984,6 +6054,24 @@ NTSTATUS smb2cli_session_set_channel_key(struct smbXcli_session *session,
                return NT_STATUS_INVALID_PARAMETER_MIX;
        }
 
+       if (conn->protocol >= PROTOCOL_SMB3_10) {
+               struct _derivation *d;
+               DATA_BLOB p;
+
+               p = data_blob_const(session->smb2_channel.preauth_sha512,
+                               sizeof(session->smb2_channel.preauth_sha512));
+
+               d = &derivation.signing;
+               d->label = data_blob_string_const_null("SMBSigningKey");
+               d->context = p;
+       } else if (conn->protocol >= PROTOCOL_SMB2_24) {
+               struct _derivation *d;
+
+               d = &derivation.signing;
+               d->label = data_blob_string_const_null("SMB2AESCMAC");
+               d->context = data_blob_string_const_null("SmbSign");
+       }
+
        ZERO_STRUCT(channel_key);
        memcpy(channel_key, _channel_key.data,
               MIN(_channel_key.length, sizeof(channel_key)));
@@ -4997,12 +6085,11 @@ NTSTATUS smb2cli_session_set_channel_key(struct smbXcli_session *session,
        }
 
        if (conn->protocol >= PROTOCOL_SMB2_24) {
-               const DATA_BLOB label = data_blob_string_const_null("SMB2AESCMAC");
-               const DATA_BLOB context = data_blob_string_const_null("SmbSign");
+               struct _derivation *d = &derivation.signing;
 
                smb2_key_derivation(channel_key, sizeof(channel_key),
-                                   label.data, label.length,
-                                   context.data, context.length,
+                                   d->label.data, d->label.length,
+                                   d->context.data, d->context.length,
                                    session->smb2_channel.signing_key.data);
        }
        ZERO_STRUCT(channel_key);
@@ -5019,6 +6106,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;
        }
@@ -5027,7 +6122,7 @@ NTSTATUS smb2cli_session_encryption_on(struct smbXcli_session *session)
                return NT_STATUS_NOT_SUPPORTED;
        }
 
-       if (!(session->conn->smb2.server.capabilities & SMB2_CAP_ENCRYPTION)) {
+       if (session->conn->smb2.server.cipher == 0) {
                return NT_STATUS_NOT_SUPPORTED;
        }
 
@@ -5050,6 +6145,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)
 {
@@ -5128,11 +6255,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,
@@ -5149,12 +6286,14 @@ void smb2cli_tcon_set_values(struct smbXcli_tcon *tcon,
        tcon->smb2.capabilities = capabilities;
        tcon->smb2.maximal_access = maximal_access;
 
+       tcon->smb2.should_sign = false;
        tcon->smb2.should_encrypt = false;
 
        if (session == NULL) {
                return;
        }
 
+       tcon->smb2.should_sign = session->smb2->should_sign;
        tcon->smb2.should_encrypt = session->smb2->should_encrypt;
 
        if (flags & SMB2_SHAREFLAG_ENCRYPT_DATA) {
@@ -5162,7 +6301,38 @@ void smb2cli_tcon_set_values(struct smbXcli_tcon *tcon,
        }
 }
 
+void smb2cli_tcon_should_sign(struct smbXcli_tcon *tcon,
+                             bool should_sign)
+{
+       tcon->smb2.should_sign = should_sign;
+}
+
+bool smb2cli_tcon_is_signing_on(struct smbXcli_tcon *tcon)
+{
+       if (tcon->smb2.should_encrypt) {
+               return true;
+       }
+
+       return tcon->smb2.should_sign;
+}
+
+void smb2cli_tcon_should_encrypt(struct smbXcli_tcon *tcon,
+                                bool should_encrypt)
+{
+       tcon->smb2.should_encrypt = should_encrypt;
+}
+
 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;
+}