libsmb: Move get_ipc_connect_master_ip_bcast() to smbtree.c
[samba.git] / source3 / libsmb / cliconnect.c
index 9e9de16dabcaa90c5f0e5b8df1f55c0b69e0b546..b87287fa24d7ff0853d430553fc954fb90b8c715 100644 (file)
@@ -22,6 +22,7 @@
 
 #include "includes.h"
 #include "libsmb/libsmb.h"
+#include "libsmb/namequery.h"
 #include "auth_info.h"
 #include "../libcli/auth/libcli_auth.h"
 #include "../libcli/auth/spnego.h"
 #include "libsmb/nmblib.h"
 #include "librpc/ndr/libndr.h"
 #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"
 
 static char *cli_session_setup_get_account(TALLOC_CTX *mem_ctx,
                                           const char *principal);
 
-static struct cli_credentials *cli_session_creds_init(TALLOC_CTX *mem_ctx,
-                                                     const char *username,
-                                                     const char *domain,
-                                                     const char *realm,
-                                                     const char *password,
-                                                     bool use_kerberos,
-                                                     bool fallback_after_kerberos,
-                                                     bool use_ccache,
-                                                     bool password_is_nt_hash)
+struct cli_credentials *cli_session_creds_init(TALLOC_CTX *mem_ctx,
+                                              const char *username,
+                                              const char *domain,
+                                              const char *realm,
+                                              const char *password,
+                                              bool use_kerberos,
+                                              bool fallback_after_kerberos,
+                                              bool use_ccache,
+                                              bool password_is_nt_hash)
 {
        struct loadparm_context *lp_ctx = NULL;
        struct cli_credentials *creds = NULL;
@@ -72,10 +75,6 @@ static struct cli_credentials *cli_session_creds_init(TALLOC_CTX *mem_ctx,
        }
        cli_credentials_set_conf(creds, lp_ctx);
 
-       if (domain == NULL) {
-               domain = "";
-       }
-
        if (username == NULL) {
                username = "";
        }
@@ -159,11 +158,13 @@ static struct cli_credentials *cli_session_creds_init(TALLOC_CTX *mem_ctx,
                goto fail;
        }
 
-       ok = cli_credentials_set_domain(creds,
-                                       domain,
-                                       CRED_SPECIFIED);
-       if (!ok) {
-               goto fail;
+       if (domain != NULL) {
+               ok = cli_credentials_set_domain(creds,
+                                               domain,
+                                               CRED_SPECIFIED);
+               if (!ok) {
+                       goto fail;
+               }
        }
 
        if (principal != NULL) {
@@ -219,6 +220,184 @@ fail:
        return NULL;
 }
 
+NTSTATUS cli_session_creds_prepare_krb5(struct cli_state *cli,
+                                       struct cli_credentials *creds)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       const char *user_principal = NULL;
+       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;
+       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);
+
+       auth_requested = cli_credentials_authentication_requested(creds);
+       if (auth_requested) {
+               errno = 0;
+               user_principal = cli_credentials_get_principal(creds, frame);
+               if (errno != 0) {
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_NO_MEMORY;
+               }
+       }
+       user_account = cli_credentials_get_username(creds);
+       user_domain = cli_credentials_get_domain(creds);
+       pass = cli_credentials_get_password(creds);
+
+       krb5_state = cli_credentials_get_kerberos_state(creds);
+
+       if (krb5_state != CRED_DONT_USE_KERBEROS) {
+               try_kerberos = true;
+       }
+
+       if (user_principal == NULL) {
+               try_kerberos = false;
+       }
+
+       if (target_hostname == NULL) {
+               try_kerberos = false;
+       } else if (is_ipaddress(target_hostname)) {
+               try_kerberos = false;
+       } else if (strequal(target_hostname, "localhost")) {
+               try_kerberos = false;
+       } else if (strequal(target_hostname, STAR_SMBSERVER)) {
+               try_kerberos = false;
+       } else if (!auth_requested) {
+               try_kerberos = false;
+       }
+
+       if (krb5_state == CRED_MUST_USE_KERBEROS && !try_kerberos) {
+               DEBUG(0, ("Kerberos auth with '%s' (%s\\%s) to access "
+                         "'%s' not possible\n",
+                         user_principal, user_domain, user_account,
+                         target_hostname));
+               TALLOC_FREE(frame);
+               return NT_STATUS_ACCESS_DENIED;
+       }
+
+       if (pass == NULL || strlen(pass) == 0) {
+               need_kinit = false;
+       } else if (krb5_state == CRED_MUST_USE_KERBEROS) {
+               need_kinit = try_kerberos;
+       } else {
+               need_kinit = try_kerberos;
+       }
+
+       if (!need_kinit) {
+               TALLOC_FREE(frame);
+               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_ext(user_principal,
+                                         pass,
+                                         0,
+                                         0,
+                                         0,
+                                         NULL,
+                                         false,
+                                         false,
+                                         0,
+                                         frame,
+                                         &canon_principal,
+                                         &canon_realm,
+                                         NULL);
+       if (ret != 0) {
+               int dbglvl = DBGLVL_NOTICE;
+
+               if (krb5_state == CRED_MUST_USE_KERBEROS) {
+                       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_MUST_USE_KERBEROS) {
+                       TALLOC_FREE(frame);
+                       return krb5_to_nt_status(ret);
+               }
+
+               /*
+                * 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
@@ -447,7 +626,6 @@ static void cli_session_setup_guest_done(struct tevent_req *subreq)
                tevent_req_nterror(req, status);
                return;
        }
-       p += ret;
 
        tevent_req_done(req);
 }
@@ -609,7 +787,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) {
@@ -631,15 +808,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
@@ -688,14 +866,6 @@ static NTSTATUS cli_sesssetup_blob_recv(struct tevent_req *req,
        return status;
 }
 
-/****************************************************************************
- Use in-memory credentials cache
-****************************************************************************/
-
-static void use_in_memory_ccache(void) {
-       setenv(KRB5_ENV_CCNAME, "MEMORY:cliconnect", 1);
-}
-
 /****************************************************************************
  Do a spnego/NTLMSSP encrypted session setup.
 ****************************************************************************/
@@ -925,13 +1095,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;
@@ -945,6 +1118,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 =
@@ -1012,6 +1237,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;
@@ -1089,16 +1319,9 @@ static struct tevent_req *cli_session_setup_spnego_send(
 {
        struct tevent_req *req, *subreq;
        struct cli_session_setup_spnego_state *state;
-       const char *user_principal = NULL;
-       const char *user_account = NULL;
-       const char *user_domain = NULL;
-       const char *pass = NULL;
+       const char *target_service = 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;
+       NTSTATUS status;
 
        req = tevent_req_create(mem_ctx, &state,
                                struct cli_session_setup_spnego_state);
@@ -1106,123 +1329,20 @@ static struct tevent_req *cli_session_setup_spnego_send(
                return NULL;
        }
 
+       target_service = "cifs";
        target_hostname = smbXcli_conn_remote_name(cli->conn);
-       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 *principal = NULL;
-               char *OIDs[ASN1_MAX_OIDS];
-               int i;
-
-               /* 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.  */
-               if (!spnego_parse_negTokenInit(state, *server_blob, OIDs,
-                                              &principal, NULL) ||
-                               OIDs[0] == NULL) {
-                       state->result = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
-                       tevent_req_done(req);
-                       return tevent_req_post(req, ev);
-               }
-               TALLOC_FREE(principal);
-
-               /* 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;
-                       }
-                       talloc_free(OIDs[i]);
-               }
-       }
-
-       auth_requested = cli_credentials_authentication_requested(creds);
-       if (auth_requested) {
-               user_principal = cli_credentials_get_principal(creds, state);
-               if (tevent_req_nomem(user_principal, req)) {
-                       return tevent_req_post(req, ev);
-               }
-       }
-       user_account = cli_credentials_get_username(creds);
-       user_domain = cli_credentials_get_domain(creds);
-       pass = cli_credentials_get_password(creds);
-
-       krb5_state = cli_credentials_get_kerberos_state(creds);
-
-       if (krb5_state != CRED_DONT_USE_KERBEROS) {
-               try_kerberos = true;
-       }
-
-       if (target_hostname == NULL) {
-               try_kerberos = false;
-       } else if (is_ipaddress(target_hostname)) {
-               try_kerberos = false;
-       } else if (strequal(target_hostname, "localhost")) {
-               try_kerberos = false;
-       } else if (strequal(target_hostname, STAR_SMBSERVER)) {
-               try_kerberos = false;
-       } else if (!auth_requested) {
-               try_kerberos = false;
-       }
 
-       if (krb5_state == CRED_MUST_USE_KERBEROS && !try_kerberos) {
-               DEBUG(0, ("Kerberos auth with '%s' (%s\\%s) to access "
-                         "'%s' not possible\n",
-                         user_principal, user_domain, user_account,
-                         target_hostname));
-               tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
+       status = cli_session_creds_prepare_krb5(cli, creds);
+       if (tevent_req_nterror(req, status)) {
                return tevent_req_post(req, ev);
        }
 
-       if (pass == NULL || strlen(pass) == 0) {
-               need_kinit = false;
-       } else if (krb5_state == CRED_MUST_USE_KERBEROS) {
-               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;
-       }
-
-       if (need_kinit) {
-               int ret;
-
-               use_in_memory_ccache();
-               ret = kerberos_kinit_password(user_principal, pass,
-                                       0 /* no time correction for now */,
-                                       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) {
-                               state->result = ADS_ERROR_KRB5(ret);
-                               tevent_req_done(req);
-                               return tevent_req_post(req, ev);
-                       }
-
-                       /*
-                        * Ignore the error and hope that NTLM will work
-                        */
-                       ret = 0;
-               }
-       }
+       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,
-                                              "cifs", target_hostname);
+                                              target_service, target_hostname);
        if (tevent_req_nomem(subreq, req)) {
                return tevent_req_post(req, ev);
        }
