Coverage Report

Created: 2026-02-14 07:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/source3/smbd/smb2_tcon.c
Line
Count
Source
1
/*
2
   Unix SMB/CIFS implementation.
3
   Core SMB2 server
4
5
   Copyright (C) Stefan Metzmacher 2009
6
7
   This program is free software; you can redistribute it and/or modify
8
   it under the terms of the GNU General Public License as published by
9
   the Free Software Foundation; either version 3 of the License, or
10
   (at your option) any later version.
11
12
   This program is distributed in the hope that it will be useful,
13
   but WITHOUT ANY WARRANTY; without even the implied warranty of
14
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
   GNU General Public License for more details.
16
17
   You should have received a copy of the GNU General Public License
18
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
*/
20
21
#include "includes.h"
22
#include "smbd/smbd.h"
23
#include "smbd/globals.h"
24
#include "../libcli/smb/smb_common.h"
25
#include "../libcli/security/security.h"
26
#include "auth.h"
27
#include "lib/param/loadparm.h"
28
#include "../lib/util/tevent_ntstatus.h"
29
#include "smbd/smbXsrv_session.h"
30
31
#undef DBGC_CLASS
32
0
#define DBGC_CLASS DBGC_SMB2
33
34
static struct tevent_req *smbd_smb2_tree_connect_send(TALLOC_CTX *mem_ctx,
35
          struct tevent_context *ev,
36
          struct smbd_smb2_request *smb2req,
37
          uint16_t in_flags,
38
          const char *in_path);
39
static NTSTATUS smbd_smb2_tree_connect_recv(struct tevent_req *req,
40
              uint8_t *out_share_type,
41
              uint32_t *out_share_flags,
42
              uint32_t *out_capabilities,
43
              uint32_t *out_maximal_access,
44
              uint32_t *out_tree_id,
45
              bool *disconnect);
