CVE-2016-2115: s3:libsmb: let SMB_SIGNING_IPC_DEFAULT use "client ipc min/max protocol"
[samba.git] / source3 / libsmb / cliconnect.c
index f46ce06e4c3ebc96493da6884a01bf2452f63d8f..2c351dda9c51238c006d1f45b64a5b3b63d5d79e 100644 (file)
 #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"
@@ -34,7 +37,6 @@
 #include "libsmb/nmblib.h"
 #include "librpc/ndr/libndr.h"
 #include "../libcli/smb/smbXcli_base.h"
-#include "smb2cli.h"
 
 #define STAR_SMBSERVER "*SMBSERVER"
 
@@ -72,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.
 ****************************************************************************/
@@ -252,7 +242,6 @@ static void cli_session_setup_lanman2_done(struct tevent_req *subreq)
        p = bytes;
 
        cli_state_set_uid(state->cli, SVAL(inhdr, HDR_UID));
-       cli->is_guestlogin = ((SVAL(vwv+2, 0) & 1) != 0);
 
        status = smb_bytes_talloc_string(cli,
                                        inhdr,
@@ -293,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);
 }
 
@@ -305,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 (smbXcli_conn_has_async_calls(cli->conn)) {
-               /*
-                * 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.
 ****************************************************************************/
@@ -353,9 +304,13 @@ static uint32_t cli_session_setup_capabilities(struct cli_state *cli,
         * - 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
@@ -383,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)
 {
@@ -442,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;
@@ -454,7 +409,7 @@ struct tevent_req *cli_session_setup_guest_send(TALLOC_CTX *mem_ctx,
        }
 
        status = smb1cli_req_chain_submit(&subreq, 1);
-       if (NT_STATUS_IS_OK(status)) {
+       if (!NT_STATUS_IS_OK(status)) {
                tevent_req_nterror(req, status);
                return tevent_req_post(req, ev);
        }
@@ -490,7 +445,6 @@ static void cli_session_setup_guest_done(struct tevent_req *subreq)
        p = bytes;
 
        cli_state_set_uid(state->cli, SVAL(inhdr, HDR_UID));
-       cli->is_guestlogin = ((SVAL(vwv+2, 0) & 1) != 0);
 
        status = smb_bytes_talloc_string(cli,
                                        inhdr,
@@ -531,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);
 }
 
@@ -544,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 (smbXcli_conn_has_async_calls(cli->conn)) {
-               /*
-                * 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.
 ****************************************************************************/
@@ -693,7 +604,6 @@ static void cli_session_setup_plain_done(struct tevent_req *subreq)
        p = bytes;
 
        cli_state_set_uid(state->cli, SVAL(inhdr, HDR_UID));
-       cli->is_guestlogin = ((SVAL(vwv+2, 0) & 1) != 0);
 
        status = smb_bytes_talloc_string(cli,
                                        inhdr,
@@ -734,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);
 }
 
@@ -747,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 (smbXcli_conn_has_async_calls(cli->conn)) {
-               /*
-                * 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.
@@ -1044,7 +915,6 @@ static void cli_session_setup_nt1_done(struct tevent_req *subreq)
        p = bytes;
 
        cli_state_set_uid(state->cli, SVAL(inhdr, HDR_UID));
-       cli->is_guestlogin = ((SVAL(vwv+2, 0) & 1) != 0);
 
        status = smb_bytes_talloc_string(cli,
                                        inhdr,
@@ -1082,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 (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);
 }
@@ -1103,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 (smbXcli_conn_has_async_calls(cli->conn)) {
-               /*
-                * 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() )
@@ -1324,7 +1160,6 @@ static void cli_sesssetup_blob_done(struct tevent_req *subreq)
        state->inbuf = in;
        inhdr = in + NBT_HDR_SIZE;
        cli_state_set_uid(state->cli, SVAL(inhdr, HDR_UID));
-       cli->is_guestlogin = ((SVAL(vwv+2, 0) & 1) != 0);
 
        blob_length = SVAL(vwv+3, 0);
        if (blob_length > num_bytes) {
@@ -1432,56 +1267,178 @@ 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;
+               }
+       }
+
+       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,
@@ -1491,322 +1448,226 @@ static struct tevent_req *cli_session_setup_kerberos_send(
                }
        }
 
-       subreq = cli_sesssetup_blob_send(state, ev, cli, state->negTokenTarg);
-       if (tevent_req_nomem(subreq, req)) {
+       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);
-       uint8_t *inbuf = NULL;
-       struct iovec *recv_iov = 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, state,
-                                        NULL, &inbuf, &recv_iov);
+       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 (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
-               struct smbXcli_session *session = state->cli->smb2.session;
-               status = smb2cli_session_set_session_key(session,
-                                               state->session_key_krb5,
-                                               recv_iov);
-               if (tevent_req_nterror(req, status)) {
-                       return;
-               }
-       } else {
-               if (smb1cli_conn_activate_signing(state->cli->conn, state->session_key_krb5,
-                                          data_blob_null)
-                   && !smb1cli_conn_check_signing(state->cli->conn, inbuf, 1)) {
-                       tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
-                       return;
-               }
+       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;
+       }
+
+       subreq = cli_sesssetup_blob_send(state, state->ev,
+                                        state->cli, state->blob_out);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
        }
-       return state->ads_status;
+       tevent_req_set_callback(subreq,
+                               cli_session_setup_gensec_remote_done,
+                               req);
 }
 
-static ADS_STATUS cli_session_setup_kerberos(struct cli_state *cli,
-                                            const char *principal)
+static void cli_session_setup_gensec_remote_done(struct tevent_req *subreq)
 {
-       struct tevent_context *ev;
-       struct tevent_req *req;
-       ADS_STATUS status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+       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;
 
-       if (smbXcli_conn_has_async_calls(cli->conn)) {
-               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 */
+       TALLOC_FREE(state->inbuf);
+       TALLOC_FREE(state->recv_iov);
 
-/****************************************************************************
- Do a spnego/NTLMSSP encrypted session setup.
-****************************************************************************/
+       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;
+       }
 
-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;
-};
+       if (NT_STATUS_IS_OK(status)) {
+               state->remote_ready = true;
+       }
 
-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);
+       if (state->local_ready && state->remote_ready) {
+               cli_session_setup_gensec_ready(req);
+               return;
        }
