This is another rather major change to the samba authenticaion
[kai/samba.git] / source3 / libsmb / cliconnect.c
index 74a10ddf8b5185cc884142548ddfc7a33f1daff0..4ea19db9ec61fa53f6f319334352900414748600 100644 (file)
@@ -24,9 +24,9 @@
 #include "includes.h"
 
 
-static  struct {
+static const struct {
     int prot;
-    char *name;
+    const char *name;
   }
 prots[] = 
     {
@@ -42,12 +42,527 @@ prots[] =
     };
 
 
+/****************************************************************************
+do an old lanman2 style session setup
+****************************************************************************/
+static BOOL cli_session_setup_lanman2(struct cli_state *cli, char *user, 
+                                     char *pass, int passlen)
+{
+       fstring pword;
+       char *p;
+
+       if (passlen > sizeof(pword)-1) {
+               return False;
+       }
+
+       /* if in share level security then don't send a password now */
+       if (!(cli->sec_mode & 1)) {
+               passlen = 0;
+       }
+
+       if (passlen > 0 && (cli->sec_mode & 2) && 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) {
+               /* Encrypted mode needed, and encrypted password supplied. */
+               memcpy(pword, pass, passlen);
+       } else if (passlen > 0) {
+               /* Plaintext mode needed, assume plaintext supplied. */
+               passlen = clistr_push(cli, pword, pass, -1, STR_TERMINATE);
+       }
+
+       /* send a session setup command */
+       memset(cli->outbuf,'\0',smb_size);
+       set_message(cli->outbuf,10, 0, True);
+       CVAL(cli->outbuf,smb_com) = SMBsesssetupX;
+       cli_setup_packet(cli);
+       
+       CVAL(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);
+       SIVAL(cli->outbuf,smb_vwv5,cli->sesskey);
+       SSVAL(cli->outbuf,smb_vwv7,passlen);
+
+       p = smb_buf(cli->outbuf);
+       memcpy(p,pword,passlen);
+       p += passlen;
+       p += clistr_push(cli, p, user, -1, STR_TERMINATE);
+       cli_setup_bcc(cli, p);
+
+       cli_send_smb(cli);
+       if (!cli_receive_smb(cli))
+               return False;
+
+       show_msg(cli->inbuf);
+
+       if (cli_is_error(cli)) {
+               return False;
+       }
+       
+       /* use the returned vuid from now on */
+       cli->vuid = SVAL(cli->inbuf,smb_uid);   
+       fstrcpy(cli->user_name, user);
+
+       return True;
+}
+
+
+/****************************************************************************
+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")) {
+               capabilities |= CAP_STATUS32;
+       }
+
+       if (cli->use_level_II_oplocks) {
+               capabilities |= CAP_LEVEL_II_OPLOCKS;
+       }
+
+       if (cli->capabilities & CAP_UNICODE) {
+               capabilities |= CAP_UNICODE;
+       }
+
+       return capabilities;
+}
+
+
+/****************************************************************************
+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;
+       cli_setup_packet(cli);
+                       
+       CVAL(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,0);
+       SSVAL(cli->outbuf,smb_vwv8,0);
+       SIVAL(cli->outbuf,smb_vwv11,capabilities); 
+       p = smb_buf(cli->outbuf);
+       p += clistr_push(cli, p, "", -1, STR_TERMINATE); /* username */
+       p += clistr_push(cli, p, "", -1, STR_TERMINATE); /* workgroup */
+       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;
+       
+       show_msg(cli->inbuf);
+       
+       if (cli_is_error(cli)) {
+               return False;
+       }
+
+       cli->vuid = SVAL(cli->inbuf,smb_uid);
+
+       p = smb_buf(cli->inbuf);
+       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, "");
+
+       return True;
+}
+
+
+/****************************************************************************
+do a NT1 plaintext session setup
+****************************************************************************/
+static BOOL cli_session_setup_plaintext(struct cli_state *cli, char *user, 
+                                       char *pass, 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);
+       CVAL(cli->outbuf,smb_com) = SMBsesssetupX;
+       cli_setup_packet(cli);
+                       
+       CVAL(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, user, -1, STR_TERMINATE); /* username */
+       p += clistr_push(cli, p, workgroup, -1, STR_TERMINATE); /* workgroup */
+       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;
+       
+       show_msg(cli->inbuf);
+       
+       if (cli_is_error(cli)) {
+               return False;
+       }
+
+       cli->vuid = SVAL(cli->inbuf,smb_uid);
+       p = smb_buf(cli->inbuf);
+       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);
+
+       return True;
+}
+
+
+/****************************************************************************
+do a NT1 NTLM/LM encrypted session setup
+****************************************************************************/
+static BOOL cli_session_setup_nt1(struct cli_state *cli, char *user, 
+                                 char *pass, int passlen,
+                                 char *ntpass, int ntpasslen,
+                                 char *workgroup)
+{
+       uint32 capabilities = cli_session_setup_capabilities(cli);
+       fstring pword, ntpword;
+       char *p;
+
+       if (passlen > sizeof(pword)-1 || ntpasslen > sizeof(ntpword)-1) {
+               return False;
+       }
+
+       if (passlen != 24) {
+               /* non encrypted password supplied. */
+               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);
+       } else {
+               memcpy(pword, pass, passlen);
+               memcpy(ntpword, ntpass, ntpasslen);
+       }
+
+       /* send a session setup command */
+       memset(cli->outbuf,'\0',smb_size);
+
+       set_message(cli->outbuf,13,0,True);
+       CVAL(cli->outbuf,smb_com) = SMBsesssetupX;
+       cli_setup_packet(cli);
+                       
+       CVAL(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,ntpasslen);
+       SIVAL(cli->outbuf,smb_vwv11,capabilities); 
+       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, "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;
+
+       show_msg(cli->inbuf);
+
+       if (cli_is_error(cli)) {
+               return False;
+       }
+       
+       /* use the returned vuid from now on */
+       cli->vuid = SVAL(cli->inbuf,smb_uid);
+       
+       p = smb_buf(cli->inbuf);
+       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);
+
+       return True;
+}
+
+
+/****************************************************************************
+send a extended security session setup blob, returning a reply blob
+****************************************************************************/
+static DATA_BLOB cli_session_setup_blob(struct cli_state *cli, DATA_BLOB blob)
+{
+       uint32 capabilities = cli_session_setup_capabilities(cli);
+       char *p;
+       DATA_BLOB blob2;
+
+       blob2 = data_blob(NULL, 0);
+
+       capabilities |= CAP_EXTENDED_SECURITY;
+
+       /* send a session setup command */
+       memset(cli->outbuf,'\0',smb_size);
+
+       set_message(cli->outbuf,12,0,True);
+       CVAL(cli->outbuf,smb_com) = SMBsesssetupX;
+       cli_setup_packet(cli);
+                       
+       CVAL(cli->outbuf,smb_vwv0) = 0xFF;
+       SSVAL(cli->outbuf,smb_vwv2,CLI_BUFFER_SIZE);
+       SSVAL(cli->outbuf,smb_vwv3,2);
+       SSVAL(cli->outbuf,smb_vwv4,1);
+       SIVAL(cli->outbuf,smb_vwv5,0);
+       SSVAL(cli->outbuf,smb_vwv7,blob.length);
+       SIVAL(cli->outbuf,smb_vwv10,capabilities); 
+       p = smb_buf(cli->outbuf);
+       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 blob2;
+
+       show_msg(cli->inbuf);
+
+       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 */
+       cli->vuid = SVAL(cli->inbuf,smb_uid);
+       
+       p = smb_buf(cli->inbuf);
+
+       blob2 = data_blob(p, SVAL(cli->inbuf, smb_vwv3));
+
+       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);
+
+       return blob2;
+}
+
+
+#if HAVE_KRB5
+/****************************************************************************
+do a spnego/kerberos encrypted session setup
+****************************************************************************/
+static BOOL cli_session_setup_kerberos(struct cli_state *cli, char *principal, char *workgroup)
+{
+       DATA_BLOB blob2, negTokenTarg;
+
+       d_printf("Doing kerberos session setup\n");
+
+       /* generate the encapsulated kerberos5 ticket */
+       negTokenTarg = spnego_gen_negTokenTarg(cli, principal);
+
+       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, char *user, 
+                                     char *pass, char *workgroup)
+{
+       const char *mechs[] = {OID_NTLMSSP, NULL};
+       DATA_BLOB msg1;
+       DATA_BLOB blob, chal1, chal2, auth;
+       uint8 challenge[8];
+       uint8 nthash[24], lmhash[24], sess_key[16];
+       uint32 neg_flags;
+
+       neg_flags = NTLMSSP_NEGOTIATE_UNICODE | 
+               NTLMSSP_NEGOTIATE_LM_KEY | 
+               NTLMSSP_NEGOTIATE_NTLM;
+
+       memset(sess_key, 0, 16);
+
+       /* generate the ntlmssp negotiate packet */
+       msrpc_gen(&blob, "CddB",
+                 "NTLMSSP",
+                 NTLMSSP_NEGOTIATE,
+                 neg_flags,
+                 sess_key, 16);
+
+       /* and wrap it in a SPNEGO wrapper */
+       msg1 = gen_negTokenTarg(mechs, 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);
+
+       /* encrypt the password with the challenge */
+       memcpy(challenge, chal1.data + 24, 8);
+       SMBencrypt((unsigned char *)pass, challenge,lmhash);
+       SMBNTencrypt((unsigned char *)pass, challenge,nthash);
+
+#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, 16,
+                 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);
+
+       return !cli_is_error(cli);
+}
+
+
+/****************************************************************************
+do a spnego encrypted session setup
+****************************************************************************/
+static BOOL cli_session_setup_spnego(struct cli_state *cli, char *user, 
+                                    char *pass, char *workgroup)
+{
+       char *principal;
+       char *OIDs[ASN1_MAX_OIDS];
+       uint8 guid[16];
+       int i;
+       BOOL got_kerberos_mechanism = False;
+
+       d_printf("Doing spnego session setup\n");
+
+       /* 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
+
+       /* the server sent us the first part of the SPNEGO exchange in the negprot 
+          reply */
+       if (!spnego_parse_negTokenInit(cli->secblob, guid, OIDs, &principal)) {
+               return False;
+       }
+
+       /* 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);
+
+#if 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,
@@ -55,12 +570,11 @@ BOOL cli_session_setup(struct cli_state *cli,
                       char *workgroup)
 {
        char *p;
-       fstring pword, ntpword;
        fstring user2;
 
        /* allow for workgroups as part of the username */
        fstrcpy(user2, user);
-       if ((p=strchr(user2,'\\')) || (p=strchr(user2,'/'))) {
+       if ((p=strchr_m(user2,'\\')) || (p=strchr_m(user2,'/'))) {
                *p = 0;
                user = p+1;
                workgroup = user2;
@@ -69,134 +583,43 @@ BOOL cli_session_setup(struct cli_state *cli,
        if (cli->protocol < PROTOCOL_LANMAN1)
                return True;
 
-       if (passlen > sizeof(pword)-1 || ntpasslen > sizeof(ntpword)-1) {
-               return False;
-       }
+       /* now work out what sort of session setup we are going to
+           do. I have split this into separate functions to make the
+           flow a bit easier to understand (tridge) */
 
-       if (((passlen == 0) || (passlen == 1)) && (pass[0] == '\0')) {
-               /* Null session connect. */
-               pword[0] = '\0';
-               ntpword[0] = '\0';
-       } else {
-               if ((cli->sec_mode & 2) && passlen != 24) {
-                       /*
-                        * Encrypted mode needed, and non encrypted password supplied.
-                        */
-                       passlen = 24;
-                       ntpasslen = 24;
-                       fstrcpy(pword, pass);
-                       unix_to_dos(pword,True);
-                       fstrcpy(ntpword, ntpass);;
-                       SMBencrypt((uchar *)pword,(uchar *)cli->cryptkey,(uchar *)pword);
-                       SMBNTencrypt((uchar *)ntpword,(uchar *)cli->cryptkey,(uchar *)ntpword);
-               } else if ((cli->sec_mode & 2) && passlen == 24) {
-                       /*
-                        * Encrypted mode needed, and encrypted password supplied.
-                        */
-                       memcpy(pword, pass, passlen);
-                       if(ntpasslen == 24) {
-                               memcpy(ntpword, ntpass, ntpasslen);
-                       } else {
-                               fstrcpy(ntpword, "");
-                               ntpasslen = 0;
-                       }
-               } else {
-                       /*
-                        * Plaintext mode needed, assume plaintext supplied.
-                        */
-                       passlen = clistr_push(cli, pword, pass, -1, CLISTR_CONVERT|CLISTR_TERMINATE);
-                       fstrcpy(ntpword, "");
-                       ntpasslen = 0;
-               }
+       /* 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 in share level security then don't send a password now */
-       if (!(cli->sec_mode & 1)) {
-               fstrcpy(pword, "");
-               passlen=1;
-               fstrcpy(ntpword, "");
-               ntpasslen=1;
-       } 
+       /* if no user is supplied then we have to do an anonymous connection.
+          passwords are ignored */
+       if (!user || !*user) {
+               return cli_session_setup_guest(cli);
+       }
 
-       /* send a session setup command */
-       memset(cli->outbuf,'\0',smb_size);
+       /* 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) {
+               return cli_session_setup_plaintext(cli, user, "", workgroup);
+       }
 
-       if (cli->protocol < PROTOCOL_NT1)
-       {
-               set_message(cli->outbuf,10,
-                           clistr_align(cli, 1) + 
-                           clistr_push_size(cli, user, -1, CLISTR_TERMINATE|CLISTR_CONVERT) + 
-                           passlen,True);
-               CVAL(cli->outbuf,smb_com) = SMBsesssetupX;
-               cli_setup_packet(cli);
-
-               CVAL(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);
-               SIVAL(cli->outbuf,smb_vwv5,cli->sesskey);
-               SSVAL(cli->outbuf,smb_vwv7,passlen);
-               p = smb_buf(cli->outbuf);
-               p += clistr_align(cli, PTR_DIFF(p,cli->outbuf));
-               memcpy(p,pword,passlen);
-               p += passlen;
-               clistr_push(cli, p, user, -1, CLISTR_CONVERT|CLISTR_UPPER|CLISTR_TERMINATE);
+       /* if the server doesn't support encryption then we have to use plaintext. The 
+          second password is ignored */
+       if ((cli->sec_mode & 2) == 0) {
+               return cli_session_setup_plaintext(cli, user, pass, workgroup);
        }
-       else
-       {
-               set_message(cli->outbuf,13,0,True);
-               CVAL(cli->outbuf,smb_com) = SMBsesssetupX;
-               cli_setup_packet(cli);
-               
-               CVAL(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,ntpasslen);
-               SSVAL(cli->outbuf,smb_vwv11,CAP_NT_SMBS|(cli->use_level_II_oplocks ? CAP_LEVEL_II_OPLOCKS : 0));
-               p = smb_buf(cli->outbuf);
-               p += clistr_align(cli, PTR_DIFF(p,cli->outbuf));
-               memcpy(p,pword,passlen); 
-               p += SVAL(cli->outbuf,smb_vwv7);
-               memcpy(p,ntpword,ntpasslen); 
-               p += SVAL(cli->outbuf,smb_vwv8);
-               p += clistr_push(cli, p, user, -1, CLISTR_CONVERT|CLISTR_TERMINATE|CLISTR_UPPER);
-               p += clistr_push(cli, p, workgroup, -1, CLISTR_CONVERT|CLISTR_TERMINATE|CLISTR_UPPER);
-               p += clistr_push(cli, p, "Unix", -1, CLISTR_CONVERT|CLISTR_TERMINATE);
-               p += clistr_push(cli, p, "Samba", -1, CLISTR_CONVERT|CLISTR_TERMINATE);
-               set_message(cli->outbuf,13,PTR_DIFF(p,smb_buf(cli->outbuf)),False);
-       }
-
-      cli_send_smb(cli);
-      if (!cli_receive_smb(cli))
-             return False;
 
-      show_msg(cli->inbuf);
+       /* if the server supports extended security then use SPNEGO */
+       if (cli->capabilities & CAP_EXTENDED_SECURITY) {
+               return cli_session_setup_spnego(cli, user, pass, workgroup);
+       }
 
-      if (CVAL(cli->inbuf,smb_rcls) != 0) {
-             return False;
-      }
-
-      /* use the returned vuid from now on */
-      cli->vuid = SVAL(cli->inbuf,smb_uid);
-
-      if (cli->protocol >= PROTOCOL_NT1) {
-             /*
-              * Save off some of the connected server
-              * info.
-              */
-             char *p = smb_buf(cli->inbuf);
-             p += clistr_align(cli, PTR_DIFF(p,cli->outbuf));
-             p += clistr_pull(cli, cli->server_os, p, sizeof(fstring), -1, CLISTR_TERMINATE|CLISTR_CONVERT);
-             p += clistr_pull(cli, cli->server_type, p, sizeof(fstring), -1, CLISTR_TERMINATE|CLISTR_CONVERT);
-             p += clistr_pull(cli, cli->server_domain, p, sizeof(fstring), -1, CLISTR_TERMINATE|CLISTR_CONVERT);
-      }
-
-      fstrcpy(cli->user_name, user);
-
-      return True;
+       /* otherwise do a NT1 style session setup */
+       return cli_session_setup_nt1(cli, user, 
+                                    pass, passlen, ntpass, ntpasslen,
+                                    workgroup);        
 }
 
 /****************************************************************************
@@ -216,14 +639,14 @@ BOOL cli_ulogoff(struct cli_state *cli)
         if (!cli_receive_smb(cli))
                 return False;
 
-        return CVAL(cli->inbuf,smb_rcls) == 0;
+        return !cli_is_error(cli);
 }
 
 /****************************************************************************
 send a tconX
 ****************************************************************************/
 BOOL cli_send_tconX(struct cli_state *cli, 
-                   char *share, char *dev, char *pass, int passlen)
+                   const char *share, const char *dev, const char *pass, int passlen)
 {
        fstring fullshare, pword, dos_pword;
        char *p;
@@ -243,30 +666,28 @@ BOOL cli_send_tconX(struct cli_state *cli,
                 * Non-encrypted passwords - convert to DOS codepage before encryption.
                 */
                passlen = 24;
-               fstrcpy(dos_pword,pass);
-               unix_to_dos(dos_pword,True);
-               SMBencrypt((uchar *)dos_pword,(uchar *)cli->cryptkey,(uchar *)pword);
+               clistr_push(cli, dos_pword, pass, -1, STR_TERMINATE);
+               SMBencrypt((uchar *)dos_pword,cli->secblob.data,(uchar *)pword);
        } else {
                if((cli->sec_mode & 3) == 0) {
                        /*
                         * Non-encrypted passwords - convert to DOS codepage before using.
                         */
-                       passlen = clistr_push(cli, pword, pass, -1, CLISTR_CONVERT|CLISTR_TERMINATE);
+                       passlen = clistr_push(cli, pword, pass, -1, STR_TERMINATE);
                } else {
                        memcpy(pword, pass, passlen);
                }
        }
 
-       slprintf(fullshare, sizeof(fullshare)-1,
-                "\\\\%s\\%s", cli->desthost, share);
-       unix_to_dos(fullshare, True);
-       strupper(fullshare);
+       if (cli->port == 445) {
+               slprintf(fullshare, sizeof(fullshare)-1,
+                        "%s", share);
+       } else {
+               slprintf(fullshare, sizeof(fullshare)-1,
+                        "\\\\%s\\%s", cli->desthost, share);
+       }
 
-       set_message(cli->outbuf,4,
-                   clistr_push_size(cli, fullshare, -1, CLISTR_TERMINATE | CLISTR_CONVERT) +
-                   passlen + 
-                   1+strlen(dev),
-                   True);
+       set_message(cli->outbuf,4, 0, True);
        CVAL(cli->outbuf,smb_com) = SMBtconX;
        cli_setup_packet(cli);
 
@@ -276,32 +697,25 @@ BOOL cli_send_tconX(struct cli_state *cli,
        p = smb_buf(cli->outbuf);
        memcpy(p,pword,passlen);
        p += passlen;
-       p += clistr_push(cli, p, fullshare, -1, CLISTR_CONVERT | CLISTR_TERMINATE);
+       p += clistr_push(cli, p, fullshare, -1, STR_TERMINATE |STR_UPPER);
        fstrcpy(p, dev); p += strlen(dev)+1;
 
-       set_message(cli->outbuf,4,PTR_DIFF(p,smb_buf(cli->outbuf)),False);
-
-       SCVAL(cli->inbuf,smb_rcls, 1);
+       cli_setup_bcc(cli, p);
 
        cli_send_smb(cli);
        if (!cli_receive_smb(cli))
                return False;
 
-       if (CVAL(cli->inbuf,smb_rcls) != 0) {
+       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, CLISTR_TERMINATE | CLISTR_CONVERT);
-       }
+       clistr_pull(cli, cli->dev, smb_buf(cli->inbuf), sizeof(fstring), -1, STR_TERMINATE|STR_ASCII);
 
        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 */
@@ -328,7 +742,7 @@ BOOL cli_tdis(struct cli_state *cli)
        if (!cli_receive_smb(cli))
                return False;
        
-       return CVAL(cli->inbuf,smb_rcls) == 0;
+       return !cli_is_error(cli);
 }
 
 
@@ -339,29 +753,22 @@ void cli_negprot_send(struct cli_state *cli)
 {
        char *p;
        int numprots;
-       int plength;
 
        memset(cli->outbuf,'\0',smb_size);
 
        /* setup the protocol strings */
-       for (plength=0,numprots=0;
-            prots[numprots].name && prots[numprots].prot<=cli->protocol;
-            numprots++)
-               plength += strlen(prots[numprots].name)+2;
-    
-       set_message(cli->outbuf,0,plength,True);
+       set_message(cli->outbuf,0,0,True);
 
        p = smb_buf(cli->outbuf);
        for (numprots=0;
             prots[numprots].name && prots[numprots].prot<=cli->protocol;
             numprots++) {
                *p++ = 2;
-               pstrcpy(p,prots[numprots].name);
-               unix_to_dos(p,True);
-               p += strlen(p) + 1;
+               p += clistr_push(cli, p, prots[numprots].name, -1, STR_TERMINATE);
        }
 
        CVAL(cli->outbuf,smb_com) = SMBnegprot;
+       cli_setup_bcc(cli, p);
        cli_setup_packet(cli);
 
        CVAL(smb_buf(cli->outbuf),0) = 2;
@@ -394,9 +801,7 @@ BOOL cli_negprot(struct cli_state *cli)
             prots[numprots].name && prots[numprots].prot<=cli->protocol;
             numprots++) {
                *p++ = 2;
-               pstrcpy(p,prots[numprots].name);
-               unix_to_dos(p,True);
-               p += strlen(p) + 1;
+               p += clistr_push(cli, p, prots[numprots].name, -1, STR_TERMINATE);
        }
 
        CVAL(cli->outbuf,smb_com) = SMBnegprot;
@@ -410,14 +815,13 @@ BOOL cli_negprot(struct cli_state *cli)
 
        show_msg(cli->inbuf);
 
-       if (CVAL(cli->inbuf,smb_rcls) != 0 || 
+       if (cli_is_error(cli) ||
            ((int)SVAL(cli->inbuf,smb_vwv0) >= numprots)) {
                return(False);
        }
 
        cli->protocol = prots[SVAL(cli->inbuf,smb_vwv0)].prot;
 
-
        if (cli->protocol >= PROTOCOL_NT1) {    
                /* NT protocol */
                cli->sec_mode = CVAL(cli->inbuf,smb_vwv1);
@@ -428,12 +832,19 @@ BOOL cli_negprot(struct cli_state *cli)
                cli->serverzone *= 60;
                /* this time arrives in real GMT */
                cli->servertime = interpret_long_date(cli->inbuf+smb_vwv11+1);
-               memcpy(cli->cryptkey,smb_buf(cli->inbuf),8);
+               cli->secblob = data_blob(smb_buf(cli->inbuf),smb_buflen(cli->inbuf));
                cli->capabilities = IVAL(cli->inbuf,smb_vwv9+1);
                if (cli->capabilities & CAP_RAW_MODE) {
                        cli->readbraw_supported = True;
                        cli->writebraw_supported = True;      
                }
+               /* work out if they sent us a workgroup */
+               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);
+               }
        } else if (cli->protocol >= PROTOCOL_LANMAN1) {
                cli->sec_mode = SVAL(cli->inbuf,smb_vwv1);
                cli->max_xmit = SVAL(cli->inbuf,smb_vwv2);
@@ -444,7 +855,7 @@ BOOL cli_negprot(struct cli_state *cli)
                cli->servertime = make_unix_date(cli->inbuf+smb_vwv8);
                cli->readbraw_supported = ((SVAL(cli->inbuf,smb_vwv5) & 0x1) != 0);
                cli->writebraw_supported = ((SVAL(cli->inbuf,smb_vwv5) & 0x2) != 0);
-               memcpy(cli->cryptkey,smb_buf(cli->inbuf),8);
+               cli->secblob = data_blob(smb_buf(cli->inbuf),smb_buflen(cli->inbuf));
        } else {
                /* the old core protocol */
                cli->sec_mode = 0;
@@ -453,8 +864,10 @@ BOOL cli_negprot(struct cli_state *cli)
 
        cli->max_xmit = MIN(cli->max_xmit, CLI_BUFFER_SIZE);
 
-       /* this ensures cli_use_unicode is setup - delete this call later (tridge) */
-       cli_setup_packet(cli);
+       /* a way to force ascii SMB */
+       if (getenv("CLI_FORCE_ASCII")) {
+               cli->capabilities &= ~CAP_UNICODE;
+       }
 
        return True;
 }
@@ -470,8 +883,10 @@ BOOL cli_session_request(struct cli_state *cli,
        int len = 4;
        extern pstring user_socket_options;
 
-       /* send a session request (RFC 1002) */
+       /* 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 ));
   
@@ -555,7 +970,6 @@ retry:
        return(True);
 }
 
-
 /****************************************************************************
 open the client sockets
 ****************************************************************************/
@@ -575,69 +989,31 @@ BOOL cli_connect(struct cli_state *cli, const char *host, struct in_addr *ip)
                cli->dest_ip = *ip;
        }
 
-        if (cli->port == 0) cli->port = 139;  /* Set to default */
-
-       cli->fd = open_socket_out(SOCK_STREAM, &cli->dest_ip, 
-                                 cli->port, cli->timeout);
-       if (cli->fd == -1)
+       if (getenv("LIBSMB_PROG")) {
+               cli->fd = sock_exec(getenv("LIBSMB_PROG"));
+       } else {
+               /* try 445 first, then 139 */
+               int port = cli->port?cli->port:445;
+               cli->fd = open_socket_out(SOCK_STREAM, &cli->dest_ip, 
+                                         port, cli->timeout);
+               if (cli->fd == -1 && cli->port == 0) {
+                       port = 139;
+                       cli->fd = open_socket_out(SOCK_STREAM, &cli->dest_ip, 
+                                                 port, cli->timeout);
+               }
+               if (cli->fd != -1) cli->port = port;
+       }
+       if (cli->fd == -1) {
+               DEBUG(1,("Error connecting to %s (%s)\n",
+                        inet_ntoa(*ip),strerror(errno)));
                return False;
+       }
 
        set_socket_options(cli->fd,user_socket_options);
 
        return True;
 }
 
-/****************************************************************************
-re-establishes a connection
-****************************************************************************/
-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);
-
-       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));
-
-       cli->fd = -1;
-
-       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;
-       }
-       return False;
-}
-
 /****************************************************************************
 establishes a connection right up to doing tconX, reading in a password.
 ****************************************************************************/
