smbd: Simplify smbXsrv_open_global_store()
[Samba.git] / source3 / smbd / smbXsrv_open.c
blob9e2bc618800b61b044a202de6a53f4276f540251
1 /*
2 Unix SMB/CIFS implementation.
4 Copyright (C) Stefan Metzmacher 2012
5 Copyright (C) Michael Adam 2012
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.
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.
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/>.
21 #include "smbXsrv_open.h"
22 #include "includes.h"
23 #include "system/filesys.h"
24 #include "lib/util/server_id.h"
25 #include "smbd/smbd.h"
26 #include "smbd/globals.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"
38 struct smbXsrv_open_table {
39 struct {
40 struct idr_context *idr;
41 struct db_context *replay_cache_db_ctx;
42 uint32_t lowest_id;
43 uint32_t highest_id;
44 uint32_t max_opens;
45 uint32_t num_opens;
46 } local;
47 struct {
48 struct db_context *db_ctx;
49 } global;
52 static struct db_context *smbXsrv_open_global_db_ctx = NULL;
54 NTSTATUS smbXsrv_open_global_init(void)
56 char *global_path = NULL;
57 struct db_context *db_ctx = NULL;
59 if (smbXsrv_open_global_db_ctx != NULL) {
60 return NT_STATUS_OK;
63 global_path = lock_path(talloc_tos(), "smbXsrv_open_global.tdb");
64 if (global_path == NULL) {
65 return NT_STATUS_NO_MEMORY;
68 db_ctx = db_open(NULL, global_path,
69 SMBD_VOLATILE_TDB_HASH_SIZE,
70 SMBD_VOLATILE_TDB_FLAGS,
71 O_RDWR | O_CREAT, 0600,
72 DBWRAP_LOCK_ORDER_1,
73 DBWRAP_FLAG_NONE);
74 TALLOC_FREE(global_path);
75 if (db_ctx == NULL) {
76 NTSTATUS status;
78 status = map_nt_error_from_unix_common(errno);
80 return status;
83 smbXsrv_open_global_db_ctx = db_ctx;
85 return NT_STATUS_OK;
89 * NOTE:
90 * We need to store the keys in big endian so that dbwrap_rbt's memcmp
91 * has the same result as integer comparison between the uint32_t
92 * values.
94 * TODO: implement string based key
97 struct smbXsrv_open_global_key_buf { uint8_t buf[sizeof(uint32_t)]; };
99 static TDB_DATA smbXsrv_open_global_id_to_key(
100 uint32_t id, struct smbXsrv_open_global_key_buf *key_buf)
102 RSIVAL(key_buf->buf, 0, id);
104 return (TDB_DATA) {
105 .dptr = key_buf->buf,
106 .dsize = sizeof(key_buf->buf),
110 static struct db_record *smbXsrv_open_global_fetch_locked(
111 struct db_context *db,
112 uint32_t id,
113 TALLOC_CTX *mem_ctx)
115 struct smbXsrv_open_global_key_buf key_buf;
116 TDB_DATA key = smbXsrv_open_global_id_to_key(id, &key_buf);
117 struct db_record *rec = NULL;
120 rec = dbwrap_fetch_locked(db, mem_ctx, key);
122 if (rec == NULL) {
123 DBG_DEBUG("Failed to lock global id 0x%08x, key '%s'\n", id,
124 tdb_data_dbg(key));
127 return rec;
130 static NTSTATUS smbXsrv_open_table_init(struct smbXsrv_connection *conn,
131 uint32_t lowest_id,
132 uint32_t highest_id,
133 uint32_t max_opens)
135 struct smbXsrv_client *client = conn->client;
136 struct smbXsrv_open_table *table;
137 NTSTATUS status;
138 uint64_t max_range;
140 if (lowest_id > highest_id) {
141 return NT_STATUS_INTERNAL_ERROR;
144 max_range = highest_id;
145 max_range -= lowest_id;
146 max_range += 1;
148 if (max_opens > max_range) {
149 return NT_STATUS_INTERNAL_ERROR;
152 table = talloc_zero(client, struct smbXsrv_open_table);
153 if (table == NULL) {
154 return NT_STATUS_NO_MEMORY;
157 table->local.idr = idr_init(table);
158 if (table->local.idr == NULL) {
159 TALLOC_FREE(table);
160 return NT_STATUS_NO_MEMORY;
162 table->local.replay_cache_db_ctx = db_open_rbt(table);
163 if (table->local.replay_cache_db_ctx == NULL) {
164 TALLOC_FREE(table);
165 return NT_STATUS_NO_MEMORY;
167 table->local.lowest_id = lowest_id;
168 table->local.highest_id = highest_id;
169 table->local.max_opens = max_opens;
171 status = smbXsrv_open_global_init();
172 if (!NT_STATUS_IS_OK(status)) {
173 TALLOC_FREE(table);
174 return status;
177 table->global.db_ctx = smbXsrv_open_global_db_ctx;
179 client->open_table = table;
180 return NT_STATUS_OK;
183 static NTSTATUS smbXsrv_open_local_lookup(struct smbXsrv_open_table *table,
184 uint32_t open_local_id,
185 uint32_t open_global_id,
186 NTTIME now,
187 struct smbXsrv_open **_open)
189 struct smbXsrv_open *op = NULL;
191 *_open = NULL;
193 if (open_local_id == 0) {
194 return NT_STATUS_FILE_CLOSED;
197 if (table == NULL) {
198 /* this might happen before the end of negprot */
199 return NT_STATUS_FILE_CLOSED;
202 if (table->local.idr == NULL) {
203 return NT_STATUS_INTERNAL_ERROR;
206 op = idr_find(table->local.idr, open_local_id);
207 if (op == NULL) {
208 return NT_STATUS_FILE_CLOSED;
211 if (open_global_id == 0) {
212 /* make the global check a no-op for SMB1 */
213 open_global_id = op->global->open_global_id;
216 if (op->global->open_global_id != open_global_id) {
217 return NT_STATUS_FILE_CLOSED;
220 if (now != 0) {
221 op->idle_time = now;
224 *_open = op;
225 return NT_STATUS_OK;
228 static NTSTATUS smbXsrv_open_global_parse_record(
229 TALLOC_CTX *mem_ctx,
230 TDB_DATA key,
231 TDB_DATA val,
232 struct smbXsrv_open_global0 **global)
234 DATA_BLOB blob = data_blob_const(val.dptr, val.dsize);
235 struct smbXsrv_open_globalB global_blob;
236 enum ndr_err_code ndr_err;
237 NTSTATUS status;
238 TALLOC_CTX *frame = talloc_stackframe();
240 ndr_err = ndr_pull_struct_blob(&blob, frame, &global_blob,
241 (ndr_pull_flags_fn_t)ndr_pull_smbXsrv_open_globalB);
242 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
243 DEBUG(1,("Invalid record in smbXsrv_open_global.tdb:"
244 "key '%s' ndr_pull_struct_blob - %s\n",
245 tdb_data_dbg(key),
246 ndr_errstr(ndr_err)));
247 status = ndr_map_error2ntstatus(ndr_err);
248 goto done;
251 DBG_DEBUG("\n");
252 if (CHECK_DEBUGLVL(10)) {
253 NDR_PRINT_DEBUG(smbXsrv_open_globalB, &global_blob);
256 if (global_blob.version != SMBXSRV_VERSION_0) {
257 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
258 DEBUG(1,("Invalid record in smbXsrv_open_global.tdb:"
259 "key '%s' unsupported version - %d - %s\n",
260 tdb_data_dbg(key),
261 (int)global_blob.version,
262 nt_errstr(status)));
263 goto done;
266 if (global_blob.info.info0 == NULL) {
267 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
268 DEBUG(1,("Invalid record in smbXsrv_tcon_global.tdb:"
269 "key '%s' info0 NULL pointer - %s\n",
270 tdb_data_dbg(key),
271 nt_errstr(status)));
272 goto done;
275 *global = talloc_move(mem_ctx, &global_blob.info.info0);
276 status = NT_STATUS_OK;
277 done:
278 talloc_free(frame);
279 return status;
282 static NTSTATUS smbXsrv_open_global_verify_record(
283 TDB_DATA key,
284 TDB_DATA val,
285 TALLOC_CTX *mem_ctx,
286 struct smbXsrv_open_global0 **_global0)
288 struct smbXsrv_open_global0 *global0 = NULL;
289 struct server_id_buf buf;
290 NTSTATUS status;
292 if (val.dsize == 0) {
293 return NT_STATUS_NOT_FOUND;
296 status = smbXsrv_open_global_parse_record(mem_ctx, key, val, &global0);
297 if (!NT_STATUS_IS_OK(status)) {
298 DBG_WARNING("smbXsrv_open_global_parse_record for %s failed: "
299 "%s\n",
300 tdb_data_dbg(key),
301 nt_errstr(status));
302 return status;
304 *_global0 = global0;
306 if (server_id_is_disconnected(&global0->server_id)) {
307 return NT_STATUS_OK;
309 if (serverid_exists(&global0->server_id)) {
310 return NT_STATUS_OK;
313 DBG_WARNING("smbd %s did not clean up record %s\n",
314 server_id_str_buf(global0->server_id, &buf),
315 tdb_data_dbg(key));
317 return NT_STATUS_FATAL_APP_EXIT;
320 static NTSTATUS smbXsrv_open_global_store(
321 struct db_record *rec,
322 TDB_DATA key,
323 TDB_DATA oldval,
324 struct smbXsrv_open_global0 *global)
326 struct smbXsrv_open_globalB global_blob;
327 DATA_BLOB blob = data_blob_null;
328 TDB_DATA val = { .dptr = NULL, };
329 NTSTATUS status;
330 enum ndr_err_code ndr_err;
333 * TODO: if we use other versions than '0'
334 * we would add glue code here, that would be able to
335 * store the information in the old format.
338 global_blob = (struct smbXsrv_open_globalB) {
339 .version = smbXsrv_version_global_current(),
342 if (oldval.dsize >= 8) {
343 global_blob.seqnum = IVAL(oldval.dptr, 4);
345 global_blob.seqnum += 1;
346 global_blob.info.info0 = global;
348 ndr_err = ndr_push_struct_blob(&blob, talloc_tos(), &global_blob,
349 (ndr_push_flags_fn_t)ndr_push_smbXsrv_open_globalB);
350 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
351 DBG_WARNING("key '%s' ndr_push - %s\n",
352 tdb_data_dbg(key),
353 ndr_map_error2string(ndr_err));
354 return ndr_map_error2ntstatus(ndr_err);
357 val = make_tdb_data(blob.data, blob.length);
358 status = dbwrap_record_store(rec, val, TDB_REPLACE);
359 TALLOC_FREE(blob.data);
360 if (!NT_STATUS_IS_OK(status)) {
361 DBG_WARNING("key '%s' store - %s\n",
362 tdb_data_dbg(key),
363 nt_errstr(status));
364 return status;
367 if (CHECK_DEBUGLVL(10)) {
368 DBG_DEBUG("key '%s' stored\n", tdb_data_dbg(key));
369 NDR_PRINT_DEBUG(smbXsrv_open_globalB, &global_blob);
372 return NT_STATUS_OK;
375 static NTSTATUS smbXsrv_open_global_allocate(
376 struct db_context *db, struct smbXsrv_open_global0 *global)
378 uint32_t i;
379 uint32_t last_free = 0;
380 const uint32_t min_tries = 3;
383 * Here we just randomly try the whole 32-bit space
385 * We use just 32-bit, because we want to reuse the
386 * ID for SRVSVC.
388 for (i = 0; i < UINT32_MAX; i++) {
389 struct smbXsrv_open_global_key_buf key_buf;
390 struct smbXsrv_open_global0 *tmp_global0 = NULL;
391 TDB_DATA key, val;
392 uint32_t id;
393 NTSTATUS status;
395 if (i >= min_tries && last_free != 0) {
396 id = last_free;
397 } else {
398 generate_nonce_buffer((uint8_t *)&id, sizeof(id));
399 id = MAX(id, 1);
400 id = MIN(id, UINT32_MAX-1);
403 key = smbXsrv_open_global_id_to_key(id, &key_buf);
405 global->db_rec = dbwrap_fetch_locked(db, global, key);
406 if (global->db_rec == NULL) {
407 return NT_STATUS_INSUFFICIENT_RESOURCES;
409 val = dbwrap_record_get_value(global->db_rec);
411 status = smbXsrv_open_global_verify_record(
412 key, val, talloc_tos(), &tmp_global0);
414 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
416 * Found an empty slot
418 global->open_global_id = id;
419 return NT_STATUS_OK;
422 TALLOC_FREE(tmp_global0);
424 if (NT_STATUS_EQUAL(status, NT_STATUS_FATAL_APP_EXIT)) {
426 * smbd crashed
428 status = dbwrap_record_delete(global->db_rec);
429 if (!NT_STATUS_IS_OK(status)) {
430 DBG_WARNING("dbwrap_record_delete() failed "
431 "for record %"PRIu32": %s\n",
433 nt_errstr(status));
434 return NT_STATUS_INTERNAL_DB_CORRUPTION;
437 if ((i < min_tries) && (last_free == 0)) {
439 * Remember "id" as free but also try
440 * others to not recycle ids too
441 * quickly.
443 last_free = id;
447 if (!NT_STATUS_IS_OK(status)) {
448 DBG_WARNING("smbXsrv_open_global_verify_record() "
449 "failed for %s: %s, ignoring\n",
450 tdb_data_dbg(key),
451 nt_errstr(status));
454 TALLOC_FREE(global->db_rec);
457 /* should not be reached */
458 return NT_STATUS_INTERNAL_ERROR;
461 static NTSTATUS smbXsrv_open_global_lookup(struct smbXsrv_open_table *table,
462 uint32_t open_global_id,
463 TALLOC_CTX *mem_ctx,
464 struct smbXsrv_open_global0 **_global)
466 struct smbXsrv_open_global_key_buf key_buf;
467 TDB_DATA key = smbXsrv_open_global_id_to_key(open_global_id, &key_buf);
468 TDB_DATA val;
469 struct db_record *global_rec = NULL;
470 NTSTATUS status;
472 *_global = NULL;
474 if (table->global.db_ctx == NULL) {
475 return NT_STATUS_INTERNAL_ERROR;
478 global_rec = dbwrap_fetch_locked(table->global.db_ctx, mem_ctx, key);
479 if (global_rec == NULL) {
480 return NT_STATUS_INTERNAL_DB_ERROR;
482 val = dbwrap_record_get_value(global_rec);
484 status = smbXsrv_open_global_verify_record(key, val, mem_ctx, _global);
485 if (NT_STATUS_IS_OK(status)) {
486 (*_global)->db_rec = talloc_move(*_global, &global_rec);
487 return NT_STATUS_OK;
490 TALLOC_FREE(global_rec);
492 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
493 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
496 return status;
499 static int smbXsrv_open_destructor(struct smbXsrv_open *op)
501 NTSTATUS status;
503 status = smbXsrv_open_close(op, 0);
504 if (!NT_STATUS_IS_OK(status)) {
505 DEBUG(0, ("smbXsrv_open_destructor: "
506 "smbXsrv_open_close() failed - %s\n",
507 nt_errstr(status)));
510 TALLOC_FREE(op->global);
512 return 0;
515 NTSTATUS smbXsrv_open_create(struct smbXsrv_connection *conn,
516 struct auth_session_info *session_info,
517 NTTIME now,
518 struct smbXsrv_open **_open)
520 struct smbXsrv_open_table *table = conn->client->open_table;
521 struct smbXsrv_open *op = NULL;
522 struct smbXsrv_open_global0 *global = NULL;
523 NTSTATUS status;
524 struct dom_sid *current_sid = NULL;
525 struct security_token *current_token = NULL;
526 int local_id;
528 if (session_info == NULL) {
529 return NT_STATUS_INVALID_HANDLE;
531 current_token = session_info->security_token;
533 if (current_token == NULL) {
534 return NT_STATUS_INVALID_HANDLE;
537 if (current_token->num_sids > PRIMARY_USER_SID_INDEX) {
538 current_sid = &current_token->sids[PRIMARY_USER_SID_INDEX];
541 if (current_sid == NULL) {
542 return NT_STATUS_INVALID_HANDLE;
545 if (table->local.num_opens >= table->local.max_opens) {
546 return NT_STATUS_INSUFFICIENT_RESOURCES;
549 op = talloc_zero(table, struct smbXsrv_open);
550 if (op == NULL) {
551 return NT_STATUS_NO_MEMORY;
553 op->table = table;
554 op->status = NT_STATUS_OK; /* TODO: start with INTERNAL_ERROR */
555 op->idle_time = now;
557 global = talloc_zero(op, struct smbXsrv_open_global0);
558 if (global == NULL) {
559 TALLOC_FREE(op);
560 return NT_STATUS_NO_MEMORY;
562 op->global = global;
565 * We mark every slot as invalid using 0xFF.
566 * Valid values are masked with 0xF.
568 memset(global->lock_sequence_array, 0xFF,
569 sizeof(global->lock_sequence_array));
571 status = smbXsrv_open_global_allocate(table->global.db_ctx, global);
572 if (!NT_STATUS_IS_OK(status)) {
573 TALLOC_FREE(op);
574 return status;
577 local_id = idr_get_new_random(
578 table->local.idr,
580 table->local.lowest_id,
581 table->local.highest_id);
582 if (local_id == -1) {
583 TALLOC_FREE(op);
584 return NT_STATUS_INSUFFICIENT_RESOURCES;
586 op->local_id = local_id;
588 global->open_persistent_id = global->open_global_id;
589 global->open_volatile_id = op->local_id;
591 global->server_id = messaging_server_id(conn->client->msg_ctx);
592 global->open_time = now;
593 global->open_owner = *current_sid;
594 if (conn->protocol >= PROTOCOL_SMB2_10) {
595 global->client_guid = conn->smb2.client.guid;
598 table->local.num_opens += 1;
600 talloc_set_destructor(op, smbXsrv_open_destructor);
602 status = smbXsrv_open_global_store(
603 global->db_rec,
604 dbwrap_record_get_key(global->db_rec),
605 dbwrap_record_get_value(global->db_rec),
606 global);
607 TALLOC_FREE(global->db_rec);
608 if (!NT_STATUS_IS_OK(status)) {
609 DEBUG(0,("smbXsrv_open_create: "
610 "global_id (0x%08x) store failed - %s\n",
611 op->global->open_global_id,
612 nt_errstr(status)));
613 TALLOC_FREE(op);
614 return status;
617 if (CHECK_DEBUGLVL(10)) {
618 struct smbXsrv_openB open_blob = {
619 .version = SMBXSRV_VERSION_0,
620 .info.info0 = op,
623 DEBUG(10,("smbXsrv_open_create: global_id (0x%08x) stored\n",
624 op->global->open_global_id));
625 NDR_PRINT_DEBUG(smbXsrv_openB, &open_blob);
628 *_open = op;
629 return NT_STATUS_OK;
632 static NTSTATUS smbXsrv_open_set_replay_cache(struct smbXsrv_open *op)
634 struct GUID *create_guid;
635 struct GUID_txt_buf buf;
636 char *guid_string;
637 struct db_context *db = op->table->local.replay_cache_db_ctx;
638 struct smbXsrv_open_replay_cache rc = {
639 .idle_time = op->idle_time,
640 .local_id = op->local_id,
642 uint8_t data[SMBXSRV_OPEN_REPLAY_CACHE_FIXED_SIZE] = { 0 };
643 DATA_BLOB blob = { .data = data, .length = sizeof(data), };
644 enum ndr_err_code ndr_err;
645 NTSTATUS status;
646 TDB_DATA val;
648 if (!(op->flags & SMBXSRV_OPEN_NEED_REPLAY_CACHE)) {
649 return NT_STATUS_OK;
652 if (op->flags & SMBXSRV_OPEN_HAVE_REPLAY_CACHE) {
653 return NT_STATUS_OK;
656 create_guid = &op->global->create_guid;
657 guid_string = GUID_buf_string(create_guid, &buf);
659 ndr_err = ndr_push_struct_into_fixed_blob(&blob, &rc,
660 (ndr_push_flags_fn_t)ndr_push_smbXsrv_open_replay_cache);
661 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
662 status = ndr_map_error2ntstatus(ndr_err);
663 return status;
665 val = make_tdb_data(blob.data, blob.length);
667 status = dbwrap_store_bystring(db, guid_string, val, TDB_REPLACE);
669 if (NT_STATUS_IS_OK(status)) {
670 op->flags |= SMBXSRV_OPEN_HAVE_REPLAY_CACHE;
671 op->flags &= ~SMBXSRV_OPEN_NEED_REPLAY_CACHE;
674 return status;
677 NTSTATUS smbXsrv_open_purge_replay_cache(struct smbXsrv_client *client,
678 const struct GUID *create_guid)
680 struct GUID_txt_buf buf;
681 char *guid_string;
682 struct db_context *db;
684 if (client->open_table == NULL) {
685 return NT_STATUS_OK;
688 db = client->open_table->local.replay_cache_db_ctx;
690 guid_string = GUID_buf_string(create_guid, &buf);
691 if (guid_string == NULL) {
692 return NT_STATUS_INVALID_PARAMETER;
695 return dbwrap_purge_bystring(db, guid_string);
698 static NTSTATUS smbXsrv_open_clear_replay_cache(struct smbXsrv_open *op)
700 struct GUID *create_guid;
701 struct GUID_txt_buf buf;
702 char *guid_string;
703 struct db_context *db;
704 NTSTATUS status;
706 if (op->table == NULL) {
707 return NT_STATUS_OK;
710 db = op->table->local.replay_cache_db_ctx;
712 if (!(op->flags & SMBXSRV_OPEN_HAVE_REPLAY_CACHE)) {
713 return NT_STATUS_OK;
716 create_guid = &op->global->create_guid;
717 if (GUID_all_zero(create_guid)) {
718 return NT_STATUS_OK;
721 guid_string = GUID_buf_string(create_guid, &buf);
722 if (guid_string == NULL) {
723 return NT_STATUS_INVALID_PARAMETER;
726 status = dbwrap_purge_bystring(db, guid_string);
728 if (NT_STATUS_IS_OK(status)) {
729 op->flags &= ~SMBXSRV_OPEN_HAVE_REPLAY_CACHE;
732 return status;
735 NTSTATUS smbXsrv_open_update(struct smbXsrv_open *op)
737 struct smbXsrv_open_table *table = op->table;
738 NTSTATUS status;
740 if (op->global->db_rec != NULL) {
741 DEBUG(0, ("smbXsrv_open_update(0x%08x): "
742 "Called with db_rec != NULL'\n",
743 op->global->open_global_id));
744 return NT_STATUS_INTERNAL_ERROR;
747 op->global->db_rec = smbXsrv_open_global_fetch_locked(
748 table->global.db_ctx,
749 op->global->open_global_id,
750 op->global /* TALLOC_CTX */);
751 if (op->global->db_rec == NULL) {
752 return NT_STATUS_INTERNAL_DB_ERROR;
755 status = smbXsrv_open_global_store(
756 op->global->db_rec,
757 dbwrap_record_get_key(op->global->db_rec),
758 dbwrap_record_get_value(op->global->db_rec),
759 op->global);
760 TALLOC_FREE(op->global->db_rec);
761 if (!NT_STATUS_IS_OK(status)) {
762 DEBUG(0,("smbXsrv_open_update: "
763 "global_id (0x%08x) store failed - %s\n",
764 op->global->open_global_id,
765 nt_errstr(status)));
766 return status;
769 status = smbXsrv_open_set_replay_cache(op);
770 if (!NT_STATUS_IS_OK(status)) {
771 DBG_ERR("smbXsrv_open_set_replay_cache failed: %s\n",
772 nt_errstr(status));
773 return status;
776 if (CHECK_DEBUGLVL(10)) {
777 struct smbXsrv_openB open_blob = {
778 .version = SMBXSRV_VERSION_0,
779 .info.info0 = op,
782 DEBUG(10,("smbXsrv_open_update: global_id (0x%08x) stored\n",
783 op->global->open_global_id));
784 NDR_PRINT_DEBUG(smbXsrv_openB, &open_blob);
787 return NT_STATUS_OK;
790 NTSTATUS smbXsrv_open_close(struct smbXsrv_open *op, NTTIME now)
792 struct smbXsrv_open_table *table;
793 struct db_record *global_rec = NULL;
794 NTSTATUS status;
795 NTSTATUS error = NT_STATUS_OK;
796 int ret;
798 error = smbXsrv_open_clear_replay_cache(op);
799 if (!NT_STATUS_IS_OK(error)) {
800 DBG_ERR("smbXsrv_open_clear_replay_cache failed: %s\n",
801 nt_errstr(error));
804 if (op->table == NULL) {
805 return error;
808 table = op->table;
809 op->table = NULL;
811 op->status = NT_STATUS_FILE_CLOSED;
812 op->global->disconnect_time = now;
813 server_id_set_disconnected(&op->global->server_id);
815 global_rec = op->global->db_rec;
816 op->global->db_rec = NULL;
817 if (global_rec == NULL) {
818 global_rec = smbXsrv_open_global_fetch_locked(
819 table->global.db_ctx,
820 op->global->open_global_id,
821 op->global /* TALLOC_CTX */);
822 if (global_rec == NULL) {
823 error = NT_STATUS_INTERNAL_ERROR;
827 if (global_rec != NULL && op->global->durable) {
829 * If it is a durable open we need to update the global part
830 * instead of deleting it
832 op->global->db_rec = global_rec;
833 status = smbXsrv_open_global_store(
834 op->global->db_rec,
835 dbwrap_record_get_key(op->global->db_rec),
836 dbwrap_record_get_value(op->global->db_rec),
837 op->global);
838 TALLOC_FREE(op->global->db_rec);
839 global_rec = NULL;
841 if (!NT_STATUS_IS_OK(status)) {
842 DEBUG(0,("smbXsrv_open_close(0x%08x)"
843 "smbXsrv_open_global_store() failed - %s\n",
844 op->global->open_global_id,
845 nt_errstr(status)));
846 error = status;
849 if (NT_STATUS_IS_OK(status) && CHECK_DEBUGLVL(10)) {
850 struct smbXsrv_openB open_blob = {
851 .version = SMBXSRV_VERSION_0,
852 .info.info0 = op,
855 DEBUG(10,("smbXsrv_open_close(0x%08x): "
856 "stored disconnect\n",
857 op->global->open_global_id));
858 NDR_PRINT_DEBUG(smbXsrv_openB, &open_blob);
862 if (global_rec != NULL) {
863 status = dbwrap_record_delete(global_rec);
864 if (!NT_STATUS_IS_OK(status)) {
865 TDB_DATA key = dbwrap_record_get_key(global_rec);
867 DEBUG(0, ("smbXsrv_open_close(0x%08x): "
868 "failed to delete global key '%s': %s\n",
869 op->global->open_global_id,
870 tdb_data_dbg(key),
871 nt_errstr(status)));
872 error = status;
875 TALLOC_FREE(global_rec);
877 ret = idr_remove(table->local.idr, op->local_id);
878 SMB_ASSERT(ret == 0);
880 table->local.num_opens -= 1;
882 if (op->compat) {
883 op->compat->op = NULL;
884 file_free(NULL, op->compat);
885 op->compat = NULL;
888 return error;
891 NTSTATUS smb1srv_open_table_init(struct smbXsrv_connection *conn)
893 uint32_t max_opens;
896 * Allow a range from 1..65534.
898 * With real_max_open_files possible ids,
899 * truncated to the SMB1 limit of 16-bit.
901 * 0 and 0xFFFF are no valid ids.
903 max_opens = conn->client->sconn->real_max_open_files;
904 max_opens = MIN(max_opens, UINT16_MAX - 1);
906 return smbXsrv_open_table_init(conn, 1, UINT16_MAX - 1, max_opens);
909 NTSTATUS smb1srv_open_lookup(struct smbXsrv_connection *conn,
910 uint16_t fnum, NTTIME now,
911 struct smbXsrv_open **_open)
913 struct smbXsrv_open_table *table = conn->client->open_table;
914 uint32_t local_id = fnum;
915 uint32_t global_id = 0;
917 return smbXsrv_open_local_lookup(table, local_id, global_id, now, _open);
920 NTSTATUS smb2srv_open_table_init(struct smbXsrv_connection *conn)
922 uint32_t max_opens;
923 uint32_t highest_id;
926 * Allow a range from 1..4294967294.
928 * With real_max_open_files possible ids,
929 * truncated to 16-bit (the same as SMB1 for now).
931 * 0 and 0xFFFFFFFF are no valid ids.
933 * The usage of conn->sconn->real_max_open_files
934 * is the reason that we use one open table per
935 * transport connection (as we still have a 1:1 mapping
936 * between process and transport connection).
938 max_opens = conn->client->sconn->real_max_open_files;
939 max_opens = MIN(max_opens, UINT16_MAX - 1);
942 * idtree uses "int" for local IDs. Limit the maximum ID to
943 * what "int" can hold.
945 highest_id = UINT32_MAX-1;
946 highest_id = MIN(highest_id, INT_MAX);
948 return smbXsrv_open_table_init(conn, 1, highest_id, max_opens);
951 NTSTATUS smb2srv_open_lookup(struct smbXsrv_connection *conn,
952 uint64_t persistent_id,
953 uint64_t volatile_id,
954 NTTIME now,
955 struct smbXsrv_open **_open)
957 struct smbXsrv_open_table *table = conn->client->open_table;
958 uint32_t local_id = volatile_id & UINT32_MAX;
959 uint64_t local_zeros = volatile_id & 0xFFFFFFFF00000000LLU;
960 uint32_t global_id = persistent_id & UINT32_MAX;
961 uint64_t global_zeros = persistent_id & 0xFFFFFFFF00000000LLU;
962 NTSTATUS status;
964 if (local_zeros != 0) {
965 return NT_STATUS_FILE_CLOSED;
968 if (global_zeros != 0) {
969 return NT_STATUS_FILE_CLOSED;
972 if (global_id == 0) {
973 return NT_STATUS_FILE_CLOSED;
976 status = smbXsrv_open_local_lookup(table, local_id, global_id, now,
977 _open);
978 if (!NT_STATUS_IS_OK(status)) {
979 return status;
983 * Clear the replay cache for this create_guid if it exists:
984 * This is based on the assumption that this lookup will be
985 * triggered by a client request using the file-id for lookup.
986 * Hence the client has proven that it has in fact seen the
987 * reply to its initial create call. So subsequent create replays
988 * should be treated as invalid. Hence the index for create_guid
989 * lookup needs to be removed.
991 status = smbXsrv_open_clear_replay_cache(*_open);
993 return status;
997 * This checks or marks the replay cache, we have the following
998 * cases:
1000 * 1. There is no record in the cache
1001 * => we add the passes caller_req_guid as holder_req_guid
1002 * together with local_id as 0.
1003 * => We return STATUS_FWP_RESERVED in order to indicate
1004 * that the caller holds the current reservation
1006 * 2. There is a record in the cache and holder_req_guid
1007 * is already the same as caller_req_guid and local_id is 0
1008 * => We return STATUS_FWP_RESERVED in order to indicate
1009 * that the caller holds the current reservation
1011 * 3. There is a record in the cache with a holder_req_guid
1012 * other than caller_req_guid (and local_id is 0):
1013 * => We return NT_STATUS_FILE_NOT_AVAILABLE to indicate
1014 * the original request is still pending
1016 * 4. There is a record in the cache with a zero holder_req_guid
1017 * and a valid local_id:
1018 * => We lookup the existing open by local_id
1019 * => We return NT_STATUS_OK together with the smbXsrv_open
1022 * With NT_STATUS_OK the caller can continue the replay processing.
1024 * With STATUS_FWP_RESERVED the caller should continue the normal
1025 * open processing:
1026 * - On success:
1027 * - smbXsrv_open_update()/smbXsrv_open_set_replay_cache()
1028 * will convert the record to a zero holder_req_guid
1029 * with a valid local_id.
1030 * - On failure:
1031 * - smbXsrv_open_purge_replay_cache() should cleanup
1032 * the reservation.
1034 * All other values should be returned to the client,
1035 * while NT_STATUS_FILE_NOT_AVAILABLE will trigger the
1036 * retry loop on the client.
1038 NTSTATUS smb2srv_open_lookup_replay_cache(struct smbXsrv_connection *conn,
1039 struct GUID caller_req_guid,
1040 struct GUID create_guid,
1041 const char *name,
1042 NTTIME now,
1043 struct smbXsrv_open **_open)
1045 TALLOC_CTX *frame = talloc_stackframe();
1046 NTSTATUS status;
1047 struct smbXsrv_open_table *table = conn->client->open_table;
1048 struct db_context *db = table->local.replay_cache_db_ctx;
1049 struct GUID_txt_buf tmp_guid_buf;
1050 struct GUID_txt_buf _create_guid_buf;
1051 const char *create_guid_str = GUID_buf_string(&create_guid, &_create_guid_buf);
1052 TDB_DATA create_guid_key = string_term_tdb_data(create_guid_str);
1053 struct db_record *db_rec = NULL;
1054 struct smbXsrv_open *op = NULL;
1055 struct smbXsrv_open_replay_cache rc = {
1056 .holder_req_guid = caller_req_guid,
1057 .idle_time = now,
1058 .local_id = 0,
1060 enum ndr_err_code ndr_err;
1061 DATA_BLOB blob = data_blob_null;
1062 TDB_DATA val;
1064 *_open = NULL;
1066 db_rec = dbwrap_fetch_locked(db, frame, create_guid_key);
1067 if (db_rec == NULL) {
1068 TALLOC_FREE(frame);
1069 return NT_STATUS_INTERNAL_DB_ERROR;
1072 val = dbwrap_record_get_value(db_rec);
1073 if (val.dsize == 0) {
1074 uint8_t data[SMBXSRV_OPEN_REPLAY_CACHE_FIXED_SIZE];
1076 blob = data_blob_const(data, ARRAY_SIZE(data));
1077 ndr_err = ndr_push_struct_into_fixed_blob(&blob, &rc,
1078 (ndr_push_flags_fn_t)ndr_push_smbXsrv_open_replay_cache);
1079 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1080 status = ndr_map_error2ntstatus(ndr_err);
1081 TALLOC_FREE(frame);
1082 return status;
1085 val = make_tdb_data(blob.data, blob.length);
1086 status = dbwrap_record_store(db_rec, val, TDB_REPLACE);
1087 if (!NT_STATUS_IS_OK(status)) {
1088 TALLOC_FREE(frame);
1089 return status;
1093 * We're the new holder
1095 *_open = NULL;
1096 TALLOC_FREE(frame);
1097 return NT_STATUS_FWP_RESERVED;
1100 if (val.dsize != SMBXSRV_OPEN_REPLAY_CACHE_FIXED_SIZE) {
1101 TALLOC_FREE(frame);
1102 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1105 blob = data_blob_const(val.dptr, val.dsize);
1106 ndr_err = ndr_pull_struct_blob_all_noalloc(&blob, &rc,
1107 (ndr_pull_flags_fn_t)ndr_pull_smbXsrv_open_replay_cache);
1108 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1109 status = ndr_map_error2ntstatus(ndr_err);
1110 TALLOC_FREE(frame);
1111 return status;
1113 if (rc.local_id != 0) {
1114 if (GUID_equal(&rc.holder_req_guid, &caller_req_guid)) {
1116 * This should not happen
1118 status = NT_STATUS_INTERNAL_ERROR;
1119 DBG_ERR("caller %s already holds local_id %u for create %s [%s] - %s\n",
1120 GUID_buf_string(&caller_req_guid, &tmp_guid_buf),
1121 (unsigned)rc.local_id,
1122 create_guid_str,
1123 name,
1124 nt_errstr(status));
1126 TALLOC_FREE(frame);
1127 return status;
1130 status = smbXsrv_open_local_lookup(table,
1131 rc.local_id,
1132 0, /* global_id */
1133 now,
1134 &op);
1135 if (!NT_STATUS_IS_OK(status)) {
1136 DBG_ERR("holder %s stale for local_id %u for create %s [%s] - %s\n",
1137 GUID_buf_string(&rc.holder_req_guid, &tmp_guid_buf),
1138 (unsigned)rc.local_id,
1139 create_guid_str,
1140 name,
1141 nt_errstr(status));
1143 TALLOC_FREE(frame);
1144 return status;
1148 * We found an open the caller can reuse.
1150 SMB_ASSERT(op != NULL);
1151 *_open = op;
1152 TALLOC_FREE(frame);
1153 return NT_STATUS_OK;
1156 if (GUID_equal(&rc.holder_req_guid, &caller_req_guid)) {
1158 * We're still the holder
1160 *_open = NULL;
1161 TALLOC_FREE(frame);
1162 return NT_STATUS_FWP_RESERVED;
1166 * The original request (or a former replay) is still
1167 * pending, ask the client to retry by sending FILE_NOT_AVAILABLE.
1169 status = NT_STATUS_FILE_NOT_AVAILABLE;
1170 DBG_DEBUG("holder %s still pending for create %s [%s] - %s\n",
1171 GUID_buf_string(&rc.holder_req_guid, &tmp_guid_buf),
1172 create_guid_str,
1173 name,
1174 nt_errstr(status));
1175 TALLOC_FREE(frame);
1176 return status;
1179 NTSTATUS smb2srv_open_recreate(struct smbXsrv_connection *conn,
1180 struct auth_session_info *session_info,
1181 uint64_t persistent_id,
1182 const struct GUID *create_guid,
1183 NTTIME now,
1184 struct smbXsrv_open **_open)
1186 struct smbXsrv_open_table *table = conn->client->open_table;
1187 struct smbXsrv_open *op = NULL;
1188 uint32_t global_id;
1189 NTSTATUS status;
1190 struct security_token *current_token = NULL;
1191 int local_id;
1193 if (session_info == NULL) {
1194 DEBUG(10, ("session_info=NULL\n"));
1195 return NT_STATUS_INVALID_HANDLE;
1197 current_token = session_info->security_token;
1199 if (current_token == NULL) {
1200 DEBUG(10, ("current_token=NULL\n"));
1201 return NT_STATUS_INVALID_HANDLE;
1204 if ((persistent_id & 0xFFFFFFFF00000000LLU) != 0) {
1206 * We only use 32 bit for the persistent ID
1208 DBG_DEBUG("persistent_id=%"PRIx64"\n", persistent_id);
1209 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1211 global_id = persistent_id & UINT32_MAX; /* truncate to 32 bit */
1213 op = talloc_zero(table, struct smbXsrv_open);
1214 if (op == NULL) {
1215 return NT_STATUS_NO_MEMORY;
1217 op->table = table;
1219 status = smbXsrv_open_global_lookup(table, global_id, op, &op->global);
1220 if (!NT_STATUS_IS_OK(status)) {
1221 TALLOC_FREE(op);
1222 DEBUG(10, ("smbXsrv_open_global_lookup returned %s\n",
1223 nt_errstr(status)));
1224 return status;
1228 * If the provided create_guid is NULL, this means that
1229 * the reconnect request was a v1 request. In that case
1230 * we should skip the create GUID verification, since
1231 * it is valid to v1-reconnect a v2-opened handle.
1233 if ((create_guid != NULL) &&
1234 !GUID_equal(&op->global->create_guid, create_guid))
1236 TALLOC_FREE(op);
1237 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1240 if (!security_token_is_sid(current_token, &op->global->open_owner)) {
1241 TALLOC_FREE(op);
1242 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1245 if (!op->global->durable) {
1246 TALLOC_FREE(op);
1247 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1250 if (table->local.num_opens >= table->local.max_opens) {
1251 TALLOC_FREE(op);
1252 return NT_STATUS_INSUFFICIENT_RESOURCES;
1255 local_id = idr_get_new_random(
1256 table->local.idr,
1258 table->local.lowest_id,
1259 table->local.highest_id);
1260 if (local_id == -1) {
1261 TALLOC_FREE(op);
1262 return NT_STATUS_INSUFFICIENT_RESOURCES;
1265 op->local_id = local_id;
1267 op->idle_time = now;
1268 op->status = NT_STATUS_FILE_CLOSED;
1270 op->global->open_volatile_id = op->local_id;
1271 op->global->server_id = messaging_server_id(conn->client->msg_ctx);
1273 table->local.num_opens += 1;
1275 talloc_set_destructor(op, smbXsrv_open_destructor);
1277 status = smbXsrv_open_global_store(
1278 op->global->db_rec,
1279 dbwrap_record_get_key(op->global->db_rec),
1280 dbwrap_record_get_value(op->global->db_rec),
1281 op->global);
1282 TALLOC_FREE(op->global->db_rec);
1283 if (!NT_STATUS_IS_OK(status)) {
1284 TALLOC_FREE(op);
1285 return status;
1288 if (CHECK_DEBUGLVL(10)) {
1289 struct smbXsrv_openB open_blob = {
1290 .info.info0 = op,
1293 DEBUG(10,("smbXsrv_open_recreate: global_id (0x%08x) stored\n",
1294 op->global->open_global_id));
1295 NDR_PRINT_DEBUG(smbXsrv_openB, &open_blob);
1298 *_open = op;
1299 return NT_STATUS_OK;
1303 struct smbXsrv_open_global_traverse_state {
1304 int (*fn)(struct smbXsrv_open_global0 *, void *);
1305 void *private_data;
1308 static int smbXsrv_open_global_traverse_fn(struct db_record *rec, void *data)
1310 struct smbXsrv_open_global_traverse_state *state =
1311 (struct smbXsrv_open_global_traverse_state*)data;
1312 struct smbXsrv_open_global0 *global = NULL;
1313 TDB_DATA key = dbwrap_record_get_key(rec);
1314 TDB_DATA val = dbwrap_record_get_value(rec);
1315 NTSTATUS status;
1316 int ret = -1;
1318 status = smbXsrv_open_global_parse_record(
1319 talloc_tos(), key, val, &global);
1320 if (!NT_STATUS_IS_OK(status)) {
1321 return -1;
1324 global->db_rec = rec;
1325 ret = state->fn(global, state->private_data);
1326 talloc_free(global);
1327 return ret;
1330 NTSTATUS smbXsrv_open_global_traverse(
1331 int (*fn)(struct smbXsrv_open_global0 *, void *),
1332 void *private_data)
1335 NTSTATUS status;
1336 int count = 0;
1337 struct smbXsrv_open_global_traverse_state state = {
1338 .fn = fn,
1339 .private_data = private_data,
1342 become_root();
1343 status = smbXsrv_open_global_init();
1344 if (!NT_STATUS_IS_OK(status)) {
1345 unbecome_root();
1346 DEBUG(0, ("Failed to initialize open_global: %s\n",
1347 nt_errstr(status)));
1348 return status;
1351 status = dbwrap_traverse_read(smbXsrv_open_global_db_ctx,
1352 smbXsrv_open_global_traverse_fn,
1353 &state,
1354 &count);
1355 unbecome_root();
1357 return status;
1360 NTSTATUS smbXsrv_open_cleanup(uint64_t persistent_id)
1362 NTSTATUS status = NT_STATUS_OK;
1363 TALLOC_CTX *frame = talloc_stackframe();
1364 struct smbXsrv_open_global0 *op = NULL;
1365 TDB_DATA key, val;
1366 struct db_record *rec;
1367 bool delete_open = false;
1368 uint32_t global_id = persistent_id & UINT32_MAX;
1370 rec = smbXsrv_open_global_fetch_locked(smbXsrv_open_global_db_ctx,
1371 global_id,
1372 frame);
1373 if (rec == NULL) {
1374 status = NT_STATUS_NOT_FOUND;
1375 goto done;
1378 key = dbwrap_record_get_key(rec);
1379 val = dbwrap_record_get_value(rec);
1381 if (val.dsize == 0) {
1382 DEBUG(10, ("smbXsrv_open_cleanup[global: 0x%08x] "
1383 "empty record in %s, skipping...\n",
1384 global_id, dbwrap_name(smbXsrv_open_global_db_ctx)));
1385 goto done;
1388 status = smbXsrv_open_global_parse_record(
1389 talloc_tos(), key, val, &op);
1390 if (!NT_STATUS_IS_OK(status)) {
1391 DEBUG(1, ("smbXsrv_open_cleanup[global: 0x%08x] "
1392 "failed to read record: %s\n",
1393 global_id, nt_errstr(status)));
1394 goto done;
1397 if (server_id_is_disconnected(&op->server_id)) {
1398 struct timeval now, disconnect_time;
1399 int64_t tdiff;
1400 now = timeval_current();
1401 nttime_to_timeval(&disconnect_time, op->disconnect_time);
1402 tdiff = usec_time_diff(&now, &disconnect_time);
1403 delete_open = (tdiff >= 1000*op->durable_timeout_msec);
1405 DEBUG(10, ("smbXsrv_open_cleanup[global: 0x%08x] "
1406 "disconnected at [%s] %us ago with "
1407 "timeout of %us -%s reached\n",
1408 global_id,
1409 nt_time_string(frame, op->disconnect_time),
1410 (unsigned)(tdiff/1000000),
1411 op->durable_timeout_msec / 1000,
1412 delete_open ? "" : " not"));
1413 } else if (!serverid_exists(&op->server_id)) {
1414 struct server_id_buf idbuf;
1415 DEBUG(10, ("smbXsrv_open_cleanup[global: 0x%08x] "
1416 "server[%s] does not exist\n",
1417 global_id,
1418 server_id_str_buf(op->server_id, &idbuf)));
1419 delete_open = true;
1422 if (!delete_open) {
1423 goto done;
1426 status = dbwrap_record_delete(rec);
1427 if (!NT_STATUS_IS_OK(status)) {
1428 DEBUG(1, ("smbXsrv_open_cleanup[global: 0x%08x] "
1429 "failed to delete record"
1430 "from %s: %s\n", global_id,
1431 dbwrap_name(smbXsrv_open_global_db_ctx),
1432 nt_errstr(status)));
1433 goto done;
1436 DEBUG(10, ("smbXsrv_open_cleanup[global: 0x%08x] "
1437 "delete record from %s\n",
1438 global_id,
1439 dbwrap_name(smbXsrv_open_global_db_ctx)));
1441 done:
1442 talloc_free(frame);
1443 return status;