[GLUE] Rsync SAMBA_3_2_0 SVN r25598 in order to create the v3-2-test branch.
[sfrench/samba-autobuild/.git] / source3 / libsmb / cliconnect.c
index b140c772a24f054613f1fb4ca2d76cd186f22179..78cc63de50f8d2dcfdfa81275432378d7a0f3851 100644 (file)
@@ -6,7 +6,7 @@
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
-#define NO_SYSLOG
-
 #include "includes.h"
 
+extern pstring user_socket_options;
 
 static const struct {
        int prot;
@@ -34,24 +32,49 @@ static const struct {
        {PROTOCOL_LANMAN1,"LANMAN1.0"},
        {PROTOCOL_LANMAN2,"LM1.2X002"},
        {PROTOCOL_LANMAN2,"DOS LANMAN2.1"},
+       {PROTOCOL_LANMAN2,"LANMAN2.1"},
        {PROTOCOL_LANMAN2,"Samba"},
        {PROTOCOL_NT1,"NT LANMAN 1.0"},
        {PROTOCOL_NT1,"NT LM 0.12"},
        {-1,NULL}
 };
 
+/**
+ * Set the user session key for a connection
+ * @param cli The cli structure to add it too
+ * @param session_key The session key used.  (A copy of this is taken for the cli struct)
+ *
+ */
+
+static void cli_set_session_key (struct cli_state *cli, const DATA_BLOB session_key) 
+{
+       cli->user_session_key = data_blob(session_key.data, session_key.length);
+}
+
 /****************************************************************************
  Do an old lanman2 style session setup.
 ****************************************************************************/
 
-static BOOL cli_session_setup_lanman2(struct cli_state *cli, const char *user, 
-                                     const char *pass, size_t passlen, const char *workgroup)
+static NTSTATUS cli_session_setup_lanman2(struct cli_state *cli,
+                                         const char *user, 
+                                         const char *pass, size_t passlen,
+                                         const char *workgroup)
 {
+       DATA_BLOB session_key = data_blob_null;
+       DATA_BLOB lm_response = data_blob_null;
        fstring pword;
        char *p;
 
-       if (passlen > sizeof(pword)-1)
-               return False;
+       if (passlen > sizeof(pword)-1) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       /* LANMAN servers predate NT status codes and Unicode and ignore those 
+          smb flags so we must disable the corresponding default capabilities  
+          that would otherwise cause the Unicode and NT Status flags to be
+          set (and even returned by the server) */
+
+       cli->capabilities &= ~(CAP_UNICODE | CAP_STATUS32);
 
        /* if in share level security then don't send a password now */
        if (!(cli->sec_mode & NEGOTIATE_SECURITY_USER_LEVEL))
@@ -59,14 +82,18 @@ static BOOL cli_session_setup_lanman2(struct cli_state *cli, const char *user,
 
        if (passlen > 0 && (cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) && passlen != 24) {
                /* Encrypted mode needed, and non encrypted password supplied. */
-               passlen = 24;
-               SMBencrypt(pass,cli->secblob.data,(uchar *)pword);
+               lm_response = data_blob(NULL, 24);
+               if (!SMBencrypt(pass, cli->secblob.data,(uchar *)lm_response.data)) {
+                       DEBUG(1, ("Password is > 14 chars in length, and is therefore incompatible with Lanman authentication\n"));
+                       return NT_STATUS_ACCESS_DENIED;
+               }
        } else if ((cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) && passlen == 24) {
                /* Encrypted mode needed, and encrypted password supplied. */
-               memcpy(pword, pass, passlen);
+               lm_response = data_blob(pass, passlen);
        } else if (passlen > 0) {
                /* Plaintext mode needed, assume plaintext supplied. */
                passlen = clistr_push(cli, pword, pass, sizeof(pword), STR_TERMINATE);
+               lm_response = data_blob(pass, passlen);
        }
 
        /* send a session setup command */
@@ -80,31 +107,37 @@ static BOOL cli_session_setup_lanman2(struct cli_state *cli, const char *user,
        SSVAL(cli->outbuf,smb_vwv3,2);
        SSVAL(cli->outbuf,smb_vwv4,1);
        SIVAL(cli->outbuf,smb_vwv5,cli->sesskey);
-       SSVAL(cli->outbuf,smb_vwv7,passlen);
+       SSVAL(cli->outbuf,smb_vwv7,lm_response.length);
 
        p = smb_buf(cli->outbuf);
-       memcpy(p,pword,passlen);
-       p += passlen;
+       memcpy(p,lm_response.data,lm_response.length);
+       p += lm_response.length;
        p += clistr_push(cli, p, user, -1, STR_TERMINATE|STR_UPPER);
        p += clistr_push(cli, p, workgroup, -1, STR_TERMINATE|STR_UPPER);
        p += clistr_push(cli, p, "Unix", -1, STR_TERMINATE);
        p += clistr_push(cli, p, "Samba", -1, STR_TERMINATE);
        cli_setup_bcc(cli, p);
 
-       cli_send_smb(cli);
-       if (!cli_receive_smb(cli))
-               return False;
+       if (!cli_send_smb(cli) || !cli_receive_smb(cli)) {
+               return cli_nt_error(cli);
+       }
 
        show_msg(cli->inbuf);
 
-       if (cli_is_error(cli))
-               return False;
+       if (cli_is_error(cli)) {
+               return cli_nt_error(cli);
+       }
        
        /* use the returned vuid from now on */
        cli->vuid = SVAL(cli->inbuf,smb_uid);   
        fstrcpy(cli->user_name, user);
 
-       return True;
+       if (session_key.data) {
+               /* Have plaintext orginal */
+               cli_set_session_key(cli, session_key);
+       }
+
+       return NT_STATUS_OK;
 }
 
 /****************************************************************************
@@ -121,12 +154,7 @@ static uint32 cli_session_setup_capabilities(struct cli_state *cli)
        if (cli->use_level_II_oplocks)
                capabilities |= CAP_LEVEL_II_OPLOCKS;
 
-       if (cli->capabilities & CAP_UNICODE)
-               capabilities |= CAP_UNICODE;
-
-       if (cli->capabilities & CAP_LARGE_FILES)
-               capabilities |= CAP_LARGE_FILES;
-
+       capabilities |= (cli->capabilities & (CAP_UNICODE|CAP_LARGE_FILES|CAP_LARGE_READX|CAP_LARGE_WRITEX|CAP_DFS));
        return capabilities;
 }
 
@@ -134,11 +162,12 @@ static uint32 cli_session_setup_capabilities(struct cli_state *cli)
  Do a NT1 guest session setup.
 ****************************************************************************/
 
-static BOOL cli_session_setup_guest(struct cli_state *cli)
+static NTSTATUS cli_session_setup_guest(struct cli_state *cli)
 {
        char *p;
        uint32 capabilities = cli_session_setup_capabilities(cli);
 
+       memset(cli->outbuf, '\0', smb_size);
        set_message(cli->outbuf,13,0,True);
        SCVAL(cli->outbuf,smb_com,SMBsesssetupX);
        cli_setup_packet(cli);
@@ -158,14 +187,15 @@ static BOOL cli_session_setup_guest(struct cli_state *cli)
        p += clistr_push(cli, p, "Samba", -1, STR_TERMINATE);
        cli_setup_bcc(cli, p);
 
-       cli_send_smb(cli);
-       if (!cli_receive_smb(cli))
-             return False;
+       if (!cli_send_smb(cli) || !cli_receive_smb(cli)) {
+               return cli_nt_error(cli);
+       }
        
        show_msg(cli->inbuf);
        
-       if (cli_is_error(cli))
-               return False;
+       if (cli_is_error(cli)) {
+               return cli_nt_error(cli);
+       }
 
        cli->vuid = SVAL(cli->inbuf,smb_uid);
 
@@ -174,17 +204,22 @@ static BOOL cli_session_setup_guest(struct cli_state *cli)
        p += clistr_pull(cli, cli->server_type, p, sizeof(fstring), -1, STR_TERMINATE);
        p += clistr_pull(cli, cli->server_domain, p, sizeof(fstring), -1, STR_TERMINATE);
 
+       if (strstr(cli->server_type, "Samba")) {
+               cli->is_samba = True;
+       }
+
        fstrcpy(cli->user_name, "");
 
-       return True;
+       return NT_STATUS_OK;
 }
 
 /****************************************************************************
  Do a NT1 plaintext session setup.
 ****************************************************************************/
 
-static BOOL cli_session_setup_plaintext(struct cli_state *cli, const char *user, 
-                                       const char *pass, const char *workgroup)
+static NTSTATUS cli_session_setup_plaintext(struct cli_state *cli,
+                                           const char *user, const char *pass,
+                                           const char *workgroup)
 {
        uint32 capabilities = cli_session_setup_capabilities(cli);
        char *p;
@@ -192,6 +227,7 @@ static BOOL cli_session_setup_plaintext(struct cli_state *cli, const char *user,
        
        fstr_sprintf( lanman, "Samba %s", SAMBA_VERSION_STRING);
 
+       memset(cli->outbuf, '\0', smb_size);
        set_message(cli->outbuf,13,0,True);
        SCVAL(cli->outbuf,smb_com,SMBsesssetupX);
        cli_setup_packet(cli);
@@ -222,14 +258,15 @@ static BOOL cli_session_setup_plaintext(struct cli_state *cli, const char *user,
        p += clistr_push(cli, p, lanman, -1, STR_TERMINATE);
        cli_setup_bcc(cli, p);
 
-       cli_send_smb(cli);
-       if (!cli_receive_smb(cli))
-             return False;
+       if (!cli_send_smb(cli) || !cli_receive_smb(cli)) {
+               return cli_nt_error(cli);
+       }
        
        show_msg(cli->inbuf);
        
-       if (cli_is_error(cli))
-               return False;
+       if (cli_is_error(cli)) {
+               return cli_nt_error(cli);
+       }
 
        cli->vuid = SVAL(cli->inbuf,smb_uid);
        p = smb_buf(cli->inbuf);
@@ -238,19 +275,11 @@ static BOOL cli_session_setup_plaintext(struct cli_state *cli, const char *user,
        p += clistr_pull(cli, cli->server_domain, p, sizeof(fstring), -1, STR_TERMINATE);
        fstrcpy(cli->user_name, user);
 
-       return True;
-}
-
-/**
- * Set the user session key for a connection
- * @param cli The cli structure to add it too
- * @param session_key The session key used.  (A copy of this is taken for the cli struct)
- *
- */
+       if (strstr(cli->server_type, "Samba")) {
+               cli->is_samba = True;
+       }
 
-static void cli_set_session_key (struct cli_state *cli, const DATA_BLOB session_key) 
-{
-       cli->user_session_key = data_blob(session_key.data, session_key.length);
+       return NT_STATUS_OK;
 }
 
 /****************************************************************************
@@ -263,16 +292,16 @@ static void cli_set_session_key (struct cli_state *cli, const DATA_BLOB session_
    @param workgroup The user's domain.
 ****************************************************************************/
 
-static BOOL cli_session_setup_nt1(struct cli_state *cli, const char *user, 
-                                 const char *pass, size_t passlen,
-                                 const char *ntpass, size_t ntpasslen,
-                                 const char *workgroup)
+static NTSTATUS cli_session_setup_nt1(struct cli_state *cli, const char *user, 
+                                     const char *pass, size_t passlen,
+                                     const char *ntpass, size_t ntpasslen,
+                                     const char *workgroup)
 {
        uint32 capabilities = cli_session_setup_capabilities(cli);
-       DATA_BLOB lm_response = data_blob(NULL, 0);
-       DATA_BLOB nt_response = data_blob(NULL, 0);
-       DATA_BLOB session_key = data_blob(NULL, 0);
-       BOOL ret = False;
+       DATA_BLOB lm_response = data_blob_null;
+       DATA_BLOB nt_response = data_blob_null;
+       DATA_BLOB session_key = data_blob_null;
+       NTSTATUS result;
        char *p;
 
        if (passlen == 0) {
@@ -294,7 +323,7 @@ static BOOL cli_session_setup_nt1(struct cli_state *cli, const char *user,
                                              &lm_response, &nt_response, &session_key)) {
                                data_blob_free(&names_blob);
                                data_blob_free(&server_chal);
-                               return False;
+                               return NT_STATUS_ACCESS_DENIED;
                        }
                        data_blob_free(&names_blob);
                        data_blob_free(&server_chal);
@@ -303,22 +332,39 @@ static BOOL cli_session_setup_nt1(struct cli_state *cli, const char *user,
                        uchar nt_hash[16];
                        E_md4hash(pass, nt_hash);
 
+#ifdef LANMAN_ONLY
+                       nt_response = data_blob_null;
+#else
                        nt_response = data_blob(NULL, 24);
                        SMBNTencrypt(pass,cli->secblob.data,nt_response.data);
-
+#endif
                        /* non encrypted password supplied. Ignore ntpass. */
                        if (lp_client_lanman_auth()) {
                                lm_response = data_blob(NULL, 24);
-                               SMBencrypt(pass,cli->secblob.data, lm_response.data);
+                               if (!SMBencrypt(pass,cli->secblob.data, 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);
                        }
 
                        session_key = data_blob(NULL, 16);
+#ifdef LANMAN_ONLY
+                       E_deshash(pass, session_key.data);
+                       memset(&session_key.data[8], '\0', 8);
+#else
                        SMBsesskeygen_ntv1(nt_hash, NULL, session_key.data);
+#endif
                }
-               cli_simple_set_signing(cli, session_key, nt_response, 0); 
+#ifdef LANMAN_ONLY
+               cli_simple_set_signing(cli, session_key, lm_response); 
+#else
+               cli_simple_set_signing(cli, session_key, nt_response); 
+#endif
        } else {
                /* pre-encrypted password supplied.  Only used for 
                   security=server, can't do
@@ -351,20 +397,22 @@ static BOOL cli_session_setup_nt1(struct cli_state *cli, const char *user,
                memcpy(p,nt_response.data, nt_response.length); p += nt_response.length;
        }
        p += clistr_push(cli, p, user, -1, STR_TERMINATE);
-       p += clistr_push(cli, p, workgroup, -1, STR_TERMINATE);
+
+       /* Upper case here might help some NTLMv2 implementations */
+       p += clistr_push(cli, p, workgroup, -1, STR_TERMINATE|STR_UPPER);
        p += clistr_push(cli, p, "Unix", -1, STR_TERMINATE);
        p += clistr_push(cli, p, "Samba", -1, STR_TERMINATE);
        cli_setup_bcc(cli, p);
 
        if (!cli_send_smb(cli) || !cli_receive_smb(cli)) {
-               ret = False;
+               result = cli_nt_error(cli);
                goto end;
        }
 
        /* show_msg(cli->inbuf); */
 
        if (cli_is_error(cli)) {
-               ret = False;
+               result = cli_nt_error(cli);
                goto end;
        }
 
@@ -376,6 +424,10 @@ static BOOL cli_session_setup_nt1(struct cli_state *cli, const char *user,
        p += clistr_pull(cli, cli->server_type, p, sizeof(fstring), -1, STR_TERMINATE);
        p += clistr_pull(cli, cli->server_domain, p, sizeof(fstring), -1, STR_TERMINATE);
 
+       if (strstr(cli->server_type, "Samba")) {
+               cli->is_samba = True;
+       }
+
        fstrcpy(cli->user_name, user);
 
        if (session_key.data) {
@@ -383,14 +435,12 @@ static BOOL cli_session_setup_nt1(struct cli_state *cli, const char *user,
                cli_set_session_key(cli, session_key);
        }
 
-       ret = True;
+       result = NT_STATUS_OK;
 end:   
        data_blob_free(&lm_response);
        data_blob_free(&nt_response);
-
-       if (!ret)
-               data_blob_free(&session_key);
-       return ret;
+       data_blob_free(&session_key);
+       return result;
 }
 
 /****************************************************************************
@@ -434,7 +484,7 @@ static BOOL cli_session_setup_blob_send(struct cli_state *cli, DATA_BLOB blob)
 
 static DATA_BLOB cli_session_setup_blob_receive(struct cli_state *cli)
 {
-       DATA_BLOB blob2 = data_blob(NULL, 0);
+       DATA_BLOB blob2 = data_blob_null;
        char *p;
        size_t len;
 
@@ -466,19 +516,79 @@ static DATA_BLOB cli_session_setup_blob_receive(struct cli_state *cli)
 }
 
 #ifdef HAVE_KRB5
-
 /****************************************************************************
  Send a extended security session setup blob, returning a reply blob.
 ****************************************************************************/
 
-static DATA_BLOB cli_session_setup_blob(struct cli_state *cli, DATA_BLOB blob)
+/* The following is calculated from :
+ * (smb_size-4) = 35
+ * (smb_wcnt * 2) = 24 (smb_wcnt == 12 in cli_session_setup_blob_send() )
+ * (strlen("Unix") + 1 + strlen("Samba") + 1) * 2 = 22 (unicode strings at
+ * end of packet.
+ */
+
+#define BASE_SESSSETUP_BLOB_PACKET_SIZE (35 + 24 + 22)
+
+static BOOL cli_session_setup_blob(struct cli_state *cli, DATA_BLOB blob, DATA_BLOB session_key_krb5)
 {
-       DATA_BLOB blob2 = data_blob(NULL, 0);
-       if (!cli_session_setup_blob_send(cli, blob)) {
-               return blob2;
+       int32 remaining = blob.length;
+       int32 cur = 0;
+       DATA_BLOB send_blob = data_blob_null;
+       int32 max_blob_size = 0;
+       DATA_BLOB receive_blob = data_blob_null;
+
+       if (cli->max_xmit < BASE_SESSSETUP_BLOB_PACKET_SIZE + 1) {
+               DEBUG(0,("cli_session_setup_blob: cli->max_xmit too small "
+                       "(was %u, need minimum %u)\n",
+                       (unsigned int)cli->max_xmit,
+                       BASE_SESSSETUP_BLOB_PACKET_SIZE));
+               cli_set_nt_error(cli, NT_STATUS_INVALID_PARAMETER);
+               return False;
        }
-               
-       return cli_session_setup_blob_receive(cli);
+
+       max_blob_size = cli->max_xmit - BASE_SESSSETUP_BLOB_PACKET_SIZE;
+
+       while ( remaining > 0) {
+               if (remaining >= max_blob_size) {
+                       send_blob.length = max_blob_size;
+                       remaining -= max_blob_size;
+               } else {
+                       DATA_BLOB null_blob = data_blob_null;
+
+                       send_blob.length = remaining; 
+                        remaining = 0;
+
+                       /* This is the last packet in the sequence - turn signing on. */
+                       cli_simple_set_signing(cli, session_key_krb5, null_blob); 
+               }
+
+               send_blob.data =  &blob.data[cur];
+               cur += send_blob.length;
+
+               DEBUG(10, ("cli_session_setup_blob: Remaining (%u) sending (%u) current (%u)\n", 
+                       (unsigned int)remaining,
+                       (unsigned int)send_blob.length,
+                       (unsigned int)cur ));
+
+               if (!cli_session_setup_blob_send(cli, send_blob)) {
+                       DEBUG(0, ("cli_session_setup_blob: send failed\n"));
+                       return False;
+               }
+
+               receive_blob = cli_session_setup_blob_receive(cli);
+               data_blob_free(&receive_blob);
+
+               if (cli_is_error(cli) &&
+                               !NT_STATUS_EQUAL( cli_get_nt_error(cli), 
+                                       NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+                       DEBUG(0, ("cli_session_setup_blob: recieve failed (%s)\n",
+                               nt_errstr(cli_get_nt_error(cli)) ));
+                       cli->vuid = 0;
+                       return False;
+               }
+       }
+
+       return True;
 }
 
 /****************************************************************************
@@ -493,41 +603,44 @@ static void use_in_memory_ccache(void) {
  Do a spnego/kerberos encrypted session setup.
 ****************************************************************************/
 
-static NTSTATUS cli_session_setup_kerberos(struct cli_state *cli, const char *principal, const char *workgroup)
+static ADS_STATUS cli_session_setup_kerberos(struct cli_state *cli, const char *principal, const char *workgroup)
 {
-       DATA_BLOB blob2, negTokenTarg;
+       DATA_BLOB negTokenTarg;
        DATA_BLOB session_key_krb5;
-       DATA_BLOB null_blob = data_blob(NULL, 0);
-       
+       int rc;
+
        DEBUG(2,("Doing kerberos session setup\n"));
 
        /* generate the encapsulated kerberos5 ticket */
-       negTokenTarg = spnego_gen_negTokenTarg(principal, 0, &session_key_krb5);
+       rc = spnego_gen_negTokenTarg(principal, 0, &negTokenTarg, &session_key_krb5, 0, NULL);
 
-       if (!negTokenTarg.data)
-               return NT_STATUS_UNSUCCESSFUL;
+       if (rc) {
+               DEBUG(1, ("cli_session_setup_kerberos: spnego_gen_negTokenTarg failed: %s\n",
+                       error_message(rc)));
+               return ADS_ERROR_KRB5(rc);
+       }
 
 #if 0
        file_save("negTokenTarg.dat", negTokenTarg.data, negTokenTarg.length);
 #endif
 
-       cli_simple_set_signing(cli, session_key_krb5, null_blob, 0); 
-                       
-       blob2 = cli_session_setup_blob(cli, negTokenTarg);
-
-       /* we don't need this blob for kerberos */
-       data_blob_free(&blob2);
+       if (!cli_session_setup_blob(cli, negTokenTarg, session_key_krb5)) {
+               data_blob_free(&negTokenTarg);
+               data_blob_free(&session_key_krb5);
+               ADS_ERROR_NT(cli_nt_error(cli));
+       }
 
        cli_set_session_key(cli, session_key_krb5);
 
        data_blob_free(&negTokenTarg);
+       data_blob_free(&session_key_krb5);
 
        if (cli_is_error(cli)) {
                if (NT_STATUS_IS_OK(cli_nt_error(cli))) {
-                       return NT_STATUS_UNSUCCESSFUL;
+                       return ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
                }
        } 
-       return NT_STATUS_OK;
+       return ADS_ERROR_NT(cli_nt_error(cli));
 }
 #endif /* HAVE_KRB5 */
 
@@ -537,26 +650,27 @@ static NTSTATUS cli_session_setup_kerberos(struct cli_state *cli, const char *pr
 ****************************************************************************/
 
 static NTSTATUS cli_session_setup_ntlmssp(struct cli_state *cli, const char *user, 
-                                     const char *pass, const char *workgroup)
+                                         const char *pass, const char *domain)
 {
        struct ntlmssp_state *ntlmssp_state;
        NTSTATUS nt_status;
        int turn = 1;
        DATA_BLOB msg1;
-       DATA_BLOB blob;
-       DATA_BLOB blob_in = data_blob(NULL, 0);
-       DATA_BLOB blob_out;
+       DATA_BLOB blob = data_blob_null;
+       DATA_BLOB blob_in = data_blob_null;
+       DATA_BLOB blob_out = data_blob_null;
 
        cli_temp_set_signing(cli);
 
        if (!NT_STATUS_IS_OK(nt_status = ntlmssp_client_start(&ntlmssp_state))) {
                return nt_status;
        }
+       ntlmssp_want_feature(ntlmssp_state, NTLMSSP_FEATURE_SESSION_KEY);
 
        if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_username(ntlmssp_state, user))) {
                return nt_status;
        }
-       if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_domain(ntlmssp_state, workgroup))) {
+       if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_domain(ntlmssp_state, domain))) {
                return nt_status;
        }
        if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_password(ntlmssp_state, pass))) {
@@ -567,7 +681,7 @@ static NTSTATUS cli_session_setup_ntlmssp(struct cli_state *cli, const char *use
                nt_status = ntlmssp_update(ntlmssp_state, 
                                                  blob_in, &blob_out);
                data_blob_free(&blob_in);
-               if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+               if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED) || NT_STATUS_IS_OK(nt_status)) {
                        if (turn == 1) {
                                /* and wrap it in a SPNEGO wrapper */
                                msg1 = gen_negTokenInit(OID_NTLMSSP, blob_out);
@@ -578,11 +692,9 @@ static NTSTATUS cli_session_setup_ntlmssp(struct cli_state *cli, const char *use
                
                        /* now send that blob on its way */
                        if (!cli_session_setup_blob_send(cli, msg1)) {
-                               DEBUG(3, ("Failed to send NTLMSSP/SPENGO blob to server!\n"));
+                               DEBUG(3, ("Failed to send NTLMSSP/SPNEGO blob to server!\n"));
                                nt_status = NT_STATUS_UNSUCCESSFUL;
                        } else {
-                               data_blob_free(&msg1);
-                               
                                blob = cli_session_setup_blob_receive(cli);
                                
                                nt_status = cli_nt_error(cli);
@@ -594,6 +706,7 @@ static NTSTATUS cli_session_setup_ntlmssp(struct cli_state *cli, const char *use
                                        }
                                }
                        }
+                       data_blob_free(&msg1);
                }
                
                if (!blob.length) {
@@ -602,7 +715,7 @@ static NTSTATUS cli_session_setup_ntlmssp(struct cli_state *cli, const char *use
                        }
                } else if ((turn == 1) && 
                           NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
-                       DATA_BLOB tmp_blob = data_blob(NULL, 0);
+                       DATA_BLOB tmp_blob = data_blob_null;
                        /* the server might give us back two challenges */
                        if (!spnego_parse_challenge(blob, &blob_in, 
                                                    &tmp_blob)) {
@@ -611,7 +724,7 @@ static NTSTATUS cli_session_setup_ntlmssp(struct cli_state *cli, const char *use
                        }
                        data_blob_free(&tmp_blob);
                } else {
-                       if (!spnego_parse_auth_response(blob, nt_status, 
+                       if (!spnego_parse_auth_response(blob, nt_status, OID_NTLMSSP, 
                                                        &blob_in)) {
                                DEBUG(3,("Failed to parse auth response\n"));
                                if (NT_STATUS_IS_OK(nt_status) 
@@ -624,22 +737,32 @@ static NTSTATUS cli_session_setup_ntlmssp(struct cli_state *cli, const char *use
                turn++;
        } while (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED));
 
+       data_blob_free(&blob_in);
+
        if (NT_STATUS_IS_OK(nt_status)) {
 
                DATA_BLOB key = data_blob(ntlmssp_state->session_key.data,
                                          ntlmssp_state->session_key.length);
-               DATA_BLOB null_blob = data_blob(NULL, 0);
+               DATA_BLOB null_blob = data_blob_null;
+               BOOL res;
 
                fstrcpy(cli->server_domain, ntlmssp_state->server_domain);
                cli_set_session_key(cli, ntlmssp_state->session_key);
 
-               /* Using NTLMSSP session setup, signing on the net only starts
-                * after a successful authentication and the session key has
-                * been determined, but with a sequence number of 2. This
-                * assumes that NTLMSSP needs exactly 2 roundtrips, for any
-                * other SPNEGO mechanism it needs adapting. */
+               res = cli_simple_set_signing(cli, key, null_blob);
 
-               cli_simple_set_signing(cli, key, null_blob, 2);
+               data_blob_free(&key);
+
+               if (res) {
+                       
+                       /* 'resign' the last message, so we get the right sequence numbers
+                          for checking the first reply from the server */
+                       cli_calculate_sign_mac(cli);
+                       
+                       if (!cli_check_sign_mac(cli)) {
+                               nt_status = NT_STATUS_ACCESS_DENIED;
+                       }
+               }
        }
 
        /* we have a reference conter on ntlmssp_state, if we are signing
@@ -647,6 +770,9 @@ static NTSTATUS cli_session_setup_ntlmssp(struct cli_state *cli, const char *use
 
        ntlmssp_end(&ntlmssp_state);
 
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               cli->vuid = 0;
+       }
        return nt_status;
 }
 
@@ -654,8 +780,8 @@ static NTSTATUS cli_session_setup_ntlmssp(struct cli_state *cli, const char *use
  Do a spnego encrypted session setup.
 ****************************************************************************/
 
-NTSTATUS cli_session_setup_spnego(struct cli_state *cli, const char *user, 
-                             const char *pass, const char *workgroup)
+ADS_STATUS cli_session_setup_spnego(struct cli_state *cli, const char *user, 
+                             const char *pass, const char *domain)
 {
        char *principal;
        char *OIDs[ASN1_MAX_OIDS];
@@ -663,7 +789,7 @@ NTSTATUS cli_session_setup_spnego(struct cli_state *cli, const char *user,
        BOOL got_kerberos_mechanism = False;
        DATA_BLOB blob;
 
-       DEBUG(2,("Doing spnego session setup (blob length=%lu)\n", (unsigned long)cli->secblob.length));
+       DEBUG(3,("Doing spnego session setup (blob length=%lu)\n", (unsigned long)cli->secblob.length));
 
        /* the server might not even do spnego */
        if (cli->secblob.length <= 16) {
@@ -682,7 +808,7 @@ NTSTATUS cli_session_setup_spnego(struct cli_state *cli, const char *user,
           reply */
        if (!spnego_parse_negTokenInit(blob, OIDs, &principal)) {
                data_blob_free(&blob);
-               return NT_STATUS_INVALID_PARAMETER;
+               return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
        }
        data_blob_free(&blob);
 
@@ -695,7 +821,20 @@ NTSTATUS cli_session_setup_spnego(struct cli_state *cli, const char *user,
                }
                free(OIDs[i]);
        }
-       DEBUG(3,("got principal=%s\n", principal));
+
+       DEBUG(3,("got principal=%s\n", principal ? principal : "<null>"));
+
+       if (got_kerberos_mechanism && (principal == NULL)) {
+               /*
+                * It is WRONG to depend on the principal sent in the negprot
+                * reply, but right now we do it. So for safety (don't
+                * segfault later) disable Kerberos when no principal was
+                * sent. -- VL
+                */
+               DEBUG(1, ("Kerberos mech was offered, but no principal was "
+                         "sent, disabling Kerberos\n"));
+               cli->use_kerberos = False;
+       }
 
        fstrcpy(cli->user_name, user);
 
@@ -704,27 +843,36 @@ NTSTATUS cli_session_setup_spnego(struct cli_state *cli, const char *user,
         * and do not store results */
 
        if (got_kerberos_mechanism && cli->use_kerberos) {
+               ADS_STATUS rc;
+
                if (pass && *pass) {
                        int ret;
                        
                        use_in_memory_ccache();
-                       ret = kerberos_kinit_password(user, pass, 0 /* no time correction for now */);
+                       ret = kerberos_kinit_password(user, pass, 0 /* no time correction for now */, NULL);
                        
                        if (ret){
+                               SAFE_FREE(principal);
                                DEBUG(0, ("Kinit failed: %s\n", error_message(ret)));
-                               return NT_STATUS_LOGON_FAILURE;
+                               if (cli->fallback_after_kerberos)
+                                       goto ntlmssp;
+                               return ADS_ERROR_KRB5(ret);
                        }
                }
                
-               return cli_session_setup_kerberos(cli, principal, workgroup);
+               rc = cli_session_setup_kerberos(cli, principal, domain);
+               if (ADS_ERR_OK(rc) || !cli->fallback_after_kerberos) {
+                       SAFE_FREE(principal);
+                       return rc;
+               }
        }
 #endif
 
-       free(principal);
+       SAFE_FREE(principal);
 
 ntlmssp:
 
-       return cli_session_setup_ntlmssp(cli, user, pass, workgroup);
+       return ADS_ERROR_NT(cli_session_setup_ntlmssp(cli, user, pass, domain));
 }
 
 /****************************************************************************
@@ -733,11 +881,11 @@ ntlmssp:
  password is in plaintext, the same should be done.
 ****************************************************************************/
 
-BOOL 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(struct cli_state *cli, 
+                          const char *user, 
+                          const char *pass, int passlen,
+                          const char *ntpass, int ntpasslen,
+                          const char *workgroup)
 {
        char *p;
        fstring user2;
@@ -751,8 +899,9 @@ BOOL cli_session_setup(struct cli_state *cli,
                workgroup = user2;
        }
 
-       if (cli->protocol < PROTOCOL_LANMAN1)
-               return True;
+       if (cli->protocol < PROTOCOL_LANMAN1) {
+               return NT_STATUS_OK;
+       }
 
        /* now work out what sort of session setup we are going to
            do. I have split this into separate functions to make the
@@ -764,17 +913,18 @@ BOOL cli_session_setup(struct cli_state *cli,
                if (!lp_client_lanman_auth() && passlen != 24 && (*pass)) {
                        DEBUG(1, ("Server requested LM password but 'client lanman auth'"
                                  " is disabled\n"));
-                       return False;
+                       return NT_STATUS_ACCESS_DENIED;
                }
 
                if ((cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) == 0 &&
                    !lp_client_plaintext_auth() && (*pass)) {
                        DEBUG(1, ("Server requested plaintext password but 'client use plaintext auth'"
                                  " is disabled\n"));
-                       return False;
+                       return NT_STATUS_ACCESS_DENIED;
                }
 
-               return cli_session_setup_lanman2(cli, user, pass, passlen, workgroup);
+               return cli_session_setup_lanman2(cli, user, pass, passlen,
+                                                workgroup);
        }
 
        /* if no user is supplied then we have to do an anonymous connection.
@@ -797,7 +947,7 @@ BOOL cli_session_setup(struct cli_state *cli,
                if (!lp_client_plaintext_auth() && (*pass)) {
                        DEBUG(1, ("Server requested plaintext password but 'client use plaintext auth'"
                                  " is disabled\n"));
-                       return False;
+                       return NT_STATUS_ACCESS_DENIED;
                }
                return cli_session_setup_plaintext(cli, user, pass, workgroup);
        }
@@ -805,19 +955,30 @@ BOOL cli_session_setup(struct cli_state *cli,
        /* if the server supports extended security then use SPNEGO */
 
        if (cli->capabilities & CAP_EXTENDED_SECURITY) {
-               NTSTATUS nt_status;
-               if (!NT_STATUS_IS_OK(nt_status = cli_session_setup_spnego(cli, user, pass, workgroup))) {
-                       DEBUG(3, ("SPENGO login failed: %s\n", get_friendly_nt_error_msg(nt_status)));
-                       return False;
+               ADS_STATUS status = cli_session_setup_spnego(cli, user, pass, workgroup);
+               if (!ADS_ERR_OK(status)) {
+                       DEBUG(3, ("SPNEGO login failed: %s\n", ads_errstr(status)));
+                       return ads_ntstatus(status);
                }
-               return True;
+       } else {
+               NTSTATUS status;
+
+               /* otherwise do a NT1 style session setup */
+               status = cli_session_setup_nt1(cli, user, pass, passlen,
+                                              ntpass, ntpasslen, workgroup);
+               if (!NT_STATUS_IS_OK(status)) {
+                       DEBUG(3,("cli_session_setup: NT1 session setup "
+                                "failed: %s\n", nt_errstr(status)));
+                       return status;
+               }
+       }
+
+       if (strstr(cli->server_type, "Samba")) {
+               cli->is_samba = True;
        }
 
-       /* otherwise do a NT1 style session setup */
+       return NT_STATUS_OK;
 
-       return cli_session_setup_nt1(cli, user, 
-                                    pass, passlen, ntpass, ntpasslen,
-                                    workgroup);        
 }
 
 /****************************************************************************
@@ -837,12 +998,18 @@ BOOL cli_ulogoff(struct cli_state *cli)
        if (!cli_receive_smb(cli))
                return False;
 
-       return !cli_is_error(cli);
+       if (cli_is_error(cli)) {
+               return False;
+       }
+
+        cli->cnum = -1;
+        return True;
 }
 
 /****************************************************************************
  Send a tconX.
 ****************************************************************************/
+
 BOOL cli_send_tconX(struct cli_state *cli, 
                    const char *share, const char *dev, const char *pass, int passlen)
 {
@@ -857,11 +1024,15 @@ BOOL cli_send_tconX(struct cli_state *cli,
        if (cli->sec_mode & NEGOTIATE_SECURITY_USER_LEVEL) {
                passlen = 1;
                pass = "";
+       } else if (!pass) {
+               DEBUG(1, ("Server not using user level security and no password supplied.\n"));
+               return False;
        }
 
-       if ((cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) && *pass && passlen != 24) {
+       if ((cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) &&
+           *pass && passlen != 24) {
                if (!lp_client_lanman_auth()) {
-                       DEBUG(1, ("Server requested LANMAN password but 'client use lanman auth'"
+                       DEBUG(1, ("Server requested LANMAN password (share-level security) but 'client use lanman auth'"
                                  " is disabled\n"));
                        return False;
                }
@@ -885,7 +1056,9 @@ BOOL cli_send_tconX(struct cli_state *cli,
                        passlen = clistr_push(cli, pword, pass, sizeof(pword), STR_TERMINATE);
                        
                } else {
-                       memcpy(pword, pass, passlen);
+                       if (passlen) {
+                               memcpy(pword, pass, passlen);
+                       }
                }
        }
 
@@ -897,13 +1070,16 @@ BOOL cli_send_tconX(struct cli_state *cli,
        cli_setup_packet(cli);
 
        SSVAL(cli->outbuf,smb_vwv0,0xFF);
+       SSVAL(cli->outbuf,smb_vwv2,TCONX_FLAG_EXTENDED_RESPONSE);
        SSVAL(cli->outbuf,smb_vwv3,passlen);
 
        p = smb_buf(cli->outbuf);
-       memcpy(p,pword,passlen);
+       if (passlen) {
+               memcpy(p,pword,passlen);
+       }
        p += passlen;
        p += clistr_push(cli, p, fullshare, -1, STR_TERMINATE |STR_UPPER);
-       fstrcpy(p, dev); p += strlen(dev)+1;
+       p += clistr_push(cli, p, dev, -1, STR_TERMINATE |STR_UPPER | STR_ASCII);
 
        cli_setup_bcc(cli, p);
 
@@ -921,6 +1097,13 @@ BOOL cli_send_tconX(struct cli_state *cli,
                /* almost certainly win95 - enable bug fixes */
                cli->win95 = True;
        }
+       
+       /* Make sure that we have the optional support 16-bit field.  WCT > 2 */
+       /* Avoids issues when connecting to Win9x boxes sharing files */
+
+       cli->dfsroot = False;
+       if ( (CVAL(cli->inbuf, smb_wct))>2 && cli->protocol >= PROTOCOL_LANMAN2 )
+               cli->dfsroot = (SVAL( cli->inbuf, smb_vwv2 ) & SMB_SHARE_IN_DFS) ? True : False;
 
        cli->cnum = SVAL(cli->inbuf,smb_tid);
        return True;
@@ -942,7 +1125,12 @@ BOOL cli_tdis(struct cli_state *cli)
        if (!cli_receive_smb(cli))
                return False;
        
-       return !cli_is_error(cli);
+       if (cli_is_error(cli)) {
+               return False;
+       }
+
+       cli->cnum = -1;
+       return True;
 }
 
 /****************************************************************************
@@ -1034,6 +1222,7 @@ BOOL cli_negprot(struct cli_state *cli)
        }
 
        if (cli->protocol >= PROTOCOL_NT1) {    
+               struct timespec ts;
                /* NT protocol */
                cli->sec_mode = CVAL(cli->inbuf,smb_vwv1);
                cli->max_mux = SVAL(cli->inbuf, smb_vwv1+1);
@@ -1042,7 +1231,8 @@ BOOL cli_negprot(struct cli_state *cli)
                cli->serverzone = SVALS(cli->inbuf,smb_vwv15+1);
                cli->serverzone *= 60;
                /* this time arrives in real GMT */
-               cli->servertime = interpret_long_date(cli->inbuf+smb_vwv11+1);
+               ts = interpret_long_date(cli->inbuf+smb_vwv11+1);
+               cli->servertime = ts.tv_sec;
                cli->secblob = data_blob(smb_buf(cli->inbuf),smb_buflen(cli->inbuf));
                cli->capabilities = IVAL(cli->inbuf,smb_vwv9+1);
                if (cli->capabilities & CAP_RAW_MODE) {
@@ -1078,17 +1268,28 @@ BOOL cli_negprot(struct cli_state *cli)
                        }
                        cli->sign_info.negotiated_smb_signing = True;
                        cli->sign_info.mandatory_signing = True;
+               } else if (cli->sec_mode & NEGOTIATE_SECURITY_SIGNATURES_ENABLED) {
+                       cli->sign_info.negotiated_smb_signing = True;
+               }
+
+               if (cli->capabilities & (CAP_LARGE_READX|CAP_LARGE_WRITEX)) {
+                       SAFE_FREE(cli->outbuf);
+                       SAFE_FREE(cli->inbuf);
+                       cli->outbuf = (char *)SMB_MALLOC(CLI_SAMBA_MAX_LARGE_READX_SIZE+SAFETY_MARGIN);
+                       cli->inbuf = (char *)SMB_MALLOC(CLI_SAMBA_MAX_LARGE_READX_SIZE+SAFETY_MARGIN);
+                       cli->bufsize = CLI_SAMBA_MAX_LARGE_READX_SIZE;
                }
 
        } else if (cli->protocol >= PROTOCOL_LANMAN1) {
                cli->use_spnego = False;
                cli->sec_mode = SVAL(cli->inbuf,smb_vwv1);
                cli->max_xmit = SVAL(cli->inbuf,smb_vwv2);
+               cli->max_mux = SVAL(cli->inbuf, smb_vwv3); 
                cli->sesskey = IVAL(cli->inbuf,smb_vwv6);
                cli->serverzone = SVALS(cli->inbuf,smb_vwv10);
                cli->serverzone *= 60;
                /* this time is converted to GMT by make_unix_date */
-               cli->servertime = make_unix_date(cli->inbuf+smb_vwv8);
+               cli->servertime = cli_make_unix_date(cli,cli->inbuf+smb_vwv8);
                cli->readbraw_supported = ((SVAL(cli->inbuf,smb_vwv5) & 0x1) != 0);
                cli->writebraw_supported = ((SVAL(cli->inbuf,smb_vwv5) & 0x2) != 0);
                cli->secblob = data_blob(smb_buf(cli->inbuf),smb_buflen(cli->inbuf));
@@ -1096,7 +1297,7 @@ BOOL cli_negprot(struct cli_state *cli)
                /* the old core protocol */
                cli->use_spnego = False;
                cli->sec_mode = 0;
-               cli->serverzone = TimeDiff(time(NULL));
+               cli->serverzone = get_time_zone(time(NULL));
        }
 
        cli->max_xmit = MIN(cli->max_xmit, CLI_BUFFER_SIZE);
@@ -1117,7 +1318,6 @@ BOOL cli_session_request(struct cli_state *cli,
 {
        char *p;
        int len = 4;
-       extern pstring user_socket_options;
 
        memcpy(&(cli->calling), calling, sizeof(*calling));
        memcpy(&(cli->called ), called , sizeof(*called ));
@@ -1205,9 +1405,8 @@ BOOL cli_session_request(struct cli_state *cli,
  Open the client sockets.
 ****************************************************************************/
 
-BOOL cli_connect(struct cli_state *cli, const char *host, struct in_addr *ip)
+NTSTATUS cli_connect(struct cli_state *cli, const char *host, struct in_addr *ip)
 {
-       extern pstring user_socket_options;
        int name_type = 0x20;
        char *p;
 
@@ -1224,7 +1423,7 @@ BOOL cli_connect(struct cli_state *cli, const char *host, struct in_addr *ip)
        
        if (!ip || is_zero_ip(*ip)) {
                 if (!resolve_name(cli->desthost, &cli->dest_ip, name_type)) {
-                        return False;
+                       return NT_STATUS_BAD_NETWORK_NAME;
                 }
                if (ip) *ip = cli->dest_ip;
        } else {
@@ -1249,31 +1448,12 @@ BOOL cli_connect(struct cli_state *cli, const char *host, struct in_addr *ip)
        if (cli->fd == -1) {
                DEBUG(1,("Error connecting to %s (%s)\n",
                         ip?inet_ntoa(*ip):host,strerror(errno)));
-               return False;
+               return map_nt_error_from_unix(errno);
        }
 
        set_socket_options(cli->fd,user_socket_options);
 
-       return True;
-}
-
-/****************************************************************************
- Initialise client credentials for authenticated pipe access.
-****************************************************************************/
-
-void init_creds(struct ntuser_creds *creds, const char* username,
-                      const char* domain, const char* password)
-{
-       ZERO_STRUCTP(creds);
-
-       pwd_set_cleartext(&creds->pwd, password);
-
-       fstrcpy(creds->user_name, username);
-       fstrcpy(creds->domain, domain);
-
-       if (!*username) {
-               creds->pwd.null_pwd = True;
-       }
+       return NT_STATUS_OK;
 }
 
 /**
@@ -1304,8 +1484,9 @@ NTSTATUS cli_start_connection(struct cli_state **output_cli,
        if (!my_name) 
                my_name = global_myname();
        
-       if (!(cli = cli_initialise(NULL)))
+       if (!(cli = cli_initialise())) {
                return NT_STATUS_NO_MEMORY;
+       }
        
        make_nmb_name(&calling, my_name, 0x0);
        make_nmb_name(&called , dest_host, 0x20);
@@ -1326,11 +1507,12 @@ again:
 
        DEBUG(3,("Connecting to host=%s\n", dest_host));
        
-       if (!cli_connect(cli, dest_host, &ip)) {
-               DEBUG(1,("cli_full_connection: failed to connect to %s (%s)\n",
-                        nmb_namestr(&called), inet_ntoa(ip)));
+       nt_status = cli_connect(cli, dest_host, &ip);
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               DEBUG(1,("cli_start_connection: failed to connect to %s (%s). Error %s\n",
+                        nmb_namestr(&called), inet_ntoa(ip), nt_errstr(nt_status) ));
                cli_shutdown(cli);
-               return NT_STATUS_UNSUCCESSFUL;
+               return nt_status;
        }
 
        if (retry)
@@ -1348,7 +1530,7 @@ again:
                        make_nmb_name(&called , "*SMBSERVER", 0x20);
                        goto again;
                }
-               return NT_STATUS_UNSUCCESSFUL;
+               return NT_STATUS_BAD_NETWORK_NAME;
        }
 
        cli_setup_signing_state(cli, signing_state);
@@ -1360,7 +1542,10 @@ again:
 
        if (!cli_negprot(cli)) {
                DEBUG(1,("failed negprot\n"));
-               nt_status = NT_STATUS_UNSUCCESSFUL;
+               nt_status = cli_nt_error(cli);
+               if (NT_STATUS_IS_OK(nt_status)) {
+                       nt_status = NT_STATUS_UNSUCCESSFUL;
+               }
                cli_shutdown(cli);
                return nt_status;
        }
@@ -1394,9 +1579,15 @@ NTSTATUS cli_full_connection(struct cli_state **output_cli,
                             int signing_state,
                             BOOL *retry) 
 {
-       struct ntuser_creds creds;
        NTSTATUS nt_status;
        struct cli_state *cli = NULL;
+       int pw_len = password ? strlen(password)+1 : 0;
+
+       *output_cli = NULL;
+
+       if (password == NULL) {
+               password = "";
+       }
 
        nt_status = cli_start_connection(&cli, my_name, dest_host, 
                                         dest_ip, port, signing_state, flags, retry);
@@ -1405,24 +1596,28 @@ NTSTATUS cli_full_connection(struct cli_state **output_cli,
                return nt_status;
        }
 
-       if (!cli_session_setup(cli, user, password, strlen(password)+1, 
-                              password, strlen(password)+1, 
-                              domain)) {
-               if ((flags & CLI_FULL_CONNECTION_ANNONYMOUS_FALLBACK)
-                   && cli_session_setup(cli, "", "", 0, "", 0, domain)) {
-               } else {
-                       nt_status = cli_nt_error(cli);
-                       DEBUG(1,("failed session setup with %s\n", nt_errstr(nt_status)));
+       nt_status = cli_session_setup(cli, user, password, pw_len, password,
+                                     pw_len, domain);
+       if (!NT_STATUS_IS_OK(nt_status)) {
+
+               if (!(flags & CLI_FULL_CONNECTION_ANONYMOUS_FALLBACK)) {
+                       DEBUG(1,("failed session setup with %s\n",
+                                nt_errstr(nt_status)));
                        cli_shutdown(cli);
-                       if (NT_STATUS_IS_OK(nt_status)) 
-                               nt_status = NT_STATUS_UNSUCCESSFUL;
                        return nt_status;
                }
-       } 
 
+               nt_status = cli_session_setup(cli, "", "", 0, "", 0, domain);
+               if (!NT_STATUS_IS_OK(nt_status)) {
+                       DEBUG(1,("anonymous failed session setup with %s\n",
+                                nt_errstr(nt_status)));
+                       cli_shutdown(cli);
+                       return nt_status;
+               }
+       }
+       
        if (service) {
-               if (!cli_send_tconX(cli, service, service_type,
-                                   password, strlen(password)+1)) {
+               if (!cli_send_tconX(cli, service, service_type, password, pw_len)) {
                        nt_status = cli_nt_error(cli);
                        DEBUG(1,("failed tcon_X with %s\n", nt_errstr(nt_status)));
                        cli_shutdown(cli);
@@ -1433,8 +1628,7 @@ NTSTATUS cli_full_connection(struct cli_state **output_cli,
                }
        }
 
-       init_creds(&creds, user, domain, password);
-       cli_init_creds(cli, &creds);
+       cli_init_creds(cli, user, domain, password);
 
        *output_cli = cli;
        return NT_STATUS_OK;
@@ -1444,7 +1638,7 @@ NTSTATUS cli_full_connection(struct cli_state **output_cli,
  Attempt a NetBIOS session request, falling back to *SMBSERVER if needed.
 ****************************************************************************/
 
-BOOL attempt_netbios_session_request(struct cli_state *cli, const char *srchost, const char *desthost,
+BOOL attempt_netbios_session_request(struct cli_state **ppcli, const char *srchost, const char *desthost,
                                      struct in_addr *pdest_ip)
 {
        struct nmb_name calling, called;
@@ -1456,12 +1650,14 @@ BOOL attempt_netbios_session_request(struct cli_state *cli, const char *srchost,
         * then use *SMBSERVER immediately.
         */
 
-       if(is_ipaddress(desthost))
+       if(is_ipaddress(desthost)) {
                make_nmb_name(&called, "*SMBSERVER", 0x20);
-       else
+       } else {
                make_nmb_name(&called, desthost, 0x20);
+       }
 
-       if (!cli_session_request(cli, &calling, &called)) {
+       if (!cli_session_request(*ppcli, &calling, &called)) {
+               NTSTATUS status;
                struct nmb_name smbservername;
 
                make_nmb_name(&smbservername , "*SMBSERVER", 0x20);
@@ -1478,23 +1674,24 @@ BOOL attempt_netbios_session_request(struct cli_state *cli, const char *srchost,
                         */
 
                        DEBUG(0,("attempt_netbios_session_request: %s rejected the session for name *SMBSERVER \
-with error %s.\n", desthost, cli_errstr(cli) ));
+with error %s.\n", desthost, cli_errstr(*ppcli) ));
                        return False;
                }
 
-               /*
-                * We need to close the connection here but can't call cli_shutdown as
-                * will free an allocated cli struct. cli_close_connection was invented
-                * for this purpose. JRA. Based on work by "Kim R. Pedersen" <krp@filanet.dk>.
-                */
+               /* Try again... */
+               cli_shutdown(*ppcli);
 
-               cli_close_connection(cli);
+               *ppcli = cli_initialise();
+               if (!*ppcli) {
+                       /* Out of memory... */
+                       return False;
+               }
 
-               if (!cli_initialise(cli) ||
-                               !cli_connect(cli, desthost, pdest_ip) ||
-                               !cli_session_request(cli, &calling, &smbservername)) {
+               status = cli_connect(*ppcli, desthost, pdest_ip);
+               if (!NT_STATUS_IS_OK(status) ||
+                               !cli_session_request(*ppcli, &calling, &smbservername)) {
                        DEBUG(0,("attempt_netbios_session_request: %s rejected the session for \
-name *SMBSERVER with error %s\n", desthost, cli_errstr(cli) ));
+name *SMBSERVER with error %s\n", desthost, cli_errstr(*ppcli) ));
                        return False;
                }
        }
@@ -1563,7 +1760,7 @@ struct cli_state *get_ipc_connect(char *server, struct in_addr *server_ip,
        
        nt_status = cli_full_connection(&cli, myname, server, server_ip, 0, "IPC$", "IPC", 
                                        user_info->username, lp_workgroup(), user_info->password, 
-                                       CLI_FULL_CONNECTION_ANNONYMOUS_FALLBACK, Undefined, NULL);
+                                       CLI_FULL_CONNECTION_ANONYMOUS_FALLBACK, Undefined, NULL);
 
        if (NT_STATUS_IS_OK(nt_status)) {
                return cli;
@@ -1580,42 +1777,89 @@ struct cli_state *get_ipc_connect(char *server, struct in_addr *server_ip,
        return NULL;
 }
 
-/* Return the IP address and workgroup of a master browser on the 
-   network. */
+/*
+ * Given the IP address of a master browser on the network, return its
+ * workgroup and connect to it.
+ *
+ * This function is provided to allow additional processing beyond what
+ * get_ipc_connect_master_ip_bcast() does, e.g. to retrieve the list of master
+ * browsers and obtain each master browsers' list of domains (in case the
+ * first master browser is recently on the network and has not yet
+ * synchronized with other master browsers and therefore does not yet have the
+ * entire network browse list)
+ */
 
-struct cli_state *get_ipc_connect_master_ip_bcast(pstring workgroup, struct user_auth_info *user_info)
+struct cli_state *get_ipc_connect_master_ip(struct ip_service * mb_ip, pstring workgroup, struct user_auth_info *user_info)
 {
-       struct ip_service *ip_list;
+        static fstring name;
        struct cli_state *cli;
-       int i, count;
        struct in_addr server_ip; 
 
-        /* Go looking for workgroups by broadcasting on the local network */ 
+        DEBUG(99, ("Looking up name of master browser %s\n",
+                   inet_ntoa(mb_ip->ip)));
+
+        /*
+         * Do a name status query to find out the name of the master browser.
+         * We use <01><02>__MSBROWSE__<02>#01 if *#00 fails because a domain
+         * master browser will not respond to a wildcard query (or, at least,
+         * an NT4 server acting as the domain master browser will not).
+         *
+         * We might be able to use ONLY the query on MSBROWSE, but that's not
+         * yet been tested with all Windows versions, so until it is, leave
+         * the original wildcard query as the first choice and fall back to
+         * MSBROWSE if the wildcard query fails.
+         */
+        if (!name_status_find("*", 0, 0x1d, mb_ip->ip, name) &&
+            !name_status_find(MSBROWSE, 1, 0x1d, mb_ip->ip, name)) {
 
-        if (!name_resolve_bcast(MSBROWSE, 1, &ip_list, &count)) {
-                return False;
+                DEBUG(99, ("Could not retrieve name status for %s\n",
+                           inet_ntoa(mb_ip->ip)));
+                return NULL;
         }
 
-       for (i = 0; i < count; i++) {
-               static fstring name;
-
-               if (!name_status_find("*", 0, 0x1d, ip_list[i].ip, name))
-                       continue;
-
-                if (!find_master_ip(name, &server_ip))
-                       continue;
+        if (!find_master_ip(name, &server_ip)) {
+                DEBUG(99, ("Could not find master ip for %s\n", name));
+                return NULL;
+        }
 
                 pstrcpy(workgroup, name);
 
                 DEBUG(4, ("found master browser %s, %s\n", 
-                          name, inet_ntoa(ip_list[i].ip)));
+                  name, inet_ntoa(mb_ip->ip)));
 
                cli = get_ipc_connect(inet_ntoa(server_ip), &server_ip, user_info);
 
-               if (!cli)
-                   continue;
-               
                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(pstring workgroup, struct user_auth_info *user_info)
+{
+       struct ip_service *ip_list;
+       struct cli_state *cli;
+       int i, count;
+
+        DEBUG(99, ("Do broadcast lookup for workgroups on local network\n"));
+
+        /* Go looking for workgroups by broadcasting on the local network */ 
+
+        if (!NT_STATUS_IS_OK(name_resolve_bcast(MSBROWSE, 1, &ip_list,
+                                               &count))) {
+                DEBUG(99, ("No master browsers responded\n"));
+                return False;
+        }
+
+       for (i = 0; i < count; i++) {
+            DEBUG(99, ("Found master browser %s\n", inet_ntoa(ip_list[i].ip)));
+
+            cli = get_ipc_connect_master_ip(&ip_list[i], workgroup, user_info);
+            if (cli)
+                    return(cli);
        }
 
        return NULL;