@@ -1284,7 +1404,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);
@@ -1424,6 +1544,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;
@@ -1502,9 +1624,8 @@ struct tevent_req *cli_session_setup_creds_send(TALLOC_CTX *mem_ctx,
                }
        } else {
                if (!lp_client_lanman_auth()) {
-                       DEBUG(1, ("Server requested LM password but "
-                                 "'client lanman auth = no' "
-                                 "or 'client ntlmv2 auth = yes' is set\n"));
+                       DEBUG(1, ("Server requested user level LM password but "
+                                 "'client lanman auth = no' is set.\n"));
                        tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
                        return tevent_req_post(req, ev);
                }
@@ -1637,14 +1758,12 @@ static void cli_session_setup_creds_done_nt1(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);
-       }
-       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,
@@ -1677,7 +1796,6 @@ 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,
@@ -1690,11 +1808,12 @@ static void cli_session_setup_creds_done_lm21(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);
+       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);
@@ -1734,7 +1853,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);
@@ -1751,43 +1870,6 @@ NTSTATUS cli_session_setup_anon(struct cli_state *cli)
        return NT_STATUS_OK;
 }
 
-NTSTATUS cli_session_setup(struct cli_state *cli,
-                          const char *user,
-                          const char *pass,
-                          const char *workgroup)
-{
-       NTSTATUS status = NT_STATUS_NO_MEMORY;
-       const char *dest_realm = NULL;
-       struct cli_credentials *creds = NULL;
-
-       /*
-        * dest_realm is only valid in the winbindd use case,
-        * where we also have the account in that realm.
-        */
-       dest_realm = cli_state_remote_realm(cli);
-
-       creds = cli_session_creds_init(cli,
-                                      user,
-                                      workgroup,
-                                      dest_realm,
-                                      pass,
-                                      cli->use_kerberos,
-                                      cli->fallback_after_kerberos,
-                                      cli->use_ccache,
-                                      cli->pw_nt_hash);
-       if (creds == NULL) {
-               return NT_STATUS_NO_MEMORY;
-       }
-
-       status = cli_session_setup_creds(cli, creds);
-       TALLOC_FREE(creds);
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
-       }
-
-       return NT_STATUS_OK;
-}
-
 /****************************************************************************
  Send a uloggoff.
 *****************************************************************************/