46
47
static void smbd_smb2_request_tcon_done(struct tevent_req *subreq);
48
49
NTSTATUS smbd_smb2_request_process_tcon(struct smbd_smb2_request *req)
50
0
{
51
0
  struct smbXsrv_connection *xconn = req->xconn;
52
0
  const uint8_t *inbody;
53
0
  uint16_t in_flags;
54
0
  uint16_t in_path_offset;
55
0
  uint16_t in_path_length;
56
0
  DATA_BLOB in_path_buffer;
57
0
  char *in_path_string;
58
0
  size_t in_path_string_size;
59
0
  NTSTATUS status;
60
0
  bool ok;
61
0
  struct tevent_req *subreq;
62
63
0
  status = smbd_smb2_request_verify_sizes(req, 0x09);
64
0
  if (!NT_STATUS_IS_OK(status)) {
65
0
    return smbd_smb2_request_error(req, status);
66
0
  }
67
0
  inbody = SMBD_SMB2_IN_BODY_PTR(req);
68
69
0
  if (xconn->protocol >= PROTOCOL_SMB3_11) {
70
0
    in_flags = SVAL(inbody, 0x02);
71
0
  } else {
72
0
    in_flags = 0;
73
0
  }
74
0
  in_path_offset = SVAL(inbody, 0x04);
75
0
  in_path_length = SVAL(inbody, 0x06);
76
77
0
  if (in_path_offset != (SMB2_HDR_BODY + SMBD_SMB2_IN_BODY_LEN(req))) {
78
0
    return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
79
0
  }
80
81
0
  if (in_path_length > SMBD_SMB2_IN_DYN_LEN(req)) {
82
0
    return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
83
0
  }
84
85
0
  in_path_buffer.data = SMBD_SMB2_IN_DYN_PTR(req);
86
0
  in_path_buffer.length = in_path_length;
87
88
0
  ok = convert_string_talloc(req, CH_UTF16, CH_UNIX,
89
0
           in_path_buffer.data,
90
0
           in_path_buffer.length,
91
0
           &in_path_string,
92
0
           &in_path_string_size);
93
0
  if (!ok) {
94
0
    return smbd_smb2_request_error(req, NT_STATUS_ILLEGAL_CHARACTER);
95
0
  }
96
97
0
  if (in_path_buffer.length == 0) {
98
0
    in_path_string_size = 0;
99
0
  }
100
101
0
  if (strlen(in_path_string) != in_path_string_size) {
102
0
    return smbd_smb2_request_error(req, NT_STATUS_BAD_NETWORK_NAME);
103
0
  }
104
105
0
  subreq = smbd_smb2_tree_connect_send(req,
106
0
               req->sconn->ev_ctx,
107
0
               req,
108
0
               in_flags,
109
0
               in_path_string);
110
0
  if (subreq == NULL) {
111
0
    return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
112
0
  }
113
0
  tevent_req_set_callback(subreq, smbd_smb2_request_tcon_done, req);
114
115
  /*
116
   * Avoid sending a STATUS_PENDING message, it's very likely
117
   * the client won't expect that.
118
   */
119
0
  return smbd_smb2_request_pending_queue(req, subreq, 0);
120
0
}
121
122
static void smbd_smb2_request_tcon_done(struct tevent_req *subreq)
123
0
{
124
0
  struct smbd_smb2_request *req =
125
0
    tevent_req_callback_data(subreq,
126
0
    struct smbd_smb2_request);
127
0
  uint8_t *outhdr;
128
0
  DATA_BLOB outbody;
129
0
  uint8_t out_share_type = 0;
130
0
  uint32_t out_share_flags = 0;
131
0
  uint32_t out_capabilities = 0;
132
0
  uint32_t out_maximal_access = 0;
133
0
  uint32_t out_tree_id = 0;
134
0
  bool disconnect = false;
135
0
  NTSTATUS status;
136
0
  NTSTATUS error;
137
138
0
  status = smbd_smb2_tree_connect_recv(subreq,
139
0
               &out_share_type,
140
0
               &out_share_flags,
141
0
               &out_capabilities,
142
0
               &out_maximal_access,
143
0
               &out_tree_id,
144
0
               &disconnect);
145
0
  TALLOC_FREE(subreq);
146
0
  if (!NT_STATUS_IS_OK(status)) {
147
0
    if (disconnect) {
148
0
      smbd_server_connection_terminate(req->xconn,
149
0
               nt_errstr(status));
150
0
      return;
151
0
    }
152
0
    error = smbd_smb2_request_error(req, status);
153
0
    if (!NT_STATUS_IS_OK(error)) {
154
0
      smbd_server_connection_terminate(req->xconn,
155
0
               nt_errstr(error));
156
0
      return;
157
0
    }
158
0
    return;
159
0
  }
160
161
0
  outhdr = SMBD_SMB2_OUT_HDR_PTR(req);
162
163
0
  outbody = smbd_smb2_generate_outbody(req, 0x10);
164
0
  if (outbody.data == NULL) {
165
0
    error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
166
0
    if (!NT_STATUS_IS_OK(error)) {
167
0
      smbd_server_connection_terminate(req->xconn,
168
0
               nt_errstr(error));
169
0
      return;
170
0
    }
171
0
    return;
172
0
  }
173
174
0
  SIVAL(outhdr, SMB2_HDR_TID, out_tree_id);
175
176
0
  SSVAL(outbody.data, 0x00, 0x10); /* struct size */
177
0
  SCVAL(outbody.data, 0x02,
178
0
        out_share_type);      /* share type */
179
0
  SCVAL(outbody.data, 0x03, 0);    /* reserved */
180
0
  SIVAL(outbody.data, 0x04,
181
0
        out_share_flags);     /* share flags */
182
0
  SIVAL(outbody.data, 0x08,
183
0
        out_capabilities);    /* capabilities */
184
0
  SIVAL(outbody.data, 0x0C,
185
0
        out_maximal_access);    /* maximal access */
186
187
0
  error = smbd_smb2_request_done(req, outbody, NULL);
188
0
  if (!NT_STATUS_IS_OK(error)) {
189
0
    smbd_server_connection_terminate(req->xconn,
190
0
             nt_errstr(error));
191
0
    return;
192
0
  }
193
0
}
194
195
static NTSTATUS smbd_smb2_tree_connect(struct smbd_smb2_request *req,
196
               const char *in_path,
197
               uint8_t *out_share_type,
198
               uint32_t *out_share_flags,
199
               uint32_t *out_capabilities,
200
               uint32_t *out_maximal_access,
201
               uint32_t *out_tree_id,
202
               bool *disconnect)
