r4088: Get medieval on our ass about malloc.... :-). Take control of all our allocation
[tprouty/samba.git] / source / smbd / ipc.c
index e853112bfee173304a0956ef2e99150cac6ba4cc..9fcd39b5002caa932a0ebbcd3163ec055fa6d42f 100644 (file)
@@ -1,6 +1,5 @@
 /* 
-   Unix SMB/Netbios implementation.
-   Version 1.9.
+   Unix SMB/CIFS implementation.
    Inter-process communication and named pipe handling
    Copyright (C) Andrew Tridgell 1992-1998
 
@@ -28,7 +27,6 @@
 
 #include "includes.h"
 
-extern int DEBUGLEVEL;
 extern int max_send;
 
 extern fstring local_machine;
@@ -36,7 +34,6 @@ extern fstring local_machine;
 #define NERR_notsupported 50
 
 extern int smb_read_error;
-extern uint32 global_client_caps;
 
 /*******************************************************************
  copies parameters and data, as needed, into the smb buffer
@@ -98,21 +95,12 @@ void send_trans_reply(char *outbuf,
 
        align = ((this_lparam)%4);
 
-       set_message(outbuf,10,1+align+this_ldata+this_lparam,True);
-
-       if (buffer_too_large)
-       {
-               /* issue a buffer size warning.  on a DCE/RPC pipe, expect an SMBreadX... */
-               if (!(global_client_caps & (CAP_NT_SMBS | CAP_STATUS32 ))) { 
-                       /* Win9x version. */
-                       SSVAL(outbuf, smb_err, ERRmoredata);
-                       SCVAL(outbuf, smb_rcls, ERRDOS);
-               } else {
-                       SIVAL(outbuf, smb_flg2, SVAL(outbuf, smb_flg2) | FLAGS2_32_BIT_ERROR_CODES);
-                       SIVAL(outbuf, smb_rcls, 0x80000000 | STATUS_BUFFER_OVERFLOW);
-               }
+       if (buffer_too_large) {
+               ERROR_BOTH(STATUS_BUFFER_OVERFLOW,ERRDOS,ERRmoredata);
        }
 
+       set_message(outbuf,10,1+align+this_ldata+this_lparam,True);
+
        copy_trans_params_and_data(outbuf, align,
                                                                rparam, tot_param_sent, this_lparam,
                                                                rdata, tot_data_sent, this_ldata);
