s4:dsdb: Fix stack use after scope in gkdi_create_root_key()
[samba.git] / source3 / libsmb / cliconnect.c
index 55768bfd6e7f26a275a32961e26279afb2ae58eb..724cdfb99ea70ef8d55fb50de813d8238863cd43 100644 (file)
@@ -1,4 +1,4 @@
-/* 
+/*
    Unix SMB/CIFS implementation.
    client connect/disconnect routines
    Copyright (C) Andrew Tridgell 1994-1998
@@ -22,9 +22,8 @@
 
 #include "includes.h"
 #include "libsmb/libsmb.h"
-#include "auth_info.h"
+#include "libsmb/namequery.h"
 #include "../libcli/auth/libcli_auth.h"
-#include "../libcli/auth/spnego.h"
 #include "smb_krb5.h"
 #include "auth/credentials/credentials.h"
 #include "auth/gensec/gensec.h"
@@ -39,6 +38,7 @@
 #include "../libcli/smb/smbXcli_base.h"
 #include "../libcli/smb/smb_seal.h"
 #include "lib/param/param.h"
+#include "../libcli/smb/smb2_negotiate_context.h"
 
 #define STAR_SMBSERVER "*SMBSERVER"
 
@@ -71,7 +71,10 @@ struct cli_credentials *cli_session_creds_init(TALLOC_CTX *mem_ctx,
        if (lp_ctx == NULL) {
                goto fail;
        }
-       cli_credentials_set_conf(creds, lp_ctx);
+       ok = cli_credentials_set_conf(creds, lp_ctx);
+       if (!ok) {
+               goto fail;
+       }
 
        if (username == NULL) {
                username = "";
@@ -122,13 +125,16 @@ struct cli_credentials *cli_session_creds_init(TALLOC_CTX *mem_ctx,
 
        if (use_kerberos && fallback_after_kerberos) {
                cli_credentials_set_kerberos_state(creds,
-                                                  CRED_AUTO_USE_KERBEROS);
+                                                  CRED_USE_KERBEROS_DESIRED,
+                                                  CRED_SPECIFIED);
        } else if (use_kerberos) {
                cli_credentials_set_kerberos_state(creds,
-                                                  CRED_MUST_USE_KERBEROS);
+                                                  CRED_USE_KERBEROS_REQUIRED,
+                                                  CRED_SPECIFIED);
        } else {
                cli_credentials_set_kerberos_state(creds,
-                                                  CRED_DONT_USE_KERBEROS);
+                                                  CRED_USE_KERBEROS_DISABLED,
+                                                  CRED_SPECIFIED);
        }
 
        if (use_ccache) {
@@ -136,7 +142,9 @@ struct cli_credentials *cli_session_creds_init(TALLOC_CTX *mem_ctx,
 
                features = cli_credentials_get_gensec_features(creds);
                features |= GENSEC_FEATURE_NTLM_CCACHE;
-               cli_credentials_set_gensec_features(creds, features);
+               cli_credentials_set_gensec_features(creds,
+                                                   features,
+                                                   CRED_SPECIFIED);
 
                if (password != NULL && strlen(password) == 0) {
                        /*
@@ -226,65 +234,23 @@ NTSTATUS cli_session_creds_prepare_krb5(struct cli_state *cli,
        const char *user_account = NULL;
        const char *user_domain = NULL;
        const char *pass = NULL;
+       char *canon_principal = NULL;
+       char *canon_realm = NULL;
        const char *target_hostname = NULL;
-       const DATA_BLOB *server_blob = NULL;
        enum credentials_use_kerberos krb5_state;
        bool try_kerberos = false;
        bool need_kinit = false;
        bool auth_requested = true;
        int ret;
+       bool ok;
 
        target_hostname = smbXcli_conn_remote_name(cli->conn);
-       if (!cli->got_kerberos_mechanism) {
-               server_blob = smbXcli_conn_server_gss_blob(cli->conn);
-       }
-
-       /* the server might not even do spnego */
-       if (server_blob != NULL && server_blob->length != 0) {
-               char *OIDs[ASN1_MAX_OIDS] = { NULL, };
-               size_t i;
-               bool ok;
-
-               /*
-                * The server sent us the first part of the SPNEGO exchange in the
-                * 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.
-                */
-               ok = spnego_parse_negTokenInit(frame,
-                                              *server_blob,
-                                              OIDs,
-                                              NULL,
-                                              NULL);
-               if (!ok) {
-                       TALLOC_FREE(frame);
-                       return NT_STATUS_INVALID_PARAMETER;
-               }
-               if (OIDs[0] == NULL) {
-                       TALLOC_FREE(frame);
-                       return NT_STATUS_INVALID_PARAMETER;
-               }
-
-               /* make sure the server understands kerberos */
-               for (i = 0; OIDs[i] != NULL; i++) {
-                       if (i == 0) {
-                               DEBUG(3,("got OID=%s\n", OIDs[i]));
-                       } else {
-                               DEBUGADD(3,("got OID=%s\n", OIDs[i]));
-                       }
-
-                       if (strcmp(OIDs[i], OID_KERBEROS5_OLD) == 0 ||
-                           strcmp(OIDs[i], OID_KERBEROS5) == 0) {
-                               cli->got_kerberos_mechanism = true;
-                               break;
-                       }
-               }
-       }
 
        auth_requested = cli_credentials_authentication_requested(creds);
        if (auth_requested) {
+               errno = 0;
                user_principal = cli_credentials_get_principal(creds, frame);
-               if (user_principal == NULL) {
+               if (errno != 0) {
                        TALLOC_FREE(frame);
                        return NT_STATUS_NO_MEMORY;
                }
@@ -295,10 +261,14 @@ NTSTATUS cli_session_creds_prepare_krb5(struct cli_state *cli,
 
        krb5_state = cli_credentials_get_kerberos_state(creds);
 
-       if (krb5_state != CRED_DONT_USE_KERBEROS) {
+       if (krb5_state != CRED_USE_KERBEROS_DISABLED) {
                try_kerberos = true;
        }
 
+       if (user_principal == NULL) {
+               try_kerberos = false;
+       }
+
        if (target_hostname == NULL) {
                try_kerberos = false;
        } else if (is_ipaddress(target_hostname)) {
@@ -311,7 +281,7 @@ NTSTATUS cli_session_creds_prepare_krb5(struct cli_state *cli,
                try_kerberos = false;
        }
 
-       if (krb5_state == CRED_MUST_USE_KERBEROS && !try_kerberos) {
+       if (krb5_state == CRED_USE_KERBEROS_REQUIRED && !try_kerberos) {
                DEBUG(0, ("Kerberos auth with '%s' (%s\\%s) to access "
                          "'%s' not possible\n",
                          user_principal, user_domain, user_account,
@@ -322,14 +292,8 @@ NTSTATUS cli_session_creds_prepare_krb5(struct cli_state *cli,
 
        if (pass == NULL || strlen(pass) == 0) {
                need_kinit = false;
-       } else if (krb5_state == CRED_MUST_USE_KERBEROS) {
+       } else if (krb5_state == CRED_USE_KERBEROS_REQUIRED) {
                need_kinit = try_kerberos;
-       } else if (!cli->got_kerberos_mechanism) {
-               /*
-                * Most likely the server doesn't support
-                * Kerberos, don't waste time doing a kinit
-                */
-               need_kinit = false;
        } else {
                need_kinit = try_kerberos;
        }
@@ -339,20 +303,38 @@ NTSTATUS cli_session_creds_prepare_krb5(struct cli_state *cli,
                return NT_STATUS_OK;
        }
 
+       DBG_INFO("Doing kinit for %s to access %s\n",
+                user_principal, target_hostname);
 
        /*
         * TODO: This should be done within the gensec layer
         * only if required!
         */
        setenv(KRB5_ENV_CCNAME, "MEMORY:cliconnect", 1);
-       ret = kerberos_kinit_password(user_principal, pass,
-                               0 /* no time correction for now */,
-                               NULL);
+       ret = kerberos_kinit_password_ext(user_principal,
+                                         pass,
+                                         0,
+                                         0,
+                                         0,
+                                         NULL,
+                                         false,
+                                         false,
+                                         0,
+                                         frame,
+                                         &canon_principal,
+                                         &canon_realm,
+                                         NULL);
        if (ret != 0) {
-               DEBUG(0, ("Kinit for %s to access %s failed: %s\n",
-                         user_principal, target_hostname,
-                         error_message(ret)));
-               if (krb5_state == CRED_MUST_USE_KERBEROS) {
+               int dbglvl = DBGLVL_NOTICE;
+
+               if (krb5_state == CRED_USE_KERBEROS_REQUIRED) {
+                       dbglvl = DBGLVL_ERR;
+               }
+
+               DEBUG(dbglvl, ("Kinit for %s to access %s failed: %s\n",
+                              user_principal, target_hostname,
+                              error_message(ret)));
+               if (krb5_state == CRED_USE_KERBEROS_REQUIRED) {
                        TALLOC_FREE(frame);
                        return krb5_to_nt_status(ret);
                }
@@ -360,12 +342,68 @@ NTSTATUS cli_session_creds_prepare_krb5(struct cli_state *cli,
                /*
                 * Ignore the error and hope that NTLM will work
                 */
+               TALLOC_FREE(frame);
+               return NT_STATUS_OK;
        }
 
+       ok = cli_credentials_set_principal(creds,
+                                          canon_principal,
+                                          CRED_SPECIFIED);
+       if (!ok) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       ok = cli_credentials_set_realm(creds,
+                                      canon_realm,
+                                      CRED_SPECIFIED);
+       if (!ok) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       DBG_DEBUG("Successfully authenticated as %s (%s) to access %s using "
+                 "Kerberos\n",
+                 user_principal,
+                 canon_principal,
+                 target_hostname);
+
        TALLOC_FREE(frame);
        return NT_STATUS_OK;
 }
 
+static NTSTATUS cli_state_update_after_sesssetup(struct cli_state *cli,
+                                                const char *native_os,
+                                                const char *native_lm,
+                                                const char *primary_domain)
+{
+#define _VALID_STR(p) ((p) != NULL && (p)[0] != '\0')
+
+       if (!_VALID_STR(cli->server_os) && _VALID_STR(native_os)) {
+               cli->server_os = talloc_strdup(cli, native_os);
+               if (cli->server_os == NULL) {
+                       return NT_STATUS_NO_MEMORY;
+               }
+       }
+
+       if (!_VALID_STR(cli->server_type) && _VALID_STR(native_lm)) {
+               cli->server_type = talloc_strdup(cli, native_lm);
+               if (cli->server_type == NULL) {
+                       return NT_STATUS_NO_MEMORY;
+               }
+       }
+
+       if (!_VALID_STR(cli->server_domain) && _VALID_STR(primary_domain)) {
+               cli->server_domain = talloc_strdup(cli, primary_domain);
+               if (cli->server_domain == NULL) {
+                       return NT_STATUS_NO_MEMORY;
+               }
+       }
+
+#undef _VALID_STRING
+       return NT_STATUS_OK;
+}
+
 /********************************************************
  Utility function to ensure we always return at least
  a valid char * pointer to an empty string for the
@@ -380,13 +418,13 @@ static NTSTATUS smb_bytes_talloc_string(TALLOC_CTX *mem_ctx,
                                        size_t srclen,
                                        ssize_t *destlen)
 {
-       *destlen = clistr_pull_talloc(mem_ctx,
-                               (const char *)hdr,
-                               SVAL(hdr, HDR_FLG2),
-                               dest,
-                               (char *)src,
-                               srclen,
-                               STR_TERMINATE);
+       *destlen = pull_string_talloc(mem_ctx,
+                                     (const char *)hdr,
+                                     SVAL(hdr, HDR_FLG2),
+                                     dest,
+                                     (char *)src,
+                                     srclen,
+                                     STR_TERMINATE);
        if (*destlen == -1) {
                return NT_STATUS_NO_MEMORY;
        }
@@ -594,7 +632,6 @@ static void cli_session_setup_guest_done(struct tevent_req *subreq)
                tevent_req_nterror(req, status);
                return;
        }
-       p += ret;
 
        tevent_req_done(req);
 }
@@ -756,7 +793,6 @@ static void cli_sesssetup_blob_done(struct tevent_req *subreq)
                subreq, struct tevent_req);
        struct cli_sesssetup_blob_state *state = tevent_req_data(
                req, struct cli_sesssetup_blob_state);
-       struct cli_state *cli = state->cli;
        NTSTATUS status;
 
        if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
@@ -778,15 +814,16 @@ static void cli_sesssetup_blob_done(struct tevent_req *subreq)
                return;
        }
 
-       if (cli->server_os == NULL) {
-               cli->server_os = talloc_move(cli, &state->out_native_os);
-       }
-       if (cli->server_type == NULL) {
-               cli->server_type = talloc_move(cli, &state->out_native_lm);
-       }
-
        state->status = status;
 
+       status = cli_state_update_after_sesssetup(state->cli,
+                                                 state->out_native_os,
+                                                 state->out_native_lm,
+                                                 NULL);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+
        if (state->blob.length != 0) {
                /*
                 * More to send
@@ -1064,13 +1101,16 @@ static void cli_session_setup_gensec_remote_done(struct tevent_req *subreq)
                         * We can't finish the gensec handshake, we don't
                         * have a negotiated session key.
                         *
-                        * So just pretend we are completely done.
+                        * So just pretend we are completely done,
+                        * we need to continue as anonymous from this point,
+                        * as we can't get a session key.
                         *
                         * Note that smbXcli_session_is_guest()
                         * always returns false if we require signing.
                         */
                        state->blob_in = data_blob_null;
                        state->local_ready = true;
+                       state->is_anonymous = true;
                }
 
                state->remote_ready = true;
@@ -1084,6 +1124,58 @@ static void cli_session_setup_gensec_remote_done(struct tevent_req *subreq)
        cli_session_setup_gensec_local_next(req);
 }
 
+static void cli_session_dump_keys(TALLOC_CTX *mem_ctx,
+                                 struct smbXcli_session *session,
+                                 DATA_BLOB session_key)
+{
+       NTSTATUS status;
+       DATA_BLOB sig = data_blob_null;
+       DATA_BLOB app = data_blob_null;
+       DATA_BLOB enc = data_blob_null;
+       DATA_BLOB dec = data_blob_null;
+       uint64_t sid = smb2cli_session_current_id(session);
+
+       status = smb2cli_session_signing_key(session, mem_ctx, &sig);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto out;
+       }
+       status = smbXcli_session_application_key(session, mem_ctx, &app);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto out;
+       }
+       status = smb2cli_session_encryption_key(session, mem_ctx, &enc);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto out;
+       }
+       status = smb2cli_session_decryption_key(session, mem_ctx, &dec);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto out;
+       }
+
+       DEBUG(0, ("debug encryption: dumping generated session keys\n"));
+       DEBUGADD(0, ("Session Id    "));
+       dump_data(0, (uint8_t*)&sid, sizeof(sid));
+       DEBUGADD(0, ("Session Key   "));
+       dump_data(0, session_key.data, session_key.length);
+       DEBUGADD(0, ("Signing Key   "));
+       dump_data(0, sig.data, sig.length);
+       DEBUGADD(0, ("App Key       "));
+       dump_data(0, app.data, app.length);
+
+       /* In client code, ServerIn is the encryption key */
+
+       DEBUGADD(0, ("ServerIn Key  "));
+       dump_data(0, enc.data, enc.length);
+       DEBUGADD(0, ("ServerOut Key "));
+       dump_data(0, dec.data, dec.length);
+
+out:
+       data_blob_clear_free(&sig);
+       data_blob_clear_free(&app);
+       data_blob_clear_free(&enc);
+       data_blob_clear_free(&dec);
+}
+
 static void cli_session_setup_gensec_ready(struct tevent_req *req)
 {
        struct cli_session_setup_gensec_state *state =
@@ -1151,6 +1243,11 @@ static void cli_session_setup_gensec_ready(struct tevent_req *req)
                if (tevent_req_nterror(req, status)) {
                        return;
                }
+               if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB3_00
+                   && lp_debug_encryption())
+               {
+                       cli_session_dump_keys(state, session, state->session_key);
+               }
        } else {
                struct smbXcli_session *session = state->cli->smb1.session;
                bool active;
@@ -1243,9 +1340,13 @@ static struct tevent_req *cli_session_setup_spnego_send(
 
        status = cli_session_creds_prepare_krb5(cli, creds);
        if (tevent_req_nterror(req, status)) {
-               return tevent_req_post(req, ev);;
+               return tevent_req_post(req, ev);
        }
 
+       DBG_INFO("Connect to %s as %s using SPNEGO\n",
+                target_hostname,
+                cli_credentials_get_principal(creds, talloc_tos()));
+
        subreq = cli_session_setup_gensec_send(state, ev, cli, creds,
                                               target_service, target_hostname);
        if (tevent_req_nomem(subreq, req)) {
@@ -1309,7 +1410,7 @@ static void cli_session_setup_creds_cleanup(struct tevent_req *req,
         * We only call data_blob_clear() as
         * some of the blobs point to the same memory.
         *
-        * We let the talloc hierachy free the memory.
+        * We let the talloc hierarchy free the memory.
         */
        data_blob_clear(&state->apassword_blob);
        data_blob_clear(&state->upassword_blob);
@@ -1338,8 +1439,6 @@ struct tevent_req *cli_session_setup_creds_send(TALLOC_CTX *mem_ctx,
        uint16_t sec_mode = smb1cli_conn_server_security_mode(cli->conn);
        bool use_spnego = false;
        int flags = 0;
-       enum credentials_use_kerberos krb5_state;
-       uint32_t gensec_features;
        const char *username = "";
        const char *domain = "";
        DATA_BLOB target_info = data_blob_null;
@@ -1350,6 +1449,8 @@ struct tevent_req *cli_session_setup_creds_send(TALLOC_CTX *mem_ctx,
        uint32_t in_sess_key = 0;
        const char *in_native_os = NULL;
        const char *in_native_lm = NULL;
+       enum credentials_use_kerberos krb5_state =
+               cli_credentials_get_kerberos_state(creds);
        NTSTATUS status;
 
        req = tevent_req_create(mem_ctx, &state,
@@ -1361,30 +1462,6 @@ struct tevent_req *cli_session_setup_creds_send(TALLOC_CTX *mem_ctx,
 
        tevent_req_set_cleanup_fn(req, cli_session_setup_creds_cleanup);
 
-       krb5_state = cli_credentials_get_kerberos_state(creds);
-       gensec_features = cli_credentials_get_gensec_features(creds);
-
-       switch (krb5_state) {
-       case CRED_MUST_USE_KERBEROS:
-               cli->use_kerberos = true;
-               cli->fallback_after_kerberos = false;
-               break;
-       case CRED_AUTO_USE_KERBEROS:
-               cli->use_kerberos = true;
-               cli->fallback_after_kerberos = true;
-               break;
-       case CRED_DONT_USE_KERBEROS:
-               cli->use_kerberos = false;
-               cli->fallback_after_kerberos = false;
-               break;
-       }
-
-       if (gensec_features & GENSEC_FEATURE_NTLM_CCACHE) {
-               cli->use_ccache = true;
-       } else {
-               cli->use_ccache = false;
-       }
-
        /*
         * Now work out what sort of session setup we are going to
         * do. I have split this into separate functions to make the flow a bit
@@ -1415,6 +1492,13 @@ struct tevent_req *cli_session_setup_creds_send(TALLOC_CTX *mem_ctx,
                return req;
        }
 
+       if (krb5_state == CRED_USE_KERBEROS_REQUIRED) {
+               DBG_WARNING("Kerberos authentication requested, but "
+                           "the server does not support SPNEGO authentication\n");
+               tevent_req_nterror(req, NT_STATUS_NETWORK_CREDENTIAL_CONFLICT);
+               return tevent_req_post(req, ev);
+       }
+
        if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_LANMAN1) {
                /*
                 * SessionSetupAndX was introduced by LANMAN 1.0. So we skip
@@ -1449,6 +1533,8 @@ struct tevent_req *cli_session_setup_creds_send(TALLOC_CTX *mem_ctx,
                return tevent_req_post(req, ev);
        }
 
+       DBG_INFO("Connect to %s as %s using NTLM\n", domain, username);
+
        if ((sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) == 0) {
                bool use_unicode = smbXcli_conn_use_unicode(cli->conn);
                uint8_t *bytes = NULL;
@@ -1655,20 +1741,17 @@ static void cli_session_setup_creds_done_nt1(struct tevent_req *subreq)
                                                &state->out_native_lm,
                                                &state->out_primary_domain);
        TALLOC_FREE(subreq);
-       if (!NT_STATUS_IS_OK(status)) {
+       if (tevent_req_nterror(req, status)) {
                DEBUG(3, ("NT1 login failed: %s\n", nt_errstr(status)));
-               tevent_req_nterror(req, status);
                return;
        }
 
-       if (cli->server_os == NULL) {
-               cli->server_os = talloc_move(cli, &state->out_native_os);
-       }
-       if (cli->server_type == NULL) {
-               cli->server_type = talloc_move(cli, &state->out_native_lm);
-       }
-       if (cli->server_domain == NULL) {
-               cli->server_domain = talloc_move(cli, &state->out_primary_domain);
+       status = cli_state_update_after_sesssetup(state->cli,
+                                                 state->out_native_os,
+                                                 state->out_native_lm,
+                                                 state->out_primary_domain);
+       if (tevent_req_nterror(req, status)) {
+               return;
        }
 
        ok = smb1cli_conn_activate_signing(cli->conn,
@@ -1701,24 +1784,23 @@ static void cli_session_setup_creds_done_lm21(struct tevent_req *subreq)
                subreq, struct tevent_req);
        struct cli_session_setup_creds_state *state = tevent_req_data(
                req, struct cli_session_setup_creds_state);
-       struct cli_state *cli = state->cli;
        NTSTATUS status;
 
        status = smb1cli_session_setup_lm21_recv(subreq, state,
                                                 &state->out_native_os,
                                                 &state->out_native_lm);
        TALLOC_FREE(subreq);
-       if (!NT_STATUS_IS_OK(status)) {
+       if (tevent_req_nterror(req, status)) {
                DEBUG(3, ("LM21 login failed: %s\n", nt_errstr(status)));
-               tevent_req_nterror(req, status);
                return;
        }
 
-       if (cli->server_os == NULL) {
-               cli->server_os = talloc_move(cli, &state->out_native_os);
-       }
-       if (cli->server_type == NULL) {
-               cli->server_type = talloc_move(cli, &state->out_native_lm);
+       status = cli_state_update_after_sesssetup(state->cli,
+                                                 state->out_native_os,
+                                                 state->out_native_lm,
+                                                 NULL);
+       if (tevent_req_nterror(req, status)) {
+               return;
        }
 
        tevent_req_done(req);
@@ -1758,7 +1840,7 @@ NTSTATUS cli_session_setup_creds(struct cli_state *cli,
 
 NTSTATUS cli_session_setup_anon(struct cli_state *cli)
 {
-       NTSTATUS status = NT_STATUS_NO_MEMORY;
+       NTSTATUS status;
        struct cli_credentials *creds = NULL;
 
        creds = cli_credentials_init_anon(cli);
@@ -1821,8 +1903,7 @@ static void cli_ulogoff_done(struct tevent_req *subreq)
        NTSTATUS status;
 
        status = cli_smb_recv(subreq, NULL, NULL, 0, NULL, NULL, NULL, NULL);
-       if (!NT_STATUS_IS_OK(status)) {
-               tevent_req_nterror(req, status);
+       if (tevent_req_nterror(req, status)) {
                return;
        }
        cli_state_set_uid(state->cli, UID_FIELD_INVALID);
@@ -1909,6 +1990,13 @@ struct tevent_req *cli_tcon_andx_create(TALLOC_CTX *mem_ctx,
        state->cli = cli;
        vwv = state->vwv;
 
+       TALLOC_FREE(cli->smb1.tcon);
+       cli->smb1.tcon = smbXcli_tcon_create(cli);
+       if (tevent_req_nomem(cli->smb1.tcon, req)) {
+               return tevent_req_post(req, ev);
+       }
+       smb1cli_tcon_set_id(cli->smb1.tcon, UINT16_MAX);
+
        cli->share = talloc_strdup(cli, share);
        if (!cli->share) {
                return NULL;
@@ -2054,8 +2142,7 @@ struct tevent_req *cli_tcon_andx_send(TALLOC_CTX *mem_ctx,
                return req;
        }
        status = smb1cli_req_chain_submit(&subreq, 1);
-       if (!NT_STATUS_IS_OK(status)) {
-               tevent_req_nterror(req, status);
+       if (tevent_req_nterror(req, status)) {
                return tevent_req_post(req, ev);
        }
        return req;
@@ -2080,21 +2167,20 @@ static void cli_tcon_andx_done(struct tevent_req *subreq)
        status = cli_smb_recv(subreq, state, &in, 0, &wct, &vwv,
                              &num_bytes, &bytes);
        TALLOC_FREE(subreq);
-       if (!NT_STATUS_IS_OK(status)) {
-               tevent_req_nterror(req, status);
+       if (tevent_req_nterror(req, status)) {
                return;
        }
 
        inhdr = in + NBT_HDR_SIZE;
 
        if (num_bytes) {
-               if (clistr_pull_talloc(cli,
-                               (const char *)inhdr,
-                               SVAL(inhdr, HDR_FLG2),
-                               &cli->dev,
-                               bytes,
-                               num_bytes,
-                               STR_TERMINATE|STR_ASCII) == -1) {
+               if (pull_string_talloc(cli,
+                                      (const char *)inhdr,
+                                      SVAL(inhdr, HDR_FLG2),
+                                      &cli->dev,
+                                      bytes,
+                                      num_bytes,
+                                      STR_TERMINATE|STR_ASCII) == -1) {
                        tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
                        return;
                }
@@ -2218,6 +2304,7 @@ static struct tevent_req *cli_tree_connect_send(
        if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
                char *unc;
 
+               TALLOC_FREE(cli->smb2.tcon);
                cli->smb2.tcon = smbXcli_tcon_create(cli);
                if (tevent_req_nomem(cli->smb2.tcon, req)) {
                        return tevent_req_post(req, ev);
@@ -2264,14 +2351,14 @@ static struct tevent_req *cli_tree_connect_send(
 
 static void cli_tree_connect_smb2_done(struct tevent_req *subreq)
 {
-       tevent_req_simple_finish_ntstatus(
-               subreq, smb2cli_tcon_recv(subreq));
+       NTSTATUS status = smb2cli_tcon_recv(subreq);
+       tevent_req_simple_finish_ntstatus(subreq, status);
 }
 
 static void cli_tree_connect_andx_done(struct tevent_req *subreq)
 {
-       tevent_req_simple_finish_ntstatus(
-               subreq, cli_tcon_andx_recv(subreq));
+       NTSTATUS status = cli_tcon_andx_recv(subreq);
+       tevent_req_simple_finish_ntstatus(subreq, status);
 }
 
 static void cli_tree_connect_raw_done(struct tevent_req *subreq)
@@ -2336,9 +2423,25 @@ NTSTATUS cli_tree_connect_creds(struct cli_state *cli,
                                const char *share, const char *dev,
                                struct cli_credentials *creds)
 {
+       bool need_pass = false;
        const char *pw = NULL;
 
-       if (creds != NULL) {
+       /*
+        * We should work out if the protocol
+        * will make use of a password for share level
+        * authentication before we may cause
+        * the password prompt to be called.
+        */
+       if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+               uint16_t sec_mode = smb1cli_conn_server_security_mode(cli->conn);
+
+               /* in user level security don't send a password now */
+               if (!(sec_mode & NEGOTIATE_SECURITY_USER_LEVEL)) {
+                       need_pass = true;
+               }
+       }
+
+       if (need_pass && creds != NULL) {
                pw = cli_credentials_get_password(creds);
        }
 
@@ -2386,11 +2489,10 @@ static void cli_tdis_done(struct tevent_req *subreq)
 
        status = cli_smb_recv(subreq, NULL, NULL, 0, NULL, NULL, NULL, NULL);
        TALLOC_FREE(subreq);
-       if (!NT_STATUS_IS_OK(status)) {
-               tevent_req_nterror(req, status);
+       if (tevent_req_nterror(req, status)) {
                return;
        }
-       cli_state_set_tid(state->cli, UINT16_MAX);
+       TALLOC_FREE(state->cli->smb1.tcon);
        tevent_req_done(req);
 }
 
@@ -2406,10 +2508,14 @@ NTSTATUS cli_tdis(struct cli_state *cli)
        NTSTATUS status = NT_STATUS_NO_MEMORY;
 
        if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
-               return smb2cli_tdis(cli->conn,
+               status = smb2cli_tdis(cli->conn,
                                    cli->timeout,
                                    cli->smb2.session,
                                    cli->smb2.tcon);
+               if (NT_STATUS_IS_OK(status)) {
+                       TALLOC_FREE(cli->smb2.tcon);
+               }
+               return status;
        }
 
        if (smbXcli_conn_has_async_calls(cli->conn)) {
@@ -2454,9 +2560,9 @@ static struct tevent_req *cli_connect_sock_send(
 {
        struct tevent_req *req, *subreq;
        struct cli_connect_sock_state *state;
-       const char *prog;
-       struct sockaddr_storage *addrs;
-       unsigned i, num_addrs;
+       struct sockaddr_storage *addrs = NULL;
+       unsigned i;
+       unsigned num_addrs = 0;
        NTSTATUS status;
 
        req = tevent_req_create(mem_ctx, &state,
@@ -2465,19 +2571,6 @@ static struct tevent_req *cli_connect_sock_send(
                return NULL;
        }
 
-       prog = getenv("LIBSMB_PROG");
-       if (prog != NULL) {
-               state->fd = sock_exec(prog);
-               if (state->fd == -1) {
-                       status = map_nt_error_from_unix(errno);
-                       tevent_req_nterror(req, status);
-               } else {
-                       state->port = 0;
-                       tevent_req_done(req);
-               }
-               return tevent_req_post(req, ev);
-       }
-
        if ((pss == NULL) || is_zero_addr(pss)) {
 
                /*
@@ -2488,8 +2581,7 @@ static struct tevent_req *cli_connect_sock_send(
 
                status = resolve_name_list(state, host, name_type,
                                           &addrs, &num_addrs);
-               if (!NT_STATUS_IS_OK(status)) {
-                       tevent_req_nterror(req, status);
+               if (tevent_req_nterror(req, status)) {
                        return tevent_req_post(req, ev);
                }
        } else {
@@ -2564,7 +2656,7 @@ static NTSTATUS cli_connect_sock_recv(struct tevent_req *req,
 
 struct cli_connect_nb_state {
        const char *desthost;
-       int signing_state;
+       enum smb_signing_setting signing_state;
        int flags;
        struct cli_state *cli;
 };
@@ -2575,7 +2667,7 @@ 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)
+       enum smb_signing_setting signing_state, int flags)
 {
        struct tevent_req *req, *subreq;
        struct cli_connect_nb_state *state;
@@ -2606,7 +2698,7 @@ static struct tevent_req *cli_connect_nb_send(
                }
        } else {
                /* No host or dest_ss given. Error out. */
-               tevent_req_error(req, EINVAL);
+               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
                return tevent_req_post(req, ev);
        }
 
@@ -2635,7 +2727,7 @@ static void cli_connect_nb_done(struct tevent_req *subreq)
                return;
        }
 
-       state->cli = cli_state_create(state, fd, state->desthost, NULL,
+       state->cli = cli_state_create(state, fd, state->desthost,
                                      state->signing_state, state->flags);
        if (tevent_req_nomem(state->cli, req)) {
                close(fd);
@@ -2645,6 +2737,7 @@ static void cli_connect_nb_done(struct tevent_req *subreq)
 }
 
 static NTSTATUS cli_connect_nb_recv(struct tevent_req *req,
+                                   TALLOC_CTX *mem_ctx,
                                    struct cli_state **pcli)
 {
        struct cli_connect_nb_state *state = tevent_req_data(
@@ -2654,19 +2747,25 @@ static NTSTATUS cli_connect_nb_recv(struct tevent_req *req,
        if (tevent_req_is_nterror(req, &status)) {
                return status;
        }
-       *pcli = talloc_move(NULL, &state->cli);
+       *pcli = talloc_move(mem_ctx, &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)
+NTSTATUS cli_connect_nb(TALLOC_CTX *mem_ctx,
+                       const char *host,
+                       const struct sockaddr_storage *dest_ss,
+                       uint16_t port,
+                       int name_type,
+                       const char *myname,
+                       enum smb_signing_setting 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());
+       ev = samba_tevent_context_init(mem_ctx);
        if (ev == NULL) {
                goto fail;
        }
@@ -2681,7 +2780,7 @@ NTSTATUS cli_connect_nb(const char *host, const struct sockaddr_storage *dest_ss
        if (!tevent_req_poll_ntstatus(req, ev, &status)) {
                goto fail;
        }
-       status = cli_connect_nb_recv(req, pcli);
+       status = cli_connect_nb_recv(req, mem_ctx, pcli);
 fail:
        TALLOC_FREE(ev);
        return status;
@@ -2692,16 +2791,17 @@ struct cli_start_connection_state {
        struct cli_state *cli;
        int min_protocol;
        int max_protocol;
+       struct smb2_negotiate_contexts *negotiate_contexts;
 };
 
 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. 
+   establishes a connection to after the negprot.
    @param output_cli A fully initialised cli structure, non-null only on success
    @param dest_host The netbios name of the remote host
-   @param dest_ss (optional) The the destination IP, NULL for name based lookup
+   @param dest_ss (optional) The destination IP, NULL for name based lookup
    @param port (optional) The destination port (0 for default)
 */
 
@@ -2709,7 +2809,8 @@ 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)
+       enum smb_signing_setting signing_state, int flags,
+       struct smb2_negotiate_contexts *negotiate_contexts)
 {
        struct tevent_req *req, *subreq;
        struct cli_start_connection_state *state;
@@ -2721,7 +2822,7 @@ static struct tevent_req *cli_start_connection_send(
        }
        state->ev = ev;
 
-       if (signing_state == SMB_SIGNING_IPC_DEFAULT) {
+       if (flags & CLI_FULL_CONNECTION_IPC) {
                state->min_protocol = lp_client_ipc_min_protocol();
                state->max_protocol = lp_client_ipc_max_protocol();
        } else {
@@ -2729,6 +2830,60 @@ static struct tevent_req *cli_start_connection_send(
                state->max_protocol = lp_client_max_protocol();
        }
 
+       if (flags & CLI_FULL_CONNECTION_FORCE_SMB1) {
+               state->max_protocol = MIN(state->max_protocol,
+                                         PROTOCOL_NT1);
+               state->min_protocol = MIN(state->min_protocol,
+                                         state->max_protocol);
+       }
+
+       if (flags & CLI_FULL_CONNECTION_DISABLE_SMB1) {
+               state->min_protocol = MAX(state->min_protocol,
+                                         PROTOCOL_SMB2_02);
+               state->max_protocol = MAX(state->max_protocol,
+                                         state->min_protocol);
+       }
+
+       state->negotiate_contexts = talloc_zero(
+               state, struct smb2_negotiate_contexts);
+       if (tevent_req_nomem(state->negotiate_contexts, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       if (flags & CLI_FULL_CONNECTION_REQUEST_POSIX) {
+               NTSTATUS status;
+
+               status = smb2_negotiate_context_add(
+                       state->negotiate_contexts,
+                       state->negotiate_contexts,
+                       SMB2_POSIX_EXTENSIONS_AVAILABLE,
+                       (const uint8_t *)SMB2_CREATE_TAG_POSIX,
+                       strlen(SMB2_CREATE_TAG_POSIX));
+               if (tevent_req_nterror(req, status)) {
+                       return tevent_req_post(req, ev);
+               }
+       }
+
+       if (negotiate_contexts != NULL) {
+               uint16_t i;
+
+               for (i=0; i<negotiate_contexts->num_contexts; i++) {
+                       struct smb2_negotiate_context *ctx =
+                               &negotiate_contexts->contexts[i];
+                       NTSTATUS status;
+
+                       status = smb2_negotiate_context_add(
+                               state->negotiate_contexts,
+                               state->negotiate_contexts,
+                               ctx->type,
+                               ctx->data.data,
+                               ctx->data.length);
+                       if (tevent_req_nterror(req, status)) {
+                               return tevent_req_post(req, ev);
+                       }
+               }
+       }
+
        subreq = cli_connect_nb_send(state, ev, dest_host, dest_ss, port,
                                     0x20, my_name, signing_state, flags);
        if (tevent_req_nomem(subreq, req)) {
@@ -2746,16 +2901,21 @@ static void cli_start_connection_connected(struct tevent_req *subreq)
                req, struct cli_start_connection_state);
        NTSTATUS status;
 
-       status = cli_connect_nb_recv(subreq, &state->cli);
+       status = cli_connect_nb_recv(subreq, state, &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);
+       subreq = smbXcli_negprot_send(
+               state,
+               state->ev,
+               state->cli->conn,
+               state->cli->timeout,
+               state->min_protocol,
+               state->max_protocol,
+               WINDOWS_CLIENT_PURE_SMB2_NEGPROT_INITIAL_CREDIT_ASK,
+               state->negotiate_contexts);
        if (tevent_req_nomem(subreq, req)) {
                return;
        }
@@ -2770,7 +2930,7 @@ static void cli_start_connection_done(struct tevent_req *subreq)
                req, struct cli_start_connection_state);
        NTSTATUS status;
 
-       status = smbXcli_negprot_recv(subreq);
+       status = smbXcli_negprot_recv(subreq, NULL, NULL);
        TALLOC_FREE(subreq);
        if (tevent_req_nterror(req, status)) {
                return;
@@ -2786,6 +2946,7 @@ static void cli_start_connection_done(struct tevent_req *subreq)
 }
 
 static NTSTATUS cli_start_connection_recv(struct tevent_req *req,
+                                         TALLOC_CTX *mem_ctx,
                                          struct cli_state **output_cli)
 {
        struct cli_start_connection_state *state = tevent_req_data(
@@ -2795,34 +2956,35 @@ static NTSTATUS cli_start_connection_recv(struct tevent_req *req,
        if (tevent_req_is_nterror(req, &status)) {
                return status;
        }
-       *output_cli = state->cli;
+       *output_cli = talloc_move(mem_ctx, &state->cli);
 
        return NT_STATUS_OK;
 }
 
-NTSTATUS cli_start_connection(struct cli_state **output_cli, 
-                             const char *my_name, 
-                             const char *dest_host, 
+NTSTATUS cli_start_connection(TALLOC_CTX *mem_ctx,
+                             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)
+                             enum smb_signing_setting signing_state, int flags)
 {
        struct tevent_context *ev;
        struct tevent_req *req;
        NTSTATUS status = NT_STATUS_NO_MEMORY;
 
-       ev = samba_tevent_context_init(talloc_tos());
+       ev = samba_tevent_context_init(mem_ctx);
        if (ev == NULL) {
                goto fail;
        }
        req = cli_start_connection_send(ev, ev, my_name, dest_host, dest_ss,
-                                       port, signing_state, flags);
+                                       port, signing_state, flags, NULL);
        if (req == NULL) {
                goto fail;
        }
        if (!tevent_req_poll_ntstatus(req, ev, &status)) {
                goto fail;
        }
-       status = cli_start_connection_recv(req, output_cli);
+       status = cli_start_connection_recv(req, mem_ctx, output_cli);
 fail:
        TALLOC_FREE(ev);
        return status;
@@ -3249,7 +3411,7 @@ NTSTATUS cli_smb1_setup_encryption(struct cli_state *cli,
    @param dest_ip (optional) The the destination IP, NULL for name based lookup
    @param port (optional) The destination port (0 for default)
    @param service (optional) The share to make the connection to.  Should be 'unqualified' in any way.
-   @param service_type The 'type' of serivice. 
+   @param service_type The 'type' of service.
    @param creds The used user credentials
 */
 
@@ -3273,8 +3435,13 @@ static int cli_full_connection_creds_state_destructor(
 }
 
 static void cli_full_connection_creds_conn_done(struct tevent_req *subreq);
-static void cli_full_connection_creds_sess_start(struct tevent_req *req);
 static void cli_full_connection_creds_sess_done(struct tevent_req *subreq);
+static void cli_full_connection_creds_enc_start(struct tevent_req *req);
+static void cli_full_connection_creds_enc_tcon(struct tevent_req *subreq);
+static void cli_full_connection_creds_enc_ver(struct tevent_req *subreq);
+static void cli_full_connection_creds_enc_done(struct tevent_req *subreq);
+static void cli_full_connection_creds_enc_tdis(struct tevent_req *req);
+static void cli_full_connection_creds_enc_finished(struct tevent_req *subreq);
 static void cli_full_connection_creds_tcon_start(struct tevent_req *req);
 static void cli_full_connection_creds_tcon_done(struct tevent_req *subreq);
 
@@ -3284,12 +3451,14 @@ struct tevent_req *cli_full_connection_creds_send(
        const struct sockaddr_storage *dest_ss, int port,
        const char *service, const char *service_type,
        struct cli_credentials *creds,
-       int flags, int signing_state)
+       int flags,
+       struct smb2_negotiate_contexts *negotiate_contexts)
 {
        struct tevent_req *req, *subreq;
        struct cli_full_connection_creds_state *state;
-       enum credentials_use_kerberos krb5_state;
-       uint32_t gensec_features = 0;
+       enum smb_signing_setting signing_state;
+       enum smb_encryption_setting encryption_state =
+               cli_credentials_get_smb_encryption(creds);
 
        req = tevent_req_create(mem_ctx, &state,
                                struct cli_full_connection_creds_state);
@@ -3298,39 +3467,32 @@ struct tevent_req *cli_full_connection_creds_send(
        }
        talloc_set_destructor(state, cli_full_connection_creds_state_destructor);
 
-       flags &= ~CLI_FULL_CONNECTION_USE_KERBEROS;
-       flags &= ~CLI_FULL_CONNECTION_FALLBACK_AFTER_KERBEROS;
-       flags &= ~CLI_FULL_CONNECTION_USE_CCACHE;
-       flags &= ~CLI_FULL_CONNECTION_USE_NT_HASH;
-
-       krb5_state = cli_credentials_get_kerberos_state(creds);
-       switch (krb5_state) {
-       case CRED_MUST_USE_KERBEROS:
-               flags |= CLI_FULL_CONNECTION_USE_KERBEROS;
-               flags &= ~CLI_FULL_CONNECTION_DONT_SPNEGO;
-               break;
-       case CRED_AUTO_USE_KERBEROS:
-               flags |= CLI_FULL_CONNECTION_USE_KERBEROS;
-               flags |= CLI_FULL_CONNECTION_FALLBACK_AFTER_KERBEROS;
-               break;
-       case CRED_DONT_USE_KERBEROS:
-               break;
-       }
-
-       gensec_features = cli_credentials_get_gensec_features(creds);
-       if (gensec_features & GENSEC_FEATURE_NTLM_CCACHE) {
-               flags |= CLI_FULL_CONNECTION_USE_CCACHE;
-       }
-
        state->ev = ev;
        state->service = service;
        state->service_type = service_type;
        state->creds = creds;
        state->flags = flags;
 
+       if (flags & CLI_FULL_CONNECTION_IPC) {
+               signing_state = cli_credentials_get_smb_ipc_signing(creds);
+       } else {
+               signing_state = cli_credentials_get_smb_signing(creds);
+       }
+
+       if (encryption_state == SMB_ENCRYPTION_REQUIRED) {
+               if (flags & CLI_FULL_CONNECTION_ANONYMOUS_FALLBACK) {
+                       encryption_state = SMB_ENCRYPTION_DESIRED;
+               }
+       }
+
+       if (encryption_state >= SMB_ENCRYPTION_DESIRED) {
+               signing_state = SMB_SIGNING_REQUIRED;
+       }
+
        subreq = cli_start_connection_send(
                state, ev, my_name, dest_host, dest_ss, port,
-               signing_state, flags);
+               signing_state, flags,
+               negotiate_contexts);
        if (tevent_req_nomem(subreq, req)) {
                return tevent_req_post(req, ev);
        }
@@ -3348,21 +3510,12 @@ static void cli_full_connection_creds_conn_done(struct tevent_req *subreq)
                req, struct cli_full_connection_creds_state);
        NTSTATUS status;
 
-       status = cli_start_connection_recv(subreq, &state->cli);
+       status = cli_start_connection_recv(subreq, state, &state->cli);
        TALLOC_FREE(subreq);
        if (tevent_req_nterror(req, status)) {
                return;
        }
 
-       cli_full_connection_creds_sess_start(req);
-}
-
-static void cli_full_connection_creds_sess_start(struct tevent_req *req)
-{
-       struct cli_full_connection_creds_state *state = tevent_req_data(
-               req, struct cli_full_connection_creds_state);
-       struct tevent_req *subreq = NULL;
-
        subreq = cli_session_setup_creds_send(
                state, state->ev, state->cli, state->creds);
        if (tevent_req_nomem(subreq, req)) {
@@ -3394,10 +3547,200 @@ static void cli_full_connection_creds_sess_done(struct tevent_req *subreq)
                        return;
                }
 
-               cli_full_connection_creds_sess_start(req);
+               subreq = cli_session_setup_creds_send(
+                       state, state->ev, state->cli, state->creds);
+               if (tevent_req_nomem(subreq, req)) {
+                       return;
+               }
+               tevent_req_set_callback(subreq,
+                                       cli_full_connection_creds_sess_done,
+                                       req);
+               return;
+       }
+
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+
+       cli_full_connection_creds_enc_start(req);
+}
+
+static void cli_full_connection_creds_enc_start(struct tevent_req *req)
+{
+       struct cli_full_connection_creds_state *state = tevent_req_data(
+               req, struct cli_full_connection_creds_state);
+       enum smb_encryption_setting encryption_state =
+               cli_credentials_get_smb_encryption(state->creds);
+       struct tevent_req *subreq = NULL;
+       NTSTATUS status;
+
+       if (encryption_state < SMB_ENCRYPTION_DESIRED) {
+               cli_full_connection_creds_tcon_start(req);
+               return;
+       }
+
+       if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
+               status = smb2cli_session_encryption_on(state->cli->smb2.session);
+               if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
+                       if (encryption_state < SMB_ENCRYPTION_REQUIRED) {
+                               cli_full_connection_creds_tcon_start(req);
+                               return;
+                       }
+                       d_printf("Encryption required and "
+                               "server doesn't support "
+                               "SMB3 encryption - failing connect\n");
+                       tevent_req_nterror(req, status);
+                       return;
+               } else if (!NT_STATUS_IS_OK(status)) {
+                       d_printf("Encryption required and "
+                               "setup failed with error %s.\n",
+                               nt_errstr(status));
+                       tevent_req_nterror(req, status);
+                       return;
+               }
+
+               cli_full_connection_creds_tcon_start(req);
+               return;
+       }
+
+       if (!SERVER_HAS_UNIX_CIFS(state->cli)) {
+               if (encryption_state < SMB_ENCRYPTION_REQUIRED) {
+                       cli_full_connection_creds_tcon_start(req);
+                       return;
+               }
+
+               status = NT_STATUS_NOT_SUPPORTED;
+               d_printf("Encryption required and "
+                       "server doesn't support "
+                       "SMB1 Unix Extensions - failing connect\n");
+               tevent_req_nterror(req, status);
+               return;
+       }
+
+       /*
+        * We do a tcon on IPC$ just to setup the encryption,
+        * the real tcon will be encrypted then.
+        */
+       subreq = cli_tree_connect_send(state, state->ev, state->cli,
+                                      "IPC$", "IPC", NULL);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq, cli_full_connection_creds_enc_tcon, req);
+}
+
+static void cli_full_connection_creds_enc_tcon(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct cli_full_connection_creds_state *state = tevent_req_data(
+               req, struct cli_full_connection_creds_state);
+       NTSTATUS status;
+
+       status = cli_tree_connect_recv(subreq);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+
+       subreq = cli_unix_extensions_version_send(state, state->ev, state->cli);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq, cli_full_connection_creds_enc_ver, req);
+}
+
+static void cli_full_connection_creds_enc_ver(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct cli_full_connection_creds_state *state = tevent_req_data(
+               req, struct cli_full_connection_creds_state);
+       enum smb_encryption_setting encryption_state =
+               cli_credentials_get_smb_encryption(state->creds);
+       uint16_t major, minor;
+       uint32_t caplow, caphigh;
+       NTSTATUS status;
+
+       status = cli_unix_extensions_version_recv(subreq,
+                                                 &major, &minor,
+                                                 &caplow,
+                                                 &caphigh);
+       TALLOC_FREE(subreq);
+       if (!NT_STATUS_IS_OK(status)) {
+               if (encryption_state < SMB_ENCRYPTION_REQUIRED) {
+                       /* disconnect ipc$ followed by the real tree connect */
+                       cli_full_connection_creds_enc_tdis(req);
+                       return;
+               }
+               DEBUG(10, ("%s: cli_unix_extensions_version "
+                          "returned %s\n", __func__, nt_errstr(status)));
+               tevent_req_nterror(req, NT_STATUS_UNKNOWN_REVISION);
+               return;
+       }
+
+       if (!(caplow & CIFS_UNIX_TRANSPORT_ENCRYPTION_CAP)) {
+               if (encryption_state < SMB_ENCRYPTION_REQUIRED) {
+                       /* disconnect ipc$ followed by the real tree connect */
+                       cli_full_connection_creds_enc_tdis(req);
+                       return;
+               }
+               DEBUG(10, ("%s: CIFS_UNIX_TRANSPORT_ENCRYPTION_CAP "
+                          "not supported\n", __func__));
+               tevent_req_nterror(req, NT_STATUS_UNSUPPORTED_COMPRESSION);
+               return;
+       }
+
+       subreq = cli_smb1_setup_encryption_send(state, state->ev,
+                                               state->cli,
+                                               state->creds);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq,
+                               cli_full_connection_creds_enc_done,
+                               req);
+}
+
+static void cli_full_connection_creds_enc_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       NTSTATUS status;
+
+       status = cli_smb1_setup_encryption_recv(subreq);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+
+       /* disconnect ipc$ followed by the real tree connect */
+       cli_full_connection_creds_enc_tdis(req);
+}
+
+static void cli_full_connection_creds_enc_tdis(struct tevent_req *req)
+{
+       struct cli_full_connection_creds_state *state = tevent_req_data(
+               req, struct cli_full_connection_creds_state);
+       struct tevent_req *subreq = NULL;
+
+       subreq = cli_tdis_send(state, state->ev, state->cli);
+       if (tevent_req_nomem(subreq, req)) {
                return;
        }
+       tevent_req_set_callback(subreq,
+                               cli_full_connection_creds_enc_finished,
+                               req);
+}
 
+static void cli_full_connection_creds_enc_finished(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       NTSTATUS status;
+
+       status = cli_tdis_recv(subreq);
+       TALLOC_FREE(subreq);
        if (tevent_req_nterror(req, status)) {
                return;
        }
@@ -3448,7 +3791,8 @@ static void cli_full_connection_creds_tcon_done(struct tevent_req *subreq)
 }
 
 NTSTATUS cli_full_connection_creds_recv(struct tevent_req *req,
-                                 struct cli_state **output_cli)
+                                       TALLOC_CTX *mem_ctx,
+                                       struct cli_state **output_cli)
 {
        struct cli_full_connection_creds_state *state = tevent_req_data(
                req, struct cli_full_connection_creds_state);
@@ -3457,103 +3801,44 @@ NTSTATUS cli_full_connection_creds_recv(struct tevent_req *req,
        if (tevent_req_is_nterror(req, &status)) {
                return status;
        }
-       *output_cli = state->cli;
+       *output_cli = talloc_move(mem_ctx, &state->cli);
        talloc_set_destructor(state, NULL);
        return NT_STATUS_OK;
 }
 
-NTSTATUS cli_full_connection_creds(struct cli_state **output_cli,
+NTSTATUS cli_full_connection_creds(TALLOC_CTX *mem_ctx,
+                                  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,
                                   struct cli_credentials *creds,
-                                  int flags,
-                                  int signing_state)
+                                  int flags)
 {
        struct tevent_context *ev;
        struct tevent_req *req;
        NTSTATUS status = NT_STATUS_NO_MEMORY;
 
-       ev = samba_tevent_context_init(talloc_tos());
+       ev = samba_tevent_context_init(mem_ctx);
        if (ev == NULL) {
                goto fail;
        }
        req = cli_full_connection_creds_send(
                ev, ev, my_name, dest_host, dest_ss, port, service,
-               service_type, creds, flags, signing_state);
+               service_type, creds, flags,
+               NULL);
        if (req == NULL) {
                goto fail;
        }
        if (!tevent_req_poll_ntstatus(req, ev, &status)) {
                goto fail;
        }
-       status = cli_full_connection_creds_recv(req, output_cli);
+       status = cli_full_connection_creds_recv(req, mem_ctx, output_cli);
  fail:
        TALLOC_FREE(ev);
        return status;
 }
 
-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)
-{
-       TALLOC_CTX *frame = talloc_stackframe();
-       NTSTATUS status;
-       bool use_kerberos = false;
-       bool fallback_after_kerberos = false;
-       bool use_ccache = false;
-       bool pw_nt_hash = false;
-       struct cli_credentials *creds = NULL;
-
-       if (flags & CLI_FULL_CONNECTION_USE_KERBEROS) {
-               use_kerberos = true;
-       }
-
-       if (flags & CLI_FULL_CONNECTION_FALLBACK_AFTER_KERBEROS) {
-               fallback_after_kerberos = true;
-       }
-
-       if (flags & CLI_FULL_CONNECTION_USE_CCACHE) {
-               use_ccache = true;
-       }
-
-       if (flags & CLI_FULL_CONNECTION_USE_NT_HASH) {
-               pw_nt_hash = true;
-       }
-
-       creds = cli_session_creds_init(frame,
-                                      user,
-                                      domain,
-                                      NULL, /* realm (use default) */
-                                      password,
-                                      use_kerberos,
-                                      fallback_after_kerberos,
-                                      use_ccache,
-                                      pw_nt_hash);
-       if (creds == NULL) {
-               TALLOC_FREE(frame);
-               return NT_STATUS_NO_MEMORY;
-       }
-
-       status = cli_full_connection_creds(output_cli, my_name,
-                                          dest_host, dest_ss, port,
-                                          service, service_type,
-                                          creds, flags, signing_state);
-       if (!NT_STATUS_IS_OK(status)) {
-               TALLOC_FREE(frame);
-               return status;
-       }
-
-       TALLOC_FREE(frame);
-       return NT_STATUS_OK;
-}
-
 /****************************************************************************
  Send an old style tcon.
 ****************************************************************************/
@@ -3583,6 +3868,13 @@ static struct tevent_req *cli_raw_tcon_send(
                return tevent_req_post(req, ev);
        }
 
+       TALLOC_FREE(cli->smb1.tcon);
+       cli->smb1.tcon = smbXcli_tcon_create(cli);
+       if (tevent_req_nomem(cli->smb1.tcon, req)) {
+               return tevent_req_post(req, ev);
+       }
+       smb1cli_tcon_set_id(cli->smb1.tcon, UINT16_MAX);
+
        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),
@@ -3666,33 +3958,38 @@ fail:
 
 /* Return a cli_state pointing at the IPC$ share for the given server */
 
-struct cli_state *get_ipc_connect(char *server,
-                               struct sockaddr_storage *server_ss,
-                               const struct user_auth_info *user_info)
+static struct cli_state *get_ipc_connect(TALLOC_CTX *mem_ctx,
+                                        char *server,
+                                        struct sockaddr_storage *server_ss,
+                                        struct cli_credentials *creds)
 {
         struct cli_state *cli;
        NTSTATUS nt_status;
        uint32_t flags = CLI_FULL_CONNECTION_ANONYMOUS_FALLBACK;
 
-       if (get_cmdline_auth_info_use_kerberos(user_info)) {
-               flags |= CLI_FULL_CONNECTION_USE_KERBEROS;
-       }
+       flags |= CLI_FULL_CONNECTION_FORCE_SMB1;
+       flags |= CLI_FULL_CONNECTION_IPC;
 
-       nt_status = cli_full_connection(&cli, NULL, server, server_ss, 0, "IPC$", "IPC", 
-                                       get_cmdline_auth_info_username(user_info),
-                                       lp_workgroup(),
-                                       get_cmdline_auth_info_password(user_info),
-                                       flags,
-                                       SMB_SIGNING_DEFAULT);
+       nt_status = cli_full_connection_creds(mem_ctx,
+                                             &cli,
+                                             NULL,
+                                             server,
+                                             server_ss,
+                                             0,
+                                             "IPC$",
+                                             "IPC",
+                                             creds,
+                                             flags);
 
        if (NT_STATUS_IS_OK(nt_status)) {
                return cli;
-       } else if (is_ipaddress(server)) {
+       }
+       if (is_ipaddress(server)) {
            /* windows 9* needs a correct NMB name for connections */
            fstring remote_name;
 
            if (name_status_find("*", 0, 0, server_ss, remote_name)) {
-               cli = get_ipc_connect(remote_name, server_ss, user_info);
+               cli = get_ipc_connect(mem_ctx, remote_name, server_ss, creds);
                if (cli)
                    return cli;
            }
@@ -3714,7 +4011,7 @@ struct cli_state *get_ipc_connect(char *server,
 
 struct cli_state *get_ipc_connect_master_ip(TALLOC_CTX *ctx,
                                struct sockaddr_storage *mb_ip,
-                               const struct user_auth_info *user_info,
+                               struct cli_credentials *creds,
                                char **pp_workgroup_out)
 {
        char addr[INET6_ADDRSTRLEN];
@@ -3757,49 +4054,7 @@ struct cli_state *get_ipc_connect_master_ip(TALLOC_CTX *ctx,
        DEBUG(4, ("found master browser %s, %s\n", name, addr));
 
        print_sockaddr(addr, sizeof(addr), &server_ss);
-       cli = get_ipc_connect(addr, &server_ss, user_info);
+       cli = get_ipc_connect(ctx, addr, &server_ss, creds);
 
        return cli;
 }
-
-/*
- * Return the IP address and workgroup of a master browser on the network, and
- * connect to it.
- */
-
-struct cli_state *get_ipc_connect_master_ip_bcast(TALLOC_CTX *ctx,
-                                       const struct user_auth_info *user_info,
-                                       char **pp_workgroup_out)
-{
-       struct sockaddr_storage *ip_list;
-       struct cli_state *cli;
-       int i, count;
-       NTSTATUS status;
-
-       *pp_workgroup_out = NULL;
-
-        DEBUG(99, ("Do broadcast lookup for workgroups on local network\n"));
-
-        /* Go looking for workgroups by broadcasting on the local network */
-
-       status = name_resolve_bcast(MSBROWSE, 1, talloc_tos(),
-                                   &ip_list, &count);
-        if (!NT_STATUS_IS_OK(status)) {
-                DEBUG(99, ("No master browsers responded: %s\n",
-                          nt_errstr(status)));
-                return NULL;
-        }
-
-       for (i = 0; i < count; i++) {
-               char addr[INET6_ADDRSTRLEN];
-               print_sockaddr(addr, sizeof(addr), &ip_list[i]);
-               DEBUG(99, ("Found master browser %s\n", addr));
-
-               cli = get_ipc_connect_master_ip(ctx, &ip_list[i],
-                               user_info, pp_workgroup_out);
-               if (cli)
-                       return(cli);
-       }
-
-       return NULL;
-}