r15191: Avoid uint_t as it's not standard.
[kai/samba.git] / source4 / libcli / smb2 / request.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    SMB2 client request handling
5
6    Copyright (C) Andrew Tridgell        2005
7    Copyright (C) Stefan Metzmacher      2005
8    
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24 #include "includes.h"
25 #include "libcli/raw/libcliraw.h"
26 #include "libcli/smb2/smb2.h"
27 #include "include/dlinklist.h"
28 #include "lib/events/events.h"
29
30 /*
31   initialise a smb2 request
32 */
33 struct smb2_request *smb2_request_init(struct smb2_transport *transport, uint16_t opcode,
34                                        uint16_t body_fixed_size, uint32_t body_dynamic_size)
35 {
36         struct smb2_request *req;
37
38         req = talloc(transport, struct smb2_request);
39         if (req == NULL) return NULL;
40
41         req->state     = SMB2_REQUEST_INIT;
42         req->transport = transport;
43         req->session   = NULL;
44         req->tree      = NULL;
45         req->seqnum    = transport->seqnum++;
46         req->status    = NT_STATUS_OK;
47         req->async.fn  = NULL;
48         req->next = req->prev = NULL;
49
50         ZERO_STRUCT(req->in);
51         
52         req->out.size      = SMB2_HDR_BODY+NBT_HDR_SIZE+body_fixed_size;
53
54         req->out.allocated = req->out.size + body_dynamic_size;
55         req->out.buffer    = talloc_size(req, req->out.allocated);
56         if (req->out.buffer == NULL) {
57                 talloc_free(req);
58                 return NULL;
59         }
60
61         req->out.hdr       = req->out.buffer + NBT_HDR_SIZE;
62         req->out.body      = req->out.hdr + SMB2_HDR_BODY;
63         req->out.body_size = body_fixed_size;
64         req->out.dynamic   = (body_dynamic_size ? req->out.body + body_fixed_size : NULL);
65
66         SIVAL(req->out.hdr, 0,                SMB2_MAGIC);
67         SSVAL(req->out.hdr, SMB2_HDR_LENGTH,  SMB2_HDR_BODY);
68         SSVAL(req->out.hdr, SMB2_HDR_PAD1,    0);
69         SIVAL(req->out.hdr, SMB2_HDR_STATUS,  0);
70         SSVAL(req->out.hdr, SMB2_HDR_OPCODE,  opcode);
71         SSVAL(req->out.hdr, SMB2_HDR_PAD2,    0);
72         SIVAL(req->out.hdr, SMB2_HDR_FLAGS,   0);
73         SIVAL(req->out.hdr, SMB2_HDR_UNKNOWN, 0);
74         SBVAL(req->out.hdr, SMB2_HDR_SEQNUM,  req->seqnum);
75         SIVAL(req->out.hdr, SMB2_HDR_PID,     0);
76         SIVAL(req->out.hdr, SMB2_HDR_TID,     0);
77         SBVAL(req->out.hdr, SMB2_HDR_UID,     0);
78         memset(req->out.hdr+SMB2_HDR_SIG, 0, 16);
79
80         /* set the length of the fixed body part and +1 if there's a dynamic part also */
81         SSVAL(req->out.body, 0, body_fixed_size + (body_dynamic_size?1:0));
82
83         /* 
84          * if we have a dynamic part, make sure the first byte
85          * which is always be part of the packet is initialized
86          */
87         if (body_dynamic_size) {
88                 SCVAL(req->out.dynamic, 0, 0);
89         }
90
91         return req;
92 }
93
94 /*
95     initialise a smb2 request for tree operations
96 */
97 struct smb2_request *smb2_request_init_tree(struct smb2_tree *tree, uint16_t opcode,
98                                             uint16_t body_fixed_size, uint32_t body_dynamic_size)
99 {
100         struct smb2_request *req = smb2_request_init(tree->session->transport, opcode, 
101                                                      body_fixed_size, body_dynamic_size);
102         if (req == NULL) return NULL;
103
104         SBVAL(req->out.hdr,  SMB2_HDR_UID, tree->session->uid);
105         SIVAL(req->out.hdr,  SMB2_HDR_TID, tree->tid);
106         req->session = tree->session;
107         req->tree = tree;
108
109         return req;     
110 }
111
112 /* destroy a request structure and return final status */
113 NTSTATUS smb2_request_destroy(struct smb2_request *req)
114 {
115         NTSTATUS status;
116
117         /* this is the error code we give the application for when a
118            _send() call fails completely */
119         if (!req) return NT_STATUS_UNSUCCESSFUL;
120
121         if (req->transport) {
122                 /* remove it from the list of pending requests (a null op if
123                    its not in the list) */
124                 DLIST_REMOVE(req->transport->pending_recv, req);
125         }
126
127         if (req->state == SMB2_REQUEST_ERROR &&
128             NT_STATUS_IS_OK(req->status)) {
129                 req->status = NT_STATUS_INTERNAL_ERROR;
130         }
131
132         status = req->status;
133         talloc_free(req);
134         return status;
135 }
136
137 /*
138   receive a response to a packet
139 */
140 BOOL smb2_request_receive(struct smb2_request *req)
141 {
142         /* req can be NULL when a send has failed. This eliminates lots of NULL
143            checks in each module */
144         if (!req) return False;
145
146         /* keep receiving packets until this one is replied to */
147         while (req->state <= SMB2_REQUEST_RECV) {
148                 if (event_loop_once(req->transport->socket->event.ctx) != 0) {
149                         return False;
150                 }
151         }
152
153         return req->state == SMB2_REQUEST_DONE;
154 }
155
156 /* Return true if the last packet was in error */
157 BOOL smb2_request_is_error(struct smb2_request *req)
158 {
159         return NT_STATUS_IS_ERR(req->status);
160 }
161
162 /* Return true if the last packet was OK */
163 BOOL smb2_request_is_ok(struct smb2_request *req)
164 {
165         return NT_STATUS_IS_OK(req->status);
166 }
167
168 /*
169   check if a range in the reply body is out of bounds
170 */
171 BOOL smb2_oob(struct smb2_request_buffer *buf, const uint8_t *ptr, size_t size)
172 {
173         /* be careful with wraparound! */
174         if (ptr < buf->body ||
175             ptr >= buf->body + buf->body_size ||
176             size > buf->body_size ||
177             ptr + size > buf->body + buf->body_size) {
178                 return True;
179         }
180         return False;
181 }
182
183 size_t smb2_padding_size(uint32_t offset, size_t n)
184 {
185         if ((offset & (n-1)) == 0) return 0;
186         return n - (offset & (n-1));
187 }
188
189 /*
190   grow a SMB2 buffer by the specified amount
191 */
192 static NTSTATUS smb2_grow_buffer(struct smb2_request_buffer *buf, size_t increase)
193 {
194         size_t dynamic_ofs;
195         uint8_t *buffer_ptr;
196         uint32_t newsize = buf->size + increase;
197
198         /* a packet size should be limited a bit */
199         if (newsize >= 0x00FFFFFF) return NT_STATUS_MARSHALL_OVERFLOW;
200
201         if (newsize <= buf->allocated) return NT_STATUS_OK;
202
203         dynamic_ofs = buf->dynamic - buf->buffer;
204
205         buffer_ptr = talloc_realloc_size(buf, buf->buffer, newsize);
206         NT_STATUS_HAVE_NO_MEMORY(buffer_ptr);
207
208         buf->buffer     = buffer_ptr;
209         buf->hdr        = buf->buffer + NBT_HDR_SIZE;
210         buf->body       = buf->hdr    + SMB2_HDR_BODY;
211         buf->dynamic    = buf->buffer + dynamic_ofs;
212         buf->allocated  = newsize;
213
214         return NT_STATUS_OK;
215 }
216
217 /*
218   pull a uint16_t ofs/ uint16_t length/blob triple from a data blob
219   the ptr points to the start of the offset/length pair
220 */
221 NTSTATUS smb2_pull_o16s16_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, uint8_t *ptr, DATA_BLOB *blob)
222 {
223         uint16_t ofs, size;
224         if (smb2_oob(buf, ptr, 4)) {
225                 return NT_STATUS_BUFFER_TOO_SMALL;
226         }
227         ofs  = SVAL(ptr, 0);
228         size = SVAL(ptr, 2);
229         if (ofs == 0 || size == 0) {
230                 *blob = data_blob(NULL, 0);
231                 return NT_STATUS_OK;
232         }
233         if (smb2_oob(buf, buf->hdr + ofs, size)) {
234                 return NT_STATUS_BUFFER_TOO_SMALL;
235         }
236         *blob = data_blob_talloc(mem_ctx, buf->hdr + ofs, size);
237         NT_STATUS_HAVE_NO_MEMORY(blob->data);
238         return NT_STATUS_OK;
239 }
240
241 /*
242   push a uint16_t ofs/ uint16_t length/blob triple into a data blob
243   the ofs points to the start of the offset/length pair, and is relative
244   to the body start
245 */
246 NTSTATUS smb2_push_o16s16_blob(struct smb2_request_buffer *buf, 
247                                uint16_t ofs, DATA_BLOB blob)
248 {
249         NTSTATUS status;
250         size_t offset;
251         size_t padding_length;
252         uint8_t *ptr = buf->body+ofs;
253
254         if (buf->dynamic == NULL) {
255                 return NT_STATUS_INVALID_PARAMETER;
256         }
257
258         /* we have only 16 bit for the size */
259         if (blob.length > 0xFFFF) {
260                 return NT_STATUS_BUFFER_TOO_SMALL;
261         }
262
263         /* check if there're enough room for ofs and size */
264         if (smb2_oob(buf, ptr, 4)) {
265                 return NT_STATUS_BUFFER_TOO_SMALL;
266         }
267
268         if (blob.length == 0) {
269                 SSVAL(ptr, 0, 0);
270                 SSVAL(ptr, 2, 0);
271                 return NT_STATUS_OK;
272         }
273
274         offset = buf->dynamic - buf->hdr;
275         padding_length = smb2_padding_size(offset, 2);
276         offset += padding_length;
277
278         SSVAL(ptr, 0, offset);
279         SSVAL(ptr, 2, blob.length);
280
281         status = smb2_grow_buffer(buf, padding_length + blob.length);
282         NT_STATUS_NOT_OK_RETURN(status);
283
284         memset(buf->dynamic, 0, padding_length);
285         buf->dynamic += padding_length;
286
287         memcpy(buf->dynamic, blob.data, blob.length);
288         buf->dynamic += blob.length;
289
290         buf->size += blob.length + padding_length;
291         buf->body_size += blob.length + padding_length;
292
293         return NT_STATUS_OK;
294 }
295
296
297 /*
298   push a uint16_t ofs/ uint32_t length/blob triple into a data blob
299   the ofs points to the start of the offset/length pair, and is relative
300   to the body start
301 */
302 NTSTATUS smb2_push_o16s32_blob(struct smb2_request_buffer *buf, 
303                                uint16_t ofs, DATA_BLOB blob)
304 {
305         NTSTATUS status;
306         size_t offset;
307         size_t padding_length;
308         uint8_t *ptr = buf->body+ofs;
309
310         if (buf->dynamic == NULL) {
311                 return NT_STATUS_INVALID_PARAMETER;
312         }
313
314         /* check if there're enough room for ofs and size */
315         if (smb2_oob(buf, ptr, 6)) {
316                 return NT_STATUS_BUFFER_TOO_SMALL;
317         }
318
319         if (blob.length == 0) {
320                 SSVAL(ptr, 0, 0);
321                 SIVAL(ptr, 2, 0);
322                 return NT_STATUS_OK;
323         }
324
325         offset = buf->dynamic - buf->hdr;
326         padding_length = smb2_padding_size(offset, 2);
327         offset += padding_length;
328
329         SSVAL(ptr, 0, offset);
330         SIVAL(ptr, 2, blob.length);
331
332         status = smb2_grow_buffer(buf, padding_length + blob.length);
333         NT_STATUS_NOT_OK_RETURN(status);
334
335         memset(buf->dynamic, 0, padding_length);
336         buf->dynamic += padding_length;
337
338         memcpy(buf->dynamic, blob.data, blob.length);
339         buf->dynamic += blob.length;
340
341         buf->size += blob.length + padding_length;
342         buf->body_size += blob.length + padding_length;
343
344         return NT_STATUS_OK;
345 }
346
347
348 /*
349   push a uint32_t ofs/ uint32_t length/blob triple into a data blob
350   the ofs points to the start of the offset/length pair, and is relative
351   to the body start
352 */
353 NTSTATUS smb2_push_o32s32_blob(struct smb2_request_buffer *buf, 
354                                uint32_t ofs, DATA_BLOB blob)
355 {
356         NTSTATUS status;
357         size_t offset;
358         size_t padding_length;
359         uint8_t *ptr = buf->body+ofs;
360
361         if (buf->dynamic == NULL) {
362                 return NT_STATUS_INVALID_PARAMETER;
363         }
364
365         /* check if there're enough room for ofs and size */
366         if (smb2_oob(buf, ptr, 8)) {
367                 return NT_STATUS_BUFFER_TOO_SMALL;
368         }
369
370         if (blob.length == 0) {
371                 SIVAL(ptr, 0, 0);
372                 SIVAL(ptr, 4, 0);
373                 return NT_STATUS_OK;
374         }
375
376         offset = buf->dynamic - buf->hdr;
377         padding_length = smb2_padding_size(offset, 8);
378         offset += padding_length;
379
380         SIVAL(ptr, 0, offset);
381         SIVAL(ptr, 4, blob.length);
382
383         status = smb2_grow_buffer(buf, padding_length + blob.length);
384         NT_STATUS_NOT_OK_RETURN(status);
385
386         memset(buf->dynamic, 0, padding_length);
387         buf->dynamic += padding_length;
388
389         memcpy(buf->dynamic, blob.data, blob.length);
390         buf->dynamic += blob.length;
391
392         buf->size += blob.length + padding_length;
393         buf->body_size += blob.length + padding_length;
394
395         return NT_STATUS_OK;
396 }
397
398
399 /*
400   push a uint32_t length/ uint32_t ofs/blob triple into a data blob
401   the ofs points to the start of the length/offset pair, and is relative
402   to the body start
403 */
404 NTSTATUS smb2_push_s32o32_blob(struct smb2_request_buffer *buf, 
405                                uint32_t ofs, DATA_BLOB blob)
406 {
407         NTSTATUS status;
408         size_t offset;
409         size_t padding_length;
410         uint8_t *ptr = buf->body+ofs;
411
412         if (buf->dynamic == NULL) {
413                 return NT_STATUS_INVALID_PARAMETER;
414         }
415
416         /* check if there're enough room for ofs and size */
417         if (smb2_oob(buf, ptr, 8)) {
418                 return NT_STATUS_BUFFER_TOO_SMALL;
419         }
420
421         if (blob.length == 0) {
422                 SIVAL(ptr, 0, 0);
423                 SIVAL(ptr, 4, 0);
424                 return NT_STATUS_OK;
425         }
426
427         offset = buf->dynamic - buf->hdr;
428         padding_length = smb2_padding_size(offset, 8);
429         offset += padding_length;
430
431         SIVAL(ptr, 0, blob.length);
432         SIVAL(ptr, 4, offset);
433
434         status = smb2_grow_buffer(buf, padding_length + blob.length);
435         NT_STATUS_NOT_OK_RETURN(status);
436
437         memset(buf->dynamic, 0, padding_length);
438         buf->dynamic += padding_length;
439
440         memcpy(buf->dynamic, blob.data, blob.length);
441         buf->dynamic += blob.length;
442
443         buf->size += blob.length + padding_length;
444         buf->body_size += blob.length + padding_length;
445
446         return NT_STATUS_OK;
447 }
448
449 /*
450   pull a uint16_t ofs/ uint32_t length/blob triple from a data blob
451   the ptr points to the start of the offset/length pair
452 */
453 NTSTATUS smb2_pull_o16s32_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, uint8_t *ptr, DATA_BLOB *blob)
454 {
455         uint16_t ofs;
456         uint32_t size;
457
458         if (smb2_oob(buf, ptr, 6)) {
459                 return NT_STATUS_BUFFER_TOO_SMALL;
460         }
461         ofs  = SVAL(ptr, 0);
462         size = IVAL(ptr, 2);
463         if (ofs == 0 || size == 0) {
464                 *blob = data_blob(NULL, 0);
465                 return NT_STATUS_OK;
466         }
467         if (smb2_oob(buf, buf->hdr + ofs, size)) {
468                 return NT_STATUS_BUFFER_TOO_SMALL;
469         }
470         *blob = data_blob_talloc(mem_ctx, buf->hdr + ofs, size);
471         NT_STATUS_HAVE_NO_MEMORY(blob->data);
472         return NT_STATUS_OK;
473 }
474
475 /*
476   pull a uint32_t ofs/ uint32_t length/blob triple from a data blob
477   the ptr points to the start of the offset/length pair
478 */
479 NTSTATUS smb2_pull_o32s32_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, uint8_t *ptr, DATA_BLOB *blob)
480 {
481         uint32_t ofs, size;
482         if (smb2_oob(buf, ptr, 8)) {
483                 return NT_STATUS_BUFFER_TOO_SMALL;
484         }
485         ofs  = IVAL(ptr, 0);
486         size = IVAL(ptr, 4);
487         if (ofs == 0 || size == 0) {
488                 *blob = data_blob(NULL, 0);
489                 return NT_STATUS_OK;
490         }
491         if (smb2_oob(buf, buf->hdr + ofs, size)) {
492                 return NT_STATUS_BUFFER_TOO_SMALL;
493         }
494         *blob = data_blob_talloc(mem_ctx, buf->hdr + ofs, size);
495         NT_STATUS_HAVE_NO_MEMORY(blob->data);
496         return NT_STATUS_OK;
497 }
498
499 /*
500   pull a string in a uint16_t ofs/ uint16_t length/blob format
501   UTF-16 without termination
502 */
503 NTSTATUS smb2_pull_o16s16_string(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx,
504                                  uint8_t *ptr, const char **str)
505 {
506         DATA_BLOB blob;
507         NTSTATUS status;
508         ssize_t size;
509         void *vstr;
510
511         status = smb2_pull_o16s16_blob(buf, mem_ctx, ptr, &blob);
512         NT_STATUS_NOT_OK_RETURN(status);
513
514         size = convert_string_talloc(mem_ctx, CH_UTF16, CH_UNIX, 
515                                      blob.data, blob.length, &vstr);
516         data_blob_free(&blob);
517         (*str) = vstr;
518         if (size == -1) {
519                 return NT_STATUS_ILLEGAL_CHARACTER;
520         }
521         return NT_STATUS_OK;
522 }
523
524 /*
525   push a string in a uint16_t ofs/ uint16_t length/blob format
526   UTF-16 without termination
527 */
528 NTSTATUS smb2_push_o16s16_string(struct smb2_request_buffer *buf,
529                                  uint16_t ofs, const char *str)
530 {
531         DATA_BLOB blob;
532         NTSTATUS status;
533         ssize_t size;
534
535         if (strcmp("", str) == 0) {
536                 return smb2_push_o16s16_blob(buf, ofs, data_blob(NULL, 0));
537         }
538
539         size = convert_string_talloc(buf->buffer, CH_UNIX, CH_UTF16, 
540                                      str, strlen(str), (void **)&blob.data);
541         if (size == -1) {
542                 return NT_STATUS_ILLEGAL_CHARACTER;
543         }
544         blob.length = size;
545
546         status = smb2_push_o16s16_blob(buf, ofs, blob);
547         data_blob_free(&blob);
548         return status;
549 }
550
551 /*
552   push a file handle into a buffer
553 */
554 void smb2_push_handle(uint8_t *data, struct smb2_handle *h)
555 {
556         SBVAL(data, 0, h->data[0]);
557         SBVAL(data, 8, h->data[1]);
558 }
559
560 /*
561   pull a file handle from a buffer
562 */
563 void smb2_pull_handle(uint8_t *ptr, struct smb2_handle *h)
564 {
565         h->data[0] = BVAL(ptr, 0);
566         h->data[1] = BVAL(ptr, 8);
567 }