@@ -129,7 +117,7 @@ void send_trans_reply(char *outbuf,
 
        show_msg(outbuf);
        if (!send_smb(smbd_server_fd(),outbuf))
-               exit_server("send_trans_reply: send_smb failed.\n");
+               exit_server("send_trans_reply: send_smb failed.");
 
        tot_data_sent = this_ldata;
        tot_param_sent = this_lparam;
@@ -150,9 +138,9 @@ void send_trans_reply(char *outbuf,
                set_message(outbuf,10,1+this_ldata+this_lparam+align,False);
 
                copy_trans_params_and_data(outbuf, align,
-                                                                       rparam, tot_param_sent, this_lparam,
-                                                                       rdata, tot_data_sent, this_ldata);
-
+                                          rparam, tot_param_sent, this_lparam,
+                                          rdata, tot_data_sent, this_ldata);
+               
                SSVAL(outbuf,smb_vwv3,this_lparam);
                SSVAL(outbuf,smb_vwv4,smb_offset(smb_buf(outbuf)+1,outbuf));
                SSVAL(outbuf,smb_vwv5,tot_param_sent);
@@ -163,7 +151,7 @@ void send_trans_reply(char *outbuf,
 
                show_msg(outbuf);
                if (!send_smb(smbd_server_fd(),outbuf))
-                       exit_server("send_trans_reply: send_smb failed.\n");
+                       exit_server("send_trans_reply: send_smb failed.");
 
                tot_data_sent  += this_ldata;
                tot_param_sent += this_lparam;
@@ -174,9 +162,10 @@ void send_trans_reply(char *outbuf,
  Start the first part of an RPC reply which began with an SMBtrans request.
 ****************************************************************************/
 
-static BOOL api_rpc_trans_reply(char *outbuf, pipes_struct *p)
+static BOOL api_rpc_trans_reply(char *outbuf, smb_np_struct *p)
 {
-       char *rdata = malloc(p->max_trans_reply);
+       BOOL is_data_outstanding;
+       char *rdata = SMB_MALLOC(p->max_trans_reply);
        int data_len;
 
        if(rdata == NULL) {
@@ -184,14 +173,15 @@ static BOOL api_rpc_trans_reply(char *outbuf, pipes_struct *p)
                return False;
        }
 
-       if((data_len = read_from_pipe( p, rdata, p->max_trans_reply)) < 0) {
-               free(rdata);
+       if((data_len = read_from_pipe( p, rdata, p->max_trans_reply,
+                                       &is_data_outstanding)) < 0) {
+               SAFE_FREE(rdata);
                return False;
        }
 
-       send_trans_reply(outbuf, NULL, 0, rdata, data_len, p->out_data.current_pdu_len > data_len);
+       send_trans_reply(outbuf, NULL, 0, rdata, data_len, is_data_outstanding);
 
-       free(rdata);
+       SAFE_FREE(rdata);
        return True;
 }
 
@@ -199,7 +189,7 @@ static BOOL api_rpc_trans_reply(char *outbuf, pipes_struct *p)
  WaitNamedPipeHandleState 
 ****************************************************************************/
 
-static BOOL api_WNPHS(char *outbuf, pipes_struct *p, char *param, int param_len)
+static BOOL api_WNPHS(char *outbuf, smb_np_struct *p, char *param, int param_len)
 {
        uint16 priority;
 
@@ -222,7 +212,7 @@ static BOOL api_WNPHS(char *outbuf, pipes_struct *p, char *param, int param_len)
  SetNamedPipeHandleState 
 ****************************************************************************/
 
-static BOOL api_SNPHS(char *outbuf, pipes_struct *p, char *param, int param_len)
+static BOOL api_SNPHS(char *outbuf, smb_np_struct *p, char *param, int param_len)
 {
        uint16 id;
 
@@ -270,7 +260,7 @@ static int api_fd_reply(connection_struct *conn,uint16 vuid,char *outbuf,
                        int suwcnt,int tdscnt,int tpscnt,int mdrcnt,int mprcnt)
 {
        BOOL reply = False;
-       pipes_struct *p = NULL;
+       smb_np_struct *p = NULL;
        int pnum;
        int subcommand;
 
@@ -291,11 +281,19 @@ static int api_fd_reply(connection_struct *conn,uint16 vuid,char *outbuf,
        subcommand = ((int)setup[0]) & 0xFFFF;
 
        if(!(p = get_rpc_pipe(pnum))) {
+               if (subcommand == TRANSACT_WAITNAMEDPIPEHANDLESTATE) {
+                       /* Win9x does this call with a unicode pipe name, not a pnum. */
+                       /* Just return success for now... */
+                       DEBUG(3,("Got TRANSACT_WAITNAMEDPIPEHANDLESTATE on text pipe name\n"));
+                       send_trans_reply(outbuf, NULL, 0, NULL, 0, False);
+                       return -1;
+               }
+
                DEBUG(1,("api_fd_reply: INVALID PIPE HANDLE: %x\n", pnum));
                return api_no_reply(outbuf, mdrcnt);
        }
 
-       DEBUG(3,("Got API command 0x%x on pipe \"%s\" (pnum %x)", subcommand, p->name, pnum));
+       DEBUG(3,("Got API command 0x%x on pipe \"%s\" (pnum %x)\n", subcommand, p->name, pnum));
 
        /* record maximum data length that can be transmitted in an SMBtrans */
        p->max_trans_reply = mdrcnt;
@@ -303,17 +301,17 @@ static int api_fd_reply(connection_struct *conn,uint16 vuid,char *outbuf,
        DEBUG(10,("api_fd_reply: p:%p max_trans_reply: %d\n", p, p->max_trans_reply));
 
        switch (subcommand) {
-       case 0x26:
+       case TRANSACT_DCERPCCMD:
                /* dce/rpc command */
                reply = write_to_pipe(p, data, tdscnt);
                if (reply)
                        reply = api_rpc_trans_reply(outbuf, p);
                break;
-       case 0x53:
+       case TRANSACT_WAITNAMEDPIPEHANDLESTATE:
                /* Wait Named Pipe Handle state */
                reply = api_WNPHS(outbuf, p, params, tpscnt);
                break;
-       case 0x01:
+       case TRANSACT_SETNAMEDPIPEHANDLESTATE:
                /* Set Named Pipe Handle state */
                reply = api_SNPHS(outbuf, p, params, tpscnt);
                break;
@@ -370,73 +368,102 @@ int reply_trans(connection_struct *conn, char *inbuf,char *outbuf, int size, int
        uint16 *setup=NULL;
        int outsize = 0;
        uint16 vuid = SVAL(inbuf,smb_uid);
-       int tpscnt = SVAL(inbuf,smb_vwv0);
-       int tdscnt = SVAL(inbuf,smb_vwv1);
-       int mprcnt = SVAL(inbuf,smb_vwv2);
-       int mdrcnt = SVAL(inbuf,smb_vwv3);
-       int msrcnt = CVAL(inbuf,smb_vwv4);
+       unsigned int tpscnt = SVAL(inbuf,smb_vwv0);
+       unsigned int tdscnt = SVAL(inbuf,smb_vwv1);
+       unsigned int mprcnt = SVAL(inbuf,smb_vwv2);
+       unsigned int mdrcnt = SVAL(inbuf,smb_vwv3);
+       unsigned int msrcnt = CVAL(inbuf,smb_vwv4);
        BOOL close_on_completion = BITSETW(inbuf+smb_vwv5,0);
        BOOL one_way = BITSETW(inbuf+smb_vwv5,1);
-       int pscnt = SVAL(inbuf,smb_vwv9);
-       int psoff = SVAL(inbuf,smb_vwv10);
-       int dscnt = SVAL(inbuf,smb_vwv11);
-       int dsoff = SVAL(inbuf,smb_vwv12);
-       int suwcnt = CVAL(inbuf,smb_vwv13);
+       unsigned int pscnt = SVAL(inbuf,smb_vwv9);
+       unsigned int psoff = SVAL(inbuf,smb_vwv10);
+       unsigned int dscnt = SVAL(inbuf,smb_vwv11);
+       unsigned int dsoff = SVAL(inbuf,smb_vwv12);
+       unsigned int suwcnt = CVAL(inbuf,smb_vwv13);
        START_PROFILE(SMBtrans);
 
        memset(name, '\0',sizeof(name));
-       srvstr_pull(inbuf, name, smb_buf(inbuf), sizeof(name), -1, STR_TERMINATE);
+       srvstr_pull_buf(inbuf, name, smb_buf(inbuf), sizeof(name), STR_TERMINATE);
 
-       if (dscnt > tdscnt || pscnt > tpscnt) {
-               exit_server("invalid trans parameters\n");
-       }
+       if (dscnt > tdscnt || pscnt > tpscnt)
+               goto bad_param;
   
        if (tdscnt)  {
-               if((data = (char *)malloc(tdscnt)) == NULL) {
-                       DEBUG(0,("reply_trans: data malloc fail for %d bytes !\n", tdscnt));
+               if((data = (char *)SMB_MALLOC(tdscnt)) == NULL) {
+                       DEBUG(0,("reply_trans: data malloc fail for %u bytes !\n", tdscnt));
                        END_PROFILE(SMBtrans);
-                       return(ERROR(ERRDOS,ERRnomem));
+                       return(ERROR_DOS(ERRDOS,ERRnomem));
                } 
+               if ((dsoff+dscnt < dsoff) || (dsoff+dscnt < dscnt))
+                       goto bad_param;
+               if ((smb_base(inbuf)+dsoff+dscnt > inbuf + size) ||
+                               (smb_base(inbuf)+dsoff+dscnt < smb_base(inbuf)))
+                       goto bad_param;
+
                memcpy(data,smb_base(inbuf)+dsoff,dscnt);
        }
 
        if (tpscnt) {
-               if((params = (char *)malloc(tpscnt)) == NULL) {
-                       DEBUG(0,("reply_trans: param malloc fail for %d bytes !\n", tpscnt));
+               if((params = (char *)SMB_MALLOC(tpscnt)) == NULL) {
+                       DEBUG(0,("reply_trans: param malloc fail for %u bytes !\n", tpscnt));
+                       SAFE_FREE(data);
                        END_PROFILE(SMBtrans);
-                       return(ERROR(ERRDOS,ERRnomem));
+                       return(ERROR_DOS(ERRDOS,ERRnomem));
                } 
+               if ((psoff+pscnt < psoff) || (psoff+pscnt < pscnt))
+                       goto bad_param;
+               if ((smb_base(inbuf)+psoff+pscnt > inbuf + size) ||
+                               (smb_base(inbuf)+psoff+pscnt < smb_base(inbuf)))
+                       goto bad_param;
+
                memcpy(params,smb_base(inbuf)+psoff,pscnt);
        }
 
        if (suwcnt) {
-               int i;
-               if((setup = (uint16 *)malloc(suwcnt*sizeof(uint16))) == NULL) {
-          DEBUG(0,("reply_trans: setup malloc fail for %d bytes !\n", (int)(suwcnt * sizeof(uint16))));
-                 END_PROFILE(SMBtrans);
-                 return(ERROR(ERRDOS,ERRnomem));
-        } 
+               unsigned int i;
+               if((setup = SMB_MALLOC_ARRAY(uint16,suwcnt)) == NULL) {
+                       DEBUG(0,("reply_trans: setup malloc fail for %u bytes !\n", (unsigned int)(suwcnt * sizeof(uint16))));
+                       SAFE_FREE(data);
+                       SAFE_FREE(params);
+                       END_PROFILE(SMBtrans);
+                       return(ERROR_DOS(ERRDOS,ERRnomem));
+               } 
+               if (inbuf+smb_vwv14+(suwcnt*SIZEOFWORD) > inbuf + size)
+                       goto bad_param;
+               if ((smb_vwv14+(suwcnt*SIZEOFWORD) < smb_vwv14) || (smb_vwv14+(suwcnt*SIZEOFWORD) < (suwcnt*SIZEOFWORD)))
+                       goto bad_param;
+
                for (i=0;i<suwcnt;i++)
                        setup[i] = SVAL(inbuf,smb_vwv14+i*SIZEOFWORD);
        }
 
 
+       srv_signing_trans_start(SVAL(inbuf,smb_mid));
+
        if (pscnt < tpscnt || dscnt < tdscnt) {
                /* We need to send an interim response then receive the rest
                   of the parameter/data bytes */
                outsize = set_message(outbuf,0,0,True);
                show_msg(outbuf);
+               srv_signing_trans_stop();
                if (!send_smb(smbd_server_fd(),outbuf))
-                       exit_server("reply_trans: send_smb failed.\n");
+                       exit_server("reply_trans: send_smb failed.");
        }
 
        /* receive the rest of the trans packet */
        while (pscnt < tpscnt || dscnt < tdscnt) {
                BOOL ret;
-               int pcnt,poff,dcnt,doff,pdisp,ddisp;
+               unsigned int pcnt,poff,dcnt,doff,pdisp,ddisp;
       
                ret = receive_next_smb(inbuf,bufsize,SMB_SECONDARY_WAIT);
 
+               /*
+                * The sequence number for the trans reply is always
+                * based on the last secondary received.
+                */
+
+               srv_signing_trans_start(SVAL(inbuf,smb_mid));
+
                if ((ret && (CVAL(inbuf, smb_com) != SMBtranss)) || !ret) {
                        if(ret) {
                                DEBUG(0,("reply_trans: Invalid secondary trans packet\n"));
@@ -444,20 +471,21 @@ int reply_trans(connection_struct *conn, char *inbuf,char *outbuf, int size, int
                                DEBUG(0,("reply_trans: %s in getting secondary trans response.\n",
                                         (smb_read_error == READ_ERROR) ? "error" : "timeout" ));
                        }
-                       if (params)
-                               free(params);
-                       if (data)
-                               free(data);
-                       if (setup)
-                               free(setup);
+                       SAFE_FREE(params);
+                       SAFE_FREE(data);
+                       SAFE_FREE(setup);
                        END_PROFILE(SMBtrans);
-                       return(ERROR(ERRSRV,ERRerror));
+                       srv_signing_trans_stop();
+                       return(ERROR_DOS(ERRSRV,ERRerror));
                }
 
                show_msg(inbuf);
       
-               tpscnt = SVAL(inbuf,smb_vwv0);
-               tdscnt = SVAL(inbuf,smb_vwv1);
+               /* Revise total_params and total_data in case they have changed downwards */
+               if (SVAL(inbuf,smb_vwv0) < tpscnt)
+                       tpscnt = SVAL(inbuf,smb_vwv0);
+               if (SVAL(inbuf,smb_vwv1) < tdscnt)
+                       tdscnt = SVAL(inbuf,smb_vwv1);
 
                pcnt = SVAL(inbuf,smb_vwv2);
                poff = SVAL(inbuf,smb_vwv3);
@@ -470,20 +498,45 @@ int reply_trans(connection_struct *conn, char *inbuf,char *outbuf, int size, int
                pscnt += pcnt;
                dscnt += dcnt;
                
-               if (dscnt > tdscnt || pscnt > tpscnt) {
-                       exit_server("invalid trans parameters\n");
-               }
+               if (dscnt > tdscnt || pscnt > tpscnt)
+                       goto bad_param;
                
-               if (pcnt)
+               if (pcnt) {
+                       if (pdisp+pcnt > tpscnt)
+                               goto bad_param;
+                       if ((pdisp+pcnt < pdisp) || (pdisp+pcnt < pcnt))
+                               goto bad_param;
+                       if (pdisp > tpscnt)
+                               goto bad_param;
+                       if ((smb_base(inbuf) + poff + pcnt >= inbuf + bufsize) ||
+                                       (smb_base(inbuf) + poff + pcnt < smb_base(inbuf)))
+                               goto bad_param;
+                       if (params + pdisp < params)
+                               goto bad_param;
+
                        memcpy(params+pdisp,smb_base(inbuf)+poff,pcnt);
-               if (dcnt)
+               }
+
+               if (dcnt) {
+                       if (ddisp+dcnt > tdscnt)
+                               goto bad_param;
+                       if ((ddisp+dcnt < ddisp) || (ddisp+dcnt < dcnt))
+                               goto bad_param;
+                       if (ddisp > tdscnt)
+                               goto bad_param;
+                       if ((smb_base(inbuf) + doff + dcnt >= inbuf + bufsize) ||
+                                       (smb_base(inbuf) + doff + dcnt < smb_base(inbuf)))
+                               goto bad_param;
+                       if (data + ddisp < data)
+                               goto bad_param;
+
                        memcpy(data+ddisp,smb_base(inbuf)+doff,dcnt);      
+               }
        }
-       
-       
-       DEBUG(3,("trans <%s> data=%d params=%d setup=%d\n",
+
+       DEBUG(3,("trans <%s> data=%u params=%u setup=%u\n",
                 name,tdscnt,tpscnt,suwcnt));
-       
+
        /*
         * WinCE wierdness....
         */
@@ -511,13 +564,12 @@ int reply_trans(connection_struct *conn, char *inbuf,char *outbuf, int size, int
        }
 
        
-       if (data)
-               free(data);
-       if (params)
-               free(params);
-       if (setup)
-               free(setup);
+       SAFE_FREE(data);
+       SAFE_FREE(params);
+       SAFE_FREE(setup);
        
+       srv_signing_trans_stop();
+
        if (close_on_completion)
                close_cnum(conn,vuid);
 
@@ -528,9 +580,20 @@ int reply_trans(connection_struct *conn, char *inbuf,char *outbuf, int size, int
        
        if (outsize == 0) {
                END_PROFILE(SMBtrans);
-               return(ERROR(ERRSRV,ERRnosupport));
+               return(ERROR_DOS(ERRSRV,ERRnosupport));
        }
        
        END_PROFILE(SMBtrans);
        return(outsize);
+
+
+  bad_param:
+
+       srv_signing_trans_stop();
+       DEBUG(0,("reply_trans: invalid trans parameters\n"));
+       SAFE_FREE(data);
+       SAFE_FREE(params);
+       SAFE_FREE(setup);
+       END_PROFILE(SMBtrans);
+       return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
 }