libsmb: Move get_ipc_connect_master_ip_bcast() to smbtree.c
[samba.git] / source3 / libsmb / cliconnect.c
index 2c351dda9c51238c006d1f45b64a5b3b63d5d79e..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"
 
-/********************************************************
- Utility function to ensure we always return at least
- a valid char * pointer to an empty string for the
- cli->server_os, cli->server_type and cli->server_domain
- strings.
-*******************************************************/
+static char *cli_session_setup_get_account(TALLOC_CTX *mem_ctx,
+                                          const char *principal);
+
+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;
+       const char *principal = NULL;
+       char *tmp = NULL;
+       char *p = NULL;
+       bool ok;
 
-static NTSTATUS smb_bytes_talloc_string(TALLOC_CTX *mem_ctx,
-                                       const uint8_t *hdr,
-                                       char **dest,
-                                       uint8_t *src,
-                                       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);
-       if (*destlen == -1) {
-               return NT_STATUS_NO_MEMORY;
+       creds = cli_credentials_init(mem_ctx);
+       if (creds == NULL) {
+               return NULL;
        }
 
-       if (*dest == NULL) {
-               *dest = talloc_strdup(mem_ctx, "");
-               if (*dest == NULL) {
-                       return NT_STATUS_NO_MEMORY;
+       lp_ctx = loadparm_init_s3(creds, loadparm_s3_helpers());
+       if (lp_ctx == NULL) {
+               goto fail;
+       }
+       cli_credentials_set_conf(creds, lp_ctx);
+
+       if (username == NULL) {
+               username = "";
+       }
+
+       if (strlen(username) == 0) {
+               if (password != NULL && strlen(password) == 0) {
+                       /*
+                        * some callers pass "" as no password
+                        *
+                        * gensec only handles NULL as no password.
+                        */
+                       password = NULL;
+               }
+               if (password == NULL) {
+                       cli_credentials_set_anonymous(creds);
+                       return creds;
                }
        }
-       return NT_STATUS_OK;
-}
 
-/****************************************************************************
- Do an old lanman2 style session setup.
-****************************************************************************/
+       tmp = talloc_strdup(creds, username);
+       if (tmp == NULL) {
+               goto fail;
+       }
+       username = tmp;
 
-struct cli_session_setup_lanman2_state {
-       struct cli_state *cli;
-       uint16_t vwv[10];
-       const char *user;
-};
+       /* allow for workgroups as part of the username */
+       if ((p = strchr_m(tmp, '\\')) ||
+           (p = strchr_m(tmp, '/')) ||
+           (p = strchr_m(tmp, *lp_winbind_separator()))) {
+               *p = 0;
+               username = p + 1;
+               domain = tmp;
+       }
 
-static void cli_session_setup_lanman2_done(struct tevent_req *subreq);
+       principal = username;
+       username = cli_session_setup_get_account(creds, principal);
+       if (username == NULL) {
+               goto fail;
+       }
+       ok = strequal(username, principal);
+       if (ok) {
+               /*
+                * Ok still the same, so it's not a principal
+                */
+               principal = NULL;
+       }
 
-static struct tevent_req *cli_session_setup_lanman2_send(
-       TALLOC_CTX *mem_ctx, struct tevent_context *ev,
-       struct cli_state *cli, const char *user,
-       const char *pass, size_t passlen,
-       const char *workgroup)
-{
-       struct tevent_req *req, *subreq;
-       struct cli_session_setup_lanman2_state *state;
-       DATA_BLOB lm_response = data_blob_null;
-       uint16_t *vwv;
-       uint8_t *bytes;
-       char *tmp;
-       uint16_t sec_mode = smb1cli_conn_server_security_mode(cli->conn);
+       if (use_kerberos && fallback_after_kerberos) {
+               cli_credentials_set_kerberos_state(creds,
+                                                  CRED_AUTO_USE_KERBEROS);
+       } else if (use_kerberos) {
+               cli_credentials_set_kerberos_state(creds,
+                                                  CRED_MUST_USE_KERBEROS);
+       } else {
+               cli_credentials_set_kerberos_state(creds,
+                                                  CRED_DONT_USE_KERBEROS);
+       }
 
-       req = tevent_req_create(mem_ctx, &state,
-                               struct cli_session_setup_lanman2_state);
-       if (req == NULL) {
-               return NULL;
+       if (use_ccache) {
+               uint32_t features;
+
+               features = cli_credentials_get_gensec_features(creds);
+               features |= GENSEC_FEATURE_NTLM_CCACHE;
+               cli_credentials_set_gensec_features(creds, features);
+
+               if (password != NULL && strlen(password) == 0) {
+                       /*
+                        * some callers pass "" as no password
+                        *
+                        * GENSEC_FEATURE_NTLM_CCACHE only handles
+                        * NULL as no password.
+                        */
+                       password = NULL;
+               }
        }
-       state->cli = cli;
-       state->user = user;
-       vwv = state->vwv;
 
-       /*
-        * if in share level security then don't send a password now
-        */
-       if (!(sec_mode & NEGOTIATE_SECURITY_USER_LEVEL)) {
-               passlen = 0;
+       ok = cli_credentials_set_username(creds,
+                                         username,
+                                         CRED_SPECIFIED);
+       if (!ok) {
+               goto fail;
        }
 
-       if (passlen > 0
-           && (sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE)
-           && passlen != 24) {
-               /*
-                * Encrypted mode needed, and non encrypted password
-                * supplied.
-                */
-               lm_response = data_blob(NULL, 24);
-               if (tevent_req_nomem(lm_response.data, req)) {
-                       return tevent_req_post(req, ev);
+       if (domain != NULL) {
+               ok = cli_credentials_set_domain(creds,
+                                               domain,
+                                               CRED_SPECIFIED);
+               if (!ok) {
+                       goto fail;
                }
+       }
 
-               if (!SMBencrypt(pass, smb1cli_conn_server_challenge(cli->conn),
-                               (uint8_t *)lm_response.data)) {
-                       DEBUG(1, ("Password is > 14 chars in length, and is "
-                                 "therefore incompatible with Lanman "
-                                 "authentication\n"));
-                       tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
-                       return tevent_req_post(req, ev);
+       if (principal != NULL) {
+               ok = cli_credentials_set_principal(creds,
+                                                  principal,
+                                                  CRED_SPECIFIED);
+               if (!ok) {
+                       goto fail;
                }
-       } else if ((sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE)
-                  && passlen == 24) {
-               /*
-                * Encrypted mode needed, and encrypted password
-                * supplied.
-                */
-               lm_response = data_blob(pass, passlen);
-               if (tevent_req_nomem(lm_response.data, req)) {
-                       return tevent_req_post(req, ev);
+       }
+
+       if (realm != NULL) {
+               ok = cli_credentials_set_realm(creds,
+                                              realm,
+                                              CRED_SPECIFIED);
+               if (!ok) {
+                       goto fail;
                }
-       } else if (passlen > 0) {
-               uint8_t *buf;
-               size_t converted_size;
-               /*
-                * Plaintext mode needed, assume plaintext supplied.
-                */
-               buf = talloc_array(talloc_tos(), uint8_t, 0);
-               buf = smb_bytes_push_str(buf, smbXcli_conn_use_unicode(cli->conn), pass, passlen+1,
-                                        &converted_size);
-               if (tevent_req_nomem(buf, req)) {
-                       return tevent_req_post(req, ev);
+       }
+
+       if (password != NULL && strlen(password) > 0) {
+               if (password_is_nt_hash) {
+                       struct samr_Password nt_hash;
+                       size_t converted;
+
+                       converted = strhex_to_str((char *)nt_hash.hash,
+                                                 sizeof(nt_hash.hash),
+                                                 password,
+                                                 strlen(password));
+                       if (converted != sizeof(nt_hash.hash)) {
+                               goto fail;
+                       }
+
+                       ok = cli_credentials_set_nt_hash(creds,
+                                                        &nt_hash,
+                                                        CRED_SPECIFIED);
+                       if (!ok) {
+                               goto fail;
+                       }
+               } else {
+                       ok = cli_credentials_set_password(creds,
+                                                         password,
+                                                         CRED_SPECIFIED);
+                       if (!ok) {
+                               goto fail;
+                       }
                }
-               lm_response = data_blob(pass, passlen);
-               TALLOC_FREE(buf);
-               if (tevent_req_nomem(lm_response.data, req)) {
-                       return tevent_req_post(req, ev);
+       }
+
+       return creds;
+fail:
+       TALLOC_FREE(creds);
+       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);
 
-       SCVAL(vwv+0, 0, 0xff);
-       SCVAL(vwv+0, 1, 0);
-       SSVAL(vwv+1, 0, 0);
-       SSVAL(vwv+2, 0, CLI_BUFFER_SIZE);
-       SSVAL(vwv+3, 0, 2);
-       SSVAL(vwv+4, 0, 1);
-       SIVAL(vwv+5, 0, smb1cli_conn_server_session_key(cli->conn));
-       SSVAL(vwv+7, 0, lm_response.length);
+       krb5_state = cli_credentials_get_kerberos_state(creds);
 
-       bytes = talloc_array(state, uint8_t, lm_response.length);
-       if (tevent_req_nomem(bytes, req)) {
-               return tevent_req_post(req, ev);
+       if (krb5_state != CRED_DONT_USE_KERBEROS) {
+               try_kerberos = true;
        }
-       if (lm_response.length != 0) {
-               memcpy(bytes, lm_response.data, lm_response.length);
+
+       if (user_principal == NULL) {
+               try_kerberos = false;
        }
-       data_blob_free(&lm_response);
 
-       tmp = talloc_strdup_upper(talloc_tos(), user);
-       if (tevent_req_nomem(tmp, req)) {
-               return tevent_req_post(req, ev);
+       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;
        }
-       bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), tmp, strlen(tmp)+1,
-                                  NULL);
-       TALLOC_FREE(tmp);
 
-       tmp = talloc_strdup_upper(talloc_tos(), workgroup);
-       if (tevent_req_nomem(tmp, req)) {
-               return tevent_req_post(req, ev);
+       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;
        }
-       bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), tmp, strlen(tmp)+1,
-                                  NULL);
-       bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), "Unix", 5, NULL);
-       bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), "Samba", 6, NULL);
 
-       if (tevent_req_nomem(bytes, req)) {
-               return tevent_req_post(req, ev);
+       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;
        }
 
-       subreq = cli_smb_send(state, ev, cli, SMBsesssetupX, 0, 10, vwv,
-                             talloc_get_size(bytes), bytes);
-       if (tevent_req_nomem(subreq, req)) {
-               return tevent_req_post(req, ev);
+       if (!need_kinit) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_OK;
        }
-       tevent_req_set_callback(subreq, cli_session_setup_lanman2_done, req);
-       return req;
-}
 
-static void cli_session_setup_lanman2_done(struct tevent_req *subreq)
-{
-       struct tevent_req *req = tevent_req_callback_data(
-               subreq, struct tevent_req);
-       struct cli_session_setup_lanman2_state *state = tevent_req_data(
-               req, struct cli_session_setup_lanman2_state);
-       struct cli_state *cli = state->cli;
-       uint32_t num_bytes;
-       uint8_t *in;
-       uint8_t *inhdr;
-       uint8_t *bytes;
-       uint8_t *p;
-       NTSTATUS status;
-       ssize_t ret;
-       uint8_t wct;
-       uint16_t *vwv;
+       DBG_INFO("Doing kinit for %s to access %s\n",
+                user_principal, target_hostname);
 
-       status = cli_smb_recv(subreq, state, &in, 3, &wct, &vwv,
-                             &num_bytes, &bytes);
-       TALLOC_FREE(subreq);
-       if (!NT_STATUS_IS_OK(status)) {
-               tevent_req_nterror(req, status);
-               return;
-       }
+       /*
+        * 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;
+               }
 
-       inhdr = in + NBT_HDR_SIZE;
-       p = bytes;
+               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);
+               }
 
-       cli_state_set_uid(state->cli, SVAL(inhdr, HDR_UID));
+               /*
+                * Ignore the error and hope that NTLM will work
+                */
+               TALLOC_FREE(frame);
+               return NT_STATUS_OK;
+       }
 
-       status = smb_bytes_talloc_string(cli,
-                                       inhdr,
-                                       &cli->server_os,
-                                       p,
-                                       bytes+num_bytes-p,
-                                       &ret);
+       ok = cli_credentials_set_principal(creds,
+                                          canon_principal,
+                                          CRED_SPECIFIED);
+       if (!ok) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_NO_MEMORY;
+       }
 
-       if (!NT_STATUS_IS_OK(status)) {
-               tevent_req_nterror(req, status);
-               return;
+       ok = cli_credentials_set_realm(creds,
+                                      canon_realm,
+                                      CRED_SPECIFIED);
+       if (!ok) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_NO_MEMORY;
        }
-       p += ret;
 
-       status = smb_bytes_talloc_string(cli,
-                                       inhdr,
-                                       &cli->server_type,
-                                       p,
-                                       bytes+num_bytes-p,
-                                       &ret);
+       DBG_DEBUG("Successfully authenticated as %s (%s) to access %s using "
+                 "Kerberos\n",
+                 user_principal,
+                 canon_principal,
+                 target_hostname);
 
-       if (!NT_STATUS_IS_OK(status)) {
-               tevent_req_nterror(req, status);
-               return;
+       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;
+               }
        }
-       p += ret;
 
-       status = smb_bytes_talloc_string(cli,
-                                       inhdr,
-                                       &cli->server_domain,
-                                       p,
-                                       bytes+num_bytes-p,
-                                       &ret);
+       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 (!NT_STATUS_IS_OK(status)) {
-               tevent_req_nterror(req, status);
-               return;
+       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;
+               }
        }
