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