Refactor the NTLMSSP code again - this time we use function pointers to
[kai/samba.git] / source3 / libsmb / cliconnect.c
index 86daafc50bd4dd7a7043834764e4ff670bd798ff..389b7a1733218222b0a3f1413bd20f6e567ee15c 100644 (file)
@@ -2,6 +2,7 @@
    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
@@ -43,20 +44,18 @@ static const struct {
  Do an old lanman2 style session setup.
 ****************************************************************************/
 
-static BOOL cli_session_setup_lanman2(struct cli_state *cli, char *user, 
-                                     char *pass, int passlen, const char *workgroup)
+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 & NEGOTIATE_SECURITY_USER_LEVEL)) {
+       if (!(cli->sec_mode & NEGOTIATE_SECURITY_USER_LEVEL))
                passlen = 0;
-       }
 
        if (passlen > 0 && (cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) && passlen != 24) {
                /* Encrypted mode needed, and non encrypted password supplied. */
@@ -98,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);   
@@ -117,17 +115,17 @@ static uint32 cli_session_setup_capabilities(struct cli_state *cli)
 {
        uint32 capabilities = CAP_NT_SMBS;
 
-       if (!cli->force_dos_errors) {
+       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;
 }
@@ -141,9 +139,6 @@ static BOOL cli_session_setup_guest(struct cli_state *cli)
        char *p;
        uint32 capabilities = cli_session_setup_capabilities(cli);
 
-       /* Guest cannot use SMB signing. */
-       cli->sign_info.use_smb_signing = False;
-
        set_message(cli->outbuf,13,0,True);
        SCVAL(cli->outbuf,smb_com,SMBsesssetupX);
        cli_setup_packet(cli);
@@ -169,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);
 
@@ -189,16 +183,12 @@ static BOOL cli_session_setup_guest(struct cli_state *cli)
  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|STR_ASCII);
-
        set_message(cli->outbuf,13,0,True);
        SCVAL(cli->outbuf,smb_com,SMBsesssetupX);
        cli_setup_packet(cli);
@@ -208,12 +198,11 @@ static BOOL cli_session_setup_plaintext(struct cli_state *cli, char *user,
        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);
@@ -226,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);
@@ -240,38 +228,74 @@ 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
    @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. Ignore ntpass. */
                passlen = 24;
                ntpasslen = 24;
-               SMBencrypt((uchar *)pass,cli->secblob.data,(uchar *)pword);
-               SMBNTencrypt((uchar *)pass,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 */
@@ -292,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);
        
@@ -318,6 +343,11 @@ 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;
 }
 
@@ -341,6 +371,9 @@ static DATA_BLOB cli_session_setup_blob(struct cli_state *cli, DATA_BLOB blob)
 
        set_message(cli->outbuf,12,0,True);
        SCVAL(cli->outbuf,smb_com,SMBsesssetupX);
+
+       set_temp_signing_on_cli(cli);
+
        cli_setup_packet(cli);
                        
        SCVAL(cli->outbuf,smb_vwv0,0xFF);
@@ -356,8 +389,8 @@ static DATA_BLOB cli_session_setup_blob(struct cli_state *cli, DATA_BLOB blob)
        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 blob2;
 
@@ -385,20 +418,19 @@ static DATA_BLOB cli_session_setup_blob(struct cli_state *cli, DATA_BLOB blob)
        return blob2;
 }
 
-
 #ifdef HAVE_KRB5
 /****************************************************************************
  Do a spnego/kerberos encrypted session setup.
 ****************************************************************************/
 
-static BOOL cli_session_setup_kerberos(struct cli_state *cli, char *principal, char *workgroup)
+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(cli, principal);
+       negTokenTarg = spnego_gen_negTokenTarg(principal, 0);
 
        if (!negTokenTarg.data) return False;
 