-       return 0;
-}
 
-static void cli_session_setup_ntlmssp_done(struct tevent_req *req);
+       cli_session_setup_gensec_local_next(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_ready(struct tevent_req *req)
 {
-       struct tevent_req *req, *subreq;
-       struct cli_session_setup_ntlmssp_state *state;
+       struct cli_session_setup_gensec_state *state =
+               tevent_req_data(req,
+               struct cli_session_setup_gensec_state);
+       const char *server_domain = NULL;
        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;
+       if (state->blob_in.length != 0) {
+               tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+               return;
        }
-       state->ev = ev;
-       state->cli = cli;
-       state->turn = 1;
 
-       state->ntlmssp_state = NULL;
-       talloc_set_destructor(
-               state, cli_session_setup_ntlmssp_state_destructor);
-
-       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;
+       if (state->blob_out.length != 0) {
+               tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+               return;
        }
 
-       state->blob_out = spnego_gen_negTokenInit(state, OIDs_ntlm, &blob_out, NULL);
-       data_blob_free(&blob_out);
-
-       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);
+       /*
+        * 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;
                }
        }
 
-       subreq = cli_sesssetup_blob_send(state, ev, cli, state->blob_out);
-       if (tevent_req_nomem(subreq, req)) {
-               return tevent_req_post(req, ev);
+       status = gensec_session_key(state->auth_generic->gensec_security,
+                                   state, &state->session_key);
+       if (tevent_req_nterror(req, status)) {
+               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);
-}
 
-static void cli_session_setup_ntlmssp_done(struct tevent_req *subreq)
-{
-       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;
-       uint8_t *inbuf = NULL;
-       struct iovec *recv_iov = NULL;
-       bool parse_ret;
-       NTSTATUS status;
+       if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
+               struct smbXcli_session *session = state->cli->smb2.session;
 
-       status = cli_sesssetup_blob_recv(subreq, talloc_tos(), &blob_in,
-                                        &inbuf, &recv_iov);
-       TALLOC_FREE(subreq);
-       data_blob_free(&state->blob_out);
+               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;
+               }
 
-       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;
-                       }
+               status = smb2cli_session_set_session_key(session,
+                                                        state->session_key,
+                                                        state->recv_iov);
+               if (tevent_req_nterror(req, status)) {
+                       return;
                }
-               cli_set_session_key(
-                       state->cli, state->ntlmssp_state->session_key);
+       } else {
+               struct smbXcli_session *session = state->cli->smb1.session;
+               bool active;
 
-               if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
-                       struct smbXcli_session *session = state->cli->smb2.session;
+               status = smb1cli_session_set_session_key(session,
+                                                        state->session_key);
+               if (tevent_req_nterror(req, status)) {
+                       return;
+               }
 
-                       if (ntlmssp_is_anonymous(state->ntlmssp_state)) {
-                               /*
-                                * 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.
-                                */
-                               TALLOC_FREE(state->ntlmssp_state);
-                               tevent_req_done(req);
-                               return;
-                       }
+               active = smb1cli_conn_activate_signing(state->cli->conn,
+                                                      state->session_key,
+                                                      data_blob_null);
+               if (active) {
+                       bool ok;
 
-                       status = smb2cli_session_set_session_key(session,
-                                               state->ntlmssp_state->session_key,
-                                               recv_iov);
-                       if (tevent_req_nterror(req, status)) {
-                               return;
-                       }
-               } else {
-                       if (smb1cli_conn_activate_signing(
-                                   state->cli->conn, state->ntlmssp_state->session_key,
-                                   data_blob_null)
-                           && !smb1cli_conn_check_signing(state->cli->conn, inbuf, 1)) {
+                       ok = smb1cli_conn_check_signing(state->cli->conn,
+                                                       state->inbuf, 1);
+                       if (!ok) {
                                tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
                                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);
-               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);
-                       return;
-               }
-       }
-
-       status = ntlmssp_update(state->ntlmssp_state, msg_in, &blob_out);
-
-       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;
        }
 
