14d1fc541e837322f348ec85e04ee5fbe6191f93
[ira/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/stream/packet.h"
31 #include "../lib/util/dlinklist.h"
32 #include "../libcli/smb/smbXcli_base.h"
33 #include "librpc/ndr/libndr.h"
34
35 /*
36   destroy a transport
37  */
38 static int transport_destructor(struct smb2_transport *transport)
39 {
40         smb2_transport_dead(transport, NT_STATUS_LOCAL_DISCONNECT);
41         return 0;
42 }
43
44 /*
45   create a transport structure based on an established socket
46 */
47 struct smb2_transport *smb2_transport_init(struct smbcli_socket *sock,
48                                            TALLOC_CTX *parent_ctx,
49                                            struct smbcli_options *options)
50 {
51         struct smb2_transport *transport;
52         struct GUID client_guid;
53
54         transport = talloc_zero(parent_ctx, struct smb2_transport);
55         if (!transport) return NULL;
56
57         transport->ev = sock->event.ctx;
58         transport->options = *options;
59
60         TALLOC_FREE(sock->event.fde);
61         TALLOC_FREE(sock->event.te);
62
63         client_guid = GUID_random();
64
65         transport->conn = smbXcli_conn_create(transport,
66                                               sock->sock->fd,
67                                               sock->hostname,
68                                               options->signing,
69                                               0, /* smb1_capabilities */
70                                               &client_guid);
71         if (transport->conn == NULL) {
72                 talloc_free(transport);
73                 return NULL;
74         }
75         sock->sock->fd = -1;
76         TALLOC_FREE(sock);
77
78         talloc_set_destructor(transport, transport_destructor);
79
80         return transport;
81 }
82
83 /*
84   mark the transport as dead
85 */
86 void smb2_transport_dead(struct smb2_transport *transport, NTSTATUS status)
87 {
88         if (NT_STATUS_EQUAL(NT_STATUS_UNSUCCESSFUL, status)) {
89                 status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
90         }
91         if (NT_STATUS_IS_OK(status)) {
92                 status = NT_STATUS_LOCAL_DISCONNECT;
93         }
94
95         smbXcli_conn_disconnect(transport->conn, status);
96 }
97
98 static void smb2_request_done(struct tevent_req *subreq);
99 static void smb2_transport_break_handler(struct tevent_req *subreq);
100
101 /*
102   put a request into the send queue
103 */
104 void smb2_transport_send(struct smb2_request *req)
105 {
106         NTSTATUS status;
107         struct smb2_transport *transport = req->transport;
108         struct tevent_req **reqs = transport->compound.reqs;
109         size_t num_reqs = talloc_array_length(reqs);
110         size_t i;
111         uint16_t cmd = SVAL(req->out.hdr, SMB2_HDR_OPCODE);
112         uint32_t additional_flags = IVAL(req->out.hdr, SMB2_HDR_FLAGS);
113         uint32_t clear_flags = 0;
114         uint32_t pid = IVAL(req->out.hdr, SMB2_HDR_PID);
115         uint32_t tid = IVAL(req->out.hdr, SMB2_HDR_TID);
116         struct smbXcli_session *session = NULL;
117         bool need_pending_break = false;
118         size_t hdr_ofs;
119         size_t pdu_len;
120         DATA_BLOB body = data_blob_null;
121         DATA_BLOB dyn = data_blob_null;
122         uint32_t timeout_msec = transport->options.request_timeout * 1000;
123
124         if (transport->oplock.handler) {
125                 need_pending_break = true;
126         }
127
128         if (transport->lease.handler) {
129                 need_pending_break = true;
130         }
131
132         if (transport->break_subreq) {
133                 need_pending_break = false;
134         }
135
136         if (need_pending_break) {
137                 struct tevent_req *subreq;
138
139                 subreq = smb2cli_req_create(transport,
140                                             transport->ev,
141                                             transport->conn,
142                                             SMB2_OP_BREAK,
143                                             0, /* additional_flags */
144                                             0, /*clear_flags */
145                                             0, /* timeout_msec */
146                                             0, /* pid */
147                                             0, /* tid */
148                                             NULL, /* session */
149                                             NULL, /* body */
150                                             0, /* body_fixed */
151                                             NULL, /* dyn */
152                                             0); /* dyn_len */
153                 if (subreq != NULL) {
154                         smbXcli_req_set_pending(subreq);
155                         tevent_req_set_callback(subreq,
156                                                 smb2_transport_break_handler,
157                                                 transport);
158                         transport->break_subreq = subreq;
159                 }
160         }
161
162         if (req->session) {
163                 session = req->session->smbXcli;
164         }
165
166         if (transport->compound.related) {
167                 additional_flags |= SMB2_HDR_FLAG_CHAINED;
168         }
169
170         hdr_ofs = PTR_DIFF(req->out.hdr, req->out.buffer);
171         pdu_len = req->out.size - hdr_ofs;
172         body.data = req->out.body;
173         body.length = req->out.body_fixed;
174         dyn.data = req->out.body + req->out.body_fixed;
175         dyn.length = pdu_len - (SMB2_HDR_BODY + req->out.body_fixed);
176
177         req->subreq = smb2cli_req_create(req,
178                                          transport->ev,
179                                          transport->conn,
180                                          cmd,
181                                          additional_flags,
182                                          clear_flags,
183                                          timeout_msec,
184                                          pid,
185                                          tid,
186                                          session,
187                                          body.data, body.length,
188                                          dyn.data, dyn.length);
189         if (req->subreq == NULL) {
190                 req->state = SMB2_REQUEST_ERROR;
191                 req->status = NT_STATUS_NO_MEMORY;
192                 return;
193         }
194
195         if (!tevent_req_is_in_progress(req->subreq)) {
196                 req->state = SMB2_REQUEST_ERROR;
197                 req->status = NT_STATUS_INTERNAL_ERROR;/* TODO */
198                 return;
199         }
200
201         tevent_req_set_callback(req->subreq, smb2_request_done, req);
202
203         smb2cli_req_set_notify_async(req->subreq);
204         if (req->credit_charge) {
205                 smb2cli_req_set_credit_charge(req->subreq, req->credit_charge);
206         }
207
208         ZERO_STRUCT(req->out);
209         req->state = SMB2_REQUEST_RECV;
210
211         if (num_reqs > 0) {
212                 for (i=0; i < num_reqs; i++) {
213                         if (reqs[i] != NULL) {
214                                 continue;
215                         }
216
217                         reqs[i] = req->subreq;
218                         i++;
219                         break;
220                 }
221
222                 if (i < num_reqs) {
223                         return;
224                 }
225         } else {
226                 reqs = &req->subreq;
227                 num_reqs = 1;
228         }
229         status = smb2cli_req_compound_submit(reqs, num_reqs);
230
231         TALLOC_FREE(transport->compound.reqs);
232
233         if (!NT_STATUS_IS_OK(status)) {
234                 req->status = status;
235                 req->state = SMB2_REQUEST_ERROR;
236                 smbXcli_conn_disconnect(transport->conn, status);
237         }
238 }
239
240 static void smb2_request_done(struct tevent_req *subreq)
241 {
242         struct smb2_request *req =
243                 tevent_req_callback_data(subreq,
244                 struct smb2_request);
245         ssize_t len;
246         size_t i;
247
248         req->recv_iov = NULL;
249
250         req->status = smb2cli_req_recv(req->subreq, req, &req->recv_iov, NULL, 0);
251         if (NT_STATUS_EQUAL(req->status, STATUS_PENDING)) {
252                 req->cancel.can_cancel = true;
253                 return;
254         }
255         TALLOC_FREE(req->subreq);
256         if (!NT_STATUS_IS_OK(req->status)) {
257                 if (req->recv_iov == NULL) {
258                         req->state = SMB2_REQUEST_ERROR;
259                         if (req->async.fn) {
260                                 req->async.fn(req);
261                         }
262                         return;
263                 }
264         }
265
266         len = req->recv_iov[0].iov_len;
267         for (i=1; i < 3; i++) {
268                 uint8_t *p = req->recv_iov[i-1].iov_base;
269                 uint8_t *c1 = req->recv_iov[i].iov_base;
270                 uint8_t *c2 = p + req->recv_iov[i-1].iov_len;
271
272                 len += req->recv_iov[i].iov_len;
273
274                 if (req->recv_iov[i].iov_len == 0) {
275                         continue;
276                 }
277
278                 if (c1 != c2) {
279                         req->status = NT_STATUS_INTERNAL_ERROR;
280                         req->state = SMB2_REQUEST_ERROR;
281                         if (req->async.fn) {
282                                 req->async.fn(req);
283                         }
284                         return;
285                 }
286         }
287
288         req->in.buffer = req->recv_iov[0].iov_base;
289         req->in.size = len;
290         req->in.allocated = req->in.size;
291
292         req->in.hdr        =  req->recv_iov[0].iov_base;
293         req->in.body       =  req->recv_iov[1].iov_base;
294         req->in.dynamic    =  req->recv_iov[2].iov_base;
295         req->in.body_fixed =  req->recv_iov[1].iov_len;
296         req->in.body_size  =  req->in.body_fixed;
297         req->in.body_size  += req->recv_iov[2].iov_len;
298
299         smb2_setup_bufinfo(req);
300
301         req->state = SMB2_REQUEST_DONE;
302         if (req->async.fn) {
303                 req->async.fn(req);
304         }
305 }
306
307 static void smb2_transport_break_handler(struct tevent_req *subreq)
308 {
309         struct smb2_transport *transport =
310                 tevent_req_callback_data(subreq,
311                 struct smb2_transport);
312         NTSTATUS status;
313         uint8_t *hdr;
314         uint8_t *body;
315         uint16_t len = 0;
316         bool lease;
317         struct iovec *recv_iov = NULL;
318
319         transport->break_subreq = NULL;
320
321         status = smb2cli_req_recv(subreq, transport, &recv_iov, NULL, 0);
322         TALLOC_FREE(subreq);
323         if (!NT_STATUS_IS_OK(status)) {
324                 TALLOC_FREE(recv_iov);
325                 smb2_transport_dead(transport, status);
326                 return;
327         }
328
329         /*
330          * Setup the subreq to handle the
331          * next incoming SMB2 Break.
332          */
333         subreq = smb2cli_req_create(transport,
334                                     transport->ev,
335                                     transport->conn,
336                                     SMB2_OP_BREAK,
337                                     0, /* additional_flags */
338                                     0, /*clear_flags */
339                                     0, /* timeout_msec */
340                                     0, /* pid */
341                                     0, /* tid */
342                                     NULL, /* session */
343                                     NULL, /* body */
344                                     0, /* body_fixed */
345                                     NULL, /* dyn */
346                                     0); /* dyn_len */
347         if (subreq != NULL) {
348                 smbXcli_req_set_pending(subreq);
349                 tevent_req_set_callback(subreq,
350                                         smb2_transport_break_handler,
351                                         transport);
352                 transport->break_subreq = subreq;
353         }
354
355         hdr = recv_iov[0].iov_base;
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 }