Coverage Report

Created: 2026-06-07 07:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/source3/smbd/smbXsrv_open.c
Line
Count
Source
1
/*
2
   Unix SMB/CIFS implementation.
3
4
   Copyright (C) Stefan Metzmacher 2012
5
   Copyright (C) Michael Adam 2012
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 "system/filesys.h"
23
#include "lib/util/server_id.h"
24
#include "smbd/smbd.h"
25
#include "smbd/globals.h"
26
#include "smbXsrv_open.h"
27
#include "dbwrap/dbwrap.h"
28
#include "dbwrap/dbwrap_rbt.h"
29
#include "dbwrap/dbwrap_open.h"
30
#include "../libcli/security/security.h"
31
#include "messages.h"
32
#include "lib/util/util_tdb.h"
33
#include "librpc/gen_ndr/ndr_smbXsrv.h"
34
#include "serverid.h"
35
#include "source3/include/util_tdb.h"
36
#include "lib/util/idtree_random.h"
37
#include "lib/util/time_basic.h"
38
#include "../librpc/gen_ndr/ndr_smb2_lease_struct.h"
39
40
struct smbXsrv_open_table {
41
  struct {
42
    struct idr_context *idr;
43
    uint32_t lowest_id;
44
    uint32_t highest_id;
45
    uint32_t max_opens;
46
    uint32_t num_opens;
47
  } local;
48
  struct {
49
    struct db_context *db_ctx;
50
  } global;
51
};
52
53
static struct db_context *smbXsrv_open_global_db_ctx = NULL;
54
55
NTSTATUS smbXsrv_open_global_init(void)
56
0
{
57
0
  char *global_path = NULL;
58
0
  struct db_context *db_ctx = NULL;
59
60
0
  if (smbXsrv_open_global_db_ctx != NULL) {
61
0
    return NT_STATUS_OK;
62
0
  }
63
64
0
  global_path = lock_path(talloc_tos(), "smbXsrv_open_global.tdb");
65
0
  if (global_path == NULL) {
66
0
    return NT_STATUS_NO_MEMORY;
67
0
  }
68
69
0
  db_ctx = db_open(NULL, global_path,
70
0
       SMBD_VOLATILE_TDB_HASH_SIZE,
71
0
       SMBD_VOLATILE_TDB_FLAGS,
72
0
       O_RDWR | O_CREAT, 0600,
73
0
       DBWRAP_LOCK_ORDER_1,
74
0
       DBWRAP_FLAG_NONE);
75
0
  TALLOC_FREE(global_path);
76
0
  if (db_ctx == NULL) {
77
0
    NTSTATUS status = map_nt_error_from_unix_common(errno);
78
0
    return status;
79
0
  }
80
81
0
  smbXsrv_open_global_db_ctx = db_ctx;
82
83
0
  return NT_STATUS_OK;
84
0
}
85
86
/*
87
 * NOTE:
88
 * We need to store the keys in big endian so that dbwrap_rbt's memcmp
89
 * has the same result as integer comparison between the uint32_t
90
 * values.
91
 *
92
 * TODO: implement string based key
93
 */
94
95
struct smbXsrv_open_global_key_buf { uint8_t buf[sizeof(uint32_t)]; };
96
97
static TDB_DATA smbXsrv_open_global_id_to_key(
98
  uint32_t id, struct smbXsrv_open_global_key_buf *key_buf)
99
0
{
100
0
  RSIVAL(key_buf->buf, 0, id);
101
102
0
  return (TDB_DATA) {
103
0
    .dptr = key_buf->buf,
104
0
    .dsize = sizeof(key_buf->buf),
105
0
  };
106
0
}
107
108
static NTSTATUS smbXsrv_open_table_init(struct smbXsrv_connection *conn,
109
          uint32_t lowest_id,
110
          uint32_t highest_id,
111
          uint32_t max_opens)
112
0
{
113
0
  struct smbXsrv_client *client = conn->client;
114
0
  struct smbXsrv_open_table *table;
115
0
  NTSTATUS status;
116
0
  uint64_t max_range;
117
118
0
  if (lowest_id > highest_id) {
119
0
    return NT_STATUS_INTERNAL_ERROR;
120
0
  }
121
122
0
  max_range = highest_id;
123
0
  max_range -= lowest_id;
124
0
  max_range += 1;
125
126
0
  if (max_opens > max_range) {
127
0
    return NT_STATUS_INTERNAL_ERROR;
128
0
  }
129
130
0
  table = talloc_zero(client, struct smbXsrv_open_table);
131
0
  if (table == NULL) {
132
0
    return NT_STATUS_NO_MEMORY;
133
0
  }
134
135
0
  table->local.idr = idr_init(table);
136
0
  if (table->local.idr == NULL) {
137
0
    TALLOC_FREE(table);
138
0
    return NT_STATUS_NO_MEMORY;
139
0
  }
140
0
  table->local.lowest_id = lowest_id;
141
0
  table->local.highest_id = highest_id;
142
0
  table->local.max_opens = max_opens;
143
144
0
  status = smbXsrv_open_global_init();
145
0
  if (!NT_STATUS_IS_OK(status)) {
146
0
    TALLOC_FREE(table);
147
0
    return status;
148
0
  }
149
150
0
  table->global.db_ctx = smbXsrv_open_global_db_ctx;
151
152
0
  client->open_table = table;
153
0
  return NT_STATUS_OK;
154
0
}
155
156
static NTSTATUS smbXsrv_open_local_lookup(struct smbXsrv_open_table *table,
157
            uint32_t open_local_id,
158
            uint32_t open_global_id,
159
            NTTIME now,
160
            struct smbXsrv_open **_open)
161
0
{
162
0
  struct smbXsrv_open *op = NULL;
163
164
0
  *_open = NULL;
165
166
0
  if (open_local_id == 0) {
167
0
    return NT_STATUS_FILE_CLOSED;
168
0
  }
169
170
0
  if (table == NULL) {
171
    /* this might happen before the end of negprot */
172
0
    return NT_STATUS_FILE_CLOSED;
173
0
  }
174
175
0
  if (table->local.idr == NULL) {
176
0
    return NT_STATUS_INTERNAL_ERROR;
177
0
  }
178
179
0
  op = idr_find(table->local.idr, open_local_id);
180
0
  if (op == NULL) {
181
0
    return NT_STATUS_FILE_CLOSED;
182
0
  }
183
184
0
  if (open_global_id == 0) {
185
    /* make the global check a no-op for SMB1 */
186
0
    open_global_id = op->global->open_global_id;
187
0
  }
188
189
0
  if (op->global->open_global_id != open_global_id) {
190
0
    return NT_STATUS_FILE_CLOSED;
191
0
  }
192
193
0
  if (now != 0) {
194
0
    op->idle_time = now;
195
0
  }
196
197
0
  *_open = op;
198
0
  return NT_STATUS_OK;
199
0
}
200
201
static NTSTATUS smbXsrv_open_global_parse_record(
202
  TALLOC_CTX *mem_ctx,
203
  TDB_DATA key,
204
  TDB_DATA val,
205
  struct smbXsrv_open_global0 **global)
206
0
{
207
0
  DATA_BLOB blob = data_blob_const(val.dptr, val.dsize);
208
0
  struct smbXsrv_open_globalB global_blob;
209
0
  enum ndr_err_code ndr_err;
210
0
  NTSTATUS status;
211
0
  TALLOC_CTX *frame = talloc_stackframe();
212
213
0
  ndr_err = ndr_pull_struct_blob(&blob, frame, &global_blob,
214
0
      (ndr_pull_flags_fn_t)ndr_pull_smbXsrv_open_globalB);
215
0
  if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
216
0
    DEBUG(1,("Invalid record in smbXsrv_open_global.tdb:"
217
0
       "key '%s' ndr_pull_struct_blob - %s\n",
218
0
       tdb_data_dbg(key),
219
0
       ndr_errstr(ndr_err)));
220
0
    status = ndr_map_error2ntstatus(ndr_err);
221
0
    goto done;
222
0
  }
223
224
0
  DBG_DEBUG("\n");
225
0
  if (CHECK_DEBUGLVL(10)) {
226
0
    NDR_PRINT_DEBUG(smbXsrv_open_globalB, &global_blob);
227
0
  }
228
229
0
  if (global_blob.version != SMBXSRV_VERSION_0) {
230
0
    status = NT_STATUS_INTERNAL_DB_CORRUPTION;
231
0
    DEBUG(1,("Invalid record in smbXsrv_open_global.tdb:"
232
0
       "key '%s' unsupported version - %d - %s\n",
233
0
       tdb_data_dbg(key),
234
0
       (int)global_blob.version,
235
0
       nt_errstr(status)));
236
0
    goto done;
237
0
  }
