Coverage Report

Created: 2025-12-31 06:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/source3/smbd/smb2_break.c
Line
Count
Source
1
/*
2
   Unix SMB/CIFS implementation.
3
   Core SMB2 server
4
5
   Copyright (C) Stefan Metzmacher 2009
6
   Copyright (C) Jeremy Allison 2010
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 "smbd/smbd.h"
24
#include "smbd/globals.h"
25
#include "../libcli/smb/smb_common.h"
26
#include "../lib/util/tevent_ntstatus.h"
27
#include "locking/leases_db.h"
28
29
#undef DBGC_CLASS
30
0
#define DBGC_CLASS DBGC_SMB2
31
32
static NTSTATUS smbd_smb2_request_process_lease_break(
33
  struct smbd_smb2_request *req);
34
35
static struct tevent_req *smbd_smb2_oplock_break_send(TALLOC_CTX *mem_ctx,
36
                  struct tevent_context *ev,
37
                  struct smbd_smb2_request *smb2req,
38
                  struct files_struct *in_fsp,
39
                  uint8_t in_oplock_level);
40
static NTSTATUS smbd_smb2_oplock_break_recv(struct tevent_req *req,
41
              uint8_t *out_oplock_level);
42
43
static void smbd_smb2_request_oplock_break_done(struct tevent_req *subreq);
44
NTSTATUS smbd_smb2_request_process_break(struct smbd_smb2_request *req)
45
0
{
46
0
  NTSTATUS status;
47
0
  const uint8_t *inbody;
48
0
  uint8_t in_oplock_level;
49
0
  uint64_t in_file_id_persistent;
50
0
  uint64_t in_file_id_volatile;
51
0
  struct files_struct *in_fsp;
52
0
  struct tevent_req *subreq;
53
54
0
  status = smbd_smb2_request_verify_sizes(req, 0x18);
55
0
  if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
56
    /*
57
     * Retry as a lease break
58
     */
59
0
    return smbd_smb2_request_process_lease_break(req);
60
0
  }
61
0
  if (!NT_STATUS_IS_OK(status)) {
62
0
    return smbd_smb2_request_error(req, status);
63
0
  }
64
0
  inbody = SMBD_SMB2_IN_BODY_PTR(req);
65
66
0
  in_oplock_level   = CVAL(inbody, 0x02);
67
68
  /* 0x03 1 bytes reserved */
69
  /* 0x04 4 bytes reserved */
70
0
  in_file_id_persistent   = BVAL(inbody, 0x08);
71
0
  in_file_id_volatile   = BVAL(inbody, 0x10);
72
73
0
  in_fsp = file_fsp_smb2(req, in_file_id_persistent, in_file_id_volatile);
74
0
  if (in_fsp == NULL) {
75
0
    return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED);
76
0
  }
77
78
  /* Are we awaiting a break message ? */
79
0
  if (in_fsp->oplock_timeout == NULL) {
80
0
    return smbd_smb2_request_error(
81
0
      req, NT_STATUS_INVALID_OPLOCK_PROTOCOL);
82
0
  }
83
84
0
  if (in_oplock_level != SMB2_OPLOCK_LEVEL_NONE &&
85
0
      in_oplock_level != SMB2_OPLOCK_LEVEL_II) {
86
0
    return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
87
0
  }
88
89
0
  subreq = smbd_smb2_oplock_break_send(req, req->sconn->ev_ctx,
90
0
               req, in_fsp, in_oplock_level);
91
0
  if (subreq == NULL) {
92
0
    return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
93
0
  }
94
0
  tevent_req_set_callback(subreq, smbd_smb2_request_oplock_break_done, req);
95
96
0
  return smbd_smb2_request_pending_queue(req, subreq, 500);
