smbd: some write time fixes
[tprouty/samba.git] / source / smbd / negprot.c
index fec597104501b46bc7d3a48b6fc947e065bfe6e2..9f56949eeb2f765caf9b8f2eaeb8c4c5a9f8ba24 100644 (file)
@@ -2,10 +2,11 @@
    Unix SMB/CIFS implementation.
    negprot reply code
    Copyright (C) Andrew Tridgell 1992-1998
+   Copyright (C) Volker Lendecke 2007
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
@@ -14,8 +15,7 @@
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "includes.h"
@@ -24,11 +24,11 @@ extern fstring remote_proto;
 extern enum protocol_types Protocol;
 extern int max_recv;
 
-BOOL global_encrypted_passwords_negotiated = False;
-BOOL global_spnego_negotiated = False;
+bool global_encrypted_passwords_negotiated = False;
+bool global_spnego_negotiated = False;
 struct auth_context *negprot_global_auth_context = NULL;
 
-static void get_challenge(char buff[8]) 
+static void get_challenge(uint8 buff[8])
 {
        NTSTATUS nt_status;
        const uint8 *cryptkey;
@@ -43,7 +43,7 @@ static void get_challenge(char buff[8])
        DEBUG(10, ("get challenge: creating negprot_global_auth_context\n"));
        if (!NT_STATUS_IS_OK(nt_status = make_auth_context_subsystem(&negprot_global_auth_context))) {
                DEBUG(0, ("make_auth_context_subsystem returned %s", nt_errstr(nt_status)));
-               smb_panic("cannot make_negprot_global_auth_context!\n");
+               smb_panic("cannot make_negprot_global_auth_context!");
        }
        DEBUG(10, ("get challenge: getting challenge\n"));
        cryptkey = negprot_global_auth_context->get_ntlm_challenge(negprot_global_auth_context);
@@ -54,39 +54,39 @@ static void get_challenge(char buff[8])
  Reply for the core protocol.
 ****************************************************************************/
 
-static int reply_corep(char *inbuf, char *outbuf)
+static void reply_corep(struct smb_request *req, uint16 choice)
 {
-       int outsize = set_message(outbuf,1,0,True);
+       reply_outbuf(req, 1, 0);
+       SSVAL(req->outbuf, smb_vwv0, choice);
 
        Protocol = PROTOCOL_CORE;
-       
-       return outsize;
 }
 
 /****************************************************************************
  Reply for the coreplus protocol.
 ****************************************************************************/
 
-static int reply_coreplus(char *inbuf, char *outbuf)
+static void reply_coreplus(struct smb_request *req, uint16 choice)
 {
        int raw = (lp_readraw()?1:0) | (lp_writeraw()?2:0);
-       int outsize = set_message(outbuf,13,0,True);
-       SSVAL(outbuf,smb_vwv5,raw); /* tell redirector we support
+
+       reply_outbuf(req, 13, 0);
+
+       SSVAL(req->outbuf,smb_vwv0,choice);
+       SSVAL(req->outbuf,smb_vwv5,raw); /* tell redirector we support
                        readbraw and writebraw (possibly) */
        /* Reply, SMBlockread, SMBwritelock supported. */
-       SCVAL(outbuf,smb_flg,FLAG_REPLY|FLAG_SUPPORT_LOCKREAD);
-       SSVAL(outbuf,smb_vwv1,0x1); /* user level security, don't encrypt */    
-
+       SCVAL(req->outbuf,smb_flg,FLAG_REPLY|FLAG_SUPPORT_LOCKREAD);
+       SSVAL(req->outbuf,smb_vwv1,0x1); /* user level security, don't
+                                         * encrypt */
        Protocol = PROTOCOL_COREPLUS;
-
-       return outsize;
 }
 
 /****************************************************************************
  Reply for the lanman 1.0 protocol.
 ****************************************************************************/
 
-static int reply_lanman1(char *inbuf, char *outbuf)
+static void reply_lanman1(struct smb_request *req, uint16 choice)
 {
        int raw = (lp_readraw()?1:0) | (lp_writeraw()?2:0);
        int secword=0;
@@ -99,36 +99,38 @@ static int reply_lanman1(char *inbuf, char *outbuf)
        if (global_encrypted_passwords_negotiated)
                secword |= NEGOTIATE_SECURITY_CHALLENGE_RESPONSE;
 
-       set_message(outbuf,13,global_encrypted_passwords_negotiated?8:0,True);
-       SSVAL(outbuf,smb_vwv1,secword); 
+       reply_outbuf(req, 13, global_encrypted_passwords_negotiated?8:0);
+
+       SSVAL(req->outbuf,smb_vwv0,choice);
+       SSVAL(req->outbuf,smb_vwv1,secword);
        /* Create a token value and add it to the outgoing packet. */
        if (global_encrypted_passwords_negotiated) {
-               get_challenge(smb_buf(outbuf));
-               SSVAL(outbuf,smb_vwv11, 8);
+               get_challenge((uint8 *)smb_buf(req->outbuf));
+               SSVAL(req->outbuf,smb_vwv11, 8);
        }
 
        Protocol = PROTOCOL_LANMAN1;
 
        /* Reply, SMBlockread, SMBwritelock supported. */
-       SCVAL(outbuf,smb_flg,FLAG_REPLY|FLAG_SUPPORT_LOCKREAD);
-       SSVAL(outbuf,smb_vwv2,max_recv);
-       SSVAL(outbuf,smb_vwv3,lp_maxmux()); /* maxmux */
-       SSVAL(outbuf,smb_vwv4,1);
-       SSVAL(outbuf,smb_vwv5,raw); /* tell redirector we support
+       SCVAL(req->outbuf,smb_flg,FLAG_REPLY|FLAG_SUPPORT_LOCKREAD);
+       SSVAL(req->outbuf,smb_vwv2,max_recv);
+       SSVAL(req->outbuf,smb_vwv3,lp_maxmux()); /* maxmux */
+       SSVAL(req->outbuf,smb_vwv4,1);
+       SSVAL(req->outbuf,smb_vwv5,raw); /* tell redirector we support
                readbraw writebraw (possibly) */
-       SIVAL(outbuf,smb_vwv6,sys_getpid());
-       SSVAL(outbuf,smb_vwv10, set_server_zone_offset(t)/60);
+       SIVAL(req->outbuf,smb_vwv6,sys_getpid());
+       SSVAL(req->outbuf,smb_vwv10, set_server_zone_offset(t)/60);
 
-       srv_put_dos_date(outbuf,smb_vwv8,t);
+       srv_put_dos_date((char *)req->outbuf,smb_vwv8,t);
 
-       return (smb_len(outbuf)+4);
+       return;
 }
 
 /****************************************************************************
  Reply for the lanman 2.0 protocol.
 ****************************************************************************/
 
-static int reply_lanman2(char *inbuf, char *outbuf)
+static void reply_lanman2(struct smb_request *req, uint16 choice)
 {
        int raw = (lp_readraw()?1:0) | (lp_writeraw()?2:0);
        int secword=0;
@@ -141,28 +143,28 @@ static int reply_lanman2(char *inbuf, char *outbuf)
        if (global_encrypted_passwords_negotiated)
                secword |= NEGOTIATE_SECURITY_CHALLENGE_RESPONSE;
 
-       set_message(outbuf,13,global_encrypted_passwords_negotiated?8:0,True);
-       SSVAL(outbuf,smb_vwv1,secword); 
-       SIVAL(outbuf,smb_vwv6,sys_getpid());
+       reply_outbuf(req, 13, global_encrypted_passwords_negotiated?8:0);
+
+       SSVAL(req->outbuf,smb_vwv0,choice);
+       SSVAL(req->outbuf,smb_vwv1,secword);
+       SIVAL(req->outbuf,smb_vwv6,sys_getpid());
 
        /* Create a token value and add it to the outgoing packet. */
        if (global_encrypted_passwords_negotiated) {
-               get_challenge(smb_buf(outbuf));
-               SSVAL(outbuf,smb_vwv11, 8);
+               get_challenge((uint8 *)smb_buf(req->outbuf));
+               SSVAL(req->outbuf,smb_vwv11, 8);
        }
 
        Protocol = PROTOCOL_LANMAN2;
 
        /* Reply, SMBlockread, SMBwritelock supported. */
-       SCVAL(outbuf,smb_flg,FLAG_REPLY|FLAG_SUPPORT_LOCKREAD);
-       SSVAL(outbuf,smb_vwv2,max_recv);
-       SSVAL(outbuf,smb_vwv3,lp_maxmux()); 
-       SSVAL(outbuf,smb_vwv4,1);
-       SSVAL(outbuf,smb_vwv5,raw); /* readbraw and/or writebraw */
-       SSVAL(outbuf,smb_vwv10, set_server_zone_offset(t)/60);
-       srv_put_dos_date(outbuf,smb_vwv8,t);
-
-       return (smb_len(outbuf)+4);
+       SCVAL(req->outbuf,smb_flg,FLAG_REPLY|FLAG_SUPPORT_LOCKREAD);
+       SSVAL(req->outbuf,smb_vwv2,max_recv);
+       SSVAL(req->outbuf,smb_vwv3,lp_maxmux());
+       SSVAL(req->outbuf,smb_vwv4,1);
+       SSVAL(req->outbuf,smb_vwv5,raw); /* readbraw and/or writebraw */
+       SSVAL(req->outbuf,smb_vwv10, set_server_zone_offset(t)/60);
+       srv_put_dos_date((char *)req->outbuf,smb_vwv8,t);
 }
 
 /****************************************************************************
@@ -227,7 +229,10 @@ static DATA_BLOB negprot_spnego(void)
                char *host_princ_s = NULL;
                name_to_fqdn(myname, global_myname());
                strlower_m(myname);
-               asprintf(&host_princ_s, "cifs/%s@%s", myname, lp_realm());
+               if (asprintf(&host_princ_s, "cifs/%s@%s", myname, lp_realm())
+                   == -1) {
+                       return data_blob_null;
+               }
                blob = spnego_gen_negTokenInit(guid, OIDs_krb5, host_princ_s);
                SAFE_FREE(host_princ_s);
        }
@@ -239,7 +244,7 @@ static DATA_BLOB negprot_spnego(void)
  Reply for the nt protocol.
 ****************************************************************************/
 
-static int reply_nt1(char *inbuf, char *outbuf)
+static void reply_nt1(struct smb_request *req, uint16 choice)
 {
        /* dual names + lock_and_read + nt SMBs + remote API calls */
        int capabilities = CAP_NT_FIND|CAP_LOCK_AND_READ|
@@ -247,24 +252,40 @@ static int reply_nt1(char *inbuf, char *outbuf)
 
        int secword=0;
        char *p, *q;
-       BOOL negotiate_spnego = False;
+       bool negotiate_spnego = False;
        time_t t = time(NULL);
+       ssize_t ret;
 
        global_encrypted_passwords_negotiated = lp_encrypted_passwords();
 
+       /* Check the flags field to see if this is Vista.
+          WinXP sets it and Vista does not. But we have to 
+          distinguish from NT which doesn't set it either. */
+
+       if ( (req->flags2 & FLAGS2_EXTENDED_SECURITY) &&
+               ((req->flags2 & FLAGS2_UNKNOWN_BIT4) == 0) )
+       {
+               if (get_remote_arch() != RA_SAMBA) {
+                       set_remote_arch( RA_VISTA );
+               }
+       }
+
+       reply_outbuf(req,17,0);
+
        /* do spnego in user level security if the client
           supports it and we can do encrypted passwords */
        
        if (global_encrypted_passwords_negotiated && 
            (lp_security() != SEC_SHARE) &&
            lp_use_spnego() &&
-           (SVAL(inbuf, smb_flg2) & FLAGS2_EXTENDED_SECURITY)) {
+           (req->flags2 & FLAGS2_EXTENDED_SECURITY)) {
                negotiate_spnego = True;
                capabilities |= CAP_EXTENDED_SECURITY;
                add_to_common_flags2(FLAGS2_EXTENDED_SECURITY);
-               /* Ensure FLAGS2_EXTENDED_SECURITY gets set in this reply (already
-                       partially constructed. */
-               SSVAL(outbuf,smb_flg2, SVAL(outbuf,smb_flg2) | FLAGS2_EXTENDED_SECURITY);
+               /* Ensure FLAGS2_EXTENDED_SECURITY gets set in this reply
+                  (already partially constructed. */
+               SSVAL(req->outbuf, smb_flg2,
+                     req->flags2 | FLAGS2_EXTENDED_SECURITY);
        }
        
        capabilities |= CAP_NT_SMBS|CAP_RPC_REMOTE_APIS|CAP_UNICODE;
@@ -309,53 +330,72 @@ static int reply_nt1(char *inbuf, char *outbuf)
                }
        }
 
