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(
326 struct db_record
*rec
,
327 struct smbXsrv_open_global0
**global
)
329 TDB_DATA key
= dbwrap_record_get_key(rec
);
330 TDB_DATA val
= dbwrap_record_get_value(rec
);
331 DATA_BLOB blob
= data_blob_const(val
.dptr
, val
.dsize
);
332 struct smbXsrv_open_globalB global_blob
;
333 enum ndr_err_code ndr_err
;
335 TALLOC_CTX
*frame
= talloc_stackframe();
337 ndr_err
= ndr_pull_struct_blob(&blob
, frame
, &global_blob
,
338 (ndr_pull_flags_fn_t
)ndr_pull_smbXsrv_open_globalB
);
339 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
340 DEBUG(1,("Invalid record in smbXsrv_open_global.tdb:"
341 "key '%s' ndr_pull_struct_blob - %s\n",
343 ndr_errstr(ndr_err
)));
344 status
= ndr_map_error2ntstatus(ndr_err
);
348 if (global_blob
.version
!= SMBXSRV_VERSION_0
) {
349 status
= NT_STATUS_INTERNAL_DB_CORRUPTION
;
350 DEBUG(1,("Invalid record in smbXsrv_open_global.tdb:"
351 "key '%s' unsupported version - %d - %s\n",
353 (int)global_blob
.version
,
358 if (global_blob
.info
.info0
== NULL
) {
359 status
= NT_STATUS_INTERNAL_DB_CORRUPTION
;
360 DEBUG(1,("Invalid record in smbXsrv_tcon_global.tdb:"
361 "key '%s' info0 NULL pointer - %s\n",
367 *global
= talloc_move(mem_ctx
, &global_blob
.info
.info0
);
368 status
= NT_STATUS_OK
;
374 static NTSTATUS
smbXsrv_open_global_verify_record(
378 struct smbXsrv_open_global0
**_global0
)
380 DATA_BLOB blob
= { .data
= val
.dptr
, .length
= val
.dsize
, };
381 struct smbXsrv_open_globalB global_blob
;
382 enum ndr_err_code ndr_err
;
383 struct smbXsrv_open_global0
*global0
= NULL
;
384 struct server_id_buf buf
;
386 if (val
.dsize
== 0) {
387 return NT_STATUS_NOT_FOUND
;
390 ndr_err
= ndr_pull_struct_blob(
394 (ndr_pull_flags_fn_t
)ndr_pull_smbXsrv_open_globalB
);
395 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
396 DBG_WARNING("key '%s' ndr_pull_struct_blob - %s\n",
398 ndr_map_error2string(ndr_err
));
399 return ndr_map_error2ntstatus(ndr_err
);
403 if (CHECK_DEBUGLVL(10)) {
404 NDR_PRINT_DEBUG(smbXsrv_open_globalB
, &global_blob
);
407 if (global_blob
.version
!= SMBXSRV_VERSION_0
) {
408 DBG_ERR("key '%s' use unsupported version %u\n",
410 global_blob
.version
);
411 NDR_PRINT_DEBUG(smbXsrv_open_globalB
, &global_blob
);
412 return NT_STATUS_INTERNAL_ERROR
;
415 global0
= global_blob
.info
.info0
;
418 if (server_id_is_disconnected(&global0
->server_id
)) {
421 if (serverid_exists(&global0
->server_id
)) {
425 DBG_WARNING("smbd %s did not clean up record %s\n",
426 server_id_str_buf(global0
->server_id
, &buf
),
429 return NT_STATUS_FATAL_APP_EXIT
;
432 static NTSTATUS
smbXsrv_open_global_store(struct smbXsrv_open_global0
*global
)
434 struct smbXsrv_open_globalB global_blob
;
435 DATA_BLOB blob
= data_blob_null
;
439 enum ndr_err_code ndr_err
;
442 * TODO: if we use other versions than '0'
443 * we would add glue code here, that would be able to
444 * store the information in the old format.
447 key
= dbwrap_record_get_key(global
->db_rec
);
448 val
= dbwrap_record_get_value(global
->db_rec
);
450 global_blob
= (struct smbXsrv_open_globalB
) {
451 .version
= smbXsrv_version_global_current(),
454 if (val
.dsize
>= 8) {
455 global_blob
.seqnum
= IVAL(val
.dptr
, 4);
457 global_blob
.seqnum
+= 1;
458 global_blob
.info
.info0
= global
;
460 ndr_err
= ndr_push_struct_blob(&blob
, talloc_tos(), &global_blob
,
461 (ndr_push_flags_fn_t
)ndr_push_smbXsrv_open_globalB
);
462 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
463 DBG_WARNING("key '%s' ndr_push - %s\n",
465 ndr_map_error2string(ndr_err
));
466 TALLOC_FREE(global
->db_rec
);
467 return ndr_map_error2ntstatus(ndr_err
);
470 val
= make_tdb_data(blob
.data
, blob
.length
);
471 status
= dbwrap_record_store(global
->db_rec
, val
, TDB_REPLACE
);
472 TALLOC_FREE(blob
.data
);
473 if (!NT_STATUS_IS_OK(status
)) {
474 DBG_WARNING("key '%s' store - %s\n",
477 TALLOC_FREE(global
->db_rec
);
481 if (CHECK_DEBUGLVL(10)) {
482 DBG_DEBUG("key '%s' stored\n", tdb_data_dbg(key
));
483 NDR_PRINT_DEBUG(smbXsrv_open_globalB
, &global_blob
);
486 TALLOC_FREE(global
->db_rec
);
491 static NTSTATUS
smbXsrv_open_global_lookup(struct smbXsrv_open_table
*table
,
492 uint32_t open_global_id
,
494 struct smbXsrv_open_global0
**_global
)
496 struct smbXsrv_open_global_key_buf key_buf
;
497 TDB_DATA key
= smbXsrv_open_global_id_to_key(open_global_id
, &key_buf
);
499 struct db_record
*global_rec
= NULL
;
504 if (table
->global
.db_ctx
== NULL
) {
505 return NT_STATUS_INTERNAL_ERROR
;
508 global_rec
= dbwrap_fetch_locked(table
->global
.db_ctx
, mem_ctx
, key
);
509 if (global_rec
== NULL
) {
510 return NT_STATUS_INTERNAL_DB_ERROR
;
512 val
= dbwrap_record_get_value(global_rec
);
514 status
= smbXsrv_open_global_verify_record(key
, val
, mem_ctx
, _global
);
515 if (NT_STATUS_IS_OK(status
)) {
516 (*_global
)->db_rec
= talloc_move(*_global
, &global_rec
);
520 TALLOC_FREE(global_rec
);
522 if (NT_STATUS_EQUAL(status
, NT_STATUS_NOT_FOUND
)) {
523 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
529 static int smbXsrv_open_destructor(struct smbXsrv_open
*op
)
533 status
= smbXsrv_open_close(op
, 0);
534 if (!NT_STATUS_IS_OK(status
)) {
535 DEBUG(0, ("smbXsrv_open_destructor: "
536 "smbXsrv_open_close() failed - %s\n",
540 TALLOC_FREE(op
->global
);
545 NTSTATUS
smbXsrv_open_create(struct smbXsrv_connection
*conn
,
546 struct auth_session_info
*session_info
,
548 struct smbXsrv_open
**_open
)
550 struct smbXsrv_open_table
*table
= conn
->client
->open_table
;
551 struct smbXsrv_open
*op
= NULL
;
552 struct smbXsrv_open_global0
*global
= NULL
;
554 struct dom_sid
*current_sid
= NULL
;
555 struct security_token
*current_token
= NULL
;
558 if (session_info
== NULL
) {
559 return NT_STATUS_INVALID_HANDLE
;
561 current_token
= session_info
->security_token
;
563 if (current_token
== NULL
) {
564 return NT_STATUS_INVALID_HANDLE
;
567 if (current_token
->num_sids
> PRIMARY_USER_SID_INDEX
) {
568 current_sid
= ¤t_token
->sids
[PRIMARY_USER_SID_INDEX
];
571 if (current_sid
== NULL
) {
572 return NT_STATUS_INVALID_HANDLE
;
575 if (table
->local
.num_opens
>= table
->local
.max_opens
) {
576 return NT_STATUS_INSUFFICIENT_RESOURCES
;
579 op
= talloc_zero(table
, struct smbXsrv_open
);
581 return NT_STATUS_NO_MEMORY
;
584 op
->status
= NT_STATUS_OK
; /* TODO: start with INTERNAL_ERROR */
587 global
= talloc_zero(op
, struct smbXsrv_open_global0
);
588 if (global
== NULL
) {
590 return NT_STATUS_NO_MEMORY
;
595 * We mark every slot as invalid using 0xFF.
596 * Valid values are masked with 0xF.
598 memset(global
->lock_sequence_array
, 0xFF,
599 sizeof(global
->lock_sequence_array
));
601 status
= smbXsrv_open_global_allocate(table
->global
.db_ctx
, global
);
602 if (!NT_STATUS_IS_OK(status
)) {
607 local_id
= idr_get_new_random(
610 table
->local
.lowest_id
,
611 table
->local
.highest_id
);
612 if (local_id
== -1) {
614 return NT_STATUS_INSUFFICIENT_RESOURCES
;
616 op
->local_id
= local_id
;
618 global
->open_persistent_id
= global
->open_global_id
;
619 global
->open_volatile_id
= op
->local_id
;
621 global
->server_id
= messaging_server_id(conn
->client
->msg_ctx
);
622 global
->open_time
= now
;
623 global
->open_owner
= *current_sid
;
624 if (conn
->protocol
>= PROTOCOL_SMB2_10
) {
625 global
->client_guid
= conn
->smb2
.client
.guid
;
628 table
->local
.num_opens
+= 1;
630 talloc_set_destructor(op
, smbXsrv_open_destructor
);
632 status
= smbXsrv_open_global_store(global
);
633 if (!NT_STATUS_IS_OK(status
)) {
634 DEBUG(0,("smbXsrv_open_create: "
635 "global_id (0x%08x) store failed - %s\n",
636 op
->global
->open_global_id
,
642 if (CHECK_DEBUGLVL(10)) {
643 struct smbXsrv_openB open_blob
= {
644 .version
= SMBXSRV_VERSION_0
,
648 DEBUG(10,("smbXsrv_open_create: global_id (0x%08x) stored\n",
649 op
->global
->open_global_id
));
650 NDR_PRINT_DEBUG(smbXsrv_openB
, &open_blob
);
657 static NTSTATUS
smbXsrv_open_set_replay_cache(struct smbXsrv_open
*op
)
659 struct GUID
*create_guid
;
660 struct GUID_txt_buf buf
;
662 struct db_context
*db
= op
->table
->local
.replay_cache_db_ctx
;
663 struct smbXsrv_open_replay_cache rc
= {
664 .idle_time
= op
->idle_time
,
665 .local_id
= op
->local_id
,
667 uint8_t data
[SMBXSRV_OPEN_REPLAY_CACHE_FIXED_SIZE
] = { 0 };
668 DATA_BLOB blob
= { .data
= data
, .length
= sizeof(data
), };
669 enum ndr_err_code ndr_err
;
673 if (!(op
->flags
& SMBXSRV_OPEN_NEED_REPLAY_CACHE
)) {
677 if (op
->flags
& SMBXSRV_OPEN_HAVE_REPLAY_CACHE
) {
681 create_guid
= &op
->global
->create_guid
;
682 guid_string
= GUID_buf_string(create_guid
, &buf
);
684 ndr_err
= ndr_push_struct_into_fixed_blob(&blob
, &rc
,
685 (ndr_push_flags_fn_t
)ndr_push_smbXsrv_open_replay_cache
);
686 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
687 status
= ndr_map_error2ntstatus(ndr_err
);
690 val
= make_tdb_data(blob
.data
, blob
.length
);
692 status
= dbwrap_store_bystring(db
, guid_string
, val
, TDB_REPLACE
);
694 if (NT_STATUS_IS_OK(status
)) {
695 op
->flags
|= SMBXSRV_OPEN_HAVE_REPLAY_CACHE
;
696 op
->flags
&= ~SMBXSRV_OPEN_NEED_REPLAY_CACHE
;
702 NTSTATUS
smbXsrv_open_purge_replay_cache(struct smbXsrv_client
*client
,
703 const struct GUID
*create_guid
)
705 struct GUID_txt_buf buf
;
707 struct db_context
*db
;
709 if (client
->open_table
== NULL
) {
713 db
= client
->open_table
->local
.replay_cache_db_ctx
;
715 guid_string
= GUID_buf_string(create_guid
, &buf
);
716 if (guid_string
== NULL
) {
717 return NT_STATUS_INVALID_PARAMETER
;
720 return dbwrap_purge_bystring(db
, guid_string
);
723 static NTSTATUS
smbXsrv_open_clear_replay_cache(struct smbXsrv_open
*op
)
725 struct GUID
*create_guid
;
726 struct GUID_txt_buf buf
;
728 struct db_context
*db
;
731 if (op
->table
== NULL
) {
735 db
= op
->table
->local
.replay_cache_db_ctx
;
737 if (!(op
->flags
& SMBXSRV_OPEN_HAVE_REPLAY_CACHE
)) {
741 create_guid
= &op
->global
->create_guid
;
742 if (GUID_all_zero(create_guid
)) {
746 guid_string
= GUID_buf_string(create_guid
, &buf
);
747 if (guid_string
== NULL
) {
748 return NT_STATUS_INVALID_PARAMETER
;
751 status
= dbwrap_purge_bystring(db
, guid_string
);
753 if (NT_STATUS_IS_OK(status
)) {
754 op
->flags
&= ~SMBXSRV_OPEN_HAVE_REPLAY_CACHE
;
760 NTSTATUS
smbXsrv_open_update(struct smbXsrv_open
*op
)
762 struct smbXsrv_open_table
*table
= op
->table
;
765 if (op
->global
->db_rec
!= NULL
) {
766 DEBUG(0, ("smbXsrv_open_update(0x%08x): "
767 "Called with db_rec != NULL'\n",
768 op
->global
->open_global_id
));
769 return NT_STATUS_INTERNAL_ERROR
;
772 op
->global
->db_rec
= smbXsrv_open_global_fetch_locked(
773 table
->global
.db_ctx
,
774 op
->global
->open_global_id
,
775 op
->global
/* TALLOC_CTX */);
776 if (op
->global
->db_rec
== NULL
) {
777 return NT_STATUS_INTERNAL_DB_ERROR
;
780 status
= smbXsrv_open_global_store(op
->global
);
781 if (!NT_STATUS_IS_OK(status
)) {
782 DEBUG(0,("smbXsrv_open_update: "
783 "global_id (0x%08x) store failed - %s\n",
784 op
->global
->open_global_id
,
789 status
= smbXsrv_open_set_replay_cache(op
);
790 if (!NT_STATUS_IS_OK(status
)) {
791 DBG_ERR("smbXsrv_open_set_replay_cache failed: %s\n",
796 if (CHECK_DEBUGLVL(10)) {
797 struct smbXsrv_openB open_blob
= {
798 .version
= SMBXSRV_VERSION_0
,
802 DEBUG(10,("smbXsrv_open_update: global_id (0x%08x) stored\n",
803 op
->global
->open_global_id
));
804 NDR_PRINT_DEBUG(smbXsrv_openB
, &open_blob
);
810 NTSTATUS
smbXsrv_open_close(struct smbXsrv_open
*op
, NTTIME now
)
812 struct smbXsrv_open_table
*table
;
813 struct db_record
*global_rec
= NULL
;
815 NTSTATUS error
= NT_STATUS_OK
;
818 error
= smbXsrv_open_clear_replay_cache(op
);
819 if (!NT_STATUS_IS_OK(error
)) {
820 DBG_ERR("smbXsrv_open_clear_replay_cache failed: %s\n",
824 if (op
->table
== NULL
) {
831 op
->status
= NT_STATUS_FILE_CLOSED
;
832 op
->global
->disconnect_time
= now
;
833 server_id_set_disconnected(&op
->global
->server_id
);
835 global_rec
= op
->global
->db_rec
;
836 op
->global
->db_rec
= NULL
;
837 if (global_rec
== NULL
) {
838 global_rec
= smbXsrv_open_global_fetch_locked(
839 table
->global
.db_ctx
,
840 op
->global
->open_global_id
,
841 op
->global
/* TALLOC_CTX */);
842 if (global_rec
== NULL
) {
843 error
= NT_STATUS_INTERNAL_ERROR
;
847 if (global_rec
!= NULL
&& op
->global
->durable
) {
849 * If it is a durable open we need to update the global part
850 * instead of deleting it
852 op
->global
->db_rec
= global_rec
;
853 status
= smbXsrv_open_global_store(op
->global
);
854 if (NT_STATUS_IS_OK(status
)) {
856 * smbXsrv_open_global_store does the free
857 * of op->global->db_rec
861 if (!NT_STATUS_IS_OK(status
)) {
862 DEBUG(0,("smbXsrv_open_close(0x%08x)"
863 "smbXsrv_open_global_store() failed - %s\n",
864 op
->global
->open_global_id
,
869 if (NT_STATUS_IS_OK(status
) && CHECK_DEBUGLVL(10)) {
870 struct smbXsrv_openB open_blob
= {
871 .version
= SMBXSRV_VERSION_0
,
875 DEBUG(10,("smbXsrv_open_close(0x%08x): "
876 "stored disconnect\n",
877 op
->global
->open_global_id
));
878 NDR_PRINT_DEBUG(smbXsrv_openB
, &open_blob
);
882 if (global_rec
!= NULL
) {
883 status
= dbwrap_record_delete(global_rec
);
884 if (!NT_STATUS_IS_OK(status
)) {
885 TDB_DATA key
= dbwrap_record_get_key(global_rec
);
887 DEBUG(0, ("smbXsrv_open_close(0x%08x): "
888 "failed to delete global key '%s': %s\n",
889 op
->global
->open_global_id
,
895 TALLOC_FREE(global_rec
);
897 ret
= idr_remove(table
->local
.idr
, op
->local_id
);
898 SMB_ASSERT(ret
== 0);
900 table
->local
.num_opens
-= 1;
903 op
->compat
->op
= NULL
;
904 file_free(NULL
, op
->compat
);
911 NTSTATUS
smb1srv_open_table_init(struct smbXsrv_connection
*conn
)
916 * Allow a range from 1..65534.
918 * With real_max_open_files possible ids,
919 * truncated to the SMB1 limit of 16-bit.
921 * 0 and 0xFFFF are no valid ids.
923 max_opens
= conn
->client
->sconn
->real_max_open_files
;
924 max_opens
= MIN(max_opens
, UINT16_MAX
- 1);
926 return smbXsrv_open_table_init(conn
, 1, UINT16_MAX
- 1, max_opens
);
929 NTSTATUS
smb1srv_open_lookup(struct smbXsrv_connection
*conn
,
930 uint16_t fnum
, NTTIME now
,
931 struct smbXsrv_open
**_open
)
933 struct smbXsrv_open_table
*table
= conn
->client
->open_table
;
934 uint32_t local_id
= fnum
;
935 uint32_t global_id
= 0;
937 return smbXsrv_open_local_lookup(table
, local_id
, global_id
, now
, _open
);
940 NTSTATUS
smb2srv_open_table_init(struct smbXsrv_connection
*conn
)
946 * Allow a range from 1..4294967294.
948 * With real_max_open_files possible ids,
949 * truncated to 16-bit (the same as SMB1 for now).
951 * 0 and 0xFFFFFFFF are no valid ids.
953 * The usage of conn->sconn->real_max_open_files
954 * is the reason that we use one open table per
955 * transport connection (as we still have a 1:1 mapping
956 * between process and transport connection).
958 max_opens
= conn
->client
->sconn
->real_max_open_files
;
959 max_opens
= MIN(max_opens
, UINT16_MAX
- 1);
962 * idtree uses "int" for local IDs. Limit the maximum ID to
963 * what "int" can hold.
965 highest_id
= UINT32_MAX
-1;
966 highest_id
= MIN(highest_id
, INT_MAX
);
968 return smbXsrv_open_table_init(conn
, 1, highest_id
, max_opens
);
971 NTSTATUS
smb2srv_open_lookup(struct smbXsrv_connection
*conn
,
972 uint64_t persistent_id
,
973 uint64_t volatile_id
,
975 struct smbXsrv_open
**_open
)
977 struct smbXsrv_open_table
*table
= conn
->client
->open_table
;
978 uint32_t local_id
= volatile_id
& UINT32_MAX
;
979 uint64_t local_zeros
= volatile_id
& 0xFFFFFFFF00000000LLU
;
980 uint32_t global_id
= persistent_id
& UINT32_MAX
;
981 uint64_t global_zeros
= persistent_id
& 0xFFFFFFFF00000000LLU
;
984 if (local_zeros
!= 0) {
985 return NT_STATUS_FILE_CLOSED
;
988 if (global_zeros
!= 0) {
989 return NT_STATUS_FILE_CLOSED
;
992 if (global_id
== 0) {
993 return NT_STATUS_FILE_CLOSED
;
996 status
= smbXsrv_open_local_lookup(table
, local_id
, global_id
, now
,
998 if (!NT_STATUS_IS_OK(status
)) {
1003 * Clear the replay cache for this create_guid if it exists:
1004 * This is based on the assumption that this lookup will be
1005 * triggered by a client request using the file-id for lookup.
1006 * Hence the client has proven that it has in fact seen the
1007 * reply to its initial create call. So subsequent create replays
1008 * should be treated as invalid. Hence the index for create_guid
1009 * lookup needs to be removed.
1011 status
= smbXsrv_open_clear_replay_cache(*_open
);
1017 * This checks or marks the replay cache, we have the following
1020 * 1. There is no record in the cache
1021 * => we add the passes caller_req_guid as holder_req_guid
1022 * together with local_id as 0.
1023 * => We return STATUS_FWP_RESERVED in order to indicate
1024 * that the caller holds the current reservation
1026 * 2. There is a record in the cache and holder_req_guid
1027 * is already the same as caller_req_guid and local_id is 0
1028 * => We return STATUS_FWP_RESERVED in order to indicate
1029 * that the caller holds the current reservation
1031 * 3. There is a record in the cache with a holder_req_guid
1032 * other than caller_req_guid (and local_id is 0):
1033 * => We return NT_STATUS_FILE_NOT_AVAILABLE to indicate
1034 * the original request is still pending
1036 * 4. There is a record in the cache with a zero holder_req_guid
1037 * and a valid local_id:
1038 * => We lookup the existing open by local_id
1039 * => We return NT_STATUS_OK together with the smbXsrv_open
1042 * With NT_STATUS_OK the caller can continue the replay processing.
1044 * With STATUS_FWP_RESERVED the caller should continue the normal
1047 * - smbXsrv_open_update()/smbXsrv_open_set_replay_cache()
1048 * will convert the record to a zero holder_req_guid
1049 * with a valid local_id.
1051 * - smbXsrv_open_purge_replay_cache() should cleanup
1054 * All other values should be returned to the client,
1055 * while NT_STATUS_FILE_NOT_AVAILABLE will trigger the
1056 * retry loop on the client.
1058 NTSTATUS
smb2srv_open_lookup_replay_cache(struct smbXsrv_connection
*conn
,
1059 struct GUID caller_req_guid
,
1060 struct GUID create_guid
,
1063 struct smbXsrv_open
**_open
)
1065 TALLOC_CTX
*frame
= talloc_stackframe();
1067 struct smbXsrv_open_table
*table
= conn
->client
->open_table
;
1068 struct db_context
*db
= table
->local
.replay_cache_db_ctx
;
1069 struct GUID_txt_buf tmp_guid_buf
;
1070 struct GUID_txt_buf _create_guid_buf
;
1071 const char *create_guid_str
= GUID_buf_string(&create_guid
, &_create_guid_buf
);
1072 TDB_DATA create_guid_key
= string_term_tdb_data(create_guid_str
);
1073 struct db_record
*db_rec
= NULL
;
1074 struct smbXsrv_open
*op
= NULL
;
1075 struct smbXsrv_open_replay_cache rc
= {
1076 .holder_req_guid
= caller_req_guid
,
1080 enum ndr_err_code ndr_err
;
1081 DATA_BLOB blob
= data_blob_null
;
1086 db_rec
= dbwrap_fetch_locked(db
, frame
, create_guid_key
);
1087 if (db_rec
== NULL
) {
1089 return NT_STATUS_INTERNAL_DB_ERROR
;
1092 val
= dbwrap_record_get_value(db_rec
);
1093 if (val
.dsize
== 0) {
1094 uint8_t data
[SMBXSRV_OPEN_REPLAY_CACHE_FIXED_SIZE
];
1096 blob
= data_blob_const(data
, ARRAY_SIZE(data
));
1097 ndr_err
= ndr_push_struct_into_fixed_blob(&blob
, &rc
,
1098 (ndr_push_flags_fn_t
)ndr_push_smbXsrv_open_replay_cache
);
1099 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1100 status
= ndr_map_error2ntstatus(ndr_err
);
1105 val
= make_tdb_data(blob
.data
, blob
.length
);
1106 status
= dbwrap_record_store(db_rec
, val
, TDB_REPLACE
);
1107 if (!NT_STATUS_IS_OK(status
)) {
1113 * We're the new holder
1117 return NT_STATUS_FWP_RESERVED
;
1120 if (val
.dsize
!= SMBXSRV_OPEN_REPLAY_CACHE_FIXED_SIZE
) {
1122 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
1125 blob
= data_blob_const(val
.dptr
, val
.dsize
);
1126 ndr_err
= ndr_pull_struct_blob_all_noalloc(&blob
, &rc
,
1127 (ndr_pull_flags_fn_t
)ndr_pull_smbXsrv_open_replay_cache
);
1128 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1129 status
= ndr_map_error2ntstatus(ndr_err
);
1133 if (rc
.local_id
!= 0) {
1134 if (GUID_equal(&rc
.holder_req_guid
, &caller_req_guid
)) {
1136 * This should not happen
1138 status
= NT_STATUS_INTERNAL_ERROR
;
1139 DBG_ERR("caller %s already holds local_id %u for create %s [%s] - %s\n",
1140 GUID_buf_string(&caller_req_guid
, &tmp_guid_buf
),
1141 (unsigned)rc
.local_id
,
1150 status
= smbXsrv_open_local_lookup(table
,
1155 if (!NT_STATUS_IS_OK(status
)) {
1156 DBG_ERR("holder %s stale for local_id %u for create %s [%s] - %s\n",
1157 GUID_buf_string(&rc
.holder_req_guid
, &tmp_guid_buf
),
1158 (unsigned)rc
.local_id
,
1168 * We found an open the caller can reuse.
1170 SMB_ASSERT(op
!= NULL
);
1173 return NT_STATUS_OK
;
1176 if (GUID_equal(&rc
.holder_req_guid
, &caller_req_guid
)) {
1178 * We're still the holder
1182 return NT_STATUS_FWP_RESERVED
;
1186 * The original request (or a former replay) is still
1187 * pending, ask the client to retry by sending FILE_NOT_AVAILABLE.
1189 status
= NT_STATUS_FILE_NOT_AVAILABLE
;
1190 DBG_DEBUG("holder %s still pending for create %s [%s] - %s\n",
1191 GUID_buf_string(&rc
.holder_req_guid
, &tmp_guid_buf
),
1199 NTSTATUS
smb2srv_open_recreate(struct smbXsrv_connection
*conn
,
1200 struct auth_session_info
*session_info
,
1201 uint64_t persistent_id
,
1202 const struct GUID
*create_guid
,
1204 struct smbXsrv_open
**_open
)
1206 struct smbXsrv_open_table
*table
= conn
->client
->open_table
;
1207 struct smbXsrv_open
*op
= NULL
;
1210 struct security_token
*current_token
= NULL
;
1213 if (session_info
== NULL
) {
1214 DEBUG(10, ("session_info=NULL\n"));
1215 return NT_STATUS_INVALID_HANDLE
;
1217 current_token
= session_info
->security_token
;
1219 if (current_token
== NULL
) {
1220 DEBUG(10, ("current_token=NULL\n"));
1221 return NT_STATUS_INVALID_HANDLE
;
1224 if ((persistent_id
& 0xFFFFFFFF00000000LLU
) != 0) {
1226 * We only use 32 bit for the persistent ID
1228 DBG_DEBUG("persistent_id=%"PRIx64
"\n", persistent_id
);
1229 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
1231 global_id
= persistent_id
& UINT32_MAX
; /* truncate to 32 bit */
1233 op
= talloc_zero(table
, struct smbXsrv_open
);
1235 return NT_STATUS_NO_MEMORY
;
1239 status
= smbXsrv_open_global_lookup(table
, global_id
, op
, &op
->global
);
1240 if (!NT_STATUS_IS_OK(status
)) {
1242 DEBUG(10, ("smbXsrv_open_global_lookup returned %s\n",
1243 nt_errstr(status
)));
1248 * If the provided create_guid is NULL, this means that
1249 * the reconnect request was a v1 request. In that case
1250 * we should skip the create GUID verification, since
1251 * it is valid to v1-reconnect a v2-opened handle.
1253 if ((create_guid
!= NULL
) &&
1254 !GUID_equal(&op
->global
->create_guid
, create_guid
))
1257 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
1260 if (!security_token_is_sid(current_token
, &op
->global
->open_owner
)) {
1262 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
1265 if (!op
->global
->durable
) {
1267 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
1270 if (table
->local
.num_opens
>= table
->local
.max_opens
) {
1272 return NT_STATUS_INSUFFICIENT_RESOURCES
;
1275 local_id
= idr_get_new_random(
1278 table
->local
.lowest_id
,
1279 table
->local
.highest_id
);
1280 if (local_id
== -1) {
1282 return NT_STATUS_INSUFFICIENT_RESOURCES
;
1285 op
->local_id
= local_id
;
1287 op
->idle_time
= now
;
1288 op
->status
= NT_STATUS_FILE_CLOSED
;
1290 op
->global
->open_volatile_id
= op
->local_id
;
1291 op
->global
->server_id
= messaging_server_id(conn
->client
->msg_ctx
);
1293 table
->local
.num_opens
+= 1;
1295 talloc_set_destructor(op
, smbXsrv_open_destructor
);
1297 status
= smbXsrv_open_global_store(op
->global
);
1298 if (!NT_STATUS_IS_OK(status
)) {
1303 if (CHECK_DEBUGLVL(10)) {
1304 struct smbXsrv_openB open_blob
= {
1308 DEBUG(10,("smbXsrv_open_recreate: global_id (0x%08x) stored\n",
1309 op
->global
->open_global_id
));
1310 NDR_PRINT_DEBUG(smbXsrv_openB
, &open_blob
);
1314 return NT_STATUS_OK
;
1318 struct smbXsrv_open_global_traverse_state
{
1319 int (*fn
)(struct smbXsrv_open_global0
*, void *);
1323 static int smbXsrv_open_global_traverse_fn(struct db_record
*rec
, void *data
)
1325 struct smbXsrv_open_global_traverse_state
*state
=
1326 (struct smbXsrv_open_global_traverse_state
*)data
;
1327 struct smbXsrv_open_global0
*global
= NULL
;
1331 status
= smbXsrv_open_global_parse_record(talloc_tos(), rec
, &global
);
1332 if (!NT_STATUS_IS_OK(status
)) {
1336 global
->db_rec
= rec
;
1337 ret
= state
->fn(global
, state
->private_data
);
1338 talloc_free(global
);
1342 NTSTATUS
smbXsrv_open_global_traverse(
1343 int (*fn
)(struct smbXsrv_open_global0
*, void *),
1349 struct smbXsrv_open_global_traverse_state state
= {
1351 .private_data
= private_data
,
1355 status
= smbXsrv_open_global_init();
1356 if (!NT_STATUS_IS_OK(status
)) {
1358 DEBUG(0, ("Failed to initialize open_global: %s\n",
1359 nt_errstr(status
)));
1363 status
= dbwrap_traverse_read(smbXsrv_open_global_db_ctx
,
1364 smbXsrv_open_global_traverse_fn
,
1372 NTSTATUS
smbXsrv_open_cleanup(uint64_t persistent_id
)
1374 NTSTATUS status
= NT_STATUS_OK
;
1375 TALLOC_CTX
*frame
= talloc_stackframe();
1376 struct smbXsrv_open_global0
*op
= NULL
;
1378 struct db_record
*rec
;
1379 bool delete_open
= false;
1380 uint32_t global_id
= persistent_id
& UINT32_MAX
;
1382 rec
= smbXsrv_open_global_fetch_locked(smbXsrv_open_global_db_ctx
,
1386 status
= NT_STATUS_NOT_FOUND
;
1390 val
= dbwrap_record_get_value(rec
);
1391 if (val
.dsize
== 0) {
1392 DEBUG(10, ("smbXsrv_open_cleanup[global: 0x%08x] "
1393 "empty record in %s, skipping...\n",
1394 global_id
, dbwrap_name(smbXsrv_open_global_db_ctx
)));
1398 status
= smbXsrv_open_global_parse_record(talloc_tos(), rec
, &op
);
1399 if (!NT_STATUS_IS_OK(status
)) {
1400 DEBUG(1, ("smbXsrv_open_cleanup[global: 0x%08x] "
1401 "failed to read record: %s\n",
1402 global_id
, nt_errstr(status
)));
1406 if (server_id_is_disconnected(&op
->server_id
)) {
1407 struct timeval now
, disconnect_time
;
1409 now
= timeval_current();
1410 nttime_to_timeval(&disconnect_time
, op
->disconnect_time
);
1411 tdiff
= usec_time_diff(&now
, &disconnect_time
);
1412 delete_open
= (tdiff
>= 1000*op
->durable_timeout_msec
);
1414 DEBUG(10, ("smbXsrv_open_cleanup[global: 0x%08x] "
1415 "disconnected at [%s] %us ago with "
1416 "timeout of %us -%s reached\n",
1418 nt_time_string(frame
, op
->disconnect_time
),
1419 (unsigned)(tdiff
/1000000),
1420 op
->durable_timeout_msec
/ 1000,
1421 delete_open
? "" : " not"));
1422 } else if (!serverid_exists(&op
->server_id
)) {
1423 struct server_id_buf idbuf
;
1424 DEBUG(10, ("smbXsrv_open_cleanup[global: 0x%08x] "
1425 "server[%s] does not exist\n",
1427 server_id_str_buf(op
->server_id
, &idbuf
)));
1435 status
= dbwrap_record_delete(rec
);
1436 if (!NT_STATUS_IS_OK(status
)) {
1437 DEBUG(1, ("smbXsrv_open_cleanup[global: 0x%08x] "
1438 "failed to delete record"
1439 "from %s: %s\n", global_id
,
1440 dbwrap_name(smbXsrv_open_global_db_ctx
),
1441 nt_errstr(status
)));
1445 DEBUG(10, ("smbXsrv_open_cleanup[global: 0x%08x] "
1446 "delete record from %s\n",
1448 dbwrap_name(smbXsrv_open_global_db_ctx
)));