97
0
}
98
99
static void smbd_smb2_request_oplock_break_done(struct tevent_req *subreq)
100
0
{
101
0
  struct smbd_smb2_request *req = tevent_req_callback_data(subreq,
102
0
          struct smbd_smb2_request);
103
0
  const uint8_t *inbody;
104
0
  uint64_t in_file_id_persistent;
105
0
  uint64_t in_file_id_volatile;
106
0
  uint8_t out_oplock_level = 0;
107
0
  DATA_BLOB outbody;
108
0
  NTSTATUS status;
109
0
  NTSTATUS error; /* transport error */
110
111
0
  status = smbd_smb2_oplock_break_recv(subreq, &out_oplock_level);
112
0
  TALLOC_FREE(subreq);
113
0
  if (!NT_STATUS_IS_OK(status)) {
114
0
    error = smbd_smb2_request_error(req, status);
115
0
    if (!NT_STATUS_IS_OK(error)) {
116
0
      smbd_server_connection_terminate(req->xconn,
117
0
               nt_errstr(error));
118
0
      return;
119
0
    }
120
0
    return;
121
0
  }
122
123
0
  inbody = SMBD_SMB2_IN_BODY_PTR(req);
124
125
0
  in_file_id_persistent = BVAL(inbody, 0x08);
126
0
  in_file_id_volatile = BVAL(inbody, 0x10);
127
128
0
  outbody = smbd_smb2_generate_outbody(req, 0x18);
129
0
  if (outbody.data == NULL) {
130
0
    error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
131
0
    if (!NT_STATUS_IS_OK(error)) {
132
0
      smbd_server_connection_terminate(req->xconn,
133
0
               nt_errstr(error));
134
0
      return;
135
0
    }
136
0
    return;
137
0
  }
138
139
0
  SSVAL(outbody.data, 0x00, 0x18); /* struct size */
140
0
  SCVAL(outbody.data, 0x02,
141
0
        out_oplock_level);    /* SMB2 oplock level */
142
0
  SCVAL(outbody.data, 0x03, 0);    /* reserved */
143
0
  SIVAL(outbody.data, 0x04, 0);    /* reserved */
144
0
  SBVAL(outbody.data, 0x08,
145
0
        in_file_id_persistent);   /* file id (persistent) */
146
0
  SBVAL(outbody.data, 0x10,
147
0
        in_file_id_volatile);   /* file id (volatile) */
148
149
0
  error = smbd_smb2_request_done(req, outbody, NULL);
150
0
  if (!NT_STATUS_IS_OK(error)) {
151
0
    smbd_server_connection_terminate(req->xconn,
152
0
             nt_errstr(error));
153
0
    return;
154
0
  }
155
0
}
156
157
struct smbd_smb2_oplock_break_state {
158
  struct smbd_smb2_request *smb2req;
159
  uint8_t out_oplock_level; /* SMB2 oplock level. */
160
};
161
162
static struct tevent_req *smbd_smb2_oplock_break_send(TALLOC_CTX *mem_ctx,
163
                  struct tevent_context *ev,
164
                  struct smbd_smb2_request *smb2req,
165
                  struct files_struct *fsp,
166
                  uint8_t in_oplock_level)
167
0
{
168
0
  struct tevent_req *req;
169
0
  struct smbd_smb2_oplock_break_state *state;
170
0
  struct smb_request *smbreq;
171
0
  int oplocklevel = map_smb2_oplock_levels_to_samba(in_oplock_level);
172
0
  bool break_to_none = (oplocklevel == NO_OPLOCK);
173
0
  bool result;
174
175
0
  req = tevent_req_create(mem_ctx, &state,
176
0
        struct smbd_smb2_oplock_break_state);
177
0
  if (req == NULL) {
178
0
    return NULL;
179
0
  }
180
0
  state->smb2req = smb2req;
181
0
  state->out_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
182
183
0
  DEBUG(10,("smbd_smb2_oplock_break_send: %s - %s, "
184
0
      "samba level %d\n",
185
0
      fsp_str_dbg(fsp), fsp_fnum_dbg(fsp),
186
0
      oplocklevel));
187
188
0
  smbreq = smbd_smb2_fake_smb_request(smb2req, fsp);
189
0
  if (tevent_req_nomem(smbreq, req)) {
190
0
    return tevent_req_post(req, ev);
191
0
  }
192
193
0
  DEBUG(5,("smbd_smb2_oplock_break_send: got SMB2 oplock break (%u) from client "
194
0
    "for file %s, %s\n",
195
0
    (unsigned int)in_oplock_level,
196
0
    fsp_str_dbg(fsp),
197
0
    fsp_fnum_dbg(fsp)));
198
199
0
  if ((fsp->sent_oplock_break == BREAK_TO_NONE_SENT) ||
200
0
      (break_to_none)) {
201
0
    result = remove_oplock(fsp);
202
0
    state->out_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
203
0
  } else {
204
0
    result = downgrade_oplock(fsp);
205
0
    state->out_oplock_level = SMB2_OPLOCK_LEVEL_II;
206
0
  }
207
208
0
  if (!result) {
209
0
    DEBUG(0, ("smbd_smb2_oplock_break_send: error in removing "
210
0
      "oplock on file %s\n", fsp_str_dbg(fsp)));
211
    /* Hmmm. Is this panic justified? */
212
0
    smb_panic("internal tdb error");
213
0
  }
214
215
0
  tevent_req_done(req);
216
0
  return tevent_req_post(req, ev);
217
0
}
218
219
static NTSTATUS smbd_smb2_oplock_break_recv(struct tevent_req *req,
220
              uint8_t *out_oplock_level)