-       set_message(outbuf,17,0,True);
-       
-       SCVAL(outbuf,smb_vwv1,secword);
+       SSVAL(req->outbuf,smb_vwv0,choice);
+       SCVAL(req->outbuf,smb_vwv1,secword);
        
        Protocol = PROTOCOL_NT1;
        
-       SSVAL(outbuf,smb_vwv1+1,lp_maxmux()); /* maxmpx */
-       SSVAL(outbuf,smb_vwv2+1,1); /* num vcs */
-       SIVAL(outbuf,smb_vwv3+1,max_recv); /* max buffer. LOTS! */
-       SIVAL(outbuf,smb_vwv5+1,0x10000); /* raw size. full 64k */
-       SIVAL(outbuf,smb_vwv7+1,sys_getpid()); /* session key */
-       SIVAL(outbuf,smb_vwv9+1,capabilities); /* capabilities */
-       put_long_date(outbuf+smb_vwv11+1,t);
-       SSVALS(outbuf,smb_vwv15+1,set_server_zone_offset(t)/60);
+       SSVAL(req->outbuf,smb_vwv1+1,lp_maxmux()); /* maxmpx */
+       SSVAL(req->outbuf,smb_vwv2+1,1); /* num vcs */
+       SIVAL(req->outbuf,smb_vwv3+1,max_recv); /* max buffer. LOTS! */
+       SIVAL(req->outbuf,smb_vwv5+1,0x10000); /* raw size. full 64k */
+       SIVAL(req->outbuf,smb_vwv7+1,sys_getpid()); /* session key */
+       SIVAL(req->outbuf,smb_vwv9+1,capabilities); /* capabilities */
+       put_long_date((char *)req->outbuf+smb_vwv11+1,t);
+       SSVALS(req->outbuf,smb_vwv15+1,set_server_zone_offset(t)/60);
        