@@ -658,12 +1034,16 @@ BOOL cli_establish_connection(struct cli_state *cli,
                return False;
        }
 
+       /* cli_establish_connection() can't handle spnego yet. Once we get rid of
+          pwd_cache and other horrors we can get rid of this */
+       cli->use_spnego = False;
+
        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(calling), inet_ntoa(*dest_ip)));
+                                         nmb_namestr(called), inet_ntoa(*dest_ip)));
                        return False;
                }
        }
@@ -672,7 +1052,7 @@ BOOL cli_establish_connection(struct cli_state *cli,
        {
                DEBUG(1,("failed session request\n"));
                if (do_shutdown)
-          cli_shutdown(cli);
+                       cli_shutdown(cli);
                return False;
        }
 
@@ -736,7 +1116,7 @@ BOOL cli_establish_connection(struct cli_state *cli,
                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->cryptkey);
+               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 */
@@ -756,9 +1136,9 @@ BOOL cli_establish_connection(struct cli_state *cli,
                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));
+                                cli->server_domain,
+                                cli->server_os,
+                                cli->server_type));
                }
                
                if (do_tcon)
@@ -775,7 +1155,7 @@ BOOL cli_establish_connection(struct cli_state *cli,
        }
 
        if (do_shutdown)
-      cli_shutdown(cli);
+               cli_shutdown(cli);
 
        return True;
 }