[GLUE] Rsync SAMBA_3_2_0 SVN r25598 in order to create the v3-2-test branch.
[samba.git] / source / libsmb / clitrans.c
index 630e9f93c00e867a957f1cc0e29c1fc32e88d219..e859dce95674f6f96d68aab33b599e907b03892b 100644 (file)
@@ -1,12 +1,11 @@
 /* 
-   Unix SMB/Netbios implementation.
-   Version 3.0
+   Unix SMB/CIFS implementation.
    client transaction calls
    Copyright (C) Andrew Tridgell 1994-1998
    
    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,
    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/>.
 */
 
-#define NO_SYSLOG
-
 #include "includes.h"
 
 
 /****************************************************************************
-  send a SMB trans or trans2 request
-  ****************************************************************************/
+ Send a SMB trans or trans2 request.
+****************************************************************************/
+
 BOOL cli_send_trans(struct cli_state *cli, int trans, 
-                   char *pipe_name, 
+                   const char *pipe_name, 
                    int fid, int flags,
-                   uint16 *setup, int lsetup, int msetup,
-                   char *param, int lparam, int mparam,
-                   char *data, int ldata, int mdata)
+                   uint16 *setup, unsigned int lsetup, unsigned int msetup,
+                   const char *param, unsigned int lparam, unsigned int mparam,
+                   const char *data, unsigned int ldata, unsigned int mdata)
 {
-       int i;
-       int this_ldata,this_lparam;
-       int tot_data=0,tot_param=0;
+       unsigned int i;
+       unsigned int this_ldata,this_lparam;
+       unsigned int tot_data=0,tot_param=0;
        char *outdata,*outparam;
        char *p;
        int pipe_name_len=0;
+       uint16 mid;
 
        this_lparam = MIN(lparam,cli->max_xmit - (500+lsetup*2)); /* hack */
        this_ldata = MIN(ldata,cli->max_xmit - (500+lsetup*2+this_lparam));
 
        memset(cli->outbuf,'\0',smb_size);
        set_message(cli->outbuf,14+lsetup,0,True);
-       CVAL(cli->outbuf,smb_com) = trans;
+       SCVAL(cli->outbuf,smb_com,trans);
        SSVAL(cli->outbuf,smb_tid, cli->cnum);
        cli_setup_packet(cli);
 
+       /*
+        * Save the mid we're using. We need this for finding
+        * signing replies.
+        */
+
+       mid = cli->mid;
+
        if (pipe_name) {
-               pipe_name_len = clistr_push_size(cli, smb_buf(cli->outbuf), 
-                                                pipe_name, -1, 
-                                                CLISTR_TERMINATE);
+               pipe_name_len = clistr_push(cli, smb_buf(cli->outbuf), pipe_name, -1, STR_TERMINATE);
        }
 
        outparam = smb_buf(cli->outbuf)+(trans==SMBtrans ? pipe_name_len : 3);
@@ -75,9 +78,7 @@ BOOL cli_send_trans(struct cli_state *cli, int trans,
        for (i=0;i<lsetup;i++)          /* setup[] */
                SSVAL(cli->outbuf,smb_setup+i*2,setup[i]);
        p = smb_buf(cli->outbuf);
-       if (trans==SMBtrans) {
-               clistr_push(cli, p, pipe_name, -1, CLISTR_TERMINATE);
-       } else {
+       if (trans != SMBtrans) {
                *p++ = 0;  /* put in a null smb_name */
                *p++ = 'D'; *p++ = ' '; /* observed in OS/2 */
        }
@@ -88,14 +89,21 @@ BOOL cli_send_trans(struct cli_state *cli, int trans,
        cli_setup_bcc(cli, outdata+this_ldata);
 
        show_msg(cli->outbuf);
-       cli_send_smb(cli);
+
+       if (!cli_send_smb(cli)) {
+               return False;
+       }
+
+       /* Note we're in a trans state. Save the sequence
+        * numbers for replies. */
+       client_set_trans_sign_state_on(cli, mid);
 
        if (this_ldata < ldata || this_lparam < lparam) {
                /* receive interim response */
-               if (!cli_receive_smb(cli) || 
-                   CVAL(cli->inbuf,smb_rcls) != 0) {
+               if (!cli_receive_smb(cli) || cli_is_error(cli)) {
+                       client_set_trans_sign_state_off(cli, mid);
                        return(False);
-               }      
+               }
 
                tot_data = this_ldata;
                tot_param = this_lparam;
@@ -105,7 +113,7 @@ BOOL cli_send_trans(struct cli_state *cli, int trans,
                        this_ldata = MIN(ldata-tot_data,cli->max_xmit - (500+this_lparam));
 
                        set_message(cli->outbuf,trans==SMBtrans?8:9,0,True);
-                       CVAL(cli->outbuf,smb_com) = trans==SMBtrans ? SMBtranss : SMBtranss2;
+                       SCVAL(cli->outbuf,smb_com,(trans==SMBtrans ? SMBtranss : SMBtranss2));
                        
                        outparam = smb_buf(cli->outbuf);
                        outdata = outparam+this_lparam;
@@ -127,8 +135,20 @@ BOOL cli_send_trans(struct cli_state *cli, int trans,
                                memcpy(outdata,data+tot_data,this_ldata);
                        cli_setup_bcc(cli, outdata+this_ldata);
                        
+                       /*
+                        * Save the mid we're using. We need this for finding
+                        * signing replies.
+                        */
+                       mid = cli->mid;
+
                        show_msg(cli->outbuf);
-                       cli_send_smb(cli);
+                       if (!cli_send_smb(cli)) {
+                               client_set_trans_sign_state_off(cli, mid);
+                               return False;
+                       }
+
+                       /* Ensure we use the same mid for the secondaries. */
+                       cli->mid = mid;
                        
                        tot_data += this_ldata;
                        tot_param += this_lparam;
@@ -138,24 +158,25 @@ BOOL cli_send_trans(struct cli_state *cli, int trans,
        return(True);
 }
 
-
 /****************************************************************************
-  receive a SMB trans or trans2 response allocating the necessary memory
-  ****************************************************************************/
+ Receive a SMB trans or trans2 response allocating the necessary memory.
+****************************************************************************/
+
 BOOL cli_receive_trans(struct cli_state *cli,int trans,
-                              char **param, int *param_len,
-                              char **data, int *data_len)
+                              char **param, unsigned int *param_len,
+                              char **data, unsigned int *data_len)
 {
-       int total_data=0;
-       int total_param=0;
-       int this_data,this_param;
-       uint8 eclass;
-       uint32 ecode;
+       unsigned int total_data=0;
+       unsigned int total_param=0;
+       unsigned int this_data,this_param;
+       NTSTATUS status;
+       BOOL ret = False;
 
        *data_len = *param_len = 0;
 
-       if (!cli_receive_smb(cli))
+       if (!cli_receive_smb(cli)) {
                return False;
+       }
 
        show_msg(cli->inbuf);
        
@@ -164,23 +185,29 @@ BOOL cli_receive_trans(struct cli_state *cli,int trans,
                DEBUG(0,("Expected %s response, got command 0x%02x\n",
                         trans==SMBtrans?"SMBtrans":"SMBtrans2", 
                         CVAL(cli->inbuf,smb_com)));
-               return(False);
+               return False;
        }
 
        /*
         * An NT RPC pipe call can return ERRDOS, ERRmoredata
         * to a trans call. This is not an error and should not
-        * be treated as such.
+        * be treated as such. Note that STATUS_NO_MORE_FILES is
+        * returned when a trans2 findfirst/next finishes.
+        * When setting up an encrypted transport we can also
+        * see NT_STATUS_MORE_PROCESSING_REQUIRED here.
+         *
+         * Vista returns NT_STATUS_INACCESSIBLE_SYSTEM_SHORTCUT if the folder
+         * "<share>/Users/All Users" is enumerated.  This is a special pseudo
+         * folder, and the response does not have parameters (nor a parameter
+         * length).
         */
-
-       if (cli_error(cli, &eclass, &ecode, NULL))
-       {
-        if(cli->nt_pipe_fnum == 0)
-                       return(False);
-
-        if(!(eclass == ERRDOS && ecode == ERRmoredata)) {
-                       if (eclass != 0 && (ecode != (0x80000000 | STATUS_BUFFER_OVERFLOW)))
-                               return(False);
+       status = cli_nt_error(cli);
+       
+       if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+               if (NT_STATUS_IS_ERR(status) ||
+                    NT_STATUS_EQUAL(status,STATUS_NO_MORE_FILES) ||
+                    NT_STATUS_EQUAL(status,NT_STATUS_INACCESSIBLE_SYSTEM_SHORTCUT)) {
+                       goto out;
                }
        }
 
@@ -189,39 +216,93 @@ BOOL cli_receive_trans(struct cli_state *cli,int trans,
        total_param = SVAL(cli->inbuf,smb_tprcnt);
 
        /* allocate it */
-       *data = Realloc(*data,total_data);
-       *param = Realloc(*param,total_param);
+       if (total_data!=0) {
+               *data = (char *)SMB_REALLOC(*data,total_data);
+               if (!(*data)) {
+                       DEBUG(0,("cli_receive_trans: failed to enlarge data buffer\n"));
+                       goto out;
+               }
+       }
 
-       while (1)  {
+       if (total_param!=0) {
+               *param = (char *)SMB_REALLOC(*param,total_param);
+               if (!(*param)) {
+                       DEBUG(0,("cli_receive_trans: failed to enlarge param buffer\n"));
+                       goto out;
+               }
+       }
+
+       for (;;)  {
                this_data = SVAL(cli->inbuf,smb_drcnt);
                this_param = SVAL(cli->inbuf,smb_prcnt);
 
                if (this_data + *data_len > total_data ||
                    this_param + *param_len > total_param) {
                        DEBUG(1,("Data overflow in cli_receive_trans\n"));
-                       return False;
+                       goto out;
                }
 
-               if (this_data)
-                       memcpy(*data + SVAL(cli->inbuf,smb_drdisp),
-                              smb_base(cli->inbuf) + SVAL(cli->inbuf,smb_droff),
-                              this_data);
-               if (this_param)
-                       memcpy(*param + SVAL(cli->inbuf,smb_prdisp),
-                              smb_base(cli->inbuf) + SVAL(cli->inbuf,smb_proff),
-                              this_param);
+               if (this_data + *data_len < this_data ||
+                               this_data + *data_len < *data_len ||
+                               this_param + *param_len < this_param ||
+                               this_param + *param_len < *param_len) {
+                       DEBUG(1,("Data overflow in cli_receive_trans\n"));
+                       goto out;
+               }
+
+               if (this_data) {
+                       unsigned int data_offset_out = SVAL(cli->inbuf,smb_drdisp);
+                       unsigned int data_offset_in = SVAL(cli->inbuf,smb_droff);
+
+                       if (data_offset_out > total_data ||
+                                       data_offset_out + this_data > total_data ||
+                                       data_offset_out + this_data < data_offset_out ||
+                                       data_offset_out + this_data < this_data) {
+                               DEBUG(1,("Data overflow in cli_receive_trans\n"));
+                               goto out;
+                       }
+                       if (data_offset_in > cli->bufsize ||
+                                       data_offset_in + this_data >  cli->bufsize ||
+                                       data_offset_in + this_data < data_offset_in ||
+                                       data_offset_in + this_data < this_data) {
+                               DEBUG(1,("Data overflow in cli_receive_trans\n"));
+                               goto out;
+                       }
+
+                       memcpy(*data + data_offset_out, smb_base(cli->inbuf) + data_offset_in, this_data);
+               }
+               if (this_param) {
+                       unsigned int param_offset_out = SVAL(cli->inbuf,smb_prdisp);
+                       unsigned int param_offset_in = SVAL(cli->inbuf,smb_proff);
+
+                       if (param_offset_out > total_param ||
+                                       param_offset_out + this_param > total_param ||
+                                       param_offset_out + this_param < param_offset_out ||
+                                       param_offset_out + this_param < this_param) {
+                               DEBUG(1,("Param overflow in cli_receive_trans\n"));
+                               goto out;
+                       }
+                       if (param_offset_in > cli->bufsize ||
+                                       param_offset_in + this_param >  cli->bufsize ||
+                                       param_offset_in + this_param < param_offset_in ||
+                                       param_offset_in + this_param < this_param) {
+                               DEBUG(1,("Param overflow in cli_receive_trans\n"));
+                               goto out;
+                       }
+
+                       memcpy(*param + param_offset_out, smb_base(cli->inbuf) + param_offset_in, this_param);
+               }
                *data_len += this_data;
                *param_len += this_param;
 
-               /* parse out the total lengths again - they can shrink! */
-               total_data = SVAL(cli->inbuf,smb_tdrcnt);
-               total_param = SVAL(cli->inbuf,smb_tprcnt);
-               
-               if (total_data <= *data_len && total_param <= *param_len)
+               if (total_data <= *data_len && total_param <= *param_len) {
+                       ret = True;
                        break;
+               }
                
-               if (!cli_receive_smb(cli))
-                       return False;
+               if (!cli_receive_smb(cli)) {
+                       goto out;
+               }
 
                show_msg(cli->inbuf);
                
@@ -230,34 +311,47 @@ BOOL cli_receive_trans(struct cli_state *cli,int trans,
                        DEBUG(0,("Expected %s response, got command 0x%02x\n",
                                 trans==SMBtrans?"SMBtrans":"SMBtrans2", 
                                 CVAL(cli->inbuf,smb_com)));
-                       return(False);
+                       goto out;
                }
-               if (cli_error(cli, &eclass, &ecode, NULL))
-               {
-               if(cli->nt_pipe_fnum == 0 || !(eclass == ERRDOS && ecode == ERRmoredata))
-                               return(False);
+               if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+                       if (NT_STATUS_IS_ERR(cli_nt_error(cli))) {
+                               goto out;
+                       }
                }
-       }
-       
-       return(True);
-}
 
+               /* parse out the total lengths again - they can shrink! */
+               if (SVAL(cli->inbuf,smb_tdrcnt) < total_data)
+                       total_data = SVAL(cli->inbuf,smb_tdrcnt);
+               if (SVAL(cli->inbuf,smb_tprcnt) < total_param)
+                       total_param = SVAL(cli->inbuf,smb_tprcnt);
+               
+               if (total_data <= *data_len && total_param <= *param_len) {
+                       ret = True;
+                       break;
+               }
+       }
 
+  out:
 
+       client_set_trans_sign_state_off(cli, SVAL(cli->inbuf,smb_mid));
+       return ret;
+}
 
 /****************************************************************************
-  send a SMB nttrans request
-  ****************************************************************************/
+ Send a SMB nttrans request.
+****************************************************************************/
+
 BOOL cli_send_nt_trans(struct cli_state *cli, 
                       int function, 
                       int flags,
-                      uint16 *setup, int lsetup, int msetup,
-                      char *param, int lparam, int mparam,
-                      char *data, int ldata, int mdata)
+                      uint16 *setup, unsigned int lsetup, unsigned int msetup,
+                      char *param, unsigned int lparam, unsigned int mparam,
+                      char *data, unsigned int ldata, unsigned int mdata)
 {
-       int i;
-       int this_ldata,this_lparam;
-       int tot_data=0,tot_param=0;
+       unsigned int i;
+       unsigned int this_ldata,this_lparam;
+       unsigned int tot_data=0,tot_param=0;
+       uint16 mid;
        char *outdata,*outparam;
 
        this_lparam = MIN(lparam,cli->max_xmit - (500+lsetup*2)); /* hack */
@@ -265,10 +359,17 @@ BOOL cli_send_nt_trans(struct cli_state *cli,
 
        memset(cli->outbuf,'\0',smb_size);
        set_message(cli->outbuf,19+lsetup,0,True);
-       CVAL(cli->outbuf,smb_com) = SMBnttrans;
+       SCVAL(cli->outbuf,smb_com,SMBnttrans);
        SSVAL(cli->outbuf,smb_tid, cli->cnum);
        cli_setup_packet(cli);
 
+       /*
+        * Save the mid we're using. We need this for finding
+        * signing replies.
+        */
+
+       mid = cli->mid;
+
        outparam = smb_buf(cli->outbuf)+3;
        outdata = outparam+this_lparam;
 
@@ -296,14 +397,20 @@ BOOL cli_send_nt_trans(struct cli_state *cli,
        cli_setup_bcc(cli, outdata+this_ldata);
 
        show_msg(cli->outbuf);
-       cli_send_smb(cli);
+       if (!cli_send_smb(cli)) {
+               return False;
+       }       
+
+       /* Note we're in a trans state. Save the sequence
+        * numbers for replies. */
+       client_set_trans_sign_state_on(cli, mid);
 
        if (this_ldata < ldata || this_lparam < lparam) {
                /* receive interim response */
-               if (!cli_receive_smb(cli) || 
-                   CVAL(cli->inbuf,smb_rcls) != 0) {
+               if (!cli_receive_smb(cli) || cli_is_error(cli)) {
+                       client_set_trans_sign_state_off(cli, mid);
                        return(False);
-               }      
+               }
 
                tot_data = this_ldata;
                tot_param = this_lparam;
@@ -313,7 +420,7 @@ BOOL cli_send_nt_trans(struct cli_state *cli,
                        this_ldata = MIN(ldata-tot_data,cli->max_xmit - (500+this_lparam));
 
                        set_message(cli->outbuf,18,0,True);
-                       CVAL(cli->outbuf,smb_com) = SMBnttranss;
+                       SCVAL(cli->outbuf,smb_com,SMBnttranss);
 
                        /* XXX - these should probably be aligned */
                        outparam = smb_buf(cli->outbuf);
@@ -334,8 +441,21 @@ BOOL cli_send_nt_trans(struct cli_state *cli,
                                memcpy(outdata,data+tot_data,this_ldata);
                        cli_setup_bcc(cli, outdata+this_ldata);
                        
+                       /*
+                        * Save the mid we're using. We need this for finding
+                        * signing replies.
+                        */
+                       mid = cli->mid;
+
                        show_msg(cli->outbuf);
-                       cli_send_smb(cli);
+
+                       if (!cli_send_smb(cli)) {
+                               client_set_trans_sign_state_off(cli, mid);
+                               return False;
+                       }
+                       
+                       /* Ensure we use the same mid for the secondaries. */
+                       cli->mid = mid;
                        
                        tot_data += this_ldata;
                        tot_param += this_lparam;
@@ -345,25 +465,26 @@ BOOL cli_send_nt_trans(struct cli_state *cli,
        return(True);
 }
 
-
-
 /****************************************************************************
-  receive a SMB nttrans response allocating the necessary memory
-  ****************************************************************************/
+ Receive a SMB nttrans response allocating the necessary memory.
+****************************************************************************/
+
 BOOL cli_receive_nt_trans(struct cli_state *cli,
-                         char **param, int *param_len,
-                         char **data, int *data_len)
+                         char **param, unsigned int *param_len,
+                         char **data, unsigned int *data_len)
 {
-       int total_data=0;
-       int total_param=0;
-       int this_data,this_param;
+       unsigned int total_data=0;
+       unsigned int total_param=0;
+       unsigned int this_data,this_param;
        uint8 eclass;
        uint32 ecode;
+       BOOL ret = False;
 
        *data_len = *param_len = 0;
 
-       if (!cli_receive_smb(cli))
+       if (!cli_receive_smb(cli)) {
                return False;
+       }
 
        show_msg(cli->inbuf);
        
@@ -379,9 +500,21 @@ BOOL cli_receive_nt_trans(struct cli_state *cli,
         * to a trans call. This is not an error and should not
         * be treated as such.
         */
-       if (cli_error(cli, &eclass, &ecode, NULL)) {
-               if (cli->nt_pipe_fnum == 0 || !(eclass == ERRDOS && ecode == ERRmoredata))
-                       return(False);
+       if (cli_is_dos_error(cli)) {
+                cli_dos_error(cli, &eclass, &ecode);
+               if (!(eclass == ERRDOS && ecode == ERRmoredata)) {
+                       goto out;
+               }
+       }
+
+       /*
+        * Likewise for NT_STATUS_BUFFER_TOO_SMALL
+        */
+       if (cli_is_nt_error(cli)) {
+               if (!NT_STATUS_EQUAL(cli_nt_error(cli),
+                                    NT_STATUS_BUFFER_TOO_SMALL)) {
+                       goto out;
+               }
        }
 
        /* parse out the lengths */
@@ -389,8 +522,21 @@ BOOL cli_receive_nt_trans(struct cli_state *cli,
        total_param = SVAL(cli->inbuf,smb_ntr_TotalParameterCount);
 
        /* allocate it */
-       *data = Realloc(*data,total_data);
-       *param = Realloc(*param,total_param);
+       if (total_data) {
+               *data = (char *)SMB_REALLOC(*data,total_data);
+               if (!(*data)) {
+                       DEBUG(0,("cli_receive_nt_trans: failed to enlarge data buffer to %d\n",total_data));
+                       goto out;
+               }
+       }
+
+       if (total_param) {
+               *param = (char *)SMB_REALLOC(*param,total_param);
+               if (!(*param)) {
+                       DEBUG(0,("cli_receive_nt_trans: failed to enlarge param buffer to %d\n", total_param));
+                       goto out;
+               }
+       }
 
        while (1)  {
                this_data = SVAL(cli->inbuf,smb_ntr_DataCount);
@@ -398,30 +544,73 @@ BOOL cli_receive_nt_trans(struct cli_state *cli,
 
                if (this_data + *data_len > total_data ||
                    this_param + *param_len > total_param) {
-                       DEBUG(1,("Data overflow in cli_receive_trans\n"));
-                       return False;
+                       DEBUG(1,("Data overflow in cli_receive_nt_trans\n"));
+                       goto out;
+               }
+
+               if (this_data + *data_len < this_data ||
+                               this_data + *data_len < *data_len ||
+                               this_param + *param_len < this_param ||
+                               this_param + *param_len < *param_len) {
+                       DEBUG(1,("Data overflow in cli_receive_nt_trans\n"));
+                       goto out;
+               }
+
+               if (this_data) {
+                       unsigned int data_offset_out = SVAL(cli->inbuf,smb_ntr_DataDisplacement);
+                       unsigned int data_offset_in = SVAL(cli->inbuf,smb_ntr_DataOffset);
+
+                       if (data_offset_out > total_data ||
+                                       data_offset_out + this_data > total_data ||
+                                       data_offset_out + this_data < data_offset_out ||
+                                       data_offset_out + this_data < this_data) {
+                               DEBUG(1,("Data overflow in cli_receive_nt_trans\n"));
+                               goto out;
+                       }
+                       if (data_offset_in > cli->bufsize ||
+                                       data_offset_in + this_data >  cli->bufsize ||
+                                       data_offset_in + this_data < data_offset_in ||
+                                       data_offset_in + this_data < this_data) {
+                               DEBUG(1,("Data overflow in cli_receive_nt_trans\n"));
+                               goto out;
+                       }
+
+                       memcpy(*data + data_offset_out, smb_base(cli->inbuf) + data_offset_in, this_data);
+               }
+
+               if (this_param) {
+                       unsigned int param_offset_out = SVAL(cli->inbuf,smb_ntr_ParameterDisplacement);
+                       unsigned int param_offset_in = SVAL(cli->inbuf,smb_ntr_ParameterOffset);
+
+                       if (param_offset_out > total_param ||
+                                       param_offset_out + this_param > total_param ||
+                                       param_offset_out + this_param < param_offset_out ||
+                                       param_offset_out + this_param < this_param) {
+                               DEBUG(1,("Param overflow in cli_receive_nt_trans\n"));
+                               goto out;
+                       }
+                       if (param_offset_in > cli->bufsize ||
+                                       param_offset_in + this_param >  cli->bufsize ||
+                                       param_offset_in + this_param < param_offset_in ||
+                                       param_offset_in + this_param < this_param) {
+                               DEBUG(1,("Param overflow in cli_receive_nt_trans\n"));
+                               goto out;
+                       }
+
+                       memcpy(*param + param_offset_out, smb_base(cli->inbuf) + param_offset_in, this_param);
                }
 
-               if (this_data)
-                       memcpy(*data + SVAL(cli->inbuf,smb_ntr_DataDisplacement),
-                              smb_base(cli->inbuf) + SVAL(cli->inbuf,smb_ntr_DataOffset),
-                              this_data);
-               if (this_param)
-                       memcpy(*param + SVAL(cli->inbuf,smb_ntr_ParameterDisplacement),
-                              smb_base(cli->inbuf) + SVAL(cli->inbuf,smb_ntr_ParameterOffset),
-                              this_param);
                *data_len += this_data;
                *param_len += this_param;
 
-               /* parse out the total lengths again - they can shrink! */
-               total_data = SVAL(cli->inbuf,smb_ntr_TotalDataCount);
-               total_param = SVAL(cli->inbuf,smb_ntr_TotalParameterCount);
-               
-               if (total_data <= *data_len && total_param <= *param_len)
+               if (total_data <= *data_len && total_param <= *param_len) {
+                       ret = True;
                        break;
+               }
                
-               if (!cli_receive_smb(cli))
-                       return False;
+               if (!cli_receive_smb(cli)) {
+                       goto out;
+               }
 
                show_msg(cli->inbuf);
                
@@ -429,13 +618,38 @@ BOOL cli_receive_nt_trans(struct cli_state *cli,
                if (CVAL(cli->inbuf,smb_com) != SMBnttrans) {
                        DEBUG(0,("Expected SMBnttrans response, got command 0x%02x\n",
                                 CVAL(cli->inbuf,smb_com)));
-                       return(False);
+                       goto out;
+               }
+               if (cli_is_dos_error(cli)) {
+                        cli_dos_error(cli, &eclass, &ecode);
+                       if(!(eclass == ERRDOS && ecode == ERRmoredata)) {
+                               goto out;
+                       }
                }
-               if (cli_error(cli, &eclass, &ecode, NULL)) {
-                       if(cli->nt_pipe_fnum == 0 || !(eclass == ERRDOS && ecode == ERRmoredata))
-                               return(False);
+               /*
+                * Likewise for NT_STATUS_BUFFER_TOO_SMALL
+                */
+               if (cli_is_nt_error(cli)) {
+                       if (!NT_STATUS_EQUAL(cli_nt_error(cli),
+                                            NT_STATUS_BUFFER_TOO_SMALL)) {
+                               goto out;
+                       }
+               }
+
+               /* parse out the total lengths again - they can shrink! */
+               if (SVAL(cli->inbuf,smb_ntr_TotalDataCount) < total_data)
+                       total_data = SVAL(cli->inbuf,smb_ntr_TotalDataCount);
+               if (SVAL(cli->inbuf,smb_ntr_TotalParameterCount) < total_param)
+                       total_param = SVAL(cli->inbuf,smb_ntr_TotalParameterCount);
+               
+               if (total_data <= *data_len && total_param <= *param_len) {
+                       ret = True;
+                       break;
                }
        }
-       
-       return(True);
+
+  out:
+
+       client_set_trans_sign_state_off(cli, SVAL(cli->inbuf,smb_mid));
+       return ret;
 }