@@ -421,31 +453,36 @@ static BOOL cli_session_setup_kerberos(struct cli_state *cli, char *principal, c
  Do a spnego/NTLMSSP encrypted session setup.
 ****************************************************************************/
 
-static BOOL cli_session_setup_ntlmssp(struct cli_state *cli, char *user, 
-                                     char *pass, char *workgroup)
+static BOOL cli_session_setup_ntlmssp(struct cli_state *cli, const char *user, 
+                                     const char *pass, const char *workgroup)
 {
-       const char *mechs[] = {OID_NTLMSSP, NULL};
-       DATA_BLOB msg1;
-       DATA_BLOB blob, chal1, chal2, auth;
+       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;
+       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_LM_KEY | 
-               NTLMSSP_NEGOTIATE_NTLM;
+               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, "CddB",
+       msrpc_gen(&blob, "CddAA",
                  "NTLMSSP",
                  NTLMSSP_NEGOTIATE,
                  neg_flags,
-                 sess_key, 16);
-
+                 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_negTokenTarg(mechs, blob);
+       msg1 = gen_negTokenInit(OID_NTLMSSP, blob);
        data_blob_free(&blob);
 
        /* now send that blob on its way */
@@ -453,9 +490,8 @@ static BOOL cli_session_setup_ntlmssp(struct cli_state *cli, char *user,
 
        data_blob_free(&msg1);
 
-       if (!NT_STATUS_EQUAL(cli_nt_error(cli), NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+       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);
@@ -469,10 +505,38 @@ static BOOL cli_session_setup_ntlmssp(struct cli_state *cli, char *user,
 
        data_blob_free(&blob);
 
-       /* encrypt the password with the challenge */
-       memcpy(challenge, chal1.data + 24, 8);
-       SMBencrypt((unsigned char *)pass, challenge,lmhash);
-       SMBNTencrypt((unsigned char *)pass, challenge,nthash);
+       /*
+        * 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);
@@ -492,7 +556,7 @@ static BOOL cli_session_setup_ntlmssp(struct cli_state *cli, char *user,
                  workgroup, 
                  user, 
                  cli->calling.name,
-                 sess_key, 16,
+                 sess_key, 0,
                  neg_flags);
 
        /* wrap it in SPNEGO */
@@ -506,26 +570,31 @@ static BOOL cli_session_setup_ntlmssp(struct cli_state *cli, char *user,
        data_blob_free(&auth);
        data_blob_free(&blob);
 
-       return !cli_is_error(cli);
+       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, char *user, 
-                                    char *pass, char *workgroup)
+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];
-       uint8 guid[16];
        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) {
+       if (cli->secblob.length <= 16) {
                DEBUG(3,("server didn't supply a full spnego negprot\n"));
                goto ntlmssp;
        }
@@ -534,11 +603,16 @@ static BOOL cli_session_setup_spnego(struct cli_state *cli, char *user,
        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(cli->secblob, guid, OIDs, &principal)) {
+       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++) {
@@ -573,10 +647,10 @@ ntlmssp:
 ****************************************************************************/
 
 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;
@@ -598,35 +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) {
+
+       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 & NEGOTIATE_SECURITY_USER_LEVEL) == 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 & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) == 0) {
+
+       if ((cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) == 0)
                return cli_session_setup_plaintext(cli, user, pass, workgroup);
-       }
 
+       /* 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);
-       }
 
        /* otherwise do a NT1 style session setup */
+
        return cli_session_setup_nt1(cli, user, 
                                     pass, passlen, ntpass, ntpasslen,
                                     workgroup);        
@@ -638,18 +715,18 @@ 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);
-        SCVAL(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);
 }
 
 /****************************************************************************
@@ -659,7 +736,7 @@ BOOL cli_ulogoff(struct cli_state *cli)
 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);
@@ -677,8 +754,7 @@ BOOL cli_send_tconX(struct cli_state *cli,
                 * 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 & (NEGOTIATE_SECURITY_USER_LEVEL|NEGOTIATE_SECURITY_CHALLENGE_RESPONSE)) == 0) {
                        /*
@@ -717,15 +793,13 @@ 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;
-       }
 
        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");
-       }
 
        if (cli->protocol >= PROTOCOL_NT1 &&
            smb_buflen(cli->inbuf) == 3) {
@@ -765,9 +839,8 @@ void cli_negprot_send(struct cli_state *cli)
        char *p;
        int numprots;
 
-       if (cli->protocol < PROTOCOL_NT1) {
+       if (cli->protocol < PROTOCOL_NT1)
                cli->use_spnego = False;
-       }
 
        memset(cli->outbuf,'\0',smb_size);
 
@@ -801,10 +874,14 @@ BOOL cli_negprot(struct cli_state *cli)
        int numprots;
        int plength;
 
-       if (cli->protocol < PROTOCOL_NT1) {
-               cli->use_spnego = False;
+       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 */
@@ -865,12 +942,8 @@ BOOL cli_negprot(struct cli_state *cli)
                                    smb_buflen(cli->inbuf)-8, STR_UNICODE|STR_NOALIGN);
                }
 
-               /* A way to attempt to force SMB signing */
-               if (getenv("CLI_FORCE_SMB_SIGNING"))
-                       cli->sign_info.use_smb_signing = True;
-
-               if (cli->sign_info.use_smb_signing && !(cli->sec_mode & NEGOTIATE_SECURITY_SIGNATURES_ENABLED))
-                       cli->sign_info.use_smb_signing = False;
+               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;
@@ -884,21 +957,18 @@ BOOL cli_negprot(struct cli_state *cli)
                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));