-       p += ret;
 
-       tevent_req_done(req);
+#undef _VALID_STRING
+       return NT_STATUS_OK;
 }
 
-static NTSTATUS cli_session_setup_lanman2_recv(struct tevent_req *req)
+/********************************************************
+ Utility function to ensure we always return at least
+ a valid char * pointer to an empty string for the
+ cli->server_os, cli->server_type and cli->server_domain
+ strings.
+*******************************************************/
+
+static NTSTATUS smb_bytes_talloc_string(TALLOC_CTX *mem_ctx,
+                                       const uint8_t *hdr,
+                                       char **dest,
+                                       uint8_t *src,
+                                       size_t srclen,
+                                       ssize_t *destlen)
 {
-       return tevent_req_simple_recv_ntstatus(req);
+       *destlen = clistr_pull_talloc(mem_ctx,
+                               (const char *)hdr,
+                               SVAL(hdr, HDR_FLG2),
+                               dest,
+                               (char *)src,
+                               srclen,
+                               STR_TERMINATE);
+       if (*destlen == -1) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       if (*dest == NULL) {
+               *dest = talloc_strdup(mem_ctx, "");
+               if (*dest == NULL) {
+                       return NT_STATUS_NO_MEMORY;
+               }
+       }
+       return NT_STATUS_OK;
 }
 
 /****************************************************************************
@@ -372,526 +514,56 @@ struct tevent_req *cli_session_setup_guest_create(TALLOC_CTX *mem_ctx,
 
        bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), "",  1, /* username */
                                   NULL);
-       bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), "", 1, /* workgroup */
-                                  NULL);
-       bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), "Unix", 5, NULL);
-       bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), "Samba", 6, NULL);
-
-       if (bytes == NULL) {
-               TALLOC_FREE(req);
-               return NULL;
-       }
-
-       state->bytes.iov_base = (void *)bytes;
-       state->bytes.iov_len = talloc_get_size(bytes);
-
-       subreq = cli_smb_req_create(state, ev, cli, SMBsesssetupX, 0, 13, vwv,
-                                   1, &state->bytes);
-       if (subreq == NULL) {
-               TALLOC_FREE(req);
-               return NULL;
-       }
-       tevent_req_set_callback(subreq, cli_session_setup_guest_done, req);
-       *psmbreq = subreq;
-       return req;
-}
-
-struct tevent_req *cli_session_setup_guest_send(TALLOC_CTX *mem_ctx,
-                                               struct tevent_context *ev,
-                                               struct cli_state *cli)
-{
-       struct tevent_req *req, *subreq;
-       NTSTATUS status;
-
-       req = cli_session_setup_guest_create(mem_ctx, ev, cli, &subreq);
-       if (req == NULL) {
-               return NULL;
-       }
-
-       status = smb1cli_req_chain_submit(&subreq, 1);
-       if (!NT_STATUS_IS_OK(status)) {
-               tevent_req_nterror(req, status);
-               return tevent_req_post(req, ev);
-       }
-       return req;
-}
-
-static void cli_session_setup_guest_done(struct tevent_req *subreq)
-{
-       struct tevent_req *req = tevent_req_callback_data(
-               subreq, struct tevent_req);
-       struct cli_session_setup_guest_state *state = tevent_req_data(
-               req, struct cli_session_setup_guest_state);
-       struct cli_state *cli = state->cli;
-       uint32_t num_bytes;
-       uint8_t *in;
-       uint8_t *inhdr;
-       uint8_t *bytes;
-       uint8_t *p;
-       NTSTATUS status;
-       ssize_t ret;
-       uint8_t wct;
-       uint16_t *vwv;
-
-       status = cli_smb_recv(subreq, state, &in, 3, &wct, &vwv,
-                             &num_bytes, &bytes);
-       TALLOC_FREE(subreq);
-       if (!NT_STATUS_IS_OK(status)) {
-               tevent_req_nterror(req, status);
-               return;
-       }
-
-       inhdr = in + NBT_HDR_SIZE;
-       p = bytes;
-
-       cli_state_set_uid(state->cli, SVAL(inhdr, HDR_UID));
-
-       status = smb_bytes_talloc_string(cli,
-                                       inhdr,
-                                       &cli->server_os,
-                                       p,
-                                       bytes+num_bytes-p,
-                                       &ret);
-
-       if (!NT_STATUS_IS_OK(status)) {
-               tevent_req_nterror(req, status);
-               return;
-       }
-       p += ret;
-
-       status = smb_bytes_talloc_string(cli,
-                                       inhdr,
-                                       &cli->server_type,
-                                       p,
-                                       bytes+num_bytes-p,
-                                       &ret);
-
-       if (!NT_STATUS_IS_OK(status)) {
-               tevent_req_nterror(req, status);
-               return;
-       }
-       p += ret;
-
-       status = smb_bytes_talloc_string(cli,
-                                       inhdr,
-                                       &cli->server_domain,
-                                       p,
-                                       bytes+num_bytes-p,
-                                       &ret);
-
-       if (!NT_STATUS_IS_OK(status)) {
-               tevent_req_nterror(req, status);
-               return;
-       }
-       p += ret;
-
-       tevent_req_done(req);
-}
-
-NTSTATUS cli_session_setup_guest_recv(struct tevent_req *req)
-{
-       return tevent_req_simple_recv_ntstatus(req);
-}
-
-/****************************************************************************
- Do a NT1 plaintext session setup.
-****************************************************************************/
-
-struct cli_session_setup_plain_state {
-       struct cli_state *cli;
-       uint16_t vwv[13];
-       const char *user;
-};
-
-static void cli_session_setup_plain_done(struct tevent_req *subreq);
-
-static struct tevent_req *cli_session_setup_plain_send(
-       TALLOC_CTX *mem_ctx, struct tevent_context *ev,
-       struct cli_state *cli,
-       const char *user, const char *pass, const char *workgroup)
-{
-       struct tevent_req *req, *subreq;
-       struct cli_session_setup_plain_state *state;
-       uint16_t *vwv;
-       uint8_t *bytes;
-       size_t passlen;
-       char *version;
-
-       req = tevent_req_create(mem_ctx, &state,
-                               struct cli_session_setup_plain_state);
-       if (req == NULL) {
-               return NULL;
-       }
-       state->cli = cli;
-       state->user = user;
-       vwv = state->vwv;
-
-       SCVAL(vwv+0, 0, 0xff);
-       SCVAL(vwv+0, 1, 0);
-       SSVAL(vwv+1, 0, 0);
-       SSVAL(vwv+2, 0, CLI_BUFFER_SIZE);
-       SSVAL(vwv+3, 0, 2);
-       SSVAL(vwv+4, 0, cli_state_get_vc_num(cli));
-       SIVAL(vwv+5, 0, smb1cli_conn_server_session_key(cli->conn));
-       SSVAL(vwv+7, 0, 0);
-       SSVAL(vwv+8, 0, 0);
-       SSVAL(vwv+9, 0, 0);
-       SSVAL(vwv+10, 0, 0);
-       SIVAL(vwv+11, 0, cli_session_setup_capabilities(cli, 0));
-
-       bytes = talloc_array(state, uint8_t, 0);
-       bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), pass, strlen(pass)+1,
-                                  &passlen);
-       if (tevent_req_nomem(bytes, req)) {
-               return tevent_req_post(req, ev);
-       }
-       SSVAL(vwv + (smbXcli_conn_use_unicode(cli->conn) ? 8 : 7), 0, passlen);
-
-       bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn),
-                                  user, strlen(user)+1, NULL);
-       bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn),
-                                  workgroup, strlen(workgroup)+1, NULL);
-       bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn),
-                                  "Unix", 5, NULL);
-
-       version = talloc_asprintf(talloc_tos(), "Samba %s",
-                                 samba_version_string());
-       if (tevent_req_nomem(version, req)){
-               return tevent_req_post(req, ev);
-       }
-       bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn),
-                                  version, strlen(version)+1, NULL);
-       TALLOC_FREE(version);
-
-       if (tevent_req_nomem(bytes, req)) {
-               return tevent_req_post(req, ev);
-       }
-
-       subreq = cli_smb_send(state, ev, cli, SMBsesssetupX, 0, 13, vwv,
-                             talloc_get_size(bytes), bytes);
-       if (tevent_req_nomem(subreq, req)) {
-               return tevent_req_post(req, ev);
-       }
-       tevent_req_set_callback(subreq, cli_session_setup_plain_done, req);
-       return req;
-}
-
-static void cli_session_setup_plain_done(struct tevent_req *subreq)
-{
-       struct tevent_req *req = tevent_req_callback_data(
-               subreq, struct tevent_req);
-       struct cli_session_setup_plain_state *state = tevent_req_data(
-               req, struct cli_session_setup_plain_state);
-       struct cli_state *cli = state->cli;
-       uint32_t num_bytes;
-       uint8_t *in;
-       uint8_t *inhdr;
-       uint8_t *bytes;
-       uint8_t *p;
-       NTSTATUS status;
-       ssize_t ret;
-       uint8_t wct;
-       uint16_t *vwv;
-
-       status = cli_smb_recv(subreq, state, &in, 3, &wct, &vwv,
-                             &num_bytes, &bytes);
-       TALLOC_FREE(subreq);
-       if (tevent_req_nterror(req, status)) {
-               return;
-       }
-
-       inhdr = in + NBT_HDR_SIZE;
-       p = bytes;
-
-       cli_state_set_uid(state->cli, SVAL(inhdr, HDR_UID));
-
-       status = smb_bytes_talloc_string(cli,
-                                       inhdr,
-                                       &cli->server_os,
-                                       p,
-                                       bytes+num_bytes-p,
-                                       &ret);
-
-       if (!NT_STATUS_IS_OK(status)) {
-               tevent_req_nterror(req, status);
-               return;
-       }
-       p += ret;
-
-       status = smb_bytes_talloc_string(cli,
-                                       inhdr,
-                                       &cli->server_type,
-                                       p,
-                                       bytes+num_bytes-p,
-                                       &ret);
-
-       if (!NT_STATUS_IS_OK(status)) {
-               tevent_req_nterror(req, status);
-               return;
-       }
-       p += ret;
-
-       status = smb_bytes_talloc_string(cli,
-                                       inhdr,
-                                       &cli->server_domain,
-                                       p,
-                                       bytes+num_bytes-p,
-                                       &ret);
-
-       if (!NT_STATUS_IS_OK(status)) {
-               tevent_req_nterror(req, status);
-               return;
-       }
-       p += ret;
-
-       tevent_req_done(req);
-}
-
-static NTSTATUS cli_session_setup_plain_recv(struct tevent_req *req)
-{
-       return tevent_req_simple_recv_ntstatus(req);
-}
-
-/****************************************************************************
-   do a NT1 NTLM/LM encrypted session setup - for when extended security
-   is not negotiated.
-   @param cli client state to create do session setup on
-   @param user username
-   @param pass *either* cleartext password (passlen !=24) or LM response.
-   @param ntpass NT response, implies ntpasslen >=24, implies pass is not clear
-   @param workgroup The user's domain.
-****************************************************************************/
-
-struct cli_session_setup_nt1_state {
-       struct cli_state *cli;
-       uint16_t vwv[13];
-       DATA_BLOB response;
-       DATA_BLOB session_key;
-       const char *user;
-};
-
-static void cli_session_setup_nt1_done(struct tevent_req *subreq);
-
-static struct tevent_req *cli_session_setup_nt1_send(
-       TALLOC_CTX *mem_ctx, struct tevent_context *ev,
-       struct cli_state *cli, const char *user,
-       const char *pass, size_t passlen,
-       const char *ntpass, size_t ntpasslen,
-       const char *workgroup)
-{
-       struct tevent_req *req, *subreq;
-       struct cli_session_setup_nt1_state *state;
-       DATA_BLOB lm_response = data_blob_null;
-       DATA_BLOB nt_response = data_blob_null;
-       DATA_BLOB session_key = data_blob_null;
-       uint16_t *vwv;
-       uint8_t *bytes;
-       char *workgroup_upper;
-
-       req = tevent_req_create(mem_ctx, &state,
-                               struct cli_session_setup_nt1_state);
-       if (req == NULL) {
-               return NULL;
-       }
-       state->cli = cli;
-       state->user = user;
-       vwv = state->vwv;
-
-       if (passlen == 0) {
-               /* do nothing - guest login */
-       } else if (passlen != 24) {
-               if (lp_client_ntlmv2_auth()) {
-                       DATA_BLOB server_chal;
-                       DATA_BLOB names_blob;
-
-                       server_chal =
-                               data_blob_const(smb1cli_conn_server_challenge(cli->conn),
-                                               8);
-
-                       /*
-                        * note that the 'workgroup' here is a best
-                        * guess - we don't know the server's domain
-                        * at this point. Windows clients also don't
-                        * use hostname...
-                        */
-                       names_blob = NTLMv2_generate_names_blob(
-                               NULL, NULL, workgroup);
-
-                       if (tevent_req_nomem(names_blob.data, req)) {
-                               return tevent_req_post(req, ev);
-                       }
-
-                       if (!SMBNTLMv2encrypt(NULL, user, workgroup, pass,
-                                             &server_chal, &names_blob,
-                                             &lm_response, &nt_response,
-                                             NULL, &session_key)) {
-                               data_blob_free(&names_blob);
-                               tevent_req_nterror(
-                                       req, NT_STATUS_ACCESS_DENIED);
-                               return tevent_req_post(req, ev);
-                       }
-                       data_blob_free(&names_blob);
-
-               } else {
-                       uchar nt_hash[16];
-                       E_md4hash(pass, nt_hash);
-
-#ifdef LANMAN_ONLY
-                       nt_response = data_blob_null;
-#else
-                       nt_response = data_blob(NULL, 24);
-                       if (tevent_req_nomem(nt_response.data, req)) {
-                               return tevent_req_post(req, ev);
-                       }
-
-                       SMBNTencrypt(pass, smb1cli_conn_server_challenge(cli->conn),
-                                    nt_response.data);
-#endif
-                       /* non encrypted password supplied. Ignore ntpass. */
-                       if (lp_client_lanman_auth()) {
-
-                               lm_response = data_blob(NULL, 24);
-                               if (tevent_req_nomem(lm_response.data, req)) {
-                                       return tevent_req_post(req, ev);
-                               }
-
-                               if (!SMBencrypt(pass,
-                                               smb1cli_conn_server_challenge(cli->conn),
-                                               lm_response.data)) {
-                                       /*
-                                        * Oops, the LM response is
-                                        * invalid, just put the NT
-                                        * response there instead
-                                        */
-                                       data_blob_free(&lm_response);
-                                       lm_response = data_blob(
-                                               nt_response.data,
-                                               nt_response.length);
-                               }
-                       } else {
-                               /*
-                                * LM disabled, place NT# in LM field
-                                * instead
-                                */
-                               lm_response = data_blob(
-                                       nt_response.data, nt_response.length);
-                       }
-
-                       if (tevent_req_nomem(lm_response.data, req)) {
-                               return tevent_req_post(req, ev);
-                       }
-
-                       session_key = data_blob(NULL, 16);
-                       if (tevent_req_nomem(session_key.data, req)) {
-                               return tevent_req_post(req, ev);
-                       }
-#ifdef LANMAN_ONLY
-                       E_deshash(pass, session_key.data);
-                       memset(&session_key.data[8], '\0', 8);
-#else
-                       SMBsesskeygen_ntv1(nt_hash, session_key.data);
-#endif
-               }
-       } else {
-               /* pre-encrypted password supplied.  Only used for 
-                  security=server, can't do
-                  signing because we don't have original key */
-
-               lm_response = data_blob(pass, passlen);
-               if (tevent_req_nomem(lm_response.data, req)) {
-                       return tevent_req_post(req, ev);
-               }
-
-               nt_response = data_blob(ntpass, ntpasslen);
-               if (tevent_req_nomem(nt_response.data, req)) {
-                       return tevent_req_post(req, ev);
-               }
-       }
-
-#ifdef LANMAN_ONLY
-       state->response = data_blob_talloc(
-               state, lm_response.data, lm_response.length);
-#else
-       state->response = data_blob_talloc(
-               state, nt_response.data, nt_response.length);
-#endif
-       if (tevent_req_nomem(state->response.data, req)) {
-               return tevent_req_post(req, ev);
-       }
-
-       if (session_key.data) {
-               state->session_key = data_blob_talloc(
-                       state, session_key.data, session_key.length);
-               if (tevent_req_nomem(state->session_key.data, req)) {
-                       return tevent_req_post(req, ev);
-               }
-       }
-       data_blob_free(&session_key);
-
-       SCVAL(vwv+0, 0, 0xff);
-       SCVAL(vwv+0, 1, 0);
-       SSVAL(vwv+1, 0, 0);
-       SSVAL(vwv+2, 0, CLI_BUFFER_SIZE);
-       SSVAL(vwv+3, 0, 2);
-       SSVAL(vwv+4, 0, cli_state_get_vc_num(cli));
-       SIVAL(vwv+5, 0, smb1cli_conn_server_session_key(cli->conn));
-       SSVAL(vwv+7, 0, lm_response.length);
-       SSVAL(vwv+8, 0, nt_response.length);
-       SSVAL(vwv+9, 0, 0);
-       SSVAL(vwv+10, 0, 0);
-       SIVAL(vwv+11, 0, cli_session_setup_capabilities(cli, 0));
+       bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), "", 1, /* workgroup */
+                                  NULL);
+       bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), "Unix", 5, NULL);
+       bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), "Samba", 6, NULL);
 