221
0
{
222
0
  NTSTATUS status;
223
0
  struct smbd_smb2_oplock_break_state *state =
224
0
    tevent_req_data(req,
225
0
    struct smbd_smb2_oplock_break_state);
226
227
0
  if (tevent_req_is_nterror(req, &status)) {
228
0
    tevent_req_received(req);
229
0
    return status;
230
0
  }
231
232
0
  *out_oplock_level = state->out_oplock_level;
233
234
0
  tevent_req_received(req);
235
0
  return NT_STATUS_OK;
236
0
}
237
238
static void smbd_smb2_request_lease_break_done(struct tevent_req *subreq);
239
240
static struct tevent_req *smbd_smb2_lease_break_send(
241
  TALLOC_CTX *mem_ctx, struct tevent_context *ev,
242
  struct smbd_smb2_request *smb2_req, struct smb2_lease_key in_lease_key,
243
  uint32_t in_lease_state);
244
static NTSTATUS smbd_smb2_lease_break_recv(struct tevent_req *req,
245
             uint32_t *out_lease_state);
246
247
248
static NTSTATUS smbd_smb2_request_process_lease_break(
249
  struct smbd_smb2_request *req)
250
0
{
251
0
  NTSTATUS status;
252
0
  const uint8_t *inbody;
253
0
  struct smb2_lease_key in_lease_key;
254
0
  uint32_t in_lease_state;
255
0
  struct tevent_req *subreq;
256
257
0
  status = smbd_smb2_request_verify_sizes(req, 0x24);
258
0
  if (!NT_STATUS_IS_OK(status)) {
259
0
    return smbd_smb2_request_error(req, status);
260
0
  }
261
262
0
  inbody = SMBD_SMB2_IN_BODY_PTR(req);
263
264
0
  in_lease_key.data[0] = BVAL(inbody, 8);
265
0
  in_lease_key.data[1] = BVAL(inbody, 16);
266
0
  in_lease_state = IVAL(inbody, 24);
267
268
0
  subreq = smbd_smb2_lease_break_send(req, req->sconn->ev_ctx, req,
269
0
              in_lease_key, in_lease_state);
270
0
  if (subreq == NULL) {
271
0
    return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
272
0
  }
273
0
  tevent_req_set_callback(subreq, smbd_smb2_request_lease_break_done, req);
274
275
0
  return smbd_smb2_request_pending_queue(req, subreq, 500);
276
0
}
277
278
static void smbd_smb2_request_lease_break_done(struct tevent_req *subreq)
279
0
{
280
0
  struct smbd_smb2_request *req = tevent_req_callback_data(
281
0
    subreq, struct smbd_smb2_request);
282
0
  const uint8_t *inbody;
283
0
  struct smb2_lease_key in_lease_key;
284
0
  uint32_t out_lease_state = 0;
285
0
  DATA_BLOB outbody;
286
0
  NTSTATUS status;
287
0
  NTSTATUS error; /* transport error */
288
289
0
  status = smbd_smb2_lease_break_recv(subreq, &out_lease_state);
290
0
  TALLOC_FREE(subreq);
291
0
  if (!NT_STATUS_IS_OK(status)) {
292
0
    error = smbd_smb2_request_error(req, status);
293
0
    if (!NT_STATUS_IS_OK(error)) {
294
0
      smbd_server_connection_terminate(req->xconn,
295
0
               nt_errstr(error));
296
0
      return;
297
0
    }
298
0
    return;
299
0
  }
300
301
0
  inbody = SMBD_SMB2_IN_BODY_PTR(req);
302
303
0
  in_lease_key.data[0] = BVAL(inbody, 8);
304
0
  in_lease_key.data[1] = BVAL(inbody, 16);
305
306
0
  outbody = smbd_smb2_generate_outbody(req, 0x24);
307
0
  if (outbody.data == NULL) {
308
0
    error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
309
0
    if (!NT_STATUS_IS_OK(error)) {
310
0
      smbd_server_connection_terminate(req->xconn,
311
0
               nt_errstr(error));
312
0
      return;
313
0
    }
314
0
    return;
315
0
  }
316
317
0
  SSVAL(outbody.data, 0x00, 0x24); /* struct size */
318
0
  SSVAL(outbody.data, 0x02, 0);    /* reserved */
319
0
  SIVAL(outbody.data, 0x04, 0);    /* flags, must be 0 */
320
0
  SBVAL(outbody.data, 0x08, in_lease_key.data[0]);
321
0
  SBVAL(outbody.data, 0x10, in_lease_key.data[1]);
322
0
  SIVAL(outbody.data, 0x18, out_lease_state);
323
0
  SBVAL(outbody.data, 0x1c, 0);           /* leaseduration, must be 0 */
324
325
0
  error = smbd_smb2_request_done(req, outbody, NULL);
326
0
  if (!NT_STATUS_IS_OK(error)) {
327
0
    smbd_server_connection_terminate(req->xconn,
328
0
             nt_errstr(error));
329
0
    return;
330
0
  }
331
0
}
332
333
struct smbd_smb2_lease_break_state {
334
  uint32_t lease_state;
335
};
336
337
struct lease_lookup_state {
338
  TALLOC_CTX *mem_ctx;
339
  /* Return parameters. */
340
  uint32_t num_file_ids;
341
  struct file_id *ids;
342
  NTSTATUS status;
343
};
344
345
static void lease_parser(
346
  uint32_t num_files,
347
  const struct leases_db_file *files,
348
  void *private_data)
