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 12835 : static int transport_destructor(struct smb2_transport *transport)
38 : {
39 12835 : smb2_transport_dead(transport, NT_STATUS_LOCAL_DISCONNECT);
40 12835 : return 0;
41 : }
42 :
43 : /*
44 : create a transport structure based on an established socket
45 : */
46 6065 : struct smb2_transport *smb2_transport_init(struct smbcli_socket *sock,
47 : TALLOC_CTX *parent_ctx,
48 : struct smbcli_options *options)
49 : {
50 311 : struct smb2_transport *transport;
51 :
52 6065 : transport = talloc_zero(parent_ctx, struct smb2_transport);
53 6065 : if (!transport) return NULL;
54 :
55 6065 : transport->ev = sock->event.ctx;
56 6065 : transport->options = *options;
57 :
58 6065 : if (transport->options.max_protocol == PROTOCOL_DEFAULT) {
59 4085 : transport->options.max_protocol = PROTOCOL_LATEST;
60 : }
61 :
62 6065 : if (transport->options.max_protocol < PROTOCOL_SMB2_02) {
63 0 : transport->options.max_protocol = PROTOCOL_LATEST;
64 : }
65 :
66 6065 : TALLOC_FREE(sock->event.fde);
67 6065 : TALLOC_FREE(sock->event.te);
68 :
69 12130 : transport->conn = smbXcli_conn_create(transport,
70 6065 : sock->sock->fd,
71 : sock->hostname,
72 : options->signing,
73 : 0, /* smb1_capabilities */
74 : &options->client_guid,
75 : options->smb2_capabilities,
76 6065 : &options->smb3_capabilities);
77 6065 : if (transport->conn == NULL) {
78 0 : talloc_free(transport);
79 0 : return NULL;
80 : }
81 6065 : sock->sock->fd = -1;
82 6065 : TALLOC_FREE(sock);
83 :
84 6065 : talloc_set_destructor(transport, transport_destructor);
85 :
86 6065 : return transport;
87 : }
88 :
89 : /*
90 : create a transport structure based on an established socket
91 : */
92 6774 : 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 6774 : struct smb2_transport *transport = NULL;
99 390 : enum protocol_types protocol;
100 :
101 6774 : if (*_conn == NULL) {
102 0 : return NT_STATUS_INVALID_PARAMETER;
103 : }
104 :
105 6774 : protocol = smbXcli_conn_protocol(*_conn);
106 6774 : if (protocol < PROTOCOL_SMB2_02) {
107 0 : return NT_STATUS_REVISION_MISMATCH;
108 : }
109 :
110 6774 : transport = talloc_zero(mem_ctx, struct smb2_transport);
111 6774 : if (transport == NULL) {
112 0 : return NT_STATUS_NO_MEMORY;
113 : }
114 :
115 6774 : transport->ev = ev;
116 6774 : transport->options = *options;
117 6774 : transport->conn = talloc_move(transport, _conn);
118 :
119 6774 : talloc_set_destructor(transport, transport_destructor);
120 6774 : *_transport = transport;
121 6774 : return NT_STATUS_OK;
122 : }
123 :
124 : /*
125 : mark the transport as dead
126 : */
127 13327 : void smb2_transport_dead(struct smb2_transport *transport, NTSTATUS status)
128 : {
129 13327 : if (NT_STATUS_EQUAL(NT_STATUS_UNSUCCESSFUL, status)) {
130 0 : status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
131 : }
132 13327 : if (NT_STATUS_IS_OK(status)) {
133 0 : status = NT_STATUS_LOCAL_DISCONNECT;
134 : }
135 :
136 13327 : smbXcli_conn_disconnect(transport->conn, status);
137 13327 : }
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 1159668 : void smb2_transport_send(struct smb2_request *req)
146 : {
147 898 : NTSTATUS status;
148 1159668 : struct smb2_transport *transport = req->transport;
149 1159668 : struct tevent_req **reqs = transport->compound.reqs;
150 1159668 : size_t num_reqs = talloc_array_length(reqs);
151 898 : size_t i;
152 1159668 : uint16_t cmd = SVAL(req->out.hdr, SMB2_HDR_OPCODE);
153 1159668 : uint32_t additional_flags = IVAL(req->out.hdr, SMB2_HDR_FLAGS);
154 1159668 : uint32_t clear_flags = 0;
155 1159668 : struct smbXcli_tcon *tcon = NULL;
156 1159668 : struct smbXcli_session *session = NULL;
157 1159668 : bool need_pending_break = false;
158 898 : size_t hdr_ofs;
159 898 : size_t pdu_len;
160 1159668 : DATA_BLOB body = data_blob_null;
161 1159668 : DATA_BLOB dyn = data_blob_null;
162 1159668 : uint32_t timeout_msec = transport->options.request_timeout * 1000;
163 :
164 1159668 : if (transport->oplock.handler) {
165 6930 : need_pending_break = true;
166 : }
167 :
168 1159668 : if (transport->lease.handler) {
169 5004 : need_pending_break = true;
170 : }
171 :
172 1159668 : if (transport->break_subreq) {
173 6936 : need_pending_break = false;
174 : }
175 :
176 1159668 : if (need_pending_break) {
177 0 : struct tevent_req *subreq;
178 :
179 1184 : 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 1184 : if (subreq != NULL) {
194 1184 : smbXcli_req_set_pending(subreq);
195 1184 : tevent_req_set_callback(subreq,
196 : smb2_transport_break_handler,
197 : transport);
198 1184 : transport->break_subreq = subreq;
199 : }
200 : }
201 :
202 1159668 : if (req->session) {
203 1158779 : session = req->session->smbXcli;
204 : }
205 :
206 1159668 : if (req->tree) {
207 1158610 : tcon = req->tree->smbXcli;
208 : }
209 :
210 1159668 : if (transport->compound.related) {
211 270 : additional_flags |= SMB2_HDR_FLAG_CHAINED;
212 : }
213 :
214 1159668 : hdr_ofs = PTR_DIFF(req->out.hdr, req->out.buffer);
215 1159668 : pdu_len = req->out.size - hdr_ofs;
216 1159668 : body.data = req->out.body;
217 1159668 : body.length = req->out.body_fixed;
218 1159668 : dyn.data = req->out.body + req->out.body_fixed;
219 1159668 : dyn.length = pdu_len - (SMB2_HDR_BODY + req->out.body_fixed);
220 :
221 2318438 : 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 1158770 : body.data, body.length,
231 1158770 : dyn.data, dyn.length,
232 : 0); /* max_dyn_len */
233 1159668 : 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 1159668 : 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 1159668 : tevent_req_set_callback(req->subreq, smb2_request_done, req);
246 :
247 1159668 : smb2cli_req_set_notify_async(req->subreq);
248 1159668 : if (req->credit_charge) {
249 24006 : smb2cli_req_set_credit_charge(req->subreq, req->credit_charge);
250 : }
251 :
252 1159668 : ZERO_STRUCT(req->out);
253 1159668 : req->state = SMB2_REQUEST_RECV;
254 :
255 1159668 : if (num_reqs > 0) {
256 1086 : for (i=0; i < num_reqs; i++) {
257 1086 : if (reqs[i] != NULL) {
258 602 : continue;
259 : }
260 :
261 484 : reqs[i] = req->subreq;
262 484 : i++;
263 484 : break;
264 : }
265 :
266 484 : if (i < num_reqs) {
267 326 : return;
268 : }
269 : } else {
270 1159184 : reqs = &req->subreq;
271 1159184 : num_reqs = 1;
272 : }
273 1159342 : status = smb2cli_req_compound_submit(reqs, num_reqs);
274 :
275 1159342 : TALLOC_FREE(transport->compound.reqs);
276 1159342 : transport->compound.related = false;
277 :
278 1159342 : if (!NT_STATUS_IS_OK(status)) {
279 35 : req->status = status;
280 35 : req->state = SMB2_REQUEST_ERROR;
281 35 : smbXcli_conn_disconnect(transport->conn, status);
282 : }
283 : }
284 :
285 1161222 : static void smb2_request_done(struct tevent_req *subreq)
286 : {
287 913 : struct smb2_request *req =
288 1161222 : tevent_req_callback_data(subreq,
289 : struct smb2_request);
290 913 : ssize_t len;
291 913 : size_t i;
292 :
293 1161222 : req->recv_iov = NULL;
294 :
295 1161222 : req->status = smb2cli_req_recv(req->subreq, req, &req->recv_iov, NULL, 0);
296 1161222 : if (NT_STATUS_EQUAL(req->status, NT_STATUS_PENDING)) {
297 1623 : struct timeval endtime = smbXcli_req_endtime(subreq);
298 16 : bool ok;
299 :
300 1623 : req->cancel.can_cancel = true;
301 1623 : if (timeval_is_zero(&endtime)) {
302 824 : return;
303 : }
304 :
305 783 : ok = tevent_req_set_endtime(
306 783 : subreq, req->transport->ev, endtime);
307 783 : 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 783 : return;
316 : }
317 1159599 : TALLOC_FREE(req->subreq);
318 1159599 : if (!NT_STATUS_IS_OK(req->status)) {
319 180718 : if (req->recv_iov == NULL) {
320 81 : req->state = SMB2_REQUEST_ERROR;
321 81 : if (req->async.fn) {
322 4 : req->async.fn(req);
323 : }
324 81 : return;
325 : }
326 : }
327 :
328 1159518 : len = req->recv_iov[0].iov_len;
329 3478554 : for (i=1; i < 3; i++) {
330 2319036 : uint8_t *p = req->recv_iov[i-1].iov_base;
331 2319036 : uint8_t *c1 = req->recv_iov[i].iov_base;
332 2319036 : uint8_t *c2 = p + req->recv_iov[i-1].iov_len;
333 :
334 2319036 : len += req->recv_iov[i].iov_len;
335 :
336 2319036 : if (req->recv_iov[i].iov_len == 0) {
337 820019 : continue;
338 : }
339 :
340 1499017 : 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 1159518 : req->in.buffer = req->recv_iov[0].iov_base;
351 1159518 : req->in.size = len;
352 1159518 : req->in.allocated = req->in.size;
353 :
354 1159518 : req->in.hdr = req->recv_iov[0].iov_base;
355 1159518 : req->in.body = req->recv_iov[1].iov_base;
356 1159518 : req->in.dynamic = req->recv_iov[2].iov_base;
357 1159518 : req->in.body_fixed = req->recv_iov[1].iov_len;
358 1159518 : req->in.body_size = req->in.body_fixed;
359 1159518 : req->in.body_size += req->recv_iov[2].iov_len;
360 :
361 1159518 : smb2_setup_bufinfo(req);
362 :
363 1159518 : req->state = SMB2_REQUEST_DONE;
364 1159518 : if (req->async.fn) {
365 625181 : req->async.fn(req);
366 : }
367 : }
368 :
369 1164 : static void smb2_transport_break_handler(struct tevent_req *subreq)
370 : {
371 0 : struct smb2_transport *transport =
372 1164 : tevent_req_callback_data(subreq,
373 : struct smb2_transport);
374 0 : NTSTATUS status;
375 0 : uint8_t *body;
376 1164 : uint16_t len = 0;
377 0 : bool lease;
378 1164 : struct iovec *recv_iov = NULL;
379 :
380 1164 : transport->break_subreq = NULL;
381 :
382 1164 : status = smb2cli_req_recv(subreq, transport, &recv_iov, NULL, 0);
383 1164 : TALLOC_FREE(subreq);
384 1164 : if (!NT_STATUS_IS_OK(status)) {
385 490 : TALLOC_FREE(recv_iov);
386 490 : smb2_transport_dead(transport, status);
387 490 : return;
388 : }
389 :
390 : /*
391 : * Setup the subreq to handle the
392 : * next incoming SMB2 Break.
393 : */
394 674 : 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 674 : if (subreq != NULL) {
409 674 : smbXcli_req_set_pending(subreq);
410 674 : tevent_req_set_callback(subreq,
411 : smb2_transport_break_handler,
412 : transport);
413 674 : transport->break_subreq = subreq;
414 : }
415 :
416 674 : body = recv_iov[1].iov_base;
417 :
418 674 : len = recv_iov[1].iov_len;
419 674 : if (recv_iov[1].iov_len >= 2) {
420 674 : len = CVAL(body, 0x00);
421 674 : if (len != recv_iov[1].iov_len) {
422 0 : len = recv_iov[1].iov_len;
423 : }
424 : }
425 :
426 674 : if (len == 24) {
427 416 : lease = false;
428 258 : } else if (len == 44) {
429 258 : 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 1090 : if (!lease && transport->oplock.handler) {
440 0 : struct smb2_handle h;
441 0 : uint8_t level;
442 :
443 416 : level = CVAL(body, 0x02);
444 416 : smb2_pull_handle(body+0x08, &h);
445 :
446 416 : TALLOC_FREE(recv_iov);
447 :
448 416 : transport->oplock.handler(transport, &h, level,
449 : transport->oplock.private_data);
450 516 : } else if (lease && transport->lease.handler) {
451 0 : struct smb2_lease_break lb;
452 :
453 258 : ZERO_STRUCT(lb);
454 258 : lb.new_epoch = SVAL(body, 0x2);
455 258 : lb.break_flags = SVAL(body, 0x4);
456 258 : memcpy(&lb.current_lease.lease_key, body+0x8,
457 : sizeof(struct smb2_lease_key));
458 258 : lb.current_lease.lease_state = SVAL(body, 0x18);
459 258 : lb.new_lease_state = SVAL(body, 0x1C);
460 258 : lb.break_reason = SVAL(body, 0x20);
461 258 : lb.access_mask_hint = SVAL(body, 0x24);
462 258 : lb.share_mask_hint = SVAL(body, 0x28);
463 :
464 258 : TALLOC_FREE(recv_iov);
465 :
466 258 : 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 674 : TALLOC_FREE(recv_iov);
473 : }
474 :
475 158 : NTSTATUS smb2_transport_compound_start(struct smb2_transport *transport,
476 : uint32_t num)
477 : {
478 158 : TALLOC_FREE(transport->compound.reqs);
479 158 : ZERO_STRUCT(transport->compound);
480 :
481 158 : transport->compound.reqs = talloc_zero_array(transport,
482 : struct tevent_req *,
483 : num);
484 158 : if (transport->compound.reqs == NULL) {
485 0 : return NT_STATUS_NO_MEMORY;
486 : }
487 :
488 158 : return NT_STATUS_OK;
489 : }
490 :
491 156 : void smb2_transport_compound_set_related(struct smb2_transport *transport,
492 : bool related)
493 : {
494 156 : transport->compound.related = related;
495 156 : }
496 :
497 104 : void smb2_transport_credits_ask_num(struct smb2_transport *transport,
498 : uint16_t ask_num)
499 : {
500 104 : smb2cli_conn_set_max_credits(transport->conn, ask_num);
501 104 : }
502 :
503 2 : static void idle_handler(struct tevent_context *ev,
504 : struct tevent_timer *te, struct timeval t, void *private_data)
505 : {
506 2 : struct smb2_transport *transport = talloc_get_type(private_data,
507 : struct smb2_transport);
508 0 : struct timeval next;
509 :
510 2 : transport->idle.func(transport, transport->idle.private_data);
511 :
512 2 : if (transport->idle.func == NULL) {
513 2 : return;
514 : }
515 :
516 2 : if (!smbXcli_conn_is_connected(transport->conn)) {
517 2 : 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 2 : 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 2 : TALLOC_FREE(transport->idle.te);
538 2 : ZERO_STRUCT(transport->idle);
539 :
540 2 : if (idle_func == NULL) {
541 0 : return;
542 : }
543 :
544 2 : if (!smbXcli_conn_is_connected(transport->conn)) {
545 0 : return;
546 : }
547 :
548 2 : transport->idle.func = idle_func;
549 2 : transport->idle.private_data = private_data;
550 2 : transport->idle.period = period;
551 :
552 2 : transport->idle.te = tevent_add_timer(transport->ev,
553 : transport,
554 : timeval_current_ofs_usec(period),
555 : idle_handler,
556 : transport);
557 : }
|