-       p = q = smb_buf(outbuf);
+       p = q = smb_buf(req->outbuf);
        if (!negotiate_spnego) {
                /* Create a token value and add it to the outgoing packet. */
                if (global_encrypted_passwords_negotiated) {
+                       uint8 chal[8];
                        /* note that we do not send a challenge at all if
                           we are using plaintext */
-                       get_challenge(p);
-                       SCVAL(outbuf,smb_vwv16+1,8);
-                       p += 8;
+                       get_challenge(chal);
+                       ret = message_push_blob(
+                               &req->outbuf, data_blob_const(chal, sizeof(chal)));
+                       if (ret == -1) {
+                               DEBUG(0, ("Could not push challenge\n"));
+                               reply_nterror(req, NT_STATUS_NO_MEMORY);
+                               return;
+                       }
+                       SCVAL(req->outbuf, smb_vwv16+1, ret);
+                       p += ret;
+               }
+               ret = message_push_string(&req->outbuf, lp_workgroup(),
+                                         STR_UNICODE|STR_TERMINATE
+                                         |STR_NOALIGN);
+               if (ret == -1) {
+                       DEBUG(0, ("Could not push challenge\n"));
+                       reply_nterror(req, NT_STATUS_NO_MEMORY);
+                       return;
                }
-               p += srvstr_push(outbuf, p, lp_workgroup(), -1, 
-                                STR_UNICODE|STR_TERMINATE|STR_NOALIGN);
                DEBUG(3,("not using SPNEGO\n"));
        } else {
                DATA_BLOB spnego_blob = negprot_spnego();
 
                if (spnego_blob.data == NULL) {
-                       return ERROR_NT(NT_STATUS_NO_MEMORY);
+                       reply_nterror(req, NT_STATUS_NO_MEMORY);
+                       return;
                }
 
-               memcpy(p, spnego_blob.data, spnego_blob.length);
-               p += spnego_blob.length;
+               ret = message_push_blob(&req->outbuf, spnego_blob);
+               if (ret == -1) {
+                       DEBUG(0, ("Could not push spnego blob\n"));
+                       reply_nterror(req, NT_STATUS_NO_MEMORY);
+                       return;
+               }
+               p += ret;
                data_blob_free(&spnego_blob);
 
-               SCVAL(outbuf,smb_vwv16+1, 0);
+               SCVAL(req->outbuf,smb_vwv16+1, 0);
                DEBUG(3,("using SPNEGO\n"));
        }
        