-       bytes = talloc_array(state, uint8_t,
-                            lm_response.length + nt_response.length);
-       if (tevent_req_nomem(bytes, req)) {
-               return tevent_req_post(req, ev);
-       }
-       if (lm_response.length != 0) {
-               memcpy(bytes, lm_response.data, lm_response.length);
-       }
-       if (nt_response.length != 0) {
-               memcpy(bytes + lm_response.length,
-                      nt_response.data, nt_response.length);
+       if (bytes == NULL) {
+               TALLOC_FREE(req);
+               return NULL;
        }
-       data_blob_free(&lm_response);
-       data_blob_free(&nt_response);
 
-       bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn),
-                                  user, strlen(user)+1, NULL);
+       state->bytes.iov_base = (void *)bytes;
+       state->bytes.iov_len = talloc_get_size(bytes);
 
-       /*
-        * Upper case here might help some NTLMv2 implementations
-        */
-       workgroup_upper = talloc_strdup_upper(talloc_tos(), workgroup);
-       if (tevent_req_nomem(workgroup_upper, req)) {
-               return tevent_req_post(req, ev);
+       subreq = cli_smb_req_create(state, ev, cli, SMBsesssetupX, 0, 0, 13,
+                       vwv, 1, &state->bytes);
+       if (subreq == NULL) {
+               TALLOC_FREE(req);
+               return NULL;
        }
-       bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn),
-                                  workgroup_upper, strlen(workgroup_upper)+1,
-                                  NULL);
-       TALLOC_FREE(workgroup_upper);
+       tevent_req_set_callback(subreq, cli_session_setup_guest_done, req);
+       *psmbreq = subreq;
+       return req;
+}
 