349
0
{
350
0
  struct lease_lookup_state *lls =
351
0
    (struct lease_lookup_state *)private_data;
352
353
0
  lls->status = NT_STATUS_OK;
354
0
  lls->num_file_ids = num_files;
355
0
  lls->status = leases_db_copy_file_ids(lls->mem_ctx,
356
0
        num_files,
357
0
        files,
358
0
        &lls->ids);
359
0
}
360
361
static struct tevent_req *smbd_smb2_lease_break_send(
362
  TALLOC_CTX *mem_ctx, struct tevent_context *ev,
363
  struct smbd_smb2_request *smb2_req, struct smb2_lease_key in_lease_key,
364
  uint32_t in_lease_state)
365
0
{
366
0
  struct tevent_req *req;
367
0
  struct smbd_smb2_lease_break_state *state;
368
0
  struct lease_lookup_state lls = {.mem_ctx = mem_ctx};
369
0
  NTSTATUS status;
370
371
0
  req = tevent_req_create(mem_ctx, &state,
372
0
        struct smbd_smb2_lease_break_state);
373
0
  if (req == NULL) {
374
0
    return NULL;
375
0
  }
376
0
  state->lease_state = in_lease_state;
377
378
  /* Find any file ids with this lease key. */
379
0
  status = leases_db_parse(&smb2_req->xconn->smb2.client.guid,
380
0
         &in_lease_key,
381
0
         lease_parser,
382
0
         &lls);
383
384
0
  if (!NT_STATUS_IS_OK(status)) {
385
0
    if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
386
0
      status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
387
0
      DEBUG(10, ("No record for lease key found\n"));
388
0
    }
389
0
    tevent_req_nterror(req, status);
390
0
    return tevent_req_post(req, ev);
391
0
  }