-       state->blob_out = spnego_gen_auth(state, blob_out);
-       if (tevent_req_nomem(state->blob_out.data, req)) {
-               return;
-       }
-
-       subreq = cli_sesssetup_blob_send(state, state->ev, state->cli,
-                                        state->blob_out);
-       if (tevent_req_nomem(subreq, req)) {
-               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)) {
@@ -1816,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 (smbXcli_conn_has_async_calls(cli->conn)) {
-               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;
 }
 
 /****************************************************************************
@@ -1852,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 = smbXcli_conn_server_gss_blob(cli->conn);
-       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;
        }
@@ -1888,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++) {
@@ -1910,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 (user && *user && cli->got_kerberos_mechanism && cli->use_kerberos) {
-               ADS_STATUS rc;
-               const char *remote_name = smbXcli_conn_remote_name(cli->conn);
+               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;
@@ -1931,103 +1853,145 @@ 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);
-                       }
-               }
-
-               /* 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 == NULL &&
-                       !is_ipaddress(remote_name) &&
-                       !strequal(STAR_SMBSERVER,
-                                 remote_name)) {
-                       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) {
-                                       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());
-                       }
-
-                       if (!principal) {
-                               return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+                               state->result = ADS_ERROR_KRB5(ret);
+                               tevent_req_done(req);
+                               return tevent_req_post(req, ev);
                        }
-                       DEBUG(3,("cli_session_setup_spnego: guessed "
-                               "server principal=%s\n",
-                               principal ? principal : "<null>"));
                }
 
                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, 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
 
-       TALLOC_FREE(principal);
-
 ntlmssp:
-
-       account = talloc_strdup(talloc_tos(), user);
-       if (!account) {
-               return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+       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;
+}
 
-       /* when falling back to ntlmssp while authenticating with a machine
-        * account strip off the realm - gd */
+#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;
+
+       status = cli_session_setup_gensec_recv(subreq);
+       TALLOC_FREE(subreq);
+       state->result = ADS_ERROR_NT(status);
+
+       if (ADS_ERR_OK(state->result) ||
+           !state->cli->fallback_after_kerberos) {
+               tevent_req_done(req);
+               return;
+       }
 
-       if ((p = strchr_m(user, '@')) != NULL) {
-               account[PTR_DIFF(p,user)] = '\0';
+       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
+
+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;
+
+       status = cli_session_setup_gensec_recv(subreq);
+       TALLOC_FREE(subreq);
+       state->result = ADS_ERROR_NT(status);
+       tevent_req_done(req);
+}
 
-       return ADS_ERROR_NT(cli_session_setup_ntlmssp(cli, account, pass, user_domain));
+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);
+
+       return state->result;
 }
 