-               cli->sign_info.use_smb_signing = False;
        } else {
                /* the old core protocol */
                cli->use_spnego = False;
                cli->sec_mode = 0;
                cli->serverzone = TimeDiff(time(NULL));
-               cli->sign_info.use_smb_signing = False;
        }
 
        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;
 }
@@ -914,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 ));
   
@@ -931,7 +997,24 @@ 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 */
+       /* 445 doesn't have session request */
+       if (cli->port == 445)
+               return True;
+
+       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);
 
@@ -1030,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;
        }
 
@@ -1047,8 +1131,8 @@ BOOL cli_connect(struct cli_state *cli, const char *host, struct in_addr *ip)
  Initialise client credentials for authenticated pipe access.
 ****************************************************************************/
 
-static void init_creds(struct ntuser_creds *creds, char* username,
-                      char* domain, char* password)
+static void init_creds(struct ntuser_creds *creds, const char* username,
+                      const char* domain, const char* password)
 {
        ZERO_STRUCTP(creds);
 
@@ -1062,16 +1146,28 @@ static void init_creds(struct ntuser_creds *creds, char* username,
        }
 }
 
-/****************************************************************************
- Establishes a connection right up to doing tconX, password specified.
-****************************************************************************/
+/**
+   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, 
+                            const char *my_name, 
+                            const char *dest_host, 
                             struct in_addr *dest_ip, int port,
-                            char *service, char *service_type,
-                            char *user, char *domain, 
-                            char *password) 
+                            const char *service, const char *service_type,
+                            const char *user, const char *domain, 
+                            const char *password, int flags,
+                            BOOL *retry) 
 {
        struct ntuser_creds creds;
        NTSTATUS nt_status;
@@ -1079,42 +1175,50 @@ NTSTATUS cli_full_connection(struct cli_state **output_cli,
        struct nmb_name called;
        struct cli_state *cli;
        struct in_addr ip;
-       
-       if (!output_cli)
-               DEBUG(0, ("output_cli is NULL!?!"));
 
-       *output_cli = NULL;
-       
-       make_nmb_name(&calling, my_name, 0x0);
-       make_nmb_name(&called , dest_host, 0x20);
-
-again:
+       if (retry)
+               *retry = 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_set_port(cli, port) != port) {
                cli_shutdown(cli);
                return NT_STATUS_UNSUCCESSFUL;
        }
 
-       ip = *dest_ip;
-       
+       cli_set_timeout(cli, 10000); /* 10 seconds. */
+
+       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(*dest_ip)));
+                        nmb_namestr(&called), inet_ntoa(ip)));
                cli_shutdown(cli);
                return NT_STATUS_UNSUCCESSFUL;
        }
 
+       if (retry)
+               *retry = True;
+
        if (!cli_session_request(cli, &calling, &called)) {
                char *p;
                DEBUG(1,("session request to %s failed (%s)\n", 
                         called.name, cli_errstr(cli)));
-               cli_shutdown(cli);
-               if ((p=strchr(called.name, '.'))) {
+               if ((p=strchr(called.name, '.')) && !is_ipaddress(called.name)) {
                        *p = 0;
                        goto again;
                }
@@ -1125,6 +1229,11 @@ again:
                return NT_STATUS_UNSUCCESSFUL;
        }
 
+       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;
@@ -1135,19 +1244,23 @@ again:
        if (!cli_session_setup(cli, user, password, strlen(password)+1, 
                               password, strlen(password)+1, 
                               domain)) {
-               DEBUG(1,("failed session setup\n"));
-               nt_status = cli_nt_error(cli);
-               cli_shutdown(cli);
-               if (NT_STATUS_IS_OK(nt_status)) 
-                       nt_status = NT_STATUS_UNSUCCESSFUL;
-               return nt_status;
+               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;
+               }
        } 
 
        if (service) {
                if (!cli_send_tconX(cli, service, service_type,
-                                   (char*)password, strlen(password)+1)) {
-                       DEBUG(1,("failed tcon_X\n"));
+                                   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;
@@ -1167,7 +1280,7 @@ again:
  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;
@@ -1202,18 +1315,22 @@ BOOL attempt_netbios_session_request(struct cli_state *cli, char *srchost, char
 
                        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;
                }
 
-               cli_shutdown(cli);
+               /*
+                * 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_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 \
 name *SMBSERVER with error %s\n", desthost, cli_errstr(cli) ));
-                       cli_shutdown(cli);
                        return False;
                }
        }