238
239
0
  if (global_blob.info.info0 == NULL) {
240
0
    status = NT_STATUS_INTERNAL_DB_CORRUPTION;
241
0
    DEBUG(1,("Invalid record in smbXsrv_open_global.tdb:"
242
0
       "key '%s' info0 NULL pointer - %s\n",
243
0
       tdb_data_dbg(key),
244
0
       nt_errstr(status)));
245
0
    goto done;
246
0
  }
247
248
0
  *global = talloc_move(mem_ctx, &global_blob.info.info0);
249
0
  status = NT_STATUS_OK;
250
0
done:
251
0
  talloc_free(frame);
252
0
  return status;
253
0
}
254
255
static NTSTATUS smbXsrv_open_global_verify_record(
256
  TDB_DATA key,
257
  TDB_DATA val,
258
  TALLOC_CTX *mem_ctx,
259
  struct smbXsrv_open_global0 **_global0)
260
0
{
261
0
  struct smbXsrv_open_global0 *global0 = NULL;
262
0
  struct server_id_buf buf;
263
0
  NTSTATUS status;
264
265
0
  if (val.dsize == 0) {
266
0
    return NT_STATUS_NOT_FOUND;
267
0
  }
268
269
0
  status = smbXsrv_open_global_parse_record(mem_ctx, key, val, &global0);
270
0
  if (!NT_STATUS_IS_OK(status)) {
271
0
    DBG_WARNING("smbXsrv_open_global_parse_record for %s failed: "
272
0
          "%s\n",
273
0
          tdb_data_dbg(key),
274
0
          nt_errstr(status));
275
0
    return status;
276
0
  }
277
0
  *_global0 = global0;
278
279
0
  if (server_id_is_disconnected(&global0->server_id)) {
280
0
    return NT_STATUS_REMOTE_DISCONNECT;
281
0
  }
282
0
  if (serverid_exists(&global0->server_id)) {
283
0
    return NT_STATUS_OBJECTID_EXISTS;
284
0
  }
285
286
0
  DBG_WARNING("smbd %s did not clean up record %s\n",
287
0
        server_id_str_buf(global0->server_id, &buf),
288
0
        tdb_data_dbg(key));
289
290
0
  return NT_STATUS_FATAL_APP_EXIT;
291
0
}
292
293
struct smbXsrv_open_global_lookup_state {
294
  TALLOC_CTX *mem_ctx;
295
  struct smbXsrv_open_global0 *global;
296
  NTSTATUS status;
297
};
298
299
static void smbXsrv_open_global_lookup_fn(struct db_record *rec,
300
            TDB_DATA val,
301
            void *private_data)
302
0
{
303
0
  struct smbXsrv_open_global_lookup_state *state = private_data;
304
0
  TDB_DATA key = dbwrap_record_get_key(rec);
305
306
0
  if (val.dsize == 0) {
307
    /* Likely a ctdb tombstone record */
308
0
    state->status = NT_STATUS_NOT_FOUND;
309
0
    return;
310
0
  }
311
312
0
  state->status = smbXsrv_open_global_parse_record(state->mem_ctx,
313
0
               key,
314
0
               val,
315
0
               &state->global);
316
0
}
317
318
static NTSTATUS smbXsrv_open_global_lookup(
319
      struct smbXsrv_open_table *table,
320
      TALLOC_CTX *mem_ctx,
321
      uint32_t open_global_id,
322
      const struct smbXsrv_open_global0 **_global)
323
0
{
324
0
  struct smbXsrv_open_global_key_buf key_buf;
325
0
  TDB_DATA key = smbXsrv_open_global_id_to_key(open_global_id, &key_buf);
326
0
  struct smbXsrv_open_global_lookup_state state = {
327
0
    .mem_ctx = mem_ctx,
328
0
  };
329
0
  NTSTATUS status;
330
331
0
  status = dbwrap_do_locked(table->global.db_ctx,
332
0
          key,
333
0
          smbXsrv_open_global_lookup_fn,
334
0
          &state);
335
0
  if (!NT_STATUS_IS_OK(status)) {
336
0
    DBG_ERR("dbwrap_do_locked failed\n");
337
0
    return status;
338
0
  }
339
0
  if (NT_STATUS_EQUAL(state.status, NT_STATUS_NOT_FOUND)) {
340
0
    DBG_DEBUG("smbXsrv_open record not found\n");
341
0
    return state.status;
342
0
  }
343
0
  if (!NT_STATUS_IS_OK(state.status)) {
344
0
    DBG_ERR("smbXsrv_open_global_lookup_fn failed\n");
345
0
    return state.status;
346
0
  }
347
0
  *_global = state.global;
348
0
  return NT_STATUS_OK;
349
0
}
350
351
static NTSTATUS smbXsrv_open_global_store(
352
  struct db_record *rec,
353
  TDB_DATA key,
354
  TDB_DATA oldval,
355
  struct smbXsrv_open_global0 *global)
356
0
{
357
0
  struct smbXsrv_open_globalB global_blob;
358
0
  DATA_BLOB blob = data_blob_null;
359
0
  TDB_DATA val = { .dptr = NULL, };
360
0
  NTSTATUS status;
361
0
  enum ndr_err_code ndr_err;
362
363
  /*
364
   * TODO: if we use other versions than '0'
365
   * we would add glue code here, that would be able to
366
   * store the information in the old format.
367
   */
368
369
0
  global_blob = (struct smbXsrv_open_globalB) {
370
0
    .version = smbXsrv_version_global_current(),
371
0
  };
372
373
0
  if (oldval.dsize >= 8) {
374
0
    global_blob.seqnum = IVAL(oldval.dptr, 4);
375
0
  }
376
0
  global_blob.seqnum += 1;
377
0
  global_blob.info.info0 = global;
378
379
0
  ndr_err = ndr_push_struct_blob(&blob, talloc_tos(), &global_blob,
380
0
      (ndr_push_flags_fn_t)ndr_push_smbXsrv_open_globalB);
381
0
  if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
382
0
    DBG_WARNING("key '%s' ndr_push - %s\n",
383
0
          tdb_data_dbg(key),
384
0
          ndr_map_error2string(ndr_err));
385
0
    return ndr_map_error2ntstatus(ndr_err);
386
0
  }
387
388
0
  val = make_tdb_data(blob.data, blob.length);
389
0
  status = dbwrap_record_store(rec, val, TDB_REPLACE);
390
0
  TALLOC_FREE(blob.data);
391
0
  if (!NT_STATUS_IS_OK(status)) {
392
0
    DBG_WARNING("key '%s' store - %s\n",
393
0
          tdb_data_dbg(key),
394
0
          nt_errstr(status));
395
0
    return status;
396
0
  }
397
398
0
  if (CHECK_DEBUGLVL(10)) {
399
0
    DBG_DEBUG("key '%s' stored\n", tdb_data_dbg(key));
400
0
    NDR_PRINT_DEBUG(smbXsrv_open_globalB, &global_blob);
401
0
  }
402
403
0
  return NT_STATUS_OK;
404
0
}
405
406
struct smbXsrv_open_global_allocate_state {
407
  uint32_t id;
408
  struct smbXsrv_open_global0 *global;
409
  NTSTATUS status;
410
};
411
412
static void smbXsrv_open_global_allocate_fn(
413
  struct db_record *rec, TDB_DATA oldval, void *private_data)
414
0
{
415
0
  struct smbXsrv_open_global_allocate_state *state = private_data;
416
0
  struct smbXsrv_open_global0 *global = state->global;
417
0
  struct smbXsrv_open_global0 *tmp_global0 = NULL;
418
0
  TDB_DATA key = dbwrap_record_get_key(rec);
419
420
0
  state->status = smbXsrv_open_global_verify_record(
421
0
    key, oldval, talloc_tos(), &tmp_global0);
422
423
0
  if (!NT_STATUS_EQUAL(state->status, NT_STATUS_NOT_FOUND)) {
424
    /*
425
     * Found an existing record
426
     */
427
0
    TALLOC_FREE(tmp_global0);
428
0
    state->status = NT_STATUS_RETRY;
429
0
    return;
430
0
  }
431
432
0
  if (NT_STATUS_EQUAL(state->status, NT_STATUS_NOT_FOUND)) {
433
    /*
434
     * Found an empty slot
435
     */
436
0
    global->open_global_id = state->id;
437
0
    global->open_persistent_id = state->id;
438
439
0
    state->status = smbXsrv_open_global_store(
440
0
      rec, key, (TDB_DATA) { .dsize = 0, }, state->global);
441
0
    if (!NT_STATUS_IS_OK(state->status)) {
442
0
      DBG_WARNING("smbXsrv_open_global_store() for "
443
0
            "id %"PRIu32" failed: %s\n",
444
0
            state->id,
445
0
            nt_errstr(state->status));
446
0
    }
447
0
    return;
448
0
  }
449
450
0
  if (NT_STATUS_EQUAL(state->status, NT_STATUS_FATAL_APP_EXIT)) {
451
0
    NTSTATUS status;
452
453
0
    TALLOC_FREE(tmp_global0);
454
455
    /*
456
     * smbd crashed
457
     */
458
0
    status = dbwrap_record_delete(rec);
459
0
    if (!NT_STATUS_IS_OK(status)) {
460
0
      DBG_WARNING("dbwrap_record_delete() failed "
461
0
            "for record %"PRIu32": %s\n",
462
0
            state->id,
463
0
            nt_errstr(status));
464
0
      state->status = NT_STATUS_INTERNAL_DB_CORRUPTION;
465
0
      return;
466
0
    }
467
0
    return;
468
0
  }
469
0
}
470
471
static NTSTATUS smbXsrv_open_global_allocate(
472
  struct db_context *db, struct smbXsrv_open_global0 *global)
