Refactor the NTLMSSP code again - this time we use function pointers to
[kai/samba.git] / source3 / libsmb / cliconnect.c
index 77a8232ed5fde3c06c66897e77cc788b9ecc124d..389b7a1733218222b0a3f1413bd20f6e567ee15c 100644 (file)
@@ -1,8 +1,8 @@
 /* 
-   Unix SMB/Netbios implementation.
-   Version 3.0
+   Unix SMB/CIFS implementation.
    client connect/disconnect routines
    Copyright (C) Andrew Tridgell 1994-1998
+   Copyright (C) Andrew Barteltt 2001-2002
    
    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
 #include "includes.h"
 
 
-static  struct {
-    int prot;
-    const char *name;
-  }
-prots[] = 
-    {
-      {PROTOCOL_CORE,"PC NETWORK PROGRAM 1.0"},
-      {PROTOCOL_LANMAN1,"LANMAN1.0"},
-      {PROTOCOL_LANMAN1,"Windows for Workgroups 3.1a"},
-      {PROTOCOL_LANMAN2,"LM1.2X002"},
-      {PROTOCOL_NT1,"LANMAN2.1"},
-      {PROTOCOL_NT1,"NT LM 0.12"},
-      {-1,NULL}
-    };
-
+static const struct {
+       int prot;
+       const char *name;
+} prots[] = {
+       {PROTOCOL_CORE,"PC NETWORK PROGRAM 1.0"},
+       {PROTOCOL_COREPLUS,"MICROSOFT NETWORKS 1.03"},
+       {PROTOCOL_LANMAN1,"MICROSOFT NETWORKS 3.0"},
+       {PROTOCOL_LANMAN1,"LANMAN1.0"},
+       {PROTOCOL_LANMAN2,"LM1.2X002"},
+       {PROTOCOL_LANMAN2,"DOS LANMAN2.1"},
+       {PROTOCOL_LANMAN2,"Samba"},
+       {PROTOCOL_NT1,"NT LANMAN 1.0"},
+       {PROTOCOL_NT1,"NT LM 0.12"},
+       {-1,NULL}
+};
 
 /****************************************************************************
-do an old lanman2 style session setup
+ Do an old lanman2 style session setup.
 ****************************************************************************/