+struct cli_session_setup_state {
+       uint8_t dummy;
+};
+
+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
  format and must be converted to DOS codepage format before sending. If the
  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 = 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) {
@@ -2039,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 (smbXcli_conn_protocol(cli->conn) < PROTOCOL_LANMAN1) {
-               return NT_STATUS_OK;
+               tevent_req_done(req);
+               return tevent_req_post(req, ev);
        }
 
        /* now work out what sort of session setup we are going to
@@ -2057,44 +2025,66 @@ NTSTATUS cli_session_setup(struct cli_state *cli,
                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 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) {
-               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, ("SMB2-SPNEGO login failed: %s\n", ads_errstr(status)));
-                       return ads_ntstatus(status);
+               subreq = cli_session_setup_spnego_send(
+                       state, ev, cli, user, pass, workgroup);
+               if (tevent_req_nomem(subreq, req)) {
+                       return tevent_req_post(req, ev);
                }
-               return NT_STATUS_OK;
+               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 */
@@ -2103,36 +2093,171 @@ NTSTATUS cli_session_setup(struct cli_state *cli,
                if (!lp_client_plaintext_auth() && (*pass)) {
                        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);
+               }
+               subreq = cli_session_setup_plain_send(
+                       state, ev, cli, user, pass, workgroup);
+               if (tevent_req_nomem(subreq, req)) {
+                       return tevent_req_post(req, ev);
                }
-               return cli_session_setup_plain(cli, user, pass, workgroup);
+               tevent_req_set_callback(subreq, cli_session_setup_done_plain,
+                                       req);
+               return req;
        }
 
        /* if the server supports extended security then use SPNEGO */
 
        if (smb1cli_conn_capabilities(cli->conn) & 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);
