Coverage Report

Created: 2026-04-01 06:26

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