-static BOOL cli_session_setup_lanman2(struct cli_state *cli, char *user, 
-                                     char *pass, int passlen)
+
+static BOOL cli_session_setup_lanman2(struct cli_state *cli, const char *user, 
+                                     const char *pass, int passlen, const char *workgroup)
 {
        fstring pword;
        char *p;
 
-       if (passlen > sizeof(pword)-1) {
+       if (passlen > sizeof(pword)-1)
                return False;
-       }
 
        /* if in share level security then don't send a password now */
-       if (!(cli->sec_mode & 1)) {
+       if (!(cli->sec_mode & NEGOTIATE_SECURITY_USER_LEVEL))
                passlen = 0;
-       }
 
-       if (passlen > 0 && (cli->sec_mode & 2) && passlen != 24) {
+       if (passlen > 0 && (cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) && passlen != 24) {
                /* Encrypted mode needed, and non encrypted password supplied. */
                passlen = 24;
-               clistr_push(cli, pword, pass, -1, STR_TERMINATE);
-               SMBencrypt((uchar *)pword,cli->secblob.data,(uchar *)pword);
-       } else if ((cli->sec_mode & 2) && passlen == 24) {
+               SMBencrypt(pass,cli->secblob.data,(uchar *)pword);
+       } else if ((cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) && passlen == 24) {
                /* Encrypted mode needed, and encrypted password supplied. */
                memcpy(pword, pass, passlen);
        } else if (passlen > 0) {
@@ -74,10 +72,10 @@ static BOOL cli_session_setup_lanman2(struct cli_state *cli, char *user,
        /* send a session setup command */
        memset(cli->outbuf,'\0',smb_size);
        set_message(cli->outbuf,10, 0, True);
-       CVAL(cli->outbuf,smb_com) = SMBsesssetupX;
+       SCVAL(cli->outbuf,smb_com,SMBsesssetupX);
        cli_setup_packet(cli);
        
-       CVAL(cli->outbuf,smb_vwv0) = 0xFF;
+       SCVAL(cli->outbuf,smb_vwv0,0xFF);
        SSVAL(cli->outbuf,smb_vwv2,cli->max_xmit);
        SSVAL(cli->outbuf,smb_vwv3,2);
        SSVAL(cli->outbuf,smb_vwv4,1);
@@ -87,7 +85,10 @@ static BOOL cli_session_setup_lanman2(struct cli_state *cli, char *user,
        p = smb_buf(cli->outbuf);
        memcpy(p,pword,passlen);
        p += passlen;
-       p += clistr_push(cli, p, user, -1, STR_UPPER|STR_TERMINATE);
+       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);
@@ -96,9 +97,8 @@ static BOOL cli_session_setup_lanman2(struct cli_state *cli, char *user,
 
        show_msg(cli->inbuf);
 
-       if (cli_is_error(cli)) {
+       if (cli_is_error(cli))
                return False;
-       }
        
        /* use the returned vuid from now on */
        cli->vuid = SVAL(cli->inbuf,smb_uid);   
@@ -107,46 +107,43 @@ static BOOL cli_session_setup_lanman2(struct cli_state *cli, char *user,
        return True;
 }
 
-
 /****************************************************************************
-work out suitable capabilities to offer the server
+ Work out suitable capabilities to offer the server.
 ****************************************************************************/
+
 static uint32 cli_session_setup_capabilities(struct cli_state *cli)
 {
        uint32 capabilities = CAP_NT_SMBS;
 
-       /* Set the CLI_FORCE_DOSERR environment variable to test
-          client routines using DOS errors instead of STATUS32
-          ones.  This intended only as a temporary hack. */    
-       if (!getenv("CLI_FORCE_DOSERR")) {
+       if (!cli->force_dos_errors)
                capabilities |= CAP_STATUS32;
-       }
 
-       if (cli->use_level_II_oplocks) {
+       if (cli->use_level_II_oplocks)
                capabilities |= CAP_LEVEL_II_OPLOCKS;
-       }
 
-       if (cli->capabilities & CAP_UNICODE) {
+       if (cli->capabilities & CAP_UNICODE)
                capabilities |= CAP_UNICODE;
-       }
+
+       if (cli->capabilities & CAP_LARGE_FILES)
+               capabilities |= CAP_LARGE_FILES;
 
        return capabilities;
 }
 
-
 /****************************************************************************
-do a NT1 guest session setup
+ Do a NT1 guest session setup.
 ****************************************************************************/
+
 static BOOL cli_session_setup_guest(struct cli_state *cli)
 {
        char *p;
        uint32 capabilities = cli_session_setup_capabilities(cli);
 
        set_message(cli->outbuf,13,0,True);
-       CVAL(cli->outbuf,smb_com) = SMBsesssetupX;
+       SCVAL(cli->outbuf,smb_com,SMBsesssetupX);
        cli_setup_packet(cli);
                        
-       CVAL(cli->outbuf,smb_vwv0) = 0xFF;
+       SCVAL(cli->outbuf,smb_vwv0,0xFF);
        SSVAL(cli->outbuf,smb_vwv2,CLI_BUFFER_SIZE);
        SSVAL(cli->outbuf,smb_vwv3,2);
        SSVAL(cli->outbuf,smb_vwv4,cli->pid);
@@ -167,9 +164,8 @@ static BOOL cli_session_setup_guest(struct cli_state *cli)
        
        show_msg(cli->inbuf);
        
-       if (cli_is_error(cli)) {
+       if (cli_is_error(cli))
                return False;
-       }
 
        cli->vuid = SVAL(cli->inbuf,smb_uid);
 
@@ -183,35 +179,30 @@ static BOOL cli_session_setup_guest(struct cli_state *cli)
        return True;
 }
 
-
 /****************************************************************************
-do a NT1 plaintext session setup
+ Do a NT1 plaintext session setup.
 ****************************************************************************/
-static BOOL cli_session_setup_plaintext(struct cli_state *cli, char *user, 
-                                       char *pass, char *workgroup)
+
+static BOOL cli_session_setup_plaintext(struct cli_state *cli, const char *user, 
+                                       const char *pass, const char *workgroup)
 {
        uint32 capabilities = cli_session_setup_capabilities(cli);
-       fstring pword;
-       int passlen;
        char *p;
 
-       passlen = clistr_push(cli, pword, pass, sizeof(pword), STR_TERMINATE);
-
        set_message(cli->outbuf,13,0,True);
-       CVAL(cli->outbuf,smb_com) = SMBsesssetupX;
+       SCVAL(cli->outbuf,smb_com,SMBsesssetupX);
        cli_setup_packet(cli);
                        
-       CVAL(cli->outbuf,smb_vwv0) = 0xFF;
+       SCVAL(cli->outbuf,smb_vwv0,0xFF);
        SSVAL(cli->outbuf,smb_vwv2,CLI_BUFFER_SIZE);
        SSVAL(cli->outbuf,smb_vwv3,2);
        SSVAL(cli->outbuf,smb_vwv4,cli->pid);
        SIVAL(cli->outbuf,smb_vwv5,cli->sesskey);
-       SSVAL(cli->outbuf,smb_vwv7,passlen);
        SSVAL(cli->outbuf,smb_vwv8,0);
        SIVAL(cli->outbuf,smb_vwv11,capabilities); 
        p = smb_buf(cli->outbuf);
-       memcpy(p, pword, passlen);
-       p += passlen;
+       p += clistr_push(cli, p, pass, -1, STR_TERMINATE); /* password */
+       SSVAL(cli->outbuf,smb_vwv7,PTR_DIFF(p, smb_buf(cli->outbuf)));
        p += clistr_push(cli, p, user, -1, STR_TERMINATE); /* username */
        p += clistr_push(cli, p, workgroup, -1, STR_TERMINATE); /* workgroup */
        p += clistr_push(cli, p, "Unix", -1, STR_TERMINATE);
@@ -224,9 +215,8 @@ static BOOL cli_session_setup_plaintext(struct cli_state *cli, char *user,
        
        show_msg(cli->inbuf);
        
-       if (cli_is_error(cli)) {
+       if (cli_is_error(cli))
                return False;
-       }
 
        cli->vuid = SVAL(cli->inbuf,smb_uid);
        p = smb_buf(cli->inbuf);
@@ -238,44 +228,84 @@ static BOOL cli_session_setup_plaintext(struct cli_state *cli, char *user,
        return True;
 }
 
+static void set_signing_on_cli (struct cli_state *cli, const char* pass, uint8 response[24]) 
+{
+       uint8 zero_sig[8];
+       ZERO_STRUCT(zero_sig);
+
+       DEBUG(5, ("Server returned security sig:\n"));
+       dump_data(5, &cli->inbuf[smb_ss_field], 8);
+
+       if (cli->sign_info.use_smb_signing) {
+               DEBUG(5, ("smb signing already active on connection\n"));
+       } else if (memcmp(&cli->inbuf[smb_ss_field], zero_sig, 8) != 0) {
+
+               DEBUG(3, ("smb signing enabled!\n"));
+               cli->sign_info.use_smb_signing = True;
+               cli_calculate_mac_key(cli, pass, response);
+       } else {
+               DEBUG(5, ("smb signing NOT enabled!\n"));
+       }
+}
+
+static void set_temp_signing_on_cli(struct cli_state *cli) 
+{
+       if (cli->sign_info.negotiated_smb_signing)
+               cli->sign_info.temp_smb_signing = True;
+}
+
 
 /****************************************************************************
-do a NT1 NTLM/LM encrypted session setup
+   do a NT1 NTLM/LM encrypted session setup
+   @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.
 ****************************************************************************/
-static BOOL cli_session_setup_nt1(struct cli_state *cli, char *user, 
-                                 char *pass, int passlen,
-                                 char *ntpass, int ntpasslen,
-                                 char *workgroup)
+
+static BOOL cli_session_setup_nt1(struct cli_state *cli, const char *user, 
+                                 const char *pass, int passlen,
+                                 const char *ntpass, int ntpasslen,
+                                 const char *workgroup)
 {
        uint32 capabilities = cli_session_setup_capabilities(cli);
-       fstring pword, ntpword;
+       uchar pword[24];
+       uchar ntpword[24];
        char *p;
+       BOOL have_plaintext = False;
 
-       if (passlen > sizeof(pword)-1 || ntpasslen > sizeof(ntpword)-1) {
+       if (passlen > sizeof(pword) || ntpasslen > sizeof(ntpword))
                return False;
-       }
 
        if (passlen != 24) {
-               /* non encrypted password supplied. */
+               /* non encrypted password supplied. Ignore ntpass. */
                passlen = 24;
                ntpasslen = 24;
-               clistr_push(cli, pword, pass, sizeof(pword), STR_TERMINATE);
-               clistr_push(cli, ntpword, ntpass, sizeof(ntpword), STR_TERMINATE);
-               SMBencrypt((uchar *)pword,cli->secblob.data,(uchar *)pword);
-               SMBNTencrypt((uchar *)ntpword,cli->secblob.data,(uchar *)ntpword);
+               SMBencrypt(pass,cli->secblob.data,pword);
+               SMBNTencrypt(pass,cli->secblob.data,ntpword);
+
+               have_plaintext = True;
+               set_temp_signing_on_cli(cli);
        } else {
-               memcpy(pword, pass, passlen);
-               memcpy(ntpword, ntpass, ntpasslen);
+               /* pre-encrypted password supplied.  Only used for 
+                  security=server, can't do
+                  signing becouse we don't have oringial key */
+               memcpy(pword, pass, 24);
+               if (ntpasslen == 24)
+                       memcpy(ntpword, ntpass, 24);
+               else
+                       ZERO_STRUCT(ntpword);
        }
 
        /* send a session setup command */
        memset(cli->outbuf,'\0',smb_size);
 
        set_message(cli->outbuf,13,0,True);
-       CVAL(cli->outbuf,smb_com) = SMBsesssetupX;
+       SCVAL(cli->outbuf,smb_com,SMBsesssetupX);
        cli_setup_packet(cli);
                        
-       CVAL(cli->outbuf,smb_vwv0) = 0xFF;
+       SCVAL(cli->outbuf,smb_vwv0,0xFF);
        SSVAL(cli->outbuf,smb_vwv2,CLI_BUFFER_SIZE);
        SSVAL(cli->outbuf,smb_vwv3,2);
        SSVAL(cli->outbuf,smb_vwv4,cli->pid);
@@ -286,22 +316,23 @@ static BOOL cli_session_setup_nt1(struct cli_state *cli, char *user,
        p = smb_buf(cli->outbuf);
        memcpy(p,pword,passlen); p += passlen;
        memcpy(p,ntpword,ntpasslen); p += ntpasslen;
-       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, user, -1, STR_TERMINATE);
+       p += clistr_push(cli, p, workgroup, -1, STR_TERMINATE);
        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_send_smb(cli))
+               return False;
+
        if (!cli_receive_smb(cli))
                return False;
 
        show_msg(cli->inbuf);
 
-       if (cli_is_error(cli)) {
+       if (cli_is_error(cli))
                return False;
-       }
-       
+
        /* use the returned vuid from now on */
        cli->vuid = SVAL(cli->inbuf,smb_uid);
        
@@ -312,21 +343,26 @@ static BOOL cli_session_setup_nt1(struct cli_state *cli, char *user,
 
        fstrcpy(cli->user_name, user);
 
+       if (have_plaintext) {
+               /* Have plaintext orginal */
+               set_signing_on_cli(cli, pass, ntpword);
+       }
+       
        return True;
 }
 
-#if HAVE_KRB5
 /****************************************************************************
-do a spnego encrypted session setup
+ Send a extended security session setup blob, returning a reply blob.
 ****************************************************************************/
-static BOOL cli_session_setup_spnego(struct cli_state *cli, char *user, 
-                                    char *pass, char *workgroup)
+
+static DATA_BLOB cli_session_setup_blob(struct cli_state *cli, DATA_BLOB blob)
 {
        uint32 capabilities = cli_session_setup_capabilities(cli);
        char *p;
-       DATA_BLOB blob2, negTokenTarg;
+       DATA_BLOB blob2;
+       uint32 len;
 
-       negTokenTarg = spnego_gen_negTokenTarg(cli);
+       blob2 = data_blob(NULL, 0);
 
        capabilities |= CAP_EXTENDED_SECURITY;
 
@@ -334,31 +370,35 @@ static BOOL cli_session_setup_spnego(struct cli_state *cli, char *user,
        memset(cli->outbuf,'\0',smb_size);
 
        set_message(cli->outbuf,12,0,True);
-       CVAL(cli->outbuf,smb_com) = SMBsesssetupX;
+       SCVAL(cli->outbuf,smb_com,SMBsesssetupX);
+
+       set_temp_signing_on_cli(cli);
+
        cli_setup_packet(cli);
                        
-       CVAL(cli->outbuf,smb_vwv0) = 0xFF;
+       SCVAL(cli->outbuf,smb_vwv0,0xFF);
        SSVAL(cli->outbuf,smb_vwv2,CLI_BUFFER_SIZE);
        SSVAL(cli->outbuf,smb_vwv3,2);
-       SSVAL(cli->outbuf,smb_vwv4,0);
+       SSVAL(cli->outbuf,smb_vwv4,1);
        SIVAL(cli->outbuf,smb_vwv5,0);
-       SSVAL(cli->outbuf,smb_vwv7,negTokenTarg.length);
+       SSVAL(cli->outbuf,smb_vwv7,blob.length);
        SIVAL(cli->outbuf,smb_vwv10,capabilities); 
        p = smb_buf(cli->outbuf);
-       memcpy(p, negTokenTarg.data, negTokenTarg.length);
-       p += negTokenTarg.length;
+       memcpy(p, blob.data, blob.length);
+       p += blob.length;
        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;
+               return blob2;
 
        show_msg(cli->inbuf);
 
-       if (cli_is_error(cli)) {
-               return False;
+       if (cli_is_error(cli) && !NT_STATUS_EQUAL(cli_nt_error(cli),
+                                                 NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+               return blob2;
        }
        
        /* use the returned vuid from now on */
@@ -370,38 +410,255 @@ static BOOL cli_session_setup_spnego(struct cli_state *cli, char *user,
 
        p += blob2.length;
        p += clistr_pull(cli, cli->server_os, p, sizeof(fstring), -1, STR_TERMINATE);
-       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);
 
-       fstrcpy(cli->user_name, user);
+       /* w2k with kerberos doesn't properly null terminate this field */
+       len = smb_buflen(cli->inbuf) - PTR_DIFF(p, smb_buf(cli->inbuf));
+       p += clistr_pull(cli, cli->server_type, p, sizeof(fstring), len, 0);
 
-       data_blob_free(negTokenTarg);
+       return blob2;
+}
 
-       /* we don't need this blob until we do NTLMSSP */
-       data_blob_free(blob2);
+#ifdef HAVE_KRB5
+/****************************************************************************
+ Do a spnego/kerberos encrypted session setup.
+****************************************************************************/
+
+static BOOL cli_session_setup_kerberos(struct cli_state *cli, const char *principal, const char *workgroup)
+{
+       DATA_BLOB blob2, negTokenTarg;
+
+       DEBUG(2,("Doing kerberos session setup\n"));
+
+       /* generate the encapsulated kerberos5 ticket */
+       negTokenTarg = spnego_gen_negTokenTarg(principal, 0);
+
+       if (!negTokenTarg.data) return False;
+
+#if 0
+       file_save("negTokenTarg.dat", negTokenTarg.data, negTokenTarg.length);
+#endif
+
+       blob2 = cli_session_setup_blob(cli, negTokenTarg);
+
+       /* we don't need this blob for kerberos */
+       data_blob_free(&blob2);
+
+       data_blob_free(&negTokenTarg);
+
+       return !cli_is_error(cli);
+}
+#endif
+
+/****************************************************************************
+ Do a spnego/NTLMSSP encrypted session setup.
+****************************************************************************/
+
+static BOOL cli_session_setup_ntlmssp(struct cli_state *cli, const char *user, 
+                                     const char *pass, const char *workgroup)
+{
+       DATA_BLOB msg1, struct_blob;
+       DATA_BLOB blob, chal1, chal2, auth, challenge_blob;
+       uint8 challenge[8];
+       uint8 nthash[24], lmhash[24], sess_key[16];
+       uint32 neg_flags, chal_flags, ntlmssp_command, unkn1, unkn2;
+       pstring server_domain;  /* FIX THIS, SHOULD be UCS2-LE */
+
+       neg_flags = NTLMSSP_NEGOTIATE_UNICODE | 
+               NTLMSSP_NEGOTIATE_128 | 
+               NTLMSSP_NEGOTIATE_NTLM |
+               NTLMSSP_REQUEST_TARGET;
+
+       memset(sess_key, 0, 16);
+
+       DEBUG(10, ("sending NTLMSSP_NEGOTIATE\n"));
+
+       /* generate the ntlmssp negotiate packet */
+       msrpc_gen(&blob, "CddAA",
+                 "NTLMSSP",
+                 NTLMSSP_NEGOTIATE,
+                 neg_flags,
+                 workgroup, 
+                 cli->calling.name);
+       DEBUG(10, ("neg_flags: %0X, workgroup: %s, calling name %s\n",
+                 neg_flags, workgroup, cli->calling.name));
+       /* and wrap it in a SPNEGO wrapper */
+       msg1 = gen_negTokenInit(OID_NTLMSSP, blob);
+       data_blob_free(&blob);
+
+       /* now send that blob on its way */
+       blob = cli_session_setup_blob(cli, msg1);
+
+       data_blob_free(&msg1);
+
+       if (!NT_STATUS_EQUAL(cli_nt_error(cli), NT_STATUS_MORE_PROCESSING_REQUIRED))
+               return False;
+
+#if 0
+       file_save("chal.dat", blob.data, blob.length);
+#endif
+
+       /* the server gives us back two challenges */
+       if (!spnego_parse_challenge(blob, &chal1, &chal2)) {
+               DEBUG(3,("Failed to parse challenges\n"));
+               return False;
+       }
+
+       data_blob_free(&blob);
+
+       /*
+        * Ok, chal1 and chal2 are actually two identical copies of
+        * the NTLMSSP Challenge BLOB, and they contain, encoded in them
+        * the challenge to use.
+        */
+
+       if (!msrpc_parse(&chal1, "CdUdbddB",
+                        "NTLMSSP",
+                        &ntlmssp_command, 
+                        &server_domain,
+                        &chal_flags,
+                        &challenge_blob, 8,
+                        &unkn1, &unkn2,
+                        &struct_blob)) {
+         DEBUG(0, ("Failed to parse the NTLMSSP Challenge\n"));
+         return False;
+       }
+                       
+       if (ntlmssp_command != NTLMSSP_CHALLENGE) {
+               DEBUG(0, ("NTLMSSP Response != NTLMSSP_CHALLENGE. Got %0X\n", 
+                       ntlmssp_command));
+               return False;
+       }
+       DEBUG(10, ("Challenge:\n"));
+       dump_data(10, challenge_blob.data, 8);
+
+       /* encrypt the password with the challenge which is in the blob */
+       memcpy(challenge, challenge_blob.data, 8); 
+       SMBencrypt(pass, challenge,lmhash);
+       SMBNTencrypt(pass, challenge,nthash);
+       data_blob_free(&challenge_blob);
+
+#if 0
+       file_save("nthash.dat", nthash, 24);
+       file_save("lmhash.dat", lmhash, 24);
+       file_save("chal1.dat", chal1.data, chal1.length);
+#endif
+
+       data_blob_free(&chal1);
+       data_blob_free(&chal2);
+
+       /* this generates the actual auth packet */
+       msrpc_gen(&blob, "CdBBUUUBd", 
+                 "NTLMSSP", 
+                 NTLMSSP_AUTH, 
+                 lmhash, 24,
+                 nthash, 24,
+                 workgroup, 
+                 user, 
+                 cli->calling.name,
+                 sess_key, 0,
+                 neg_flags);
+
+       /* wrap it in SPNEGO */
+       auth = spnego_gen_auth(blob);
+
+       data_blob_free(&blob);
+
+       /* now send the auth packet and we should be done */
+       blob = cli_session_setup_blob(cli, auth);
+
+       data_blob_free(&auth);
+       data_blob_free(&blob);
+
+       if (cli_is_error(cli))
+               return False;
+
+       set_signing_on_cli(cli, pass, nthash);
 
        return True;
 }
+
+/****************************************************************************
+ Do a spnego encrypted session setup.
+****************************************************************************/
+
+static BOOL cli_session_setup_spnego(struct cli_state *cli, const char *user, 
+                                    const char *pass, const char *workgroup)
+{
+       char *principal;
+       char *OIDs[ASN1_MAX_OIDS];
+       int i;
+       BOOL got_kerberos_mechanism = False;
+       DATA_BLOB blob;
+
+       DEBUG(2,("Doing spnego session setup (blob length=%d)\n", cli->secblob.length));
+
+       /* the server might not even do spnego */
+       if (cli->secblob.length <= 16) {
+               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
 
+       /* there is 16 bytes of GUID before the real spnego packet starts */
+       blob = data_blob(cli->secblob.data+16, cli->secblob.length-16);
+
+       /* the server sent us the first part of the SPNEGO exchange in the negprot 
+          reply */
+       if (!spnego_parse_negTokenInit(blob, OIDs, &principal)) {
+               data_blob_free(&blob);
+               return False;
+       }
+       data_blob_free(&blob);
+
+       /* make sure the server understands kerberos */
+       for (i=0;OIDs[i];i++) {
+               DEBUG(3,("got OID=%s\n", OIDs[i]));
+               if (strcmp(OIDs[i], OID_KERBEROS5_OLD) == 0 ||
+                   strcmp(OIDs[i], OID_KERBEROS5) == 0) {
+                       got_kerberos_mechanism = True;
+               }
+               free(OIDs[i]);
+       }
+       DEBUG(3,("got principal=%s\n", principal));
+
+       fstrcpy(cli->user_name, user);
+
+#ifdef HAVE_KRB5
+       if (got_kerberos_mechanism && cli->use_kerberos) {
+               return cli_session_setup_kerberos(cli, principal, workgroup);
+       }
+#endif
+
+       free(principal);
+
+ntlmssp:
+
+       return cli_session_setup_ntlmssp(cli, user, pass, workgroup);
+}
 
 /****************************************************************************
  Send a session setup. The username and workgroup is in UNIX character
  format and must be converted to DOS codepage format before sending. If the
  password is in plaintext, the same should be done.
 ****************************************************************************/
+
 BOOL cli_session_setup(struct cli_state *cli, 
-                      char *user, 
-                      char *pass, int passlen,
-                      char *ntpass, int ntpasslen,
-                      char *workgroup)
+                      const char *user, 
+                      const char *pass, int passlen,
+                      const char *ntpass, int ntpasslen,
+                      const char *workgroup)
 {
        char *p;
        fstring user2;
 
        /* allow for workgroups as part of the username */
        fstrcpy(user2, user);
-       if ((p=strchr_m(user2,'\\')) || (p=strchr_m(user2,'/'))) {
+       if ((p=strchr_m(user2,'\\')) || (p=strchr_m(user2,'/')) ||
+           (p=strchr_m(user2,*lp_winbind_separator()))) {
                *p = 0;
                user = p+1;
                workgroup = user2;
@@ -415,37 +672,38 @@ BOOL cli_session_setup(struct cli_state *cli,
            flow a bit easier to understand (tridge) */
 
        /* if its an older server then we have to use the older request format */
-       if (cli->protocol < PROTOCOL_NT1) {
-               return cli_session_setup_lanman2(cli, user, pass, passlen);
-       }
+
+       if (cli->protocol < PROTOCOL_NT1)
+               return cli_session_setup_lanman2(cli, user, pass, passlen, workgroup);
 
        /* if no user is supplied then we have to do an anonymous connection.
           passwords are ignored */
-       if (!user || !*user) {
+
+       if (!user || !*user)
                return cli_session_setup_guest(cli);
-       }
 
        /* 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->sec_mode & 1) == 0) {
+
+       if ((cli->sec_mode & NEGOTIATE_SECURITY_USER_LEVEL) == 0)
                return cli_session_setup_plaintext(cli, user, "", workgroup);
-       }
 
-       /* if the server doesn't support encryption then we have to use plaintext. The 
-          second password is ignored */
-       if ((cli->sec_mode & 2) == 0) {
+       /* if the server doesn't support encryption then we have to use 
+          plaintext. The second password is ignored */
+
+       if ((cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) == 0)
                return cli_session_setup_plaintext(cli, user, pass, workgroup);
-       }
 
-#if HAVE_KRB5
+       /* Indidicate signing */
+       
        /* if the server supports extended security then use SPNEGO */
-       if (cli->capabilities & CAP_EXTENDED_SECURITY) {
+
+       if (cli->capabilities & CAP_EXTENDED_SECURITY)
                return cli_session_setup_spnego(cli, user, pass, workgroup);
-       }
-#endif
 
        /* otherwise do a NT1 style session setup */
+
        return cli_session_setup_nt1(cli, user, 
                                     pass, passlen, ntpass, ntpasslen,
                                     workgroup);        
@@ -457,27 +715,28 @@ BOOL cli_session_setup(struct cli_state *cli,
 
 BOOL cli_ulogoff(struct cli_state *cli)
 {
-        memset(cli->outbuf,'\0',smb_size);
-        set_message(cli->outbuf,2,0,True);
-        CVAL(cli->outbuf,smb_com) = SMBulogoffX;
-        cli_setup_packet(cli);
+       memset(cli->outbuf,'\0',smb_size);
+       set_message(cli->outbuf,2,0,True);
+       SCVAL(cli->outbuf,smb_com,SMBulogoffX);
+       cli_setup_packet(cli);
        SSVAL(cli->outbuf,smb_vwv0,0xFF);
        SSVAL(cli->outbuf,smb_vwv2,0);  /* no additional info */
 
-        cli_send_smb(cli);
-        if (!cli_receive_smb(cli))
-                return False;
+       cli_send_smb(cli);
+       if (!cli_receive_smb(cli))
+               return False;
 
-        return !cli_is_error(cli);
+       return !cli_is_error(cli);
 }
 
 /****************************************************************************
-send a tconX
+ Send a tconX.
 ****************************************************************************/
+
 BOOL cli_send_tconX(struct cli_state *cli, 
                    const char *share, const char *dev, const char *pass, int passlen)
 {
-       fstring fullshare, pword, dos_pword;
+       fstring fullshare, pword;
        char *p;
        memset(cli->outbuf,'\0',smb_size);
        memset(cli->inbuf,'\0',smb_size);
@@ -485,20 +744,19 @@ BOOL cli_send_tconX(struct cli_state *cli,
        fstrcpy(cli->share, share);
 
        /* in user level security don't send a password now */
-       if (cli->sec_mode & 1) {
+       if (cli->sec_mode & NEGOTIATE_SECURITY_USER_LEVEL) {
                passlen = 1;
                pass = "";
        }
 
-       if ((cli->sec_mode & 2) && *pass && passlen != 24) {
+       if ((cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) && *pass && passlen != 24) {
                /*
                 * Non-encrypted passwords - convert to DOS codepage before encryption.
                 */
                passlen = 24;
-               clistr_push(cli, dos_pword, pass, -1, STR_TERMINATE);
-               SMBencrypt((uchar *)dos_pword,cli->secblob.data,(uchar *)pword);
+               SMBencrypt(pass,cli->secblob.data,(uchar *)pword);
        } else {
-               if((cli->sec_mode & 3) == 0) {
+               if((cli->sec_mode & (NEGOTIATE_SECURITY_USER_LEVEL|NEGOTIATE_SECURITY_CHALLENGE_RESPONSE)) == 0) {
                        /*
                         * Non-encrypted passwords - convert to DOS codepage before using.
                         */
@@ -517,7 +775,7 @@ BOOL cli_send_tconX(struct cli_state *cli,
        }
 
        set_message(cli->outbuf,4, 0, True);
-       CVAL(cli->outbuf,smb_com) = SMBtconX;
+       SCVAL(cli->outbuf,smb_com,SMBtconX);
        cli_setup_packet(cli);
 
        SSVAL(cli->outbuf,smb_vwv0,0xFF);
@@ -535,21 +793,14 @@ BOOL cli_send_tconX(struct cli_state *cli,
        if (!cli_receive_smb(cli))
                return False;
 
-       if (cli_is_error(cli)) {
+       if (cli_is_error(cli))
                return False;
-       }
 
-       fstrcpy(cli->dev, "A:");
-
-       if (cli->protocol >= PROTOCOL_NT1) {
-               clistr_pull(cli, cli->dev, smb_buf(cli->inbuf), sizeof(fstring), -1, STR_TERMINATE);
-       }
+       clistr_pull(cli, cli->dev, smb_buf(cli->inbuf), sizeof(fstring), -1, STR_TERMINATE|STR_ASCII);
 
-       if (strcasecmp(share,"IPC$")==0) {
+       if (strcasecmp(share,"IPC$")==0)
                fstrcpy(cli->dev, "IPC");
-       }
 
-       /* only grab the device if we have a recent protocol level */
        if (cli->protocol >= PROTOCOL_NT1 &&
            smb_buflen(cli->inbuf) == 3) {
                /* almost certainly win95 - enable bug fixes */
@@ -560,15 +811,15 @@ BOOL cli_send_tconX(struct cli_state *cli,
        return True;
 }
 
-
 /****************************************************************************
-send a tree disconnect
+ Send a tree disconnect.
 ****************************************************************************/
+
 BOOL cli_tdis(struct cli_state *cli)
 {
        memset(cli->outbuf,'\0',smb_size);
        set_message(cli->outbuf,0,0,True);
-       CVAL(cli->outbuf,smb_com) = SMBtdis;
+       SCVAL(cli->outbuf,smb_com,SMBtdis);
        SSVAL(cli->outbuf,smb_tid,cli->cnum);
        cli_setup_packet(cli);
        
@@ -579,15 +830,18 @@ BOOL cli_tdis(struct cli_state *cli)
        return !cli_is_error(cli);
 }
 
-
 /****************************************************************************
-send a negprot command
+ Send a negprot command.
 ****************************************************************************/
+
 void cli_negprot_send(struct cli_state *cli)
 {
        char *p;
        int numprots;
 
+       if (cli->protocol < PROTOCOL_NT1)
+               cli->use_spnego = False;
+
        memset(cli->outbuf,'\0',smb_size);
 
        /* setup the protocol strings */
@@ -601,25 +855,33 @@ void cli_negprot_send(struct cli_state *cli)
                p += clistr_push(cli, p, prots[numprots].name, -1, STR_TERMINATE);
        }
 
-       CVAL(cli->outbuf,smb_com) = SMBnegprot;
+       SCVAL(cli->outbuf,smb_com,SMBnegprot);
        cli_setup_bcc(cli, p);
        cli_setup_packet(cli);
 
-       CVAL(smb_buf(cli->outbuf),0) = 2;
+       SCVAL(smb_buf(cli->outbuf),0,2);
 
        cli_send_smb(cli);
 }
 
-
 /****************************************************************************
-send a negprot command
+ Send a negprot command.
 ****************************************************************************/
+
 BOOL cli_negprot(struct cli_state *cli)
 {
        char *p;
        int numprots;
        int plength;
 
+       if (cli->sign_info.use_smb_signing) {
+               DEBUG(0, ("Cannot send negprot again, particularly after setting up SMB Signing\n"));
+               return False;
+       }
+
+       if (cli->protocol < PROTOCOL_NT1)
+               cli->use_spnego = False;
+
        memset(cli->outbuf,'\0',smb_size);
 
        /* setup the protocol strings */
@@ -638,15 +900,10 @@ BOOL cli_negprot(struct cli_state *cli)
                p += clistr_push(cli, p, prots[numprots].name, -1, STR_TERMINATE);
        }
 
-       CVAL(cli->outbuf,smb_com) = SMBnegprot;
+       SCVAL(cli->outbuf,smb_com,SMBnegprot);
        cli_setup_packet(cli);
 
-       CVAL(smb_buf(cli->outbuf),0) = 2;
-
-       if (cli->use_spnego) {
-               SSVAL(cli->outbuf, smb_flg2, 
-                     SVAL(cli->outbuf, smb_flg2) | FLAGS2_EXTENDED_SECURITY);
-       }
+       SCVAL(smb_buf(cli->outbuf),0,2);
 
        cli_send_smb(cli);
        if (!cli_receive_smb(cli))
@@ -659,7 +916,7 @@ BOOL cli_negprot(struct cli_state *cli)
                return(False);
        }
 
-       cli->protocol = prots[SVAL(cli->inbuf,smb_vwv0)].prot;
+       cli->protocol = prots[SVAL(cli->inbuf,smb_vwv0)].prot;  
 
        if (cli->protocol >= PROTOCOL_NT1) {    
                /* NT protocol */
@@ -678,12 +935,18 @@ BOOL cli_negprot(struct cli_state *cli)
                        cli->writebraw_supported = True;      
                }
                /* work out if they sent us a workgroup */
-               if (smb_buflen(cli->inbuf) > 8) {
+               if (!(cli->capabilities & CAP_EXTENDED_SECURITY) &&
+                   smb_buflen(cli->inbuf) > 8) {
                        clistr_pull(cli, cli->server_domain, 
                                    smb_buf(cli->inbuf)+8, sizeof(cli->server_domain),
                                    smb_buflen(cli->inbuf)-8, STR_UNICODE|STR_NOALIGN);
                }
+
+               if ((cli->sec_mode & NEGOTIATE_SECURITY_SIGNATURES_ENABLED))
+                       cli->sign_info.negotiated_smb_signing = True;
+
        } 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->sesskey = IVAL(cli->inbuf,smb_vwv6);
@@ -696,6 +959,7 @@ BOOL cli_negprot(struct cli_state *cli)
                cli->secblob = data_blob(smb_buf(cli->inbuf),smb_buflen(cli->inbuf));
        } else {
                /* the old core protocol */
+               cli->use_spnego = False;
                cli->sec_mode = 0;
                cli->serverzone = TimeDiff(time(NULL));
        }
@@ -703,17 +967,16 @@ BOOL cli_negprot(struct cli_state *cli)
        cli->max_xmit = MIN(cli->max_xmit, CLI_BUFFER_SIZE);
 
        /* a way to force ascii SMB */
-       if (getenv("CLI_FORCE_ASCII")) {
+       if (getenv("CLI_FORCE_ASCII"))
                cli->capabilities &= ~CAP_UNICODE;
-       }
 
        return True;
 }
 
-
 /****************************************************************************
-  send a session request.  see rfc1002.txt 4.3 and 4.3.2
+ Send a session request. See rfc1002.txt 4.3 and 4.3.2.
 ****************************************************************************/
+
 BOOL cli_session_request(struct cli_state *cli,
                         struct nmb_name *calling, struct nmb_name *called)
 {
@@ -721,10 +984,6 @@ BOOL cli_session_request(struct cli_state *cli,
        int len = 4;
        extern pstring user_socket_options;
 
-       /* 445 doesn't have session request */
-       if (cli->port == 445) return True;
-
-       /* send a session request (RFC 1002) */
        memcpy(&(cli->calling), calling, sizeof(*calling));
        memcpy(&(cli->called ), called , sizeof(*called ));
   
@@ -738,13 +997,26 @@ BOOL cli_session_request(struct cli_state *cli,
        name_mangle(cli->calling.name, p, cli->calling.name_type);
        len += name_len(p);
 
-       /* setup the packet length */
-       _smb_setlen(cli->outbuf,len);
-       CVAL(cli->outbuf,0) = 0x81;
+       /* 445 doesn't have session request */
+       if (cli->port == 445)
+               return True;
 
-#ifdef WITH_SSL
-retry:
-#endif /* WITH_SSL */
+       if (cli->sign_info.use_smb_signing) {
+               DEBUG(0, ("Cannot send session resquest again, particularly after setting up SMB Signing\n"));
+               return False;
+       }
+
+       /* send a session request (RFC 1002) */
+       /* setup the packet length
+         * Remove four bytes from the length count, since the length
+         * field in the NBT Session Service header counts the number
+         * of bytes which follow.  The cli_send_smb() function knows
+         * about this and accounts for those four bytes.
+         * CRH.
+         */
+        len -= 4;
+       _smb_setlen(cli->outbuf,len);
+       SCVAL(cli->outbuf,0,0x81);
 
        cli_send_smb(cli);
        DEBUG(5,("Sent session request\n"));
@@ -791,15 +1063,6 @@ retry:
                }
        } /* C. Hoch 9/14/95 End */
 
-#ifdef WITH_SSL
-    if (CVAL(cli->inbuf,0) == 0x83 && CVAL(cli->inbuf,4) == 0x8e){ /* use ssl */
-        if (!sslutil_fd_is_ssl(cli->fd)){
-            if (sslutil_connect(cli->fd) == 0)
-                goto retry;
-        }
-    }
-#endif /* WITH_SSL */
-
        if (CVAL(cli->inbuf,0) != 0x82) {
                 /* This is the wrong place to put the error... JRA. */
                cli->rap_error = CVAL(cli->inbuf,4);
@@ -809,17 +1072,28 @@ retry:
 }
 
 /****************************************************************************
-open the client sockets
+ Open the client sockets.
 ****************************************************************************/
+
 BOOL cli_connect(struct cli_state *cli, const char *host, struct in_addr *ip)
 {
-       extern struct in_addr ipzero;
        extern pstring user_socket_options;
+       int name_type = 0x20;
+       char *p;
+
+       /* reasonable default hostname */
+       if (!host) host = "*SMBSERVER";
 
        fstrcpy(cli->desthost, host);
+
+       /* allow hostnames of the form NAME#xx and do a netbios lookup */
+       if ((p = strchr(cli->desthost, '#'))) {
+               name_type = strtol(p+1, NULL, 16);              
+               *p = 0;
+       }
        
-       if (!ip || ip_equal(*ip, ipzero)) {
-                if (!resolve_name( cli->desthost, &cli->dest_ip, 0x20)) {
+       if (!ip || is_zero_ip(*ip)) {
+                if (!resolve_name(cli->desthost, &cli->dest_ip, name_type)) {
                         return False;
                 }
                if (ip) *ip = cli->dest_ip;
@@ -839,11 +1113,12 @@ BOOL cli_connect(struct cli_state *cli, const char *host, struct in_addr *ip)
                        cli->fd = open_socket_out(SOCK_STREAM, &cli->dest_ip, 
                                                  port, cli->timeout);
                }
-               if (cli->fd != -1) cli->port = port;
+               if (cli->fd != -1)
+                       cli->port = port;
        }
        if (cli->fd == -1) {
                DEBUG(1,("Error connecting to %s (%s)\n",
-                        inet_ntoa(*ip),strerror(errno)));
+                        ip?inet_ntoa(*ip):host,strerror(errno)));
                return False;
        }
 
@@ -853,253 +1128,212 @@ BOOL cli_connect(struct cli_state *cli, const char *host, struct in_addr *ip)
 }
 
 /****************************************************************************
-re-establishes a connection
+ Initialise client credentials for authenticated pipe access.
 ****************************************************************************/
-BOOL cli_reestablish_connection(struct cli_state *cli)
-{
-       struct nmb_name calling;
-       struct nmb_name called;
-       fstring dest_host;
-       fstring share;
-       fstring dev;
-       BOOL do_tcon = False;
-       int oldfd = cli->fd;
-
-       if (!cli->initialised || cli->fd == -1)
-       {
-               DEBUG(3,("cli_reestablish_connection: not connected\n"));
-               return False;
-       }
 
-       /* copy the parameters necessary to re-establish the connection */
-
-       if (cli->cnum != 0)
-       {
-               fstrcpy(share, cli->share);
-               fstrcpy(dev  , cli->dev);
-               do_tcon = True;
-       }
-
-       memcpy(&called , &(cli->called ), sizeof(called ));
-       memcpy(&calling, &(cli->calling), sizeof(calling));
-       fstrcpy(dest_host, cli->full_dest_host_name);
+static void init_creds(struct ntuser_creds *creds, const char* username,
+                      const char* domain, const char* password)
+{
+       ZERO_STRUCTP(creds);
 
-       DEBUG(5,("cli_reestablish_connection: %s connecting to %s (ip %s) - %s [%s]\n",
-                nmb_namestr(&calling), nmb_namestr(&called), 
-                inet_ntoa(cli->dest_ip),
-                cli->user_name, cli->domain));
+       pwd_set_cleartext(&creds->pwd, password);
 
-       cli->fd = -1;
+       fstrcpy(creds->user_name, username);
+       fstrcpy(creds->domain, domain);
 
-       if (cli_establish_connection(cli,
-                                    dest_host, &cli->dest_ip,
-                                    &calling, &called,
-                                    share, dev, False, do_tcon)) {
-               if ((cli->fd != oldfd) && (oldfd != -1)) {
-                       close( oldfd );
-               }
-               return True;
+       if (!*username) {
+               creds->pwd.null_pwd = True;
        }
-       return False;
 }
 
-/****************************************************************************
-establishes a connection right up to doing tconX, reading in a password.
-****************************************************************************/
-BOOL cli_establish_connection(struct cli_state *cli, 
-                               char *dest_host, struct in_addr *dest_ip,
-                               struct nmb_name *calling, struct nmb_name *called,
-                               char *service, char *service_type,
-                               BOOL do_shutdown, BOOL do_tcon)
+/**
+   establishes a connection right up to doing tconX, password specified.
+   @param output_cli A fully initialised cli structure, non-null only on success
+   @param dest_host The netbios name of the remote host
+   @param dest_ip (optional) The the destination IP, NULL for name based lookup
+   @param port (optional) The destination port (0 for default)
+   @param service (optional) The share to make the connection to.  Should be 'unqualified' in any way.
+   @param service_type The 'type' of serivice. 
+   @param user Username, unix string
+   @param domain User's domain
+   @param password User's password, unencrypted unix string.
+   @param retry BOOL. Did this connection fail with a retryable error ?
+*/
+
+NTSTATUS cli_full_connection(struct cli_state **output_cli, 
+                            const char *my_name, 
+                            const char *dest_host, 
+                            struct in_addr *dest_ip, int port,
+                            const char *service, const char *service_type,
+                            const char *user, const char *domain, 
+                            const char *password, int flags,
+                            BOOL *retry) 
 {
-       DEBUG(5,("cli_establish_connection: %s connecting to %s (%s) - %s [%s]\n",
-                         nmb_namestr(calling), nmb_namestr(called), inet_ntoa(*dest_ip),
-                     cli->user_name, cli->domain));
+       struct ntuser_creds creds;
+       NTSTATUS nt_status;
+       struct nmb_name calling;
+       struct nmb_name called;
+       struct cli_state *cli;
+       struct in_addr ip;
 
-       /* establish connection */
+       if (retry)
+               *retry = False;
 
-       if ((!cli->initialised))
-       {
-               return False;
-       }
+       if (!my_name) 
+               my_name = global_myname();
+       
+       if (!(cli = cli_initialise(NULL)))
+               return NT_STATUS_NO_MEMORY;
+       
+       make_nmb_name(&calling, my_name, 0x0);
+       make_nmb_name(&called , dest_host, 0x20);
 
-       if (cli->fd == -1)
-       {
-               if (!cli_connect(cli, dest_host, dest_ip))
-               {
-                       DEBUG(1,("cli_establish_connection: failed to connect to %s (%s)\n",
-                                         nmb_namestr(called), inet_ntoa(*dest_ip)));
-                       return False;
-               }
+       if (cli_set_port(cli, port) != port) {
+               cli_shutdown(cli);
+               return NT_STATUS_UNSUCCESSFUL;
        }
 
-       if (!cli_session_request(cli, calling, called))
-       {
-               DEBUG(1,("failed session request\n"));
-               if (do_shutdown)
-                       cli_shutdown(cli);
-               return False;
-       }
+       cli_set_timeout(cli, 10000); /* 10 seconds. */
 
-       if (!cli_negprot(cli))
-       {
-               DEBUG(1,("failed negprot\n"));
-               if (do_shutdown)
-                       cli_shutdown(cli);
-               return False;
+       if (dest_ip)
+               ip = *dest_ip;
+       else
+               ZERO_STRUCT(ip);
+
+again:
+
+       DEBUG(3,("Connecting to host=%s share=%s\n", dest_host, service));
+       
+       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)));
+               cli_shutdown(cli);
+               return NT_STATUS_UNSUCCESSFUL;
        }
 
-       if (cli->pwd.cleartext || cli->pwd.null_pwd)
-       {
-               fstring passwd;
-               int pass_len;
+       if (retry)
+               *retry = True;
 
-               if (cli->pwd.null_pwd)
-               {
-                       /* attempt null session */
-                       passwd[0] = 0;
-                       pass_len = 1;
+       if (!cli_session_request(cli, &calling, &called)) {
+               char *p;
+               DEBUG(1,("session request to %s failed (%s)\n", 
+                        called.name, cli_errstr(cli)));
+               if ((p=strchr(called.name, '.')) && !is_ipaddress(called.name)) {
+                       *p = 0;
+                       goto again;
                }
-               else
-               {
-                       /* attempt clear-text session */
-                       pwd_get_cleartext(&(cli->pwd), passwd);
-                       pass_len = strlen(passwd);
+               if (strcmp(called.name, "*SMBSERVER")) {
+                       make_nmb_name(&called , "*SMBSERVER", 0x20);
+                       goto again;
                }
+               return NT_STATUS_UNSUCCESSFUL;
+       }
 
-               /* attempt clear-text session */
-               if (!cli_session_setup(cli, cli->user_name,
-                              passwd, pass_len,
-                              NULL, 0,
-                              cli->domain))
-               {
-                       DEBUG(1,("failed session setup\n"));
-                       if (do_shutdown)
-                       {
-                               cli_shutdown(cli);
-                       }
-                       return False;
-               }
-               if (do_tcon)
-               {
-                       if (!cli_send_tconX(cli, service, service_type,
-                                           (char*)passwd, strlen(passwd)))
-                       {
-                               DEBUG(1,("failed tcon_X\n"));
-                               if (do_shutdown)
-                               {
-                                       cli_shutdown(cli);
-                               }
-                               return False;
-                       }
-               }
+       if (flags & CLI_FULL_CONNECTION_DONT_SPNEGO)
+               cli->use_spnego = False;
+       else if (flags & CLI_FULL_CONNECTION_USE_KERBEROS)
+               cli->use_kerberos = True;
+
+       if (!cli_negprot(cli)) {
+               DEBUG(1,("failed negprot\n"));
+               nt_status = NT_STATUS_UNSUCCESSFUL;
+               cli_shutdown(cli);
+               return nt_status;
        }
-       else
-       {
-               /* attempt encrypted session */
-               unsigned char nt_sess_pwd[24];
-               unsigned char lm_sess_pwd[24];
-
-               /* creates (storing a copy of) and then obtains a 24 byte password OWF */
-               pwd_make_lm_nt_owf(&(cli->pwd), cli->secblob.data);
-               pwd_get_lm_nt_owf(&(cli->pwd), lm_sess_pwd, nt_sess_pwd);
-
-               /* attempt encrypted session */
-               if (!cli_session_setup(cli, cli->user_name,
-                              (char*)lm_sess_pwd, sizeof(lm_sess_pwd),
-                              (char*)nt_sess_pwd, sizeof(nt_sess_pwd),
-                              cli->domain))
-               {
-                       DEBUG(1,("failed session setup\n"));
-                       if (do_shutdown)
-                             cli_shutdown(cli);
-                       return False;
+
+       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)));
+                       cli_shutdown(cli);
+                       if (NT_STATUS_IS_OK(nt_status)) 
+                               nt_status = NT_STATUS_UNSUCCESSFUL;
+                       return nt_status;
                }
+       } 
 
-               DEBUG(1,("session setup ok\n"));
-    
-               if (*cli->server_domain || *cli->server_os || *cli->server_type)
-               {
-                       DEBUG(1,("Domain=[%s] OS=[%s] Server=[%s]\n",
-                                cli->server_domain,
-                                cli->server_os,
-                                cli->server_type));
-               }
-               
-               if (do_tcon)
-               {
-                       if (!cli_send_tconX(cli, service, service_type,
-                                           (char*)nt_sess_pwd, sizeof(nt_sess_pwd)))
-                       {
-                               DEBUG(1,("failed tcon_X\n"));
-                               if (do_shutdown)
-                  cli_shutdown(cli);
-                               return False;
+       if (service) {
+               if (!cli_send_tconX(cli, service, service_type,
+                                   password, strlen(password)+1)) {
+                       nt_status = cli_nt_error(cli);
+                       DEBUG(1,("failed tcon_X with %s\n", nt_errstr(nt_status)));
+                       cli_shutdown(cli);
+                       if (NT_STATUS_IS_OK(nt_status)) {
+                               nt_status = NT_STATUS_UNSUCCESSFUL;
                        }
+                       return nt_status;
                }
        }
 
-       if (do_shutdown)
-               cli_shutdown(cli);
+       init_creds(&creds, user, domain, password);
+       cli_init_creds(cli, &creds);
 
-       return True;
+       *output_cli = cli;
+       return NT_STATUS_OK;
 }
 
-
 /****************************************************************************
  Attempt a NetBIOS session request, falling back to *SMBSERVER if needed.
 ****************************************************************************/
 
-BOOL attempt_netbios_session_request(struct cli_state *cli, char *srchost, char *desthost,
+BOOL attempt_netbios_session_request(struct cli_state *cli, const char *srchost, const char *desthost,
                                      struct in_addr *pdest_ip)
 {
-  struct nmb_name calling, called;
+       struct nmb_name calling, called;
 
-  make_nmb_name(&calling, srchost, 0x0);
+       make_nmb_name(&calling, srchost, 0x0);
 
-  /*
-   * If the called name is an IP address
-   * then use *SMBSERVER immediately.
-   */
+       /*
+        * If the called name is an IP address
+        * then use *SMBSERVER immediately.
+        */
 
-  if(is_ipaddress(desthost))
-    make_nmb_name(&called, "*SMBSERVER", 0x20);
-  else
-    make_nmb_name(&called, desthost, 0x20);
+       if(is_ipaddress(desthost))
+               make_nmb_name(&called, "*SMBSERVER", 0x20);
+       else
+               make_nmb_name(&called, desthost, 0x20);
 
-  if (!cli_session_request(cli, &calling, &called)) {
-    struct nmb_name smbservername;
+       if (!cli_session_request(cli, &calling, &called)) {
+               struct nmb_name smbservername;
 
-    make_nmb_name(&smbservername , "*SMBSERVER", 0x20);
+               make_nmb_name(&smbservername , "*SMBSERVER", 0x20);
 
-    /*
-     * If the name wasn't *SMBSERVER then
-     * try with *SMBSERVER if the first name fails.
-     */
+               /*
+                * If the name wasn't *SMBSERVER then
+                * try with *SMBSERVER if the first name fails.
+                */
 
-    if (nmb_name_equal(&called, &smbservername)) {
+               if (nmb_name_equal(&called, &smbservername)) {
 
-        /*
-         * The name used was *SMBSERVER, don't bother with another name.
-         */
+                       /*
+                        * The name used was *SMBSERVER, don't bother with another name.
+                        */
 
-        DEBUG(0,("attempt_netbios_session_request: %s rejected the session for name *SMBSERVER \
+                       DEBUG(0,("attempt_netbios_session_request: %s rejected the session for name *SMBSERVER \
 with error %s.\n", desthost, cli_errstr(cli) ));
-           cli_shutdown(cli);
-               return False;
-       }
+                       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>.
+                */
 
-    cli_shutdown(cli);
+               cli_close_connection(cli);
 
-    if (!cli_initialise(cli) ||
-        !cli_connect(cli, desthost, pdest_ip) ||
-        !cli_session_request(cli, &calling, &smbservername)) {
-          DEBUG(0,("attempt_netbios_session_request: %s rejected the session for \
+               if (!cli_initialise(cli) ||
+                               !cli_connect(cli, desthost, pdest_ip) ||
+                               !cli_session_request(cli, &calling, &smbservername)) {
+                       DEBUG(0,("attempt_netbios_session_request: %s rejected the session for \
 name *SMBSERVER with error %s\n", desthost, cli_errstr(cli) ));
-          cli_shutdown(cli);
-          return False;
-    }
-  }
+                       return False;
+               }
+       }
 
-  return True;
+       return True;
 }