@@ -1922,6 +2004,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;
@@ -2205,10 +2294,16 @@ 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)
+       const char *share, const char *dev, const char *pass)
 {
        struct tevent_req *req, *subreq;
        struct cli_tree_connect_state *state;
+       int passlen;
+
+       if (pass == NULL) {
+               pass = "";
+       }
+       passlen = strlen(pass) + 1;
 
        req = tevent_req_create(mem_ctx, &state,
                                struct cli_tree_connect_state);
@@ -2225,6 +2320,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);
@@ -2313,7 +2409,7 @@ static NTSTATUS cli_tree_connect_recv(struct tevent_req *req)
 }
 
 NTSTATUS cli_tree_connect(struct cli_state *cli, const char *share,
-                         const char *dev, const char *pass, int passlen)
+                         const char *dev, const char *pass)
 {
        struct tevent_context *ev;
        struct tevent_req *req;
@@ -2326,7 +2422,7 @@ NTSTATUS cli_tree_connect(struct cli_state *cli, const char *share,
        if (ev == NULL) {
                goto fail;
        }
-       req = cli_tree_connect_send(ev, ev, cli, share, dev, pass, passlen);
+       req = cli_tree_connect_send(ev, ev, cli, share, dev, pass);
        if (req == NULL) {
                goto fail;
        }
@@ -2339,7 +2435,20 @@ fail:
        return status;
 }
 
-/****************************************************************************
+NTSTATUS cli_tree_connect_creds(struct cli_state *cli,
+                               const char *share, const char *dev,
+                               struct cli_credentials *creds)
+{
+       const char *pw = NULL;
+
+       if (creds != NULL) {
+               pw = cli_credentials_get_password(creds);
+       }
+
+       return cli_tree_connect(cli, share, dev, pw);
+}
+
+/****************************************************************************
  Send a tree disconnect.
 ****************************************************************************/
 
@@ -2384,7 +2493,7 @@ static void cli_tdis_done(struct tevent_req *subreq)
                tevent_req_nterror(req, status);
                return;
        }