+               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;
 }
 
 /****************************************************************************
@@ -2146,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)
 {
@@ -2189,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);
 }
@@ -2200,10 +2325,22 @@ NTSTATUS cli_ulogoff(struct cli_state *cli)
        struct tevent_req *req;
        NTSTATUS status = NT_STATUS_NO_MEMORY;
 
+       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;
        }
@@ -2233,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,
@@ -2246,6 +2383,7 @@ struct tevent_req *cli_tcon_andx_create(TALLOC_CTX *mem_ctx,
        char *tmp = NULL;
        uint8_t *bytes;
        uint16_t sec_mode = smb1cli_conn_server_security_mode(cli->conn);
+       uint16_t tcon_flags = 0;
 
        *psmbreq = NULL;
 
@@ -2304,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);
                        }
@@ -2321,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) {
@@ -2381,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)
@@ -2419,6 +2560,7 @@ static void cli_tcon_andx_done(struct tevent_req *subreq)
        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);
@@ -2459,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)) {
-               cli->dfsroot = ((SVAL(vwv+2, 0) & SMB_SHARE_IN_DFS) != 0);
+               optional_support = SVAL(vwv+2, 0);
+       }
+
+       if (optional_support & SMB_EXTENDED_SIGNATURES) {
+               smb1cli_session_protect_session_key(cli->smb1.session);
        }
 
-       cli->smb1.tid = SVAL(inhdr, HDR_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);
 }
 
@@ -2478,9 +2629,9 @@ 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 (smbXcli_conn_has_async_calls(cli->conn)) {
                /*
@@ -2490,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;
        }
 
@@ -2513,33 +2661,154 @@ NTSTATUS cli_tcon_andx(struct cli_state *cli, const char *share,
        return status;
 }
 
-NTSTATUS cli_tree_connect(struct cli_state *cli, const char *share,
-                         const char *dev, const char *pass, int passlen)
+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)
 {
-       NTSTATUS status;
-       uint16_t max_xmit = 0;
-       uint16_t tid = 0;
+       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 (!cli->share) {
-               return NT_STATUS_NO_MEMORY;
+       if (tevent_req_nomem(cli->share, req)) {
+               return tevent_req_post(req, ev);
        }
 
        if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
-               return smb2cli_tcon(cli, share);
+               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) {
-               return cli_tcon_andx(cli, share, dev, pass, passlen);
+               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;
        }
 
-       status = cli_raw_tcon(cli, share, pass, dev, &max_xmit, &tid);
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
+       subreq = cli_raw_tcon_send(state, ev, cli, share, pass, dev);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
        }
-       cli->smb1.tid = tid;
+       tevent_req_set_callback(subreq, cli_tree_connect_raw_done, req);
 
-       return NT_STATUS_OK;
+       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;
 }
 
 /****************************************************************************
@@ -2552,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)
 {
@@ -2587,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);
 }
@@ -2602,10 +2871,17 @@ NTSTATUS cli_tdis(struct cli_state *cli)
        struct tevent_req *req;
        NTSTATUS status = NT_STATUS_NO_MEMORY;
 
-       if (smbXcli_conn_has_async_calls(cli->conn)) {
-               return NT_STATUS_INVALID_PARAMETER;
+       if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+               return smb2cli_tdis(cli->conn,
+                                   cli->timeout,
+                                   cli->smb2.session,
+                                   cli->smb2.tcon);
        }
-       ev = tevent_context_init(talloc_tos());
+
+       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;
        }
@@ -2622,123 +2898,267 @@ fail:
        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;
+struct cli_connect_sock_state {
        const char **called_names;
        const char **calling_names;
        int *called_types;
-       NTSTATUS status;
        int fd;
+       uint16_t port;
+};
+
+static void cli_connect_sock_done(struct tevent_req *subreq);
+
+/*
+ * 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_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_connect_sock_state);
+       if (req == NULL) {
+               return NULL;
+       }
 
        prog = getenv("LIBSMB_PROG");
        if (prog != NULL) {
-               fd = sock_exec(prog);
-               if (fd == -1) {
-                       return map_nt_error_from_unix(errno);
+               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);
                }
-               port = 0;
-               goto done;
+               return tevent_req_post(req, ev);
        }
 
        if ((pss == NULL) || is_zero_addr(pss)) {
-               struct sockaddr_storage *addrs;
-               status = resolve_name_list(talloc_tos(), host, name_type,
+
+               /*
+                * 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)) {
-                       goto fail;
+                       tevent_req_nterror(req, status);
+                       return tevent_req_post(req, ev);
                }
-               pss = addrs;
        } 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;
        }
 
-       called_names = talloc_array(talloc_tos(), const char *, num_addrs);
-       if (called_names == NULL) {
-               status = NT_STATUS_NO_MEMORY;
-               goto fail;
+       state->called_names = talloc_array(state, const char *, num_addrs);
+       if (tevent_req_nomem(state->called_names, req)) {
+               return tevent_req_post(req, ev);
        }
-       called_types = talloc_array(talloc_tos(), int, num_addrs);
-       if (called_types == NULL) {
-               status = NT_STATUS_NO_MEMORY;
-               goto fail;
+       state->called_types = talloc_array(state, int, num_addrs);
+       if (tevent_req_nomem(state->called_types, req)) {
+               return tevent_req_post(req, ev);
        }
-       calling_names = talloc_array(talloc_tos(), const char *, num_addrs);
-       if (calling_names == NULL) {
-               status = NT_STATUS_NO_MEMORY;
-               goto fail;
+       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++) {
-               called_names[i] = host;
-               called_types[i] = name_type;
-               calling_names[i] = myname;
+               state->called_names[i] = host;
+               state->called_types[i] = name_type;
+               state->calling_names[i] = myname;
        }
-       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 = 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);
        }
-       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_connect_sock_done, req);
+       return 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_connect_sock_done(struct tevent_req *subreq)
 {
-       TALLOC_CTX *frame = talloc_stackframe();
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct cli_connect_sock_state *state = tevent_req_data(
+               req, struct cli_connect_sock_state);
+       NTSTATUS status;
+
+       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);
+}
+
+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 (tevent_req_is_nterror(req, &status)) {
+               return status;
+       }
+       *pfd = state->fd;
+       *pport = state->port;
+       return NT_STATUS_OK;
+}
+
+struct cli_connect_nb_state {
+       const char *desthost;
+       int signing_state;
+       int flags;
        struct cli_state *cli;
-       NTSTATUS status = NT_STATUS_NO_MEMORY;
-       int fd = -1;
-       char *desthost;
-       char *p;
+};
 
-       desthost = talloc_strdup(talloc_tos(), host);
-       if (desthost == NULL) {
-               goto fail;
+static void cli_connect_nb_done(struct tevent_req *subreq);
+
+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;
+
+       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;
 
-       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 (host != NULL) {
+               char *p = strchr(host, '#');
+
+               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);
+                       }
+               }
+
+               state->desthost = host;
+       } else {
+               state->desthost = print_canonical_sockaddr(state, dest_ss);
+               if (tevent_req_nomem(state->desthost, req)) {
+                       return tevent_req_post(req, ev);
                }
        }
 
-       status = cli_connect_sock(host, name_type, dest_ss, myname, port,
-                                 20, &fd, &port);
-       if (!NT_STATUS_IS_OK(status)) {
-               goto fail;
+       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;
+}
 
-       cli = cli_state_create(NULL, fd, desthost, NULL, signing_state, flags);
-       if (cli == NULL) {
+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;
+       }
+
+       state->cli = cli_state_create(state, fd, state->desthost, NULL,
+                                     state->signing_state, state->flags);
+       if (tevent_req_nomem(state->cli, req)) {
                close(fd);
-               fd = -1;
-               goto fail;
+               return;
        }
+       tevent_req_done(req);
+}
 
-       *pcli = cli;
-       status = NT_STATUS_OK;
+static NTSTATUS cli_connect_nb_recv(struct tevent_req *req,
+                                   struct cli_state **pcli)
+{
+       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_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)
+{
+       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_connect_nb_send(ev, ev, host, dest_ss, port, name_type,
+                                 myname, signing_state, flags);
+       if (req == NULL) {
+               goto fail;
+       }
+       if (!tevent_req_set_endtime(req, ev, timeval_current_ofs(20, 0))) {
+               goto fail;
+       }
+       if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+               goto fail;
+       }
+       status = cli_connect_nb_recv(req, pcli);
 fail:
-       TALLOC_FREE(frame);
+       TALLOC_FREE(ev);
        return status;
 }
 
+struct cli_start_connection_state {
+       struct tevent_context *ev;
+       struct cli_state *cli;
+       int min_protocol;
+       int max_protocol;
+};
+
+static void cli_start_connection_connected(struct tevent_req *subreq);
+static void cli_start_connection_done(struct tevent_req *subreq);
+
 /**
    establishes a connection to after the negprot. 
    @param output_cli A fully initialised cli structure, non-null only on success
@@ -2746,36 +3166,130 @@ fail:
    @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;
+
+       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();
+       }
+
+       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);
+       }
+       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;
+       }
+
+       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;
+       }
+       tevent_req_set_callback(subreq, cli_start_connection_done, req);
+}
+
+static void cli_start_connection_done(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 = smbXcli_negprot_recv(subreq);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+
+       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);
+       }
+
+       tevent_req_done(req);
+}
+
+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;
+
+       return NT_STATUS_OK;
+}
+
 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 = smbXcli_negprot(cli->conn, cli->timeout, PROTOCOL_CORE,
-                                   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
@@ -2789,95 +3303,236 @@ 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);
+}
 
-               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;
+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);
+
+       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_tree_connect(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 = 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);
        }
 
-       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, smbXcli_conn_use_unicode(cli->conn),
                                   service, strlen(service)+1, NULL);
@@ -2888,19 +3543,76 @@ NTSTATUS cli_raw_tcon(struct cli_state *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);
        }
 
-       *max_xmit = SVAL(ret_vwv + 0, 0);
-       *tid = SVAL(ret_vwv + 1, 0);
+       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);
+}
+
+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,