392
393
0
  if (tevent_req_nterror(req, lls.status)) {
394
0
    return tevent_req_post(req, ev);
395
0
  }
396
397
0
  if (lls.num_file_ids == 0) {
398
0
    tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
399
0
    return tevent_req_post(req, ev);
400
0
  }
401
402
0
  status = downgrade_lease(smb2_req->xconn->client,
403
0
        lls.num_file_ids,
404
0
        lls.ids,
405
0
        &in_lease_key,
406
0
        in_lease_state);
407
408
0
  if (NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_BREAK_IN_PROGRESS)) {
409
0
    tevent_req_done(req);
410
0
    return tevent_req_post(req, ev);
411
0
  }
412
0
  if (tevent_req_nterror(req, status)) {
413
0
    DEBUG(10, ("downgrade_lease returned %s\n",
414
0
         nt_errstr(status)));
415
0
    return tevent_req_post(req, ev);
416
0
  }
417
418
0
  tevent_req_done(req);
419
0
  return tevent_req_post(req, ev);
420
0
}
421
422
static NTSTATUS smbd_smb2_lease_break_recv(struct tevent_req *req,
423
             uint32_t *out_lease_state)
424
0
{
425
0
  struct smbd_smb2_lease_break_state *state = tevent_req_data(
426
0
    req, struct smbd_smb2_lease_break_state);
427
0
  NTSTATUS status;
428
429
0
  if (tevent_req_is_nterror(req, &status)) {
430
0
    return status;
431
0
  }
432
0
  *out_lease_state = state->lease_state;
433
0
  return NT_STATUS_OK;
434
0
}
435
436
/*********************************************************
437
 Create and send an asynchronous
438
 SMB2 OPLOCK_BREAK_NOTIFICATION.
439
*********************************************************/
440
441
void send_break_message_smb2(files_struct *fsp,
442
           uint32_t break_from,
443
           uint32_t break_to)
444
0
{
445
0
  struct smbXsrv_client *client =
446
0
    fsp->conn->sconn->client;
447
0
  NTSTATUS status;
448
449
0
  if (!NT_STATUS_IS_OK(fsp->op->status)) {
450
0
    DBG_DEBUG("skip oplock break for file %s, %s, "
451
0
        "smb2 level %u fsp status=%s\n",
452
0
        fsp_str_dbg(fsp),
453
0
        fsp_fnum_dbg(fsp),
454
0
        (unsigned int)break_to,
455
0
        nt_errstr(fsp->op->status));
456
0
    return;
457
0
  }
458
459
0
  DBG_DEBUG("sending oplock break "
460
0
      "for file %s, %s, smb2 level %u\n",
461
0
      fsp_str_dbg(fsp),
462
0
      fsp_fnum_dbg(fsp),
463
0
      (unsigned int)break_to);
464
465
0
  if (fsp->oplock_type == LEASE_OPLOCK) {
466
0
    uint32_t break_flags = 0;
467
0
    uint16_t new_epoch;
468
469
0
    if (fsp->lease->lease.lease_state != SMB2_LEASE_NONE) {
470
0
      break_flags = SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED;
471
0
    }
472
473
0
    if (fsp->lease->lease.lease_version > 1) {
474
0
      new_epoch = fsp->lease->lease.lease_epoch;
475
0
    } else {
476
0
      new_epoch = 0;
477
0
    }
478
479
0
    status = smbd_smb2_send_lease_break(client, new_epoch, break_flags,
480
0
                &fsp->lease->lease.lease_key,
481
0
                break_from, break_to);
482
0
  } else {
483
0
    uint8_t smb2_oplock_level;
484
0
    smb2_oplock_level = (break_to & SMB2_LEASE_READ) ?
485
0
      SMB2_OPLOCK_LEVEL_II : SMB2_OPLOCK_LEVEL_NONE;
486
0
    status = smbd_smb2_send_oplock_break(client,
487
0
                 fsp->op,
488
0
                 smb2_oplock_level);
489
0
  }
490
0
  if (!NT_STATUS_IS_OK(status)) {
491
    smbd_server_disconnect_client(client,
492
0
                nt_errstr(status));
493
0
    return;
494
0
  }
495
0
}