-       SSVAL(outbuf,smb_vwv17, p - q); /* length of challenge+domain strings */
-       set_message_end(outbuf, p);
-       
-       return (smb_len(outbuf)+4);
+       SSVAL(req->outbuf,smb_vwv17, p - q); /* length of challenge+domain
+                                             * strings */
+
+       return;
 }
 
 /* these are the protocol lists used for auto architecture detection:
@@ -388,6 +428,15 @@ protocol [LM1.2X002]
 protocol [LANMAN2.1]
 protocol [NT LM 0.12]
 
+Vista:
+protocol [PC NETWORK PROGRAM 1.0]
+protocol [LANMAN1.0]
+protocol [Windows for Workgroups 3.1a]
+protocol [LM1.2X002]
+protocol [LANMAN2.1]
+protocol [NT LM 0.12]
+protocol [SMB 2.001]
+
 OS/2:
 protocol [PC NETWORK PROGRAM 1.0]
 protocol [XENIX CORE]
@@ -401,18 +450,19 @@ protocol [LANMAN2.1]
   *
   * This appears to be the matrix of which protocol is used by which
   * MS product.
-       Protocol                       WfWg    Win95   WinNT  Win2K  OS/2
-       PC NETWORK PROGRAM 1.0          1       1       1      1      1
+       Protocol                       WfWg    Win95   WinNT  Win2K  OS/2 Vista
+       PC NETWORK PROGRAM 1.0          1       1       1      1      1     1
        XENIX CORE                                      2             2
        MICROSOFT NETWORKS 3.0          2       2       
        DOS LM1.2X002                   3       3       
        MICROSOFT NETWORKS 1.03                         3
        DOS LANMAN2.1                   4       4       
-       LANMAN1.0                                       4      2      3
-       Windows for Workgroups 3.1a     5       5       5      3
-       LM1.2X002                                       6      4      4
-       LANMAN2.1                                       7      5      5
-       NT LM 0.12                              6       8      6
+       LANMAN1.0                                       4      2      3     2
+       Windows for Workgroups 3.1a     5       5       5      3            3
+       LM1.2X002                                       6      4      4     4
+       LANMAN2.1                                       7      5      5     5
+       NT LM 0.12                              6       8      6            6
+       SMB 2.001                                                           7
   *
   *  tim@fsg.com 09/29/95
   *  Win2K added by matty 17/7/99
@@ -425,6 +475,7 @@ protocol [LANMAN2.1]
 #define ARCH_OS2      0x14     /* Again OS/2 is like NT */
 #define ARCH_SAMBA    0x20
 #define ARCH_CIFSFS   0x40
