CVE-2016-2115: s3:libsmb: let SMB_SIGNING_IPC_DEFAULT use "client ipc min/max protocol"
[samba.git] / source3 / libsmb / cliconnect.c
index c2fa9239efe6620606ddbff34613e929140c8250..2c351dda9c51238c006d1f45b64a5b3b63d5d79e 100644 (file)
 
 #include "includes.h"
 #include "libsmb/libsmb.h"
-#include "popt_common.h"
+#include "auth_info.h"
 #include "../libcli/auth/libcli_auth.h"
 #include "../libcli/auth/spnego.h"
 #include "smb_krb5.h"
-#include "../auth/ntlmssp/ntlmssp.h"
+#include "auth/credentials/credentials.h"
+#include "auth/gensec/gensec.h"
+#include "auth/ntlmssp/ntlmssp.h"
+#include "auth_generic.h"
 #include "libads/kerberos_proto.h"
 #include "krb5_env.h"
 #include "../lib/util/tevent_ntstatus.h"
 #include "async_smb.h"
 #include "libsmb/nmblib.h"
 #include "librpc/ndr/libndr.h"
-
-static const struct {
-       int prot;
-       const char name[24];
-} prots[10] = {
-       {PROTOCOL_CORE,         "PC NETWORK PROGRAM 1.0"},
-       {PROTOCOL_COREPLUS,     "MICROSOFT NETWORKS 1.03"},
-       {PROTOCOL_LANMAN1,      "MICROSOFT NETWORKS 3.0"},
-       {PROTOCOL_LANMAN1,      "LANMAN1.0"},
-       {PROTOCOL_LANMAN2,      "LM1.2X002"},
-       {PROTOCOL_LANMAN2,      "DOS LANMAN2.1"},
-       {PROTOCOL_LANMAN2,      "LANMAN2.1"},
-       {PROTOCOL_LANMAN2,      "Samba"},
-       {PROTOCOL_NT1,          "NT LANMAN 1.0"},
-       {PROTOCOL_NT1,          "NT LM 0.12"},
-};
+#include "../libcli/smb/smbXcli_base.h"
 
 #define STAR_SMBSERVER "*SMBSERVER"
 
@@ -60,15 +48,15 @@ static const struct {
 *******************************************************/
 
 static NTSTATUS smb_bytes_talloc_string(TALLOC_CTX *mem_ctx,
-                                       char *inbuf,
+                                       const uint8_t *hdr,
                                        char **dest,
                                        uint8_t *src,
                                        size_t srclen,
                                        ssize_t *destlen)
 {
        *destlen = clistr_pull_talloc(mem_ctx,
-                               inbuf,
-                               SVAL(inbuf, smb_flg2),
+                               (const char *)hdr,
+                               SVAL(hdr, HDR_FLG2),
                                dest,
                                (char *)src,
                                srclen,
@@ -86,18 +74,6 @@ static NTSTATUS smb_bytes_talloc_string(TALLOC_CTX *mem_ctx,
        return NT_STATUS_OK;
 }
 
-/**
- * Set the user session key for a connection
- * @param cli The cli structure to add it too
- * @param session_key The session key used.  (A copy of this is taken for the cli struct)
- *
- */
-
-static void cli_set_session_key (struct cli_state *cli, const DATA_BLOB session_key) 
-{
-       cli->user_session_key = data_blob(session_key.data, session_key.length);
-}
-
 /****************************************************************************
  Do an old lanman2 style session setup.
 ****************************************************************************/
@@ -122,7 +98,7 @@ static struct tevent_req *cli_session_setup_lanman2_send(
        uint16_t *vwv;
        uint8_t *bytes;
        char *tmp;
-       uint16_t sec_mode = cli_state_security_mode(cli);
+       uint16_t sec_mode = smb1cli_conn_server_security_mode(cli->conn);
 
        req = tevent_req_create(mem_ctx, &state,
                                struct cli_session_setup_lanman2_state);
@@ -152,7 +128,7 @@ static struct tevent_req *cli_session_setup_lanman2_send(
                        return tevent_req_post(req, ev);
                }
 
-               if (!SMBencrypt(pass, cli_state_server_challenge(cli),
+               if (!SMBencrypt(pass, smb1cli_conn_server_challenge(cli->conn),
                                (uint8_t *)lm_response.data)) {
                        DEBUG(1, ("Password is > 14 chars in length, and is "
                                  "therefore incompatible with Lanman "
@@ -177,7 +153,7 @@ static struct tevent_req *cli_session_setup_lanman2_send(
                 * Plaintext mode needed, assume plaintext supplied.
                 */
                buf = talloc_array(talloc_tos(), uint8_t, 0);
-               buf = smb_bytes_push_str(buf, cli_ucs2(cli), pass, passlen+1,
+               buf = smb_bytes_push_str(buf, smbXcli_conn_use_unicode(cli->conn), pass, passlen+1,
                                         &converted_size);
                if (tevent_req_nomem(buf, req)) {
                        return tevent_req_post(req, ev);
@@ -195,7 +171,7 @@ static struct tevent_req *cli_session_setup_lanman2_send(
        SSVAL(vwv+2, 0, CLI_BUFFER_SIZE);
        SSVAL(vwv+3, 0, 2);
        SSVAL(vwv+4, 0, 1);
-       SIVAL(vwv+5, 0, cli_state_server_session_key(cli));
+       SIVAL(vwv+5, 0, smb1cli_conn_server_session_key(cli->conn));
        SSVAL(vwv+7, 0, lm_response.length);
 
        bytes = talloc_array(state, uint8_t, lm_response.length);
@@ -211,7 +187,7 @@ static struct tevent_req *cli_session_setup_lanman2_send(
        if (tevent_req_nomem(tmp, req)) {
                return tevent_req_post(req, ev);
        }
-       bytes = smb_bytes_push_str(bytes, cli_ucs2(cli), tmp, strlen(tmp)+1,
+       bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), tmp, strlen(tmp)+1,
                                   NULL);
        TALLOC_FREE(tmp);
 
@@ -219,10 +195,10 @@ static struct tevent_req *cli_session_setup_lanman2_send(
        if (tevent_req_nomem(tmp, req)) {
                return tevent_req_post(req, ev);
        }
-       bytes = smb_bytes_push_str(bytes, cli_ucs2(cli), tmp, strlen(tmp)+1,
+       bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), tmp, strlen(tmp)+1,
                                   NULL);
-       bytes = smb_bytes_push_str(bytes, cli_ucs2(cli), "Unix", 5, NULL);
-       bytes = smb_bytes_push_str(bytes, cli_ucs2(cli), "Samba", 6, NULL);
+       bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), "Unix", 5, NULL);
+       bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), "Samba", 6, NULL);
 
        if (tevent_req_nomem(bytes, req)) {
                return tevent_req_post(req, ev);
@@ -246,7 +222,7 @@ static void cli_session_setup_lanman2_done(struct tevent_req *subreq)
        struct cli_state *cli = state->cli;
        uint32_t num_bytes;
        uint8_t *in;
-       char *inbuf;
+       uint8_t *inhdr;
        uint8_t *bytes;
        uint8_t *p;
        NTSTATUS status;
@@ -262,14 +238,13 @@ static void cli_session_setup_lanman2_done(struct tevent_req *subreq)
                return;
        }
 
-       inbuf = (char *)in;
+       inhdr = in + NBT_HDR_SIZE;
        p = bytes;
 
-       cli_state_set_uid(state->cli, SVAL(inbuf, smb_uid));
-       cli->is_guestlogin = ((SVAL(vwv+2, 0) & 1) != 0);
+       cli_state_set_uid(state->cli, SVAL(inhdr, HDR_UID));
 
        status = smb_bytes_talloc_string(cli,
-                                       inbuf,
+                                       inhdr,
                                        &cli->server_os,
                                        p,
                                        bytes+num_bytes-p,
@@ -282,7 +257,7 @@ static void cli_session_setup_lanman2_done(struct tevent_req *subreq)
        p += ret;
 
        status = smb_bytes_talloc_string(cli,
-                                       inbuf,
+                                       inhdr,
                                        &cli->server_type,
                                        p,
                                        bytes+num_bytes-p,
@@ -295,7 +270,7 @@ static void cli_session_setup_lanman2_done(struct tevent_req *subreq)
        p += ret;
 
        status = smb_bytes_talloc_string(cli,
-                                       inbuf,
+                                       inhdr,
                                        &cli->server_domain,
                                        p,
                                        bytes+num_bytes-p,
@@ -307,10 +282,6 @@ static void cli_session_setup_lanman2_done(struct tevent_req *subreq)
        }
        p += ret;
 
-       status = cli_set_username(cli, state->user);
-       if (tevent_req_nterror(req, status)) {
-               return;
-       }
        tevent_req_done(req);
 }
 
@@ -319,40 +290,6 @@ static NTSTATUS cli_session_setup_lanman2_recv(struct tevent_req *req)
        return tevent_req_simple_recv_ntstatus(req);
 }
 
-static NTSTATUS cli_session_setup_lanman2(struct cli_state *cli, const char *user,
-                                         const char *pass, size_t passlen,
-                                         const char *workgroup)
-{
-       TALLOC_CTX *frame = talloc_stackframe();
-       struct event_context *ev;
-       struct tevent_req *req;
-       NTSTATUS status = NT_STATUS_NO_MEMORY;
-
-       if (cli_has_async_calls(cli)) {
-               /*
-                * Can't use sync call while an async call is in flight
-                */
-               status = NT_STATUS_INVALID_PARAMETER;
-               goto fail;
-       }
-       ev = event_context_init(frame);
-       if (ev == NULL) {
-               goto fail;
-       }
-       req = cli_session_setup_lanman2_send(frame, ev, cli, user, pass, passlen,
-                                            workgroup);
-       if (req == NULL) {
-               goto fail;
-       }
-       if (!tevent_req_poll_ntstatus(req, ev, &status)) {
-               goto fail;
-       }
-       status = cli_session_setup_lanman2_recv(req);
- fail:
-       TALLOC_FREE(frame);
-       return status;
-}
-
 /****************************************************************************
  Work out suitable capabilities to offer the server.
 ****************************************************************************/
@@ -360,16 +297,20 @@ static NTSTATUS cli_session_setup_lanman2(struct cli_state *cli, const char *use
 static uint32_t cli_session_setup_capabilities(struct cli_state *cli,
                                               uint32_t sesssetup_capabilities)
 {
-       uint32_t client_capabilities = cli_state_capabilities(cli);
+       uint32_t client_capabilities = smb1cli_conn_capabilities(cli->conn);
 
        /*
         * We only send capabilities based on the mask for:
         * - client only flags
         * - flags used in both directions
         *
-        * We do not echo the server only flags.
+        * We do not echo the server only flags, except some legacy flags.
+        *
+        * SMB_CAP_LEGACY_CLIENT_MASK contains CAP_LARGE_READX and
+        * CAP_LARGE_WRITEX in order to allow us to do large reads
+        * against old Samba releases (<= 3.6.x).
         */
-       client_capabilities &= (SMB_CAP_BOTH_MASK | SMB_CAP_CLIENT_MASK);
+       client_capabilities &= (SMB_CAP_BOTH_MASK | SMB_CAP_LEGACY_CLIENT_MASK);
 
        /*
         * Session Setup specific flags CAP_DYNAMIC_REAUTH
@@ -397,7 +338,7 @@ struct cli_session_setup_guest_state {
 static void cli_session_setup_guest_done(struct tevent_req *subreq);
 
 struct tevent_req *cli_session_setup_guest_create(TALLOC_CTX *mem_ctx,
-                                                 struct event_context *ev,
+                                                 struct tevent_context *ev,
                                                  struct cli_state *cli,
                                                  struct tevent_req **psmbreq)
 {
@@ -420,7 +361,7 @@ struct tevent_req *cli_session_setup_guest_create(TALLOC_CTX *mem_ctx,
        SSVAL(vwv+2, 0, CLI_BUFFER_SIZE);
        SSVAL(vwv+3, 0, 2);
        SSVAL(vwv+4, 0, cli_state_get_vc_num(cli));
-       SIVAL(vwv+5, 0, cli_state_server_session_key(cli));
+       SIVAL(vwv+5, 0, smb1cli_conn_server_session_key(cli->conn));
        SSVAL(vwv+7, 0, 0);
        SSVAL(vwv+8, 0, 0);
        SSVAL(vwv+9, 0, 0);
@@ -429,12 +370,12 @@ struct tevent_req *cli_session_setup_guest_create(TALLOC_CTX *mem_ctx,
 
        bytes = talloc_array(state, uint8_t, 0);
 
-       bytes = smb_bytes_push_str(bytes, cli_ucs2(cli), "",  1, /* username */
+       bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), "",  1, /* username */
                                   NULL);
-       bytes = smb_bytes_push_str(bytes, cli_ucs2(cli), "", 1, /* workgroup */
+       bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), "", 1, /* workgroup */
                                   NULL);
-       bytes = smb_bytes_push_str(bytes, cli_ucs2(cli), "Unix", 5, NULL);
-       bytes = smb_bytes_push_str(bytes, cli_ucs2(cli), "Samba", 6, NULL);
+       bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), "Unix", 5, NULL);
+       bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), "Samba", 6, NULL);
 
        if (bytes == NULL) {
                TALLOC_FREE(req);
@@ -456,7 +397,7 @@ struct tevent_req *cli_session_setup_guest_create(TALLOC_CTX *mem_ctx,
 }
 
 struct tevent_req *cli_session_setup_guest_send(TALLOC_CTX *mem_ctx,
-                                               struct event_context *ev,
+                                               struct tevent_context *ev,
                                                struct cli_state *cli)
 {
        struct tevent_req *req, *subreq;
@@ -467,8 +408,8 @@ struct tevent_req *cli_session_setup_guest_send(TALLOC_CTX *mem_ctx,
                return NULL;
        }
 
-       status = cli_smb_req_send(subreq);
-       if (NT_STATUS_IS_OK(status)) {
+       status = smb1cli_req_chain_submit(&subreq, 1);
+       if (!NT_STATUS_IS_OK(status)) {
                tevent_req_nterror(req, status);
                return tevent_req_post(req, ev);
        }
@@ -484,7 +425,7 @@ static void cli_session_setup_guest_done(struct tevent_req *subreq)
        struct cli_state *cli = state->cli;
        uint32_t num_bytes;
        uint8_t *in;
-       char *inbuf;
+       uint8_t *inhdr;
        uint8_t *bytes;
        uint8_t *p;
        NTSTATUS status;
@@ -500,14 +441,13 @@ static void cli_session_setup_guest_done(struct tevent_req *subreq)
                return;
        }
 
-       inbuf = (char *)in;
+       inhdr = in + NBT_HDR_SIZE;
        p = bytes;
 
-       cli_state_set_uid(state->cli, SVAL(inbuf, smb_uid));
-       cli->is_guestlogin = ((SVAL(vwv+2, 0) & 1) != 0);
+       cli_state_set_uid(state->cli, SVAL(inhdr, HDR_UID));
 
        status = smb_bytes_talloc_string(cli,
-                                       inbuf,
+                                       inhdr,
                                        &cli->server_os,
                                        p,
                                        bytes+num_bytes-p,
@@ -520,7 +460,7 @@ static void cli_session_setup_guest_done(struct tevent_req *subreq)
        p += ret;
 
        status = smb_bytes_talloc_string(cli,
-                                       inbuf,
+                                       inhdr,
                                        &cli->server_type,
                                        p,
                                        bytes+num_bytes-p,
@@ -533,7 +473,7 @@ static void cli_session_setup_guest_done(struct tevent_req *subreq)
        p += ret;
 
        status = smb_bytes_talloc_string(cli,
-                                       inbuf,
+                                       inhdr,
                                        &cli->server_domain,
                                        p,
                                        bytes+num_bytes-p,
@@ -545,11 +485,6 @@ static void cli_session_setup_guest_done(struct tevent_req *subreq)
        }
        p += ret;
 
-       status = cli_set_username(cli, "");
-       if (!NT_STATUS_IS_OK(status)) {
-               tevent_req_nterror(req, status);
-               return;
-       }
        tevent_req_done(req);
 }
 
@@ -558,44 +493,6 @@ NTSTATUS cli_session_setup_guest_recv(struct tevent_req *req)
        return tevent_req_simple_recv_ntstatus(req);
 }
 
-static NTSTATUS cli_session_setup_guest(struct cli_state *cli)
-{
-       TALLOC_CTX *frame = talloc_stackframe();
-       struct event_context *ev;
-       struct tevent_req *req;
-       NTSTATUS status = NT_STATUS_OK;
-
-       if (cli_has_async_calls(cli)) {
-               /*
-                * Can't use sync call while an async call is in flight
-                */
-               status = NT_STATUS_INVALID_PARAMETER;
-               goto fail;
-       }
-
-       ev = event_context_init(frame);
-       if (ev == NULL) {
-               status = NT_STATUS_NO_MEMORY;
-               goto fail;
-       }
-
-       req = cli_session_setup_guest_send(frame, ev, cli);
-       if (req == NULL) {
-               status = NT_STATUS_NO_MEMORY;
-               goto fail;
-       }
-
-       if (!tevent_req_poll(req, ev)) {
-               status = map_nt_error_from_unix(errno);
-               goto fail;
-       }
-
-       status = cli_session_setup_guest_recv(req);
- fail:
-       TALLOC_FREE(frame);
-       return status;
-}
-
 /****************************************************************************
  Do a NT1 plaintext session setup.
 ****************************************************************************/
@@ -635,7 +532,7 @@ static struct tevent_req *cli_session_setup_plain_send(
        SSVAL(vwv+2, 0, CLI_BUFFER_SIZE);
        SSVAL(vwv+3, 0, 2);
        SSVAL(vwv+4, 0, cli_state_get_vc_num(cli));
-       SIVAL(vwv+5, 0, cli_state_server_session_key(cli));
+       SIVAL(vwv+5, 0, smb1cli_conn_server_session_key(cli->conn));
        SSVAL(vwv+7, 0, 0);
        SSVAL(vwv+8, 0, 0);
        SSVAL(vwv+9, 0, 0);
@@ -643,18 +540,18 @@ static struct tevent_req *cli_session_setup_plain_send(
        SIVAL(vwv+11, 0, cli_session_setup_capabilities(cli, 0));
 
        bytes = talloc_array(state, uint8_t, 0);
-       bytes = smb_bytes_push_str(bytes, cli_ucs2(cli), pass, strlen(pass)+1,
+       bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), pass, strlen(pass)+1,
                                   &passlen);
        if (tevent_req_nomem(bytes, req)) {
                return tevent_req_post(req, ev);
        }
-       SSVAL(vwv + (cli_ucs2(cli) ? 8 : 7), 0, passlen);
+       SSVAL(vwv + (smbXcli_conn_use_unicode(cli->conn) ? 8 : 7), 0, passlen);
 
-       bytes = smb_bytes_push_str(bytes, cli_ucs2(cli),
+       bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn),
                                   user, strlen(user)+1, NULL);
-       bytes = smb_bytes_push_str(bytes, cli_ucs2(cli),
+       bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn),
                                   workgroup, strlen(workgroup)+1, NULL);
-       bytes = smb_bytes_push_str(bytes, cli_ucs2(cli),
+       bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn),
                                   "Unix", 5, NULL);
 
        version = talloc_asprintf(talloc_tos(), "Samba %s",
@@ -662,7 +559,7 @@ static struct tevent_req *cli_session_setup_plain_send(
        if (tevent_req_nomem(version, req)){
                return tevent_req_post(req, ev);
        }
-       bytes = smb_bytes_push_str(bytes, cli_ucs2(cli),
+       bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn),
                                   version, strlen(version)+1, NULL);
        TALLOC_FREE(version);
 
@@ -688,7 +585,7 @@ static void cli_session_setup_plain_done(struct tevent_req *subreq)
        struct cli_state *cli = state->cli;
        uint32_t num_bytes;
        uint8_t *in;
-       char *inbuf;
+       uint8_t *inhdr;
        uint8_t *bytes;
        uint8_t *p;
        NTSTATUS status;
@@ -703,14 +600,13 @@ static void cli_session_setup_plain_done(struct tevent_req *subreq)
                return;
        }
 
-       inbuf = (char *)in;
+       inhdr = in + NBT_HDR_SIZE;
        p = bytes;
 
-       cli_state_set_uid(state->cli, SVAL(inbuf, smb_uid));
-       cli->is_guestlogin = ((SVAL(vwv+2, 0) & 1) != 0);
+       cli_state_set_uid(state->cli, SVAL(inhdr, HDR_UID));
 
        status = smb_bytes_talloc_string(cli,
-                                       inbuf,
+                                       inhdr,
                                        &cli->server_os,
                                        p,
                                        bytes+num_bytes-p,
@@ -723,7 +619,7 @@ static void cli_session_setup_plain_done(struct tevent_req *subreq)
        p += ret;
 
        status = smb_bytes_talloc_string(cli,
-                                       inbuf,
+                                       inhdr,
                                        &cli->server_type,
                                        p,
                                        bytes+num_bytes-p,
@@ -736,7 +632,7 @@ static void cli_session_setup_plain_done(struct tevent_req *subreq)
        p += ret;
 
        status = smb_bytes_talloc_string(cli,
-                                       inbuf,
+                                       inhdr,
                                        &cli->server_domain,
                                        p,
                                        bytes+num_bytes-p,
@@ -748,11 +644,6 @@ static void cli_session_setup_plain_done(struct tevent_req *subreq)
        }
        p += ret;
 
-       status = cli_set_username(cli, state->user);
-       if (tevent_req_nterror(req, status)) {
-               return;
-       }
-
        tevent_req_done(req);
 }
 
@@ -761,40 +652,6 @@ static NTSTATUS cli_session_setup_plain_recv(struct tevent_req *req)
        return tevent_req_simple_recv_ntstatus(req);
 }
 
-static NTSTATUS cli_session_setup_plain(struct cli_state *cli,
-                                       const char *user, const char *pass,
-                                       const char *workgroup)
-{
-       TALLOC_CTX *frame = talloc_stackframe();
-       struct event_context *ev;
-       struct tevent_req *req;
-       NTSTATUS status = NT_STATUS_NO_MEMORY;
-
-       if (cli_has_async_calls(cli)) {
-               /*
-                * Can't use sync call while an async call is in flight
-                */
-               status = NT_STATUS_INVALID_PARAMETER;
-               goto fail;
-       }
-       ev = event_context_init(frame);
-       if (ev == NULL) {
-               goto fail;
-       }
-       req = cli_session_setup_plain_send(frame, ev, cli, user, pass,
-                                          workgroup);
-       if (req == NULL) {
-               goto fail;
-       }
-       if (!tevent_req_poll_ntstatus(req, ev, &status)) {
-               goto fail;
-       }
-       status = cli_session_setup_plain_recv(req);
- fail:
-       TALLOC_FREE(frame);
-       return status;
-}
-
 /****************************************************************************
    do a NT1 NTLM/LM encrypted session setup - for when extended security
    is not negotiated.
@@ -848,7 +705,7 @@ static struct tevent_req *cli_session_setup_nt1_send(
                        DATA_BLOB names_blob;
 
                        server_chal =
-                               data_blob_const(cli_state_server_challenge(cli),
+                               data_blob_const(smb1cli_conn_server_challenge(cli->conn),
                                                8);
 
                        /*
@@ -887,7 +744,7 @@ static struct tevent_req *cli_session_setup_nt1_send(
                                return tevent_req_post(req, ev);
                        }
 
-                       SMBNTencrypt(pass, cli_state_server_challenge(cli),
+                       SMBNTencrypt(pass, smb1cli_conn_server_challenge(cli->conn),
                                     nt_response.data);
 #endif
                        /* non encrypted password supplied. Ignore ntpass. */
@@ -899,7 +756,7 @@ static struct tevent_req *cli_session_setup_nt1_send(
                                }
 
                                if (!SMBencrypt(pass,
-                                               cli_state_server_challenge(cli),
+                                               smb1cli_conn_server_challenge(cli->conn),
                                                lm_response.data)) {
                                        /*
                                         * Oops, the LM response is
@@ -977,7 +834,7 @@ static struct tevent_req *cli_session_setup_nt1_send(
        SSVAL(vwv+2, 0, CLI_BUFFER_SIZE);
        SSVAL(vwv+3, 0, 2);
        SSVAL(vwv+4, 0, cli_state_get_vc_num(cli));
-       SIVAL(vwv+5, 0, cli_state_server_session_key(cli));
+       SIVAL(vwv+5, 0, smb1cli_conn_server_session_key(cli->conn));
        SSVAL(vwv+7, 0, lm_response.length);
        SSVAL(vwv+8, 0, nt_response.length);
        SSVAL(vwv+9, 0, 0);
@@ -999,7 +856,7 @@ static struct tevent_req *cli_session_setup_nt1_send(
        data_blob_free(&lm_response);
        data_blob_free(&nt_response);
 
-       bytes = smb_bytes_push_str(bytes, cli_ucs2(cli),
+       bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn),
                                   user, strlen(user)+1, NULL);
 
        /*
@@ -1009,13 +866,13 @@ static struct tevent_req *cli_session_setup_nt1_send(
        if (tevent_req_nomem(workgroup_upper, req)) {
                return tevent_req_post(req, ev);
        }
-       bytes = smb_bytes_push_str(bytes, cli_ucs2(cli),
+       bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn),
                                   workgroup_upper, strlen(workgroup_upper)+1,
                                   NULL);
        TALLOC_FREE(workgroup_upper);
 
-       bytes = smb_bytes_push_str(bytes, cli_ucs2(cli), "Unix", 5, NULL);
-       bytes = smb_bytes_push_str(bytes, cli_ucs2(cli), "Samba", 6, NULL);
+       bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), "Unix", 5, NULL);
+       bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), "Samba", 6, NULL);
        if (tevent_req_nomem(bytes, req)) {
                return tevent_req_post(req, ev);
        }
@@ -1038,7 +895,7 @@ static void cli_session_setup_nt1_done(struct tevent_req *subreq)
        struct cli_state *cli = state->cli;
        uint32_t num_bytes;
        uint8_t *in;
-       char *inbuf;
+       uint8_t *inhdr;
        uint8_t *bytes;
        uint8_t *p;
        NTSTATUS status;
@@ -1054,14 +911,13 @@ static void cli_session_setup_nt1_done(struct tevent_req *subreq)
                return;
        }
 
-       inbuf = (char *)in;
+       inhdr = in + NBT_HDR_SIZE;
        p = bytes;
 
-       cli_state_set_uid(state->cli, SVAL(inbuf, smb_uid));
-       cli->is_guestlogin = ((SVAL(vwv+2, 0) & 1) != 0);
+       cli_state_set_uid(state->cli, SVAL(inhdr, HDR_UID));
 
        status = smb_bytes_talloc_string(cli,
-                                       inbuf,
+                                       inhdr,
                                        &cli->server_os,
                                        p,
                                        bytes+num_bytes-p,
@@ -1073,7 +929,7 @@ static void cli_session_setup_nt1_done(struct tevent_req *subreq)
        p += ret;
 
        status = smb_bytes_talloc_string(cli,
-                                       inbuf,
+                                       inhdr,
                                        &cli->server_type,
                                        p,
                                        bytes+num_bytes-p,
@@ -1085,7 +941,7 @@ static void cli_session_setup_nt1_done(struct tevent_req *subreq)
        p += ret;
 
        status = smb_bytes_talloc_string(cli,
-                                       inbuf,
+                                       inhdr,
                                        &cli->server_domain,
                                        p,
                                        bytes+num_bytes-p,
@@ -1096,18 +952,19 @@ static void cli_session_setup_nt1_done(struct tevent_req *subreq)
        }
        p += ret;
 
-       status = cli_set_username(cli, state->user);
-       if (tevent_req_nterror(req, status)) {
-               return;
-       }
-       if (cli_simple_set_signing(cli, state->session_key, state->response)
-           && !cli_check_sign_mac(cli, (char *)in, 1)) {
+       if (smb1cli_conn_activate_signing(cli->conn, state->session_key, state->response)
+           && !smb1cli_conn_check_signing(cli->conn, (uint8_t *)in, 1)) {
                tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
                return;
        }
        if (state->session_key.data) {
-               /* Have plaintext orginal */
-               cli_set_session_key(cli, state->session_key);
+               struct smbXcli_session *session = state->cli->smb1.session;
+
+               status = smb1cli_session_set_session_key(session,
+                               state->session_key);
+               if (tevent_req_nterror(req, status)) {
+                       return;
+               }
        }
        tevent_req_done(req);
 }
@@ -1117,41 +974,6 @@ static NTSTATUS cli_session_setup_nt1_recv(struct tevent_req *req)
        return tevent_req_simple_recv_ntstatus(req);
 }
 
-static NTSTATUS cli_session_setup_nt1(struct cli_state *cli, const char *user,
-                                     const char *pass, size_t passlen,
-                                     const char *ntpass, size_t ntpasslen,
-                                     const char *workgroup)
-{
-       TALLOC_CTX *frame = talloc_stackframe();
-       struct event_context *ev;
-       struct tevent_req *req;
-       NTSTATUS status = NT_STATUS_NO_MEMORY;
-
-       if (cli_has_async_calls(cli)) {
-               /*
-                * Can't use sync call while an async call is in flight
-                */
-               status = NT_STATUS_INVALID_PARAMETER;
-               goto fail;
-       }
-       ev = event_context_init(frame);
-       if (ev == NULL) {
-               goto fail;
-       }
-       req = cli_session_setup_nt1_send(frame, ev, cli, user, pass, passlen,
-                                        ntpass, ntpasslen, workgroup);
-       if (req == NULL) {
-               goto fail;
-       }
-       if (!tevent_req_poll_ntstatus(req, ev, &status)) {
-               goto fail;
-       }
-       status = cli_session_setup_nt1_recv(req);
- fail:
-       TALLOC_FREE(frame);
-       return status;
-}
-
 /* The following is calculated from :
  * (smb_size-4) = 35
  * (smb_wcnt * 2) = 24 (smb_wcnt == 12 in cli_session_setup_blob_send() )
@@ -1169,8 +991,11 @@ struct cli_sesssetup_blob_state {
        uint16_t vwv[12];
        uint8_t *buf;
 
+       DATA_BLOB smb2_blob;
+       struct iovec *recv_iov;
+
        NTSTATUS status;
-       char *inbuf;
+       uint8_t *inbuf;
        DATA_BLOB ret_blob;
 };
 
@@ -1196,8 +1021,12 @@ static struct tevent_req *cli_sesssetup_blob_send(TALLOC_CTX *mem_ctx,
        state->blob = blob;
        state->cli = cli;
 
-       usable_space = cli_state_available_size(cli,
+       if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+               usable_space = UINT16_MAX;
+       } else {
+               usable_space = cli_state_available_size(cli,
                                BASE_SESSSETUP_BLOB_PACKET_SIZE);
+       }
 
        if (usable_space == 0) {
                DEBUG(1, ("cli_session_setup_blob: cli->max_xmit too small "
@@ -1222,6 +1051,32 @@ static bool cli_sesssetup_blob_next(struct cli_sesssetup_blob_state *state,
        struct tevent_req *subreq;
        uint16_t thistime;
 
+       thistime = MIN(state->blob.length, state->max_blob_size);
+
+       if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
+
+               state->smb2_blob.data = state->blob.data;
+               state->smb2_blob.length = thistime;
+
+               state->blob.data += thistime;
+               state->blob.length -= thistime;
+
+               subreq = smb2cli_session_setup_send(state, state->ev,
+                                                   state->cli->conn,
+                                                   state->cli->timeout,
+                                                   state->cli->smb2.session,
+                                                   0, /* in_flags */
+                                                   SMB2_CAP_DFS, /* in_capabilities */
+                                                   0, /* in_channel */
+                                                   0, /* in_previous_session_id */
+                                                   &state->smb2_blob);
+               if (subreq == NULL) {
+                       return false;
+               }
+               *psubreq = subreq;
+               return true;
+       }
+
        SCVAL(state->vwv+0, 0, 0xFF);
        SCVAL(state->vwv+0, 1, 0);
        SSVAL(state->vwv+1, 0, 0);