-       bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), "Unix", 5, NULL);
-       bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), "Samba", 6, NULL);
-       if (tevent_req_nomem(bytes, req)) {
-               return tevent_req_post(req, ev);
+struct tevent_req *cli_session_setup_guest_send(TALLOC_CTX *mem_ctx,
+                                               struct tevent_context *ev,
+                                               struct cli_state *cli)
+{
+       struct tevent_req *req, *subreq;
+       NTSTATUS status;
+
+       req = cli_session_setup_guest_create(mem_ctx, ev, cli, &subreq);
+       if (req == NULL) {
+               return NULL;
        }
 
-       subreq = cli_smb_send(state, ev, cli, SMBsesssetupX, 0, 13, vwv,
-                             talloc_get_size(bytes), bytes);
-       if (tevent_req_nomem(subreq, req)) {
+       status = smb1cli_req_chain_submit(&subreq, 1);
+       if (!NT_STATUS_IS_OK(status)) {
+               tevent_req_nterror(req, status);
                return tevent_req_post(req, ev);
        }
-       tevent_req_set_callback(subreq, cli_session_setup_nt1_done, req);
        return req;
 }
 
-static void cli_session_setup_nt1_done(struct tevent_req *subreq)
+static void cli_session_setup_guest_done(struct tevent_req *subreq)
 {
        struct tevent_req *req = tevent_req_callback_data(
                subreq, struct tevent_req);
-       struct cli_session_setup_nt1_state *state = tevent_req_data(
-               req, struct cli_session_setup_nt1_state);
+       struct cli_session_setup_guest_state *state = tevent_req_data(
+               req, struct cli_session_setup_guest_state);
        struct cli_state *cli = state->cli;
        uint32_t num_bytes;
        uint8_t *in;
@@ -915,6 +587,7 @@ static void cli_session_setup_nt1_done(struct tevent_req *subreq)
        p = bytes;
 
        cli_state_set_uid(state->cli, SVAL(inhdr, HDR_UID));
+       smb1cli_session_set_action(cli->smb1.session, SVAL(vwv+2, 0));
 
        status = smb_bytes_talloc_string(cli,
                                        inhdr,
@@ -922,6 +595,7 @@ static void cli_session_setup_nt1_done(struct tevent_req *subreq)
                                        p,
                                        bytes+num_bytes-p,
                                        &ret);
+
        if (!NT_STATUS_IS_OK(status)) {
                tevent_req_nterror(req, status);
                return;
@@ -934,6 +608,7 @@ static void cli_session_setup_nt1_done(struct tevent_req *subreq)
                                        p,
                                        bytes+num_bytes-p,
                                        &ret);
+
        if (!NT_STATUS_IS_OK(status)) {
                tevent_req_nterror(req, status);
                return;
@@ -946,30 +621,16 @@ static void cli_session_setup_nt1_done(struct tevent_req *subreq)
                                        p,
                                        bytes+num_bytes-p,
                                        &ret);
+
        if (!NT_STATUS_IS_OK(status)) {
                tevent_req_nterror(req, status);
                return;
        }
-       p += ret;
-
-       if (smb1cli_conn_activate_signing(cli->conn, state->session_key, state->response)
-           && !smb1cli_conn_check_signing(cli->conn, (uint8_t *)in, 1)) {
-               tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
-               return;
-       }
-       if (state->session_key.data) {
-               struct smbXcli_session *session = state->cli->smb1.session;
 
-               status = smb1cli_session_set_session_key(session,
-                               state->session_key);
-               if (tevent_req_nterror(req, status)) {
-                       return;
-               }
-       }
        tevent_req_done(req);
 }
 
-static NTSTATUS cli_session_setup_nt1_recv(struct tevent_req *req)
+NTSTATUS cli_session_setup_guest_recv(struct tevent_req *req)
 {
        return tevent_req_simple_recv_ntstatus(req);
 }
@@ -988,15 +649,16 @@ struct cli_sesssetup_blob_state {
        struct cli_state *cli;
        DATA_BLOB blob;
        uint16_t max_blob_size;
-       uint16_t vwv[12];
-       uint8_t *buf;
 
-       DATA_BLOB smb2_blob;
+       DATA_BLOB this_blob;
        struct iovec *recv_iov;
 
        NTSTATUS status;
-       uint8_t *inbuf;
+       const uint8_t *inbuf;
        DATA_BLOB ret_blob;
+
+       char *out_native_os;
+       char *out_native_lm;
 };
 
 static bool cli_sesssetup_blob_next(struct cli_sesssetup_blob_state *state,
@@ -1053,14 +715,13 @@ static bool cli_sesssetup_blob_next(struct cli_sesssetup_blob_state *state,
 
        thistime = MIN(state->blob.length, state->max_blob_size);
 
-       if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
-
-               state->smb2_blob.data = state->blob.data;
-               state->smb2_blob.length = thistime;
+       state->this_blob.data = state->blob.data;
+       state->this_blob.length = thistime;
 
-               state->blob.data += thistime;
-               state->blob.length -= thistime;
+       state->blob.data += thistime;
+       state->blob.length -= thistime;
 
+       if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
                subreq = smb2cli_session_setup_send(state, state->ev,
                                                    state->cli->conn,
                                                    state->cli->timeout,
@@ -1069,49 +730,52 @@ static bool cli_sesssetup_blob_next(struct cli_sesssetup_blob_state *state,
                                                    SMB2_CAP_DFS, /* in_capabilities */
                                                    0, /* in_channel */
                                                    0, /* in_previous_session_id */
-                                                   &state->smb2_blob);
+                                                   &state->this_blob);
                if (subreq == NULL) {
                        return false;
                }
-               *psubreq = subreq;
-               return true;
-       }
-
-       SCVAL(state->vwv+0, 0, 0xFF);
-       SCVAL(state->vwv+0, 1, 0);
-       SSVAL(state->vwv+1, 0, 0);
-       SSVAL(state->vwv+2, 0, CLI_BUFFER_SIZE);
-       SSVAL(state->vwv+3, 0, 2);
-       SSVAL(state->vwv+4, 0, 1);
-       SIVAL(state->vwv+5, 0, 0);
-
-       SSVAL(state->vwv+7, 0, thistime);
-
-       SSVAL(state->vwv+8, 0, 0);
-       SSVAL(state->vwv+9, 0, 0);
-       SIVAL(state->vwv+10, 0,
-               cli_session_setup_capabilities(state->cli, CAP_EXTENDED_SECURITY));
-
-       state->buf = (uint8_t *)talloc_memdup(state, state->blob.data,
-                                             thistime);
-       if (state->buf == NULL) {
-               return false;
-       }
-       state->blob.data += thistime;
-       state->blob.length -= thistime;
+       } else {
+               uint16_t in_buf_size = 0;
+               uint16_t in_mpx_max = 0;
+               uint16_t in_vc_num = 0;
+               uint32_t in_sess_key = 0;
+               uint32_t in_capabilities = 0;
+               const char *in_native_os = NULL;
+               const char *in_native_lm = NULL;
+
+               in_buf_size = CLI_BUFFER_SIZE;
+               in_mpx_max = smbXcli_conn_max_requests(state->cli->conn);
+               in_vc_num = cli_state_get_vc_num(state->cli);
+               in_sess_key = smb1cli_conn_server_session_key(state->cli->conn);
+               in_capabilities = cli_session_setup_capabilities(state->cli,
+                                                               CAP_EXTENDED_SECURITY);
+               in_native_os = "Unix";
+               in_native_lm = "Samba";
 
-       state->buf = smb_bytes_push_str(state->buf, smbXcli_conn_use_unicode(state->cli->conn),
-                                       "Unix", 5, NULL);
-       state->buf = smb_bytes_push_str(state->buf, smbXcli_conn_use_unicode(state->cli->conn),
-                                       "Samba", 6, NULL);
-       if (state->buf == NULL) {
-               return false;
-       }
-       subreq = cli_smb_send(state, state->ev, state->cli, SMBsesssetupX, 0,
-                             12, state->vwv,
-                             talloc_get_size(state->buf), state->buf);
-       if (subreq == NULL) {
-               return false;
+               /*
+                * For now we keep the same values as before,
+                * we may remove these in a separate commit later.
+                */
+               in_mpx_max = 2;
+               in_vc_num = 1;
+               in_sess_key = 0;
+
+               subreq = smb1cli_session_setup_ext_send(state, state->ev,
+                                                       state->cli->conn,
+                                                       state->cli->timeout,
+                                                       state->cli->smb1.pid,
+                                                       state->cli->smb1.session,
+                                                       in_buf_size,
+                                                       in_mpx_max,
+                                                       in_vc_num,
+                                                       in_sess_key,
+                                                       state->this_blob,
+                                                       in_capabilities,
+                                                       in_native_os,
+                                                       in_native_lm);
+               if (subreq == NULL) {
+                       return false;
+               }
        }
        *psubreq = subreq;
        return true;
@@ -1123,26 +787,19 @@ 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;
-       uint8_t wct;
-       uint16_t *vwv;
-       uint32_t num_bytes;
-       uint8_t *bytes;
        NTSTATUS status;
-       uint8_t *p;
-       uint16_t blob_length;
-       uint8_t *in;
-       uint8_t *inhdr;
-       ssize_t ret;
 
        if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
                status = smb2cli_session_setup_recv(subreq, state,
                                                    &state->recv_iov,
                                                    &state->ret_blob);
        } else {
-               status = cli_smb_recv(subreq, state, &in, 4, &wct, &vwv,
-                                     &num_bytes, &bytes);
-               TALLOC_FREE(state->buf);
+               status = smb1cli_session_setup_ext_recv(subreq, state,
+                                                       &state->recv_iov,
+                                                       &state->inbuf,
+                                                       &state->ret_blob,
+                                                       &state->out_native_os,
+                                                       &state->out_native_lm);
        }
        TALLOC_FREE(subreq);
        if (!NT_STATUS_IS_OK(status)
@@ -1153,63 +810,14 @@ static void cli_sesssetup_blob_done(struct tevent_req *subreq)
 
        state->status = status;
 
-       if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
-               goto next;
-       }
-
-       state->inbuf = in;
-       inhdr = in + NBT_HDR_SIZE;
-       cli_state_set_uid(state->cli, SVAL(inhdr, HDR_UID));
-
-       blob_length = SVAL(vwv+3, 0);
-       if (blob_length > num_bytes) {
-               tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
-               return;
-       }
-       state->ret_blob = data_blob_const(bytes, blob_length);
-
-       p = bytes + blob_length;
-
-       status = smb_bytes_talloc_string(cli,
-                                       inhdr,
-                                       &cli->server_os,
-                                       p,
-                                       bytes+num_bytes-p,
-                                       &ret);
-
-       if (!NT_STATUS_IS_OK(status)) {
-               tevent_req_nterror(req, status);
-               return;
-       }
-       p += ret;
-
-       status = smb_bytes_talloc_string(cli,
-                                       inhdr,
-                                       &cli->server_type,
-                                       p,
-                                       bytes+num_bytes-p,
-                                       &ret);
-
-       if (!NT_STATUS_IS_OK(status)) {
-               tevent_req_nterror(req, status);
-               return;
-       }
-       p += ret;
-
-       status = smb_bytes_talloc_string(cli,
-                                       inhdr,
-                                       &cli->server_domain,
-                                       p,
-                                       bytes+num_bytes-p,
-                                       &ret);
-
-       if (!NT_STATUS_IS_OK(status)) {
-               tevent_req_nterror(req, 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;
        }
-       p += ret;
 
-next:
        if (state->blob.length != 0) {
                /*
                 * More to send
@@ -1227,48 +835,37 @@ next:
 static NTSTATUS cli_sesssetup_blob_recv(struct tevent_req *req,
                                        TALLOC_CTX *mem_ctx,
                                        DATA_BLOB *pblob,
-                                       uint8_t **pinbuf,
+                                       const uint8_t **pinbuf,
                                        struct iovec **precv_iov)
 {
        struct cli_sesssetup_blob_state *state = tevent_req_data(
                req, struct cli_sesssetup_blob_state);
        NTSTATUS status;
-       uint8_t *inbuf;
        struct iovec *recv_iov;
 
        if (tevent_req_is_nterror(req, &status)) {
                TALLOC_FREE(state->cli->smb2.session);
                cli_state_set_uid(state->cli, UID_FIELD_INVALID);
+               tevent_req_received(req);
                return status;
        }
 
-       inbuf = talloc_move(mem_ctx, &state->inbuf);
        recv_iov = talloc_move(mem_ctx, &state->recv_iov);
        if (pblob != NULL) {
                *pblob = state->ret_blob;
        }
        if (pinbuf != NULL) {
-               *pinbuf = inbuf;
+               *pinbuf = state->inbuf;
        }
        if (precv_iov != NULL) {
                *precv_iov = recv_iov;
        }
         /* could be NT_STATUS_MORE_PROCESSING_REQUIRED */
-       return state->status;
-}
-
-#ifdef HAVE_KRB5
-
-/****************************************************************************
- Use in-memory credentials cache
-****************************************************************************/
-
-static void use_in_memory_ccache(void) {
-       setenv(KRB5_ENV_CCNAME, "MEMORY:cliconnect", 1);
+       status = state->status;
+       tevent_req_received(req);
+       return status;
 }
 
-#endif /* HAVE_KRB5 */
-
 /****************************************************************************
  Do a spnego/NTLMSSP encrypted session setup.
 ****************************************************************************/
@@ -1279,7 +876,7 @@ struct cli_session_setup_gensec_state {
        struct auth_generic_state *auth_generic;
        bool is_anonymous;
        DATA_BLOB blob_in;
-       uint8_t *inbuf;
+       const uint8_t *inbuf;
        struct iovec *recv_iov;
        DATA_BLOB blob_out;
        bool local_ready;
@@ -1303,16 +900,14 @@ static void cli_session_setup_gensec_ready(struct tevent_req *req);
 
 static struct tevent_req *cli_session_setup_gensec_send(
        TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli,
-       const char *user, const char *pass, const char *domain,
-       enum credentials_use_kerberos krb5_state,
+       struct cli_credentials *creds,
        const char *target_service,
-       const char *target_hostname,
-       const char *target_principal)
+       const char *target_hostname)
 {
        struct tevent_req *req;
        struct cli_session_setup_gensec_state *state;
        NTSTATUS status;
-       bool use_spnego_principal = lp_client_use_spnego_principal();
+       const DATA_BLOB *b = NULL;
 
        req = tevent_req_create(mem_ctx, &state,
                                struct cli_session_setup_gensec_state);
@@ -1330,69 +925,13 @@ static struct tevent_req *cli_session_setup_gensec_send(
                return tevent_req_post(req, ev);
        }
 
-       gensec_want_feature(state->auth_generic->gensec_security,
-                           GENSEC_FEATURE_SESSION_KEY);
-       if (cli->use_ccache) {
-               gensec_want_feature(state->auth_generic->gensec_security,
-                                   GENSEC_FEATURE_NTLM_CCACHE);
-               if (pass != NULL && strlen(pass) == 0) {
-                       /*
-                        * some callers pass "" as no password
-                        *
-                        * GENSEC_FEATURE_NTLM_CCACHE only handles
-                        * NULL as no password.
-                        */
-                       pass = NULL;
-               }
-       }
-
-       status = auth_generic_set_username(state->auth_generic, user);
-       if (tevent_req_nterror(req, status)) {
-               return tevent_req_post(req, ev);
-       }
-
-       status = auth_generic_set_domain(state->auth_generic, domain);
+       status = auth_generic_set_creds(state->auth_generic, creds);
        if (tevent_req_nterror(req, status)) {
                return tevent_req_post(req, ev);
        }
 
-       if (cli->pw_nt_hash) {
-               struct samr_Password nt_hash;
-               size_t converted;
-               bool ok;
-
-               if (pass == NULL) {
-                       tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
-                       return tevent_req_post(req, ev);
-               }
-
-               converted = strhex_to_str((char *)nt_hash.hash,
-                                         sizeof(nt_hash.hash),
-                                         pass, strlen(pass));
-               if (converted != sizeof(nt_hash.hash)) {
-                       tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
-                       return tevent_req_post(req, ev);
-               }
-
-               ok = cli_credentials_set_nt_hash(state->auth_generic->credentials,
-                                                &nt_hash, CRED_SPECIFIED);
-               if (!ok) {
-                       tevent_req_oom(req);
-                       return tevent_req_post(req, ev);
-               }
-       } else {
-               status = auth_generic_set_password(state->auth_generic, pass);
-               if (tevent_req_nterror(req, status)) {
-                       return tevent_req_post(req, ev);
-               }
-       }
-
-       cli_credentials_set_kerberos_state(state->auth_generic->credentials,
-                                          krb5_state);
-
-       if (krb5_state == CRED_DONT_USE_KERBEROS) {
-               use_spnego_principal = false;
-       }
+       gensec_want_feature(state->auth_generic->gensec_security,
+                           GENSEC_FEATURE_SESSION_KEY);
 
        if (target_service != NULL) {
                status = gensec_set_target_service(
@@ -1412,24 +951,9 @@ static struct tevent_req *cli_session_setup_gensec_send(
                }
        }
 
-       if (target_principal != NULL) {
-               status = gensec_set_target_principal(
-                               state->auth_generic->gensec_security,
-                               target_principal);
-               if (tevent_req_nterror(req, status)) {
-                       return tevent_req_post(req, ev);
-               }
-               use_spnego_principal = false;
-       } else if (target_service != NULL && target_hostname != NULL) {
-               use_spnego_principal = false;
-       }
-
-       if (use_spnego_principal) {
-               const DATA_BLOB *b;
-               b = smbXcli_conn_server_gss_blob(cli->conn);
-               if (b != NULL) {
-                       state->blob_in = *b;
-               }
+       b = smbXcli_conn_server_gss_blob(cli->conn);
+       if (b != NULL) {
+               state->blob_in = *b;
        }
 
        state->is_anonymous = cli_credentials_is_anonymous(state->auth_generic->credentials);
@@ -1541,7 +1065,7 @@ static void cli_session_setup_gensec_remote_done(struct tevent_req *subreq)
                struct cli_session_setup_gensec_state);
        NTSTATUS status;
 
-       TALLOC_FREE(state->inbuf);
+       state->inbuf = NULL;
        TALLOC_FREE(state->recv_iov);
 
        status = cli_sesssetup_blob_recv(subreq, state, &state->blob_in,
@@ -1556,6 +1080,33 @@ static void cli_session_setup_gensec_remote_done(struct tevent_req *subreq)
        }
 
        if (NT_STATUS_IS_OK(status)) {
+               struct smbXcli_session *session = NULL;
+               bool is_guest = false;
+
+               if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
+                       session = state->cli->smb2.session;
+               } else {
+                       session = state->cli->smb1.session;
+               }
+
+               is_guest = smbXcli_session_is_guest(session);
+               if (is_guest) {
+                       /*
+                        * We can't finish the gensec handshake, we don't
+                        * have a negotiated session key.
+                        *
+                        * 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;
        }
 
@@ -1567,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 =
@@ -1606,6 +1209,19 @@ static void cli_session_setup_gensec_ready(struct tevent_req *req)
                }
        }
 
+       if (state->is_anonymous) {
+               /*
+                * Windows server does not set the
+                * SMB2_SESSION_FLAG_IS_NULL flag.
+                *
+                * This fix makes sure we do not try
+                * to verify a signature on the final
+                * session setup response.
+                */
+               tevent_req_done(req);
+               return;
+       }
+
        status = gensec_session_key(state->auth_generic->gensec_security,
                                    state, &state->session_key);
        if (tevent_req_nterror(req, status)) {
@@ -1615,26 +1231,17 @@ static void cli_session_setup_gensec_ready(struct tevent_req *req)
        if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
                struct smbXcli_session *session = state->cli->smb2.session;
 
-               if (state->is_anonymous) {
-                       /*
-                        * Windows server does not set the
-                        * SMB2_SESSION_FLAG_IS_GUEST nor
-                        * SMB2_SESSION_FLAG_IS_NULL flag.
-                        *
-                        * This fix makes sure we do not try
-                        * to verify a signature on the final
-                        * session setup response.
-                        */
-                       tevent_req_done(req);
-                       return;
-               }
-
                status = smb2cli_session_set_session_key(session,
                                                         state->session_key,
                                                         state->recv_iov);
                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;
@@ -1670,56 +1277,12 @@ static NTSTATUS cli_session_setup_gensec_recv(struct tevent_req *req)
                struct cli_session_setup_gensec_state);
        NTSTATUS status;
 
-       if (tevent_req_is_nterror(req, &status)) {
-               cli_state_set_uid(state->cli, UID_FIELD_INVALID);
-               return status;
-       }
-       return NT_STATUS_OK;
-}
-
-#ifdef HAVE_KRB5
-
-static char *cli_session_setup_get_principal(
-       TALLOC_CTX *mem_ctx, const char *spnego_principal,
-       const char *remote_name, const char *dest_realm)
-{
-       char *principal = NULL;
-
-       if (!lp_client_use_spnego_principal() ||
-           strequal(spnego_principal, ADS_IGNORE_PRINCIPAL)) {
-               spnego_principal = NULL;
-       }
-       if (spnego_principal != NULL) {
-               DEBUG(3, ("cli_session_setup_spnego: using spnego provided "
-                         "principal %s\n", spnego_principal));
-               return talloc_strdup(mem_ctx, spnego_principal);
-       }
-       if (is_ipaddress(remote_name) ||
-           strequal(remote_name, STAR_SMBSERVER)) {
-               return NULL;
-       }
-
-       DEBUG(3, ("cli_session_setup_spnego: using target "
-                 "hostname not SPNEGO principal\n"));
-
-       if (dest_realm) {
-               char *realm = strupper_talloc(talloc_tos(), dest_realm);
-               if (realm == NULL) {
-                       return NULL;
-               }
-               principal = talloc_asprintf(talloc_tos(), "cifs/%s@%s",
-                                           remote_name, realm);
-               TALLOC_FREE(realm);
-       } else {
-               principal = kerberos_get_principal_from_service_hostname(
-                       talloc_tos(), "cifs", remote_name, lp_realm());
-       }
-       DEBUG(3, ("cli_session_setup_spnego: guessed server principal=%s\n",
-                 principal ? principal : "<null>"));
-
-       return principal;
+       if (tevent_req_is_nterror(req, &status)) {
+               cli_state_set_uid(state->cli, UID_FIELD_INVALID);
+               return status;
+       }
+       return NT_STATUS_OK;
 }
-#endif
 
 static char *cli_session_setup_get_account(TALLOC_CTX *mem_ctx,
                                           const char *principal)
@@ -1745,198 +1308,61 @@ static char *cli_session_setup_get_account(TALLOC_CTX *mem_ctx,
 ****************************************************************************/
 
 struct cli_session_setup_spnego_state {
-       struct tevent_context *ev;
-       struct cli_state *cli;
-       const char *target_hostname;
-       const char *user;
-       const char *account;
-       const char *pass;
-       const char *user_domain;
-       const char *dest_realm;
        ADS_STATUS result;
 };
 
-#ifdef HAVE_KRB5
-static void cli_session_setup_spnego_done_krb(struct tevent_req *subreq);
-#endif
-
-static void cli_session_setup_spnego_done_ntlmssp(struct tevent_req *subreq);
+static void cli_session_setup_spnego_done(struct tevent_req *subreq);
 
 static struct tevent_req *cli_session_setup_spnego_send(
        TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli,
-       const char *user, const char *pass, const char *user_domain)
+       struct cli_credentials *creds)
 {
        struct tevent_req *req, *subreq;
        struct cli_session_setup_spnego_state *state;
-       char *principal = NULL;
-       char *OIDs[ASN1_MAX_OIDS];
-       int i;
-       const char *dest_realm = cli_state_remote_realm(cli);
-       const DATA_BLOB *server_blob;
+       const char *target_service = NULL;
+       const char *target_hostname = NULL;
+       NTSTATUS status;
 
        req = tevent_req_create(mem_ctx, &state,
                                struct cli_session_setup_spnego_state);
        if (req == NULL) {
                return NULL;
        }
-       state->ev = ev;
-       state->cli = cli;
-       state->user = user;
-       state->pass = pass;
-       state->user_domain = user_domain;
-       state->dest_realm = dest_realm;
-
-       state->account = cli_session_setup_get_account(state, user);
-       if (tevent_req_nomem(state->account, req)) {
-               return tevent_req_post(req, ev);
-       }
-
-       state->target_hostname = smbXcli_conn_remote_name(cli->conn);
-       server_blob = smbXcli_conn_server_gss_blob(cli->conn);
-
-       DEBUG(3,("Doing spnego session setup (blob length=%lu)\n",
-                (unsigned long)server_blob->length));
-
-       /* the server might not even do spnego */
-       if (server_blob->length == 0) {
-               DEBUG(3,("server didn't supply a full spnego negprot\n"));
-               goto ntlmssp;
-       }
 
-#if 0
-       file_save("negprot.dat", cli->secblob.data, cli->secblob.length);
-#endif
+       target_service = "cifs";
+       target_hostname = smbXcli_conn_remote_name(cli->conn);
 
-       /* 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);
+       status = cli_session_creds_prepare_krb5(cli, creds);
+       if (tevent_req_nterror(req, status)) {
                return tevent_req_post(req, ev);
        }
 
-       /* make sure the server understands kerberos */
-       for (i=0;OIDs[i];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]);
-       }
-
-       DEBUG(3,("got principal=%s\n", principal ? principal : "<null>"));
-
-#ifdef HAVE_KRB5
-       /* If password is set we reauthenticate to kerberos server
-        * and do not store results */
-
-       if (user && *user && cli->got_kerberos_mechanism && cli->use_kerberos) {
-               char *tmp;
-
-               tmp = cli_session_setup_get_principal(
-                       talloc_tos(), principal, state->target_hostname, dest_realm);
-               TALLOC_FREE(principal);
-               principal = tmp;
-
-               if (pass && *pass) {
-                       int ret;
-
-                       use_in_memory_ccache();
-                       ret = kerberos_kinit_password(user, pass, 0 /* no time correction for now */, NULL);
-
-                       if (ret){
-                               DEBUG(0, ("Kinit for %s to access %s failed: %s\n", user, principal, error_message(ret)));
-                               TALLOC_FREE(principal);
-                               if (cli->fallback_after_kerberos)
-                                       goto ntlmssp;
-                               state->result = ADS_ERROR_KRB5(ret);
-                               tevent_req_done(req);
-                               return tevent_req_post(req, ev);
-                       }
-               }
-
-               if (principal) {
-                       subreq = cli_session_setup_gensec_send(
-                               state, ev, cli,
-                               state->account, pass, user_domain,
-                               CRED_MUST_USE_KERBEROS,
-                               "cifs", state->target_hostname, principal);
-                       if (tevent_req_nomem(subreq, req)) {
-                               return tevent_req_post(req, ev);
-                       }
-                       tevent_req_set_callback(
-                               subreq, cli_session_setup_spnego_done_krb,
-                               req);
-                       return req;
-               }
-       }
-#endif
+       DBG_INFO("Connect to %s as %s using SPNEGO\n",
+                target_hostname,
+                cli_credentials_get_principal(creds, talloc_tos()));
 
-ntlmssp:
-       subreq = cli_session_setup_gensec_send(
-               state, state->ev, state->cli,
-               state->account, state->pass, state->user_domain,
-               CRED_DONT_USE_KERBEROS,
-               "cifs", state->target_hostname, NULL);
+       subreq = cli_session_setup_gensec_send(state, ev, cli, creds,
+                                              target_service, target_hostname);
        if (tevent_req_nomem(subreq, req)) {
                return tevent_req_post(req, ev);
        }
        tevent_req_set_callback(
-               subreq, cli_session_setup_spnego_done_ntlmssp, req);
+               subreq, cli_session_setup_spnego_done, req);
        return req;
 }
 
-#ifdef HAVE_KRB5
-static void cli_session_setup_spnego_done_krb(struct tevent_req *subreq)
+static void cli_session_setup_spnego_done(struct tevent_req *subreq)
 {
        struct tevent_req *req = tevent_req_callback_data(
                subreq, struct tevent_req);
-       struct cli_session_setup_spnego_state *state = tevent_req_data(
-               req, struct cli_session_setup_spnego_state);
        NTSTATUS status;
 
        status = cli_session_setup_gensec_recv(subreq);
        TALLOC_FREE(subreq);
-       state->result = ADS_ERROR_NT(status);
-
-       if (ADS_ERR_OK(state->result) ||
-           !state->cli->fallback_after_kerberos) {
-               tevent_req_done(req);
-               return;
-       }
-
-       subreq = cli_session_setup_gensec_send(
-               state, state->ev, state->cli,
-               state->account, state->pass, state->user_domain,
-               CRED_DONT_USE_KERBEROS,
-               "cifs", state->target_hostname, NULL);
-       if (tevent_req_nomem(subreq, req)) {
+       if (tevent_req_nterror(req, status)) {
                return;
        }
-       tevent_req_set_callback(subreq, cli_session_setup_spnego_done_ntlmssp,
-                               req);
-}
-#endif
-
-static void cli_session_setup_spnego_done_ntlmssp(struct tevent_req *subreq)
-{
-       struct tevent_req *req = tevent_req_callback_data(
-               subreq, struct tevent_req);
-       struct cli_session_setup_spnego_state *state = tevent_req_data(
-               req, struct cli_session_setup_spnego_state);
-       NTSTATUS status;
 
-       status = cli_session_setup_gensec_recv(subreq);
-       TALLOC_FREE(subreq);
-       state->result = ADS_ERROR_NT(status);
        tevent_req_done(req);
 }
 
@@ -1944,19 +1370,52 @@ static ADS_STATUS cli_session_setup_spnego_recv(struct tevent_req *req)
 {
        struct cli_session_setup_spnego_state *state = tevent_req_data(
                req, struct cli_session_setup_spnego_state);
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               state->result = ADS_ERROR_NT(status);
+       }
 
        return state->result;
 }
 
-struct cli_session_setup_state {
-       uint8_t dummy;
+struct cli_session_setup_creds_state {
+       struct cli_state *cli;
+       DATA_BLOB apassword_blob;
+       DATA_BLOB upassword_blob;
+       DATA_BLOB lm_session_key;
+       DATA_BLOB session_key;
+       char *out_native_os;
+       char *out_native_lm;
+       char *out_primary_domain;
 };
 
-static void cli_session_setup_done_lanman2(struct tevent_req *subreq);
-static void cli_session_setup_done_spnego(struct tevent_req *subreq);
-static void cli_session_setup_done_guest(struct tevent_req *subreq);
-static void cli_session_setup_done_plain(struct tevent_req *subreq);
-static void cli_session_setup_done_nt1(struct tevent_req *subreq);
+static void cli_session_setup_creds_cleanup(struct tevent_req *req,
+                                           enum tevent_req_state req_state)
+{
+       struct cli_session_setup_creds_state *state = tevent_req_data(
+               req, struct cli_session_setup_creds_state);
+
+       if (req_state != TEVENT_REQ_RECEIVED) {
+               return;
+       }
+
+       /*
+        * We only call data_blob_clear() as
+        * some of the blobs point to the same memory.
+        *
+        * We let the talloc hierarchy free the memory.
+        */
+       data_blob_clear(&state->apassword_blob);
+       data_blob_clear(&state->upassword_blob);
+       data_blob_clear(&state->lm_session_key);
+       data_blob_clear(&state->session_key);
+       ZERO_STRUCTP(state);
+}
+
+static void cli_session_setup_creds_done_spnego(struct tevent_req *subreq);
+static void cli_session_setup_creds_done_nt1(struct tevent_req *subreq);
+static void cli_session_setup_creds_done_lm21(struct tevent_req *subreq);
 
 /****************************************************************************
  Send a session setup. The username and workgroup is in UNIX character
@@ -1964,204 +1423,301 @@ static void cli_session_setup_done_nt1(struct tevent_req *subreq);
  password is in plaintext, the same should be done.
 ****************************************************************************/
 
-struct tevent_req *cli_session_setup_send(TALLOC_CTX *mem_ctx,
-                                         struct tevent_context *ev,
-                                         struct cli_state *cli,
-                                         const char *user,
-                                         const char *pass, int passlen,
-                                         const char *ntpass, int ntpasslen,
-                                         const char *workgroup)
+struct tevent_req *cli_session_setup_creds_send(TALLOC_CTX *mem_ctx,
+                                       struct tevent_context *ev,
+                                       struct cli_state *cli,
+                                       struct cli_credentials *creds)
 {
        struct tevent_req *req, *subreq;
-       struct cli_session_setup_state *state;
-       char *p;
-       char *user2;
+       struct cli_session_setup_creds_state *state;
        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;
+       DATA_BLOB challenge = data_blob_null;
+       uint16_t in_buf_size = 0;
+       uint16_t in_mpx_max = 0;
+       uint16_t in_vc_num = 0;
+       uint32_t in_sess_key = 0;
+       const char *in_native_os = NULL;
+       const char *in_native_lm = NULL;
+       NTSTATUS status;
 
        req = tevent_req_create(mem_ctx, &state,
-                               struct cli_session_setup_state);
+                               struct cli_session_setup_creds_state);
        if (req == NULL) {
                return NULL;
        }
+       state->cli = cli;
 
-       if (user) {
-               user2 = talloc_strdup(state, user);
-       } else {
-               user2 = talloc_strdup(state, "");
-       }
-       if (user2 == NULL) {
-               tevent_req_oom(req);
-               return tevent_req_post(req, ev);
-       }
+       tevent_req_set_cleanup_fn(req, cli_session_setup_creds_cleanup);
 
-       if (!workgroup) {
-               workgroup = "";
-       }
+       krb5_state = cli_credentials_get_kerberos_state(creds);
+       gensec_features = cli_credentials_get_gensec_features(creds);
 
-       /* allow for workgroups as part of the username */
-       if ((p=strchr_m(user2,'\\')) || (p=strchr_m(user2,'/')) ||
-           (p=strchr_m(user2,*lp_winbind_separator()))) {
-               *p = 0;
-               user = p+1;
-               if (!strupper_m(user2)) {
-                       tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
-                       return tevent_req_post(req, ev);
-               }
-               workgroup = user2;
+       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 (smbXcli_conn_protocol(cli->conn) < PROTOCOL_LANMAN1) {
-               tevent_req_done(req);
-               return tevent_req_post(req, ev);
+       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 easier to understand (tridge) */
-
-       /* if its an older server then we have to use the older request format */
-
+       /*
+        * 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
+        * easier to understand (tridge).
+        */
        if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_NT1) {
-               if (!lp_client_lanman_auth() && passlen != 24 && (*pass)) {
-                       DEBUG(1, ("Server requested LM password but 'client lanman auth = no'"
-                                 " or 'client ntlmv2 auth = yes'\n"));
-                       tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
-                       return tevent_req_post(req, ev);
-               }
-
-               if ((sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) == 0 &&
-                   !lp_client_plaintext_auth() && (*pass)) {
-                       DEBUG(1, ("Server requested PLAINTEXT password but 'client plaintext auth = no'"
-                                 " or 'client ntlmv2 auth = yes'\n"));
-                       tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
-                       return tevent_req_post(req, ev);
-               }
-
-               subreq = cli_session_setup_lanman2_send(
-                       state, ev, cli, user, pass, passlen, workgroup);
-               if (tevent_req_nomem(subreq, req)) {
-                       return tevent_req_post(req, ev);
-               }
-               tevent_req_set_callback(subreq, cli_session_setup_done_lanman2,
-                                       req);
-               return req;
+               use_spnego = false;
+       } else if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+               use_spnego = true;
+       } else if (smb1cli_conn_capabilities(cli->conn) & CAP_EXTENDED_SECURITY) {
+               /*
+                * if the server supports extended security then use SPNEGO
+                * even for anonymous connections.
+                */
+               use_spnego = true;
+       } else {
+               use_spnego = false;
        }
 
-       if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+       if (use_spnego) {
                subreq = cli_session_setup_spnego_send(
-                       state, ev, cli, user, pass, workgroup);
+                       state, ev, cli, creds);
                if (tevent_req_nomem(subreq, req)) {
                        return tevent_req_post(req, ev);
                }
-               tevent_req_set_callback(subreq, cli_session_setup_done_spnego,
+               tevent_req_set_callback(subreq, cli_session_setup_creds_done_spnego,
                                        req);
                return req;
        }
 
-       /* if no user is supplied then we have to do an anonymous connection.
-          passwords are ignored */
-
-       if (!user || !*user) {
-               subreq = cli_session_setup_guest_send(state, ev, cli);
-               if (tevent_req_nomem(subreq, req)) {
-                       return tevent_req_post(req, ev);
-               }
-               tevent_req_set_callback(subreq, cli_session_setup_done_guest,
-                                       req);
-               return req;
+       if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_LANMAN1) {
+               /*
+                * SessionSetupAndX was introduced by LANMAN 1.0. So we skip
+                * this step against older servers.
+                */
+               tevent_req_done(req);
+               return tevent_req_post(req, ev);
        }
 
-       /* if the server is share level then send a plaintext null
-           password at this point. The password is sent in the tree
-           connect */
+       if (cli_credentials_is_anonymous(creds)) {
+               /*
+                * Do an anonymous session setup
+                */
+               goto non_spnego_creds_done;
+       }
 
        if ((sec_mode & NEGOTIATE_SECURITY_USER_LEVEL) == 0) {
-               subreq = cli_session_setup_plain_send(
-                       state, ev, cli, user, "", workgroup);
-               if (tevent_req_nomem(subreq, req)) {
-                       return tevent_req_post(req, ev);
-               }
-               tevent_req_set_callback(subreq, cli_session_setup_done_plain,
-                                       req);
-               return req;
+               /*
+                * Do an anonymous session setup,
+                * the password is passed via the tree connect.
+                */
+               goto non_spnego_creds_done;
+       }
+
+       cli_credentials_get_ntlm_username_domain(creds, state,
+                                                &username,
+                                                &domain);
+       if (tevent_req_nomem(username, req)) {
+               return tevent_req_post(req, ev);
+       }
+       if (tevent_req_nomem(domain, req)) {
+               return tevent_req_post(req, ev);
        }
 
-       /* if the server doesn't support encryption then we have to use 
-          plaintext. The second password is ignored */
+       DBG_INFO("Connect to %s as %s using NTLM\n", domain, username);
 
        if ((sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) == 0) {
-               if (!lp_client_plaintext_auth() && (*pass)) {
-                       DEBUG(1, ("Server requested PLAINTEXT password but 'client plaintext auth = no'"
-                                 " or 'client ntlmv2 auth = yes'\n"));
+               bool use_unicode = smbXcli_conn_use_unicode(cli->conn);
+               uint8_t *bytes = NULL;
+               size_t bytes_len = 0;
+               const char *pw = cli_credentials_get_password(creds);
+               size_t pw_len = 0;
+
+               if (pw == NULL) {
+                       pw = "";
+               }
+               pw_len = strlen(pw) + 1;
+
+               if (!lp_client_plaintext_auth()) {
+                       DEBUG(1, ("Server requested PLAINTEXT password but "
+                                 "'client plaintext auth = no'\n"));
                        tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
                        return tevent_req_post(req, ev);
                }
-               subreq = cli_session_setup_plain_send(
-                       state, ev, cli, user, pass, workgroup);
-               if (tevent_req_nomem(subreq, req)) {
+
+               bytes = talloc_array(state, uint8_t, 0);
+               bytes = trans2_bytes_push_str(bytes, use_unicode,
+                                             pw, pw_len, &bytes_len);
+               if (tevent_req_nomem(bytes, req)) {
                        return tevent_req_post(req, ev);
                }
-               tevent_req_set_callback(subreq, cli_session_setup_done_plain,
-                                       req);
-               return req;
+
+               if (use_unicode) {
+                       /*
+                        * CAP_UNICODE, can only be negotiated by NT1.
+                        */
+                       state->upassword_blob = data_blob_const(bytes,
+                                                               bytes_len);
+               } else {
+                       state->apassword_blob = data_blob_const(bytes,
+                                                               bytes_len);
+               }
+
+               goto non_spnego_creds_done;
        }
 
-       /* if the server supports extended security then use SPNEGO */
+       challenge = data_blob_const(smb1cli_conn_server_challenge(cli->conn), 8);
 
-       if (smb1cli_conn_capabilities(cli->conn) & CAP_EXTENDED_SECURITY) {
-               subreq = cli_session_setup_spnego_send(
-                       state, ev, cli, user, pass, workgroup);
-               if (tevent_req_nomem(subreq, req)) {
-                       return tevent_req_post(req, ev);
-               }
-               tevent_req_set_callback(subreq, cli_session_setup_done_spnego,
-                                       req);
-               return req;
-       } else {
-               /* otherwise do a NT1 style session setup */
+       if (smbXcli_conn_protocol(cli->conn) == PROTOCOL_NT1) {
                if (lp_client_ntlmv2_auth() && lp_client_use_spnego()) {
                        /*
-                        * Don't send an NTLMv2 response without NTLMSSP
-                        * if we want to use spnego support
+                        * Don't send an NTLMv2 response without NTLMSSP if we
+                        * want to use spnego support.
                         */
                        DEBUG(1, ("Server does not support EXTENDED_SECURITY "
-                                 " but 'client use spnego = yes"
-                                 " and 'client ntlmv2 auth = yes'\n"));
+                                 " but 'client use spnego = yes'"
+                                 " and 'client ntlmv2 auth = yes' is set\n"));
+                       tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
+                       return tevent_req_post(req, ev);
+               }
+
+               if (lp_client_ntlmv2_auth()) {
+                       flags |= CLI_CRED_NTLMv2_AUTH;
+
+                       /*
+                        * note that the 'domain' here is a best
+                        * guess - we don't know the server's domain
+                        * at this point. Windows clients also don't
+                        * use hostname...
+                        */
+                       target_info = NTLMv2_generate_names_blob(state,
+                                                                NULL,
+                                                                domain);
+                       if (tevent_req_nomem(target_info.data, req)) {
+                               return tevent_req_post(req, ev);
+                       }
+               } else {
+                       flags |= CLI_CRED_NTLM_AUTH;
+                       if (lp_client_lanman_auth()) {
+                               flags |= CLI_CRED_LANMAN_AUTH;
+                       }
+               }
+       } else {
+               if (!lp_client_lanman_auth()) {
+                       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);
                }
 
-               subreq = cli_session_setup_nt1_send(
-                       state, ev, cli, user, pass, passlen, ntpass, ntpasslen,
-                       workgroup);
+               flags |= CLI_CRED_LANMAN_AUTH;
+       }
+
+       status = cli_credentials_get_ntlm_response(creds, state, &flags,
+                                                  challenge, NULL,
+                                                  target_info,
+                                                  &state->apassword_blob,
+                                                  &state->upassword_blob,
+                                                  &state->lm_session_key,
+                                                  &state->session_key);
+       if (tevent_req_nterror(req, status)) {
+               return tevent_req_post(req, ev);
+       }
+
+non_spnego_creds_done:
+
+       in_buf_size = CLI_BUFFER_SIZE;
+       in_mpx_max = smbXcli_conn_max_requests(cli->conn);
+       in_vc_num = cli_state_get_vc_num(cli);
+       in_sess_key = smb1cli_conn_server_session_key(cli->conn);
+       in_native_os = "Unix";
+       in_native_lm = "Samba";
+
+       if (smbXcli_conn_protocol(cli->conn) == PROTOCOL_NT1) {
+               uint32_t in_capabilities = 0;
+
+               in_capabilities = cli_session_setup_capabilities(cli, 0);
+
+               /*
+                * For now we keep the same values as before,
+                * we may remove these in a separate commit later.
+                */
+               in_mpx_max = 2;
+
+               subreq = smb1cli_session_setup_nt1_send(state, ev,
+                                                       cli->conn,
+                                                       cli->timeout,
+                                                       cli->smb1.pid,
+                                                       cli->smb1.session,
+                                                       in_buf_size,
+                                                       in_mpx_max,
+                                                       in_vc_num,
+                                                       in_sess_key,
+                                                       username,
+                                                       domain,
+                                                       state->apassword_blob,
+                                                       state->upassword_blob,
+                                                       in_capabilities,
+                                                       in_native_os,
+                                                       in_native_lm);
                if (tevent_req_nomem(subreq, req)) {
                        return tevent_req_post(req, ev);
                }
-               tevent_req_set_callback(subreq, cli_session_setup_done_nt1,
+               tevent_req_set_callback(subreq, cli_session_setup_creds_done_nt1,
                                        req);
                return req;
        }
 
-       tevent_req_done(req);
-       return tevent_req_post(req, ev);
-}
-
-static void cli_session_setup_done_lanman2(struct tevent_req *subreq)
-{
-       struct tevent_req *req = tevent_req_callback_data(
-               subreq, struct tevent_req);
-       NTSTATUS status;
-
-       status = cli_session_setup_lanman2_recv(subreq);
-       TALLOC_FREE(subreq);
-       if (!NT_STATUS_IS_OK(status)) {
-               tevent_req_nterror(req, status);
-               return;
+       /*
+        * For now we keep the same values as before,
+        * we may remove these in a separate commit later.
+        */
+       in_mpx_max = 2;
+       in_vc_num = 1;
+
+       subreq = smb1cli_session_setup_lm21_send(state, ev,
+                                                cli->conn,
+                                                cli->timeout,
+                                                cli->smb1.pid,
+                                                cli->smb1.session,
+                                                in_buf_size,
+                                                in_mpx_max,
+                                                in_vc_num,
+                                                in_sess_key,
+                                                username,
+                                                domain,
+                                                state->apassword_blob,
+                                                in_native_os,
+                                                in_native_lm);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
        }
-       tevent_req_done(req);
+       tevent_req_set_callback(subreq, cli_session_setup_creds_done_lm21,
+                               req);
+       return req;
 }
 
-static void cli_session_setup_done_spnego(struct tevent_req *subreq)
+static void cli_session_setup_creds_done_spnego(struct tevent_req *subreq)
 {
        struct tevent_req *req = tevent_req_callback_data(
                subreq, struct tevent_req);
@@ -2177,63 +1733,99 @@ static void cli_session_setup_done_spnego(struct tevent_req *subreq)
        tevent_req_done(req);
 }
 
-static void cli_session_setup_done_guest(struct tevent_req *subreq)
+static void cli_session_setup_creds_done_nt1(struct tevent_req *subreq)
 {
        struct tevent_req *req = tevent_req_callback_data(
                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 = cli_session_setup_guest_recv(subreq);
+       struct iovec *recv_iov = NULL;
+       const uint8_t *inbuf = NULL;
+       bool ok;
+
+       status = smb1cli_session_setup_nt1_recv(subreq, state,
+                                               &recv_iov,
+                                               &inbuf,
+                                               &state->out_native_os,
+                                               &state->out_native_lm,
+                                               &state->out_primary_domain);
        TALLOC_FREE(subreq);
        if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(3, ("NT1 login failed: %s\n", nt_errstr(status)));
                tevent_req_nterror(req, status);
                return;
        }
-       tevent_req_done(req);
-}
-
-static void cli_session_setup_done_plain(struct tevent_req *subreq)
-{
-       struct tevent_req *req = tevent_req_callback_data(
-               subreq, struct tevent_req);
-       NTSTATUS status;
 
-       status = cli_session_setup_plain_recv(subreq);
-       TALLOC_FREE(subreq);
-       if (!NT_STATUS_IS_OK(status)) {
-               tevent_req_nterror(req, status);
+       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,
+                                          state->session_key,
+                                          state->upassword_blob);
+       if (ok) {
+               ok = smb1cli_conn_check_signing(cli->conn, inbuf, 1);
+               if (!ok) {
+                       tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
+                       return;
+               }
+       }
+
+       if (state->session_key.data) {
+               struct smbXcli_session *session = cli->smb1.session;
+
+               status = smb1cli_session_set_session_key(session,
+                                                        state->session_key);
+               if (tevent_req_nterror(req, status)) {
+                       return;
+               }
+       }
+
        tevent_req_done(req);
 }
 
-static void cli_session_setup_done_nt1(struct tevent_req *subreq)
+static void cli_session_setup_creds_done_lm21(struct tevent_req *subreq)
 {
        struct tevent_req *req = tevent_req_callback_data(
                subreq, struct tevent_req);
+       struct cli_session_setup_creds_state *state = tevent_req_data(
+               req, struct cli_session_setup_creds_state);
        NTSTATUS status;
 
-       status = cli_session_setup_nt1_recv(subreq);
+       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)) {
-               DEBUG(3, ("cli_session_setup: NT1 session setup "
-                         "failed: %s\n", nt_errstr(status)));
+               DEBUG(3, ("LM21 login failed: %s\n", nt_errstr(status)));
                tevent_req_nterror(req, status);
                return;
        }
+
+       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);
 }
 
