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"
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"
32 #include "lib/util/util_tdb.h"
33 #include "librpc/gen_ndr/ndr_smbXsrv.h"
35 #include "source3/include/util_tdb.h"
36 #include "lib/util/idtree_random.h"
38 struct smbXsrv_open_table {
40 struct idr_context *idr;
41 struct db_context *replay_cache_db_ctx;
48 struct db_context *db_ctx;
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) {
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,
74 TALLOC_FREE(global_path);
78 status = map_nt_error_from_unix_common(errno);
83 smbXsrv_open_global_db_ctx = db_ctx;
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
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);
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,
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);
123 DBG_DEBUG("Failed to lock global id 0x%08x, key '%s'\n", id,
130 static NTSTATUS smbXsrv_open_table_init(struct smbXsrv_connection *conn,
135 struct smbXsrv_client *client = conn->client;
136 struct smbXsrv_open_table *table;
140 if (lowest_id > highest_id) {
141 return NT_STATUS_INTERNAL_ERROR;
144 max_range = highest_id;
145 max_range -= lowest_id;
148 if (max_opens > max_range) {
149 return NT_STATUS_INTERNAL_ERROR;
152 table = talloc_zero(client, struct smbXsrv_open_table);
154 return NT_STATUS_NO_MEMORY;
157 table->local.idr = idr_init(table);
158 if (table->local.idr == NULL) {
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) {
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)) {
177 table->global.db_ctx = smbXsrv_open_global_db_ctx;
179 client->open_table = table;
183 static NTSTATUS smbXsrv_open_local_lookup(struct smbXsrv_open_table *table,
184 uint32_t open_local_id,
185 uint32_t open_global_id,
187 struct smbXsrv_open **_open)
189 struct smbXsrv_open *op = NULL;
193 if (open_local_id == 0) {
194 return NT_STATUS_FILE_CLOSED;
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);
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;
228 static NTSTATUS smbXsrv_open_global_verify_record(
232 struct smbXsrv_open_global0 **_global0);
234 static NTSTATUS smbXsrv_open_global_allocate(
235 struct db_context *db, struct smbXsrv_open_global0 *global)
238 uint32_t last_free = 0;
239 const uint32_t min_tries = 3;
242 * Here we just randomly try the whole 32-bit space
244 * We use just 32-bit, because we want to reuse the
247 for (i = 0; i < UINT32_MAX; i++) {
248 struct smbXsrv_open_global_key_buf key_buf;
249 struct smbXsrv_open_global0 *tmp_global0 = NULL;
254 if (i >= min_tries && last_free != 0) {
257 id = generate_random();
262 if (id == UINT32_MAX) {
266 key = smbXsrv_open_global_id_to_key(id, &key_buf);
268 global->db_rec = dbwrap_fetch_locked(db, global, key);
269 if (global->db_rec == NULL) {
270 return NT_STATUS_INSUFFICIENT_RESOURCES;
272 val = dbwrap_record_get_value(global->db_rec);
274 status = smbXsrv_open_global_verify_record(
275 key, val, talloc_tos(), &tmp_global0);
277 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
279 * Found an empty slot
281 global->open_global_id = id;
285 TALLOC_FREE(tmp_global0);
287 if (NT_STATUS_EQUAL(status, NT_STATUS_FATAL_APP_EXIT)) {
291 status = dbwrap_record_delete(global->db_rec);
292 if (!NT_STATUS_IS_OK(status)) {
293 DBG_WARNING("dbwrap_record_delete() failed "
294 "for record %"PRIu32": %s\n",
297 return NT_STATUS_INTERNAL_DB_CORRUPTION;
300 if ((i < min_tries) && (last_free == 0)) {
302 * Remember "id" as free but also try
303 * others to not recycle ids too
310 if (!NT_STATUS_IS_OK(status)) {
311 DBG_WARNING("smbXsrv_open_global_verify_record() "
312 "failed for %s: %s, ignoring\n",
317 TALLOC_FREE(global->db_rec);
320 /* should not be reached */
321 return NT_STATUS_INTERNAL_ERROR;
324 static NTSTATUS smbXsrv_open_global_parse_record(
328 struct smbXsrv_open_global0 **global)
330 DATA_BLOB blob = data_blob_const(val.dptr, val.dsize);
331 struct smbXsrv_open_globalB global_blob;
332 enum ndr_err_code ndr_err;
334 TALLOC_CTX *frame = talloc_stackframe();
336 ndr_err = ndr_pull_struct_blob(&blob, frame, &global_blob,
337 (ndr_pull_flags_fn_t)ndr_pull_smbXsrv_open_globalB);
338 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
339 DEBUG(1,("Invalid record in smbXsrv_open_global.tdb:"
340 "key '%s' ndr_pull_struct_blob - %s\n",
342 ndr_errstr(ndr_err)));
343 status = ndr_map_error2ntstatus(ndr_err);
347 if (global_blob.version != SMBXSRV_VERSION_0) {
348 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
349 DEBUG(1,("Invalid record in smbXsrv_open_global.tdb:"
350 "key '%s' unsupported version - %d - %s\n",
352 (int)global_blob.version,
357 if (global_blob.info.info0 == NULL) {
358 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
359 DEBUG(1,("Invalid record in smbXsrv_tcon_global.tdb:"
360 "key '%s' info0 NULL pointer - %s\n",
366 *global = talloc_move(mem_ctx, &global_blob.info.info0);
367 status = NT_STATUS_OK;
373 static NTSTATUS smbXsrv_open_global_verify_record(
377 struct smbXsrv_open_global0 **_global0)
379 DATA_BLOB blob = { .data = val.dptr, .length = val.dsize, };
380 struct smbXsrv_open_globalB global_blob;
381 enum ndr_err_code ndr_err;
382 struct smbXsrv_open_global0 *global0 = NULL;
383 struct server_id_buf buf;
385 if (val.dsize == 0) {
386 return NT_STATUS_NOT_FOUND;
389 ndr_err = ndr_pull_struct_blob(
393 (ndr_pull_flags_fn_t)ndr_pull_smbXsrv_open_globalB);
394 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
395 DBG_WARNING("key '%s' ndr_pull_struct_blob - %s\n",
397 ndr_map_error2string(ndr_err));
398 return ndr_map_error2ntstatus(ndr_err);
402 if (CHECK_DEBUGLVL(10)) {
403 NDR_PRINT_DEBUG(smbXsrv_open_globalB, &global_blob);
406 if (global_blob.version != SMBXSRV_VERSION_0) {
407 DBG_ERR("key '%s' use unsupported version %u\n",
409 global_blob.version);
410 NDR_PRINT_DEBUG(smbXsrv_open_globalB, &global_blob);
411 return NT_STATUS_INTERNAL_ERROR;
414 global0 = global_blob.info.info0;
417 if (server_id_is_disconnected(&global0->server_id)) {
420 if (serverid_exists(&global0->server_id)) {
424 DBG_WARNING("smbd %s did not clean up record %s\n",
425 server_id_str_buf(global0->server_id, &buf),
428 return NT_STATUS_FATAL_APP_EXIT;
431 static NTSTATUS smbXsrv_open_global_store(struct smbXsrv_open_global0 *global)
433 struct smbXsrv_open_globalB global_blob;
434 DATA_BLOB blob = data_blob_null;
438 enum ndr_err_code ndr_err;
441 * TODO: if we use other versions than '0'
442 * we would add glue code here, that would be able to
443 * store the information in the old format.
446 key = dbwrap_record_get_key(global->db_rec);
447 val = dbwrap_record_get_value(global->db_rec);
449 global_blob = (struct smbXsrv_open_globalB) {
450 .version = smbXsrv_version_global_current(),
453 if (val.dsize >= 8) {
454 global_blob.seqnum = IVAL(val.dptr, 4);
456 global_blob.seqnum += 1;
457 global_blob.info.info0 = global;
459 ndr_err = ndr_push_struct_blob(&blob, talloc_tos(), &global_blob,
460 (ndr_push_flags_fn_t)ndr_push_smbXsrv_open_globalB);
461 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
462 DBG_WARNING("key '%s' ndr_push - %s\n",
464 ndr_map_error2string(ndr_err));
465 TALLOC_FREE(global->db_rec);
466 return ndr_map_error2ntstatus(ndr_err);
469 val = make_tdb_data(blob.data, blob.length);
470 status = dbwrap_record_store(global->db_rec, val, TDB_REPLACE);
471 TALLOC_FREE(blob.data);
472 if (!NT_STATUS_IS_OK(status)) {
473 DBG_WARNING("key '%s' store - %s\n",
476 TALLOC_FREE(global->db_rec);
480 if (CHECK_DEBUGLVL(10)) {
481 DBG_DEBUG("key '%s' stored\n", tdb_data_dbg(key));
482 NDR_PRINT_DEBUG(smbXsrv_open_globalB, &global_blob);
485 TALLOC_FREE(global->db_rec);
490 static NTSTATUS smbXsrv_open_global_lookup(struct smbXsrv_open_table *table,
491 uint32_t open_global_id,
493 struct smbXsrv_open_global0 **_global)
495 struct smbXsrv_open_global_key_buf key_buf;
496 TDB_DATA key = smbXsrv_open_global_id_to_key(open_global_id, &key_buf);
498 struct db_record *global_rec = NULL;
503 if (table->global.db_ctx == NULL) {
504 return NT_STATUS_INTERNAL_ERROR;
507 global_rec = dbwrap_fetch_locked(table->global.db_ctx, mem_ctx, key);
508 if (global_rec == NULL) {
509 return NT_STATUS_INTERNAL_DB_ERROR;
511 val = dbwrap_record_get_value(global_rec);
513 status = smbXsrv_open_global_verify_record(key, val, mem_ctx, _global);
514 if (NT_STATUS_IS_OK(status)) {
515 (*_global)->db_rec = talloc_move(*_global, &global_rec);
519 TALLOC_FREE(global_rec);
521 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
522 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
528 static int smbXsrv_open_destructor(struct smbXsrv_open *op)
532 status = smbXsrv_open_close(op, 0);
533 if (!NT_STATUS_IS_OK(status)) {
534 DEBUG(0, ("smbXsrv_open_destructor: "
535 "smbXsrv_open_close() failed - %s\n",
539 TALLOC_FREE(op->global);
544 NTSTATUS smbXsrv_open_create(struct smbXsrv_connection *conn,
545 struct auth_session_info *session_info,
547 struct smbXsrv_open **_open)
549 struct smbXsrv_open_table *table = conn->client->open_table;
550 struct smbXsrv_open *op = NULL;
551 struct smbXsrv_open_global0 *global = NULL;
553 struct dom_sid *current_sid = NULL;
554 struct security_token *current_token = NULL;
557 if (session_info == NULL) {
558 return NT_STATUS_INVALID_HANDLE;
560 current_token = session_info->security_token;
562 if (current_token == NULL) {
563 return NT_STATUS_INVALID_HANDLE;
566 if (current_token->num_sids > PRIMARY_USER_SID_INDEX) {
567 current_sid = ¤t_token->sids[PRIMARY_USER_SID_INDEX];
570 if (current_sid == NULL) {
571 return NT_STATUS_INVALID_HANDLE;
574 if (table->local.num_opens >= table->local.max_opens) {
575 return NT_STATUS_INSUFFICIENT_RESOURCES;
578 op = talloc_zero(table, struct smbXsrv_open);
580 return NT_STATUS_NO_MEMORY;
583 op->status = NT_STATUS_OK; /* TODO: start with INTERNAL_ERROR */
586 global = talloc_zero(op, struct smbXsrv_open_global0);
587 if (global == NULL) {
589 return NT_STATUS_NO_MEMORY;
594 * We mark every slot as invalid using 0xFF.
595 * Valid values are masked with 0xF.
597 memset(global->lock_sequence_array, 0xFF,
598 sizeof(global->lock_sequence_array));
600 status = smbXsrv_open_global_allocate(table->global.db_ctx, global);
601 if (!NT_STATUS_IS_OK(status)) {
606 local_id = idr_get_new_random(
609 table->local.lowest_id,
610 table->local.highest_id);
611 if (local_id == -1) {
613 return NT_STATUS_INSUFFICIENT_RESOURCES;
615 op->local_id = local_id;
617 global->open_persistent_id = global->open_global_id;
618 global->open_volatile_id = op->local_id;
620 global->server_id = messaging_server_id(conn->client->msg_ctx);
621 global->open_time = now;
622 global->open_owner = *current_sid;
623 if (conn->protocol >= PROTOCOL_SMB2_10) {
624 global->client_guid = conn->smb2.client.guid;
627 table->local.num_opens += 1;
629 talloc_set_destructor(op, smbXsrv_open_destructor);
631 status = smbXsrv_open_global_store(global);
632 if (!NT_STATUS_IS_OK(status)) {
633 DEBUG(0,("smbXsrv_open_create: "
634 "global_id (0x%08x) store failed - %s\n",
635 op->global->open_global_id,
641 if (CHECK_DEBUGLVL(10)) {
642 struct smbXsrv_openB open_blob = {
643 .version = SMBXSRV_VERSION_0,
647 DEBUG(10,("smbXsrv_open_create: global_id (0x%08x) stored\n",
648 op->global->open_global_id));
649 NDR_PRINT_DEBUG(smbXsrv_openB, &open_blob);
656 static NTSTATUS smbXsrv_open_set_replay_cache(struct smbXsrv_open *op)
658 struct GUID *create_guid;
659 struct GUID_txt_buf buf;
661 struct db_context *db = op->table->local.replay_cache_db_ctx;
662 struct smbXsrv_open_replay_cache rc = {
663 .idle_time = op->idle_time,
664 .local_id = op->local_id,
666 uint8_t data[SMBXSRV_OPEN_REPLAY_CACHE_FIXED_SIZE] = { 0 };
667 DATA_BLOB blob = { .data = data, .length = sizeof(data), };
668 enum ndr_err_code ndr_err;
672 if (!(op->flags & SMBXSRV_OPEN_NEED_REPLAY_CACHE)) {
676 if (op->flags & SMBXSRV_OPEN_HAVE_REPLAY_CACHE) {
680 create_guid = &op->global->create_guid;
681 guid_string = GUID_buf_string(create_guid, &buf);
683 ndr_err = ndr_push_struct_into_fixed_blob(&blob, &rc,
684 (ndr_push_flags_fn_t)ndr_push_smbXsrv_open_replay_cache);
685 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
686 status = ndr_map_error2ntstatus(ndr_err);
689 val = make_tdb_data(blob.data, blob.length);
691 status = dbwrap_store_bystring(db, guid_string, val, TDB_REPLACE);
693 if (NT_STATUS_IS_OK(status)) {
694 op->flags |= SMBXSRV_OPEN_HAVE_REPLAY_CACHE;
695 op->flags &= ~SMBXSRV_OPEN_NEED_REPLAY_CACHE;
701 NTSTATUS smbXsrv_open_purge_replay_cache(struct smbXsrv_client *client,
702 const struct GUID *create_guid)
704 struct GUID_txt_buf buf;
706 struct db_context *db;
708 if (client->open_table == NULL) {
712 db = client->open_table->local.replay_cache_db_ctx;
714 guid_string = GUID_buf_string(create_guid, &buf);
715 if (guid_string == NULL) {
716 return NT_STATUS_INVALID_PARAMETER;
719 return dbwrap_purge_bystring(db, guid_string);
722 static NTSTATUS smbXsrv_open_clear_replay_cache(struct smbXsrv_open *op)
724 struct GUID *create_guid;
725 struct GUID_txt_buf buf;
727 struct db_context *db;
730 if (op->table == NULL) {
734 db = op->table->local.replay_cache_db_ctx;
736 if (!(op->flags & SMBXSRV_OPEN_HAVE_REPLAY_CACHE)) {
740 create_guid = &op->global->create_guid;
741 if (GUID_all_zero(create_guid)) {
745 guid_string = GUID_buf_string(create_guid, &buf);
746 if (guid_string == NULL) {
747 return NT_STATUS_INVALID_PARAMETER;
750 status = dbwrap_purge_bystring(db, guid_string);
752 if (NT_STATUS_IS_OK(status)) {
753 op->flags &= ~SMBXSRV_OPEN_HAVE_REPLAY_CACHE;
759 NTSTATUS smbXsrv_open_update(struct smbXsrv_open *op)
761 struct smbXsrv_open_table *table = op->table;
764 if (op->global->db_rec != NULL) {
765 DEBUG(0, ("smbXsrv_open_update(0x%08x): "
766 "Called with db_rec != NULL'\n",
767 op->global->open_global_id));
768 return NT_STATUS_INTERNAL_ERROR;
771 op->global->db_rec = smbXsrv_open_global_fetch_locked(
772 table->global.db_ctx,
773 op->global->open_global_id,
774 op->global /* TALLOC_CTX */);
775 if (op->global->db_rec == NULL) {
776 return NT_STATUS_INTERNAL_DB_ERROR;
779 status = smbXsrv_open_global_store(op->global);
780 if (!NT_STATUS_IS_OK(status)) {
781 DEBUG(0,("smbXsrv_open_update: "
782 "global_id (0x%08x) store failed - %s\n",
783 op->global->open_global_id,
788 status = smbXsrv_open_set_replay_cache(op);
789 if (!NT_STATUS_IS_OK(status)) {
790 DBG_ERR("smbXsrv_open_set_replay_cache failed: %s\n",
795 if (CHECK_DEBUGLVL(10)) {
796 struct smbXsrv_openB open_blob = {
797 .version = SMBXSRV_VERSION_0,
801 DEBUG(10,("smbXsrv_open_update: global_id (0x%08x) stored\n",
802 op->global->open_global_id));
803 NDR_PRINT_DEBUG(smbXsrv_openB, &open_blob);
809 NTSTATUS smbXsrv_open_close(struct smbXsrv_open *op, NTTIME now)
811 struct smbXsrv_open_table *table;
812 struct db_record *global_rec = NULL;
814 NTSTATUS error = NT_STATUS_OK;
817 error = smbXsrv_open_clear_replay_cache(op);
818 if (!NT_STATUS_IS_OK(error)) {
819 DBG_ERR("smbXsrv_open_clear_replay_cache failed: %s\n",
823 if (op->table == NULL) {
830 op->status = NT_STATUS_FILE_CLOSED;
831 op->global->disconnect_time = now;
832 server_id_set_disconnected(&op->global->server_id);
834 global_rec = op->global->db_rec;
835 op->global->db_rec = NULL;
836 if (global_rec == NULL) {
837 global_rec = smbXsrv_open_global_fetch_locked(
838 table->global.db_ctx,
839 op->global->open_global_id,
840 op->global /* TALLOC_CTX */);
841 if (global_rec == NULL) {
842 error = NT_STATUS_INTERNAL_ERROR;
846 if (global_rec != NULL && op->global->durable) {
848 * If it is a durable open we need to update the global part
849 * instead of deleting it
851 op->global->db_rec = global_rec;
852 status = smbXsrv_open_global_store(op->global);
853 if (NT_STATUS_IS_OK(status)) {
855 * smbXsrv_open_global_store does the free
856 * of op->global->db_rec
860 if (!NT_STATUS_IS_OK(status)) {
861 DEBUG(0,("smbXsrv_open_close(0x%08x)"
862 "smbXsrv_open_global_store() failed - %s\n",
863 op->global->open_global_id,
868 if (NT_STATUS_IS_OK(status) && CHECK_DEBUGLVL(10)) {
869 struct smbXsrv_openB open_blob = {
870 .version = SMBXSRV_VERSION_0,
874 DEBUG(10,("smbXsrv_open_close(0x%08x): "
875 "stored disconnect\n",
876 op->global->open_global_id));
877 NDR_PRINT_DEBUG(smbXsrv_openB, &open_blob);
881 if (global_rec != NULL) {
882 status = dbwrap_record_delete(global_rec);
883 if (!NT_STATUS_IS_OK(status)) {
884 TDB_DATA key = dbwrap_record_get_key(global_rec);
886 DEBUG(0, ("smbXsrv_open_close(0x%08x): "
887 "failed to delete global key '%s': %s\n",
888 op->global->open_global_id,
894 TALLOC_FREE(global_rec);
896 ret = idr_remove(table->local.idr, op->local_id);
897 SMB_ASSERT(ret == 0);
899 table->local.num_opens -= 1;
902 op->compat->op = NULL;
903 file_free(NULL, op->compat);
910 NTSTATUS smb1srv_open_table_init(struct smbXsrv_connection *conn)
915 * Allow a range from 1..65534.
917 * With real_max_open_files possible ids,
918 * truncated to the SMB1 limit of 16-bit.
920 * 0 and 0xFFFF are no valid ids.
922 max_opens = conn->client->sconn->real_max_open_files;
923 max_opens = MIN(max_opens, UINT16_MAX - 1);
925 return smbXsrv_open_table_init(conn, 1, UINT16_MAX - 1, max_opens);
928 NTSTATUS smb1srv_open_lookup(struct smbXsrv_connection *conn,
929 uint16_t fnum, NTTIME now,
930 struct smbXsrv_open **_open)
932 struct smbXsrv_open_table *table = conn->client->open_table;
933 uint32_t local_id = fnum;
934 uint32_t global_id = 0;
936 return smbXsrv_open_local_lookup(table, local_id, global_id, now, _open);
939 NTSTATUS smb2srv_open_table_init(struct smbXsrv_connection *conn)
945 * Allow a range from 1..4294967294.
947 * With real_max_open_files possible ids,
948 * truncated to 16-bit (the same as SMB1 for now).
950 * 0 and 0xFFFFFFFF are no valid ids.
952 * The usage of conn->sconn->real_max_open_files
953 * is the reason that we use one open table per
954 * transport connection (as we still have a 1:1 mapping
955 * between process and transport connection).
957 max_opens = conn->client->sconn->real_max_open_files;
958 max_opens = MIN(max_opens, UINT16_MAX - 1);
961 * idtree uses "int" for local IDs. Limit the maximum ID to
962 * what "int" can hold.
964 highest_id = UINT32_MAX-1;
965 highest_id = MIN(highest_id, INT_MAX);
967 return smbXsrv_open_table_init(conn, 1, highest_id, max_opens);
970 NTSTATUS smb2srv_open_lookup(struct smbXsrv_connection *conn,
971 uint64_t persistent_id,
972 uint64_t volatile_id,
974 struct smbXsrv_open **_open)
976 struct smbXsrv_open_table *table = conn->client->open_table;
977 uint32_t local_id = volatile_id & UINT32_MAX;
978 uint64_t local_zeros = volatile_id & 0xFFFFFFFF00000000LLU;
979 uint32_t global_id = persistent_id & UINT32_MAX;
980 uint64_t global_zeros = persistent_id & 0xFFFFFFFF00000000LLU;
983 if (local_zeros != 0) {
984 return NT_STATUS_FILE_CLOSED;
987 if (global_zeros != 0) {
988 return NT_STATUS_FILE_CLOSED;
991 if (global_id == 0) {
992 return NT_STATUS_FILE_CLOSED;
995 status = smbXsrv_open_local_lookup(table, local_id, global_id, now,
997 if (!NT_STATUS_IS_OK(status)) {
1002 * Clear the replay cache for this create_guid if it exists:
1003 * This is based on the assumption that this lookup will be
1004 * triggered by a client request using the file-id for lookup.
1005 * Hence the client has proven that it has in fact seen the
1006 * reply to its initial create call. So subsequent create replays
1007 * should be treated as invalid. Hence the index for create_guid
1008 * lookup needs to be removed.
1010 status = smbXsrv_open_clear_replay_cache(*_open);
1016 * This checks or marks the replay cache, we have the following
1019 * 1. There is no record in the cache
1020 * => we add the passes caller_req_guid as holder_req_guid
1021 * together with local_id as 0.
1022 * => We return STATUS_FWP_RESERVED in order to indicate
1023 * that the caller holds the current reservation
1025 * 2. There is a record in the cache and holder_req_guid
1026 * is already the same as caller_req_guid and local_id is 0
1027 * => We return STATUS_FWP_RESERVED in order to indicate
1028 * that the caller holds the current reservation
1030 * 3. There is a record in the cache with a holder_req_guid
1031 * other than caller_req_guid (and local_id is 0):
1032 * => We return NT_STATUS_FILE_NOT_AVAILABLE to indicate
1033 * the original request is still pending
1035 * 4. There is a record in the cache with a zero holder_req_guid
1036 * and a valid local_id:
1037 * => We lookup the existing open by local_id
1038 * => We return NT_STATUS_OK together with the smbXsrv_open
1041 * With NT_STATUS_OK the caller can continue the replay processing.
1043 * With STATUS_FWP_RESERVED the caller should continue the normal
1046 * - smbXsrv_open_update()/smbXsrv_open_set_replay_cache()
1047 * will convert the record to a zero holder_req_guid
1048 * with a valid local_id.
1050 * - smbXsrv_open_purge_replay_cache() should cleanup
1053 * All other values should be returned to the client,
1054 * while NT_STATUS_FILE_NOT_AVAILABLE will trigger the
1055 * retry loop on the client.
1057 NTSTATUS smb2srv_open_lookup_replay_cache(struct smbXsrv_connection *conn,
1058 struct GUID caller_req_guid,
1059 struct GUID create_guid,
1062 struct smbXsrv_open **_open)
1064 TALLOC_CTX *frame = talloc_stackframe();
1066 struct smbXsrv_open_table *table = conn->client->open_table;
1067 struct db_context *db = table->local.replay_cache_db_ctx;
1068 struct GUID_txt_buf tmp_guid_buf;
1069 struct GUID_txt_buf _create_guid_buf;
1070 const char *create_guid_str = GUID_buf_string(&create_guid, &_create_guid_buf);
1071 TDB_DATA create_guid_key = string_term_tdb_data(create_guid_str);
1072 struct db_record *db_rec = NULL;
1073 struct smbXsrv_open *op = NULL;
1074 struct smbXsrv_open_replay_cache rc = {
1075 .holder_req_guid = caller_req_guid,
1079 enum ndr_err_code ndr_err;
1080 DATA_BLOB blob = data_blob_null;
1085 db_rec = dbwrap_fetch_locked(db, frame, create_guid_key);
1086 if (db_rec == NULL) {
1088 return NT_STATUS_INTERNAL_DB_ERROR;
1091 val = dbwrap_record_get_value(db_rec);
1092 if (val.dsize == 0) {
1093 uint8_t data[SMBXSRV_OPEN_REPLAY_CACHE_FIXED_SIZE];
1095 blob = data_blob_const(data, ARRAY_SIZE(data));
1096 ndr_err = ndr_push_struct_into_fixed_blob(&blob, &rc,
1097 (ndr_push_flags_fn_t)ndr_push_smbXsrv_open_replay_cache);
1098 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1099 status = ndr_map_error2ntstatus(ndr_err);
1104 val = make_tdb_data(blob.data, blob.length);
1105 status = dbwrap_record_store(db_rec, val, TDB_REPLACE);
1106 if (!NT_STATUS_IS_OK(status)) {
1112 * We're the new holder
1116 return NT_STATUS_FWP_RESERVED;
1119 if (val.dsize != SMBXSRV_OPEN_REPLAY_CACHE_FIXED_SIZE) {
1121 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1124 blob = data_blob_const(val.dptr, val.dsize);
1125 ndr_err = ndr_pull_struct_blob_all_noalloc(&blob, &rc,
1126 (ndr_pull_flags_fn_t)ndr_pull_smbXsrv_open_replay_cache);
1127 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1128 status = ndr_map_error2ntstatus(ndr_err);
1132 if (rc.local_id != 0) {
1133 if (GUID_equal(&rc.holder_req_guid, &caller_req_guid)) {
1135 * This should not happen
1137 status = NT_STATUS_INTERNAL_ERROR;
1138 DBG_ERR("caller %s already holds local_id %u for create %s [%s] - %s\n",
1139 GUID_buf_string(&caller_req_guid, &tmp_guid_buf),
1140 (unsigned)rc.local_id,
1149 status = smbXsrv_open_local_lookup(table,
1154 if (!NT_STATUS_IS_OK(status)) {
1155 DBG_ERR("holder %s stale for local_id %u for create %s [%s] - %s\n",
1156 GUID_buf_string(&rc.holder_req_guid, &tmp_guid_buf),
1157 (unsigned)rc.local_id,
1167 * We found an open the caller can reuse.
1169 SMB_ASSERT(op != NULL);
1172 return NT_STATUS_OK;
1175 if (GUID_equal(&rc.holder_req_guid, &caller_req_guid)) {
1177 * We're still the holder
1181 return NT_STATUS_FWP_RESERVED;
1185 * The original request (or a former replay) is still
1186 * pending, ask the client to retry by sending FILE_NOT_AVAILABLE.
1188 status = NT_STATUS_FILE_NOT_AVAILABLE;
1189 DBG_DEBUG("holder %s still pending for create %s [%s] - %s\n",
1190 GUID_buf_string(&rc.holder_req_guid, &tmp_guid_buf),
1198 NTSTATUS smb2srv_open_recreate(struct smbXsrv_connection *conn,
1199 struct auth_session_info *session_info,
1200 uint64_t persistent_id,
1201 const struct GUID *create_guid,
1203 struct smbXsrv_open **_open)
1205 struct smbXsrv_open_table *table = conn->client->open_table;
1206 struct smbXsrv_open *op = NULL;
1209 struct security_token *current_token = NULL;
1212 if (session_info == NULL) {
1213 DEBUG(10, ("session_info=NULL\n"));
1214 return NT_STATUS_INVALID_HANDLE;
1216 current_token = session_info->security_token;
1218 if (current_token == NULL) {
1219 DEBUG(10, ("current_token=NULL\n"));
1220 return NT_STATUS_INVALID_HANDLE;
1223 if ((persistent_id & 0xFFFFFFFF00000000LLU) != 0) {
1225 * We only use 32 bit for the persistent ID
1227 DBG_DEBUG("persistent_id=%"PRIx64"\n", persistent_id);
1228 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1230 global_id = persistent_id & UINT32_MAX; /* truncate to 32 bit */
1232 op = talloc_zero(table, struct smbXsrv_open);
1234 return NT_STATUS_NO_MEMORY;
1238 status = smbXsrv_open_global_lookup(table, global_id, op, &op->global);
1239 if (!NT_STATUS_IS_OK(status)) {
1241 DEBUG(10, ("smbXsrv_open_global_lookup returned %s\n",
1242 nt_errstr(status)));
1247 * If the provided create_guid is NULL, this means that
1248 * the reconnect request was a v1 request. In that case
1249 * we should skip the create GUID verification, since
1250 * it is valid to v1-reconnect a v2-opened handle.
1252 if ((create_guid != NULL) &&
1253 !GUID_equal(&op->global->create_guid, create_guid))
1256 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1259 if (!security_token_is_sid(current_token, &op->global->open_owner)) {
1261 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1264 if (!op->global->durable) {
1266 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1269 if (table->local.num_opens >= table->local.max_opens) {
1271 return NT_STATUS_INSUFFICIENT_RESOURCES;
1274 local_id = idr_get_new_random(
1277 table->local.lowest_id,
1278 table->local.highest_id);
1279 if (local_id == -1) {
1281 return NT_STATUS_INSUFFICIENT_RESOURCES;
1284 op->local_id = local_id;
1286 op->idle_time = now;
1287 op->status = NT_STATUS_FILE_CLOSED;
1289 op->global->open_volatile_id = op->local_id;
1290 op->global->server_id = messaging_server_id(conn->client->msg_ctx);
1292 table->local.num_opens += 1;
1294 talloc_set_destructor(op, smbXsrv_open_destructor);
1296 status = smbXsrv_open_global_store(op->global);
1297 if (!NT_STATUS_IS_OK(status)) {
1302 if (CHECK_DEBUGLVL(10)) {
1303 struct smbXsrv_openB open_blob = {
1307 DEBUG(10,("smbXsrv_open_recreate: global_id (0x%08x) stored\n",
1308 op->global->open_global_id));
1309 NDR_PRINT_DEBUG(smbXsrv_openB, &open_blob);
1313 return NT_STATUS_OK;
1317 struct smbXsrv_open_global_traverse_state {
1318 int (*fn)(struct smbXsrv_open_global0 *, void *);
1322 static int smbXsrv_open_global_traverse_fn(struct db_record *rec, void *data)
1324 struct smbXsrv_open_global_traverse_state *state =
1325 (struct smbXsrv_open_global_traverse_state*)data;
1326 struct smbXsrv_open_global0 *global = NULL;
1327 TDB_DATA key = dbwrap_record_get_key(rec);
1328 TDB_DATA val = dbwrap_record_get_value(rec);
1332 status = smbXsrv_open_global_parse_record(
1333 talloc_tos(), key, val, &global);
1334 if (!NT_STATUS_IS_OK(status)) {
1338 global->db_rec = rec;
1339 ret = state->fn(global, state->private_data);
1340 talloc_free(global);
1344 NTSTATUS smbXsrv_open_global_traverse(
1345 int (*fn)(struct smbXsrv_open_global0 *, void *),
1351 struct smbXsrv_open_global_traverse_state state = {
1353 .private_data = private_data,
1357 status = smbXsrv_open_global_init();
1358 if (!NT_STATUS_IS_OK(status)) {
1360 DEBUG(0, ("Failed to initialize open_global: %s\n",
1361 nt_errstr(status)));
1365 status = dbwrap_traverse_read(smbXsrv_open_global_db_ctx,
1366 smbXsrv_open_global_traverse_fn,
1374 NTSTATUS smbXsrv_open_cleanup(uint64_t persistent_id)
1376 NTSTATUS status = NT_STATUS_OK;
1377 TALLOC_CTX *frame = talloc_stackframe();
1378 struct smbXsrv_open_global0 *op = NULL;
1380 struct db_record *rec;
1381 bool delete_open = false;
1382 uint32_t global_id = persistent_id & UINT32_MAX;
1384 rec = smbXsrv_open_global_fetch_locked(smbXsrv_open_global_db_ctx,
1388 status = NT_STATUS_NOT_FOUND;
1392 key = dbwrap_record_get_key(rec);
1393 val = dbwrap_record_get_value(rec);
1395 if (val.dsize == 0) {
1396 DEBUG(10, ("smbXsrv_open_cleanup[global: 0x%08x] "
1397 "empty record in %s, skipping...\n",
1398 global_id, dbwrap_name(smbXsrv_open_global_db_ctx)));
1402 status = smbXsrv_open_global_parse_record(
1403 talloc_tos(), key, val, &op);
1404 if (!NT_STATUS_IS_OK(status)) {
1405 DEBUG(1, ("smbXsrv_open_cleanup[global: 0x%08x] "
1406 "failed to read record: %s\n",
1407 global_id, nt_errstr(status)));
1411 if (server_id_is_disconnected(&op->server_id)) {
1412 struct timeval now, disconnect_time;
1414 now = timeval_current();
1415 nttime_to_timeval(&disconnect_time, op->disconnect_time);
1416 tdiff = usec_time_diff(&now, &disconnect_time);
1417 delete_open = (tdiff >= 1000*op->durable_timeout_msec);
1419 DEBUG(10, ("smbXsrv_open_cleanup[global: 0x%08x] "
1420 "disconnected at [%s] %us ago with "
1421 "timeout of %us -%s reached\n",
1423 nt_time_string(frame, op->disconnect_time),
1424 (unsigned)(tdiff/1000000),
1425 op->durable_timeout_msec / 1000,
1426 delete_open ? "" : " not"));
1427 } else if (!serverid_exists(&op->server_id)) {
1428 struct server_id_buf idbuf;
1429 DEBUG(10, ("smbXsrv_open_cleanup[global: 0x%08x] "
1430 "server[%s] does not exist\n",
1432 server_id_str_buf(op->server_id, &idbuf)));
1440 status = dbwrap_record_delete(rec);
1441 if (!NT_STATUS_IS_OK(status)) {
1442 DEBUG(1, ("smbXsrv_open_cleanup[global: 0x%08x] "
1443 "failed to delete record"
1444 "from %s: %s\n", global_id,
1445 dbwrap_name(smbXsrv_open_global_db_ctx),
1446 nt_errstr(status)));
1450 DEBUG(10, ("smbXsrv_open_cleanup[global: 0x%08x] "
1451 "delete record from %s\n",
1453 dbwrap_name(smbXsrv_open_global_db_ctx)));