203
0
{
204
0
  const struct loadparm_substitution *lp_sub =
205
0
    loadparm_s3_global_substitution();
206
0
  struct smbXsrv_connection *conn = req->xconn;
207
0
  struct smbXsrv_session *session = req->session;
208
0
  struct auth_session_info *session_info =
209
0
    session->global->auth_session_info;
210
0
  const char *share = in_path;
211
0
  char *service = NULL;
212
0
  int snum = -1;
213
0
  struct smbXsrv_tcon *tcon;
214
0
  NTTIME now = timeval_to_nttime(&req->request_time);
215
0
  connection_struct *compat_conn = NULL;
216
0
  NTSTATUS status;
217
0
  bool encryption_desired = req->session->global->encryption_flags & SMBXSRV_ENCRYPTION_DESIRED;
218
0
  bool encryption_required = req->session->global->encryption_flags & SMBXSRV_ENCRYPTION_REQUIRED;
219
0
  bool guest_session = false;
220
0
  bool require_signed_tcon = false;
221
0
  uint32_t session_global_id;
222
0
  char *share_name = NULL;
223
0
  uint8_t encryption_flags = 0;
224
225
0
  *disconnect = false;
226
227
0
  if (strncmp(share, "\\\\", 2) == 0) {
228
0
    const char *p = strchr(share+2, '\\');
229
0
    if (p) {
230
0
      share = p + 1;
231
0
    }
232
0
  }
233
234
0
  DEBUG(10,("smbd_smb2_tree_connect: path[%s] share[%s]\n",
235
0
      in_path, share));
236
237
0
  if (security_session_user_level(session_info, NULL) < SECURITY_USER) {
238
0
    guest_session = true;
239
0
  }
240
241
0
  if (conn->protocol >= PROTOCOL_SMB3_11 && !guest_session) {
242
0
    require_signed_tcon = true;
243
0
  }
244
245
0
  if (require_signed_tcon && !req->do_encryption && !req->do_signing) {
246
0
    DEBUG(1, ("smbd_smb2_tree_connect: reject request to share "
247
0
        "[%s] as '%s\\%s' without encryption or signing. "
248
0
        "Disconnecting.\n",
249
0
        share,
250
0
        req->session->global->auth_session_info->info->domain_name,
251
0
        req->session->global->auth_session_info->info->account_name));
252
0
    *disconnect = true;
253
0
    return NT_STATUS_ACCESS_DENIED;
254
0
  }
255
256
0
  service = talloc_strdup(talloc_tos(), share);
257
0
  if(!service) {
258
0
    return NT_STATUS_NO_MEMORY;
259
0
  }
260
261
0
  if (!strlower_m(service)) {
262
0
    DEBUG(2, ("strlower_m %s failed\n", service));
263
0
    return NT_STATUS_INVALID_PARAMETER;
264
0
  }
265
266
  /* TODO: do more things... */
267
0
  if (strequal(service,HOMES_NAME)) {
268
0
    if (session->homes_snum == -1) {
269
0
      DEBUG(2, ("[homes] share not available for "
270
0
        "user %s because it was not found "
271
0
        "or created at session setup "
272
0
        "time\n",
273
0
        session_info->unix_info->unix_name));
274
0
      return NT_STATUS_BAD_NETWORK_NAME;
275
0
    }
276
0
    snum = session->homes_snum;
277
0
  } else if ((session->homes_snum != -1)
278
0
                   && strequal(service,
279
0
      lp_servicename(talloc_tos(), lp_sub, session->homes_snum))) {
280
0
    snum = session->homes_snum;
281
0
  } else {
282
0
    snum = find_service(talloc_tos(), service, &service);
283
0
    if (!service) {
284
0
      return NT_STATUS_NO_MEMORY;
285
0
    }
286
0
  }
287
288
0
  if (snum < 0) {
289
0
    DEBUG(3,("smbd_smb2_tree_connect: couldn't find service %s\n",
290
0
       service));
291
0
    return NT_STATUS_BAD_NETWORK_NAME;
292
0
  }
293
294
  /* Handle non-DFS clients attempting connections to msdfs proxy */
295
0
  if (lp_host_msdfs()) {
296
0
    char *proxy = lp_msdfs_proxy(talloc_tos(), lp_sub, snum);
297
298
0
    if ((proxy != NULL) && (*proxy != '\0')) {
299
0
      DBG_NOTICE("refusing connection to dfs proxy share "
300
0
           "'%s' (pointing to %s)\n",
301
0
           service,
302
0
           proxy);
303
0
      TALLOC_FREE(proxy);
304
0
      return NT_STATUS_BAD_NETWORK_NAME;
305
0
    }
306
0
    TALLOC_FREE(proxy);
307
0
  }
308
309
0
  if ((lp_server_smb_encrypt(conn, snum) >= SMB_ENCRYPTION_DESIRED) &&
310
0
      (conn->smb2.server.cipher != 0))
311
0
  {
312
0
    encryption_desired = true;
313
0
  }
314
315
0
  if (lp_server_smb_encrypt(conn, snum) == SMB_ENCRYPTION_REQUIRED) {
316
0
    encryption_desired = true;
317
0
    encryption_required = true;
318
0
  }
319
320
0
  if (guest_session && encryption_required) {
321
0
    DEBUG(1,("reject guest as encryption is required for service %s\n",
322
0
       service));
323
0
    return NT_STATUS_ACCESS_DENIED;
324
0
  }
325
326
0
  if (conn->smb2.server.cipher == 0) {
327
0
    if (encryption_required) {
328
0
      DEBUG(1,("reject tcon with dialect[0x%04X] "
329
0
         "as encryption is required for service %s\n",
330
0
         conn->smb2.server.dialect, service));
331
0
      return NT_STATUS_ACCESS_DENIED;
332
0
    }
333
0
  }
334
335
0
  if (guest_session) {
336
    /* make sure we don't ask for optional encryption */
337
0
    encryption_desired = false;
338
0
  }
339
0
  if (encryption_desired) {
340
0
    encryption_flags |= SMBXSRV_ENCRYPTION_DESIRED;
341
0
  }
342
0
  if (encryption_required) {
343
0
    encryption_flags |= SMBXSRV_ENCRYPTION_REQUIRED;
344
0
  }
345
346
0
  session_global_id = req->session->global->session_global_id;
347
0
  share_name = lp_servicename(talloc_tos(), lp_sub, snum);
348
0
  if (share_name == NULL) {
349
0
    return NT_STATUS_NO_MEMORY;
350
0
  }
351
352
0
  if ((lp_max_connections(snum) > 0)
353
0
      && (count_current_connections(lp_const_servicename(snum), true) >=
354
0
    lp_max_connections(snum))) {
355
356
0
    DBG_WARNING("Max connections (%d) exceeded for [%s][%s]\n",
357
0
        lp_max_connections(snum),
358
0
        lp_const_servicename(snum), share_name);
359
0
    TALLOC_FREE(share_name);
360
0
    return NT_STATUS_INSUFFICIENT_RESOURCES;
361
0
  }
362
363
  /* create a new tcon as child of the session */
364
0
  status = smb2srv_tcon_create(req->session,
365
0
             session_global_id,
366
0
             encryption_flags,
367
0
             share_name,
368
0
             now, &tcon);
369
0
  TALLOC_FREE(share_name);
370
0
  if (!NT_STATUS_IS_OK(status)) {
371
0
    return status;
372
0
  }
373
374
0
  compat_conn = make_connection_smb2(req,
375
0
          tcon, snum,
376
0
          "???",
377
0
          &status);
378
0
  if (compat_conn == NULL) {
379
0
    TALLOC_FREE(tcon);
380
0
    return status;
381
0
  }
382
383
0
  tcon->compat = talloc_move(tcon, &compat_conn);
384
385
0
  tcon->status = NT_STATUS_OK;
386
387
0
  if (IS_PRINT(tcon->compat)) {
388
0
    *out_share_type = SMB2_SHARE_TYPE_PRINT;
389
0
  } else if (IS_IPC(tcon->compat)) {
390
0
    *out_share_type = SMB2_SHARE_TYPE_PIPE;
391
0
  } else {
392
0
    *out_share_type = SMB2_SHARE_TYPE_DISK;
393
0
  }
394
395
0
  *out_share_flags = 0;
396
397
0
  if (lp_msdfs_root(SNUM(tcon->compat)) && lp_host_msdfs()) {
398
0
    *out_share_flags |= (SMB2_SHAREFLAG_DFS|SMB2_SHAREFLAG_DFS_ROOT);
399
0
    *out_capabilities = SMB2_SHARE_CAP_DFS;
400
0
  } else {
401
0
    *out_capabilities = 0;
402
0
  }
403
404
0
  switch(lp_csc_policy(SNUM(tcon->compat))) {
405
0
  case CSC_POLICY_MANUAL:
406
0
    break;
407
0
  case CSC_POLICY_DOCUMENTS:
408
0
    *out_share_flags |= SMB2_SHAREFLAG_AUTO_CACHING;
409
0
    break;
410
0
  case CSC_POLICY_PROGRAMS:
411
0
    *out_share_flags |= SMB2_SHAREFLAG_VDO_CACHING;
412
0
    break;
413
0
  case CSC_POLICY_DISABLE:
414
0
    *out_share_flags |= SMB2_SHAREFLAG_NO_CACHING;
415
0
    break;
416
0
  default:
417
0
    break;
418
0
  }
419
420
0
  if (lp_hide_unreadable(SNUM(tcon->compat)) ||
421
0
      lp_hide_unwriteable_files(SNUM(tcon->compat))) {
422
0
    *out_share_flags |= SMB2_SHAREFLAG_ACCESS_BASED_DIRECTORY_ENUM;
423
0
  }
424
425
0
  if (encryption_desired) {
426
0
    *out_share_flags |= SMB2_SHAREFLAG_ENCRYPT_DATA;
427
0
  }
428
429
  /*
430
   * For disk shares we can change the client
431
   * behavior on a cluster...
432
   */
433
0
  if (conn->protocol >= PROTOCOL_SMB3_00 &&
434
0
      *out_share_type == SMB2_SHARE_TYPE_DISK)
435
0
  {
436
0
    bool persistent = false; /* persistent handles not implemented yet */
437
0
    bool cluster = lp_clustering();
438
0
    bool scaleout = cluster;
439
0
    bool witness = cluster && !lp_rpc_start_on_demand_helpers();
440
0
    bool asymmetric = false; /* shares are symmetric by default */
441
0
    bool announce;
442
443
    /*
444
     * In a ctdb cluster shares are continuously available,
445
     * but windows clients mix this with the global persistent
446
     * handles support.
447
     *
448
     * Persistent handles are requested if
449
     * SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY is present
450
     * even without SMB2_CAP_PERSISTENT_HANDLES.
451
     *
452
     * And SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY is
453
     * required for SMB2_SHARE_CAP_CLUSTER to have
454
     * an effect.
455
     *
456
     * So we better don't announce this by default
457
     * until we support persistent handles.
458
     */
459
0
    announce = lp_parm_bool(SNUM(tcon->compat),
460
0
          "smb3 share cap",
461
0
          "CONTINUOUS AVAILABILITY",
462
0
          persistent);
463
0
    if (announce) {
464
0
      *out_capabilities |= SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY;
465
0
    }
466
467
    /*
468
     * ctdb clusters are always scale out...
469
     */
470
0
    announce = lp_parm_bool(SNUM(tcon->compat),
471
0
          "smb3 share cap",
472
0
          "SCALE OUT",
473
0
          scaleout);
474
0
    if (announce) {
475
0
      *out_capabilities |= SMB2_SHARE_CAP_SCALEOUT;
476
0
    }
477
478
    /*
479
     * We support the witness service when ctdb is active
480
     */
481
0
    announce = lp_parm_bool(SNUM(tcon->compat),
482
0
          "smb3 share cap",
483
0
          "CLUSTER",
484
0
          witness);
485
0
    if (announce) {
486
0
      *out_capabilities |= SMB2_SHARE_CAP_CLUSTER;
487
0
    }
488
489
    /*
490
     * Shares in a ctdb cluster are symmetric by design.
491
     *
492
     * But it might be useful to let the client use
493
     * an isolated transport and witness registration for the
494
     * specific share.
495
     */
496
0
    if (conn->protocol >= PROTOCOL_SMB3_02) {
497
0
      announce = lp_parm_bool(SNUM(tcon->compat),
498
0
            "smb3 share cap",
499
0
            "ASYMMETRIC",
500
0
            asymmetric);
501
0
    }
502
0
    if (announce) {
503
0
      *out_capabilities |= SMB2_SHARE_CAP_ASYMMETRIC;
504
0
    }
505
0
  }
506
507
0
  *out_maximal_access = tcon->compat->share_access;
508
509
0
  *out_tree_id = tcon->global->tcon_wire_id;
510
0
  req->last_tid = tcon->global->tcon_wire_id;
511
512
0
  return NT_STATUS_OK;
513
0
}
514
515
struct smbd_smb2_tree_connect_state {
516
  const char *in_path;
517
  uint8_t out_share_type;
518
  uint32_t out_share_flags;
519
  uint32_t out_capabilities;
520
  uint32_t out_maximal_access;
521
  uint32_t out_tree_id;
522
  bool disconnect;
523
};
524
525
static struct tevent_req *smbd_smb2_tree_connect_send(TALLOC_CTX *mem_ctx,
526
          struct tevent_context *ev,
527
          struct smbd_smb2_request *smb2req,
528
          uint16_t in_flags,
529
          const char *in_path)