473
0
{
474
0
  struct smbXsrv_open_global_allocate_state state = {
475
0
    .global = global,
476
0
  };
477
0
  uint32_t i;
478
0
  uint32_t last_free = 0;
479
0
  const uint32_t min_tries = 3;
480
481
  /*
482
   * Here we just randomly try the whole 32-bit space
483
   *
484
   * We use just 32-bit, because we want to reuse the
485
   * ID for SRVSVC.
486
   */
487
0
  for (i = 0; i < UINT32_MAX; i++) {
488
0
    struct smbXsrv_open_global_key_buf key_buf;
489
0
    TDB_DATA key;
490
0
    NTSTATUS status;
491
492
0
    if (i >= min_tries && last_free != 0) {
493
0
      state.id = last_free;
494
0
    } else {
495
0
      generate_nonce_buffer(
496
0
        (uint8_t *)&state.id, sizeof(state.id));
497
0
      state.id = MAX(state.id, 1);
498
0
      state.id = MIN(state.id, UINT32_MAX-1);
499
0
    }
500
501
0
    key = smbXsrv_open_global_id_to_key(state.id, &key_buf);
502
503
0
    status = dbwrap_do_locked(
504
0
      db, key, smbXsrv_open_global_allocate_fn, &state);
505
506
0
    if (!NT_STATUS_IS_OK(status)) {
507
0
      DBG_WARNING("dbwrap_do_locked() failed: %s\n",
508
0
            nt_errstr(status));
509
0
      return NT_STATUS_INTERNAL_DB_ERROR;
510
0
    }
511
512
0
    if (NT_STATUS_IS_OK(state.status)) {
513
      /*
514
       * Found an empty slot, done.
515
       */
516
0
      DBG_DEBUG("Found slot %"PRIu32"\n", state.id);
517
0
      return NT_STATUS_OK;
518
0
    }
519
520
0
    if (NT_STATUS_EQUAL(state.status, NT_STATUS_FATAL_APP_EXIT)) {
521
522
0
      if ((i < min_tries) && (last_free == 0)) {
523
        /*
524
         * Remember "id" as free but also try
525
         * others to not recycle ids too
526
         * quickly.
527
         */
528
0
        last_free = state.id;
529
0
      }
530
0
      continue;
531
0
    }
532
533
0
    if (NT_STATUS_EQUAL(state.status, NT_STATUS_RETRY)) {
534
      /*
535
       * Normal collision, try next
536
       */
537
0
      DBG_DEBUG("Found record for id %"PRIu32"\n",
538
0
          state.id);
539
0
      continue;
540
0
    }
541
542
0
    DBG_WARNING("smbXsrv_open_global_allocate_fn() failed: %s\n",
543
0
          nt_errstr(state.status));
544
0
    return state.status;
545
0
  }
546
547
  /* should not be reached */
548
0
  return NT_STATUS_INTERNAL_ERROR;
549
0
}
550
551
static int smbXsrv_open_destructor(struct smbXsrv_open *op)
552
0
{
553
0
  NTSTATUS status;
554
555
0
  status = smbXsrv_open_close(op, 0);
556
0
  if (!NT_STATUS_IS_OK(status)) {
557
0
    DEBUG(0, ("smbXsrv_open_destructor: "
558
0
        "smbXsrv_open_close() failed - %s\n",
559
0
        nt_errstr(status)));
560
0
  }
561
562
0
  TALLOC_FREE(op->global);
563
564
0
  return 0;
565
0
}
566
567
NTSTATUS smbXsrv_open_create(struct smbXsrv_connection *conn,
568
           struct smbXsrv_session *session,
569
           struct smbXsrv_tcon *tcon,
570
           NTTIME now,
571
           struct smbXsrv_open **_open)
572
0
{
573
0
  struct smbXsrv_open_table *table = conn->client->open_table;
574
0
  struct auth_session_info *session_info = NULL;
575
0
  struct smbXsrv_open *op = NULL;
576
0
  struct smbXsrv_open_global0 *global = NULL;
577
0
  NTSTATUS status;
578
0
  struct dom_sid *current_sid = NULL;
579
0
  struct security_token *current_token = NULL;
580
0
  int local_id;
581
582
0
  session_info = session->global->auth_session_info;
583
0
  if (session_info == NULL) {
584
0
    return NT_STATUS_INVALID_HANDLE;
585
0
  }
586
0
  current_token = session_info->security_token;
587
588
0
  if ((current_token == NULL) ||
589
0
      (current_token->num_sids <= PRIMARY_USER_SID_INDEX)) {
590
0
    return NT_STATUS_INVALID_HANDLE;
591
0
  }
592
0
  current_sid = &current_token->sids[PRIMARY_USER_SID_INDEX];
593
594
0
  if (table->local.num_opens >= table->local.max_opens) {
595
0
    return NT_STATUS_INSUFFICIENT_RESOURCES;
596
0
  }
597
598
0
  op = talloc_zero(table, struct smbXsrv_open);
599
0
  if (op == NULL) {
600
0
    return NT_STATUS_NO_MEMORY;
601
0
  }
602
0
  op->table = table;
603
0
  op->status = NT_STATUS_OK; /* TODO: start with INTERNAL_ERROR */
604
0
  op->idle_time = now;
605
0
  op->session = session;
606
0
  op->tcon = tcon;
607
608
0
  global = talloc_zero(op, struct smbXsrv_open_global0);
609
0
  if (global == NULL) {
610
0
    TALLOC_FREE(op);
611
0
    return NT_STATUS_NO_MEMORY;
612
0
  }
613
0
  op->global = global;
614
615
  /*
616
   * We mark every slot as invalid using 0xFF.
617
   * Valid values are masked with 0xF.
618
   */
619
0
  memset(global->lock_sequence_array, 0xFF,
620
0
         sizeof(global->lock_sequence_array));
621
622
0
  local_id = idr_get_new_random(
623
0
    table->local.idr,
624
0
    op,
625
0
    table->local.lowest_id,
626
0
    table->local.highest_id);
627
0
  if (local_id == -1) {
628
0
    TALLOC_FREE(op);
629
0
    return NT_STATUS_INSUFFICIENT_RESOURCES;
630
0
  }
631
0
  op->local_id = local_id;
632
633
0
  global->open_volatile_id = op->local_id;
634
635
0
  global->server_id = messaging_server_id(conn->client->msg_ctx);
636
0
  global->open_time = now;
637
0
  global->open_owner = *current_sid;
638
0
  global->session_global_id = session->global->session_global_id;
639
0
  global->tcon_global_id = tcon->global->tcon_global_id;
640
0
  if (conn->protocol >= PROTOCOL_SMB2_10) {
641
0
    global->client_guid = conn->smb2.client.guid;
642
0
  }
643
644
0
  status = smbXsrv_open_global_allocate(table->global.db_ctx,
645
0
                global);
646
0
  if (!NT_STATUS_IS_OK(status)) {
647
0
    int ret = idr_remove(table->local.idr, local_id);
648
0
    SMB_ASSERT(ret == 0);
649
650
0
    DBG_WARNING("smbXsrv_open_global_allocate() failed: %s\n",
651
0
          nt_errstr(status));
652
0
    TALLOC_FREE(op);
653
0
    return status;
654
0
  }
655
656
0
  table->local.num_opens += 1;
657
0
  talloc_set_destructor(op, smbXsrv_open_destructor);
658
659
0
  if (CHECK_DEBUGLVL(10)) {
660
0
    struct smbXsrv_openB open_blob = {
661
0
      .version = SMBXSRV_VERSION_0,
662
0
      .info.info0 = op,
663
0
    };
664
665
0
    DEBUG(10,("smbXsrv_open_create: global_id (0x%08x) stored\n",
666
0
       op->global->open_global_id));
667
0
    NDR_PRINT_DEBUG(smbXsrv_openB, &open_blob);
668
0
  }
669
670
0
  *_open = op;
671
0
  return NT_STATUS_OK;
672
0
}
673
674
struct smbXsrv_open_replay_cache_key_buf {
675
  uint8_t buf[SMBXSRV_OPEN_REPLAY_CACHE_KEY_FIXED_SIZE];
676
};
677
678
static TDB_DATA smbXsrv_open_replay_cache_key(
679
      const struct GUID *client_guid,
680
      const struct GUID *create_guid,
681
      struct smbXsrv_open_replay_cache_key_buf *key_buf)
