b4a6c943dd19f5b9fdb2ea193f9f3761512ab31c
[mat/samba.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         uint32_t smb2_capabilities = 0;
52
53         transport = talloc_zero(parent_ctx, struct smb2_transport);
54         if (!transport) return NULL;
55
56         transport->ev = sock->event.ctx;
57         transport->options = *options;
58
59         TALLOC_FREE(sock->event.fde);
60         TALLOC_FREE(sock->event.te);
61
62         transport->conn = smbXcli_conn_create(transport,
63                                               sock->sock->fd,
64                                               sock->hostname,
65                                               options->signing,
66                                               0, /* smb1_capabilities */
67                                               &options->client_guid,
68                                               options->smb2_capabilities);
69         if (transport->conn == NULL) {
70                 talloc_free(transport);
71                 return NULL;
72         }
73         sock->sock->fd = -1;
74         TALLOC_FREE(sock);
75
76         talloc_set_destructor(transport, transport_destructor);
77
78         return transport;
79 }
80
81 /*
82   mark the transport as dead
83 */
84 void smb2_transport_dead(struct smb2_transport *transport, NTSTATUS status)
85 {
86         if (NT_STATUS_EQUAL(NT_STATUS_UNSUCCESSFUL, status)) {
87                 status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
88         }
89         if (NT_STATUS_IS_OK(status)) {
90                 status = NT_STATUS_LOCAL_DISCONNECT;
91         }
92
93         smbXcli_conn_disconnect(transport->conn, status);
94 }
95
96 static void smb2_request_done(struct tevent_req *subreq);
97 static void smb2_transport_break_handler(struct tevent_req *subreq);
98
99 /*
100   put a request into the send queue
101 */
102 void smb2_transport_send(struct smb2_request *req)
103 {
104         NTSTATUS status;
105         struct smb2_transport *transport = req->transport;
106         struct tevent_req **reqs = transport->compound.reqs;
107         size_t num_reqs = talloc_array_length(reqs);
108         size_t i;
109         uint16_t cmd = SVAL(req->out.hdr, SMB2_HDR_OPCODE);
110         uint32_t additional_flags = IVAL(req->out.hdr, SMB2_HDR_FLAGS);
111         uint32_t clear_flags = 0;
112         struct smbXcli_tcon *tcon = NULL;
113         struct smbXcli_session *session = NULL;
114         bool need_pending_break = false;
115         size_t hdr_ofs;
116         size_t pdu_len;
117         DATA_BLOB body = data_blob_null;
118         DATA_BLOB dyn = data_blob_null;
119         uint32_t timeout_msec = transport->options.request_timeout * 1000;
120
121         if (transport->oplock.handler) {
122                 need_pending_break = true;
123         }
124
125         if (transport->lease.handler) {
126                 need_pending_break = true;
127         }
128
129         if (transport->break_subreq) {
130                 need_pending_break = false;
131         }
132
133         if (need_pending_break) {
134                 struct tevent_req *subreq;
135
136                 subreq = smb2cli_req_create(transport,
137                                             transport->ev,
138                                             transport->conn,
139                                             SMB2_OP_BREAK,
140                                             0, /* additional_flags */
141                                             0, /*clear_flags */
142                                             0, /* timeout_msec */
143                                             NULL, /* tcon */
144                                             NULL, /* session */
145                                             NULL, /* body */
146                                             0, /* body_fixed */
147                                             NULL, /* dyn */
148                                             0, /* dyn_len */
149                                             0); /* max_dyn_len */
150                 if (subreq != NULL) {
151                         smbXcli_req_set_pending(subreq);
152                         tevent_req_set_callback(subreq,
153                                                 smb2_transport_break_handler,
154                                                 transport);
155                         transport->break_subreq = subreq;
156                 }
157         }
158
159         if (req->session) {
160                 session = req->session->smbXcli;
161         }
162
163         if (req->tree) {
164                 tcon = req->tree->smbXcli;
165         }
166
167         if (transport->compound.related) {
168                 additional_flags |= SMB2_HDR_FLAG_CHAINED;
169         }
170
171         hdr_ofs = PTR_DIFF(req->out.hdr, req->out.buffer);
172         pdu_len = req->out.size - hdr_ofs;
173         body.data = req->out.body;
174         body.length = req->out.body_fixed;
175         dyn.data = req->out.body + req->out.body_fixed;
176         dyn.length = pdu_len - (SMB2_HDR_BODY + req->out.body_fixed);
177
178         req->subreq = smb2cli_req_create(req,
179                                          transport->ev,
180                                          transport->conn,
181                                          cmd,
182                                          additional_flags,
183                                          clear_flags,
184                                          timeout_msec,
185                                          tcon,
186                                          session,
187                                          body.data, body.length,
188                                          dyn.data, dyn.length,
189                                          0); /* max_dyn_len */
190         if (req->subreq == NULL) {
191                 req->state = SMB2_REQUEST_ERROR;
192                 req->status = NT_STATUS_NO_MEMORY;
193                 return;
194         }
195
196         if (!tevent_req_is_in_progress(req->subreq)) {
197                 req->state = SMB2_REQUEST_ERROR;
198                 req->status = NT_STATUS_INTERNAL_ERROR;/* TODO */
199                 return;
200         }
201
202         tevent_req_set_callback(req->subreq, smb2_request_done, req);
203
204         smb2cli_req_set_notify_async(req->subreq);
205         if (req->credit_charge) {
206                 smb2cli_req_set_credit_charge(req->subreq, req->credit_charge);
207         }
208
209         ZERO_STRUCT(req->out);
210         req->state = SMB2_REQUEST_RECV;
211
212         if (num_reqs > 0) {
213                 for (i=0; i < num_reqs; i++) {
214                         if (reqs[i] != NULL) {
215                                 continue;
216                         }
217
218                         reqs[i] = req->subreq;
219                         i++;
220                         break;
221                 }
222
223                 if (i < num_reqs) {
224                         return;
225                 }
226         } else {
227                 reqs = &req->subreq;
228                 num_reqs = 1;
229         }
230         status = smb2cli_req_compound_submit(reqs, num_reqs);
231
232         TALLOC_FREE(transport->compound.reqs);
233         transport->compound.related = false;
234
235         if (!NT_STATUS_IS_OK(status)) {
236                 req->status = status;
237                 req->state = SMB2_REQUEST_ERROR;
238                 smbXcli_conn_disconnect(transport->conn, status);
239         }
240 }
241
242 static void smb2_request_done(struct tevent_req *subreq)
243 {
244         struct smb2_request *req =
245                 tevent_req_callback_data(subreq,
246                 struct smb2_request);
247         ssize_t len;
248         size_t i;
249
250         req->recv_iov = NULL;
251
252         req->status = smb2cli_req_recv(req->subreq, req, &req->recv_iov, NULL, 0);
253         if (NT_STATUS_EQUAL(req->status, STATUS_PENDING)) {
254                 req->cancel.can_cancel = true;
255                 return;
256         }
257         TALLOC_FREE(req->subreq);
258         if (!NT_STATUS_IS_OK(req->status)) {
259                 if (req->recv_iov == NULL) {
260                         req->state = SMB2_REQUEST_ERROR;
261                         if (req->async.fn) {
262                                 req->async.fn(req);
263                         }
264                         return;
265                 }
266         }
267
268         len = req->recv_iov[0].iov_len;
269         for (i=1; i < 3; i++) {
270                 uint8_t *p = req->recv_iov[i-1].iov_base;
271                 uint8_t *c1 = req->recv_iov[i].iov_base;
272                 uint8_t *c2 = p + req->recv_iov[i-1].iov_len;
273
274                 len += req->recv_iov[i].iov_len;
275
276                 if (req->recv_iov[i].iov_len == 0) {
277                         continue;
278                 }
279
280                 if (c1 != c2) {
281                         req->status = NT_STATUS_INTERNAL_ERROR;
282                         req->state = SMB2_REQUEST_ERROR;
283                         if (req->async.fn) {
284                                 req->async.fn(req);
285                         }
286                         return;
287                 }
288         }
289
290         req->in.buffer = req->recv_iov[0].iov_base;
291         req->in.size = len;
292         req->in.allocated = req->in.size;
293
294         req->in.hdr        =  req->recv_iov[0].iov_base;
295         req->in.body       =  req->recv_iov[1].iov_base;
296         req->in.dynamic    =  req->recv_iov[2].iov_base;
297         req->in.body_fixed =  req->recv_iov[1].iov_len;
298         req->in.body_size  =  req->in.body_fixed;
299         req->in.body_size  += req->recv_iov[2].iov_len;
300
301         smb2_setup_bufinfo(req);
302
303         req->state = SMB2_REQUEST_DONE;
304         if (req->async.fn) {
305                 req->async.fn(req);
306         }
307 }
308
309 static void smb2_transport_break_handler(struct tevent_req *subreq)
310 {
311         struct smb2_transport *transport =
312                 tevent_req_callback_data(subreq,
313                 struct smb2_transport);
314         NTSTATUS status;
315         uint8_t *body;
316         uint16_t len = 0;
317         bool lease;
318         struct iovec *recv_iov = NULL;
319
320         transport->break_subreq = NULL;
321
322         status = smb2cli_req_recv(subreq, transport, &recv_iov, NULL, 0);
323         TALLOC_FREE(subreq);
324         if (!NT_STATUS_IS_OK(status)) {
325                 TALLOC_FREE(recv_iov);
326                 smb2_transport_dead(transport, status);
327                 return;
328         }
329
330         /*
331          * Setup the subreq to handle the
332          * next incoming SMB2 Break.
333          */
334         subreq = smb2cli_req_create(transport,
335                                     transport->ev,
336                                     transport->conn,
337                                     SMB2_OP_BREAK,
338                                     0, /* additional_flags */
339                                     0, /*clear_flags */
340                                     0, /* timeout_msec */
341                                     NULL, /* tcon */
342                                     NULL, /* session */
343                                     NULL, /* body */
344                                     0, /* body_fixed */
345                                     NULL, /* dyn */
346                                     0, /* dyn_len */
347                                     0); /* max_dyn_len */
348         if (subreq != NULL) {
349                 smbXcli_req_set_pending(subreq);
350                 tevent_req_set_callback(subreq,
351                                         smb2_transport_break_handler,
352                                         transport);
353                 transport->break_subreq = subreq;
354         }
355
356         body = recv_iov[1].iov_base;
357
358         len = recv_iov[1].iov_len;
359         if (recv_iov[1].iov_len >= 2) {
360                 len = CVAL(body, 0x00);
361                 if (len != recv_iov[1].iov_len) {
362                         len = recv_iov[1].iov_len;
363                 }
364         }
365
366         if (len == 24) {
367                 lease = false;
368         } else if (len == 44) {
369                 lease = true;
370         } else {
371                 DEBUG(1,("Discarding smb2 oplock reply of invalid size %u\n",
372                         (unsigned)len));
373                 TALLOC_FREE(recv_iov);
374                 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
375                 smb2_transport_dead(transport, status);
376                 return;
377         }
378
379         if (!lease && transport->oplock.handler) {
380                 struct smb2_handle h;
381                 uint8_t level;
382
383                 level = CVAL(body, 0x02);
384                 smb2_pull_handle(body+0x08, &h);
385
386                 TALLOC_FREE(recv_iov);
387
388                 transport->oplock.handler(transport, &h, level,
389                                           transport->oplock.private_data);
390         } else if (lease && transport->lease.handler) {
391                 struct smb2_lease_break lb;
392
393                 ZERO_STRUCT(lb);
394                 lb.break_flags =                SVAL(body, 0x4);
395                 memcpy(&lb.current_lease.lease_key, body+0x8,
396                     sizeof(struct smb2_lease_key));
397                 lb.current_lease.lease_state =  SVAL(body, 0x18);
398                 lb.new_lease_state =            SVAL(body, 0x1C);
399                 lb.break_reason =               SVAL(body, 0x20);
400                 lb.access_mask_hint =           SVAL(body, 0x24);
401                 lb.share_mask_hint =            SVAL(body, 0x28);
402
403                 TALLOC_FREE(recv_iov);
404
405                 transport->lease.handler(transport, &lb,
406                     transport->lease.private_data);
407         } else {
408                 DEBUG(5,("Got SMB2 %s break with no handler\n",
409                         lease ? "lease" : "oplock"));
410         }
411         TALLOC_FREE(recv_iov);
412 }
413
414 NTSTATUS smb2_transport_compound_start(struct smb2_transport *transport,
415                                        uint32_t num)
416 {
417         TALLOC_FREE(transport->compound.reqs);
418         ZERO_STRUCT(transport->compound);
419
420         transport->compound.reqs = talloc_zero_array(transport,
421                                                      struct tevent_req *,
422                                                      num);
423         if (transport->compound.reqs == NULL) {
424                 return NT_STATUS_NO_MEMORY;
425         }
426
427         return NT_STATUS_OK;
428 }
429
430 void smb2_transport_compound_set_related(struct smb2_transport *transport,
431                                          bool related)
432 {
433         transport->compound.related = related;
434 }
435
436 void smb2_transport_credits_ask_num(struct smb2_transport *transport,
437                                     uint16_t ask_num)
438 {
439         smb2cli_conn_set_max_credits(transport->conn, ask_num);
440 }
441
442 static void idle_handler(struct tevent_context *ev, 
443                          struct tevent_timer *te, struct timeval t, void *private_data)
444 {
445         struct smb2_transport *transport = talloc_get_type(private_data,
446                                                            struct smb2_transport);
447         struct timeval next;
448
449         transport->idle.func(transport, transport->idle.private_data);
450
451         next = timeval_current_ofs_usec(transport->idle.period);
452         transport->idle.te = tevent_add_timer(transport->ev,
453                                               transport,
454                                               next,
455                                               idle_handler,
456                                               transport);
457 }
458
459 /*
460   setup the idle handler for a transport
461   the period is in microseconds
462 */
463 void smb2_transport_idle_handler(struct smb2_transport *transport, 
464                                  void (*idle_func)(struct smb2_transport *, void *),
465                                  uint64_t period,
466                                  void *private_data)
467 {
468         TALLOC_FREE(transport->idle.te);
469
470         transport->idle.func = idle_func;
471         transport->idle.private_data = private_data;
472         transport->idle.period = period;
473
474         transport->idle.te = tevent_add_timer(transport->ev,
475                                               transport,
476                                               timeval_current_ofs_usec(period),
477                                               idle_handler,
478                                               transport);
479 }