530
0
{
531
0
  struct tevent_req *req;
532
0
  struct smbd_smb2_tree_connect_state *state;
533
0
  NTSTATUS status;
534
535
0
  req = tevent_req_create(mem_ctx, &state,
536
0
        struct smbd_smb2_tree_connect_state);
537
0
  if (req == NULL) {
538
0
    return NULL;
539
0
  }
540
0
  state->in_path = in_path;
541
542
0
  status = smbd_smb2_tree_connect(smb2req,
543
0
          state->in_path,
544
0
          &state->out_share_type,
545
0
          &state->out_share_flags,
546
0
          &state->out_capabilities,
547
0
          &state->out_maximal_access,
548
0
          &state->out_tree_id,
549
0
          &state->disconnect);
550
0
  if (tevent_req_nterror(req, status)) {
551
0
    return tevent_req_post(req, ev);
552
0
  }
553
554
0
  tevent_req_done(req);
555
0
  return tevent_req_post(req, ev);
556
0
}
557
558
static NTSTATUS smbd_smb2_tree_connect_recv(struct tevent_req *req,
559
              uint8_t *out_share_type,
560
              uint32_t *out_share_flags,
561
              uint32_t *out_capabilities,
562
              uint32_t *out_maximal_access,
563
              uint32_t *out_tree_id,
564
              bool *disconnect)