682
0
{
683
0
  struct smbXsrv_open_replay_cache_key key = {
684
0
    .client_guid = *client_guid,
685
0
    .create_guid = *create_guid,
686
0
  };
687
0
  DATA_BLOB blob = {
688
0
    .data = key_buf->buf,
689
0
    .length = sizeof(key_buf->buf)
690
0
  };
691
0
  enum ndr_err_code ndr_err;
692
0
  NTSTATUS status;
693
694
0
  ndr_err = ndr_push_struct_into_fixed_blob(&blob, &key,
695
0
    (ndr_push_flags_fn_t)ndr_push_smbXsrv_open_replay_cache_key);
696
0
  if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
697
0
    status = ndr_map_error2ntstatus(ndr_err);
698
0
    DBG_ERR("ndr_push_struct_into_fixed_blob failed: %s\n",
699
0
      nt_errstr(status));
700
0
    smb_panic("ndr_push_struct_into_fixed_blob failed\n");
701
0
    return tdb_null;
702
0
  }
703
704
0
  if (CHECK_DEBUGLVL(10)) {
705
0
    NDR_PRINT_DEBUG(smbXsrv_open_replay_cache_key, &key);
706
0
  }
707
708
0
  return make_tdb_data(blob.data, blob.length);
709
0
}
710
711
static NTSTATUS smbXsrv_open_set_replay_cache(struct smbXsrv_open *op)
712
0
{
713
0
  struct GUID_txt_buf buf;
714
0
  struct db_context *db = op->table->global.db_ctx;
715
0
  struct smbXsrv_open_global_key_buf open_key_buf;
716
0
  NTSTATUS status;
717
0
  struct smbXsrv_open_replay_cache_key_buf key_buf;
718
0
  TDB_DATA key;
719
0
  TDB_DATA val;
720
721
0
  if (!(op->flags & SMBXSRV_OPEN_NEED_REPLAY_CACHE)) {
722
0
    return NT_STATUS_OK;
723
0
  }
724
725
0
  if (op->flags & SMBXSRV_OPEN_HAVE_REPLAY_CACHE) {
726
0
    return NT_STATUS_OK;
727
0
  }
728
729
0
  key = smbXsrv_open_replay_cache_key(&op->global->client_guid,
730
0
              &op->global->create_guid,
731
0
              &key_buf);
732
733
0
  val = smbXsrv_open_global_id_to_key(op->global->open_global_id,
734
0
              &open_key_buf);
735
736
0
  DBG_DEBUG("Replay Cache: store create_guid [%s]\n",
737
0
      GUID_buf_string(&op->global->create_guid, &buf));
738
739
0
  status = dbwrap_store(db, key, val, TDB_REPLACE);
740
741
0
  if (NT_STATUS_IS_OK(status)) {
742
0
    op->flags |= SMBXSRV_OPEN_HAVE_REPLAY_CACHE;
743
0
    op->flags &= ~SMBXSRV_OPEN_NEED_REPLAY_CACHE;
744
0
  }
745
746
0
  return status;
747
0
}
748
749
NTSTATUS smbXsrv_open_purge_replay_cache(struct smbXsrv_client *client,
750
           const struct GUID *create_guid)
751
0
{
752
0
  struct GUID_txt_buf buf;
753
0
  struct smbXsrv_open_replay_cache_key_buf key_buf;
754
0
  TDB_DATA key;
755
0
  NTSTATUS status;
756
757
0
  DBG_DEBUG("Replay Cache: purge create_guid [%s]\n",
758
0
      GUID_buf_string(create_guid, &buf));
759
760
0
  if (client->open_table == NULL) {
761
0
    return NT_STATUS_OK;
762
0
  }
763
764
0
  key = smbXsrv_open_replay_cache_key(&client->global->client_guid,
765
0
              create_guid,
766
0
              &key_buf);
767
768
0
  status = dbwrap_purge(client->open_table->global.db_ctx, key);
769
0
  return status;
770
0
}
771
772
static NTSTATUS smbXsrv_open_clear_replay_cache(struct smbXsrv_open *op)
773
0
{
774
0
  struct GUID *create_guid;
775
0
  struct GUID_txt_buf buf;
776
0
  struct smbXsrv_open_replay_cache_key_buf key_buf;
777
0
  TDB_DATA key;
778
0
  struct db_context *db;
779
0
  NTSTATUS status;
780
781
0
  if (op->table == NULL) {
782
0
    return NT_STATUS_OK;
783
0
  }
784
785
0
  db = op->table->global.db_ctx;
786
787
0
  if (!(op->flags & SMBXSRV_OPEN_HAVE_REPLAY_CACHE)) {
788
0
    return NT_STATUS_OK;
789
0
  }
790
791
0
  create_guid = &op->global->create_guid;
792
0
  if (GUID_all_zero(create_guid)) {
793
0
    return NT_STATUS_OK;
794
0
  }
795
796
0
  DBG_DEBUG("Replay Cache: clear create_guid [%s]\n",
797
0
      GUID_buf_string(create_guid, &buf));
798
799
0
  key = smbXsrv_open_replay_cache_key(&op->global->client_guid,
800
0
              create_guid,
801
0
              &key_buf);
802
803
0
  status = dbwrap_purge(db, key);
804
805
0
  if (NT_STATUS_IS_OK(status)) {
806
0
    op->flags &= ~SMBXSRV_OPEN_HAVE_REPLAY_CACHE;
807
0
  }
808
809
0
  return status;
810
0
}
811
812
struct smbXsrv_open_update_state {
813
  struct smbXsrv_open_global0 *global;
814
  NTSTATUS status;
815
};
816
817
static void smbXsrv_open_update_fn(
818
  struct db_record *rec, TDB_DATA oldval, void *private_data)
819
0
{
820
0
  struct smbXsrv_open_update_state *state = private_data;
821
0
  TDB_DATA key = dbwrap_record_get_key(rec);
822
823
0
  state->status = smbXsrv_open_global_store(
824
0
    rec, key, oldval, state->global);
825
0
}
826
827
NTSTATUS smbXsrv_open_update(struct smbXsrv_open *op)
828
0
{
829
0
  struct smbXsrv_open_update_state state = { .global = op->global, };
830
0
  struct smbXsrv_open_table *table = op->table;
831
0
  struct smbXsrv_open_global_key_buf key_buf;
832
0
  TDB_DATA key = smbXsrv_open_global_id_to_key(
833
0
    op->global->open_global_id, &key_buf);
834
0
  NTSTATUS status;
835
836
0
  status = dbwrap_do_locked(
837
0
    table->global.db_ctx, key, smbXsrv_open_update_fn, &state);
838
0
  if (!NT_STATUS_IS_OK(status)) {
839
0
    DBG_WARNING("global_id (0x%08x) dbwrap_do_locked failed: %s\n",
840
0
          op->global->open_global_id,
841
0
          nt_errstr(status));
842
0
    return NT_STATUS_INTERNAL_DB_ERROR;
843
0
  }
844
845
0
  if (!NT_STATUS_IS_OK(state.status)) {
846
0
    DBG_WARNING("global_id (0x%08x) smbXsrv_open_global_store "
847
0
          "failed: %s\n",
848
0
          op->global->open_global_id,
849
0
          nt_errstr(state.status));
850
0
    return state.status;
851
0
  }
852
853
0
  status = smbXsrv_open_set_replay_cache(op);
854
0
  if (!NT_STATUS_IS_OK(status)) {
855
0
    DBG_ERR("smbXsrv_open_set_replay_cache failed: %s\n",
856
0
      nt_errstr(status));
857
0
    return status;
858
0
  }
859
860
0
  if (CHECK_DEBUGLVL(10)) {
861
0
    struct smbXsrv_openB open_blob = {
862
0
      .version = SMBXSRV_VERSION_0,
863
0
      .info.info0 = op,
864
0
    };
865
866
0
    DEBUG(10,("smbXsrv_open_update: global_id (0x%08x) stored\n",
867
0
        op->global->open_global_id));
868
0
    NDR_PRINT_DEBUG(smbXsrv_openB, &open_blob);
869
0
  }
870
871
0
  return NT_STATUS_OK;
872
0
}
873
874
struct smbXsrv_open_close_state {
875
  struct smbXsrv_open *op;
876
  NTSTATUS status;
877
};
878
879
static void smbXsrv_open_close_fn(
880
  struct db_record *rec, TDB_DATA oldval, void *private_data)