-NTSTATUS cli_session_setup_recv(struct tevent_req *req)
+NTSTATUS cli_session_setup_creds_recv(struct tevent_req *req)
 {
        return tevent_req_simple_recv_ntstatus(req);
 }
 
-NTSTATUS cli_session_setup(struct cli_state *cli,
-                          const char *user,
-                          const char *pass, int passlen,
-                          const char *ntpass, int ntpasslen,
-                          const char *workgroup)
+NTSTATUS cli_session_setup_creds(struct cli_state *cli,
+                                struct cli_credentials *creds)
 {
        struct tevent_context *ev;
        struct tevent_req *req;
@@ -2246,20 +1838,38 @@ NTSTATUS cli_session_setup(struct cli_state *cli,
        if (ev == NULL) {
                goto fail;
        }
-       req = cli_session_setup_send(ev, ev, cli, user, pass, passlen,
-                                    ntpass, ntpasslen, workgroup);
+       req = cli_session_setup_creds_send(ev, ev, cli, creds);
        if (req == NULL) {
                goto fail;
        }
        if (!tevent_req_poll_ntstatus(req, ev, &status)) {
                goto fail;
        }
-       status = cli_session_setup_recv(req);
+       status = cli_session_setup_creds_recv(req);
  fail:
        TALLOC_FREE(ev);
        return status;
 }
 
+NTSTATUS cli_session_setup_anon(struct cli_state *cli)
+{
+       NTSTATUS status;
+       struct cli_credentials *creds = NULL;
+
+       creds = cli_credentials_init_anon(cli);
+       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.
 *****************************************************************************/
@@ -2288,7 +1898,7 @@ static struct tevent_req *cli_ulogoff_send(TALLOC_CTX *mem_ctx,
        SCVAL(state->vwv+1, 0, 0);
        SSVAL(state->vwv+2, 0, 0);
 
-       subreq = cli_smb_send(state, ev, cli, SMBulogoffX, 0, 2, state->vwv,
+       subreq = cli_smb_send(state, ev, cli, SMBulogoffX, 0, 0, 2, state->vwv,
                              0, NULL);
        if (tevent_req_nomem(subreq, req)) {
                return tevent_req_post(req, ev);
@@ -2394,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;
@@ -2506,7 +2123,7 @@ struct tevent_req *cli_tcon_andx_create(TALLOC_CTX *mem_ctx,
        state->bytes.iov_base = (void *)bytes;
        state->bytes.iov_len = talloc_get_size(bytes);
 
-       subreq = cli_smb_req_create(state, ev, cli, SMBtconX, 0, 4, vwv,
+       subreq = cli_smb_req_create(state, ev, cli, SMBtconX, 0, 0, 4, vwv,
                                    1, &state->bytes);
        if (subreq == NULL) {
                TALLOC_FREE(req);
@@ -2677,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);
@@ -2697,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);
@@ -2785,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;
@@ -2798,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;
        }
@@ -2811,6 +2435,19 @@ 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.
 ****************************************************************************/
@@ -2834,7 +2471,7 @@ static struct tevent_req *cli_tdis_send(TALLOC_CTX *mem_ctx,
        }
        state->cli = cli;
 
-       subreq = cli_smb_send(state, ev, cli, SMBtdis, 0, 0, NULL, 0, NULL);
+       subreq = cli_smb_send(state, ev, cli, SMBtdis, 0, 0, 0, NULL, 0, NULL);
        if (tevent_req_nomem(subreq, req)) {
                return tevent_req_post(req, ev);
        }
@@ -2856,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);
 }
 
@@ -2872,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)) {
@@ -2920,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;
@@ -2931,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)) {
 
                /*
@@ -3065,11 +2692,15 @@ static struct tevent_req *cli_connect_nb_send(
                }
 
                state->desthost = host;
-       } else {
+       } else if (dest_ss != NULL) {
                state->desthost = print_canonical_sockaddr(state, dest_ss);
                if (tevent_req_nomem(state->desthost, req)) {
                        return tevent_req_post(req, ev);
                }
+       } else {
+               /* No host or dest_ss given. Error out. */
+               tevent_req_error(req, EINVAL);
+               return tevent_req_post(req, ev);
        }
 
        subreq = cli_connect_sock_send(state, ev, host, name_type, dest_ss,
@@ -3097,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);
@@ -3181,111 +2812,535 @@ static struct tevent_req *cli_start_connection_send(
        if (req == NULL) {
                return NULL;
        }
-       state->ev = ev;
+       state->ev = ev;
+
+       if (signing_state == SMB_SIGNING_IPC_DEFAULT) {
+               state->min_protocol = lp_client_ipc_min_protocol();
+               state->max_protocol = lp_client_ipc_max_protocol();
+       } else {
+               state->min_protocol = lp_client_min_protocol();
+               state->max_protocol = lp_client_max_protocol();
+       }
+
+       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)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, cli_start_connection_connected, req);
+       return req;
+}
+
+static void cli_start_connection_connected(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct cli_start_connection_state *state = tevent_req_data(
+               req, struct cli_start_connection_state);
+       NTSTATUS status;
+
+       status = cli_connect_nb_recv(subreq, &state->cli);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+
+       subreq = smbXcli_negprot_send(state, state->ev, state->cli->conn,
+                                     state->cli->timeout,
+                                     state->min_protocol,
+                                     state->max_protocol,
+                                     WINDOWS_CLIENT_PURE_SMB2_NEGPROT_INITIAL_CREDIT_ASK);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq, cli_start_connection_done, req);
+}
+
+static void cli_start_connection_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct cli_start_connection_state *state = tevent_req_data(
+               req, struct cli_start_connection_state);
+       NTSTATUS status;
+
+       status = smbXcli_negprot_recv(subreq);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+
+       if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
+               /* Ensure we ask for some initial credits. */
+               smb2cli_conn_set_max_credits(state->cli->conn,
+                                            DEFAULT_SMB2_MAX_CREDITS);
+       }
+
+       tevent_req_done(req);
+}
+
+static NTSTATUS cli_start_connection_recv(struct tevent_req *req,
+                                         struct cli_state **output_cli)
+{
+       struct cli_start_connection_state *state = tevent_req_data(
+               req, struct cli_start_connection_state);
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               return status;
+       }
+       *output_cli = state->cli;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS cli_start_connection(struct cli_state **output_cli, 
+                             const char *my_name, 
+                             const char *dest_host, 
+                             const struct sockaddr_storage *dest_ss, int port,
+                             int signing_state, int flags)
+{
+       struct tevent_context *ev;
+       struct tevent_req *req;
+       NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+       ev = samba_tevent_context_init(talloc_tos());
+       if (ev == NULL) {
+               goto fail;
+       }
+       req = cli_start_connection_send(ev, ev, my_name, dest_host, dest_ss,
+                                       port, signing_state, flags);
+       if (req == NULL) {
+               goto fail;
+       }
+       if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+               goto fail;
+       }
+       status = cli_start_connection_recv(req, output_cli);
+fail:
+       TALLOC_FREE(ev);
+       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);
+       }
 
