Fix up 'net ads join' to delete and rejoin if the account already exists.
[ira/wip.git] / source3 / libsmb / cliconnect.c
index 4a957a9ccd97692c19334f75b5a81fe8e02471d1..7649a88ffdd56778ccb7583778f2b387baf2252f 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,529 @@ 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;
+
+       if (!cli->force_dos_errors) {
+               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. Ignore ntpass. */
+               passlen = 24;
+               ntpasslen = 24;
+               clistr_push(cli, pword, 
+                           pass?pass:"", sizeof(pword), STR_TERMINATE|STR_ASCII);
+               clistr_push(cli, ntpword, 
+                           pass?pass:"", sizeof(ntpword), STR_TERMINATE|STR_ASCII);
+               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;
+       uint32 len;
+
+       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);
+
+       /* 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);
+
+       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)
+{
+       DATA_BLOB blob2, negTokenTarg;
+
+       DEBUG(2,("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;
+
+       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
+
+       /* 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);
+
+#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,
@@ -55,12 +572,12 @@ 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=strchr_m(user2,*lp_winbind_separator()))) {
                *p = 0;
                user = p+1;
                workgroup = user2;
@@ -69,138 +586,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;
-       } 
-
-       /* send a session setup command */
-       memset(cli->outbuf,'\0',smb_size);
-
-       if (cli->protocol < PROTOCOL_NT1)
-       {
-               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, CLISTR_CONVERT|CLISTR_UPPER|CLISTR_TERMINATE);
-               cli_setup_bcc(cli, p);
+       /* 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);
        }
-       else
-       {
-               uint32 capabilities;
-
-               capabilities = CAP_NT_SMBS;
-               if (cli->use_level_II_oplocks) 
-                       capabilities |= CAP_LEVEL_II_OPLOCKS;
-               if (getenv("USE_UNICODE") && 
-                   (cli->capabilities & CAP_UNICODE)) {
-                       capabilities |= CAP_UNICODE;
-               }
-               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 += 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);
-               cli_setup_bcc(cli, p);
-       }
-
-      cli_send_smb(cli);
-      if (!cli_receive_smb(cli))
-             return False;
-
-      show_msg(cli->inbuf);
 
-      if (CVAL(cli->inbuf,smb_rcls) != 0) {
-             return False;
-      }
-
-      /* use the returned vuid from now on */
-      cli->vuid = SVAL(cli->inbuf,smb_uid);
+       /* 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) {
-             /*
-              * Save off some of the connected server
-              * info.
-              */
-             char *p = smb_buf(cli->inbuf);
-             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);
-      }
+       /* 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);
+       }
 
-      fstrcpy(cli->user_name, user);
+       /* if the server supports extended security then use SPNEGO */
+       if (cli->capabilities & CAP_EXTENDED_SECURITY) {
+               return cli_session_setup_spnego(cli, user, pass, workgroup);
+       }
 
-      return True;
+       /* otherwise do a NT1 style session setup */
+       return cli_session_setup_nt1(cli, user, 
+                                    pass, passlen, ntpass, ntpasslen,
+                                    workgroup);        
 }
 
 /****************************************************************************
@@ -220,14 +642,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;
@@ -247,24 +669,26 @@ 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, 0, True);
        CVAL(cli->outbuf,smb_com) = SMBtconX;
@@ -276,32 +700,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;
 
        cli_setup_bcc(cli, p);
 
-       SCVAL(cli->inbuf,smb_rcls, 1);
-
        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 +745,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 +756,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 +804,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 +818,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 +835,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 +858,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 +867,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 +886,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,19 +973,28 @@ retry:
        return(True);
 }
 
-
 /****************************************************************************
 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;
@@ -575,12 +1002,25 @@ 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);
 
@@ -588,58 +1028,7 @@ BOOL cli_connect(struct cli_state *cli, const char *host, struct in_addr *ip)
 }
 
 /****************************************************************************
-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.
+establishes a connection right up to doing tconX, password in cache.
 ****************************************************************************/
 BOOL cli_establish_connection(struct cli_state *cli, 
                                char *dest_host, struct in_addr *dest_ip,
@@ -658,12 +1047,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 +1065,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 +1129,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 +1149,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)
@@ -768,18 +1161,136 @@ BOOL cli_establish_connection(struct cli_state *cli,
                        {
                                DEBUG(1,("failed tcon_X\n"));
                                if (do_shutdown)
-                  cli_shutdown(cli);
+                                       cli_shutdown(cli);
                                return False;
                        }
                }
        }
 
        if (do_shutdown)