881
0
{
882
0
  struct smbXsrv_open_close_state *state = private_data;
883
0
  struct smbXsrv_open_global0 *global = state->op->global;
884
0
  TDB_DATA key = dbwrap_record_get_key(rec);
885
886
0
  if (global->durable) {
887
    /*
888
     * Durable open -- we need to update the global part
889
     * instead of deleting it
890
     */
891
0
    state->status = smbXsrv_open_global_store(
892
0
      rec, key, oldval, global);
893
0
    if (!NT_STATUS_IS_OK(state->status)) {
894
0
      DBG_WARNING("failed to store global key '%s': %s\n",
895
0
            tdb_data_dbg(key),
896
0
            nt_errstr(state->status));
897
0
      return;
898
0
    }
899
900
0
    if (CHECK_DEBUGLVL(10)) {
901
0
      struct smbXsrv_openB open_blob = {
902
0
        .version = SMBXSRV_VERSION_0,
903
0
        .info.info0 = state->op,
904
0
      };
905
906
0
      DBG_DEBUG("(0x%08x) stored disconnect\n",
907
0
          global->open_global_id);
908
0
      NDR_PRINT_DEBUG(smbXsrv_openB, &open_blob);
909
0
    }
910
0
    return;
911
0
  }
912
913
0
  state->status = dbwrap_record_delete(rec);
914
0
  if (!NT_STATUS_IS_OK(state->status)) {
915
0
    DBG_WARNING("failed to delete global key '%s': %s\n",
916
0
          tdb_data_dbg(key),
917
0
          nt_errstr(state->status));
918
0
  }
919
0
}
920
921
NTSTATUS smbXsrv_open_close(struct smbXsrv_open *op, NTTIME now)
922
0
{
923
0
  struct smbXsrv_open_close_state state = { .op = op, };
924
0
  struct smbXsrv_open_global0 *global = op->global;
925
0
  struct smbXsrv_open_table *table;
926
0
  NTSTATUS status;
927
0
  NTSTATUS error = NT_STATUS_OK;
928
0
  struct smbXsrv_open_global_key_buf key_buf;
929
0
  TDB_DATA key = smbXsrv_open_global_id_to_key(
930
0
    global->open_global_id, &key_buf);
931
0
  int ret;
932
933
0
  if (op->table == NULL) {
934
0
    return error;
935
0
  }
936
937
0
  table = op->table;
938
0
  op->table = NULL;
939
940
0
  op->status = NT_STATUS_FILE_CLOSED;
941
0
  global->disconnect_time = now;
942
0
  global->session_global_id = 0;
943
0
  global->tcon_global_id = 0;
944
0
  server_id_set_disconnected(&global->server_id);
945
946
0
  status = dbwrap_do_locked(
947
0
    table->global.db_ctx, key, smbXsrv_open_close_fn, &state);
948
0
  if (!NT_STATUS_IS_OK(status)) {
949
0
    DBG_WARNING("dbwrap_do_locked() for %s failed: %s\n",
950
0
          tdb_data_dbg(key),
951
0
          nt_errstr(status));
952
0
    error = status;
953
0
  } else if (!NT_STATUS_IS_OK(state.status)) {
954
0
    DBG_WARNING("smbXsrv_open_close_fn() for %s failed: %s\n",
955
0
          tdb_data_dbg(key),
956
0
          nt_errstr(state.status));
957
0
    error = state.status;
958
0
  }
959
960
0
  if (!op->global->durable) {
961
    /*
962
     * If this is not a Durable Handle, remove the Replay-Cache entry.
963
     */
964
0
    error = smbXsrv_open_clear_replay_cache(op);
965
0
    if (!NT_STATUS_IS_OK(error)) {
966
0
      DBG_ERR("smbXsrv_open_clear_replay_cache failed: %s\n",
967
0
        nt_errstr(error));
968
0
    }
969
0
  }
970
971
0
  ret = idr_remove(table->local.idr, op->local_id);
972
0
  SMB_ASSERT(ret == 0);
973
974
0
  table->local.num_opens -= 1;
975
976
0
  if (op->compat) {
977
0
    op->compat->op = NULL;
978
0
    file_free(NULL, op->compat);
979
0
    op->compat = NULL;
980
0
  }
981
982
0
  return error;
983
0
}
984
985
NTSTATUS smb1srv_open_table_init(struct smbXsrv_connection *conn)
986
0
{
987
0
  uint32_t max_opens;
988
989
  /*
990
   * Allow a range from 1..65534.
991
   *
992
   * With real_max_open_files possible ids,
993
   * truncated to the SMB1 limit of 16-bit.
994
   *
995
   * 0 and 0xFFFF are no valid ids.
996
   */
997
0
  max_opens = conn->client->sconn->real_max_open_files;
998
0
  max_opens = MIN(max_opens, UINT16_MAX - 1);
999
1000
0
  return smbXsrv_open_table_init(conn, 1, UINT16_MAX - 1, max_opens);
1001
0
}
1002
1003
NTSTATUS smb1srv_open_lookup(struct smbXsrv_connection *conn,
1004
           uint16_t fnum, NTTIME now,
1005
           struct smbXsrv_open **_open)
1006
0
{
1007
0
  struct smbXsrv_open_table *table = conn->client->open_table;
1008
0
  uint32_t local_id = fnum;
1009
0
  uint32_t global_id = 0;
1010
1011
0
  return smbXsrv_open_local_lookup(table, local_id, global_id, now, _open);
1012
0
}
1013
1014
NTSTATUS smb2srv_open_table_init(struct smbXsrv_connection *conn)
1015
0
{
1016
0
  uint32_t max_opens;
1017
0
  uint32_t highest_id;
1018
1019
  /*
1020
   * Allow a range from 1..4294967294.
1021
   *
1022
   * With real_max_open_files possible ids,
1023
   * truncated to 16-bit (the same as SMB1 for now).
1024
   *
1025
   * 0 and 0xFFFFFFFF are no valid ids.
1026
   *
1027
   * The usage of conn->sconn->real_max_open_files
1028
   * is the reason that we use one open table per
1029
   * transport connection (as we still have a 1:1 mapping
1030
   * between process and transport connection).
1031
   */
1032
0
  max_opens = conn->client->sconn->real_max_open_files;
1033
0
  max_opens = MIN(max_opens, UINT16_MAX - 1);
1034
1035
  /*
1036
   * idtree uses "int" for local IDs. Limit the maximum ID to
1037
   * what "int" can hold.
1038
   */
1039
0
  highest_id = UINT32_MAX-1;
1040
0
  highest_id = MIN(highest_id, INT_MAX);
1041
1042
0
  return smbXsrv_open_table_init(conn, 1, highest_id, max_opens);
1043
0
}
1044
1045
NTSTATUS smb2srv_open_lookup(struct smbXsrv_connection *conn,
1046
           uint64_t persistent_id,
1047
           uint64_t volatile_id,
1048
           NTTIME now,
1049
           struct smbXsrv_open **_open)
1050
0
{
1051
0
  struct smbXsrv_open_table *table = conn->client->open_table;
1052
0
  uint32_t local_id = volatile_id & UINT32_MAX;
1053
0
  uint64_t local_zeros = volatile_id & 0xFFFFFFFF00000000LLU;
1054
0
  uint32_t global_id = persistent_id & UINT32_MAX;
1055
0
  uint64_t global_zeros = persistent_id & 0xFFFFFFFF00000000LLU;
1056
0
  NTSTATUS status;
1057
1058
0
  if (local_zeros != 0) {
1059
0
    return NT_STATUS_FILE_CLOSED;
1060
0
  }
1061
1062
0
  if (global_zeros != 0) {
1063
0
    return NT_STATUS_FILE_CLOSED;
1064
0
  }
1065
1066
0
  if (global_id == 0) {
1067
0
    return NT_STATUS_FILE_CLOSED;
1068
0
  }
1069
1070
0
  status = smbXsrv_open_local_lookup(table, local_id, global_id, now,
1071
0
             _open);
1072
0
  if (!NT_STATUS_IS_OK(status)) {
1073
0
    return status;
1074
0
  }
1075
1076
  /*
1077
   * Clear the replay cache for this create_guid if it exists:
1078
   * This is based on the assumption that this lookup will be
1079
   * triggered by a client request using the file-id for lookup.
1080
   * Hence the client has proven that it has in fact seen the
1081
   * reply to its initial create call. So subsequent create replays
1082
   * should be treated as invalid. Hence the index for create_guid
1083
   * lookup needs to be removed.
1084
   */
1085
0
  status = smbXsrv_open_clear_replay_cache(*_open);
1086
1087
0
  return status;
1088
0
}
1089
1090
/*
1091
 * This checks or marks the replay cache, we have the following
1092
 * cases:
1093
 *
1094
 * 1. There is no record in the cache
1095
 *    => We return STATUS_FWP_RESERVED in order to indicate
1096
 *       that the caller holds the current reservation
1097
 *    This is the "normal" CREATE processing
1098
 *
1099
 * 2. There is a record in the cache and open_persistent_id is 0
1100
 *    => We return NT_STATUS_FILE_NOT_AVAILABLE to indicate
1101
 *       the original request is still pending
1102
 *    This is the CREATE pending (still being processed) case
1103
 *
1104
 * 3. There is a record in the cache with a valid open_persistent_id:
1105
 *    => We lookup the existing global open by open_persistent_id
1106
 *    => We lookup a matching local open and return NT_STATUS_OK together
1107
 *       with the smbXsrv_open if one is found
1108
 *       This is the successful CREATE-replay case in the same process
1109
 *    => Otherwise we return NT_STATUS_HANDLE_NO_LONGER_VALID together with
1110
 *       open_persistent_id to trigger a handle reconnect
1111
 *       This is the successful CREATE-replay case in a different process
1112
 *
1113
 * With NT_STATUS_OK the caller can continue the replay processing.
1114
 *
1115
 * With STATUS_FWP_RESERVED the caller should continue the normal
1116
 * open processing:
1117
 * - On success:
1118
 *   - smbXsrv_open_update()/smbXsrv_open_set_replay_cache()
1119
 *     will update the record with a valid open_persistent_id.
1120
 * - On failure:
1121
 *   - smbXsrv_open_purge_replay_cache() should cleanup
1122
 *     the reservation.
1123
 *
1124
 * All other values should be returned to the client,
1125
 * while NT_STATUS_FILE_NOT_AVAILABLE will trigger the
1126
 * retry loop on the client.
1127
 */