-       if (signing_state == SMB_SIGNING_IPC_DEFAULT) {
-               state->min_protocol = lp_client_ipc_min_protocol();
-               state->max_protocol = lp_client_ipc_max_protocol();
-       } else {
-               state->min_protocol = lp_client_min_protocol();
-               state->max_protocol = lp_client_max_protocol();
+       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 = cli_connect_nb_send(state, ev, dest_host, dest_ss, port,
-                                    0x20, my_name, signing_state, flags);
+       subreq = gensec_update_send(state, state->ev,
+                       state->es->gensec_security,
+                       state->blob_in);
        if (tevent_req_nomem(subreq, req)) {
-               return tevent_req_post(req, ev);
+               return;
        }
-       tevent_req_set_callback(subreq, cli_start_connection_connected, req);
-       return req;
+       tevent_req_set_callback(subreq, cli_smb1_setup_encryption_local_done, req);
 }
 
-static void cli_start_connection_connected(struct tevent_req *subreq)
+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_start_connection_state *state = tevent_req_data(
-               req, struct cli_start_connection_state);
+       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_connect_nb_recv(subreq, &state->cli);
+       status = gensec_update_recv(subreq, state, &state->blob_out);
        TALLOC_FREE(subreq);
-       if (tevent_req_nterror(req, status)) {
+       state->blob_in = data_blob_null;
+       if (!NT_STATUS_IS_OK(status) &&
+           !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED))
+       {
+               tevent_req_nterror(req, status);
                return;
        }
 