-       cli_state_set_tid(state->cli, UINT16_MAX);
+       TALLOC_FREE(state->cli->smb1.tcon);
        tevent_req_done(req);
 }
 
@@ -2400,10 +2509,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)) {
@@ -2448,7 +2561,6 @@ 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;
        NTSTATUS status;
@@ -2459,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)) {
 
                /*
@@ -2629,7 +2728,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);
@@ -2723,6 +2822,15 @@ 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);
+       }
+
+       if (flags & CLI_FULL_CONNECTION_DISABLE_SMB1) {
+               state->min_protocol = MAX(state->max_protocol, PROTOCOL_SMB2_02);
+               state->max_protocol = MAX(state->max_protocol, PROTOCOL_LATEST);
+       }
+
        subreq = cli_connect_nb_send(state, ev, dest_host, dest_ss, port,
                                     0x20, my_name, signing_state, flags);
        if (tevent_req_nomem(subreq, req)) {
@@ -2749,7 +2857,8 @@ static void cli_start_connection_connected(struct tevent_req *subreq)
        subreq = smbXcli_negprot_send(state, state->ev, state->cli->conn,
                                      state->cli->timeout,
                                      state->min_protocol,
-                                     state->max_protocol);
+                                     state->max_protocol,
+                                     WINDOWS_CLIENT_PURE_SMB2_NEGPROT_INITIAL_CREDIT_ASK);
        if (tevent_req_nomem(subreq, req)) {
                return;
        }
@@ -2822,6 +2931,420 @@ fail:
        return status;
 }
 
+struct cli_smb1_setup_encryption_blob_state {
+       uint16_t setup[1];
+       uint8_t param[4];
+       NTSTATUS status;
+       DATA_BLOB out;
+       uint16_t enc_ctx_id;
+};
+
+static void cli_smb1_setup_encryption_blob_done(struct tevent_req *subreq);
+
+static struct tevent_req *cli_smb1_setup_encryption_blob_send(TALLOC_CTX *mem_ctx,
+                                                       struct tevent_context *ev,
+                                                       struct cli_state *cli,
+                                                       const DATA_BLOB in)
+{
+       struct tevent_req *req = NULL;
+       struct cli_smb1_setup_encryption_blob_state *state = NULL;
+       struct tevent_req *subreq = NULL;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct cli_smb1_setup_encryption_blob_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       if (in.length > CLI_BUFFER_SIZE) {
+               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+               return tevent_req_post(req, ev);
+       }
+
+       SSVAL(state->setup+0,  0, TRANSACT2_SETFSINFO);
+       SSVAL(state->param, 0, 0);
+       SSVAL(state->param, 2, SMB_REQUEST_TRANSPORT_ENCRYPTION);
+
+       subreq = smb1cli_trans_send(state, ev, cli->conn,
+                                   SMBtrans2,
+                                   0, 0, /* _flags */
+                                   0, 0, /* _flags2 */
+                                   cli->timeout,
+                                   cli->smb1.pid,
+                                   cli->smb1.tcon,
+                                   cli->smb1.session,
+                                   NULL, /* pipe_name */
+                                   0, /* fid */
+                                   0, /* function */
+                                   0, /* flags */
+                                   state->setup, 1, 0,
+                                   state->param, 4, 2,
+                                   in.data, in.length, CLI_BUFFER_SIZE);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq,
+                               cli_smb1_setup_encryption_blob_done,
+                               req);
+
+       return req;
+}
+
+static void cli_smb1_setup_encryption_blob_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req =
+               tevent_req_callback_data(subreq,
+                               struct tevent_req);
+       struct cli_smb1_setup_encryption_blob_state *state =
+               tevent_req_data(req,
+               struct cli_smb1_setup_encryption_blob_state);
+       uint8_t *rparam=NULL, *rdata=NULL;
+       uint32_t num_rparam, num_rdata;
+       NTSTATUS status;
+
+       status = smb1cli_trans_recv(subreq, state,
+                                   NULL, /* recv_flags */
+                                   NULL, 0, NULL, /* rsetup */
+                                   &rparam, 0, &num_rparam,
+                                   &rdata, 0, &num_rdata);
+       TALLOC_FREE(subreq);
+       state->status = status;
+       if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+               status = NT_STATUS_OK;
+       }
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+
+       if (num_rparam == 2) {
+               state->enc_ctx_id = SVAL(rparam, 0);
+       }
+       TALLOC_FREE(rparam);
+
+       state->out = data_blob_const(rdata, num_rdata);
+
+       tevent_req_done(req);
+}
+
+static NTSTATUS cli_smb1_setup_encryption_blob_recv(struct tevent_req *req,
+                                                   TALLOC_CTX *mem_ctx,
+                                                   DATA_BLOB *out,
+                                                   uint16_t *enc_ctx_id)
+{
+       struct cli_smb1_setup_encryption_blob_state *state =
+               tevent_req_data(req,
+               struct cli_smb1_setup_encryption_blob_state);
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               tevent_req_received(req);
+               return status;
+       }
+
+       status = state->status;
+
+       *out = state->out;
+       talloc_steal(mem_ctx, out->data);
+
+       *enc_ctx_id = state->enc_ctx_id;
+
+       tevent_req_received(req);
+       return status;
+}
+
+struct cli_smb1_setup_encryption_state {
+       struct tevent_context *ev;
+       struct cli_state *cli;
+       struct smb_trans_enc_state *es;
+       DATA_BLOB blob_in;
+       DATA_BLOB blob_out;
+       bool local_ready;
+       bool remote_ready;
+};
+
+static void cli_smb1_setup_encryption_local_next(struct tevent_req *req);
+static void cli_smb1_setup_encryption_local_done(struct tevent_req *subreq);
+static void cli_smb1_setup_encryption_remote_next(struct tevent_req *req);
+static void cli_smb1_setup_encryption_remote_done(struct tevent_req *subreq);
+static void cli_smb1_setup_encryption_ready(struct tevent_req *req);
+
+static struct tevent_req *cli_smb1_setup_encryption_send(TALLOC_CTX *mem_ctx,
+                                               struct tevent_context *ev,
+                                               struct cli_state *cli,
+                                               struct cli_credentials *creds)
+{
+       struct tevent_req *req = NULL;
+       struct cli_smb1_setup_encryption_state *state = NULL;
+       struct auth_generic_state *ags = NULL;
+       const DATA_BLOB *b = NULL;
+       bool auth_requested = false;
+       const char *target_service = NULL;
+       const char *target_hostname = NULL;
+       NTSTATUS status;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct cli_smb1_setup_encryption_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       state->ev = ev;
+       state->cli = cli;
+
+       auth_requested = cli_credentials_authentication_requested(creds);
+       if (!auth_requested) {
+               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+               return tevent_req_post(req, ev);
+       }
+
+       target_service = "cifs";
+       target_hostname = smbXcli_conn_remote_name(cli->conn);
+
+       status = cli_session_creds_prepare_krb5(cli, creds);
+       if (tevent_req_nterror(req, status)) {
+               return tevent_req_post(req, ev);
+       }
+
+       state->es = talloc_zero(state, struct smb_trans_enc_state);
+       if (tevent_req_nomem(state->es, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       status = auth_generic_client_prepare(state->es, &ags);
+       if (tevent_req_nterror(req, status)) {
+               return tevent_req_post(req, ev);
+       }
+
+       gensec_want_feature(ags->gensec_security,
+                           GENSEC_FEATURE_SIGN);
+       gensec_want_feature(ags->gensec_security,
+                           GENSEC_FEATURE_SEAL);
+
+       status = auth_generic_set_creds(ags, creds);
+       if (tevent_req_nterror(req, status)) {
+               return tevent_req_post(req, ev);
+       }
+
+       if (target_service != NULL) {
+               status = gensec_set_target_service(ags->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(ags->gensec_security,
+                                                   target_hostname);
+               if (tevent_req_nterror(req, status)) {
+                       return tevent_req_post(req, ev);
+               }
+       }
+
+       gensec_set_max_update_size(ags->gensec_security,
+                                  CLI_BUFFER_SIZE);
+
+       b = smbXcli_conn_server_gss_blob(state->cli->conn);
+       if (b != NULL) {
+               state->blob_in = *b;
+       }
+
+       status = auth_generic_client_start(ags, GENSEC_OID_SPNEGO);
+       if (tevent_req_nterror(req, status)) {
+               return tevent_req_post(req, ev);
+       }
+
+       /*
+        * We only need the gensec_security part from here.
+        */
+       state->es->gensec_security = talloc_move(state->es,
+                                                &ags->gensec_security);
+       TALLOC_FREE(ags);
+
+       cli_smb1_setup_encryption_local_next(req);
+       if (!tevent_req_is_in_progress(req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       return req;
+}
+
+static void cli_smb1_setup_encryption_local_next(struct tevent_req *req)
+{
+       struct cli_smb1_setup_encryption_state *state =
+               tevent_req_data(req,
+               struct cli_smb1_setup_encryption_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->es->gensec_security,
+                       state->blob_in);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq, cli_smb1_setup_encryption_local_done, req);
+}
+
+static void cli_smb1_setup_encryption_local_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req =
+               tevent_req_callback_data(subreq,
+               struct tevent_req);
+       struct cli_smb1_setup_encryption_state *state =
+               tevent_req_data(req,
+               struct cli_smb1_setup_encryption_state);
+       NTSTATUS status;
+
+       status = gensec_update_recv(subreq, state, &state->blob_out);
+       TALLOC_FREE(subreq);
+       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;
+       }
+
+       if (NT_STATUS_IS_OK(status)) {
+               state->local_ready = true;
+       }
+
+       /*
+        * We always get NT_STATUS_OK from the server even if it is not ready.
+        * So guess the server is ready when we are ready and already sent
+        * our last blob to the server.
+        */
+       if (state->local_ready && state->blob_out.length == 0) {
+               state->remote_ready = true;
+       }
+
+       if (state->local_ready && state->remote_ready) {
+               cli_smb1_setup_encryption_ready(req);
+               return;
+       }
+
+       cli_smb1_setup_encryption_remote_next(req);
+}
+
+static void cli_smb1_setup_encryption_remote_next(struct tevent_req *req)
+{
+       struct cli_smb1_setup_encryption_state *state =
+               tevent_req_data(req,
+               struct cli_smb1_setup_encryption_state);
+       struct tevent_req *subreq = NULL;
+
+       if (state->remote_ready) {
+               tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+               return;
+       }
+
+       subreq = cli_smb1_setup_encryption_blob_send(state, state->ev,
+                                                    state->cli, state->blob_out);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq,
+                               cli_smb1_setup_encryption_remote_done,
+                               req);
+}
+
+static void cli_smb1_setup_encryption_remote_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req =
+               tevent_req_callback_data(subreq,
+               struct tevent_req);
+       struct cli_smb1_setup_encryption_state *state =
+               tevent_req_data(req,
+               struct cli_smb1_setup_encryption_state);
+       NTSTATUS status;
+
+       status = cli_smb1_setup_encryption_blob_recv(subreq, state,
+                                                    &state->blob_in,
+                                                    &state->es->enc_ctx_num);
+       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;
+       }
+
+       /*
+        * We always get NT_STATUS_OK even if the server is not ready.
+        * So guess the server is ready when we are ready and sent
+        * our last blob to the server.
+        */
+       if (state->local_ready) {
+               state->remote_ready = true;
+       }
+
+       if (state->local_ready && state->remote_ready) {
+               cli_smb1_setup_encryption_ready(req);
+               return;
+       }
+
+       cli_smb1_setup_encryption_local_next(req);
+}
+
+static void cli_smb1_setup_encryption_ready(struct tevent_req *req)
+{
+       struct cli_smb1_setup_encryption_state *state =
+               tevent_req_data(req,
+               struct cli_smb1_setup_encryption_state);
+       struct smb_trans_enc_state *es = NULL;
+
+       if (state->blob_in.length != 0) {
+               tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+               return;
+       }
+
+       if (state->blob_out.length != 0) {
+               tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+               return;
+       }
+
+       es = talloc_move(state->cli->conn, &state->es);
+       es->enc_on = true;
+       smb1cli_conn_set_encryption(state->cli->conn, es);
+       es = NULL;
+
+       tevent_req_done(req);
+}
+
+static NTSTATUS cli_smb1_setup_encryption_recv(struct tevent_req *req)
+{
+       return tevent_req_simple_recv_ntstatus(req);
+}
+
+NTSTATUS cli_smb1_setup_encryption(struct cli_state *cli,
+                                  struct cli_credentials *creds)
+{
+       struct tevent_context *ev = NULL;
+       struct tevent_req *req = NULL;
+       NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+       ev = samba_tevent_context_init(talloc_tos());
+       if (ev == NULL) {
+               goto fail;
+       }
+       req = cli_smb1_setup_encryption_send(ev, ev, cli, creds);
+       if (req == NULL) {
+               goto fail;
+       }
+       if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+               goto fail;
+       }
+       status = cli_smb1_setup_encryption_recv(req);
+ 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
@@ -2830,88 +3353,103 @@ fail:
    @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 user Username, unix string