1128
NTSTATUS smb2srv_open_lookup_replay_cache(struct smbXsrv_connection *conn,
1129
            struct smbXsrv_session *session,
1130
            struct GUID create_guid,
1131
            const char *name,
1132
            NTTIME now,
1133
            uint64_t *persistent_id,
1134
            struct smbXsrv_open **_open)
1135
0
{
1136
0
  TALLOC_CTX *frame = talloc_stackframe();
1137
0
  NTSTATUS status;
1138
0
  struct smbXsrv_open_table *table = conn->client->open_table;
1139
0
  struct db_context *db = table->global.db_ctx;
1140
0
  struct GUID_txt_buf _create_guid_buf;
1141
0
  const char *create_guid_str = GUID_buf_string(&create_guid, &_create_guid_buf);
1142
0
  struct db_record *db_rec = NULL;
1143
0
  const struct smbXsrv_open_global0 *global = NULL;
1144
0
  struct smbXsrv_open *op = NULL;
1145
0
  uint32_t open_global_id;
1146
0
  struct smbXsrv_open_replay_cache_key_buf key_buf;
1147
0
  TDB_DATA key;
1148
0
  TDB_DATA val;
1149
1150
0
  *_open = NULL;
1151
1152
0
  key = smbXsrv_open_replay_cache_key(&conn->client->global->client_guid,
1153
0
              &create_guid,
1154
0
              &key_buf);
1155
1156
0
  db_rec = dbwrap_fetch_locked(db, frame, key);
1157
0
  if (db_rec == NULL) {
1158
0
    TALLOC_FREE(frame);
1159
0
    return NT_STATUS_INTERNAL_DB_ERROR;
1160
0
  }
1161
1162
0
  val = dbwrap_record_get_value(db_rec);
1163
0
  if (val.dsize == 0) {
1164
0
    struct smbXsrv_open_global_key_buf open_key_buf;
1165
1166
0
    DBG_DEBUG("Fresh replay-cache record\n");
1167
1168
0
    val = smbXsrv_open_global_id_to_key(0, &open_key_buf);
1169
0
    status = dbwrap_record_store(db_rec, val, TDB_REPLACE);
1170
0
    if (!NT_STATUS_IS_OK(status)) {
1171
0
      TALLOC_FREE(frame);
1172
0
      return status;
1173
0
    }
1174
1175
    /*
1176
     * We're the new holder
1177
     */
1178
0
    *_open = NULL;
1179
0
    TALLOC_FREE(frame);
1180
0
    return NT_STATUS_FWP_RESERVED;
1181
0
  }
1182
1183
  /*
1184
   * For now we expect the size of the record containing the global
1185
   * key to be the size of an uint32_t. If we ever change this, this
1186
   * will needs revisiting.
1187
   */
1188
0
  if (val.dsize != sizeof(uint32_t)) {
1189
0
    TALLOC_FREE(frame);
1190
0
    return NT_STATUS_INTERNAL_DB_CORRUPTION;
1191
0
  }
1192
0
  open_global_id = PULL_BE_U32(val.dptr, 0);
1193
1194
0
  DBG_DEBUG("Found replay cache record open_global_id [%"PRIx32"]\n",
1195
0
      open_global_id);
1196
1197
0
  if (open_global_id == 0) {
1198
    /*
1199
     * The original request (or a former replay) is still
1200
     * pending, ask the client to retry by sending
1201
     * STATUS_FILE_NOT_AVAILABLE.
1202
     */
1203
0
    DBG_DEBUG("Pending create [%s] [%s]\n", create_guid_str, name);
1204
0
    TALLOC_FREE(frame);
1205
0
    return NT_STATUS_FILE_NOT_AVAILABLE;
1206
0
  }
1207
1208
0
  TALLOC_FREE(db_rec);
1209
0
  status = smbXsrv_open_global_lookup(table,
1210
0
              frame,
1211
0
              open_global_id,
1212
0
              &global);
1213
0
  if (!NT_STATUS_IS_OK(status)) {
1214
0
    DBG_DEBUG("Global open not found for create "
1215
0
        "[0x%"PRIx32"] [%s] [%s]\n",
1216
0
        open_global_id, create_guid_str, name);
1217
0
    TALLOC_FREE(frame);
1218
0
    return status;
1219
0
  }
1220
1221
0
  if (CHECK_DEBUGLVL(DBGLVL_DEBUG)) {
1222
0
    NDR_PRINT_DEBUG(smbXsrv_open_global0, global);
1223
0
  }
1224
1225
0
  status = smbXsrv_open_local_lookup(table,
1226
0
             global->open_volatile_id,
1227
0
             global->open_persistent_id,
1228
0
             now,
1229
0
             &op);
1230
0
  if (NT_STATUS_IS_OK(status)) {
1231
0
    if (op->session->global->session_global_id !=
1232
0
        session->global->session_global_id)
1233
0
    {
1234
0
      TALLOC_FREE(frame);
1235
0
      return NT_STATUS_DUPLICATE_OBJECTID;
1236
0
    }
1237
0
    DBG_DEBUG("Found local open\n");
1238
    /*
1239
     * We found an open the caller can reuse.
1240
     */
1241
0
    SMB_ASSERT(op != NULL);
1242
0
    *_open = op;
1243
0
    TALLOC_FREE(frame);
1244
0
    return NT_STATUS_OK;
1245
0
  }
1246
1247
0
  DBG_DEBUG("No local open, triggering reconnect\n");
1248
0
  *persistent_id = open_global_id;
1249
0
  TALLOC_FREE(frame);
1250
0
  return NT_STATUS_HANDLE_NO_LONGER_VALID;
1251
0
}
1252
1253
struct smb2srv_open_recreate_state {
1254
  struct smbXsrv_session *session;
1255
  struct smbXsrv_tcon *tcon;
1256
  struct smbXsrv_open *op;
1257
  const struct GUID *client_guid;
1258
  const struct GUID *create_guid;
1259
  const struct smb2_lease_key *lease_key;
1260
  struct security_token *current_token;
1261
  struct server_id me;
1262
1263
  NTSTATUS status;
1264
};
1265
1266
static void smb2srv_open_recreate_fn(
1267
  struct db_record *rec, TDB_DATA oldval, void *private_data)