-       subreq = smbXcli_negprot_send(state, state->ev, state->cli->conn,
-                                     state->cli->timeout,
-                                     state->min_protocol,
-                                     state->max_protocol);
+       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_start_connection_done, req);
+       tevent_req_set_callback(subreq,
+                               cli_smb1_setup_encryption_remote_done,
+                               req);
 }
 
-static void cli_start_connection_done(struct tevent_req *subreq)
+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_start_connection_state *state = tevent_req_data(
-               req, struct cli_start_connection_state);
+       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 = smbXcli_negprot_recv(subreq);
+       status = cli_smb1_setup_encryption_blob_recv(subreq, state,
+                                                    &state->blob_in,
+                                                    &state->es->enc_ctx_num);
        TALLOC_FREE(subreq);
-       if (tevent_req_nterror(req, status)) {
+       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;
        }
 
-       if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
-               /* Ensure we ask for some initial credits. */
-               smb2cli_conn_set_max_credits(state->cli->conn,
-                                            DEFAULT_SMB2_MAX_CREDITS);
+       /*
+        * 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;
        }
 
-       tevent_req_done(req);
+       if (state->local_ready && state->remote_ready) {
+               cli_smb1_setup_encryption_ready(req);
+               return;
+       }
+
+       cli_smb1_setup_encryption_local_next(req);
 }
 
-static NTSTATUS cli_start_connection_recv(struct tevent_req *req,
-                                         struct cli_state **output_cli)
+static void cli_smb1_setup_encryption_ready(struct tevent_req *req)
 {
-       struct cli_start_connection_state *state = tevent_req_data(
-               req, struct cli_start_connection_state);
-       NTSTATUS status;
+       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 (tevent_req_is_nterror(req, &status)) {
-               return status;
+       if (state->blob_in.length != 0) {
+               tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+               return;
        }
-       *output_cli = state->cli;
 
-       return NT_STATUS_OK;
+       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);
 }
 
-NTSTATUS cli_start_connection(struct cli_state **output_cli, 
-                             const char *my_name, 
-                             const char *dest_host, 
-                             const struct sockaddr_storage *dest_ss, int port,
-                             int signing_state, int flags)
+static NTSTATUS cli_smb1_setup_encryption_recv(struct tevent_req *req)
 {
-       struct tevent_context *ev;
-       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_start_connection_send(ev, ev, my_name, dest_host, dest_ss,
-                                       port, signing_state, flags);
+       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_start_connection_recv(req, output_cli);
-fail:
+       status = cli_smb1_setup_encryption_recv(req);
+ fail:
        TALLOC_FREE(ev);
        return status;
 }
@@ -3298,86 +3353,102 @@ 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;
+}
+
+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_send(
+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;
 
        status = cli_start_connection_recv(subreq, &state->cli);
@@ -3385,25 +3456,35 @@ static void cli_full_connection_started(struct tevent_req *subreq)
        if (tevent_req_nterror(req, status)) {
                return;
        }
-       subreq = cli_session_setup_send(
-               state, state->ev, state->cli, state->user,
-               state->password, state->pw_len, state->password, state->pw_len,
-               state->domain);
+
+       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)) {
                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_recv(subreq);
+       status = cli_session_setup_creds_recv(subreq);
        TALLOC_FREE(subreq);
 
        if (!NT_STATUS_IS_OK(status) &&
@@ -3411,14 +3492,12 @@ static void cli_full_connection_sess_set_up(struct tevent_req *subreq)
 
                state->flags &= ~CLI_FULL_CONNECTION_ANONYMOUS_FALLBACK;
 
-               subreq = cli_session_setup_send(
-                       state, state->ev, state->cli, "", "", 0, "", 0,
-                       state->domain);
-               if (tevent_req_nomem(subreq, req)) {
+               state->creds = cli_credentials_init_anon(state);
+               if (tevent_req_nomem(state->creds, req)) {
                        return;
                }
-               tevent_req_set_callback(
-                       subreq, cli_full_connection_sess_set_up, req);
+
+               cli_full_connection_creds_sess_start(req);
                return;
        }
 
@@ -3426,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);
@@ -3456,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)) {
@@ -3471,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;
@@ -3488,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.
 ****************************************************************************/
@@ -3532,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),
@@ -3547,7 +3708,7 @@ static struct tevent_req *cli_raw_tcon_send(
                return tevent_req_post(req, ev);
        }
 
-       subreq = cli_smb_send(state, ev, cli, SMBtcon, 0, 0, NULL,
+       subreq = cli_smb_send(state, ev, cli, SMBtcon, 0, 0, 0, NULL,
                              talloc_get_size(bytes), bytes);
        if (tevent_req_nomem(subreq, req)) {
                return tevent_req_post(req, ev);
@@ -3623,14 +3784,16 @@ struct cli_state *get_ipc_connect(char *server,
        NTSTATUS nt_status;
        uint32_t flags = CLI_FULL_CONNECTION_ANONYMOUS_FALLBACK;
 
-       if (user_info->use_kerberos) {
+       if (get_cmdline_auth_info_use_kerberos(user_info)) {
                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", 
-                                       user_info->username ? user_info->username : "",
+                                       get_cmdline_auth_info_username(user_info),
                                        lp_workgroup(),
-                                       user_info->password ? user_info->password : "",
+                                       get_cmdline_auth_info_password(user_info),
                                        flags,
                                        SMB_SIGNING_DEFAULT);
 
@@ -3710,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;
-}