-   @param domain User's domain
-   @param password User's password, unencrypted unix string.
+   @param creds The used user credentials
 */
 
-struct cli_full_connection_state {
+struct cli_full_connection_creds_state {
        struct tevent_context *ev;
        const char *service;
        const char *service_type;
-       const char *user;
-       const char *domain;
-       const char *password;
-       int pw_len;
+       struct cli_credentials *creds;
        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);
+static int cli_full_connection_creds_state_destructor(
+       struct cli_full_connection_creds_state *s)
+{
+       if (s->cli != NULL) {
+               cli_shutdown(s->cli);
+               s->cli = NULL;
+       }
+       return 0;
+}
 
-struct tevent_req *cli_full_connection_send(
+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_tcon_start(struct tevent_req *req);
+static void cli_full_connection_creds_tcon_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_full_connection_creds_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)
+       struct cli_credentials *creds,
+       int flags, int signing_state)
 {
        struct tevent_req *req, *subreq;
-       struct cli_full_connection_state *state;
+       struct cli_full_connection_creds_state *state;
+       enum credentials_use_kerberos krb5_state;
+       uint32_t gensec_features = 0;
 
        req = tevent_req_create(mem_ctx, &state,
-                               struct cli_full_connection_state);
+                               struct cli_full_connection_creds_state);
        if (req == NULL) {
                return NULL;
        }