1268
0
{
1269
0
  struct smb2srv_open_recreate_state *state = private_data;
1270
0
  TDB_DATA key = dbwrap_record_get_key(rec);
1271
0
  struct smbXsrv_open_global0 *global = NULL;
1272
0
  struct GUID_txt_buf buf1, buf2;
1273
1274
0
  state->status = smbXsrv_open_global_verify_record(
1275
0
    key, oldval, state->op, &state->op->global);
1276
0
  if (!NT_STATUS_EQUAL(state->status, NT_STATUS_REMOTE_DISCONNECT)) {
1277
0
    DBG_WARNING("smbXsrv_open_global_verify_record for %s "
1278
0
          "failed: %s\n",
1279
0
          tdb_data_dbg(key),
1280
0
          nt_errstr(state->status));
1281
0
    goto not_found;
1282
0
  }
1283
0
  global = state->op->global;
1284
1285
0
  if (state->lease_key != NULL &&
1286
0
      !GUID_equal(&global->client_guid, state->client_guid))
1287
0
  {
1288
0
    DBG_NOTICE("client guid: %s != %s in %s\n",
1289
0
         GUID_buf_string(&global->client_guid, &buf1),
1290
0
         GUID_buf_string(state->client_guid, &buf2),
1291
0
         tdb_data_dbg(key));
1292
0
    goto not_found;
1293
0
  }
1294
1295
  /*
1296
   * If the provided create_guid is NULL, this means that
1297
   * the reconnect request was a v1 request. In that case
1298
   * we should skip the create GUID verification, since
1299
   * it is valid to v1-reconnect a v2-opened handle.
1300
   */
1301
0
  if ((state->create_guid != NULL) &&
1302
0
      !GUID_equal(&global->create_guid, state->create_guid)) {
1303
0
    DBG_NOTICE("%s != %s in %s\n",
1304
0
         GUID_buf_string(&global->create_guid, &buf1),
1305
0
         GUID_buf_string(state->create_guid, &buf2),
1306
0
         tdb_data_dbg(key));
1307
0
    goto not_found;
1308
0
  }
1309
1310
0
  if (!security_token_is_sid(
1311
0
        state->current_token, &global->open_owner)) {
1312
0
    struct dom_sid_buf buf;
1313
0
    DBG_NOTICE("global owner %s not in our token in %s\n",
1314
0
         dom_sid_str_buf(&global->open_owner, &buf),
1315
0
         tdb_data_dbg(key));
1316
0
    state->status = NT_STATUS_ACCESS_DENIED;
1317
0
    return;
1318
0
  }
1319
1320
0
  if (!global->durable) {
1321
0
    DBG_NOTICE("%"PRIu64"/%"PRIu64" not durable in %s\n",
1322
0
         global->open_persistent_id,
1323
0
         global->open_volatile_id,
1324
0
         tdb_data_dbg(key));
1325
0
    goto not_found;
1326
0
  }
1327
1328
0
  global->open_volatile_id = state->op->local_id;
1329
0
  global->server_id = state->me;
1330
0
  global->session_global_id = state->session->global->session_global_id;
1331
0
  global->tcon_global_id = state->tcon->global->tcon_global_id;
1332
1333
0
  state->status = smbXsrv_open_global_store(rec, key, oldval, global);
1334
0
  if (!NT_STATUS_IS_OK(state->status)) {
1335
0
    DBG_WARNING("smbXsrv_open_global_store for %s failed: %s\n",
1336
0
          tdb_data_dbg(key),
1337
0
          nt_errstr(state->status));
1338
0
    return;
1339
0
  }
1340
0
  return;
1341
1342
0
not_found:
1343
0
  state->status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
1344
0
}
1345
1346
NTSTATUS smb2srv_open_recreate(struct smbXsrv_connection *conn,
1347
             struct smbXsrv_session *session,
1348
             struct smbXsrv_tcon *tcon,
1349
             uint64_t persistent_id,
1350
             const struct GUID *create_guid,
1351
             const struct smb2_lease_key *lease_key,
1352
             NTTIME now,
1353
             struct smbXsrv_open **_open)
1354
0
{
1355
0
  struct smbXsrv_open_table *table = conn->client->open_table;
1356
0
  struct smb2srv_open_recreate_state state = {
1357
0
    .session = session,
1358
0
    .tcon = tcon,
1359
0
    .client_guid = &conn->client->global->client_guid,
1360
0
    .create_guid = create_guid,
1361
0
    .lease_key = lease_key,
1362
0
    .me = messaging_server_id(conn->client->msg_ctx),
1363
0
  };
1364
0
  struct auth_session_info *session_info =
1365
0
    session->global->auth_session_info;
1366
0
  struct smbXsrv_open_global_key_buf key_buf;
1367
0
  TDB_DATA key = smbXsrv_open_global_id_to_key(
1368
0
    persistent_id & UINT32_MAX, &key_buf);
1369
0
  int ret, local_id;
1370
0
  NTSTATUS status;
1371
1372
0
  if (session_info == NULL) {
1373
0
    DEBUG(10, ("session_info=NULL\n"));
1374
0
    return NT_STATUS_INVALID_HANDLE;
1375
0
  }
1376
0
  state.current_token = session_info->security_token;
1377
1378
0
  if (state.current_token == NULL) {
1379
0
    DEBUG(10, ("current_token=NULL\n"));
1380
0
    return NT_STATUS_INVALID_HANDLE;
1381
0
  }
1382
1383
0
  if ((persistent_id & 0xFFFFFFFF00000000LLU) != 0) {
1384
    /*
1385
     * We only use 32 bit for the persistent ID
1386
     */
1387
0
    DBG_DEBUG("persistent_id=%"PRIx64"\n", persistent_id);
1388
0
    return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1389
0
  }
1390
1391
0
  if (table->local.num_opens >= table->local.max_opens) {
1392
0
    return NT_STATUS_INSUFFICIENT_RESOURCES;
1393
0
  }
1394
1395
0
  state.op = talloc_zero(table, struct smbXsrv_open);
1396
0
  if (state.op == NULL) {
1397
0
    return NT_STATUS_NO_MEMORY;
1398
0
  }
1399
0
  state.op->table = table;
1400
1401
0
  local_id =  idr_get_new_random(
1402
0
    table->local.idr,
1403
0
    state.op,
1404
0
    table->local.lowest_id,
1405
0
    table->local.highest_id);
1406
0
  if (local_id == -1) {
1407
0
    TALLOC_FREE(state.op);
1408
0
    return NT_STATUS_INSUFFICIENT_RESOURCES;
1409
0
  }
1410
0
  state.op->local_id = local_id;
1411
0
  SMB_ASSERT(state.op->local_id == local_id); /* No coercion loss */
1412
1413
0
  table->local.num_opens += 1;
1414
1415
0
  state.op->idle_time = now;
1416
0
  state.op->status = NT_STATUS_FILE_CLOSED;
1417
1418
0
  status = dbwrap_do_locked(
1419
0
    table->global.db_ctx, key, smb2srv_open_recreate_fn, &state);
1420
0
  if (!NT_STATUS_IS_OK(status)) {
1421
0
    DBG_DEBUG("dbwrap_do_locked() for %s failed: %s\n",
1422
0
        tdb_data_dbg(key),
1423
0
        nt_errstr(status));
1424
0
    goto fail;
1425
0
  }
1426
1427
0
  if (!NT_STATUS_IS_OK(state.status)) {
1428
0
    status = state.status;
1429
0
    DBG_DEBUG("smb2srv_open_recreate_fn for %s failed: %s\n",
1430
0
        tdb_data_dbg(key),
1431
0
        nt_errstr(status));
1432
0
    goto fail;
1433
0
  }
1434
1435
0
  state.op->session = session;
1436
0
  state.op->tcon = tcon;
1437
1438
0
  talloc_set_destructor(state.op, smbXsrv_open_destructor);
1439
1440
0
  if (CHECK_DEBUGLVL(10)) {
1441
0
    struct smbXsrv_openB open_blob = {
1442
0
      .info.info0 = state.op,
1443
0
    };
1444
0
    DBG_DEBUG("global_id (0x%08x) stored\n",
1445
0
        state.op->global->open_global_id);
1446
0
    NDR_PRINT_DEBUG(smbXsrv_openB, &open_blob);
1447
0
  }
1448
1449
0
  *_open = state.op;
1450
1451
0
  return NT_STATUS_OK;
1452
0
fail:
1453
0
  table->local.num_opens -= 1;
1454
1455
0
  ret = idr_remove(table->local.idr, state.op->local_id);
1456
0
  SMB_ASSERT(ret == 0);
1457
0
  TALLOC_FREE(state.op);
1458
0
  return status;
1459
0
}
1460
1461
struct smbXsrv_open_global_traverse_state {
1462
  int (*fn)(struct db_record *rec,
1463
      struct smbXsrv_open_global0 *global,
1464
      TDB_DATA *rc_open_global_key,
1465
      void *private_data);
1466
  void *private_data;
1467
};
1468
1469
static int smbXsrv_open_global_traverse_fn(struct db_record *rec, void *data)
1470
0
{
1471
0
  struct smbXsrv_open_global_traverse_state *state =
1472
0
    (struct smbXsrv_open_global_traverse_state*)data;
1473
0
  struct smbXsrv_open_global0 *global = NULL;
1474
0
  TDB_DATA key = dbwrap_record_get_key(rec);
1475
0
  TDB_DATA val = dbwrap_record_get_value(rec);
1476
0
  NTSTATUS status;
1477
0
  int ret = -1;
1478
1479
  /*
1480
   * Currently expected fixed size of the replay cache record is the
1481
   * size of the open_global record's key and the expected size of
1482
   * the key is 32 bytes (client GUID + create GUID).
1483
   */
1484
0
  if ((key.dsize == 32) && (val.dsize == sizeof(uint32_t))) {
1485
0
    return state->fn(rec, NULL, &val, state->private_data);
1486
0
  }
1487
1488
0
  status = smbXsrv_open_global_parse_record(
1489
0
    talloc_tos(), key, val, &global);
1490
0
  if (!NT_STATUS_IS_OK(status)) {
1491
0
    return -1;
1492
0
  }
1493
1494
0
  ret = state->fn(rec, global, NULL, state->private_data);
1495
0
  talloc_free(global);
1496
0
  return ret;
1497
0
}
1498
1499
NTSTATUS smbXsrv_open_global_traverse(
1500
  int (*fn)(struct db_record *rec,
1501
      struct smbXsrv_open_global0 *global,
1502
      TDB_DATA *rc_open_global_key,
1503
      void *private_data),
1504
  void *private_data)