@@ -1230,7 +1085,6 @@ static bool cli_sesssetup_blob_next(struct cli_sesssetup_blob_state *state,
        SSVAL(state->vwv+4, 0, 1);
        SIVAL(state->vwv+5, 0, 0);
 
-       thistime = MIN(state->blob.length, state->max_blob_size);
        SSVAL(state->vwv+7, 0, thistime);
 
        SSVAL(state->vwv+8, 0, 0);
@@ -1246,9 +1100,9 @@ static bool cli_sesssetup_blob_next(struct cli_sesssetup_blob_state *state,
        state->blob.data += thistime;
        state->blob.length -= thistime;
 
-       state->buf = smb_bytes_push_str(state->buf, cli_ucs2(state->cli),
+       state->buf = smb_bytes_push_str(state->buf, smbXcli_conn_use_unicode(state->cli->conn),
                                        "Unix", 5, NULL);
-       state->buf = smb_bytes_push_str(state->buf, cli_ucs2(state->cli),
+       state->buf = smb_bytes_push_str(state->buf, smbXcli_conn_use_unicode(state->cli->conn),
                                        "Samba", 6, NULL);
        if (state->buf == NULL) {
                return false;
@@ -1277,11 +1131,19 @@ static void cli_sesssetup_blob_done(struct tevent_req *subreq)
        NTSTATUS status;
        uint8_t *p;
        uint16_t blob_length;
-       uint8_t *inbuf;
+       uint8_t *in;
+       uint8_t *inhdr;
        ssize_t ret;
 
-       status = cli_smb_recv(subreq, state, &inbuf, 4, &wct, &vwv,
-                             &num_bytes, &bytes);
+       if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
+               status = smb2cli_session_setup_recv(subreq, state,
+                                                   &state->recv_iov,
+                                                   &state->ret_blob);
+       } else {
+               status = cli_smb_recv(subreq, state, &in, 4, &wct, &vwv,
+                                     &num_bytes, &bytes);
+               TALLOC_FREE(state->buf);
+       }
        TALLOC_FREE(subreq);
        if (!NT_STATUS_IS_OK(status)
            && !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
@@ -1290,11 +1152,14 @@ static void cli_sesssetup_blob_done(struct tevent_req *subreq)
        }
 
        state->status = status;
-       TALLOC_FREE(state->buf);
 
-       state->inbuf = (char *)inbuf;
-       cli_state_set_uid(state->cli, SVAL(inbuf, smb_uid));
-       cli->is_guestlogin = ((SVAL(vwv+2, 0) & 1) != 0);
+       if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
+               goto next;
+       }
+
+       state->inbuf = in;
+       inhdr = in + NBT_HDR_SIZE;
+       cli_state_set_uid(state->cli, SVAL(inhdr, HDR_UID));
 
        blob_length = SVAL(vwv+3, 0);
        if (blob_length > num_bytes) {
@@ -1306,7 +1171,7 @@ static void cli_sesssetup_blob_done(struct tevent_req *subreq)
        p = bytes + blob_length;
 
        status = smb_bytes_talloc_string(cli,
-                                       (char *)inbuf,
+                                       inhdr,
                                        &cli->server_os,
                                        p,
                                        bytes+num_bytes-p,
@@ -1319,7 +1184,7 @@ static void cli_sesssetup_blob_done(struct tevent_req *subreq)
        p += ret;
 
        status = smb_bytes_talloc_string(cli,
-                                       (char *)inbuf,
+                                       inhdr,
                                        &cli->server_type,
                                        p,
                                        bytes+num_bytes-p,
@@ -1332,7 +1197,7 @@ static void cli_sesssetup_blob_done(struct tevent_req *subreq)
        p += ret;
 
        status = smb_bytes_talloc_string(cli,
-                                       (char *)inbuf,
+                                       inhdr,
                                        &cli->server_domain,
                                        p,
                                        bytes+num_bytes-p,
@@ -1344,6 +1209,7 @@ static void cli_sesssetup_blob_done(struct tevent_req *subreq)
        }
        p += ret;
 
+next:
        if (state->blob.length != 0) {
                /*
                 * More to send
@@ -1361,25 +1227,32 @@ static void cli_sesssetup_blob_done(struct tevent_req *subreq)
 static NTSTATUS cli_sesssetup_blob_recv(struct tevent_req *req,
                                        TALLOC_CTX *mem_ctx,
                                        DATA_BLOB *pblob,
-                                       char **pinbuf)
+                                       uint8_t **pinbuf,
+                                       struct iovec **precv_iov)
 {
        struct cli_sesssetup_blob_state *state = tevent_req_data(
                req, struct cli_sesssetup_blob_state);
        NTSTATUS status;
-       char *inbuf;
+       uint8_t *inbuf;
+       struct iovec *recv_iov;
 
        if (tevent_req_is_nterror(req, &status)) {
+               TALLOC_FREE(state->cli->smb2.session);
                cli_state_set_uid(state->cli, UID_FIELD_INVALID);
                return status;
        }
 
        inbuf = talloc_move(mem_ctx, &state->inbuf);
+       recv_iov = talloc_move(mem_ctx, &state->recv_iov);
        if (pblob != NULL) {
                *pblob = state->ret_blob;
        }
        if (pinbuf != NULL) {
                *pinbuf = inbuf;
        }
+       if (precv_iov != NULL) {
+               *precv_iov = recv_iov;
+       }
         /* could be NT_STATUS_MORE_PROCESSING_REQUIRED */
        return state->status;
 }
@@ -1394,326 +1267,407 @@ static void use_in_memory_ccache(void) {
        setenv(KRB5_ENV_CCNAME, "MEMORY:cliconnect", 1);
 }
 
+#endif /* HAVE_KRB5 */
+
 /****************************************************************************
- Do a spnego/kerberos encrypted session setup.
+ Do a spnego/NTLMSSP encrypted session setup.
 ****************************************************************************/
 
-struct cli_session_setup_kerberos_state {
+struct cli_session_setup_gensec_state {
+       struct tevent_context *ev;
        struct cli_state *cli;
-       DATA_BLOB negTokenTarg;
-       DATA_BLOB session_key_krb5;
-       ADS_STATUS ads_status;
+       struct auth_generic_state *auth_generic;
+       bool is_anonymous;
+       DATA_BLOB blob_in;
+       uint8_t *inbuf;
+       struct iovec *recv_iov;
+       DATA_BLOB blob_out;
+       bool local_ready;
+       bool remote_ready;
+       DATA_BLOB session_key;
 };
 
-static void cli_session_setup_kerberos_done(struct tevent_req *subreq);
+static int cli_session_setup_gensec_state_destructor(
+       struct cli_session_setup_gensec_state *state)
+{
+       TALLOC_FREE(state->auth_generic);
+       data_blob_clear_free(&state->session_key);
+       return 0;
+}
+
+static void cli_session_setup_gensec_local_next(struct tevent_req *req);
+static void cli_session_setup_gensec_local_done(struct tevent_req *subreq);
+static void cli_session_setup_gensec_remote_next(struct tevent_req *req);
+static void cli_session_setup_gensec_remote_done(struct tevent_req *subreq);
+static void cli_session_setup_gensec_ready(struct tevent_req *req);
 
-static struct tevent_req *cli_session_setup_kerberos_send(
+static struct tevent_req *cli_session_setup_gensec_send(
        TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli,
-       const char *principal)
+       const char *user, const char *pass, const char *domain,
+       enum credentials_use_kerberos krb5_state,
+       const char *target_service,
+       const char *target_hostname,
+       const char *target_principal)
 {
-       struct tevent_req *req, *subreq;
-       struct cli_session_setup_kerberos_state *state;
-       int rc;
-
-       DEBUG(2,("Doing kerberos session setup\n"));
+       struct tevent_req *req;
+       struct cli_session_setup_gensec_state *state;
+       NTSTATUS status;
+       bool use_spnego_principal = lp_client_use_spnego_principal();
 
        req = tevent_req_create(mem_ctx, &state,
-                               struct cli_session_setup_kerberos_state);
+                               struct cli_session_setup_gensec_state);
        if (req == NULL) {
                return NULL;
        }
+       state->ev = ev;
        state->cli = cli;
-       state->ads_status = ADS_SUCCESS;
 
-       /*
-        * Ok, this is cheating: spnego_gen_krb5_negTokenInit can block if
-        * we have to acquire a ticket. To be fixed later :-)
-        */
-       rc = spnego_gen_krb5_negTokenInit(state, principal, 0, &state->negTokenTarg,
-                                    &state->session_key_krb5, 0, NULL);
-       if (rc) {
-               DEBUG(1, ("cli_session_setup_kerberos: "
-                         "spnego_gen_krb5_negTokenInit failed: %s\n",
-                         error_message(rc)));
-               state->ads_status = ADS_ERROR_KRB5(rc);
-               tevent_req_nterror(req, NT_STATUS_UNSUCCESSFUL);
+       talloc_set_destructor(
+               state, cli_session_setup_gensec_state_destructor);
+
+       status = auth_generic_client_prepare(state, &state->auth_generic);
+       if (tevent_req_nterror(req, status)) {
                return tevent_req_post(req, ev);
        }
 
-#if 0
-       file_save("negTokenTarg.dat", state->negTokenTarg.data,
-                 state->negTokenTarg.length);
-#endif
+       gensec_want_feature(state->auth_generic->gensec_security,
+                           GENSEC_FEATURE_SESSION_KEY);
+       if (cli->use_ccache) {
+               gensec_want_feature(state->auth_generic->gensec_security,
+                                   GENSEC_FEATURE_NTLM_CCACHE);
+               if (pass != NULL && strlen(pass) == 0) {
+                       /*
+                        * some callers pass "" as no password
+                        *
+                        * GENSEC_FEATURE_NTLM_CCACHE only handles
+                        * NULL as no password.
+                        */
+                       pass = NULL;
+               }
+       }
 
-       subreq = cli_sesssetup_blob_send(state, ev, cli, state->negTokenTarg);
-       if (tevent_req_nomem(subreq, req)) {
+       status = auth_generic_set_username(state->auth_generic, user);
+       if (tevent_req_nterror(req, status)) {
+               return tevent_req_post(req, ev);
+       }
+
+       status = auth_generic_set_domain(state->auth_generic, domain);
+       if (tevent_req_nterror(req, status)) {
+               return tevent_req_post(req, ev);
+       }
+
+       if (cli->pw_nt_hash) {
+               struct samr_Password nt_hash;
+               size_t converted;
+               bool ok;
+
+               if (pass == NULL) {
+                       tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+                       return tevent_req_post(req, ev);
+               }
+
+               converted = strhex_to_str((char *)nt_hash.hash,
+                                         sizeof(nt_hash.hash),
+                                         pass, strlen(pass));
+               if (converted != sizeof(nt_hash.hash)) {
+                       tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+                       return tevent_req_post(req, ev);
+               }
+
+               ok = cli_credentials_set_nt_hash(state->auth_generic->credentials,
+                                                &nt_hash, CRED_SPECIFIED);
+               if (!ok) {
+                       tevent_req_oom(req);
+                       return tevent_req_post(req, ev);
+               }
+       } else {
+               status = auth_generic_set_password(state->auth_generic, pass);
+               if (tevent_req_nterror(req, status)) {
+                       return tevent_req_post(req, ev);
+               }
+       }
+
+       cli_credentials_set_kerberos_state(state->auth_generic->credentials,
+                                          krb5_state);
+
+       if (krb5_state == CRED_DONT_USE_KERBEROS) {
+               use_spnego_principal = false;
+       }
+
+       if (target_service != NULL) {
+               status = gensec_set_target_service(
+                               state->auth_generic->gensec_security,
+                               target_service);
+               if (tevent_req_nterror(req, status)) {
+                       return tevent_req_post(req, ev);
+               }
+       }
+
+       if (target_hostname != NULL) {
+               status = gensec_set_target_hostname(
+                               state->auth_generic->gensec_security,
+                               target_hostname);
+               if (tevent_req_nterror(req, status)) {
+                       return tevent_req_post(req, ev);
+               }
+       }
+
+       if (target_principal != NULL) {
+               status = gensec_set_target_principal(
+                               state->auth_generic->gensec_security,
+                               target_principal);
+               if (tevent_req_nterror(req, status)) {
+                       return tevent_req_post(req, ev);
+               }
+               use_spnego_principal = false;
+       } else if (target_service != NULL && target_hostname != NULL) {
+               use_spnego_principal = false;
+       }
+
+       if (use_spnego_principal) {
+               const DATA_BLOB *b;
+               b = smbXcli_conn_server_gss_blob(cli->conn);
+               if (b != NULL) {
+                       state->blob_in = *b;
+               }
+       }
+
+       state->is_anonymous = cli_credentials_is_anonymous(state->auth_generic->credentials);
+
+       status = auth_generic_client_start(state->auth_generic,
+                                          GENSEC_OID_SPNEGO);
+       if (tevent_req_nterror(req, status)) {
+               return tevent_req_post(req, ev);
+       }
+
+       if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+               state->cli->smb2.session = smbXcli_session_create(cli,
+                                                                 cli->conn);
+               if (tevent_req_nomem(state->cli->smb2.session, req)) {
+                       return tevent_req_post(req, ev);
+               }
+       }
+
+       cli_session_setup_gensec_local_next(req);
+       if (!tevent_req_is_in_progress(req)) {
                return tevent_req_post(req, ev);
        }
-       tevent_req_set_callback(subreq, cli_session_setup_kerberos_done, req);
+
        return req;
 }
 
-static void cli_session_setup_kerberos_done(struct tevent_req *subreq)
+static void cli_session_setup_gensec_local_next(struct tevent_req *req)
 {
-       struct tevent_req *req = tevent_req_callback_data(
-               subreq, struct tevent_req);
-       struct cli_session_setup_kerberos_state *state = tevent_req_data(
-               req, struct cli_session_setup_kerberos_state);
-       char *inbuf = NULL;
+       struct cli_session_setup_gensec_state *state =
+               tevent_req_data(req,
+               struct cli_session_setup_gensec_state);
+       struct tevent_req *subreq = NULL;
+
+       if (state->local_ready) {
+               tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+               return;
+       }
+
+       subreq = gensec_update_send(state, state->ev,
+                       state->auth_generic->gensec_security,
+                       state->blob_in);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq, cli_session_setup_gensec_local_done, req);
+}
+
+static void cli_session_setup_gensec_local_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req =
+               tevent_req_callback_data(subreq,
+               struct tevent_req);
+       struct cli_session_setup_gensec_state *state =
+               tevent_req_data(req,
+               struct cli_session_setup_gensec_state);
        NTSTATUS status;
 
-       status = cli_sesssetup_blob_recv(subreq, talloc_tos(), NULL, &inbuf);
+       status = gensec_update_recv(subreq, state, &state->blob_out);
        TALLOC_FREE(subreq);
-       if (!NT_STATUS_IS_OK(status)) {
+       state->blob_in = data_blob_null;
+       if (!NT_STATUS_IS_OK(status) &&
+           !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED))
+       {
                tevent_req_nterror(req, status);
                return;
        }
 
-       cli_set_session_key(state->cli, state->session_key_krb5);
+       if (NT_STATUS_IS_OK(status)) {
+               state->local_ready = true;
+       }
 
-       if (cli_simple_set_signing(state->cli, state->session_key_krb5,
-                                  data_blob_null)
-           && !cli_check_sign_mac(state->cli, inbuf, 1)) {
-               tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
+       if (state->local_ready && state->remote_ready) {
+               cli_session_setup_gensec_ready(req);
                return;
        }
 
-       tevent_req_done(req);
+       cli_session_setup_gensec_remote_next(req);
 }
 
-static ADS_STATUS cli_session_setup_kerberos_recv(struct tevent_req *req)
+static void cli_session_setup_gensec_remote_next(struct tevent_req *req)
 {
-       struct cli_session_setup_kerberos_state *state = tevent_req_data(
-               req, struct cli_session_setup_kerberos_state);
-       NTSTATUS status;
+       struct cli_session_setup_gensec_state *state =
+               tevent_req_data(req,
+               struct cli_session_setup_gensec_state);
+       struct tevent_req *subreq = NULL;
 
-       if (tevent_req_is_nterror(req, &status)) {
-               return ADS_ERROR_NT(status);
+       if (state->remote_ready) {
+               tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+               return;
        }
-       return state->ads_status;
-}
-
-static ADS_STATUS cli_session_setup_kerberos(struct cli_state *cli,
-                                            const char *principal)
-{
-       struct tevent_context *ev;
-       struct tevent_req *req;
-       ADS_STATUS status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
-
-       if (cli_has_async_calls(cli)) {
-               return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
-       }
-       ev = tevent_context_init(talloc_tos());
-       if (ev == NULL) {
-               goto fail;
-       }
-       req = cli_session_setup_kerberos_send(ev, ev, cli, principal);
-       if (req == NULL) {
-               goto fail;
-       }
-       if (!tevent_req_poll(req, ev)) {
-               status = ADS_ERROR_SYSTEM(errno);
-               goto fail;
-       }
-       status = cli_session_setup_kerberos_recv(req);
-fail:
-       TALLOC_FREE(ev);
-       return status;
-}
-#endif /* HAVE_KRB5 */
-
-/****************************************************************************
- Do a spnego/NTLMSSP encrypted session setup.
-****************************************************************************/
 
-struct cli_session_setup_ntlmssp_state {
-       struct tevent_context *ev;
-       struct cli_state *cli;
-       struct ntlmssp_state *ntlmssp_state;
-       int turn;
-       DATA_BLOB blob_out;
-};
-
-static int cli_session_setup_ntlmssp_state_destructor(
-       struct cli_session_setup_ntlmssp_state *state)
-{
-       if (state->ntlmssp_state != NULL) {
-               TALLOC_FREE(state->ntlmssp_state);
+       subreq = cli_sesssetup_blob_send(state, state->ev,
+                                        state->cli, state->blob_out);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
        }
-       return 0;
+       tevent_req_set_callback(subreq,
+                               cli_session_setup_gensec_remote_done,
+                               req);
 }
 
-static void cli_session_setup_ntlmssp_done(struct tevent_req *req);
-
-static struct tevent_req *cli_session_setup_ntlmssp_send(
-       TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli,
-       const char *user, const char *pass, const char *domain)
+static void cli_session_setup_gensec_remote_done(struct tevent_req *subreq)
 {
-       struct tevent_req *req, *subreq;
-       struct cli_session_setup_ntlmssp_state *state;
+       struct tevent_req *req =
+               tevent_req_callback_data(subreq,
+               struct tevent_req);
+       struct cli_session_setup_gensec_state *state =
+               tevent_req_data(req,
+               struct cli_session_setup_gensec_state);
        NTSTATUS status;
-       DATA_BLOB blob_out;
-       const char *OIDs_ntlm[] = {OID_NTLMSSP, NULL};
-
-       req = tevent_req_create(mem_ctx, &state,
-                               struct cli_session_setup_ntlmssp_state);
-       if (req == NULL) {
-               return NULL;
-       }
-       state->ev = ev;
-       state->cli = cli;
-       state->turn = 1;
 
-       state->ntlmssp_state = NULL;
-       talloc_set_destructor(
-               state, cli_session_setup_ntlmssp_state_destructor);
+       TALLOC_FREE(state->inbuf);
+       TALLOC_FREE(state->recv_iov);
 
-       status = ntlmssp_client_start(state,
-                                     lp_netbios_name(),
-                                     lp_workgroup(),
-                                     lp_client_ntlmv2_auth(),
-                                     &state->ntlmssp_state);
-       if (!NT_STATUS_IS_OK(status)) {
-               goto fail;
-       }
-       ntlmssp_want_feature(state->ntlmssp_state,
-                            NTLMSSP_FEATURE_SESSION_KEY);
-       if (cli->use_ccache) {
-               ntlmssp_want_feature(state->ntlmssp_state,
-                                    NTLMSSP_FEATURE_CCACHE);
-       }
-       status = ntlmssp_set_username(state->ntlmssp_state, user);
-       if (!NT_STATUS_IS_OK(status)) {
-               goto fail;
-       }
-       status = ntlmssp_set_domain(state->ntlmssp_state, domain);
-       if (!NT_STATUS_IS_OK(status)) {
-               goto fail;
-       }
-       status = ntlmssp_set_password(state->ntlmssp_state, pass);
-       if (!NT_STATUS_IS_OK(status)) {
-               goto fail;
-       }
-       status = ntlmssp_update(state->ntlmssp_state, data_blob_null,
-                               &blob_out);
-       if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
-               goto fail;
+       status = cli_sesssetup_blob_recv(subreq, state, &state->blob_in,
+                                        &state->inbuf, &state->recv_iov);
+       TALLOC_FREE(subreq);
+       data_blob_free(&state->blob_out);
+       if (!NT_STATUS_IS_OK(status) &&
+           !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED))
+       {
+               tevent_req_nterror(req, status);
+               return;
        }
 
-       state->blob_out = spnego_gen_negTokenInit(state, OIDs_ntlm, &blob_out, NULL);
-       data_blob_free(&blob_out);
+       if (NT_STATUS_IS_OK(status)) {
+               state->remote_ready = true;
+       }
 
-       subreq = cli_sesssetup_blob_send(state, ev, cli, state->blob_out);
-       if (tevent_req_nomem(subreq, req)) {
-               return tevent_req_post(req, ev);
+       if (state->local_ready && state->remote_ready) {
+               cli_session_setup_gensec_ready(req);
+               return;
        }
-       tevent_req_set_callback(subreq, cli_session_setup_ntlmssp_done, req);
-       return req;
-fail:
-       tevent_req_nterror(req, status);
-       return tevent_req_post(req, ev);
+
+       cli_session_setup_gensec_local_next(req);
 }
 
-static void cli_session_setup_ntlmssp_done(struct tevent_req *subreq)
+static void cli_session_setup_gensec_ready(struct tevent_req *req)
 {
-       struct tevent_req *req = tevent_req_callback_data(
-               subreq, struct tevent_req);
-       struct cli_session_setup_ntlmssp_state *state = tevent_req_data(
-               req, struct cli_session_setup_ntlmssp_state);
-       DATA_BLOB blob_in, msg_in, blob_out;
-       char *inbuf = NULL;
-       bool parse_ret;
+       struct cli_session_setup_gensec_state *state =
+               tevent_req_data(req,
+               struct cli_session_setup_gensec_state);
+       const char *server_domain = NULL;
        NTSTATUS status;
 
-       status = cli_sesssetup_blob_recv(subreq, talloc_tos(), &blob_in,
-                                        &inbuf);
-       TALLOC_FREE(subreq);
-       data_blob_free(&state->blob_out);
+       if (state->blob_in.length != 0) {
+               tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+               return;
+       }
 
-       if (NT_STATUS_IS_OK(status)) {
-               if (state->cli->server_domain[0] == '\0') {
-                       TALLOC_FREE(state->cli->server_domain);
-                       state->cli->server_domain = talloc_strdup(state->cli,
-                                               state->ntlmssp_state->server.netbios_domain);
-                       if (state->cli->server_domain == NULL) {
-                               tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
-                               return;
-                       }
-               }
-               cli_set_session_key(
-                       state->cli, state->ntlmssp_state->session_key);
+       if (state->blob_out.length != 0) {
+               tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+               return;
+       }
 
-               if (cli_simple_set_signing(
-                           state->cli, state->ntlmssp_state->session_key,
-                           data_blob_null)
-                   && !cli_check_sign_mac(state->cli, inbuf, 1)) {
-                       tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
+       /*
+        * gensec_ntlmssp_server_domain() returns NULL
+        * if NTLMSSP is not used.
+        *
+        * We can remove this later
+        * and leave the server domain empty for SMB2 and above
+        * in future releases.
+        */
+       server_domain = gensec_ntlmssp_server_domain(
+                               state->auth_generic->gensec_security);
+
+       if (state->cli->server_domain[0] == '\0' && server_domain != NULL) {
+               TALLOC_FREE(state->cli->server_domain);
+               state->cli->server_domain = talloc_strdup(state->cli,
+                                       server_domain);
+               if (state->cli->server_domain == NULL) {
+                       tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
                        return;
                }
-               TALLOC_FREE(state->ntlmssp_state);
-               tevent_req_done(req);
-               return;
-       }
-       if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
-               tevent_req_nterror(req, status);
-               return;
        }
 
-       if (blob_in.length == 0) {
-               tevent_req_nterror(req, NT_STATUS_UNSUCCESSFUL);
+       status = gensec_session_key(state->auth_generic->gensec_security,
+                                   state, &state->session_key);
+       if (tevent_req_nterror(req, status)) {
                return;
        }
 
-       if ((state->turn == 1)
-           && NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
-               DATA_BLOB tmp_blob = data_blob_null;
-               /* the server might give us back two challenges */
-               parse_ret = spnego_parse_challenge(state, blob_in, &msg_in,
-                                                  &tmp_blob);
-               data_blob_free(&tmp_blob);
-       } else {
-               parse_ret = spnego_parse_auth_response(state, blob_in, status,
-                                                      OID_NTLMSSP, &msg_in);
-       }
-       state->turn += 1;
-
-       if (!parse_ret) {
-               DEBUG(3,("Failed to parse auth response\n"));
-               if (NT_STATUS_IS_OK(status)
-                   || NT_STATUS_EQUAL(status,
-                                      NT_STATUS_MORE_PROCESSING_REQUIRED)) {
-                       tevent_req_nterror(
-                               req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+       if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
+               struct smbXcli_session *session = state->cli->smb2.session;
+
+               if (state->is_anonymous) {
+                       /*
+                        * Windows server does not set the
+                        * SMB2_SESSION_FLAG_IS_GUEST nor
+                        * SMB2_SESSION_FLAG_IS_NULL flag.
+                        *
+                        * This fix makes sure we do not try
+                        * to verify a signature on the final
+                        * session setup response.
+                        */
+                       tevent_req_done(req);
                        return;
                }
-       }
 
-       status = ntlmssp_update(state->ntlmssp_state, msg_in, &blob_out);
+               status = smb2cli_session_set_session_key(session,
+                                                        state->session_key,
+                                                        state->recv_iov);
+               if (tevent_req_nterror(req, status)) {
+                       return;
+               }
+       } else {
+               struct smbXcli_session *session = state->cli->smb1.session;
+               bool active;
 
-       if (!NT_STATUS_IS_OK(status)
-           && !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
-               TALLOC_FREE(state->ntlmssp_state);
-               tevent_req_nterror(req, status);
-               return;
-       }
+               status = smb1cli_session_set_session_key(session,
+                                                        state->session_key);
+               if (tevent_req_nterror(req, status)) {
+                       return;
+               }
 
-       state->blob_out = spnego_gen_auth(state, blob_out);
-       if (tevent_req_nomem(state->blob_out.data, req)) {
-               return;
-       }
+               active = smb1cli_conn_activate_signing(state->cli->conn,
+                                                      state->session_key,
+                                                      data_blob_null);
+               if (active) {
+                       bool ok;
 
-       subreq = cli_sesssetup_blob_send(state, state->ev, state->cli,
-                                        state->blob_out);
-       if (tevent_req_nomem(subreq, req)) {
-               return;
+                       ok = smb1cli_conn_check_signing(state->cli->conn,
+                                                       state->inbuf, 1);
+                       if (!ok) {
+                               tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
+                               return;
+                       }
+               }
        }
-       tevent_req_set_callback(subreq, cli_session_setup_ntlmssp_done, req);
+
+       tevent_req_done(req);
 }
 
-static NTSTATUS cli_session_setup_ntlmssp_recv(struct tevent_req *req)
+static NTSTATUS cli_session_setup_gensec_recv(struct tevent_req *req)
 {
-       struct cli_session_setup_ntlmssp_state *state = tevent_req_data(
-               req, struct cli_session_setup_ntlmssp_state);
+       struct cli_session_setup_gensec_state *state =
+               tevent_req_data(req,
+               struct cli_session_setup_gensec_state);
        NTSTATUS status;
 
        if (tevent_req_is_nterror(req, &status)) {
@@ -1723,33 +1677,64 @@ static NTSTATUS cli_session_setup_ntlmssp_recv(struct tevent_req *req)
        return NT_STATUS_OK;
 }
 
-static NTSTATUS cli_session_setup_ntlmssp(struct cli_state *cli,
-                                         const char *user,
-                                         const char *pass,
-                                         const char *domain)
+#ifdef HAVE_KRB5
+
+static char *cli_session_setup_get_principal(
+       TALLOC_CTX *mem_ctx, const char *spnego_principal,
+       const char *remote_name, const char *dest_realm)
 {
-       struct tevent_context *ev;
-       struct tevent_req *req;
-       NTSTATUS status = NT_STATUS_NO_MEMORY;
+       char *principal = NULL;
 
-       if (cli_has_async_calls(cli)) {
-               return NT_STATUS_INVALID_PARAMETER;
+       if (!lp_client_use_spnego_principal() ||
+           strequal(spnego_principal, ADS_IGNORE_PRINCIPAL)) {
+               spnego_principal = NULL;
        }
-       ev = tevent_context_init(talloc_tos());
-       if (ev == NULL) {
-               goto fail;
+       if (spnego_principal != NULL) {
+               DEBUG(3, ("cli_session_setup_spnego: using spnego provided "
+                         "principal %s\n", spnego_principal));
+               return talloc_strdup(mem_ctx, spnego_principal);
        }
-       req = cli_session_setup_ntlmssp_send(ev, ev, cli, user, pass, domain);
-       if (req == NULL) {
-               goto fail;
+       if (is_ipaddress(remote_name) ||
+           strequal(remote_name, STAR_SMBSERVER)) {
+               return NULL;
        }
-       if (!tevent_req_poll_ntstatus(req, ev, &status)) {
-               goto fail;
+
+       DEBUG(3, ("cli_session_setup_spnego: using target "
+                 "hostname not SPNEGO principal\n"));
+
+       if (dest_realm) {
+               char *realm = strupper_talloc(talloc_tos(), dest_realm);
+               if (realm == NULL) {
+                       return NULL;
+               }
+               principal = talloc_asprintf(talloc_tos(), "cifs/%s@%s",
+                                           remote_name, realm);
+               TALLOC_FREE(realm);
+       } else {
+               principal = kerberos_get_principal_from_service_hostname(
+                       talloc_tos(), "cifs", remote_name, lp_realm());
        }
-       status = cli_session_setup_ntlmssp_recv(req);
-fail:
-       TALLOC_FREE(ev);
-       return status;
+       DEBUG(3, ("cli_session_setup_spnego: guessed server principal=%s\n",
+                 principal ? principal : "<null>"));
+
+       return principal;
+}
+#endif
+
+static char *cli_session_setup_get_account(TALLOC_CTX *mem_ctx,
+                                          const char *principal)
+{
+       char *account, *p;
+
+       account = talloc_strdup(mem_ctx, principal);
+       if (account == NULL) {
+               return NULL;
+       }
+       p = strchr_m(account, '@');
+       if (p != NULL) {
+               *p = '\0';
+       }
+       return account;
 }
 
 /****************************************************************************
@@ -1759,30 +1744,61 @@ fail:
  dest_realm: The realm we're connecting to, if NULL we use our default realm.
 ****************************************************************************/
 
-static ADS_STATUS cli_session_setup_spnego(struct cli_state *cli,
-                             const char *user,
-                             const char *pass,
-                             const char *user_domain,
-                             const char * dest_realm)
+struct cli_session_setup_spnego_state {
+       struct tevent_context *ev;
+       struct cli_state *cli;
+       const char *target_hostname;
+       const char *user;
+       const char *account;
+       const char *pass;
+       const char *user_domain;
+       const char *dest_realm;
+       ADS_STATUS result;
+};
+
+#ifdef HAVE_KRB5
+static void cli_session_setup_spnego_done_krb(struct tevent_req *subreq);
+#endif
+
+static void cli_session_setup_spnego_done_ntlmssp(struct tevent_req *subreq);
+
+static struct tevent_req *cli_session_setup_spnego_send(
+       TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli,
+       const char *user, const char *pass, const char *user_domain)
 {
+       struct tevent_req *req, *subreq;
+       struct cli_session_setup_spnego_state *state;
        char *principal = NULL;
        char *OIDs[ASN1_MAX_OIDS];
        int i;
+       const char *dest_realm = cli_state_remote_realm(cli);
        const DATA_BLOB *server_blob;
-       DATA_BLOB blob = data_blob_null;
-       const char *p = NULL;
-       char *account = NULL;
-       NTSTATUS status;
 
-       server_blob = cli_state_server_gss_blob(cli);
-       if (server_blob) {
-               blob = data_blob(server_blob->data, server_blob->length);
+       req = tevent_req_create(mem_ctx, &state,
+                               struct cli_session_setup_spnego_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       state->ev = ev;
+       state->cli = cli;
+       state->user = user;
+       state->pass = pass;
+       state->user_domain = user_domain;
+       state->dest_realm = dest_realm;
+
+       state->account = cli_session_setup_get_account(state, user);
+       if (tevent_req_nomem(state->account, req)) {
+               return tevent_req_post(req, ev);
        }
 
-       DEBUG(3,("Doing spnego session setup (blob length=%lu)\n", (unsigned long)blob.length));
+       state->target_hostname = smbXcli_conn_remote_name(cli->conn);
+       server_blob = smbXcli_conn_server_gss_blob(cli->conn);
+
+       DEBUG(3,("Doing spnego session setup (blob length=%lu)\n",
+                (unsigned long)server_blob->length));
 
        /* the server might not even do spnego */
-       if (blob.length == 0) {
+       if (server_blob->length == 0) {
                DEBUG(3,("server didn't supply a full spnego negprot\n"));
                goto ntlmssp;
        }
@@ -1795,12 +1811,13 @@ static ADS_STATUS cli_session_setup_spnego(struct cli_state *cli,
         * negprot reply. It is WRONG to depend on the principal sent in the
         * negprot reply, but right now we do it. If we don't receive one,
         * we try to best guess, then fall back to NTLM.  */
-       if (!spnego_parse_negTokenInit(talloc_tos(), blob, OIDs, &principal, NULL) ||
+       if (!spnego_parse_negTokenInit(state, *server_blob, OIDs,
+                                      &principal, NULL) ||
                        OIDs[0] == NULL) {
-               data_blob_free(&blob);
-               return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+               state->result = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+               tevent_req_done(req);
+               return tevent_req_post(req, ev);
        }
-       data_blob_free(&blob);
 
        /* make sure the server understands kerberos */
        for (i=0;OIDs[i];i++) {
@@ -1817,19 +1834,17 @@ static ADS_STATUS cli_session_setup_spnego(struct cli_state *cli,
 
        DEBUG(3,("got principal=%s\n", principal ? principal : "<null>"));
 
-       status = cli_set_username(cli, user);
-       if (!NT_STATUS_IS_OK(status)) {
-               TALLOC_FREE(principal);
-               return ADS_ERROR_NT(status);
-       }
-
 #ifdef HAVE_KRB5
        /* If password is set we reauthenticate to kerberos server
         * and do not store results */
 
-       if (cli->got_kerberos_mechanism && cli->use_kerberos) {
-               ADS_STATUS rc;
-               const char *remote_name = cli_state_remote_name(cli);
+       if (user && *user && cli->got_kerberos_mechanism && cli->use_kerberos) {
+               char *tmp;
+
+               tmp = cli_session_setup_get_principal(
+                       talloc_tos(), principal, state->target_hostname, dest_realm);
+               TALLOC_FREE(principal);
+               principal = tmp;
 
                if (pass && *pass) {
                        int ret;
@@ -1838,104 +1853,110 @@ static ADS_STATUS cli_session_setup_spnego(struct cli_state *cli,
                        ret = kerberos_kinit_password(user, pass, 0 /* no time correction for now */, NULL);
 
                        if (ret){
+                               DEBUG(0, ("Kinit for %s to access %s failed: %s\n", user, principal, error_message(ret)));
                                TALLOC_FREE(principal);
-                               DEBUG(0, ("Kinit failed: %s\n", error_message(ret)));
                                if (cli->fallback_after_kerberos)
                                        goto ntlmssp;
-                               return ADS_ERROR_KRB5(ret);
+                               state->result = ADS_ERROR_KRB5(ret);
+                               tevent_req_done(req);
+                               return tevent_req_post(req, ev);
                        }
                }
 
-               /* We may not be allowed to use the server-supplied SPNEGO principal, or it may not have been supplied to us
-                */
-               if (!lp_client_use_spnego_principal() || strequal(principal, ADS_IGNORE_PRINCIPAL)) {
-                       TALLOC_FREE(principal);
+               if (principal) {
+                       subreq = cli_session_setup_gensec_send(
+                               state, ev, cli,
+                               state->account, pass, user_domain,
+                               CRED_MUST_USE_KERBEROS,
+                               "cifs", state->target_hostname, principal);
+                       if (tevent_req_nomem(subreq, req)) {
+                               return tevent_req_post(req, ev);
+                       }
+                       tevent_req_set_callback(
+                               subreq, cli_session_setup_spnego_done_krb,
+                               req);
+                       return req;
                }
+       }
+#endif
 
-               if (principal == NULL &&
-                       !is_ipaddress(remote_name) &&
-                       !strequal(STAR_SMBSERVER,
-                                 remote_name)) {
-                       char *realm = NULL;
-                       char *host = NULL;
-                       DEBUG(3,("cli_session_setup_spnego: using target "
-                                "hostname not SPNEGO principal\n"));
-
-                       host = strchr_m(remote_name, '.');
-                       if (dest_realm) {
-                               realm = SMB_STRDUP(dest_realm);
-                               if (!realm) {
-                                       return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
-                               }
-                               strupper_m(realm);
-                       } else {
-                               if (host) {
-                                       /* DNS name. */
-                                       realm = kerberos_get_realm_from_hostname(remote_name);
-                               } else {
-                                       /* NetBIOS name - use our realm. */
-                                       realm = kerberos_get_default_realm_from_ccache();
-                               }
-                       }
+ntlmssp:
+       subreq = cli_session_setup_gensec_send(
+               state, state->ev, state->cli,
+               state->account, state->pass, state->user_domain,
+               CRED_DONT_USE_KERBEROS,
+               "cifs", state->target_hostname, NULL);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(
+               subreq, cli_session_setup_spnego_done_ntlmssp, req);
+       return req;
+}
 
-                       if (realm == NULL || *realm == '\0') {
-                               realm = SMB_STRDUP(lp_realm());
-                               if (!realm) {
-                                       return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
-                               }
-                               strupper_m(realm);
-                               DEBUG(3,("cli_session_setup_spnego: cannot "
-                                       "get realm from dest_realm %s, "
-                                       "desthost %s. Using default "
-                                       "smb.conf realm %s\n",
-                                       dest_realm ? dest_realm : "<null>",
-                                       remote_name,
-                                       realm));
-                       }
+#ifdef HAVE_KRB5
+static void cli_session_setup_spnego_done_krb(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct cli_session_setup_spnego_state *state = tevent_req_data(
+               req, struct cli_session_setup_spnego_state);
+       NTSTATUS status;
 
-                       principal = talloc_asprintf(talloc_tos(),
-                                                   "cifs/%s@%s",
-                                                   remote_name,
-                                                   realm);
-                       if (!principal) {
-                               SAFE_FREE(realm);
-                               return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
-                       }
-                       DEBUG(3,("cli_session_setup_spnego: guessed "
-                               "server principal=%s\n",
-                               principal ? principal : "<null>"));
+       status = cli_session_setup_gensec_recv(subreq);
+       TALLOC_FREE(subreq);
+       state->result = ADS_ERROR_NT(status);
 
-                       SAFE_FREE(realm);
-               }
+       if (ADS_ERR_OK(state->result) ||
+           !state->cli->fallback_after_kerberos) {
+               tevent_req_done(req);
+               return;
+       }
 
-               if (principal) {
-                       rc = cli_session_setup_kerberos(cli, principal);
-                       if (ADS_ERR_OK(rc) || !cli->fallback_after_kerberos) {
-                               TALLOC_FREE(principal);
-                               return rc;
-                       }
-               }
+       subreq = cli_session_setup_gensec_send(
+               state, state->ev, state->cli,
+               state->account, state->pass, state->user_domain,
+               CRED_DONT_USE_KERBEROS,
+               "cifs", state->target_hostname, NULL);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
        }
+       tevent_req_set_callback(subreq, cli_session_setup_spnego_done_ntlmssp,
+                               req);
+}
 #endif
 
-       TALLOC_FREE(principal);
+static void cli_session_setup_spnego_done_ntlmssp(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct cli_session_setup_spnego_state *state = tevent_req_data(
+               req, struct cli_session_setup_spnego_state);
+       NTSTATUS status;
 
-ntlmssp:
+       status = cli_session_setup_gensec_recv(subreq);
+       TALLOC_FREE(subreq);
+       state->result = ADS_ERROR_NT(status);
+       tevent_req_done(req);
+}
 
-       account = talloc_strdup(talloc_tos(), user);
-       if (!account) {
-               return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
-       }
+static ADS_STATUS cli_session_setup_spnego_recv(struct tevent_req *req)
+{
+       struct cli_session_setup_spnego_state *state = tevent_req_data(
+               req, struct cli_session_setup_spnego_state);
 
-       /* when falling back to ntlmssp while authenticating with a machine
-        * account strip off the realm - gd */
+       return state->result;
+}
 
-       if ((p = strchr_m(user, '@')) != NULL) {
-               account[PTR_DIFF(p,user)] = '\0';
-       }
+struct cli_session_setup_state {
+       uint8_t dummy;
+};
 
-       return ADS_ERROR_NT(cli_session_setup_ntlmssp(cli, account, pass, user_domain));
-}
+static void cli_session_setup_done_lanman2(struct tevent_req *subreq);
+static void cli_session_setup_done_spnego(struct tevent_req *subreq);
+static void cli_session_setup_done_guest(struct tevent_req *subreq);
+static void cli_session_setup_done_plain(struct tevent_req *subreq);
+static void cli_session_setup_done_nt1(struct tevent_req *subreq);
 
 /****************************************************************************
  Send a session setup. The username and workgroup is in UNIX character
@@ -1943,23 +1964,34 @@ ntlmssp:
  password is in plaintext, the same should be done.
 ****************************************************************************/
 
-NTSTATUS cli_session_setup(struct cli_state *cli,
-                          const char *user,
-                          const char *pass, int passlen,
-                          const char *ntpass, int ntpasslen,
-                          const char *workgroup)
+struct tevent_req *cli_session_setup_send(TALLOC_CTX *mem_ctx,
+                                         struct tevent_context *ev,
+                                         struct cli_state *cli,
+                                         const char *user,
+                                         const char *pass, int passlen,
+                                         const char *ntpass, int ntpasslen,
+                                         const char *workgroup)
 {
+       struct tevent_req *req, *subreq;
+       struct cli_session_setup_state *state;
        char *p;
        char *user2;
-       uint16_t sec_mode = cli_state_security_mode(cli);
+       uint16_t sec_mode = smb1cli_conn_server_security_mode(cli->conn);
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct cli_session_setup_state);
+       if (req == NULL) {
+               return NULL;
+       }
 
        if (user) {
-               user2 = talloc_strdup(talloc_tos(), user);
+               user2 = talloc_strdup(state, user);
        } else {
-               user2 = talloc_strdup(talloc_tos(), "");
+               user2 = talloc_strdup(state, "");
        }
        if (user2 == NULL) {
-               return NT_STATUS_NO_MEMORY;
+               tevent_req_oom(req);
+               return tevent_req_post(req, ev);
        }
 
        if (!workgroup) {
@@ -1971,12 +2003,16 @@ NTSTATUS cli_session_setup(struct cli_state *cli,
            (p=strchr_m(user2,*lp_winbind_separator()))) {
                *p = 0;
                user = p+1;
-               strupper_m(user2);
+               if (!strupper_m(user2)) {
+                       tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+                       return tevent_req_post(req, ev);
+               }
                workgroup = user2;
        }
 
-       if (cli_state_protocol(cli) < PROTOCOL_LANMAN1) {
-               return NT_STATUS_OK;
+       if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_LANMAN1) {
+               tevent_req_done(req);
+               return tevent_req_post(req, ev);
        }
 
        /* now work out what sort of session setup we are going to
@@ -1985,74 +2021,243 @@ NTSTATUS cli_session_setup(struct cli_state *cli,
 
        /* if its an older server then we have to use the older request format */
 
-       if (cli_state_protocol(cli) < PROTOCOL_NT1) {
+       if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_NT1) {
                if (!lp_client_lanman_auth() && passlen != 24 && (*pass)) {
                        DEBUG(1, ("Server requested LM password but 'client lanman auth = no'"
                                  " or 'client ntlmv2 auth = yes'\n"));
-                       return NT_STATUS_ACCESS_DENIED;
-               }
+                       tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
+                       return tevent_req_post(req, ev);
+               }
 
                if ((sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) == 0 &&
                    !lp_client_plaintext_auth() && (*pass)) {
-                       DEBUG(1, ("Server requested LM password but 'client plaintext auth = no'"
+                       DEBUG(1, ("Server requested PLAINTEXT password but 'client plaintext auth = no'"
                                  " or 'client ntlmv2 auth = yes'\n"));
-                       return NT_STATUS_ACCESS_DENIED;
+                       tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
+                       return tevent_req_post(req, ev);
                }
 
-               return cli_session_setup_lanman2(cli, user, pass, passlen,
-                                                workgroup);
+               subreq = cli_session_setup_lanman2_send(
+                       state, ev, cli, user, pass, passlen, workgroup);
+               if (tevent_req_nomem(subreq, req)) {
+                       return tevent_req_post(req, ev);
+               }
+               tevent_req_set_callback(subreq, cli_session_setup_done_lanman2,
+                                       req);
+               return req;
+       }
+
+       if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+               subreq = cli_session_setup_spnego_send(
+                       state, ev, cli, user, pass, workgroup);
+               if (tevent_req_nomem(subreq, req)) {
+                       return tevent_req_post(req, ev);
+               }
+               tevent_req_set_callback(subreq, cli_session_setup_done_spnego,
+                                       req);
+               return req;
        }
 
        /* if no user is supplied then we have to do an anonymous connection.
           passwords are ignored */
 
-       if (!user || !*user)
-               return cli_session_setup_guest(cli);
+       if (!user || !*user) {
+               subreq = cli_session_setup_guest_send(state, ev, cli);
+               if (tevent_req_nomem(subreq, req)) {
+                       return tevent_req_post(req, ev);
+               }
+               tevent_req_set_callback(subreq, cli_session_setup_done_guest,
+                                       req);
+               return req;
+       }
 
        /* if the server is share level then send a plaintext null
            password at this point. The password is sent in the tree
            connect */
 
-       if ((sec_mode & NEGOTIATE_SECURITY_USER_LEVEL) == 0)
-               return cli_session_setup_plain(cli, user, "", workgroup);
+       if ((sec_mode & NEGOTIATE_SECURITY_USER_LEVEL) == 0) {
+               subreq = cli_session_setup_plain_send(
+                       state, ev, cli, user, "", workgroup);
+               if (tevent_req_nomem(subreq, req)) {
+                       return tevent_req_post(req, ev);
+               }
+               tevent_req_set_callback(subreq, cli_session_setup_done_plain,
+                                       req);
+               return req;
+       }
 
        /* if the server doesn't support encryption then we have to use 
           plaintext. The second password is ignored */
 
        if ((sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) == 0) {
                if (!lp_client_plaintext_auth() && (*pass)) {
-                       DEBUG(1, ("Server requested LM password but 'client plaintext auth = no'"
+                       DEBUG(1, ("Server requested PLAINTEXT password but 'client plaintext auth = no'"
                                  " or 'client ntlmv2 auth = yes'\n"));
-                       return NT_STATUS_ACCESS_DENIED;
+                       tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
+                       return tevent_req_post(req, ev);
                }
-               return cli_session_setup_plain(cli, user, pass, workgroup);
+               subreq = cli_session_setup_plain_send(
+                       state, ev, cli, user, pass, workgroup);
+               if (tevent_req_nomem(subreq, req)) {
+                       return tevent_req_post(req, ev);
+               }
+               tevent_req_set_callback(subreq, cli_session_setup_done_plain,
+                                       req);
+               return req;
        }
 
        /* if the server supports extended security then use SPNEGO */
 
-       if (cli_state_capabilities(cli) & CAP_EXTENDED_SECURITY) {
-               const char *remote_realm = cli_state_remote_realm(cli);
-               ADS_STATUS status = cli_session_setup_spnego(cli, user, pass,
-                                                            workgroup,
-                                                            remote_realm);
-               if (!ADS_ERR_OK(status)) {
-                       DEBUG(3, ("SPNEGO login failed: %s\n", ads_errstr(status)));
-                       return ads_ntstatus(status);
+       if (smb1cli_conn_capabilities(cli->conn) & CAP_EXTENDED_SECURITY) {
+               subreq = cli_session_setup_spnego_send(
+                       state, ev, cli, user, pass, workgroup);
+               if (tevent_req_nomem(subreq, req)) {
+                       return tevent_req_post(req, ev);
                }
+               tevent_req_set_callback(subreq, cli_session_setup_done_spnego,
+                                       req);
+               return req;
        } else {
-               NTSTATUS status;
-
                /* otherwise do a NT1 style session setup */
-               status = cli_session_setup_nt1(cli, user, pass, passlen,
-                                              ntpass, ntpasslen, workgroup);
-               if (!NT_STATUS_IS_OK(status)) {
-                       DEBUG(3,("cli_session_setup: NT1 session setup "
-                                "failed: %s\n", nt_errstr(status)));
-                       return status;
+               if (lp_client_ntlmv2_auth() && lp_client_use_spnego()) {
+                       /*
+                        * Don't send an NTLMv2 response without NTLMSSP
+                        * if we want to use spnego support
+                        */
+                       DEBUG(1, ("Server does not support EXTENDED_SECURITY "
+                                 " but 'client use spnego = yes"
+                                 " and 'client ntlmv2 auth = yes'\n"));
+                       tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
+                       return tevent_req_post(req, ev);
+               }
+
+               subreq = cli_session_setup_nt1_send(
+                       state, ev, cli, user, pass, passlen, ntpass, ntpasslen,
+                       workgroup);
+               if (tevent_req_nomem(subreq, req)) {
+                       return tevent_req_post(req, ev);
                }
+               tevent_req_set_callback(subreq, cli_session_setup_done_nt1,
+                                       req);
+               return req;
        }
 
-       return NT_STATUS_OK;
+       tevent_req_done(req);
+       return tevent_req_post(req, ev);
+}
+
+static void cli_session_setup_done_lanman2(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       NTSTATUS status;
+
+       status = cli_session_setup_lanman2_recv(subreq);
+       TALLOC_FREE(subreq);
+       if (!NT_STATUS_IS_OK(status)) {
+               tevent_req_nterror(req, status);
+               return;
+       }
+       tevent_req_done(req);
+}
+
+static void cli_session_setup_done_spnego(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       ADS_STATUS status;
+
+       status = cli_session_setup_spnego_recv(subreq);
+       TALLOC_FREE(subreq);
+       if (!ADS_ERR_OK(status)) {
+               DEBUG(3, ("SPNEGO login failed: %s\n", ads_errstr(status)));
+               tevent_req_nterror(req, ads_ntstatus(status));
+               return;
+       }
+       tevent_req_done(req);
+}
+
+static void cli_session_setup_done_guest(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       NTSTATUS status;
+
+       status = cli_session_setup_guest_recv(subreq);
+       TALLOC_FREE(subreq);
+       if (!NT_STATUS_IS_OK(status)) {
+               tevent_req_nterror(req, status);
+               return;
+       }
+       tevent_req_done(req);
+}
+
+static void cli_session_setup_done_plain(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       NTSTATUS status;
+
+       status = cli_session_setup_plain_recv(subreq);
+       TALLOC_FREE(subreq);
+       if (!NT_STATUS_IS_OK(status)) {
+               tevent_req_nterror(req, status);
+               return;
+       }
+       tevent_req_done(req);
+}
+
+static void cli_session_setup_done_nt1(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       NTSTATUS status;
+
+       status = cli_session_setup_nt1_recv(subreq);
+       TALLOC_FREE(subreq);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(3, ("cli_session_setup: NT1 session setup "
+                         "failed: %s\n", nt_errstr(status)));
+               tevent_req_nterror(req, status);
+               return;
+       }
+       tevent_req_done(req);
+}
+
+NTSTATUS cli_session_setup_recv(struct tevent_req *req)
+{
+       return tevent_req_simple_recv_ntstatus(req);
+}
+
+NTSTATUS cli_session_setup(struct cli_state *cli,
+                          const char *user,
+                          const char *pass, int passlen,
+                          const char *ntpass, int ntpasslen,
+                          const char *workgroup)
+{
+       struct tevent_context *ev;
+       struct tevent_req *req;
+       NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+       if (smbXcli_conn_has_async_calls(cli->conn)) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+       ev = samba_tevent_context_init(talloc_tos());
+       if (ev == NULL) {
+               goto fail;
+       }
+       req = cli_session_setup_send(ev, ev, cli, user, pass, passlen,
+                                    ntpass, ntpasslen, workgroup);
+       if (req == NULL) {
+               goto fail;
+       }
+       if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+               goto fail;
+       }
+       status = cli_session_setup_recv(req);
+ fail:
+       TALLOC_FREE(ev);
+       return status;
 }
 
 /****************************************************************************
@@ -2066,7 +2271,7 @@ struct cli_ulogoff_state {
 
 static void cli_ulogoff_done(struct tevent_req *subreq);
 
-struct tevent_req *cli_ulogoff_send(TALLOC_CTX *mem_ctx,
+static struct tevent_req *cli_ulogoff_send(TALLOC_CTX *mem_ctx,
                                    struct tevent_context *ev,
                                    struct cli_state *cli)
 {
@@ -2109,7 +2314,7 @@ static void cli_ulogoff_done(struct tevent_req *subreq)
        tevent_req_done(req);
 }
 
-NTSTATUS cli_ulogoff_recv(struct tevent_req *req)
+static NTSTATUS cli_ulogoff_recv(struct tevent_req *req)
 {
        return tevent_req_simple_recv_ntstatus(req);
 }
@@ -2120,10 +2325,22 @@ NTSTATUS cli_ulogoff(struct cli_state *cli)
        struct tevent_req *req;
        NTSTATUS status = NT_STATUS_NO_MEMORY;
 
-       if (cli_has_async_calls(cli)) {
+       if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+               status = smb2cli_logoff(cli->conn,
+                                       cli->timeout,
+                                       cli->smb2.session);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+               smb2cli_session_set_id_and_flags(cli->smb2.session,
+                                                UINT64_MAX, 0);
+               return NT_STATUS_OK;
+       }
+
+       if (smbXcli_conn_has_async_calls(cli->conn)) {
                return NT_STATUS_INVALID_PARAMETER;
        }
-       ev = tevent_context_init(talloc_tos());
+       ev = samba_tevent_context_init(talloc_tos());
        if (ev == NULL) {
                goto fail;
        }
@@ -2153,7 +2370,7 @@ struct cli_tcon_andx_state {
 static void cli_tcon_andx_done(struct tevent_req *subreq);
 
 struct tevent_req *cli_tcon_andx_create(TALLOC_CTX *mem_ctx,
-                                       struct event_context *ev,
+                                       struct tevent_context *ev,
                                        struct cli_state *cli,
                                        const char *share, const char *dev,
                                        const char *pass, int passlen,
@@ -2165,7 +2382,8 @@ struct tevent_req *cli_tcon_andx_create(TALLOC_CTX *mem_ctx,
        uint16_t *vwv;
        char *tmp = NULL;
        uint8_t *bytes;
-       uint16_t sec_mode = cli_state_security_mode(cli);
+       uint16_t sec_mode = smb1cli_conn_server_security_mode(cli->conn);
+       uint16_t tcon_flags = 0;
 
        *psmbreq = NULL;
 
@@ -2204,7 +2422,7 @@ struct tevent_req *cli_tcon_andx_create(TALLOC_CTX *mem_ctx,
                 * Non-encrypted passwords - convert to DOS codepage before
                 * encryption.
                 */
-               SMBencrypt(pass, cli_state_server_challenge(cli), p24);
+               SMBencrypt(pass, smb1cli_conn_server_challenge(cli->conn), p24);
                passlen = 24;
                pass = (const char *)p24;
        } else {
@@ -2214,9 +2432,9 @@ struct tevent_req *cli_tcon_andx_create(TALLOC_CTX *mem_ctx,
                        uint8_t *tmp_pass;
 
                        if (!lp_client_plaintext_auth() && (*pass)) {
-                               DEBUG(1, ("Server requested plaintext "
+                               DEBUG(1, ("Server requested PLAINTEXT "
                                          "password but "
-                                         "'client lanman auth = no' or 'client ntlmv2 auth = yes'\n"));
+                                         "'client plaintext auth = no' or 'client ntlmv2 auth = yes'\n"));
                                goto access_denied;
                        }
 
@@ -2224,7 +2442,7 @@ struct tevent_req *cli_tcon_andx_create(TALLOC_CTX *mem_ctx,
                         * Non-encrypted passwords - convert to DOS codepage
                         * before using.
                         */
-                       tmp_pass = talloc_array(talloc_tos(), uint8, 0);
+                       tmp_pass = talloc_array(talloc_tos(), uint8_t, 0);
                        if (tevent_req_nomem(tmp_pass, req)) {
                                return tevent_req_post(req, ev);
                        }
@@ -2241,10 +2459,13 @@ struct tevent_req *cli_tcon_andx_create(TALLOC_CTX *mem_ctx,
                }
        }
 
+       tcon_flags |= TCONX_FLAG_EXTENDED_RESPONSE;
+       tcon_flags |= TCONX_FLAG_EXTENDED_SIGNATURES;
+
        SCVAL(vwv+0, 0, 0xFF);
        SCVAL(vwv+0, 1, 0);
        SSVAL(vwv+1, 0, 0);
-       SSVAL(vwv+2, 0, TCONX_FLAG_EXTENDED_RESPONSE);
+       SSVAL(vwv+2, 0, tcon_flags);
        SSVAL(vwv+3, 0, passlen);
 
        if (passlen && pass) {
@@ -2257,12 +2478,12 @@ struct tevent_req *cli_tcon_andx_create(TALLOC_CTX *mem_ctx,
         * Add the sharename
         */
        tmp = talloc_asprintf_strupper_m(talloc_tos(), "\\\\%s\\%s",
-                                        cli_state_remote_name(cli), share);
+                                        smbXcli_conn_remote_name(cli->conn), share);
        if (tmp == NULL) {
                TALLOC_FREE(req);
                return NULL;
        }
-       bytes = smb_bytes_push_str(bytes, cli_ucs2(cli), tmp, strlen(tmp)+1,
+       bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), tmp, strlen(tmp)+1,
                                   NULL);
        TALLOC_FREE(tmp);
 
@@ -2301,7 +2522,7 @@ struct tevent_req *cli_tcon_andx_create(TALLOC_CTX *mem_ctx,
 }
 
 struct tevent_req *cli_tcon_andx_send(TALLOC_CTX *mem_ctx,
-                                     struct event_context *ev,
+                                     struct tevent_context *ev,
                                      struct cli_state *cli,
                                      const char *share, const char *dev,
                                      const char *pass, int passlen)
@@ -2317,7 +2538,7 @@ struct tevent_req *cli_tcon_andx_send(TALLOC_CTX *mem_ctx,
        if (subreq == NULL) {
                return req;
        }
-       status = cli_smb_req_send(subreq);
+       status = smb1cli_req_chain_submit(&subreq, 1);
        if (!NT_STATUS_IS_OK(status)) {
                tevent_req_nterror(req, status);
                return tevent_req_post(req, ev);
@@ -2333,12 +2554,13 @@ static void cli_tcon_andx_done(struct tevent_req *subreq)
                req, struct cli_tcon_andx_state);
        struct cli_state *cli = state->cli;
        uint8_t *in;
-       char *inbuf;
+       uint8_t *inhdr;
        uint8_t wct;
        uint16_t *vwv;
        uint32_t num_bytes;
        uint8_t *bytes;
        NTSTATUS status;
+       uint16_t optional_support = 0;
 
        status = cli_smb_recv(subreq, state, &in, 0, &wct, &vwv,
                              &num_bytes, &bytes);
@@ -2348,12 +2570,12 @@ static void cli_tcon_andx_done(struct tevent_req *subreq)
                return;
        }
 
-       inbuf = (char *)in;
+       inhdr = in + NBT_HDR_SIZE;
 
        if (num_bytes) {
                if (clistr_pull_talloc(cli,
-                               inbuf,
-                               SVAL(inbuf, smb_flg2),
+                               (const char *)inhdr,
+                               SVAL(inhdr, HDR_FLG2),
                                &cli->dev,
                                bytes,
                                num_bytes,
@@ -2369,7 +2591,7 @@ static void cli_tcon_andx_done(struct tevent_req *subreq)
                }
        }
 
-       if ((cli_state_protocol(cli) >= PROTOCOL_NT1) && (num_bytes == 3)) {
+       if ((smbXcli_conn_protocol(cli->conn) >= PROTOCOL_NT1) && (num_bytes == 3)) {
                /* almost certainly win95 - enable bug fixes */
                cli->win95 = True;
        }
@@ -2379,13 +2601,22 @@ static void cli_tcon_andx_done(struct tevent_req *subreq)
         * Avoids issues when connecting to Win9x boxes sharing files
         */
 
-       cli->dfsroot = false;
+       if ((wct > 2) && (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_LANMAN2)) {
+               optional_support = SVAL(vwv+2, 0);
+       }
 
-       if ((wct > 2) && (cli_state_protocol(cli) >= PROTOCOL_LANMAN2)) {
-               cli->dfsroot = ((SVAL(vwv+2, 0) & SMB_SHARE_IN_DFS) != 0);
+       if (optional_support & SMB_EXTENDED_SIGNATURES) {
+               smb1cli_session_protect_session_key(cli->smb1.session);
        }
 
-       cli->smb1.tid = SVAL(inbuf,smb_tid);
+       smb1cli_tcon_set_values(state->cli->smb1.tcon,
+                               SVAL(inhdr, HDR_TID),
+                               optional_support,
+                               0, /* maximal_access */
+                               0, /* guest_maximal_access */
+                               NULL, /* service */
+                               NULL); /* fs_type */
+
        tevent_req_done(req);
 }
 
@@ -2398,11 +2629,11 @@ NTSTATUS cli_tcon_andx(struct cli_state *cli, const char *share,
                       const char *dev, const char *pass, int passlen)
 {
        TALLOC_CTX *frame = talloc_stackframe();
-       struct event_context *ev;
+       struct tevent_context *ev;
        struct tevent_req *req;
-       NTSTATUS status = NT_STATUS_OK;
+       NTSTATUS status = NT_STATUS_NO_MEMORY;
 
-       if (cli_has_async_calls(cli)) {
+       if (smbXcli_conn_has_async_calls(cli->conn)) {
                /*
                 * Can't use sync call while an async call is in flight
                 */
@@ -2410,20 +2641,17 @@ NTSTATUS cli_tcon_andx(struct cli_state *cli, const char *share,
                goto fail;
        }
 
-       ev = event_context_init(frame);
+       ev = samba_tevent_context_init(frame);
        if (ev == NULL) {
-               status = NT_STATUS_NO_MEMORY;
                goto fail;
        }
 
        req = cli_tcon_andx_send(frame, ev, cli, share, dev, pass, passlen);
        if (req == NULL) {
-               status = NT_STATUS_NO_MEMORY;
                goto fail;
        }
 
-       if (!tevent_req_poll(req, ev)) {
-               status = map_nt_error_from_unix(errno);
+       if (!tevent_req_poll_ntstatus(req, ev, &status)) {
                goto fail;
        }
 
@@ -2433,6 +2661,156 @@ NTSTATUS cli_tcon_andx(struct cli_state *cli, const char *share,
        return status;
 }
 
+struct cli_tree_connect_state {
+       struct cli_state *cli;
+};
+
+static struct tevent_req *cli_raw_tcon_send(
+       TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli,
+       const char *service, const char *pass, const char *dev);
+static NTSTATUS cli_raw_tcon_recv(struct tevent_req *req,
+                                 uint16_t *max_xmit, uint16_t *tid);
+
+static void cli_tree_connect_smb2_done(struct tevent_req *subreq);
+static void cli_tree_connect_andx_done(struct tevent_req *subreq);
+static void cli_tree_connect_raw_done(struct tevent_req *subreq);
+
+static struct tevent_req *cli_tree_connect_send(
+       TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli,
+       const char *share, const char *dev, const char *pass, int passlen)
+{
+       struct tevent_req *req, *subreq;
+       struct cli_tree_connect_state *state;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct cli_tree_connect_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       state->cli = cli;
+
+       cli->share = talloc_strdup(cli, share);
+       if (tevent_req_nomem(cli->share, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+               char *unc;
+
+               cli->smb2.tcon = smbXcli_tcon_create(cli);
+               if (tevent_req_nomem(cli->smb2.tcon, req)) {
+                       return tevent_req_post(req, ev);
+               }
+
+               unc = talloc_asprintf(state, "\\\\%s\\%s",
+                                     smbXcli_conn_remote_name(cli->conn),
+                                     share);
+               if (tevent_req_nomem(unc, req)) {
+                       return tevent_req_post(req, ev);
+               }
+
+               subreq = smb2cli_tcon_send(state, ev, cli->conn, cli->timeout,
+                                          cli->smb2.session, cli->smb2.tcon,
+                                          0, /* flags */
+                                          unc);
+               if (tevent_req_nomem(subreq, req)) {
+                       return tevent_req_post(req, ev);
+               }
+               tevent_req_set_callback(subreq, cli_tree_connect_smb2_done,
+                                       req);
+               return req;
+       }
+
+       if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_LANMAN1) {
+               subreq = cli_tcon_andx_send(state, ev, cli, share, dev,
+                                           pass, passlen);
+               if (tevent_req_nomem(subreq, req)) {
+                       return tevent_req_post(req, ev);
+               }
+               tevent_req_set_callback(subreq, cli_tree_connect_andx_done,
+                                       req);
+               return req;
+       }
+
+       subreq = cli_raw_tcon_send(state, ev, cli, share, pass, dev);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, cli_tree_connect_raw_done, req);
+
+       return req;
+}
+
+static void cli_tree_connect_smb2_done(struct tevent_req *subreq)
+{
+       tevent_req_simple_finish_ntstatus(
+               subreq, smb2cli_tcon_recv(subreq));
+}
+
+static void cli_tree_connect_andx_done(struct tevent_req *subreq)
+{
+       tevent_req_simple_finish_ntstatus(
+               subreq, cli_tcon_andx_recv(subreq));
+}
+
+static void cli_tree_connect_raw_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct cli_tree_connect_state *state = tevent_req_data(
+               req, struct cli_tree_connect_state);
+       NTSTATUS status;
+       uint16_t max_xmit = 0;
+       uint16_t tid = 0;
+
+       status = cli_raw_tcon_recv(subreq, &max_xmit, &tid);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+
+       smb1cli_tcon_set_values(state->cli->smb1.tcon,
+                               tid,
+                               0, /* optional_support */
+                               0, /* maximal_access */
+                               0, /* guest_maximal_access */
+                               NULL, /* service */
+                               NULL); /* fs_type */
+
+       tevent_req_done(req);
+}
+
+static NTSTATUS cli_tree_connect_recv(struct tevent_req *req)
+{
+       return tevent_req_simple_recv_ntstatus(req);
+}
+
+NTSTATUS cli_tree_connect(struct cli_state *cli, const char *share,
+                         const char *dev, const char *pass, int passlen)
+{
+       struct tevent_context *ev;
+       struct tevent_req *req;
+       NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+       if (smbXcli_conn_has_async_calls(cli->conn)) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+       ev = samba_tevent_context_init(talloc_tos());
+       if (ev == NULL) {
+               goto fail;
+       }
+       req = cli_tree_connect_send(ev, ev, cli, share, dev, pass, passlen);
+       if (req == NULL) {
+               goto fail;
+       }
+       if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+               goto fail;
+       }
+       status = cli_tree_connect_recv(req);
+fail:
+       TALLOC_FREE(ev);
+       return status;
+}
+
 /****************************************************************************
  Send a tree disconnect.
 ****************************************************************************/
@@ -2443,7 +2821,7 @@ struct cli_tdis_state {
 
 static void cli_tdis_done(struct tevent_req *subreq);
 
-struct tevent_req *cli_tdis_send(TALLOC_CTX *mem_ctx,
+static struct tevent_req *cli_tdis_send(TALLOC_CTX *mem_ctx,
                                 struct tevent_context *ev,
                                 struct cli_state *cli)
 {
@@ -2478,11 +2856,11 @@ static void cli_tdis_done(struct tevent_req *subreq)
                tevent_req_nterror(req, status);
                return;
        }
-       state->cli->smb1.tid = UINT16_MAX;
+       cli_state_set_tid(state->cli, UINT16_MAX);
        tevent_req_done(req);
 }
 
-NTSTATUS cli_tdis_recv(struct tevent_req *req)
+static NTSTATUS cli_tdis_recv(struct tevent_req *req)
 {
        return tevent_req_simple_recv_ntstatus(req);
 }
@@ -2493,10 +2871,17 @@ NTSTATUS cli_tdis(struct cli_state *cli)
        struct tevent_req *req;
        NTSTATUS status = NT_STATUS_NO_MEMORY;
 
-       if (cli_has_async_calls(cli)) {
+       if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+               return smb2cli_tdis(cli->conn,
+                                   cli->timeout,
+                                   cli->smb2.session,
+                                   cli->smb2.tcon);
+       }
+
+       if (smbXcli_conn_has_async_calls(cli->conn)) {
                return NT_STATUS_INVALID_PARAMETER;
        }
-       ev = tevent_context_init(talloc_tos());
+       ev = samba_tevent_context_init(talloc_tos());
        if (ev == NULL) {
                goto fail;
        }
@@ -2513,601 +2898,398 @@ fail:
        return status;
 }
 
-/****************************************************************************
- Send a negprot command.
-****************************************************************************/
-
-struct cli_negprot_state {
-       struct cli_state *cli;
-       enum protocol_types max_protocol;
+struct cli_connect_sock_state {
+       const char **called_names;
+       const char **calling_names;
+       int *called_types;
+       int fd;
+       uint16_t port;
 };
 
-static void cli_negprot_done(struct tevent_req *subreq);
+static void cli_connect_sock_done(struct tevent_req *subreq);
 
-struct tevent_req *cli_negprot_send(TALLOC_CTX *mem_ctx,
-                                   struct event_context *ev,
-                                   struct cli_state *cli,
-                                   enum protocol_types max_protocol)
+/*
+ * Async only if we don't have to look up the name, i.e. "pss" is set with a
+ * nonzero address.
+ */
+
+static struct tevent_req *cli_connect_sock_send(
+       TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+       const char *host, int name_type, const struct sockaddr_storage *pss,
+       const char *myname, uint16_t port)
 {
        struct tevent_req *req, *subreq;
-       struct cli_negprot_state *state;
-       uint8_t *bytes = NULL;
-       int numprots;
-       enum protocol_types tmp_protocol;
+       struct cli_connect_sock_state *state;
+       const char *prog;
+       struct sockaddr_storage *addrs;
+       unsigned i, num_addrs;
+       NTSTATUS status;
 
-       req = tevent_req_create(mem_ctx, &state, struct cli_negprot_state);
+       req = tevent_req_create(mem_ctx, &state,
+                               struct cli_connect_sock_state);
        if (req == NULL) {
                return NULL;
        }
-       state->cli = cli;
-       state->max_protocol = max_protocol;
 
-       /* setup the protocol strings */
-       for (numprots=0; numprots < ARRAY_SIZE(prots); numprots++) {
-               uint8_t c = 2;
-               if (prots[numprots].prot > state->max_protocol) {
-                       break;
+       prog = getenv("LIBSMB_PROG");
+       if (prog != NULL) {
+               state->fd = sock_exec(prog);
+               if (state->fd == -1) {
+                       status = map_nt_error_from_unix(errno);
+                       tevent_req_nterror(req, status);
+               } else {
+                       state->port = 0;
+                       tevent_req_done(req);
                }
-               bytes = (uint8_t *)talloc_append_blob(
-                       state, bytes, data_blob_const(&c, sizeof(c)));
-               if (tevent_req_nomem(bytes, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       if ((pss == NULL) || is_zero_addr(pss)) {
+
+               /*
+                * Here we cheat. resolve_name_list is not async at all. So
+                * this call will only be really async if the name lookup has
+                * been done externally.
+                */
+
+               status = resolve_name_list(state, host, name_type,
+                                          &addrs, &num_addrs);
+               if (!NT_STATUS_IS_OK(status)) {
+                       tevent_req_nterror(req, status);
                        return tevent_req_post(req, ev);
                }
-               bytes = smb_bytes_push_str(bytes, false,
-                                          prots[numprots].name,
-                                          strlen(prots[numprots].name)+1,
-                                          NULL);
-               if (tevent_req_nomem(bytes, req)) {
+       } else {
+               addrs = talloc_array(state, struct sockaddr_storage, 1);
+               if (tevent_req_nomem(addrs, req)) {
                        return tevent_req_post(req, ev);
                }
+               addrs[0] = *pss;
+               num_addrs = 1;
        }
 
-       tmp_protocol = cli->conn.protocol;
-       cli->conn.protocol = state->max_protocol;
-       subreq = cli_smb_send(state, ev, cli, SMBnegprot, 0, 0, NULL,
-                             talloc_get_size(bytes), bytes);
-       cli->conn.protocol = tmp_protocol;
+       state->called_names = talloc_array(state, const char *, num_addrs);
+       if (tevent_req_nomem(state->called_names, req)) {
+               return tevent_req_post(req, ev);
+       }
+       state->called_types = talloc_array(state, int, num_addrs);
+       if (tevent_req_nomem(state->called_types, req)) {
+               return tevent_req_post(req, ev);
+       }
+       state->calling_names = talloc_array(state, const char *, num_addrs);
+       if (tevent_req_nomem(state->calling_names, req)) {
+               return tevent_req_post(req, ev);
+       }
+       for (i=0; i<num_addrs; i++) {
+               state->called_names[i] = host;
+               state->called_types[i] = name_type;
+               state->calling_names[i] = myname;
+       }
+
+       subreq = smbsock_any_connect_send(
+               state, ev, addrs, state->called_names, state->called_types,
+               state->calling_names, NULL, num_addrs, port);
        if (tevent_req_nomem(subreq, req)) {
                return tevent_req_post(req, ev);
        }
-       tevent_req_set_callback(subreq, cli_negprot_done, req);
+       tevent_req_set_callback(subreq, cli_connect_sock_done, req);
        return req;
 }
 
-static void cli_negprot_done(struct tevent_req *subreq)
+static void cli_connect_sock_done(struct tevent_req *subreq)
 {
        struct tevent_req *req = tevent_req_callback_data(
                subreq, struct tevent_req);
-       struct cli_negprot_state *state = tevent_req_data(
-               req, struct cli_negprot_state);
-       struct cli_state *cli = state->cli;
-       uint8_t flags;
-       uint8_t wct;
-       uint16_t *vwv;
-       uint32_t num_bytes;
-       uint8_t *bytes;
+       struct cli_connect_sock_state *state = tevent_req_data(
+               req, struct cli_connect_sock_state);
        NTSTATUS status;
-       uint16_t protnum;
-       uint8_t *inbuf;
-       uint32_t client_capabilities = cli->conn.smb1.client.capabilities;
-       uint32_t both_capabilities;
-       uint32_t server_capabilities = 0;
-       uint32_t capabilities;
-       uint32_t client_max_xmit = cli->conn.smb1.client.max_xmit;
-       uint32_t server_max_xmit = 0;
-       uint32_t max_xmit;
-       uint32_t server_max_mux = 0;
-       uint16_t server_security_mode = 0;
-       uint32_t server_session_key = 0;
-       bool server_readbraw = false;
-       bool server_writebraw = false;
-       bool server_lockread = false;
-       bool server_writeunlock = false;
-       struct GUID server_guid = GUID_zero();
-       DATA_BLOB server_gss_blob = data_blob_null;
-       uint8_t server_challenge[8];
-       char *server_workgroup = NULL;
-       char *server_name = NULL;
-       int server_time_zone = 0;
-       time_t server_system_time = 0;
-       enum protocol_types protocol;
-
-       ZERO_STRUCT(server_challenge);
-
-       status = cli_smb_recv(subreq, state, &inbuf, 1, &wct, &vwv,
-                             &num_bytes, &bytes);
-       TALLOC_FREE(subreq);
-       if (!NT_STATUS_IS_OK(status)) {
-               tevent_req_nterror(req, status);
-               return;
-       }
-
-       flags = CVAL(inbuf, smb_flg);
 
-       protnum = SVAL(vwv, 0);
-
-       if ((protnum >= ARRAY_SIZE(prots))
-           || (prots[protnum].prot > state->max_protocol)) {
-               tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+       status = smbsock_any_connect_recv(subreq, &state->fd, NULL,
+                                         &state->port);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
                return;
        }
+       set_socket_options(state->fd, lp_socket_options());
+       tevent_req_done(req);
+}
 
-       protocol = prots[protnum].prot;
-
-       if ((protocol < PROTOCOL_NT1) &&
-           client_is_signing_mandatory(cli)) {
-               DEBUG(0,("cli_negprot: SMB signing is mandatory and the selected protocol level doesn't support it.\n"));
-               tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
-               return;
-       }
+static NTSTATUS cli_connect_sock_recv(struct tevent_req *req,
+                                     int *pfd, uint16_t *pport)
+{
+       struct cli_connect_sock_state *state = tevent_req_data(
+               req, struct cli_connect_sock_state);
+       NTSTATUS status;
 
-       if (flags & FLAG_SUPPORT_LOCKREAD) {
-               server_lockread = true;
-               server_writeunlock = true;
+       if (tevent_req_is_nterror(req, &status)) {
+               return status;
        }
+       *pfd = state->fd;
+       *pport = state->port;
+       return NT_STATUS_OK;
+}
 
-       if (protocol >= PROTOCOL_NT1) {
-               struct timespec ts;
-               const char *client_signing = NULL;
-               bool server_mandatory;
-               bool server_allowed;
-               const char *server_signing = NULL;
-               bool ok;
-               uint8_t key_len;
-
-               if (wct != 0x11) {
-                       tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
-                       return;
-               }
-
-               /* NT protocol */
-               server_security_mode = CVAL(vwv + 1, 0);
-               server_max_mux = SVAL(vwv + 1, 1);
-               server_max_xmit = IVAL(vwv + 3, 1);
-               server_session_key = IVAL(vwv + 7, 1);
-               server_time_zone = SVALS(vwv + 15, 1);
-               server_time_zone *= 60;
-               /* this time arrives in real GMT */
-               ts = interpret_long_date(((char *)(vwv+11))+1);
-               server_system_time = ts.tv_sec;
-               server_capabilities = IVAL(vwv + 9, 1);
-
-               key_len = CVAL(vwv + 16, 1);
-
-               if (server_capabilities & CAP_RAW_MODE) {
-                       server_readbraw = true;
-                       server_writebraw = true;
-               }
-               if (server_capabilities & CAP_LOCK_AND_READ) {
-                       server_lockread = true;
-               }
-
-               if (server_capabilities & CAP_EXTENDED_SECURITY) {
-                       DATA_BLOB blob1, blob2;
-
-                       if (num_bytes < 16) {
-                               tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
-                               return;
-                       }
-
-                       blob1 = data_blob_const(bytes, 16);
-                       GUID_from_data_blob(&blob1, &server_guid);
-
-                       blob1 = data_blob_const(bytes+16, num_bytes-16);
-                       blob2 = data_blob_dup_talloc(state, blob1);
-                       if (blob1.length > 0 &&
-                           tevent_req_nomem(blob2.data, req)) {
-                               return;
-                       }
-                       server_gss_blob = blob2;
-               } else {
-                       DATA_BLOB blob1, blob2;
-                       ssize_t ret = 0;
+struct cli_connect_nb_state {
+       const char *desthost;
+       int signing_state;
+       int flags;
+       struct cli_state *cli;
+};
 
-                       if (num_bytes < key_len) {
-                               tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
-                               return;
-                       }
+static void cli_connect_nb_done(struct tevent_req *subreq);
 
-                       if (key_len != 0 && key_len != 8) {
-                               tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
-                               return;
-                       }
+static struct tevent_req *cli_connect_nb_send(
+       TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+       const char *host, const struct sockaddr_storage *dest_ss,
+       uint16_t port, int name_type, const char *myname,
+       int signing_state, int flags)
+{
+       struct tevent_req *req, *subreq;
+       struct cli_connect_nb_state *state;
 
-                       if (key_len == 8) {
-                               memcpy(server_challenge, bytes, 8);
-                       }
+       req = tevent_req_create(mem_ctx, &state, struct cli_connect_nb_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       state->signing_state = signing_state;
+       state->flags = flags;
 
-                       blob1 = data_blob_const(bytes+key_len, num_bytes-key_len);
-                       blob2 = data_blob_const(bytes+key_len, num_bytes-key_len);
-                       if (blob1.length > 0) {
-                               ret = pull_string_talloc(state,
-                                                        (char *)inbuf,
-                                                        SVAL(inbuf, smb_flg2),
-                                                        &server_workgroup,
-                                                        blob1.data,
-                                                        blob1.length,
-                                                        STR_TERMINATE|
-                                                        STR_UNICODE|
-                                                        STR_NOALIGN);
-                               if (ret == -1) {
-                                       tevent_req_oom(req);
-                                       return;
-                               }
-                       }
+       if (host != NULL) {
+               char *p = strchr(host, '#');
 
-                       blob2.data += ret;
-                       blob2.length -= ret;
-                       if (blob2.length > 0) {
-                               ret = pull_string_talloc(state,
-                                                        (char *)inbuf,
-                                                        SVAL(inbuf, smb_flg2),
-                                                        &server_name,
-                                                        blob2.data,
-                                                        blob2.length,
-                                                        STR_TERMINATE|
-                                                        STR_UNICODE|
-                                                        STR_NOALIGN);
-                               if (ret == -1) {
-                                       tevent_req_oom(req);
-                                       return;
-                               }
+               if (p != NULL) {
+                       name_type = strtol(p+1, NULL, 16);
+                       host = talloc_strndup(state, host, p - host);
+                       if (tevent_req_nomem(host, req)) {
+                               return tevent_req_post(req, ev);
                        }
                }
 
-               client_signing = "disabled";
-               if (client_is_signing_allowed(cli)) {
-                       client_signing = "allowed";
-               }
-               if (client_is_signing_mandatory(cli)) {
-                       client_signing = "required";
-               }
-
-               server_signing = "not supported";
-
-               server_allowed = false;
-               if (server_security_mode & NEGOTIATE_SECURITY_SIGNATURES_ENABLED) {
-                       server_signing = "supported";
-                       server_allowed = true;
-               }
-
-               server_mandatory = false;
-               if (server_security_mode & NEGOTIATE_SECURITY_SIGNATURES_REQUIRED) {
-                       server_signing = "required";
-                       server_mandatory = true;
-               }
-
-               ok = cli_set_signing_negotiated(cli,
-                                               server_allowed,
-                                               server_mandatory);
-               if (!ok) {
-                       DEBUG(1,("cli_negprot: SMB signing is required, "
-                                "but client[%s] and server[%s] mismatch\n",
-                                client_signing, server_signing));
-                       tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
-                       return;
-               }
-
-       } else if (protocol >= PROTOCOL_LANMAN1) {
-               DATA_BLOB blob1;
-               ssize_t ret = 0;
-               uint16_t key_len;
-
-               if (wct != 0x0D) {
-                       tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
-                       return;
-               }
-
-               server_security_mode = SVAL(vwv + 1, 0);
-               server_max_xmit = SVAL(vwv + 2, 0);
-               server_max_mux = SVAL(vwv + 3, 0);
-               server_readbraw = ((SVAL(vwv + 5, 0) & 0x1) != 0);
-               server_writebraw = ((SVAL(vwv + 5, 0) & 0x2) != 0);
-               server_session_key = IVAL(vwv + 6, 0);
-               server_time_zone = SVALS(vwv + 10, 0);
-               server_time_zone *= 60;
-               /* this time is converted to GMT by make_unix_date */
-               server_system_time = make_unix_date(
-                       (char *)(vwv + 8), server_time_zone);
-               key_len = SVAL(vwv + 11, 0);
-
-               if (num_bytes < key_len) {
-                       tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
-                       return;
-               }
-
-               if (key_len != 0 && key_len != 8) {
-                       tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
-                       return;
-               }
-
-               if (key_len == 8) {
-                       memcpy(server_challenge, bytes, 8);
-               }
-
-               blob1 = data_blob_const(bytes+key_len, num_bytes-key_len);
-               if (blob1.length > 0) {
-                       ret = pull_string_talloc(state,
-                                                (char *)inbuf,
-                                                SVAL(inbuf, smb_flg2),
-                                                &server_workgroup,
-                                                blob1.data,
-                                                blob1.length,
-                                                STR_TERMINATE|
-                                                STR_ASCII);
-                       if (ret == -1) {
-                               tevent_req_oom(req);
-                               return;
-                       }
-               }
+               state->desthost = host;
        } else {
-               /* the old core protocol */
-               server_time_zone = get_time_zone(time(NULL));
-               server_system_time = 0;
-               server_max_xmit = 1024;
-               server_max_mux = 1;
-               server_security_mode = 0;
+               state->desthost = print_canonical_sockaddr(state, dest_ss);
+               if (tevent_req_nomem(state->desthost, req)) {
+                       return tevent_req_post(req, ev);
+               }
        }
 
-       if (server_max_xmit < 1024) {
-               tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
-               return;
+       subreq = cli_connect_sock_send(state, ev, host, name_type, dest_ss,
+                                      myname, port);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
        }
+       tevent_req_set_callback(subreq, cli_connect_nb_done, req);
+       return req;
+}
 
-       if (server_max_mux < 1) {
-               tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+static void cli_connect_nb_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct cli_connect_nb_state *state = tevent_req_data(
+               req, struct cli_connect_nb_state);
+       NTSTATUS status;
+       int fd = 0;
+       uint16_t port;
+
+       status = cli_connect_sock_recv(subreq, &fd, &port);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
                return;
        }
 
-       /*
-        * Now calculate the negotiated capabilities
-        * based on the mask for:
-        * - client only flags
-        * - flags used in both directions
-        * - server only flags
-        */
-       both_capabilities = client_capabilities & server_capabilities;
-       capabilities = client_capabilities & SMB_CAP_CLIENT_MASK;
-       capabilities |= both_capabilities & SMB_CAP_BOTH_MASK;
-       capabilities |= server_capabilities & SMB_CAP_SERVER_MASK;
-
-       max_xmit = MIN(client_max_xmit, server_max_xmit);
-
-       if (server_workgroup) {
-               cli->server_domain = talloc_strdup(cli, server_workgroup);
-               if (tevent_req_nomem(cli->server_domain, req)) {
-                       return;
-               }
+       state->cli = cli_state_create(state, fd, state->desthost, NULL,
+                                     state->signing_state, state->flags);
+       if (tevent_req_nomem(state->cli, req)) {
+               close(fd);
+               return;
        }
-
-       cli->conn.protocol = protocol;
-
-       cli->conn.smb1.server.capabilities = server_capabilities;
-       cli->conn.smb1.capabilities = capabilities;
-
-       cli->conn.smb1.server.max_xmit = server_max_xmit;
-       cli->conn.smb1.max_xmit = max_xmit;
-
-       cli->conn.smb1.server.max_mux = server_max_mux;
-
-       cli->conn.smb1.server.security_mode = server_security_mode;
-
-       cli->conn.smb1.server.readbraw = server_readbraw;
-       cli->conn.smb1.server.writebraw = server_writebraw;
-       cli->conn.smb1.server.lockread = server_lockread;
-       cli->conn.smb1.server.writeunlock = server_writeunlock;
-
-       cli->conn.smb1.server.session_key = server_session_key;
-
-       talloc_steal(cli, server_gss_blob.data);
-       cli->conn.smb1.server.gss_blob = server_gss_blob;
-       cli->conn.smb1.server.guid = server_guid;
-       memcpy(cli->conn.smb1.server.challenge, server_challenge, 8);
-       cli->conn.smb1.server.workgroup = talloc_move(cli, &server_workgroup);
-       cli->conn.smb1.server.name = talloc_move(cli, &server_name);
-
-       cli->conn.smb1.server.time_zone = server_time_zone;
-       cli->conn.smb1.server.system_time = server_system_time;
-
        tevent_req_done(req);
 }
 
-NTSTATUS cli_negprot_recv(struct tevent_req *req)
+static NTSTATUS cli_connect_nb_recv(struct tevent_req *req,
+                                   struct cli_state **pcli)
 {
-       return tevent_req_simple_recv_ntstatus(req);
+       struct cli_connect_nb_state *state = tevent_req_data(
+               req, struct cli_connect_nb_state);
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               return status;
+       }
+       *pcli = talloc_move(NULL, &state->cli);
+       return NT_STATUS_OK;
 }
 
-NTSTATUS cli_negprot(struct cli_state *cli, enum protocol_types max_protocol)
+NTSTATUS cli_connect_nb(const char *host, const struct sockaddr_storage *dest_ss,
+                       uint16_t port, int name_type, const char *myname,
+                       int signing_state, int flags, struct cli_state **pcli)
 {
-       TALLOC_CTX *frame = talloc_stackframe();
-       struct event_context *ev;
+       struct tevent_context *ev;
        struct tevent_req *req;
-       NTSTATUS status = NT_STATUS_OK;
-
-       if (cli_has_async_calls(cli)) {
-               /*
-                * Can't use sync call while an async call is in flight
-                */
-               status = NT_STATUS_INVALID_PARAMETER;
-               goto fail;
-       }
+       NTSTATUS status = NT_STATUS_NO_MEMORY;
 
-       ev = event_context_init(frame);
+       ev = samba_tevent_context_init(talloc_tos());
        if (ev == NULL) {
-               status = NT_STATUS_NO_MEMORY;
                goto fail;
        }
-
-       req = cli_negprot_send(frame, ev, cli, max_protocol);
+       req = cli_connect_nb_send(ev, ev, host, dest_ss, port, name_type,
+                                 myname, signing_state, flags);
        if (req == NULL) {
-               status = NT_STATUS_NO_MEMORY;
                goto fail;
        }
-
-       if (!tevent_req_poll(req, ev)) {
-               status = map_nt_error_from_unix(errno);
+       if (!tevent_req_set_endtime(req, ev, timeval_current_ofs(20, 0))) {
                goto fail;
        }
-
-       status = cli_negprot_recv(req);
- fail:
-       TALLOC_FREE(frame);
+       if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+               goto fail;
+       }
+       status = cli_connect_nb_recv(req, pcli);
+fail:
+       TALLOC_FREE(ev);
        return status;
 }
 
-static NTSTATUS cli_connect_sock(const char *host, int name_type,
-                                const struct sockaddr_storage *pss,
-                                const char *myname, uint16_t port,
-                                int sec_timeout, int *pfd, uint16_t *pport)
-{
-       TALLOC_CTX *frame = talloc_stackframe();
-       const char *prog;
-       unsigned int i, num_addrs;
-       const char **called_names;
-       const char **calling_names;
-       int *called_types;
-       NTSTATUS status;
-       int fd;
+struct cli_start_connection_state {
+       struct tevent_context *ev;
+       struct cli_state *cli;
+       int min_protocol;
+       int max_protocol;
+};
 
-       prog = getenv("LIBSMB_PROG");
-       if (prog != NULL) {
-               fd = sock_exec(prog);
-               if (fd == -1) {
-                       return map_nt_error_from_unix(errno);
-               }
-               port = 0;
-               goto done;
-       }
+static void cli_start_connection_connected(struct tevent_req *subreq);
+static void cli_start_connection_done(struct tevent_req *subreq);
 
-       if ((pss == NULL) || is_zero_addr(pss)) {
-               struct sockaddr_storage *addrs;
-               status = resolve_name_list(talloc_tos(), host, name_type,
-                                          &addrs, &num_addrs);
-               if (!NT_STATUS_IS_OK(status)) {
-                       goto fail;
-               }
-               pss = addrs;
-       } else {
-               num_addrs = 1;
+/**
+   establishes a connection to after the negprot. 
+   @param output_cli A fully initialised cli structure, non-null only on success
+   @param dest_host The netbios name of the remote host
+   @param dest_ss (optional) The the destination IP, NULL for name based lookup
+   @param port (optional) The destination port (0 for default)
+*/
+
+static struct tevent_req *cli_start_connection_send(
+       TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+       const char *my_name, const char *dest_host,
+       const struct sockaddr_storage *dest_ss, int port,
+       int signing_state, int flags)
+{
+       struct tevent_req *req, *subreq;
+       struct cli_start_connection_state *state;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct cli_start_connection_state);
+       if (req == NULL) {
+               return NULL;
        }
+       state->ev = ev;
 
-       called_names = talloc_array(talloc_tos(), const char *, num_addrs);
-       if (called_names == NULL) {
-               status = NT_STATUS_NO_MEMORY;
-               goto fail;
-       }
-       called_types = talloc_array(talloc_tos(), int, num_addrs);
-       if (called_types == NULL) {
-               status = NT_STATUS_NO_MEMORY;
-               goto fail;
+       if (signing_state == SMB_SIGNING_IPC_DEFAULT) {
+               state->min_protocol = lp_client_ipc_min_protocol();
+               state->max_protocol = lp_client_ipc_max_protocol();
+       } else {
+               state->min_protocol = lp_client_min_protocol();
+               state->max_protocol = lp_client_max_protocol();
        }
-       calling_names = talloc_array(talloc_tos(), const char *, num_addrs);
-       if (calling_names == NULL) {
-               status = NT_STATUS_NO_MEMORY;
-               goto fail;
+
+       subreq = cli_connect_nb_send(state, ev, dest_host, dest_ss, port,
+                                    0x20, my_name, signing_state, flags);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
        }
-       for (i=0; i<num_addrs; i++) {
-               called_names[i] = host;
-               called_types[i] = name_type;
-               calling_names[i] = myname;
+       tevent_req_set_callback(subreq, cli_start_connection_connected, req);
+       return req;
+}
+
+static void cli_start_connection_connected(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct cli_start_connection_state *state = tevent_req_data(
+               req, struct cli_start_connection_state);
+       NTSTATUS status;
+
+       status = cli_connect_nb_recv(subreq, &state->cli);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
        }
-       status = smbsock_any_connect(pss, called_names, called_types,
-                                    calling_names, NULL, num_addrs, port,
-                                    sec_timeout, &fd, NULL, &port);
-       if (!NT_STATUS_IS_OK(status)) {
-               goto fail;
+
+       subreq = smbXcli_negprot_send(state, state->ev, state->cli->conn,
+                                     state->cli->timeout,
+                                     state->min_protocol,
+                                     state->max_protocol);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
        }
-       set_socket_options(fd, lp_socket_options());
-done:
-       *pfd = fd;
-       *pport = port;
-       status = NT_STATUS_OK;
-fail:
-       TALLOC_FREE(frame);
-       return status;
+       tevent_req_set_callback(subreq, cli_start_connection_done, req);
 }
 
-NTSTATUS cli_connect_nb(const char *host, const struct sockaddr_storage *dest_ss,
-                       uint16_t port, int name_type, const char *myname,
-                       int signing_state, int flags, struct cli_state **pcli)
+static void cli_start_connection_done(struct tevent_req *subreq)
 {
-       TALLOC_CTX *frame = talloc_stackframe();
-       struct cli_state *cli;
-       NTSTATUS status = NT_STATUS_NO_MEMORY;
-       int fd = -1;
-       char *desthost;
-       char *p;
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct cli_start_connection_state *state = tevent_req_data(
+               req, struct cli_start_connection_state);
+       NTSTATUS status;
 
-       desthost = talloc_strdup(talloc_tos(), host);
-       if (desthost == NULL) {
-               goto fail;
+       status = smbXcli_negprot_recv(subreq);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
        }
 
-       p = strchr(host, '#');
-       if (p != NULL) {
-               name_type = strtol(p+1, NULL, 16);
-               host = talloc_strndup(talloc_tos(), host, p - host);
-               if (host == NULL) {
-                       goto fail;
-               }
+       if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
+               /* Ensure we ask for some initial credits. */
+               smb2cli_conn_set_max_credits(state->cli->conn,
+                                            DEFAULT_SMB2_MAX_CREDITS);
        }
 
-       status = cli_connect_sock(host, name_type, dest_ss, myname, port,
-                                 20, &fd, &port);
-       if (!NT_STATUS_IS_OK(status)) {
-               goto fail;
-       }
+       tevent_req_done(req);
+}
 
-       cli = cli_state_create(NULL, fd, desthost, NULL, signing_state, flags);
-       if (cli == NULL) {
-               close(fd);
-               fd = -1;
-               goto fail;
+static NTSTATUS cli_start_connection_recv(struct tevent_req *req,
+                                         struct cli_state **output_cli)
+{
+       struct cli_start_connection_state *state = tevent_req_data(
+               req, struct cli_start_connection_state);
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               return status;
        }
+       *output_cli = state->cli;
 
-       *pcli = cli;
-       status = NT_STATUS_OK;
-fail:
-       TALLOC_FREE(frame);
-       return status;
+       return NT_STATUS_OK;
 }
 
-/**
-   establishes a connection to after the negprot. 
-   @param output_cli A fully initialised cli structure, non-null only on success
-   @param dest_host The netbios name of the remote host
-   @param dest_ss (optional) The the destination IP, NULL for name based lookup
-   @param port (optional) The destination port (0 for default)
-*/
 NTSTATUS cli_start_connection(struct cli_state **output_cli, 
                              const char *my_name, 
                              const char *dest_host, 
                              const struct sockaddr_storage *dest_ss, int port,
                              int signing_state, int flags)
 {
-       NTSTATUS nt_status;
-       struct cli_state *cli;
+       struct tevent_context *ev;
+       struct tevent_req *req;
+       NTSTATUS status = NT_STATUS_NO_MEMORY;
 
-       nt_status = cli_connect_nb(dest_host, dest_ss, port, 0x20, my_name,
-                                  signing_state, flags, &cli);
-       if (!NT_STATUS_IS_OK(nt_status)) {
-               DEBUG(10, ("cli_connect_nb failed: %s\n",
-                          nt_errstr(nt_status)));
-               return nt_status;
+       ev = samba_tevent_context_init(talloc_tos());
+       if (ev == NULL) {
+               goto fail;
        }
-
-       nt_status = cli_negprot(cli, PROTOCOL_NT1);
-       if (!NT_STATUS_IS_OK(nt_status)) {
-               DEBUG(1, ("failed negprot: %s\n", nt_errstr(nt_status)));
-               cli_shutdown(cli);
-               return nt_status;
+       req = cli_start_connection_send(ev, ev, my_name, dest_host, dest_ss,
+                                       port, signing_state, flags);
+       if (req == NULL) {
+               goto fail;
        }
-
-       *output_cli = cli;
-       return NT_STATUS_OK;
+       if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+               goto fail;
+       }
+       status = cli_start_connection_recv(req, output_cli);
+fail:
+       TALLOC_FREE(ev);
+       return status;
 }
 
-
 /**
    establishes a connection right up to doing tconX, password specified.
    @param output_cli A fully initialised cli structure, non-null only on success
@@ -3121,118 +3303,316 @@ NTSTATUS cli_start_connection(struct cli_state **output_cli,
    @param password User's password, unencrypted unix string.
 */
 
-NTSTATUS cli_full_connection(struct cli_state **output_cli, 
-                            const char *my_name, 
-                            const char *dest_host, 
-                            const struct sockaddr_storage *dest_ss, int port,
-                            const char *service, const char *service_type,
-                            const char *user, const char *domain, 
-                            const char *password, int flags,
-                            int signing_state)
+struct cli_full_connection_state {
+       struct tevent_context *ev;
+       const char *service;
+       const char *service_type;
+       const char *user;
+       const char *domain;
+       const char *password;
+       int pw_len;
+       int flags;
+       struct cli_state *cli;
+};
+
+static int cli_full_connection_state_destructor(
+       struct cli_full_connection_state *s);
+static void cli_full_connection_started(struct tevent_req *subreq);
+static void cli_full_connection_sess_set_up(struct tevent_req *subreq);
+static void cli_full_connection_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_full_connection_send(
+       TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+       const char *my_name, const char *dest_host,
+       const struct sockaddr_storage *dest_ss, int port,
+       const char *service, const char *service_type,
+       const char *user, const char *domain,
+       const char *password, int flags, int signing_state)
 {
-       NTSTATUS nt_status;
-       struct cli_state *cli = NULL;
-       int pw_len = password ? strlen(password)+1 : 0;
+       struct tevent_req *req, *subreq;
+       struct cli_full_connection_state *state;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct cli_full_connection_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       talloc_set_destructor(state, cli_full_connection_state_destructor);
 
-       *output_cli = NULL;
+       state->ev = ev;
+       state->service = service;
+       state->service_type = service_type;
+       state->user = user;
+       state->domain = domain;
+       state->password = password;
+       state->flags = flags;
 
-       if (password == NULL) {
-               password = "";
+       state->pw_len = state->password ? strlen(state->password)+1 : 0;
+       if (state->password == NULL) {
+               state->password = "";
        }
 
-       nt_status = cli_start_connection(&cli, my_name, dest_host,
-                                        dest_ss, port, signing_state,
-                                        flags);
+       subreq = cli_start_connection_send(
+               state, ev, my_name, dest_host, dest_ss, port,
+               signing_state, flags);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, cli_full_connection_started, req);
+       return req;
+}
 
-       if (!NT_STATUS_IS_OK(nt_status)) {
-               return nt_status;
+static int cli_full_connection_state_destructor(
+       struct cli_full_connection_state *s)
+{
+       if (s->cli != NULL) {
+               cli_shutdown(s->cli);
+               s->cli = NULL;
        }
+       return 0;
+}
 
-       nt_status = cli_session_setup(cli, user, password, pw_len, password,
-                                     pw_len, domain);
-       if (!NT_STATUS_IS_OK(nt_status)) {
+static void cli_full_connection_started(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct cli_full_connection_state *state = tevent_req_data(
+               req, struct cli_full_connection_state);
+       NTSTATUS status;
 
-               if (!(flags & CLI_FULL_CONNECTION_ANONYMOUS_FALLBACK)) {
-                       DEBUG(1,("failed session setup with %s\n",
-                                nt_errstr(nt_status)));
-                       cli_shutdown(cli);
-                       return nt_status;
-               }
+       status = cli_start_connection_recv(subreq, &state->cli);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+       subreq = cli_session_setup_send(
+               state, state->ev, state->cli, state->user,
+               state->password, state->pw_len, state->password, state->pw_len,
+               state->domain);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq, cli_full_connection_sess_set_up, req);
+}
+
+static void cli_full_connection_sess_set_up(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct cli_full_connection_state *state = tevent_req_data(
+               req, struct cli_full_connection_state);
+       NTSTATUS status;
+
+       status = cli_session_setup_recv(subreq);
+       TALLOC_FREE(subreq);
 
-               nt_status = cli_session_setup(cli, "", "", 0, "", 0, domain);
-               if (!NT_STATUS_IS_OK(nt_status)) {
-                       DEBUG(1,("anonymous failed session setup with %s\n",
-                                nt_errstr(nt_status)));
-                       cli_shutdown(cli);
-                       return nt_status;
+       if (!NT_STATUS_IS_OK(status) &&
+           (state->flags & CLI_FULL_CONNECTION_ANONYMOUS_FALLBACK)) {
+
+               state->flags &= ~CLI_FULL_CONNECTION_ANONYMOUS_FALLBACK;
+
+               subreq = cli_session_setup_send(
+                       state, state->ev, state->cli, "", "", 0, "", 0,
+                       state->domain);
+               if (tevent_req_nomem(subreq, req)) {
+                       return;
                }
+               tevent_req_set_callback(
+                       subreq, cli_full_connection_sess_set_up, req);
+               return;
        }
 
-       if (service) {
-               nt_status = cli_tcon_andx(cli, service, service_type, password,
-                                         pw_len);
-               if (!NT_STATUS_IS_OK(nt_status)) {
-                       DEBUG(1,("failed tcon_X with %s\n", nt_errstr(nt_status)));
-                       cli_shutdown(cli);
-                       if (NT_STATUS_IS_OK(nt_status)) {
-                               nt_status = NT_STATUS_UNSUCCESSFUL;
-                       }
-                       return nt_status;
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+
+       if (state->service != NULL) {
+               subreq = cli_tree_connect_send(
+                       state, state->ev, state->cli,
+                       state->service, state->service_type,
+                       state->password, state->pw_len);
+               if (tevent_req_nomem(subreq, req)) {
+                       return;
                }
+               tevent_req_set_callback(subreq, cli_full_connection_done, req);
+               return;
        }
 
-       nt_status = cli_init_creds(cli, user, domain, password);
-       if (!NT_STATUS_IS_OK(nt_status)) {
-               cli_shutdown(cli);
-               return nt_status;
+       tevent_req_done(req);
+}
+
+static void cli_full_connection_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       NTSTATUS status;
+
+       status = cli_tree_connect_recv(subreq);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
        }
 
-       *output_cli = cli;
+       tevent_req_done(req);
+}
+
+NTSTATUS cli_full_connection_recv(struct tevent_req *req,
+                                 struct cli_state **output_cli)
+{
+       struct cli_full_connection_state *state = tevent_req_data(
+               req, struct cli_full_connection_state);
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               return status;
+       }
+       *output_cli = state->cli;
+       talloc_set_destructor(state, NULL);
        return NT_STATUS_OK;
 }
 
+NTSTATUS cli_full_connection(struct cli_state **output_cli,
+                            const char *my_name,
+                            const char *dest_host,
+                            const struct sockaddr_storage *dest_ss, int port,
+                            const char *service, const char *service_type,
+                            const char *user, const char *domain,
+                            const char *password, int flags,
+                            int signing_state)
+{
+       struct tevent_context *ev;
+       struct tevent_req *req;
+       NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+       ev = samba_tevent_context_init(talloc_tos());
+       if (ev == NULL) {
+               goto fail;
+       }
+       req = cli_full_connection_send(
+               ev, ev, my_name, dest_host, dest_ss, port, service,
+               service_type, user, domain, password, flags, signing_state);
+       if (req == NULL) {
+               goto fail;
+       }
+       if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+               goto fail;
+       }
+       status = cli_full_connection_recv(req, output_cli);
+ fail:
+       TALLOC_FREE(ev);
+       return status;
+}
+
 /****************************************************************************
  Send an old style tcon.
 ****************************************************************************/
-NTSTATUS cli_raw_tcon(struct cli_state *cli, 
-                     const char *service, const char *pass, const char *dev,
-                     uint16 *max_xmit, uint16 *tid)
-{
-       struct tevent_req *req;
+struct cli_raw_tcon_state {
        uint16_t *ret_vwv;
+};
+
+static void cli_raw_tcon_done(struct tevent_req *subreq);
+
+static struct tevent_req *cli_raw_tcon_send(
+       TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli,
+       const char *service, const char *pass, const char *dev)
+{
+       struct tevent_req *req, *subreq;
+       struct cli_raw_tcon_state *state;
        uint8_t *bytes;
-       NTSTATUS status;
+
+       req = tevent_req_create(mem_ctx, &state, struct cli_raw_tcon_state);
+       if (req == NULL) {
+               return NULL;
+       }
 
        if (!lp_client_plaintext_auth() && (*pass)) {
-               DEBUG(1, ("Server requested plaintext password but 'client "
-                         "plaintext auth' is disabled\n"));
-               return NT_STATUS_ACCESS_DENIED;
+               DEBUG(1, ("Server requested PLAINTEXT password but 'client plaintext auth = no'"
+                         " or 'client ntlmv2 auth = yes'\n"));
+               tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
+               return tevent_req_post(req, ev);
        }
 
-       bytes = talloc_array(talloc_tos(), uint8_t, 0);
+       bytes = talloc_array(state, uint8_t, 0);
        bytes = smb_bytes_push_bytes(bytes, 4, NULL, 0);
-       bytes = smb_bytes_push_str(bytes, cli_ucs2(cli),
+       bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn),
                                   service, strlen(service)+1, NULL);
        bytes = smb_bytes_push_bytes(bytes, 4, NULL, 0);
-       bytes = smb_bytes_push_str(bytes, cli_ucs2(cli),
+       bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn),
                                   pass, strlen(pass)+1, NULL);
        bytes = smb_bytes_push_bytes(bytes, 4, NULL, 0);
-       bytes = smb_bytes_push_str(bytes, cli_ucs2(cli),
+       bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn),
                                   dev, strlen(dev)+1, NULL);
 
-       status = cli_smb(talloc_tos(), cli, SMBtcon, 0, 0, NULL,
-                        talloc_get_size(bytes), bytes, &req,
-                        2, NULL, &ret_vwv, NULL, NULL);
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
+       if (tevent_req_nomem(bytes, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       subreq = cli_smb_send(state, ev, cli, SMBtcon, 0, 0, NULL,
+                             talloc_get_size(bytes), bytes);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, cli_raw_tcon_done, req);
+       return req;
+}
+
+static void cli_raw_tcon_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct cli_raw_tcon_state *state = tevent_req_data(
+               req, struct cli_raw_tcon_state);
+       NTSTATUS status;
+
+       status = cli_smb_recv(subreq, state, NULL, 2, NULL, &state->ret_vwv,
+                             NULL, NULL);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
        }
+       tevent_req_done(req);
+}
 
-       *max_xmit = SVAL(ret_vwv + 0, 0);
-       *tid = SVAL(ret_vwv + 1, 0);
+static NTSTATUS cli_raw_tcon_recv(struct tevent_req *req,
+                                 uint16_t *max_xmit, uint16_t *tid)
+{
+       struct cli_raw_tcon_state *state = tevent_req_data(
+               req, struct cli_raw_tcon_state);
+       NTSTATUS status;
 
+       if (tevent_req_is_nterror(req, &status)) {
+               return status;
+       }
+       *max_xmit = SVAL(state->ret_vwv + 0, 0);
+       *tid = SVAL(state->ret_vwv + 1, 0);
        return NT_STATUS_OK;
 }
 
+NTSTATUS cli_raw_tcon(struct cli_state *cli,
+                     const char *service, const char *pass, const char *dev,
+                     uint16_t *max_xmit, uint16_t *tid)
+{
+       struct tevent_context *ev;
+       struct tevent_req *req;
+       NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+       ev = samba_tevent_context_init(talloc_tos());
+       if (ev == NULL) {
+               goto fail;
+       }
+       req = cli_raw_tcon_send(ev, ev, cli, service, pass, dev);
+       if (req == NULL) {
+               goto fail;
+       }
+       if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+               goto fail;
+       }
+       status = cli_raw_tcon_recv(req, max_xmit, tid);
+fail:
+       TALLOC_FREE(ev);
+       return status;
+}
+
 /* Return a cli_state pointing at the IPC$ share for the given server */
 
 struct cli_state *get_ipc_connect(char *server,
@@ -3356,7 +3736,7 @@ struct cli_state *get_ipc_connect_master_ip_bcast(TALLOC_CTX *ctx,
         if (!NT_STATUS_IS_OK(status)) {
                 DEBUG(99, ("No master browsers responded: %s\n",
                           nt_errstr(status)));
-                return False;
+                return NULL;
         }
 
        for (i = 0; i < count; i++) {