-       talloc_set_destructor(state, cli_full_connection_state_destructor);
+       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->user = user;
-       state->domain = domain;
-       state->password = password;
+       state->creds = creds;
        state->flags = flags;
 
-       state->pw_len = state->password ? strlen(state->password)+1 : 0;
-       if (state->password == NULL) {
-               state->password = "";
-       }
-
        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);
+       tevent_req_set_callback(subreq,
+                               cli_full_connection_creds_conn_done,
+                               req);
        return req;
 }
 
-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;
-}
-
-static void cli_full_connection_started(struct tevent_req *subreq)
+static void cli_full_connection_creds_conn_done(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);
+       struct cli_full_connection_creds_state *state = tevent_req_data(
+               req, struct cli_full_connection_creds_state);
        NTSTATUS status;
-       struct cli_credentials *creds = NULL;
 
        status = cli_start_connection_recv(subreq, &state->cli);
        TALLOC_FREE(subreq);
@@ -2919,33 +3457,31 @@ static void cli_full_connection_started(struct tevent_req *subreq)
                return;
        }
 
-       creds = cli_session_creds_init(state,
-                                      state->user,
-                                      state->domain,
-                                      NULL, /* realm (use default) */
-                                      state->password,
-                                      state->cli->use_kerberos,
-                                      state->cli->fallback_after_kerberos,
-                                      state->cli->use_ccache,
-                                      state->cli->pw_nt_hash);
-       if (tevent_req_nomem(creds, req)) {
-               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, creds);
+               state, state->ev, state->cli, state->creds);
        if (tevent_req_nomem(subreq, req)) {
                return;
        }