-      cli_shutdown(cli);
+               cli_shutdown(cli);
 
        return True;
 }
 
+/* Initialise client credentials for authenticated pipe access */
+
+static void init_creds(struct ntuser_creds *creds, char* username,
+                      char* domain, char* password, int pass_len)
+{
+       ZERO_STRUCTP(creds);
+
+       pwd_set_cleartext(&creds->pwd, password);
+
+       fstrcpy(creds->user_name, username);
+       fstrcpy(creds->domain, domain);
+
+       if (!*username) {
+               creds->pwd.null_pwd = True;
+       }
+}
+
+/****************************************************************************
+establishes a connection right up to doing tconX, password specified.
+****************************************************************************/
+NTSTATUS cli_full_connection(struct cli_state **output_cli, 
+                            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, int pass_len) 
+{
+       struct ntuser_creds creds;
+       NTSTATUS nt_status;
+       struct nmb_name calling;
+       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 (!(cli = cli_initialise(NULL))) {
+               return NT_STATUS_NO_MEMORY;
+       }
+       
+       if (cli_set_port(cli, port) != port) {
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       ip = *dest_ip;
+       
+       DEBUG(3,("Connecting to host=%s share=%s\n\n", 
+                dest_host, service));
+       
+       if (!cli_connect(cli, dest_host, &ip))
+       {
+               DEBUG(1,("cli_establish_connection: failed to connect to %s (%s)\n",
+                        nmb_namestr(&called), inet_ntoa(*dest_ip)));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       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, '.'))) {
+                       *p = 0;
+                       goto again;
+               }
+               if (strcmp(called.name, "*SMBSERVER")) {
+                       make_nmb_name(&called , "*SMBSERVER", 0x20);
+                       goto again;
+               }
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       if (!cli_negprot(cli))
+       {
+               DEBUG(1,("failed negprot\n"));
+               nt_status = NT_STATUS_UNSUCCESSFUL;
+               cli_shutdown(cli);
+               return nt_status;
+       }
+
+       if (!cli_session_setup(cli, user,
+                              password, pass_len,
+                              NULL, 0,
+                              domain))
+       {
+               DEBUG(1,("failed session setup\n"));
+               nt_status = cli_nt_error(cli);
+               cli_shutdown(cli);
+               return nt_status;
+       } 
+
+       if (service)
+       {
+               if (!cli_send_tconX(cli, service, service_type,
+                                   (char*)password, pass_len))
+               {
+                       DEBUG(1,("failed tcon_X\n"));
+                       nt_status = cli_nt_error(cli);
+                       cli_shutdown(cli);
+                       return nt_status;
+               }
+       }
+
+       init_creds(&creds, user, domain, password, pass_len);
+       cli_init_creds(cli, &creds);
+
+       *output_cli = cli;
+       return NT_STATUS_OK;
+}
 
 /****************************************************************************
  Attempt a NetBIOS session request, falling back to *SMBSERVER if needed.
@@ -820,9 +1331,9 @@ 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);
+       return False;
+    }
 
     cli_shutdown(cli);
 
@@ -838,3 +1349,5 @@ name *SMBSERVER with error %s\n", desthost, cli_errstr(cli) ));
 
   return True;
 }
+
+