565
0
{
566
0
  struct smbd_smb2_tree_connect_state *state =
567
0
    tevent_req_data(req,
568
0
    struct smbd_smb2_tree_connect_state);
569
0
  NTSTATUS status;
570
571
0
  if (tevent_req_is_nterror(req, &status)) {
572
0
    tevent_req_received(req);
573
0
    return status;
574
0
  }
575
576
0
  *out_share_type = state->out_share_type;
577
0
  *out_share_flags = state->out_share_flags;
578
0
  *out_capabilities = state->out_capabilities;
579
0
  *out_maximal_access = state->out_maximal_access;
580
0
  *out_tree_id = state->out_tree_id;
581
0
  *disconnect = state->disconnect;
582
583
0
  tevent_req_received(req);
584
0
  return NT_STATUS_OK;
585
0
}
586
587
static struct tevent_req *smbd_smb2_tdis_send(TALLOC_CTX *mem_ctx,
588
          struct tevent_context *ev,
589
          struct smbd_smb2_request *smb2req);
590
static NTSTATUS smbd_smb2_tdis_recv(struct tevent_req *req);
591
static void smbd_smb2_request_tdis_done(struct tevent_req *subreq);
592
593
NTSTATUS smbd_smb2_request_process_tdis(struct smbd_smb2_request *req)
594
0
{
595
0
  NTSTATUS status;
596
0
  struct tevent_req *subreq = NULL;
597
598
0
  status = smbd_smb2_request_verify_sizes(req, 0x04);
599
0
  if (!NT_STATUS_IS_OK(status)) {
600
0
    return smbd_smb2_request_error(req, status);
601
0
  }
602
603
0
  subreq = smbd_smb2_tdis_send(req, req->sconn->ev_ctx, req);
604
0
  if (subreq == NULL) {
605
0
    return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
606
0
  }
607
0
  tevent_req_set_callback(subreq, smbd_smb2_request_tdis_done, req);
608
609
  /*
610
   * Avoid sending a STATUS_PENDING message, it's very likely
611
   * the client won't expect that.
612
   */
613
0
  return smbd_smb2_request_pending_queue(req, subreq, 0);
614
0
}
615
616
static void smbd_smb2_request_tdis_done(struct tevent_req *subreq)
617
0
{
618
0
  struct smbd_smb2_request *smb2req =
619
0
    tevent_req_callback_data(subreq,
620
0
    struct smbd_smb2_request);
621
0
  DATA_BLOB outbody;
622
0
  NTSTATUS status;
623
0
  NTSTATUS error;
624
625
0
  status = smbd_smb2_tdis_recv(subreq);
626
0
  TALLOC_FREE(subreq);
627
0
  if (!NT_STATUS_IS_OK(status)) {
628
0
    error = smbd_smb2_request_error(smb2req, status);
629
0
    if (!NT_STATUS_IS_OK(error)) {
630
0
      smbd_server_connection_terminate(smb2req->xconn,
631
0
              nt_errstr(error));
632
0
      return;
633
0
    }
634
0
    return;
635
0
  }
636
637
0
  outbody = smbd_smb2_generate_outbody(smb2req, 0x04);
638
0
  if (outbody.data == NULL) {
639
0
    error = smbd_smb2_request_error(smb2req, NT_STATUS_NO_MEMORY);
640
0
    if (!NT_STATUS_IS_OK(error)) {
641
0
      smbd_server_connection_terminate(smb2req->xconn,
642
0
              nt_errstr(error));
643
0
      return;
644
0
    }
645
0
    return;
646
0
  }
647
648
0
  SSVAL(outbody.data, 0x00, 0x04); /* struct size */
649
0
  SSVAL(outbody.data, 0x02, 0);    /* reserved */
650
651
0
  error = smbd_smb2_request_done(smb2req, outbody, NULL);
652
0
  if (!NT_STATUS_IS_OK(error)) {
653
0
    smbd_server_connection_terminate(smb2req->xconn,
654
0
            nt_errstr(error));
655
0
    return;
656
0
  }
657
0
}
658
659
struct smbd_smb2_tdis_state {
660
  struct smbd_smb2_request *smb2req;
661
  struct tevent_queue *wait_queue;
662
};
663
664
static void smbd_smb2_tdis_wait_done(struct tevent_req *subreq);
665
666
struct check_for_lease_break_fsp_cmp_state {
667
  struct smbXsrv_tcon *tcon;
668
};
669
670
static bool check_for_lease_break_fsp_cmp_fn(struct files_struct *fsp,
671
               void *private_data)