1505
0
{
1506
1507
0
  NTSTATUS status;
1508
0
  int count = 0;
1509
0
  struct smbXsrv_open_global_traverse_state state = {
1510
0
    .fn = fn,
1511
0
    .private_data = private_data,
1512
0
  };
1513
1514
0
  become_root();
1515
0
  status = smbXsrv_open_global_init();
1516
0
  if (!NT_STATUS_IS_OK(status)) {
1517
0
    unbecome_root();
1518
0
    DEBUG(0, ("Failed to initialize open_global: %s\n",
1519
0
        nt_errstr(status)));
1520
0
    return status;
1521
0
  }
1522
1523
0
  status = dbwrap_traverse_read(smbXsrv_open_global_db_ctx,
1524
0
              smbXsrv_open_global_traverse_fn,
1525
0
              &state,
1526
0
              &count);
1527
0
  unbecome_root();
1528
1529
0
  return status;
1530
0
}
1531
1532
struct smbXsrv_open_cleanup_state {
1533
  uint32_t global_id;
1534
  struct GUID client_guid;
1535
  struct GUID create_guid;
1536
  NTSTATUS status;
1537
};
1538
1539
static void smbXsrv_open_cleanup_fn(
1540
  struct db_record *rec, TDB_DATA oldval, void *private_data)
1541
0
{
1542
0
  struct smbXsrv_open_cleanup_state *state = private_data;
1543
0
  struct smbXsrv_open_global0 *global = NULL;
1544
0
  TDB_DATA key = dbwrap_record_get_key(rec);
1545
0
  bool delete_open = false;
1546
1547
0
  if (oldval.dsize == 0) {
1548
0
    DBG_DEBUG("[global: 0x%08x] "
1549
0
        "empty record in %s, skipping...\n",
1550
0
        state->global_id,
1551
0
        dbwrap_name(dbwrap_record_get_db(rec)));
1552
0
    state->status = NT_STATUS_OK;
1553
0
    return;
1554
0
  }
1555
1556
0
  state->status = smbXsrv_open_global_parse_record(
1557
0
    talloc_tos(), key, oldval, &global);
1558
0
  if (!NT_STATUS_IS_OK(state->status)) {
1559
0
    DBG_WARNING("[global: %x08x] "
1560
0
          "smbXsrv_open_global_parse_record() in %s "
1561
0
          "failed: %s, deleting record\n",
1562
0
          state->global_id,
1563
0
          dbwrap_name(dbwrap_record_get_db(rec)),
1564
0
          nt_errstr(state->status));
1565
0
    delete_open = true;
1566
0
    goto do_delete;
1567
0
  }
1568
1569
0
  if (server_id_is_disconnected(&global->server_id)) {
1570
0
    struct timeval now = timeval_current();
1571
0
    struct timeval disconnect_time;
1572
0
    struct timeval_buf buf;
1573
0
    int64_t tdiff;
1574
1575
0
    nttime_to_timeval(&disconnect_time, global->disconnect_time);
1576
0
    tdiff = usec_time_diff(&now, &disconnect_time);
1577
0
    delete_open = (tdiff >= 1000*global->durable_timeout_msec);
1578
1579
0
    DBG_DEBUG("[global: 0x%08x] "
1580
0
        "disconnected at [%s] %"PRIi64"s ago with "
1581
0
        "timeout of %"PRIu32"s -%s reached\n",
1582
0
        state->global_id,
1583
0
        timeval_str_buf(&disconnect_time,
1584
0
            false,
1585
0
            false,
1586
0
            &buf),
1587
0
        tdiff/1000000,
1588
0
        global->durable_timeout_msec / 1000,
1589
0
        delete_open ? "" : " not");
1590
0
  } else if (!serverid_exists(&global->server_id)) {
1591
0
    struct server_id_buf idbuf;
1592
0
    DBG_DEBUG("[global: 0x%08x] "
1593
0
        "server[%s] does not exist\n",
1594
0
        state->global_id,
1595
0
        server_id_str_buf(global->server_id, &idbuf));
1596
0
    delete_open = true;
1597
0
  }
1598
1599
0
  if (!delete_open) {
1600
0
    state->status = NT_STATUS_CANNOT_DELETE;
1601
0
    return;
1602
0
  }
1603
0
do_delete:
1604
0
  state->status = dbwrap_record_delete(rec);
1605
0
  if (!NT_STATUS_IS_OK(state->status)) {
1606
0
    DBG_WARNING("[global: 0x%08x] "
1607
0
          "failed to delete record "
1608
0
          "from %s: %s\n",
1609
0
          state->global_id,
1610
0
          dbwrap_name(dbwrap_record_get_db(rec)),
1611
0
          nt_errstr(state->status));
1612
0
    return;
1613
0
  }
1614
1615
0
  DBG_DEBUG("[global: 0x%08x] "
1616
0
      "deleted record from %s\n",
1617
0
      state->global_id,
1618
0
      dbwrap_name(dbwrap_record_get_db(rec)));
1619
0
  state->status = NT_STATUS_OK;
1620
0
  if (global != NULL) {
1621
0
    state->client_guid = global->client_guid;
1622
0
    state->create_guid = global->create_guid;
1623
0
  }
1624
0
}
1625
1626
NTSTATUS smbXsrv_open_cleanup(uint64_t persistent_id)
1627
0
{
1628
0
  struct smbXsrv_open_cleanup_state state = {
1629
0
    .global_id = persistent_id & UINT32_MAX,
1630
0
  };
1631
0
  struct smbXsrv_open_global_key_buf key_buf;
1632
0
  TDB_DATA key = smbXsrv_open_global_id_to_key(
1633
0
    state.global_id, &key_buf);
1634
0
  NTSTATUS status;
1635
1636
0
  status = dbwrap_do_locked(
1637
0
    smbXsrv_open_global_db_ctx,
1638
0
    key,
1639
0
    smbXsrv_open_cleanup_fn,
1640
0
    &state);
1641
0
  if (!NT_STATUS_IS_OK(status)) {
1642
0
    DBG_DEBUG("[global: 0x%08x] dbwrap_do_locked failed: %s\n",
1643
0
        state.global_id,
1644
0
        nt_errstr(status));
1645
0
    return status;
1646
0
  }
1647
1648
0
  if (!NT_STATUS_IS_OK(state.status)) {
1649
0
    return state.status;
1650
0
  }
1651
1652
0
  if (GUID_all_zero(&state.create_guid)) {
1653
0
    return NT_STATUS_OK;
1654
0
  }
1655
0
  status = smbXsrv_replay_cleanup(&state.client_guid, &state.create_guid);
1656
0
  if (!NT_STATUS_IS_OK(status)) {
1657
0
    DBG_DEBUG("Failed to remove replay record\n");
1658
0
    return status;
1659
0
  }
1660
1661
0
  return NT_STATUS_OK;
1662
0
}
1663
1664
NTSTATUS smbXsrv_replay_cleanup(const struct GUID *client_guid,
1665
        const struct GUID *create_guid)
1666
0
{
1667
0
  struct smbXsrv_open_replay_cache_key_buf rc_key_buf;
1668
0
  TDB_DATA key;
1669
0
  NTSTATUS status;
1670
1671
0
  key = smbXsrv_open_replay_cache_key(client_guid,
1672
0
              create_guid,
1673
0
              &rc_key_buf);
1674
1675
0
  status = dbwrap_purge(smbXsrv_open_global_db_ctx, key);
1676
0
  if (!NT_STATUS_IS_OK(status)) {
1677
0
    struct GUID_txt_buf create_guid_buf;
1678
    DBG_ERR("create_guid [%s] purge replay-cache "
1679
0
      "record failed: %s\n",
1680
0
      GUID_buf_string(create_guid, &create_guid_buf),
1681
0
      nt_errstr(status));
1682
0
  }
1683
0
  return status;
1684
0
}