-       tevent_req_set_callback(subreq, cli_full_connection_sess_set_up, req);
+       tevent_req_set_callback(subreq,
+                               cli_full_connection_creds_sess_done,
+                               req);
 }
 
-static void cli_full_connection_sess_set_up(struct tevent_req *subreq)
+static void cli_full_connection_creds_sess_done(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);
+       struct cli_full_connection_creds_state *state = tevent_req_data(
+               req, struct cli_full_connection_creds_state);
        NTSTATUS status;
 
        status = cli_session_setup_creds_recv(subreq);
@@ -2953,22 +3489,15 @@ static void cli_full_connection_sess_set_up(struct tevent_req *subreq)
 
        if (!NT_STATUS_IS_OK(status) &&
            (state->flags & CLI_FULL_CONNECTION_ANONYMOUS_FALLBACK)) {
-               struct cli_credentials *creds = NULL;
 
                state->flags &= ~CLI_FULL_CONNECTION_ANONYMOUS_FALLBACK;
 
-               creds = cli_credentials_init_anon(state);
-               if (tevent_req_nomem(creds, req)) {
+               state->creds = cli_credentials_init_anon(state);
+               if (tevent_req_nomem(state->creds, req)) {
                        return;
                }
 
-               subreq = cli_session_setup_creds_send(
-                       state, state->ev, state->cli, creds);
-               if (tevent_req_nomem(subreq, req)) {
-                       return;
-               }
-               tevent_req_set_callback(
-                       subreq, cli_full_connection_sess_set_up, req);
+               cli_full_connection_creds_sess_start(req);
                return;
        }
 
@@ -2976,22 +3505,37 @@ static void cli_full_connection_sess_set_up(struct tevent_req *subreq)
                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);
