18cc490be4804e9c1770aab51a53aca59a2de95f
[metze/samba/wip.git] / source4 / libcli / smb2 / transport.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    SMB2 client transport context management functions
5
6    Copyright (C) Andrew Tridgell 2005
7    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "includes.h"
23 #include "system/network.h"
24 #include "libcli/raw/libcliraw.h"
25 #include "libcli/raw/raw_proto.h"
26 #include "libcli/smb2/smb2.h"
27 #include "libcli/smb2/smb2_calls.h"
28 #include "lib/socket/socket.h"
29 #include "lib/events/events.h"
30 #include "../lib/util/dlinklist.h"
31 #include "../libcli/smb/smbXcli_base.h"
32 #include "librpc/ndr/libndr.h"
33
34 /*
35   destroy a transport
36  */
37 static int transport_destructor(struct smb2_transport *transport)
38 {
39         smb2_transport_dead(transport, NT_STATUS_LOCAL_DISCONNECT);
40         return 0;
41 }
42
43 /*
44   create a transport structure based on an established socket
45 */
46 struct smb2_transport *smb2_transport_init(struct smbcli_socket *sock,
47                                            TALLOC_CTX *parent_ctx,
48                                            struct smbcli_options *options)
49 {
50         struct smb2_transport *transport;
51
52         transport = talloc_zero(parent_ctx, struct smb2_transport);
53         if (!transport) return NULL;
54
55         transport->ev = sock->event.ctx;
56         transport->options = *options;
57
58         if (transport->options.max_protocol == PROTOCOL_DEFAULT) {
59                 transport->options.max_protocol = PROTOCOL_LATEST;
60         }
61
62         if (transport->options.max_protocol < PROTOCOL_SMB2_02) {
63                 transport->options.max_protocol = PROTOCOL_LATEST;
64         }
65
66         TALLOC_FREE(sock->event.fde);
67         TALLOC_FREE(sock->event.te);
68
69         transport->conn = smbXcli_conn_create(transport,
70                                               sock->sock->fd,
71                                               sock->hostname,
72                                               options->signing,
73                                               0, /* smb1_capabilities */
74                                               &options->client_guid,
75                                               options->smb2_capabilities);
76         if (transport->conn == NULL) {
77                 talloc_free(transport);
78                 return NULL;
79         }
80         sock->sock->fd = -1;
81         TALLOC_FREE(sock);
82
83         talloc_set_destructor(transport, transport_destructor);
84
85         return transport;
86 }
87
88 /*
89   create a transport structure based on an established socket
90 */
91 NTSTATUS smb2_transport_raw_init(TALLOC_CTX *mem_ctx,
92                                  struct tevent_context *ev,
93                                  struct smbXcli_conn **_conn,
94                                  const struct smbcli_options *options,
95                                  struct smb2_transport **_transport)
96 {
97         struct smb2_transport *transport = NULL;
98         enum protocol_types protocol;
99
100         if (*_conn == NULL) {
101                 return NT_STATUS_INVALID_PARAMETER;
102         }
103
104         protocol = smbXcli_conn_protocol(*_conn);
105         if (protocol < PROTOCOL_SMB2_02) {
106                 return NT_STATUS_REVISION_MISMATCH;
107         }
108
109         transport = talloc_zero(mem_ctx, struct smb2_transport);
110         if (transport == NULL) {
111                 return NT_STATUS_NO_MEMORY;
112         }
113
114         transport->ev = ev;
115         transport->options = *options;
116         transport->conn = talloc_move(transport, _conn);
117
118         talloc_set_destructor(transport, transport_destructor);
119         *_transport = transport;
120         return NT_STATUS_OK;
121 }
122
123 /*
124   mark the transport as dead
125 */
126 void smb2_transport_dead(struct smb2_transport *transport, NTSTATUS status)
127 {
128         if (NT_STATUS_EQUAL(NT_STATUS_UNSUCCESSFUL, status)) {
129                 status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
130         }
131         if (NT_STATUS_IS_OK(status)) {
132                 status = NT_STATUS_LOCAL_DISCONNECT;
133         }
134
135         smbXcli_conn_disconnect(transport->conn, status);
136 }
137
138 static void smb2_request_done(struct tevent_req *subreq);
139 static void smb2_transport_break_handler(struct tevent_req *subreq);
140
141 /*
142   put a request into the send queue
143 */
144 void smb2_transport_send(struct smb2_request *req)
145 {
146         NTSTATUS status;
147         struct smb2_transport *transport = req->transport;
148         struct tevent_req **reqs = transport->compound.reqs;
149         size_t num_reqs = talloc_array_length(reqs);
150         size_t i;
151         uint16_t cmd = SVAL(req->out.hdr, SMB2_HDR_OPCODE);
152         uint32_t additional_flags = IVAL(req->out.hdr, SMB2_HDR_FLAGS);
153         uint32_t clear_flags = 0;
154         struct smbXcli_tcon *tcon = NULL;
155         struct smbXcli_session *session = NULL;
156         bool need_pending_break = false;
157         size_t hdr_ofs;
158         size_t pdu_len;
159         DATA_BLOB body = data_blob_null;
160         DATA_BLOB dyn = data_blob_null;
161         uint32_t timeout_msec = transport->options.request_timeout * 1000;
162
163         if (transport->oplock.handler) {
164                 need_pending_break = true;
165         }
166
167         if (transport->lease.handler) {
168                 need_pending_break = true;
169         }
170
171         if (transport->break_subreq) {
172                 need_pending_break = false;
173         }
174
175         if (need_pending_break) {
176                 struct tevent_req *subreq;
177
178                 subreq = smb2cli_req_create(transport,
179                                             transport->ev,
180                                             transport->conn,
181                                             SMB2_OP_BREAK,
182                                             0, /* additional_flags */
183                                             0, /*clear_flags */
184                                             0, /* timeout_msec */
185                                             NULL, /* tcon */
186                                             NULL, /* session */
187                                             NULL, /* body */
188                                             0, /* body_fixed */
189                                             NULL, /* dyn */
190                                             0, /* dyn_len */
191                                             0); /* max_dyn_len */
192                 if (subreq != NULL) {
193                         smbXcli_req_set_pending(subreq);
194                         tevent_req_set_callback(subreq,
195                                                 smb2_transport_break_handler,
196                                                 transport);
197                         transport->break_subreq = subreq;
198                 }
199         }
200
201         if (req->session) {
202                 session = req->session->smbXcli;
203         }
204
205         if (req->tree) {
206                 tcon = req->tree->smbXcli;
207         }
208
209         if (transport->compound.related) {
210                 additional_flags |= SMB2_HDR_FLAG_CHAINED;
211         }
212
213         hdr_ofs = PTR_DIFF(req->out.hdr, req->out.buffer);
214         pdu_len = req->out.size - hdr_ofs;
215         body.data = req->out.body;
216         body.length = req->out.body_fixed;
217         dyn.data = req->out.body + req->out.body_fixed;
218         dyn.length = pdu_len - (SMB2_HDR_BODY + req->out.body_fixed);
219
220         req->subreq = smb2cli_req_create(req,
221                                          transport->ev,
222                                          transport->conn,
223                                          cmd,
224                                          additional_flags,
225                                          clear_flags,
226                                          timeout_msec,
227                                          tcon,
228                                          session,
229                                          body.data, body.length,
230                                          dyn.data, dyn.length,
231                                          0); /* max_dyn_len */
232         if (req->subreq == NULL) {
233                 req->state = SMB2_REQUEST_ERROR;
234                 req->status = NT_STATUS_NO_MEMORY;
235                 return;
236         }
237
238         if (!tevent_req_is_in_progress(req->subreq)) {
239                 req->state = SMB2_REQUEST_ERROR;
240                 req->status = NT_STATUS_INTERNAL_ERROR;/* TODO */
241                 return;
242         }
243
244         tevent_req_set_callback(req->subreq, smb2_request_done, req);
245
246         smb2cli_req_set_notify_async(req->subreq);
247         if (req->credit_charge) {
248                 smb2cli_req_set_credit_charge(req->subreq, req->credit_charge);
249         }
250
251         ZERO_STRUCT(req->out);
252         req->state = SMB2_REQUEST_RECV;
253
254         if (num_reqs > 0) {
255                 for (i=0; i < num_reqs; i++) {
256                         if (reqs[i] != NULL) {
257                                 continue;
258                         }
259
260                         reqs[i] = req->subreq;
261                         i++;
262                         break;
263                 }
264
265                 if (i < num_reqs) {
266                         return;
267                 }
268         } else {
269                 reqs = &req->subreq;
270                 num_reqs = 1;
271         }
272         status = smb2cli_req_compound_submit(reqs, num_reqs);
273
274         TALLOC_FREE(transport->compound.reqs);
275         transport->compound.related = false;
276
277         if (!NT_STATUS_IS_OK(status)) {
278                 req->status = status;
279                 req->state = SMB2_REQUEST_ERROR;
280                 smbXcli_conn_disconnect(transport->conn, status);
281         }
282 }
283
284 static void smb2_request_done(struct tevent_req *subreq)
285 {
286         struct smb2_request *req =
287                 tevent_req_callback_data(subreq,
288                 struct smb2_request);
289         ssize_t len;
290         size_t i;
291
292         req->recv_iov = NULL;
293
294         req->status = smb2cli_req_recv(req->subreq, req, &req->recv_iov, NULL, 0);
295         if (NT_STATUS_EQUAL(req->status, STATUS_PENDING)) {
296                 struct timeval endtime = smbXcli_req_endtime(subreq);
297                 bool ok;
298
299                 req->cancel.can_cancel = true;
300                 if (timeval_is_zero(&endtime)) {
301                         return;
302                 }
303
304                 ok = tevent_req_set_endtime(
305                         subreq, req->transport->ev, endtime);
306                 if (!ok) {
307                         req->status = NT_STATUS_INTERNAL_ERROR;
308                         req->state = SMB2_REQUEST_ERROR;
309                         if (req->async.fn) {
310                                 req->async.fn(req);
311                         }
312                         return;
313                 }
314                 return;
315         }
316         TALLOC_FREE(req->subreq);
317         if (!NT_STATUS_IS_OK(req->status)) {
318                 if (req->recv_iov == NULL) {
319                         req->state = SMB2_REQUEST_ERROR;
320                         if (req->async.fn) {
321                                 req->async.fn(req);
322                         }
323                         return;
324                 }
325         }
326
327         len = req->recv_iov[0].iov_len;
328         for (i=1; i < 3; i++) {
329                 uint8_t *p = req->recv_iov[i-1].iov_base;
330                 uint8_t *c1 = req->recv_iov[i].iov_base;
331                 uint8_t *c2 = p + req->recv_iov[i-1].iov_len;
332
333                 len += req->recv_iov[i].iov_len;
334
335                 if (req->recv_iov[i].iov_len == 0) {
336                         continue;
337                 }
338
339                 if (c1 != c2) {
340                         req->status = NT_STATUS_INTERNAL_ERROR;
341                         req->state = SMB2_REQUEST_ERROR;
342                         if (req->async.fn) {
343                                 req->async.fn(req);
344                         }
345                         return;
346                 }
347         }
348
349         req->in.buffer = req->recv_iov[0].iov_base;
350         req->in.size = len;
351         req->in.allocated = req->in.size;
352
353         req->in.hdr        =  req->recv_iov[0].iov_base;
354         req->in.body       =  req->recv_iov[1].iov_base;
355         req->in.dynamic    =  req->recv_iov[2].iov_base;
356         req->in.body_fixed =  req->recv_iov[1].iov_len;
357         req->in.body_size  =  req->in.body_fixed;
358         req->in.body_size  += req->recv_iov[2].iov_len;
359
360         smb2_setup_bufinfo(req);
361
362         req->state = SMB2_REQUEST_DONE;
363         if (req->async.fn) {
364                 req->async.fn(req);
365         }
366 }
367
368 static void smb2_transport_break_handler(struct tevent_req *subreq)
369 {
370         struct smb2_transport *transport =
371                 tevent_req_callback_data(subreq,
372                 struct smb2_transport);
373         NTSTATUS status;
374         uint8_t *body;
375         uint16_t len = 0;
376         bool lease;
377         struct iovec *recv_iov = NULL;
378
379         transport->break_subreq = NULL;
380
381         status = smb2cli_req_recv(subreq, transport, &recv_iov, NULL, 0);
382         TALLOC_FREE(subreq);
383         if (!NT_STATUS_IS_OK(status)) {
384                 TALLOC_FREE(recv_iov);
385                 smb2_transport_dead(transport, status);
386                 return;
387         }
388
389         /*
390          * Setup the subreq to handle the
391          * next incoming SMB2 Break.
392          */
393         subreq = smb2cli_req_create(transport,
394                                     transport->ev,
395                                     transport->conn,
396                                     SMB2_OP_BREAK,
397                                     0, /* additional_flags */
398                                     0, /*clear_flags */
399                                     0, /* timeout_msec */
400                                     NULL, /* tcon */
401                                     NULL, /* session */
402                                     NULL, /* body */
403                                     0, /* body_fixed */
404                                     NULL, /* dyn */
405                                     0, /* dyn_len */
406                                     0); /* max_dyn_len */
407         if (subreq != NULL) {
408                 smbXcli_req_set_pending(subreq);
409                 tevent_req_set_callback(subreq,
410                                         smb2_transport_break_handler,
411                                         transport);
412                 transport->break_subreq = subreq;
413         }
414
415         body = recv_iov[1].iov_base;
416
417         len = recv_iov[1].iov_len;
418         if (recv_iov[1].iov_len >= 2) {
419                 len = CVAL(body, 0x00);
420                 if (len != recv_iov[1].iov_len) {
421                         len = recv_iov[1].iov_len;
422                 }
423         }
424
425         if (len == 24) {
426                 lease = false;
427         } else if (len == 44) {
428                 lease = true;
429         } else {
430                 DEBUG(1,("Discarding smb2 oplock reply of invalid size %u\n",
431                         (unsigned)len));
432                 TALLOC_FREE(recv_iov);
433                 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
434                 smb2_transport_dead(transport, status);
435                 return;
436         }
437
438         if (!lease && transport->oplock.handler) {
439                 struct smb2_handle h;
440                 uint8_t level;
441
442                 level = CVAL(body, 0x02);
443                 smb2_pull_handle(body+0x08, &h);
444
445                 TALLOC_FREE(recv_iov);
446
447                 transport->oplock.handler(transport, &h, level,
448                                           transport->oplock.private_data);
449         } else if (lease && transport->lease.handler) {
450                 struct smb2_lease_break lb;
451
452                 ZERO_STRUCT(lb);
453                 lb.new_epoch =                  SVAL(body, 0x2);
454                 lb.break_flags =                SVAL(body, 0x4);
455                 memcpy(&lb.current_lease.lease_key, body+0x8,
456                     sizeof(struct smb2_lease_key));
457                 lb.current_lease.lease_state =  SVAL(body, 0x18);
458                 lb.new_lease_state =            SVAL(body, 0x1C);
459                 lb.break_reason =               SVAL(body, 0x20);
460                 lb.access_mask_hint =           SVAL(body, 0x24);
461                 lb.share_mask_hint =            SVAL(body, 0x28);
462
463                 TALLOC_FREE(recv_iov);
464
465                 transport->lease.handler(transport, &lb,
466                     transport->lease.private_data);
467         } else {
468                 DEBUG(5,("Got SMB2 %s break with no handler\n",
469                         lease ? "lease" : "oplock"));
470         }
471         TALLOC_FREE(recv_iov);
472 }
473
474 NTSTATUS smb2_transport_compound_start(struct smb2_transport *transport,
475                                        uint32_t num)
476 {
477         TALLOC_FREE(transport->compound.reqs);
478         ZERO_STRUCT(transport->compound);
479
480         transport->compound.reqs = talloc_zero_array(transport,
481                                                      struct tevent_req *,
482                                                      num);
483         if (transport->compound.reqs == NULL) {
484                 return NT_STATUS_NO_MEMORY;
485         }
486
487         return NT_STATUS_OK;
488 }
489
490 void smb2_transport_compound_set_related(struct smb2_transport *transport,
491                                          bool related)
492 {
493         transport->compound.related = related;
494 }
495
496 void smb2_transport_credits_ask_num(struct smb2_transport *transport,
497                                     uint16_t ask_num)
498 {
499         smb2cli_conn_set_max_credits(transport->conn, ask_num);
500 }
501
502 static void idle_handler(struct tevent_context *ev, 
503                          struct tevent_timer *te, struct timeval t, void *private_data)
504 {
505         struct smb2_transport *transport = talloc_get_type(private_data,
506                                                            struct smb2_transport);
507         struct timeval next;
508
509         transport->idle.func(transport, transport->idle.private_data);
510
511         next = timeval_current_ofs_usec(transport->idle.period);
512         transport->idle.te = tevent_add_timer(transport->ev,
513                                               transport,
514                                               next,
515                                               idle_handler,
516                                               transport);
517 }
518
519 /*
520   setup the idle handler for a transport
521   the period is in microseconds
522 */
523 void smb2_transport_idle_handler(struct smb2_transport *transport, 
524                                  void (*idle_func)(struct smb2_transport *, void *),
525                                  uint64_t period,
526                                  void *private_data)
527 {
528         TALLOC_FREE(transport->idle.te);
529
530         transport->idle.func = idle_func;
531         transport->idle.private_data = private_data;
532         transport->idle.period = period;
533
534         transport->idle.te = tevent_add_timer(transport->ev,
535                                               transport,
536                                               timeval_current_ofs_usec(period),
537                                               idle_handler,
538                                               transport);
539 }