+#define ARCH_VISTA    0x8C     /* Vista is like XP/2K */
  
 #define ARCH_ALL      0x7F
  
@@ -432,7 +483,7 @@ protocol [LANMAN2.1]
 static const struct {
        const char *proto_name;
        const char *short_name;
-       int (*proto_reply_fn)(char *, char *);
+       void (*proto_reply_fn)(struct smb_request *req, uint16 choice);
        int protocol_level;
 } supported_protocols[] = {
        {"NT LANMAN 1.0",           "NT1",      reply_nt1,      PROTOCOL_NT1},
@@ -454,19 +505,19 @@ static const struct {
  conn POINTER CAN BE NULL HERE !
 ****************************************************************************/
 
-int reply_negprot(connection_struct *conn, 
-                 char *inbuf,char *outbuf, int dum_size, 
-                 int dum_buffsize)
+void reply_negprot(struct smb_request *req)
 {
-       int outsize = set_message(outbuf,1,0,True);
-       int Index=0;
+       size_t size = smb_len(req->inbuf) + 4;
        int choice= -1;
        int protocol;
        char *p;
-       int bcc = SVAL(smb_buf(inbuf),-2);
+       int bcc = SVAL(smb_buf(req->inbuf),-2);
        int arch = ARCH_ALL;
+       int num_cliprotos;
+       char **cliprotos;
+       int i;
 
-       static BOOL done_negprot = False;
+       static bool done_negprot = False;
 
        START_PROFILE(SMBnegprot);
 
@@ -476,39 +527,82 @@ int reply_negprot(connection_struct *conn,
        }
        done_negprot = True;
 
-       p = smb_buf(inbuf)+1;
-       while (p < (smb_buf(inbuf) + bcc)) { 
-               Index++;
-               DEBUG(3,("Requested protocol [%s]\n",p));
-               if (strcsequal(p,"Windows for Workgroups 3.1a"))
-                       arch &= ( ARCH_WFWG | ARCH_WIN95 | ARCH_WINNT | ARCH_WIN2K );
-               else if (strcsequal(p,"DOS LM1.2X002"))
+       if (req->inbuf[size-1] != '\0') {
+               DEBUG(0, ("negprot protocols not 0-terminated\n"));
+               reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
+               END_PROFILE(SMBnegprot);
+               return;
+       }
+
+       p = smb_buf(req->inbuf) + 1;
+
+       num_cliprotos = 0;
+       cliprotos = NULL;
+
+       while (p < (smb_buf(req->inbuf) + bcc)) {
+
+               char **tmp;
+
+               tmp = TALLOC_REALLOC_ARRAY(talloc_tos(), cliprotos, char *,
+                                          num_cliprotos+1);
+               if (tmp == NULL) {
+                       DEBUG(0, ("talloc failed\n"));
+                       TALLOC_FREE(cliprotos);
+                       reply_nterror(req, NT_STATUS_NO_MEMORY);
+                       END_PROFILE(SMBnegprot);
+                       return;
+               }
+
+               cliprotos = tmp;
+
+               if (pull_ascii_talloc(cliprotos, &cliprotos[num_cliprotos], p)
+                   == (size_t)-1) {
+                       DEBUG(0, ("pull_ascii_talloc failed\n"));
+                       TALLOC_FREE(cliprotos);
+                       reply_nterror(req, NT_STATUS_NO_MEMORY);
+                       END_PROFILE(SMBnegprot);
+                       return;
+               }
+
+               DEBUG(3, ("Requested protocol [%s]\n",
+                         cliprotos[num_cliprotos]));
+
+               num_cliprotos += 1;
+               p += strlen(p) + 2;
+       }
+
+       for (i=0; i<num_cliprotos; i++) {
+               if (strcsequal(cliprotos[i], "Windows for Workgroups 3.1a"))
+                       arch &= ( ARCH_WFWG | ARCH_WIN95 | ARCH_WINNT
+                                 | ARCH_WIN2K );
+               else if (strcsequal(cliprotos[i], "DOS LM1.2X002"))
                        arch &= ( ARCH_WFWG | ARCH_WIN95 );
-               else if (strcsequal(p,"DOS LANMAN2.1"))
+               else if (strcsequal(cliprotos[i], "DOS LANMAN2.1"))
                        arch &= ( ARCH_WFWG | ARCH_WIN95 );
-               else if (strcsequal(p,"NT LM 0.12"))
-                       arch &= ( ARCH_WIN95 | ARCH_WINNT | ARCH_WIN2K | ARCH_CIFSFS);
-               else if (strcsequal(p,"LANMAN2.1"))
+               else if (strcsequal(cliprotos[i], "NT LM 0.12"))
+                       arch &= ( ARCH_WIN95 | ARCH_WINNT | ARCH_WIN2K
+                                 | ARCH_CIFSFS);
+               else if (strcsequal(cliprotos[i], "SMB 2.001"))
+                       arch = ARCH_VISTA;              
+               else if (strcsequal(cliprotos[i], "LANMAN2.1"))
                        arch &= ( ARCH_WINNT | ARCH_WIN2K | ARCH_OS2 );
-               else if (strcsequal(p,"LM1.2X002"))
+               else if (strcsequal(cliprotos[i], "LM1.2X002"))
                        arch &= ( ARCH_WINNT | ARCH_WIN2K | ARCH_OS2 );
-               else if (strcsequal(p,"MICROSOFT NETWORKS 1.03"))
+               else if (strcsequal(cliprotos[i], "MICROSOFT NETWORKS 1.03"))
                        arch &= ARCH_WINNT;
-               else if (strcsequal(p,"XENIX CORE"))
+               else if (strcsequal(cliprotos[i], "XENIX CORE"))
                        arch &= ( ARCH_WINNT | ARCH_OS2 );
-               else if (strcsequal(p,"Samba")) {
+               else if (strcsequal(cliprotos[i], "Samba")) {
                        arch = ARCH_SAMBA;
                        break;
-               } else if (strcsequal(p,"POSIX 2")) {
+               } else if (strcsequal(cliprotos[i], "POSIX 2")) {
                        arch = ARCH_CIFSFS;
                        break;
                }
-               p += strlen(p) + 2;
        }
 
        /* CIFSFS can send one arch only, NT LM 0.12. */
-       if (Index == 1 && (arch & ARCH_CIFSFS)) {
+       if (i == 1 && (arch & ARCH_CIFSFS)) {
                arch = ARCH_CIFSFS;
        }
 
@@ -526,13 +620,19 @@ int reply_negprot(connection_struct *conn,
                        set_remote_arch(RA_WIN95);
                        break;
                case ARCH_WINNT:
-                       if(SVAL(inbuf,smb_flg2)==FLAGS2_WIN2K_SIGNATURE)
+                       if(req->flags2 == FLAGS2_WIN2K_SIGNATURE)
                                set_remote_arch(RA_WIN2K);
                        else
                                set_remote_arch(RA_WINNT);
                        break;
                case ARCH_WIN2K:
-                       set_remote_arch(RA_WIN2K);
+                       /* Vista may have been set in the negprot so don't 
+                          override it here */
+                       if ( get_remote_arch() != RA_VISTA )
+                               set_remote_arch(RA_WIN2K);
+                       break;
+               case ARCH_VISTA:
+                       set_remote_arch(RA_VISTA);
                        break;
                case ARCH_OS2:
                        set_remote_arch(RA_OS2);
@@ -549,34 +649,33 @@ int reply_negprot(connection_struct *conn,
           when the client connects to port 445.  Of course there is a small
           window where we are listening to messages   -- jerry */
 
-       claim_connection(NULL,"",0,True,FLAG_MSG_GENERAL|FLAG_MSG_SMBD|FLAG_MSG_PRINT_GENERAL);
+       claim_connection(
+               NULL,"",FLAG_MSG_GENERAL|FLAG_MSG_SMBD|FLAG_MSG_PRINT_GENERAL);
     
        /* Check for protocols, most desirable first */
        for (protocol = 0; supported_protocols[protocol].proto_name; protocol++) {
-               p = smb_buf(inbuf)+1;
-               Index = 0;
+               i = 0;
                if ((supported_protocols[protocol].protocol_level <= lp_maxprotocol()) &&
                                (supported_protocols[protocol].protocol_level >= lp_minprotocol()))
-                       while (p < (smb_buf(inbuf) + bcc)) { 
-                               if (strequal(p,supported_protocols[protocol].proto_name))
-                                       choice = Index;
-                               Index++;
-                               p += strlen(p) + 2;
+                       while (i < num_cliprotos) {
+                               if (strequal(cliprotos[i],supported_protocols[protocol].proto_name))
+                                       choice = i;
+                               i++;
                        }
                if(choice != -1)
                        break;
        }
   
-       SSVAL(outbuf,smb_vwv0,choice);
        if(choice != -1) {
                fstrcpy(remote_proto,supported_protocols[protocol].short_name);
                reload_services(True);          
-               outsize = supported_protocols[protocol].proto_reply_fn(inbuf, outbuf);
+               supported_protocols[protocol].proto_reply_fn(req, choice);
                DEBUG(3,("Selected protocol %s\n",supported_protocols[protocol].proto_name));
        } else {
                DEBUG(0,("No protocol supported !\n"));
+               reply_outbuf(req, 1, 0);
+               SSVAL(req->outbuf, smb_vwv0, choice);
        }
-       SSVAL(outbuf,smb_vwv0,choice);
   
        DEBUG( 5, ( "negprot index=%d\n", choice ) );
 
@@ -585,6 +684,7 @@ int reply_negprot(connection_struct *conn,
                        "client negotiated a downlevel protocol");
        }
 
+       TALLOC_FREE(cliprotos);
        END_PROFILE(SMBnegprot);
-       return(outsize);
+       return;
 }