+       cli_full_connection_creds_tcon_start(req);
+}
+
+static void cli_full_connection_creds_tcon_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;
+       const char *password = NULL;
+
+       if (state->service == NULL) {
+               tevent_req_done(req);
                return;
        }
 
-       tevent_req_done(req);
+       password = cli_credentials_get_password(state->creds);
+
+       subreq = cli_tree_connect_send(state, state->ev,
+                                      state->cli,
+                                      state->service,
+                                      state->service_type,
+                                      password);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq,
+                               cli_full_connection_creds_tcon_done,
+                               req);
 }
 
-static void cli_full_connection_done(struct tevent_req *subreq)
+static void cli_full_connection_creds_tcon_done(struct tevent_req *subreq)
 {
        struct tevent_req *req = tevent_req_callback_data(
                subreq, struct tevent_req);
@@ -3006,11 +3550,11 @@ static void cli_full_connection_done(struct tevent_req *subreq)
        tevent_req_done(req);
 }
 
-NTSTATUS cli_full_connection_recv(struct tevent_req *req,
+NTSTATUS cli_full_connection_creds_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);
+       struct cli_full_connection_creds_state *state = tevent_req_data(
+               req, struct cli_full_connection_creds_state);
        NTSTATUS status;
 
        if (tevent_req_is_nterror(req, &status)) {
@@ -3021,14 +3565,14 @@ NTSTATUS cli_full_connection_recv(struct tevent_req *req,
        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)
+NTSTATUS cli_full_connection_creds(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)
 {
        struct tevent_context *ev;
        struct tevent_req *req;
@@ -3038,21 +3582,81 @@ NTSTATUS cli_full_connection(struct cli_state **output_cli,
        if (ev == NULL) {
                goto fail;
        }
-       req = cli_full_connection_send(
+       req = cli_full_connection_creds_send(
                ev, ev, my_name, dest_host, dest_ss, port, service,
-               service_type, user, domain, password, flags, signing_state);
+               service_type, creds, 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);
+       status = cli_full_connection_creds_recv(req, 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.
 ****************************************************************************/
@@ -3082,6 +3686,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),
@@ -3177,6 +3788,8 @@ struct cli_state *get_ipc_connect(char *server,
                flags |= CLI_FULL_CONNECTION_USE_KERBEROS;
        }
 
+       flags |= CLI_FULL_CONNECTION_FORCE_SMB1;
+
        nt_status = cli_full_connection(&cli, NULL, server, server_ss, 0, "IPC$", "IPC", 
                                        get_cmdline_auth_info_username(user_info),
                                        lp_workgroup(),
@@ -3260,45 +3873,3 @@ struct cli_state *get_ipc_connect_master_ip(TALLOC_CTX *ctx,
 
        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;
-}