672
0
{
673
0
  struct check_for_lease_break_fsp_cmp_state *state =
674
0
    (struct check_for_lease_break_fsp_cmp_state *)private_data;
675
676
0
  return (fsp->conn == state->tcon->compat);
677
0
}
678
679
static struct tevent_req *smbd_smb2_tdis_send(TALLOC_CTX *mem_ctx,
680
          struct tevent_context *ev,
681
          struct smbd_smb2_request *smb2req)
682
0
{
683
0
  struct tevent_req *req;
684
0
  struct smbd_smb2_tdis_state *state;
685
0
  struct tevent_req *subreq;
686
0
  struct smbXsrv_connection *xconn = NULL;
687
0
  struct check_for_lease_break_fsp_cmp_state fsp_cmp_state;
688
689
0
  req = tevent_req_create(mem_ctx, &state,
690
0
      struct smbd_smb2_tdis_state);
691
0
  if (req == NULL) {
692
0
    return NULL;
693
0
  }
694
0
  state->smb2req = smb2req;
695
696
0
  state->wait_queue = tevent_queue_create(state, "tdis_wait_queue");
697
0
  if (tevent_req_nomem(state->wait_queue, req)) {
698
0
    return tevent_req_post(req, ev);
699
0
  }
700
701
  /*
702
   * Make sure that no new request will be able to use this tcon.
703
   */
704
0
  smb2req->tcon->status = NT_STATUS_NETWORK_NAME_DELETED;
705
706
0
  xconn = smb2req->xconn->client->connections;
707
0
  for (; xconn != NULL; xconn = xconn->next) {
708
0
    struct smbd_smb2_request *preq;
709
710
0
    for (preq = xconn->smb2.requests; preq != NULL; preq = preq->next) {
711
0
      if (preq == smb2req) {
712
        /* Can't cancel current request. */
713
0
        continue;
714
0
      }
715
0
      if (preq->tcon != smb2req->tcon) {
716
        /* Request on different tcon. */
717
0
        continue;
718
0
      }
719
720
0
      if (preq->subreq != NULL) {
721
0
        tevent_req_cancel(preq->subreq);
722
0
      }
723
724
      /*
725
       * Now wait until the request is finished.
726
       *
727
       * We don't set a callback, as we just want to block the
728
       * wait queue and the talloc_free() of the request will
729
       * remove the item from the wait queue.
730
       */
731
0
      subreq = tevent_queue_wait_send(preq, ev, state->wait_queue);
732
0
      if (tevent_req_nomem(subreq, req)) {
733
0
        return tevent_req_post(req, ev);
734
0
      }
735
0
    }
736
0
  }
737
738
0
  fsp_cmp_state = (struct check_for_lease_break_fsp_cmp_state) {
739
0
    .tcon = smb2req->tcon,
740
0
  };
741
742
0
  smbXsrv_wait_for_handle_lease_break(req,
743
0
              ev,
744
0
              smb2req->xconn->client,
745
0
              state->wait_queue,
746
0
              check_for_lease_break_fsp_cmp_fn,
747
0
              &fsp_cmp_state);
748
0
  if (!tevent_req_is_in_progress(req)) {
749
0
    return tevent_req_post(req, ev);
750
0
  }
751
752
  /*
753
   * Now we add our own waiter to the end of the queue,
754
   * this way we get notified when all pending requests are finished
755
   * and send to the socket.
756
   */
757
0
  subreq = tevent_queue_wait_send(state, ev, state->wait_queue);
758
0
  if (tevent_req_nomem(subreq, req)) {
759
0
    return tevent_req_post(req, ev);
760
0
  }
761
0
  tevent_req_set_callback(subreq, smbd_smb2_tdis_wait_done, req);
762
763
0
  return req;
764
0
}
765
766
static void smbd_smb2_tdis_wait_done(struct tevent_req *subreq)
767
0
{
768
0
  struct tevent_req *req = tevent_req_callback_data(
769
0
    subreq, struct tevent_req);
770
0
  struct smbd_smb2_tdis_state *state = tevent_req_data(
771
0
    req, struct smbd_smb2_tdis_state);
772
0
  NTSTATUS status;
773
774
0
  tevent_queue_wait_recv(subreq);
775
0
  TALLOC_FREE(subreq);
776
777
  /*
778
   * As we've been awoken, we may have changed
779
   * uid in the meantime. Ensure we're still
780
   * root (SMB2_OP_TDIS has .as_root = true).
781
   */
782
0
  change_to_root_user();
783
784
0
  status = smbXsrv_tcon_disconnect(state->smb2req->tcon,
785
0
           state->smb2req->tcon->compat->vuid);
786
0
  if (tevent_req_nterror(req, status)) {
787
0
    return;
788
0
  }
789
790
  /* We did tear down the tcon. */
791
0
  TALLOC_FREE(state->smb2req->tcon);
792
0
  tevent_req_done(req);
793
0
}
794
795
static NTSTATUS smbd_smb2_tdis_recv(struct tevent_req *req)
796
0
{
797
0
  return tevent_req_simple_recv_ntstatus(req);
798
0
}