Coverage Report

Created: 2025-12-31 06:20

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