smbXcli: add the possiblilty to negotiate client capabilites in smb >= 2.2
[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/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                                               0 /* smb2_capabilities */);
72         if (transport->conn == NULL) {
73                 talloc_free(transport);
74                 return NULL;
75         }
76         sock->sock->fd = -1;
77         TALLOC_FREE(sock);
78
79         talloc_set_destructor(transport, transport_destructor);
80
81         return transport;
82 }
83
84 /*
85   mark the transport as dead
86 */
87 void smb2_transport_dead(struct smb2_transport *transport, NTSTATUS status)
88 {
89         if (NT_STATUS_EQUAL(NT_STATUS_UNSUCCESSFUL, status)) {
90                 status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
91         }
92         if (NT_STATUS_IS_OK(status)) {
93                 status = NT_STATUS_LOCAL_DISCONNECT;
94         }
95
96         smbXcli_conn_disconnect(transport->conn, status);
97 }
98
99 static void smb2_request_done(struct tevent_req *subreq);
100 static void smb2_transport_break_handler(struct tevent_req *subreq);
101
102 /*
103   put a request into the send queue
104 */
105 void smb2_transport_send(struct smb2_request *req)
106 {
107         NTSTATUS status;
108         struct smb2_transport *transport = req->transport;
109         struct tevent_req **reqs = transport->compound.reqs;
110         size_t num_reqs = talloc_array_length(reqs);
111         size_t i;
112         uint16_t cmd = SVAL(req->out.hdr, SMB2_HDR_OPCODE);
113         uint32_t additional_flags = IVAL(req->out.hdr, SMB2_HDR_FLAGS);
114         uint32_t clear_flags = 0;
115         uint32_t pid = IVAL(req->out.hdr, SMB2_HDR_PID);
116         uint32_t tid = IVAL(req->out.hdr, SMB2_HDR_TID);
117         struct smbXcli_session *session = NULL;
118         bool need_pending_break = false;
119         size_t hdr_ofs;
120         size_t pdu_len;
121         DATA_BLOB body = data_blob_null;
122         DATA_BLOB dyn = data_blob_null;
123         uint32_t timeout_msec = transport->options.request_timeout * 1000;
124
125         if (transport->oplock.handler) {
126                 need_pending_break = true;
127         }
128
129         if (transport->lease.handler) {
130                 need_pending_break = true;
131         }
132
133         if (transport->break_subreq) {
134                 need_pending_break = false;
135         }
136
137         if (need_pending_break) {
138                 struct tevent_req *subreq;
139
140                 subreq = smb2cli_req_create(transport,
141                                             transport->ev,
142                                             transport->conn,
143                                             SMB2_OP_BREAK,
144                                             0, /* additional_flags */
145                                             0, /*clear_flags */
146                                             0, /* timeout_msec */
147                                             0, /* pid */
148                                             0, /* tid */
149                                             NULL, /* session */
150                                             NULL, /* body */
151                                             0, /* body_fixed */
152                                             NULL, /* dyn */
153                                             0); /* dyn_len */
154                 if (subreq != NULL) {
155                         smbXcli_req_set_pending(subreq);
156                         tevent_req_set_callback(subreq,
157                                                 smb2_transport_break_handler,
158                                                 transport);
159                         transport->break_subreq = subreq;
160                 }
161         }
162
163         if (req->session) {
164                 session = req->session->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                                          pid,
186                                          tid,
187                                          session,
188                                          body.data, body.length,
189                                          dyn.data, dyn.length);
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
234         if (!NT_STATUS_IS_OK(status)) {
235                 req->status = status;
236                 req->state = SMB2_REQUEST_ERROR;
237                 smbXcli_conn_disconnect(transport->conn, status);
238         }
239 }
240
241 static void smb2_request_done(struct tevent_req *subreq)
242 {
243         struct smb2_request *req =
244                 tevent_req_callback_data(subreq,
245                 struct smb2_request);
246         ssize_t len;
247         size_t i;
248
249         req->recv_iov = NULL;
250
251         req->status = smb2cli_req_recv(req->subreq, req, &req->recv_iov, NULL, 0);
252         if (NT_STATUS_EQUAL(req->status, STATUS_PENDING)) {
253                 req->cancel.can_cancel = true;
254                 return;
255         }
256         TALLOC_FREE(req->subreq);
257         if (!NT_STATUS_IS_OK(req->status)) {
258                 if (req->recv_iov == NULL) {
259                         req->state = SMB2_REQUEST_ERROR;
260                         if (req->async.fn) {
261                                 req->async.fn(req);
262                         }
263                         return;
264                 }
265         }
266
267         len = req->recv_iov[0].iov_len;
268         for (i=1; i < 3; i++) {
269                 uint8_t *p = req->recv_iov[i-1].iov_base;
270                 uint8_t *c1 = req->recv_iov[i].iov_base;
271                 uint8_t *c2 = p + req->recv_iov[i-1].iov_len;
272
273                 len += req->recv_iov[i].iov_len;
274
275                 if (req->recv_iov[i].iov_len == 0) {
276                         continue;
277                 }
278
279                 if (c1 != c2) {
280                         req->status = NT_STATUS_INTERNAL_ERROR;
281                         req->state = SMB2_REQUEST_ERROR;
282                         if (req->async.fn) {
283                                 req->async.fn(req);
284                         }
285                         return;
286                 }
287         }
288
289         req->in.buffer = req->recv_iov[0].iov_base;
290         req->in.size = len;
291         req->in.allocated = req->in.size;
292
293         req->in.hdr        =  req->recv_iov[0].iov_base;
294         req->in.body       =  req->recv_iov[1].iov_base;
295         req->in.dynamic    =  req->recv_iov[2].iov_base;
296         req->in.body_fixed =  req->recv_iov[1].iov_len;
297         req->in.body_size  =  req->in.body_fixed;
298         req->in.body_size  += req->recv_iov[2].iov_len;
299
300         smb2_setup_bufinfo(req);
301
302         req->state = SMB2_REQUEST_DONE;
303         if (req->async.fn) {
304                 req->async.fn(req);
305         }
306 }
307
308 static void smb2_transport_break_handler(struct tevent_req *subreq)
309 {
310         struct smb2_transport *transport =
311                 tevent_req_callback_data(subreq,
312                 struct smb2_transport);
313         NTSTATUS status;
314         uint8_t *hdr;
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                                     0, /* pid */
342                                     0, /* tid */
343                                     NULL, /* session */
344                                     NULL, /* body */
345                                     0, /* body_fixed */
346                                     NULL, /* dyn */
347                                     0); /* 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         hdr = recv_iov[0].iov_base;
357         body = recv_iov[1].iov_base;
358
359         len = recv_iov[1].iov_len;
360         if (recv_iov[1].iov_len >= 2) {
361                 len = CVAL(body, 0x00);
362                 if (len != recv_iov[1].iov_len) {
363                         len = recv_iov[1].iov_len;
364                 }
365         }
366
367         if (len == 24) {
368                 lease = false;
369         } else if (len == 44) {
370                 lease = true;
371         } else {
372                 DEBUG(1,("Discarding smb2 oplock reply of invalid size %u\n",
373                         (unsigned)len));
374                 TALLOC_FREE(recv_iov);
375                 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
376                 smb2_transport_dead(transport, status);
377                 return;
378         }
379
380         if (!lease && transport->oplock.handler) {
381                 struct smb2_handle h;
382                 uint8_t level;
383
384                 level = CVAL(body, 0x02);
385                 smb2_pull_handle(body+0x08, &h);
386
387                 TALLOC_FREE(recv_iov);
388
389                 transport->oplock.handler(transport, &h, level,
390                                           transport->oplock.private_data);
391         } else if (lease && transport->lease.handler) {
392                 struct smb2_lease_break lb;
393
394                 ZERO_STRUCT(lb);
395                 lb.break_flags =                SVAL(body, 0x4);
396                 memcpy(&lb.current_lease.lease_key, body+0x8,
397                     sizeof(struct smb2_lease_key));
398                 lb.current_lease.lease_state =  SVAL(body, 0x18);
399                 lb.new_lease_state =            SVAL(body, 0x1C);
400                 lb.break_reason =               SVAL(body, 0x20);
401                 lb.access_mask_hint =           SVAL(body, 0x24);
402                 lb.share_mask_hint =            SVAL(body, 0x28);
403
404                 TALLOC_FREE(recv_iov);
405
406                 transport->lease.handler(transport, &lb,
407                     transport->lease.private_data);
408         } else {
409                 DEBUG(5,("Got SMB2 %s break with no handler\n",
410                         lease ? "lease" : "oplock"));
411         }
412         TALLOC_FREE(recv_iov);
413 }
414
415 NTSTATUS smb2_transport_compound_start(struct smb2_transport *transport,
416                                        uint32_t num)
417 {
418         TALLOC_FREE(transport->compound.reqs);
419         ZERO_STRUCT(transport->compound);
420
421         transport->compound.reqs = talloc_zero_array(transport,
422                                                      struct tevent_req *,
423                                                      num);
424         if (transport->compound.reqs == NULL) {
425                 return NT_STATUS_NO_MEMORY;
426         }
427
428         return NT_STATUS_OK;
429 }
430
431 void smb2_transport_compound_set_related(struct smb2_transport *transport,
432                                          bool related)
433 {
434         transport->compound.related = related;
435 }
436
437 void smb2_transport_credits_ask_num(struct smb2_transport *transport,
438                                     uint16_t ask_num)
439 {
440         smb2cli_conn_set_max_credits(transport->conn, ask_num);
441 }
442
443 static void idle_handler(struct tevent_context *ev, 
444                          struct tevent_timer *te, struct timeval t, void *private_data)
445 {
446         struct smb2_transport *transport = talloc_get_type(private_data,
447                                                            struct smb2_transport);
448         struct timeval next;
449
450         transport->idle.func(transport, transport->idle.private_data);
451
452         next = timeval_current_ofs_usec(transport->idle.period);
453         transport->idle.te = tevent_add_timer(transport->ev,
454                                               transport,
455                                               next,
456                                               idle_handler,
457                                               transport);
458 }
459
460 /*
461   setup the idle handler for a transport
462   the period is in microseconds
463 */
464 void smb2_transport_idle_handler(struct smb2_transport *transport, 
465                                  void (*idle_func)(struct smb2_transport *, void *),
466                                  uint64_t period,
467                                  void *private_data)
468 {
469         TALLOC_FREE(transport->idle.te);
470
471         transport->idle.func = idle_func;
472         transport->idle.private_data = private_data;
473         transport->idle.period = period;
474
475         transport->idle.te = tevent_add_timer(transport->ev,
476                                               transport,
477                                               timeval_current_ofs_usec(period),
478                                               idle_handler,
479                                               transport);
480 }