r23792: convert Samba4 to GPLv3
[amitay/samba.git] / source4 / libcli / raw / rawtrans.c
1 /* 
2    Unix SMB/CIFS implementation.
3    raw trans/trans2/nttrans operations
4
5    Copyright (C) James Myers 2003 <myersjj@samba.org>
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "includes.h"
22 #include "lib/util/dlinklist.h"
23 #include "libcli/raw/libcliraw.h"
24
25 #define TORTURE_TRANS_DATA 0
26
27 /*
28   check out of bounds for incoming data
29 */
30 static BOOL raw_trans_oob(struct smbcli_request *req,
31                           uint_t offset, uint_t count)
32 {
33         uint8_t *ptr;
34
35         if (count == 0) {
36                 return False;
37         }
38
39         ptr = req->in.hdr + offset;
40         
41         /* be careful with wraparound! */
42         if (ptr < req->in.data ||
43             ptr >= req->in.data + req->in.data_size ||
44             count > req->in.data_size ||
45             ptr + count > req->in.data + req->in.data_size) {
46                 return True;
47         }
48         return False;   
49 }
50
51 /****************************************************************************
52   receive a SMB trans or trans2 response allocating the necessary memory
53   ****************************************************************************/
54 NTSTATUS smb_raw_trans2_recv(struct smbcli_request *req,
55                              TALLOC_CTX *mem_ctx,
56                              struct smb_trans2 *parms)
57 {
58         int total_data=0;
59         int total_param=0;
60         uint8_t *tdata;
61         uint8_t *tparam;
62
63         parms->out.data.length = 0;
64         parms->out.data.data = NULL;
65         parms->out.params.length = 0;
66         parms->out.params.data = NULL;
67
68         if (!smbcli_request_receive(req)) {
69                 return smbcli_request_destroy(req);
70         }
71         
72         /*
73          * An NT RPC pipe call can return ERRDOS, ERRmoredata
74          * to a trans call. This is not an error and should not
75          * be treated as such.
76          */
77         if (NT_STATUS_IS_ERR(req->status)) {
78                 return smbcli_request_destroy(req);
79         }
80
81         SMBCLI_CHECK_MIN_WCT(req, 10);
82
83         /* parse out the lengths */
84         total_data = SVAL(req->in.vwv, VWV(1));
85         total_param = SVAL(req->in.vwv, VWV(0));
86
87         /* allocate it */
88         if (total_data != 0) {
89                 tdata = talloc_size(mem_ctx, total_data);
90                 if (!tdata) {
91                         DEBUG(0,("smb_raw_receive_trans: failed to enlarge data buffer to %d bytes\n", total_data));
92                         req->status = NT_STATUS_NO_MEMORY;
93                         return smbcli_request_destroy(req);
94                 }
95                 parms->out.data.data = tdata;
96         }
97
98         if (total_param != 0) {
99                 tparam = talloc_size(mem_ctx, total_param);
100                 if (!tparam) {
101                         DEBUG(0,("smb_raw_receive_trans: failed to enlarge param buffer to %d bytes\n", total_param));
102                         req->status = NT_STATUS_NO_MEMORY;
103                         return smbcli_request_destroy(req);
104                 }
105                 parms->out.params.data = tparam;
106         }
107
108         parms->out.setup_count = SVAL(req->in.vwv, VWV(9));
109         SMBCLI_CHECK_WCT(req, 10 + parms->out.setup_count);
110
111         if (parms->out.setup_count > 0) {
112                 int i;
113                 parms->out.setup = talloc_array(mem_ctx, uint16_t, parms->out.setup_count);
114                 if (!parms->out.setup) {
115                         req->status = NT_STATUS_NO_MEMORY;
116                         return smbcli_request_destroy(req);
117                 }
118                 for (i=0;i<parms->out.setup_count;i++) {
119                         parms->out.setup[i] = SVAL(req->in.vwv, VWV(10+i));
120                 }
121         }
122
123         while (1)  {
124                 uint16_t param_count, param_ofs, param_disp;
125                 uint16_t data_count, data_ofs, data_disp;
126                 uint16_t total_data2, total_param2;
127
128                 /* parse out the total lengths again - they can shrink! */
129                 total_data2 = SVAL(req->in.vwv, VWV(1));
130                 total_param2 = SVAL(req->in.vwv, VWV(0));
131
132                 if (total_data2 > total_data ||
133                     total_param2 > total_param) {
134                         /* they must *only* shrink */
135                         DEBUG(1,("smb_raw_receive_trans: data/params expanded!\n"));
136                         req->status = NT_STATUS_BUFFER_TOO_SMALL;
137                         return smbcli_request_destroy(req);
138                 }
139
140                 total_data = total_data2;
141                 total_param = total_param2;             
142
143                 /* parse params for this lump */
144                 param_count = SVAL(req->in.vwv, VWV(3));
145                 param_ofs   = SVAL(req->in.vwv, VWV(4));
146                 param_disp  = SVAL(req->in.vwv, VWV(5));
147
148                 data_count = SVAL(req->in.vwv, VWV(6));
149                 data_ofs   = SVAL(req->in.vwv, VWV(7));
150                 data_disp  = SVAL(req->in.vwv, VWV(8));
151
152                 if (data_count + data_disp > total_data ||
153                     param_count + param_disp > total_param) {
154                         DEBUG(1,("smb_raw_receive_trans: Buffer overflow\n"));
155                         req->status = NT_STATUS_BUFFER_TOO_SMALL;
156                         return smbcli_request_destroy(req);
157                 }
158                 
159                 /* check the server isn't being nasty */
160                 if (raw_trans_oob(req, param_ofs, param_count) ||
161                     raw_trans_oob(req, data_ofs, data_count)) {
162                         DEBUG(1,("smb_raw_receive_trans: out of bounds parameters!\n"));
163                         req->status = NT_STATUS_BUFFER_TOO_SMALL;
164                         return smbcli_request_destroy(req);
165                 }
166
167                 if (data_count) {
168                         memcpy(parms->out.data.data + data_disp,
169                                req->in.hdr + data_ofs, 
170                                data_count);
171                 }
172
173                 if (param_count) {
174                         memcpy(parms->out.params.data + param_disp,
175                                req->in.hdr + param_ofs, 
176                                param_count);
177                 }
178
179                 parms->out.data.length += data_count;
180                 parms->out.params.length += param_count;
181
182                 if (total_data <= parms->out.data.length && total_param <= parms->out.params.length)
183                         break;
184         
185                 if (!smbcli_request_receive_more(req)) {
186                         req->status = NT_STATUS_UNSUCCESSFUL;
187                         return smbcli_request_destroy(req);
188                 }
189         }
190
191 failed:
192         return smbcli_request_destroy(req);
193 }
194
195 NTSTATUS smb_raw_trans_recv(struct smbcli_request *req,
196                              TALLOC_CTX *mem_ctx,
197                              struct smb_trans2 *parms)
198 {
199         return smb_raw_trans2_recv(req, mem_ctx, parms);
200 }
201
202
203 /*
204   trans/trans2 raw async interface - only BLOBs used in this interface.
205 */
206 struct smbcli_request *smb_raw_trans_send_backend(struct smbcli_tree *tree,
207                                                   struct smb_trans2 *parms,
208                                                   uint8_t command)
209 {
210         int wct = 14 + parms->in.setup_count;
211         struct smbcli_request *req, *req2; 
212         uint8_t *outdata,*outparam;
213         int i;
214         int padding;
215         size_t namelen = 0;
216         uint16_t data_disp, data_length, max_data;
217
218         if (parms->in.params.length > UINT16_MAX ||
219             parms->in.data.length > UINT16_MAX) {
220                 DEBUG(3,("Attempt to send invalid trans2 request (params %u, data %u)\n",
221                          (unsigned)parms->in.params.length, (unsigned)parms->in.data.length));
222                 return NULL;
223         }
224             
225
226         if (command == SMBtrans)
227                 padding = 1;
228         else
229                 padding = 3;
230         
231         req = smbcli_request_setup(tree, command, wct, padding);
232         if (!req) {
233                 return NULL;
234         }
235
236         /* Watch out, this changes the req->out.* pointers */
237         if (command == SMBtrans && parms->in.trans_name) {
238                 namelen = smbcli_req_append_string(req, parms->in.trans_name, 
239                                                 STR_TERMINATE);
240         }
241
242         /* fill in SMB parameters */
243         outparam = req->out.data + padding;
244         outdata = outparam + parms->in.params.length;
245
246         /* make sure we don't leak data via the padding */
247         memset(req->out.data, 0, padding);
248
249         data_length = parms->in.data.length;
250
251         max_data = smb_raw_max_trans_data(tree, parms->in.params.length);
252         if (max_data < data_length) {
253                 data_length = max_data;
254         }
255
256 #if TORTURE_TRANS_DATA
257         if (data_length > 1) {
258                 data_length /= 2;
259         }
260 #endif
261
262         /* primary request */
263         SSVAL(req->out.vwv,VWV(0),parms->in.params.length);
264         SSVAL(req->out.vwv,VWV(1),parms->in.data.length);
265         SSVAL(req->out.vwv,VWV(2),parms->in.max_param);
266         SSVAL(req->out.vwv,VWV(3),parms->in.max_data);
267         SSVAL(req->out.vwv,VWV(4),parms->in.max_setup);
268         SSVAL(req->out.vwv,VWV(5),parms->in.flags);
269         SIVAL(req->out.vwv,VWV(6),parms->in.timeout);
270         SSVAL(req->out.vwv,VWV(8),0); /* reserved */
271         SSVAL(req->out.vwv,VWV(9),parms->in.params.length);
272         SSVAL(req->out.vwv,VWV(10),PTR_DIFF(outparam,req->out.hdr)+namelen);
273         SSVAL(req->out.vwv,VWV(11),data_length);
274         SSVAL(req->out.vwv,VWV(12),PTR_DIFF(outdata,req->out.hdr)+namelen);
275         SSVAL(req->out.vwv,VWV(13),parms->in.setup_count);
276         for (i=0;i<parms->in.setup_count;i++)   {
277                 SSVAL(req->out.vwv,VWV(14)+i*2,parms->in.setup[i]);
278         }
279         if (parms->in.params.data)      {
280                 smbcli_req_append_blob(req, &parms->in.params);
281         }
282         if (parms->in.data.data) {
283                 DATA_BLOB data;
284                 data.data = parms->in.data.data;
285                 data.length = data_length;
286                 smbcli_req_append_blob(req, &data);
287         }
288
289         if (!smbcli_request_send(req)) {
290                 smbcli_request_destroy(req);
291                 return NULL;
292         }
293
294         data_disp = data_length;
295
296
297         if (data_disp != parms->in.data.length) {
298                 /* TODO: this should be done asynchronously .... */
299                 if (!smbcli_request_receive(req) ||
300                     !NT_STATUS_IS_OK(req->status)) {
301                         return req;
302                 }
303
304                 req->state = SMBCLI_REQUEST_RECV;
305                 DLIST_ADD(req->transport->pending_recv, req);
306         }
307
308
309         while (data_disp != parms->in.data.length) {
310                 data_length = parms->in.data.length - data_disp;
311
312                 max_data = smb_raw_max_trans_data(tree, 0);
313                 if (max_data < data_length) {
314                         data_length = max_data;
315                 }
316
317 #if TORTURE_TRANS_DATA
318                 if (data_length > 1) {
319                         data_length /= 2;
320                 }
321 #endif
322
323                 req2 = smbcli_request_setup(tree, command+1, 9, data_length);
324                 if (!req2) {
325                         return NULL;
326                 }
327                 req2->mid = req->mid;
328                 SSVAL(req2->out.hdr, HDR_MID, req2->mid);
329
330                 outdata = req2->out.data;
331
332                 SSVAL(req2->out.vwv,VWV(0), parms->in.params.length);
333                 SSVAL(req2->out.vwv,VWV(1), parms->in.data.length);
334                 SSVAL(req2->out.vwv,VWV(2), 0);
335                 SSVAL(req2->out.vwv,VWV(3), 0);
336                 SSVAL(req2->out.vwv,VWV(4), 0);
337                 SSVAL(req2->out.vwv,VWV(5), data_length);
338                 SSVAL(req2->out.vwv,VWV(6), PTR_DIFF(outdata,req2->out.hdr));
339                 SSVAL(req2->out.vwv,VWV(7), data_disp);
340                 SSVAL(req2->out.vwv,VWV(8), 0xFFFF);
341
342                 if (data_length != 0) {
343                         memcpy(req2->out.data, parms->in.data.data + data_disp, 
344                                data_length);
345                 }
346                 
347                 data_disp += data_length;
348
349                 req2->one_way_request = 1;
350
351                 if (!smbcli_request_send(req2)) {
352                         smbcli_request_destroy(req2);
353                         return NULL;
354                 }
355
356                 req->seq_num = req2->seq_num;
357         }
358         
359         
360         return req;
361 }
362
363
364 /*
365   trans/trans2 raw async interface - only BLOBs used in this interface.
366   note that this doesn't yet support multi-part requests
367 */
368 struct smbcli_request *smb_raw_trans_send(struct smbcli_tree *tree,
369                                        struct smb_trans2 *parms)
370 {
371         return smb_raw_trans_send_backend(tree, parms, SMBtrans);
372 }
373
374 struct smbcli_request *smb_raw_trans2_send(struct smbcli_tree *tree,
375                                        struct smb_trans2 *parms)
376 {
377         return smb_raw_trans_send_backend(tree, parms, SMBtrans2);
378 }
379
380 /*
381   trans2 synchronous blob interface
382 */
383 NTSTATUS smb_raw_trans2(struct smbcli_tree *tree,
384                         TALLOC_CTX *mem_ctx,
385                         struct smb_trans2 *parms)
386 {
387         struct smbcli_request *req;
388         req = smb_raw_trans2_send(tree, parms);
389         if (!req) return NT_STATUS_UNSUCCESSFUL;
390         return smb_raw_trans2_recv(req, mem_ctx, parms);
391 }
392
393
394 /*
395   trans synchronous blob interface
396 */
397 NTSTATUS smb_raw_trans(struct smbcli_tree *tree,
398                        TALLOC_CTX *mem_ctx,
399                        struct smb_trans2 *parms)
400 {
401         struct smbcli_request *req;
402         req = smb_raw_trans_send(tree, parms);
403         if (!req) return NT_STATUS_UNSUCCESSFUL;
404         return smb_raw_trans_recv(req, mem_ctx, parms);
405 }
406
407
408 /****************************************************************************
409   receive a SMB nttrans response allocating the necessary memory
410   ****************************************************************************/
411 NTSTATUS smb_raw_nttrans_recv(struct smbcli_request *req,
412                               TALLOC_CTX *mem_ctx,
413                               struct smb_nttrans *parms)
414 {
415         uint32_t total_data, recvd_data=0;
416         uint32_t total_param, recvd_param=0;
417
418         if (!smbcli_request_receive(req) ||
419             smbcli_request_is_error(req)) {
420                 return smbcli_request_destroy(req);
421         }
422
423         /* sanity check */
424         if (CVAL(req->in.hdr, HDR_COM) != SMBnttrans) {
425                 DEBUG(0,("smb_raw_receive_nttrans: Expected %s response, got command 0x%02x\n",
426                          "SMBnttrans", 
427                          CVAL(req->in.hdr,HDR_COM)));
428                 req->status = NT_STATUS_UNSUCCESSFUL;
429                 return smbcli_request_destroy(req);
430         }
431
432         SMBCLI_CHECK_MIN_WCT(req, 18);
433
434         /* parse out the lengths */
435         total_param = IVAL(req->in.vwv, 3);
436         total_data  = IVAL(req->in.vwv, 7);
437
438         parms->out.data = data_blob_talloc(mem_ctx, NULL, total_data);
439         parms->out.params = data_blob_talloc(mem_ctx, NULL, total_param);
440
441         if (parms->out.data.length != total_data ||
442             parms->out.params.length != total_param) {
443                 req->status = NT_STATUS_NO_MEMORY;
444                 return smbcli_request_destroy(req);
445         }
446
447         parms->out.setup_count = CVAL(req->in.vwv, 35);
448         SMBCLI_CHECK_WCT(req, 18 + parms->out.setup_count);
449
450         if (parms->out.setup_count > 0) {
451                 parms->out.setup = talloc_array(mem_ctx, uint8_t, 
452                                                 parms->out.setup_count*2);
453                 if (!parms->out.setup) {
454                         req->status = NT_STATUS_NO_MEMORY;
455                         return smbcli_request_destroy(req);
456                 }
457                 memcpy(parms->out.setup, VWV(18) + (uint8_t *)req->out.vwv,
458                        sizeof(uint16_t) * parms->out.setup_count);
459         }
460         
461         while (recvd_data < total_data || 
462                recvd_param < total_param)  {
463                 uint32_t param_count, param_ofs, param_disp;
464                 uint32_t data_count, data_ofs, data_disp;
465                 uint32_t total_data2, total_param2;
466
467                 /* parse out the total lengths again - they can shrink! */
468                 total_param2 = IVAL(req->in.vwv, 3);
469                 total_data2  = IVAL(req->in.vwv, 7);
470
471                 if (total_data2 > total_data ||
472                     total_param2 > total_param) {
473                         /* they must *only* shrink */
474                         DEBUG(1,("smb_raw_receive_nttrans: data/params expanded!\n"));
475                         req->status = NT_STATUS_BUFFER_TOO_SMALL;
476                         return smbcli_request_destroy(req);
477                 }
478
479                 total_data = total_data2;
480                 total_param = total_param2;
481                 parms->out.data.length = total_data;
482                 parms->out.params.length = total_param;
483
484                 /* parse params for this lump */
485                 param_count = IVAL(req->in.vwv, 11);
486                 param_ofs   = IVAL(req->in.vwv, 15);
487                 param_disp  = IVAL(req->in.vwv, 19);
488
489                 data_count = IVAL(req->in.vwv, 23);
490                 data_ofs   = IVAL(req->in.vwv, 27);
491                 data_disp  = IVAL(req->in.vwv, 31);
492
493                 if (data_count + data_disp > total_data ||
494                     param_count + param_disp > total_param) {
495                         DEBUG(1,("smb_raw_receive_nttrans: Buffer overflow\n"));
496                         req->status = NT_STATUS_BUFFER_TOO_SMALL;
497                         return smbcli_request_destroy(req);
498                 }
499                 
500                 /* check the server isn't being nasty */
501                 if (raw_trans_oob(req, param_ofs, param_count) ||
502                     raw_trans_oob(req, data_ofs, data_count)) {
503                         DEBUG(1,("smb_raw_receive_nttrans: out of bounds parameters!\n"));
504                         req->status = NT_STATUS_BUFFER_TOO_SMALL;
505                         return smbcli_request_destroy(req);
506                 }
507
508                 if (data_count) {
509                         memcpy(parms->out.data.data + data_disp,
510                                req->in.hdr + data_ofs, 
511                                data_count);
512                 }
513
514                 if (param_count) {
515                         memcpy(parms->out.params.data + param_disp,
516                                req->in.hdr + param_ofs, 
517                                param_count);
518                 }
519
520                 recvd_param += param_count;
521                 recvd_data += data_count;
522
523                 if (recvd_data >= total_data &&
524                     recvd_param >= total_param) {
525                         break;
526                 }
527                 
528                 if (!smbcli_request_receive(req) ||
529                     smbcli_request_is_error(req)) {
530                         return smbcli_request_destroy(req);
531                 }
532                 
533                 /* sanity check */
534                 if (CVAL(req->in.hdr, HDR_COM) != SMBnttrans) {
535                         DEBUG(0,("smb_raw_receive_nttrans: Expected nttranss, got command 0x%02x\n",
536                                  CVAL(req->in.hdr, HDR_COM)));
537                         req->status = NT_STATUS_UNSUCCESSFUL;
538                         return smbcli_request_destroy(req);
539                 }
540         }
541
542 failed:
543         return smbcli_request_destroy(req);
544 }
545
546
547 /****************************************************************************
548  nttrans raw - only BLOBs used in this interface.
549  at the moment we only handle a single primary request 
550 ****************************************************************************/
551 struct smbcli_request *smb_raw_nttrans_send(struct smbcli_tree *tree,
552                                          struct smb_nttrans *parms)
553 {
554         struct smbcli_request *req; 
555         uint8_t *outdata, *outparam;
556         int align = 0;
557
558         /* only align if there are parameters or data */
559         if (parms->in.params.length || parms->in.data.length) {
560                 align = 3;
561         }
562         
563         req = smbcli_request_setup(tree, SMBnttrans, 
564                                 19 + parms->in.setup_count, 
565                                 align +
566                                 parms->in.params.length +
567                                 parms->in.data.length);
568         if (!req) {
569                 return NULL;
570         }
571         
572         /* fill in SMB parameters */
573         outparam = req->out.data + align;
574         outdata = outparam + parms->in.params.length;
575
576         if (align != 0) {
577                 memset(req->out.data, 0, align);
578         }
579
580         SCVAL(req->out.vwv,  0, parms->in.max_setup);
581         SSVAL(req->out.vwv,  1, 0); /* reserved */
582         SIVAL(req->out.vwv,  3, parms->in.params.length);
583         SIVAL(req->out.vwv,  7, parms->in.data.length);
584         SIVAL(req->out.vwv, 11, parms->in.max_param);
585         SIVAL(req->out.vwv, 15, parms->in.max_data);
586         SIVAL(req->out.vwv, 19, parms->in.params.length);
587         SIVAL(req->out.vwv, 23, PTR_DIFF(outparam,req->out.hdr));
588         SIVAL(req->out.vwv, 27, parms->in.data.length);
589         SIVAL(req->out.vwv, 31, PTR_DIFF(outdata,req->out.hdr));
590         SCVAL(req->out.vwv, 35, parms->in.setup_count);
591         SSVAL(req->out.vwv, 36, parms->in.function);
592         memcpy(req->out.vwv + VWV(19), parms->in.setup,
593                sizeof(uint16_t) * parms->in.setup_count);
594         if (parms->in.params.length) {
595                 memcpy(outparam, parms->in.params.data, parms->in.params.length);
596         }
597         if (parms->in.data.length) {
598                 memcpy(outdata, parms->in.data.data, parms->in.data.length);
599         }
600
601         if (!smbcli_request_send(req)) {
602                 smbcli_request_destroy(req);
603                 return NULL;
604         }
605
606         return req;
607 }
608
609
610 /****************************************************************************
611   receive a SMB nttrans response allocating the necessary memory
612   ****************************************************************************/
613 NTSTATUS smb_raw_nttrans(struct smbcli_tree *tree,
614                          TALLOC_CTX *mem_ctx,
615                          struct smb_nttrans *parms)
616 {
617         struct smbcli_request *req;
618
619         req = smb_raw_nttrans_send(tree, parms);
620         if (!req) {
621                 return NT_STATUS_UNSUCCESSFUL;
622         }
623
624         return smb_raw_nttrans_recv(req, mem_ctx, parms);
625 }
626
627 /*
628   work out the maximum data size for a trans request while avoiding 
629   multi-part replies
630
631   TODO: we only need to avoid multi-part replies because the
632   multi-part trans receive code is broken.
633 */
634 size_t smb_raw_max_trans_data(struct smbcli_tree *tree, size_t param_size)
635 {
636         return tree->session->transport->negotiate.max_xmit - (70 + param_size);
637 }