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/>.
22 #include "system/filesys.h"
23 #include "smbd/smbd.h"
24 #include "smbd/globals.h"
25 #include "dbwrap/dbwrap.h"
26 #include "dbwrap/dbwrap_rbt.h"
27 #include "dbwrap/dbwrap_open.h"
28 #include "../libcli/security/security.h"
30 #include "lib/util/util_tdb.h"
31 #include "librpc/gen_ndr/ndr_smbXsrv.h"
34 struct smbXsrv_open_table
{
36 struct db_context
*db_ctx
;
37 struct db_context
*replay_cache_db_ctx
;
44 struct db_context
*db_ctx
;
48 static struct db_context
*smbXsrv_open_global_db_ctx
= NULL
;
50 NTSTATUS
smbXsrv_open_global_init(void)
52 char *global_path
= NULL
;
53 struct db_context
*db_ctx
= NULL
;
55 if (smbXsrv_open_global_db_ctx
!= NULL
) {
59 global_path
= lock_path("smbXsrv_open_global.tdb");
60 if (global_path
== NULL
) {
61 return NT_STATUS_NO_MEMORY
;
64 db_ctx
= db_open(NULL
, global_path
,
68 TDB_INCOMPATIBLE_HASH
,
69 O_RDWR
| O_CREAT
, 0600,
72 TALLOC_FREE(global_path
);
76 status
= map_nt_error_from_unix_common(errno
);
81 smbXsrv_open_global_db_ctx
= db_ctx
;
88 * We need to store the keys in big endian so that dbwrap_rbt's memcmp
89 * has the same result as integer comparison between the uint32_t
92 * TODO: implement string based key
95 #define SMBXSRV_OPEN_GLOBAL_TDB_KEY_SIZE sizeof(uint32_t)
97 static TDB_DATA
smbXsrv_open_global_id_to_key(uint32_t id
,
102 RSIVAL(key_buf
, 0, id
);
104 key
= make_tdb_data(key_buf
, SMBXSRV_OPEN_GLOBAL_TDB_KEY_SIZE
);
110 static NTSTATUS
smbXsrv_open_global_key_to_id(TDB_DATA key
, uint32_t *id
)
113 return NT_STATUS_INVALID_PARAMETER
;
116 if (key
.dsize
!= SMBXSRV_OPEN_GLOBAL_TDB_KEY_SIZE
) {
117 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
120 *id
= RIVAL(key
.dptr
, 0);
126 #define SMBXSRV_OPEN_LOCAL_TDB_KEY_SIZE sizeof(uint32_t)
128 static TDB_DATA
smbXsrv_open_local_id_to_key(uint32_t id
,
133 RSIVAL(key_buf
, 0, id
);
135 key
= make_tdb_data(key_buf
, SMBXSRV_OPEN_LOCAL_TDB_KEY_SIZE
);
140 static NTSTATUS
smbXsrv_open_local_key_to_id(TDB_DATA key
, uint32_t *id
)
143 return NT_STATUS_INVALID_PARAMETER
;
146 if (key
.dsize
!= SMBXSRV_OPEN_LOCAL_TDB_KEY_SIZE
) {
147 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
150 *id
= RIVAL(key
.dptr
, 0);
155 static struct db_record
*smbXsrv_open_global_fetch_locked(
156 struct db_context
*db
,
161 uint8_t key_buf
[SMBXSRV_OPEN_GLOBAL_TDB_KEY_SIZE
];
162 struct db_record
*rec
= NULL
;
164 key
= smbXsrv_open_global_id_to_key(id
, key_buf
);
166 rec
= dbwrap_fetch_locked(db
, mem_ctx
, key
);
169 DBG_DEBUG("Failed to lock global id 0x%08x, key '%s'\n", id
,
170 hex_encode_talloc(talloc_tos(), key
.dptr
, key
.dsize
));
176 static struct db_record
*smbXsrv_open_local_fetch_locked(
177 struct db_context
*db
,
182 uint8_t key_buf
[SMBXSRV_OPEN_LOCAL_TDB_KEY_SIZE
];
183 struct db_record
*rec
= NULL
;
185 key
= smbXsrv_open_local_id_to_key(id
, key_buf
);
187 rec
= dbwrap_fetch_locked(db
, mem_ctx
, key
);
190 DBG_DEBUG("Failed to lock local id 0x%08x, key '%s'\n", id
,
191 hex_encode_talloc(talloc_tos(), key
.dptr
, key
.dsize
));
197 static NTSTATUS
smbXsrv_open_table_init(struct smbXsrv_connection
*conn
,
202 struct smbXsrv_client
*client
= conn
->client
;
203 struct smbXsrv_open_table
*table
;
207 if (lowest_id
> highest_id
) {
208 return NT_STATUS_INTERNAL_ERROR
;
211 max_range
= highest_id
;
212 max_range
-= lowest_id
;
215 if (max_opens
> max_range
) {
216 return NT_STATUS_INTERNAL_ERROR
;
219 table
= talloc_zero(client
, struct smbXsrv_open_table
);
221 return NT_STATUS_NO_MEMORY
;
224 table
->local
.db_ctx
= db_open_rbt(table
);
225 if (table
->local
.db_ctx
== NULL
) {
227 return NT_STATUS_NO_MEMORY
;
229 table
->local
.replay_cache_db_ctx
= db_open_rbt(table
);
230 if (table
->local
.replay_cache_db_ctx
== NULL
) {
232 return NT_STATUS_NO_MEMORY
;
234 table
->local
.lowest_id
= lowest_id
;
235 table
->local
.highest_id
= highest_id
;
236 table
->local
.max_opens
= max_opens
;
238 status
= smbXsrv_open_global_init();
239 if (!NT_STATUS_IS_OK(status
)) {
244 table
->global
.db_ctx
= smbXsrv_open_global_db_ctx
;
246 client
->open_table
= table
;
250 struct smbXsrv_open_local_allocate_state
{
251 const uint32_t lowest_id
;
252 const uint32_t highest_id
;
258 static int smbXsrv_open_local_allocate_traverse(struct db_record
*rec
,
261 struct smbXsrv_open_local_allocate_state
*state
=
262 (struct smbXsrv_open_local_allocate_state
*)private_data
;
263 TDB_DATA key
= dbwrap_record_get_key(rec
);
267 status
= smbXsrv_open_local_key_to_id(key
, &id
);
268 if (!NT_STATUS_IS_OK(status
)) {
269 state
->status
= status
;
273 if (id
<= state
->last_id
) {
274 state
->status
= NT_STATUS_INTERNAL_DB_CORRUPTION
;
279 if (id
> state
->useable_id
) {
280 state
->status
= NT_STATUS_OK
;
284 if (state
->useable_id
== state
->highest_id
) {
285 state
->status
= NT_STATUS_INSUFFICIENT_RESOURCES
;
289 state
->useable_id
+=1;
293 static NTSTATUS
smbXsrv_open_local_allocate_id(struct db_context
*db
,
297 struct db_record
**_rec
,
300 struct smbXsrv_open_local_allocate_state state
= {
301 .lowest_id
= lowest_id
,
302 .highest_id
= highest_id
,
304 .useable_id
= lowest_id
,
305 .status
= NT_STATUS_INTERNAL_ERROR
,
315 if (lowest_id
> highest_id
) {
316 return NT_STATUS_INSUFFICIENT_RESOURCES
;
320 * first we try randomly
322 range
= (highest_id
- lowest_id
) + 1;
324 for (i
= 0; i
< (range
/ 2); i
++) {
327 struct db_record
*rec
= NULL
;
329 id
= generate_random() % range
;
332 if (id
< lowest_id
) {
335 if (id
> highest_id
) {
339 rec
= smbXsrv_open_local_fetch_locked(db
, id
, mem_ctx
);
341 return NT_STATUS_INSUFFICIENT_RESOURCES
;
344 val
= dbwrap_record_get_value(rec
);
345 if (val
.dsize
!= 0) {
356 * if the range is almost full,
357 * we traverse the whole table
358 * (this relies on sorted behavior of dbwrap_rbt)
360 status
= dbwrap_traverse_read(db
, smbXsrv_open_local_allocate_traverse
,
362 if (NT_STATUS_IS_OK(status
)) {
363 if (NT_STATUS_IS_OK(state
.status
)) {
364 return NT_STATUS_INTERNAL_ERROR
;
367 if (!NT_STATUS_EQUAL(state
.status
, NT_STATUS_INTERNAL_ERROR
)) {
371 if (state
.useable_id
<= state
.highest_id
) {
372 state
.status
= NT_STATUS_OK
;
374 return NT_STATUS_INSUFFICIENT_RESOURCES
;
376 } else if (!NT_STATUS_EQUAL(status
, NT_STATUS_INTERNAL_DB_CORRUPTION
)) {
378 * Here we really expect NT_STATUS_INTERNAL_DB_CORRUPTION!
380 * If we get anything else it is an error, because it
381 * means we did not manage to find a free slot in
384 return NT_STATUS_INSUFFICIENT_RESOURCES
;
387 if (NT_STATUS_IS_OK(state
.status
)) {
390 struct db_record
*rec
= NULL
;
392 id
= state
.useable_id
;
394 rec
= smbXsrv_open_local_fetch_locked(db
, id
, mem_ctx
);
396 return NT_STATUS_INSUFFICIENT_RESOURCES
;
399 val
= dbwrap_record_get_value(rec
);
400 if (val
.dsize
!= 0) {
402 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
413 struct smbXsrv_open_local_fetch_state
{
414 struct smbXsrv_open
*op
;
418 static void smbXsrv_open_local_fetch_parser(TDB_DATA key
, TDB_DATA data
,
421 struct smbXsrv_open_local_fetch_state
*state
=
422 (struct smbXsrv_open_local_fetch_state
*)private_data
;
425 if (data
.dsize
!= sizeof(ptr
)) {
426 state
->status
= NT_STATUS_INTERNAL_DB_ERROR
;
430 memcpy(&ptr
, data
.dptr
, data
.dsize
);
431 state
->op
= talloc_get_type_abort(ptr
, struct smbXsrv_open
);
432 state
->status
= NT_STATUS_OK
;
435 static NTSTATUS
smbXsrv_open_local_lookup(struct smbXsrv_open_table
*table
,
436 uint32_t open_local_id
,
437 uint32_t open_global_id
,
439 struct smbXsrv_open
**_open
)
441 struct smbXsrv_open_local_fetch_state state
= {
443 .status
= NT_STATUS_INTERNAL_ERROR
,
445 uint8_t key_buf
[SMBXSRV_OPEN_LOCAL_TDB_KEY_SIZE
];
451 if (open_local_id
== 0) {
452 return NT_STATUS_FILE_CLOSED
;
456 /* this might happen before the end of negprot */
457 return NT_STATUS_FILE_CLOSED
;
460 if (table
->local
.db_ctx
== NULL
) {
461 return NT_STATUS_INTERNAL_ERROR
;
464 key
= smbXsrv_open_local_id_to_key(open_local_id
, key_buf
);
466 status
= dbwrap_parse_record(table
->local
.db_ctx
, key
,
467 smbXsrv_open_local_fetch_parser
,
469 if (NT_STATUS_EQUAL(status
, NT_STATUS_NOT_FOUND
)) {
470 return NT_STATUS_FILE_CLOSED
;
472 if (!NT_STATUS_IS_OK(status
)) {
475 if (!NT_STATUS_IS_OK(state
.status
)) {
479 if (NT_STATUS_EQUAL(state
.op
->status
, NT_STATUS_FILE_CLOSED
)) {
480 return NT_STATUS_FILE_CLOSED
;
483 if (open_global_id
== 0) {
484 /* make the global check a no-op for SMB1 */
485 open_global_id
= state
.op
->global
->open_global_id
;
488 if (state
.op
->global
->open_global_id
!= open_global_id
) {
489 return NT_STATUS_FILE_CLOSED
;
493 state
.op
->idle_time
= now
;
497 return state
.op
->status
;
500 static int smbXsrv_open_global_destructor(struct smbXsrv_open_global0
*global
)
505 static void smbXsrv_open_global_verify_record(struct db_record
*db_rec
,
509 struct smbXsrv_open_global0
**_g
);
511 static NTSTATUS
smbXsrv_open_global_allocate(struct db_context
*db
,
513 struct smbXsrv_open_global0
**_global
)
516 struct smbXsrv_open_global0
*global
= NULL
;
517 uint32_t last_free
= 0;
518 const uint32_t min_tries
= 3;
522 global
= talloc_zero(mem_ctx
, struct smbXsrv_open_global0
);
523 if (global
== NULL
) {
524 return NT_STATUS_NO_MEMORY
;
526 talloc_set_destructor(global
, smbXsrv_open_global_destructor
);
529 * Here we just randomly try the whole 32-bit space
531 * We use just 32-bit, because we want to reuse the
534 for (i
= 0; i
< UINT32_MAX
; i
++) {
535 bool is_free
= false;
536 bool was_free
= false;
539 if (i
>= min_tries
&& last_free
!= 0) {
542 id
= generate_random();
547 if (id
== UINT32_MAX
) {
551 global
->db_rec
= smbXsrv_open_global_fetch_locked(db
, id
, mem_ctx
);
552 if (global
->db_rec
== NULL
) {
554 return NT_STATUS_INSUFFICIENT_RESOURCES
;
557 smbXsrv_open_global_verify_record(global
->db_rec
,
563 TALLOC_FREE(global
->db_rec
);
567 if (!was_free
&& i
< min_tries
) {
569 * The session_id is free now,
570 * but was not free before.
572 * This happens if a smbd crashed
573 * and did not cleanup the record.
575 * If this is one of our first tries,
576 * then we try to find a real free one.
578 if (last_free
== 0) {
581 TALLOC_FREE(global
->db_rec
);
585 global
->open_global_id
= id
;
591 /* should not be reached */
593 return NT_STATUS_INTERNAL_ERROR
;
596 static void smbXsrv_open_global_verify_record(struct db_record
*db_rec
,
600 struct smbXsrv_open_global0
**_g
)
605 struct smbXsrv_open_globalB global_blob
;
606 enum ndr_err_code ndr_err
;
607 struct smbXsrv_open_global0
*global
= NULL
;
609 TALLOC_CTX
*frame
= talloc_stackframe();
620 key
= dbwrap_record_get_key(db_rec
);
622 val
= dbwrap_record_get_value(db_rec
);
623 if (val
.dsize
== 0) {
624 DEBUG(10, ("%s: empty value\n", __func__
));
633 blob
= data_blob_const(val
.dptr
, val
.dsize
);
635 ndr_err
= ndr_pull_struct_blob(&blob
, frame
, &global_blob
,
636 (ndr_pull_flags_fn_t
)ndr_pull_smbXsrv_open_globalB
);
637 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
638 NTSTATUS status
= ndr_map_error2ntstatus(ndr_err
);
639 DEBUG(1,("smbXsrv_open_global_verify_record: "
640 "key '%s' ndr_pull_struct_blob - %s\n",
641 hex_encode_talloc(frame
, key
.dptr
, key
.dsize
),
647 DEBUG(10,("smbXsrv_open_global_verify_record\n"));
648 if (CHECK_DEBUGLVL(10)) {
649 NDR_PRINT_DEBUG(smbXsrv_open_globalB
, &global_blob
);
652 if (global_blob
.version
!= SMBXSRV_VERSION_0
) {
653 DEBUG(0,("smbXsrv_open_global_verify_record: "
654 "key '%s' use unsupported version %u\n",
655 hex_encode_talloc(frame
, key
.dptr
, key
.dsize
),
656 global_blob
.version
));
657 NDR_PRINT_DEBUG(smbXsrv_open_globalB
, &global_blob
);
662 global
= global_blob
.info
.info0
;
664 if (server_id_is_disconnected(&global
->server_id
)) {
667 exists
= serverid_exists(&global
->server_id
);
670 struct server_id_buf idbuf
;
671 DEBUG(2,("smbXsrv_open_global_verify_record: "
672 "key '%s' server_id %s does not exist.\n",
673 hex_encode_talloc(frame
, key
.dptr
, key
.dsize
),
674 server_id_str_buf(global
->server_id
, &idbuf
)));
675 if (CHECK_DEBUGLVL(2)) {
676 NDR_PRINT_DEBUG(smbXsrv_open_globalB
, &global_blob
);
679 dbwrap_record_delete(db_rec
);
685 *_g
= talloc_move(mem_ctx
, &global
);
690 static NTSTATUS
smbXsrv_open_global_store(struct smbXsrv_open_global0
*global
)
692 struct smbXsrv_open_globalB global_blob
;
693 DATA_BLOB blob
= data_blob_null
;
697 enum ndr_err_code ndr_err
;
700 * TODO: if we use other versions than '0'
701 * we would add glue code here, that would be able to
702 * store the information in the old format.
705 if (global
->db_rec
== NULL
) {
706 return NT_STATUS_INTERNAL_ERROR
;
709 key
= dbwrap_record_get_key(global
->db_rec
);
710 val
= dbwrap_record_get_value(global
->db_rec
);
712 ZERO_STRUCT(global_blob
);
713 global_blob
.version
= smbXsrv_version_global_current();
714 if (val
.dsize
>= 8) {
715 global_blob
.seqnum
= IVAL(val
.dptr
, 4);
717 global_blob
.seqnum
+= 1;
718 global_blob
.info
.info0
= global
;
720 ndr_err
= ndr_push_struct_blob(&blob
, global
->db_rec
, &global_blob
,
721 (ndr_push_flags_fn_t
)ndr_push_smbXsrv_open_globalB
);
722 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
723 status
= ndr_map_error2ntstatus(ndr_err
);
724 DEBUG(1,("smbXsrv_open_global_store: key '%s' ndr_push - %s\n",
725 hex_encode_talloc(global
->db_rec
, key
.dptr
, key
.dsize
),
727 TALLOC_FREE(global
->db_rec
);
731 val
= make_tdb_data(blob
.data
, blob
.length
);
732 status
= dbwrap_record_store(global
->db_rec
, val
, TDB_REPLACE
);
733 if (!NT_STATUS_IS_OK(status
)) {
734 DEBUG(1,("smbXsrv_open_global_store: key '%s' store - %s\n",
735 hex_encode_talloc(global
->db_rec
, key
.dptr
, key
.dsize
),
737 TALLOC_FREE(global
->db_rec
);
741 if (CHECK_DEBUGLVL(10)) {
742 DEBUG(10,("smbXsrv_open_global_store: key '%s' stored\n",
743 hex_encode_talloc(global
->db_rec
, key
.dptr
, key
.dsize
)));
744 NDR_PRINT_DEBUG(smbXsrv_open_globalB
, &global_blob
);
747 TALLOC_FREE(global
->db_rec
);
752 static NTSTATUS
smbXsrv_open_global_lookup(struct smbXsrv_open_table
*table
,
753 uint32_t open_global_id
,
755 struct smbXsrv_open_global0
**_global
)
757 struct db_record
*global_rec
= NULL
;
758 bool is_free
= false;
762 if (table
->global
.db_ctx
== NULL
) {
763 return NT_STATUS_INTERNAL_ERROR
;
766 global_rec
= smbXsrv_open_global_fetch_locked(table
->global
.db_ctx
,
769 if (global_rec
== NULL
) {
770 return NT_STATUS_INTERNAL_DB_ERROR
;
773 smbXsrv_open_global_verify_record(global_rec
,
779 DEBUG(10, ("%s: is_free=true\n", __func__
));
780 talloc_free(global_rec
);
781 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
784 (*_global
)->db_rec
= talloc_move(*_global
, &global_rec
);
786 talloc_set_destructor(*_global
, smbXsrv_open_global_destructor
);
791 static int smbXsrv_open_destructor(struct smbXsrv_open
*op
)
795 status
= smbXsrv_open_close(op
, 0);
796 if (!NT_STATUS_IS_OK(status
)) {
797 DEBUG(0, ("smbXsrv_open_destructor: "
798 "smbXsrv_open_close() failed - %s\n",
802 TALLOC_FREE(op
->global
);
807 NTSTATUS
smbXsrv_open_create(struct smbXsrv_connection
*conn
,
808 struct auth_session_info
*session_info
,
810 struct smbXsrv_open
**_open
)
812 struct smbXsrv_open_table
*table
= conn
->client
->open_table
;
813 struct db_record
*local_rec
= NULL
;
814 struct smbXsrv_open
*op
= NULL
;
817 struct smbXsrv_open_global0
*global
= NULL
;
819 struct dom_sid
*current_sid
= NULL
;
820 struct security_token
*current_token
= NULL
;
822 if (session_info
== NULL
) {
823 return NT_STATUS_INVALID_HANDLE
;
825 current_token
= session_info
->security_token
;
827 if (current_token
== NULL
) {
828 return NT_STATUS_INVALID_HANDLE
;
831 if (current_token
->num_sids
> PRIMARY_USER_SID_INDEX
) {
832 current_sid
= ¤t_token
->sids
[PRIMARY_USER_SID_INDEX
];
835 if (current_sid
== NULL
) {
836 return NT_STATUS_INVALID_HANDLE
;
839 if (table
->local
.num_opens
>= table
->local
.max_opens
) {
840 return NT_STATUS_INSUFFICIENT_RESOURCES
;
843 op
= talloc_zero(table
, struct smbXsrv_open
);
845 return NT_STATUS_NO_MEMORY
;
848 op
->status
= NT_STATUS_OK
; /* TODO: start with INTERNAL_ERROR */
851 status
= smbXsrv_open_global_allocate(table
->global
.db_ctx
,
853 if (!NT_STATUS_IS_OK(status
)) {
859 status
= smbXsrv_open_local_allocate_id(table
->local
.db_ctx
,
860 table
->local
.lowest_id
,
861 table
->local
.highest_id
,
865 if (!NT_STATUS_IS_OK(status
)) {
870 global
->open_persistent_id
= global
->open_global_id
;
871 global
->open_volatile_id
= op
->local_id
;
873 global
->server_id
= messaging_server_id(conn
->msg_ctx
);
874 global
->open_time
= now
;
875 global
->open_owner
= *current_sid
;
876 if (conn
->protocol
>= PROTOCOL_SMB2_10
) {
877 global
->client_guid
= conn
->smb2
.client
.guid
;
881 val
= make_tdb_data((uint8_t const *)&ptr
, sizeof(ptr
));
882 status
= dbwrap_record_store(local_rec
, val
, TDB_REPLACE
);
883 TALLOC_FREE(local_rec
);
884 if (!NT_STATUS_IS_OK(status
)) {
888 table
->local
.num_opens
+= 1;
890 talloc_set_destructor(op
, smbXsrv_open_destructor
);
892 status
= smbXsrv_open_global_store(global
);
893 if (!NT_STATUS_IS_OK(status
)) {
894 DEBUG(0,("smbXsrv_open_create: "
895 "global_id (0x%08x) store failed - %s\n",
896 op
->global
->open_global_id
,
902 if (CHECK_DEBUGLVL(10)) {
903 struct smbXsrv_openB open_blob
;
905 ZERO_STRUCT(open_blob
);
906 open_blob
.version
= SMBXSRV_VERSION_0
;
907 open_blob
.info
.info0
= op
;
909 DEBUG(10,("smbXsrv_open_create: global_id (0x%08x) stored\n",
910 op
->global
->open_global_id
));
911 NDR_PRINT_DEBUG(smbXsrv_openB
, &open_blob
);
918 uint32_t smbXsrv_open_hash(struct smbXsrv_open
*_open
)
924 SBVAL(buf
, 0, _open
->global
->open_persistent_id
);
925 SBVAL(buf
, 8, _open
->global
->open_volatile_id
);
926 SBVAL(buf
, 16, _open
->global
->open_time
);
928 key
= (TDB_DATA
) { .dptr
= buf
, .dsize
= sizeof(buf
) };
929 ret
= tdb_jenkins_hash(&key
);
938 static NTSTATUS
smbXsrv_open_set_replay_cache(struct smbXsrv_open
*op
)
940 struct GUID
*create_guid
;
941 struct GUID_txt_buf buf
;
943 struct db_context
*db
= op
->table
->local
.replay_cache_db_ctx
;
946 if (!(op
->flags
& SMBXSRV_OPEN_NEED_REPLAY_CACHE
)) {
950 if (op
->flags
& SMBXSRV_OPEN_HAVE_REPLAY_CACHE
) {
954 create_guid
= &op
->global
->create_guid
;
955 if (GUID_all_zero(create_guid
)) {
959 guid_string
= GUID_buf_string(create_guid
, &buf
);
960 if (guid_string
== NULL
) {
961 return NT_STATUS_INVALID_PARAMETER
;
964 status
= dbwrap_store_uint32_bystring(db
, guid_string
, op
->local_id
);
966 if (NT_STATUS_IS_OK(status
)) {
967 op
->flags
|= SMBXSRV_OPEN_HAVE_REPLAY_CACHE
;
968 op
->flags
&= ~SMBXSRV_OPEN_NEED_REPLAY_CACHE
;
974 static NTSTATUS
smbXsrv_open_clear_replay_cache(struct smbXsrv_open
*op
)
976 struct GUID
*create_guid
;
977 struct GUID_txt_buf buf
;
979 struct db_context
*db
;
982 if (op
->table
== NULL
) {
986 db
= op
->table
->local
.replay_cache_db_ctx
;
988 if (!(op
->flags
& SMBXSRV_OPEN_HAVE_REPLAY_CACHE
)) {
992 create_guid
= &op
->global
->create_guid
;
993 if (GUID_all_zero(create_guid
)) {
997 guid_string
= GUID_buf_string(create_guid
, &buf
);
998 if (guid_string
== NULL
) {
999 return NT_STATUS_INVALID_PARAMETER
;
1002 status
= dbwrap_purge_bystring(db
, guid_string
);
1004 if (NT_STATUS_IS_OK(status
)) {
1005 op
->flags
&= ~SMBXSRV_OPEN_HAVE_REPLAY_CACHE
;
1011 NTSTATUS
smbXsrv_open_update(struct smbXsrv_open
*op
)
1013 struct smbXsrv_open_table
*table
= op
->table
;
1016 if (op
->global
->db_rec
!= NULL
) {
1017 DEBUG(0, ("smbXsrv_open_update(0x%08x): "
1018 "Called with db_rec != NULL'\n",
1019 op
->global
->open_global_id
));
1020 return NT_STATUS_INTERNAL_ERROR
;
1023 op
->global
->db_rec
= smbXsrv_open_global_fetch_locked(
1024 table
->global
.db_ctx
,
1025 op
->global
->open_global_id
,
1026 op
->global
/* TALLOC_CTX */);
1027 if (op
->global
->db_rec
== NULL
) {
1028 return NT_STATUS_INTERNAL_DB_ERROR
;
1031 status
= smbXsrv_open_global_store(op
->global
);
1032 if (!NT_STATUS_IS_OK(status
)) {
1033 DEBUG(0,("smbXsrv_open_update: "
1034 "global_id (0x%08x) store failed - %s\n",
1035 op
->global
->open_global_id
,
1036 nt_errstr(status
)));
1040 status
= smbXsrv_open_set_replay_cache(op
);
1041 if (!NT_STATUS_IS_OK(status
)) {
1042 DBG_ERR("smbXsrv_open_set_replay_cache failed: %s\n",
1047 if (CHECK_DEBUGLVL(10)) {
1048 struct smbXsrv_openB open_blob
;
1050 ZERO_STRUCT(open_blob
);
1051 open_blob
.version
= SMBXSRV_VERSION_0
;
1052 open_blob
.info
.info0
= op
;
1054 DEBUG(10,("smbXsrv_open_update: global_id (0x%08x) stored\n",
1055 op
->global
->open_global_id
));
1056 NDR_PRINT_DEBUG(smbXsrv_openB
, &open_blob
);
1059 return NT_STATUS_OK
;
1062 NTSTATUS
smbXsrv_open_close(struct smbXsrv_open
*op
, NTTIME now
)
1064 struct smbXsrv_open_table
*table
;
1065 struct db_record
*local_rec
= NULL
;
1066 struct db_record
*global_rec
= NULL
;
1068 NTSTATUS error
= NT_STATUS_OK
;
1070 error
= smbXsrv_open_clear_replay_cache(op
);
1071 if (!NT_STATUS_IS_OK(error
)) {
1072 DBG_ERR("smbXsrv_open_clear_replay_cache failed: %s\n",
1076 if (op
->table
== NULL
) {
1083 op
->status
= NT_STATUS_FILE_CLOSED
;
1084 op
->global
->disconnect_time
= now
;
1085 server_id_set_disconnected(&op
->global
->server_id
);
1087 global_rec
= op
->global
->db_rec
;
1088 op
->global
->db_rec
= NULL
;
1089 if (global_rec
== NULL
) {
1090 global_rec
= smbXsrv_open_global_fetch_locked(
1091 table
->global
.db_ctx
,
1092 op
->global
->open_global_id
,
1093 op
->global
/* TALLOC_CTX */);
1094 if (global_rec
== NULL
) {
1095 error
= NT_STATUS_INTERNAL_ERROR
;
1099 if (global_rec
!= NULL
&& op
->global
->durable
) {
1101 * If it is a durable open we need to update the global part
1102 * instead of deleting it
1104 op
->global
->db_rec
= global_rec
;
1105 status
= smbXsrv_open_global_store(op
->global
);
1106 if (NT_STATUS_IS_OK(status
)) {
1108 * smbXsrv_open_global_store does the free
1109 * of op->global->db_rec
1113 if (!NT_STATUS_IS_OK(status
)) {
1114 DEBUG(0,("smbXsrv_open_close(0x%08x)"
1115 "smbXsrv_open_global_store() failed - %s\n",
1116 op
->global
->open_global_id
,
1117 nt_errstr(status
)));
1121 if (NT_STATUS_IS_OK(status
) && CHECK_DEBUGLVL(10)) {
1122 struct smbXsrv_openB open_blob
;
1124 ZERO_STRUCT(open_blob
);
1125 open_blob
.version
= SMBXSRV_VERSION_0
;
1126 open_blob
.info
.info0
= op
;
1128 DEBUG(10,("smbXsrv_open_close(0x%08x): "
1129 "stored disconnect\n",
1130 op
->global
->open_global_id
));
1131 NDR_PRINT_DEBUG(smbXsrv_openB
, &open_blob
);
1135 if (global_rec
!= NULL
) {
1136 status
= dbwrap_record_delete(global_rec
);
1137 if (!NT_STATUS_IS_OK(status
)) {
1138 TDB_DATA key
= dbwrap_record_get_key(global_rec
);
1140 DEBUG(0, ("smbXsrv_open_close(0x%08x): "
1141 "failed to delete global key '%s': %s\n",
1142 op
->global
->open_global_id
,
1143 hex_encode_talloc(global_rec
, key
.dptr
,
1145 nt_errstr(status
)));
1149 TALLOC_FREE(global_rec
);
1151 local_rec
= op
->db_rec
;
1152 if (local_rec
== NULL
) {
1153 local_rec
= smbXsrv_open_local_fetch_locked(table
->local
.db_ctx
,
1155 op
/* TALLOC_CTX*/);
1156 if (local_rec
== NULL
) {
1157 error
= NT_STATUS_INTERNAL_ERROR
;
1161 if (local_rec
!= NULL
) {
1162 status
= dbwrap_record_delete(local_rec
);
1163 if (!NT_STATUS_IS_OK(status
)) {
1164 TDB_DATA key
= dbwrap_record_get_key(local_rec
);
1166 DEBUG(0, ("smbXsrv_open_close(0x%08x): "
1167 "failed to delete local key '%s': %s\n",
1168 op
->global
->open_global_id
,
1169 hex_encode_talloc(local_rec
, key
.dptr
,
1171 nt_errstr(status
)));
1174 table
->local
.num_opens
-= 1;
1176 if (op
->db_rec
== NULL
) {
1177 TALLOC_FREE(local_rec
);
1182 op
->compat
->op
= NULL
;
1183 file_free(NULL
, op
->compat
);
1190 NTSTATUS
smb1srv_open_table_init(struct smbXsrv_connection
*conn
)
1195 * Allow a range from 1..65534.
1197 * With real_max_open_files possible ids,
1198 * truncated to the SMB1 limit of 16-bit.
1200 * 0 and 0xFFFF are no valid ids.
1202 max_opens
= conn
->client
->sconn
->real_max_open_files
;
1203 max_opens
= MIN(max_opens
, UINT16_MAX
- 1);
1205 return smbXsrv_open_table_init(conn
, 1, UINT16_MAX
- 1, max_opens
);
1208 NTSTATUS
smb1srv_open_lookup(struct smbXsrv_connection
*conn
,
1209 uint16_t fnum
, NTTIME now
,
1210 struct smbXsrv_open
**_open
)
1212 struct smbXsrv_open_table
*table
= conn
->client
->open_table
;
1213 uint32_t local_id
= fnum
;
1214 uint32_t global_id
= 0;
1216 return smbXsrv_open_local_lookup(table
, local_id
, global_id
, now
, _open
);
1219 NTSTATUS
smb2srv_open_table_init(struct smbXsrv_connection
*conn
)
1224 * Allow a range from 1..4294967294.
1226 * With real_max_open_files possible ids,
1227 * truncated to 16-bit (the same as SMB1 for now).
1229 * 0 and 0xFFFFFFFF are no valid ids.
1231 * The usage of conn->sconn->real_max_open_files
1232 * is the reason that we use one open table per
1233 * transport connection (as we still have a 1:1 mapping
1234 * between process and transport connection).
1236 max_opens
= conn
->client
->sconn
->real_max_open_files
;
1237 max_opens
= MIN(max_opens
, UINT16_MAX
- 1);
1239 return smbXsrv_open_table_init(conn
, 1, UINT32_MAX
- 1, max_opens
);
1242 NTSTATUS
smb2srv_open_lookup(struct smbXsrv_connection
*conn
,
1243 uint64_t persistent_id
,
1244 uint64_t volatile_id
,
1246 struct smbXsrv_open
**_open
)
1248 struct smbXsrv_open_table
*table
= conn
->client
->open_table
;
1249 uint32_t local_id
= volatile_id
& UINT32_MAX
;
1250 uint64_t local_zeros
= volatile_id
& 0xFFFFFFFF00000000LLU
;
1251 uint32_t global_id
= persistent_id
& UINT32_MAX
;
1252 uint64_t global_zeros
= persistent_id
& 0xFFFFFFFF00000000LLU
;
1255 if (local_zeros
!= 0) {
1256 return NT_STATUS_FILE_CLOSED
;
1259 if (global_zeros
!= 0) {
1260 return NT_STATUS_FILE_CLOSED
;
1263 if (global_id
== 0) {
1264 return NT_STATUS_FILE_CLOSED
;
1267 status
= smbXsrv_open_local_lookup(table
, local_id
, global_id
, now
,
1269 if (!NT_STATUS_IS_OK(status
)) {
1274 * Clear the replay cache for this create_guid if it exists:
1275 * This is based on the assumption that this lookup will be
1276 * triggered by a client request using the file-id for lookup.
1277 * Hence the client has proven that it has in fact seen the
1278 * reply to its initial create call. So subsequent create replays
1279 * should be treated as invalid. Hence the index for create_guid
1280 * lookup needs to be removed.
1282 status
= smbXsrv_open_clear_replay_cache(*_open
);
1287 NTSTATUS
smb2srv_open_lookup_replay_cache(struct smbXsrv_connection
*conn
,
1288 const struct GUID
*create_guid
,
1289 NTTIME now
, /* TODO: needed ? */
1290 struct smbXsrv_open
**_open
)
1294 struct GUID_txt_buf buf
;
1295 uint32_t local_id
= 0;
1296 struct smbXsrv_open_table
*table
= conn
->client
->open_table
;
1297 struct db_context
*db
= table
->local
.replay_cache_db_ctx
;
1299 if (GUID_all_zero(create_guid
)) {
1300 return NT_STATUS_NOT_FOUND
;
1303 guid_string
= GUID_buf_string(create_guid
, &buf
);
1304 if (guid_string
== NULL
) {
1305 return NT_STATUS_INVALID_PARAMETER
;
1308 status
= dbwrap_fetch_uint32_bystring(db
, guid_string
, &local_id
);
1309 if (NT_STATUS_EQUAL(status
, NT_STATUS_NOT_FOUND
)) {
1312 if (!NT_STATUS_IS_OK(status
)) {
1313 DBG_ERR("failed to fetch local_id from replay cache: %s\n",
1318 status
= smbXsrv_open_local_lookup(table
, local_id
, 0, /* global_id */
1320 if (!NT_STATUS_IS_OK(status
)) {
1321 DBG_ERR("smbXsrv_open_local_lookup failed for local_id %u\n",
1322 (unsigned)local_id
);
1328 NTSTATUS
smb2srv_open_recreate(struct smbXsrv_connection
*conn
,
1329 struct auth_session_info
*session_info
,
1330 uint64_t persistent_id
,
1331 const struct GUID
*create_guid
,
1333 struct smbXsrv_open
**_open
)
1335 struct smbXsrv_open_table
*table
= conn
->client
->open_table
;
1336 struct db_record
*local_rec
= NULL
;
1337 struct smbXsrv_open
*op
= NULL
;
1340 uint32_t global_id
= persistent_id
& UINT32_MAX
;
1341 uint64_t global_zeros
= persistent_id
& 0xFFFFFFFF00000000LLU
;
1343 struct security_token
*current_token
= NULL
;
1345 if (session_info
== NULL
) {
1346 DEBUG(10, ("session_info=NULL\n"));
1347 return NT_STATUS_INVALID_HANDLE
;
1349 current_token
= session_info
->security_token
;
1351 if (current_token
== NULL
) {
1352 DEBUG(10, ("current_token=NULL\n"));
1353 return NT_STATUS_INVALID_HANDLE
;
1356 if (global_zeros
!= 0) {
1357 DEBUG(10, ("global_zeros!=0\n"));
1358 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
1361 op
= talloc_zero(table
, struct smbXsrv_open
);
1363 return NT_STATUS_NO_MEMORY
;
1367 status
= smbXsrv_open_global_lookup(table
, global_id
, op
, &op
->global
);
1368 if (!NT_STATUS_IS_OK(status
)) {
1370 DEBUG(10, ("smbXsrv_open_global_lookup returned %s\n",
1371 nt_errstr(status
)));
1376 * If the provided create_guid is NULL, this means that
1377 * the reconnect request was a v1 request. In that case
1378 * we should skipt the create GUID verification, since
1379 * it is valid to v1-reconnect a v2-opened handle.
1381 if ((create_guid
!= NULL
) &&
1382 !GUID_equal(&op
->global
->create_guid
, create_guid
))
1385 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
1388 if (!security_token_is_sid(current_token
, &op
->global
->open_owner
)) {
1390 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
1393 if (!op
->global
->durable
) {
1395 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
1398 if (table
->local
.num_opens
>= table
->local
.max_opens
) {
1400 return NT_STATUS_INSUFFICIENT_RESOURCES
;
1403 status
= smbXsrv_open_local_allocate_id(table
->local
.db_ctx
,
1404 table
->local
.lowest_id
,
1405 table
->local
.highest_id
,
1409 if (!NT_STATUS_IS_OK(status
)) {
1414 op
->idle_time
= now
;
1415 op
->status
= NT_STATUS_FILE_CLOSED
;
1417 op
->global
->open_volatile_id
= op
->local_id
;
1418 op
->global
->server_id
= messaging_server_id(conn
->msg_ctx
);
1421 val
= make_tdb_data((uint8_t const *)&ptr
, sizeof(ptr
));
1422 status
= dbwrap_record_store(local_rec
, val
, TDB_REPLACE
);
1423 TALLOC_FREE(local_rec
);
1424 if (!NT_STATUS_IS_OK(status
)) {
1428 table
->local
.num_opens
+= 1;
1430 talloc_set_destructor(op
, smbXsrv_open_destructor
);
1432 status
= smbXsrv_open_global_store(op
->global
);
1433 if (!NT_STATUS_IS_OK(status
)) {
1438 if (CHECK_DEBUGLVL(10)) {
1439 struct smbXsrv_openB open_blob
;
1441 ZERO_STRUCT(open_blob
);
1442 open_blob
.version
= 0;
1443 open_blob
.info
.info0
= op
;
1445 DEBUG(10,("smbXsrv_open_recreate: global_id (0x%08x) stored\n",
1446 op
->global
->open_global_id
));
1447 NDR_PRINT_DEBUG(smbXsrv_openB
, &open_blob
);
1451 return NT_STATUS_OK
;
1455 static NTSTATUS
smbXsrv_open_global_parse_record(TALLOC_CTX
*mem_ctx
,
1456 struct db_record
*rec
,
1457 struct smbXsrv_open_global0
**global
)
1459 TDB_DATA key
= dbwrap_record_get_key(rec
);
1460 TDB_DATA val
= dbwrap_record_get_value(rec
);
1461 DATA_BLOB blob
= data_blob_const(val
.dptr
, val
.dsize
);
1462 struct smbXsrv_open_globalB global_blob
;
1463 enum ndr_err_code ndr_err
;
1465 TALLOC_CTX
*frame
= talloc_stackframe();
1467 ndr_err
= ndr_pull_struct_blob(&blob
, frame
, &global_blob
,
1468 (ndr_pull_flags_fn_t
)ndr_pull_smbXsrv_open_globalB
);
1469 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1470 DEBUG(1,("Invalid record in smbXsrv_open_global.tdb:"
1471 "key '%s' ndr_pull_struct_blob - %s\n",
1472 hex_encode_talloc(frame
, key
.dptr
, key
.dsize
),
1473 ndr_errstr(ndr_err
)));
1474 status
= ndr_map_error2ntstatus(ndr_err
);
1478 if (global_blob
.version
!= SMBXSRV_VERSION_0
) {
1479 status
= NT_STATUS_INTERNAL_DB_CORRUPTION
;
1480 DEBUG(1,("Invalid record in smbXsrv_open_global.tdb:"
1481 "key '%s' unsuported version - %d - %s\n",
1482 hex_encode_talloc(frame
, key
.dptr
, key
.dsize
),
1483 (int)global_blob
.version
,
1484 nt_errstr(status
)));
1488 *global
= talloc_move(mem_ctx
, &global_blob
.info
.info0
);
1489 status
= NT_STATUS_OK
;
1495 struct smbXsrv_open_global_traverse_state
{
1496 int (*fn
)(struct smbXsrv_open_global0
*, void *);
1500 static int smbXsrv_open_global_traverse_fn(struct db_record
*rec
, void *data
)
1502 struct smbXsrv_open_global_traverse_state
*state
=
1503 (struct smbXsrv_open_global_traverse_state
*)data
;
1504 struct smbXsrv_open_global0
*global
= NULL
;
1508 status
= smbXsrv_open_global_parse_record(talloc_tos(), rec
, &global
);
1509 if (!NT_STATUS_IS_OK(status
)) {
1513 global
->db_rec
= rec
;
1514 ret
= state
->fn(global
, state
->private_data
);
1515 talloc_free(global
);
1519 NTSTATUS
smbXsrv_open_global_traverse(
1520 int (*fn
)(struct smbXsrv_open_global0
*, void *),
1526 struct smbXsrv_open_global_traverse_state state
= {
1528 .private_data
= private_data
,
1532 status
= smbXsrv_open_global_init();
1533 if (!NT_STATUS_IS_OK(status
)) {
1535 DEBUG(0, ("Failed to initialize open_global: %s\n",
1536 nt_errstr(status
)));
1540 status
= dbwrap_traverse_read(smbXsrv_open_global_db_ctx
,
1541 smbXsrv_open_global_traverse_fn
,
1549 NTSTATUS
smbXsrv_open_cleanup(uint64_t persistent_id
)
1551 NTSTATUS status
= NT_STATUS_OK
;
1552 TALLOC_CTX
*frame
= talloc_stackframe();
1553 struct smbXsrv_open_global0
*op
= NULL
;
1555 struct db_record
*rec
;
1556 bool delete_open
= false;
1557 uint32_t global_id
= persistent_id
& UINT32_MAX
;
1559 rec
= smbXsrv_open_global_fetch_locked(smbXsrv_open_global_db_ctx
,
1563 status
= NT_STATUS_NOT_FOUND
;
1567 val
= dbwrap_record_get_value(rec
);
1568 if (val
.dsize
== 0) {
1569 DEBUG(10, ("smbXsrv_open_cleanup[global: 0x%08x] "
1570 "empty record in %s, skipping...\n",
1571 global_id
, dbwrap_name(smbXsrv_open_global_db_ctx
)));
1575 status
= smbXsrv_open_global_parse_record(talloc_tos(), rec
, &op
);
1576 if (!NT_STATUS_IS_OK(status
)) {
1577 DEBUG(1, ("smbXsrv_open_cleanup[global: 0x%08x] "
1578 "failed to read record: %s\n",
1579 global_id
, nt_errstr(status
)));
1583 if (server_id_is_disconnected(&op
->server_id
)) {
1584 struct timeval now
, disconnect_time
;
1586 now
= timeval_current();
1587 nttime_to_timeval(&disconnect_time
, op
->disconnect_time
);
1588 tdiff
= usec_time_diff(&now
, &disconnect_time
);
1589 delete_open
= (tdiff
>= 1000*op
->durable_timeout_msec
);
1591 DEBUG(10, ("smbXsrv_open_cleanup[global: 0x%08x] "
1592 "disconnected at [%s] %us ago with "
1593 "timeout of %us -%s reached\n",
1595 nt_time_string(frame
, op
->disconnect_time
),
1596 (unsigned)(tdiff
/1000000),
1597 op
->durable_timeout_msec
/ 1000,
1598 delete_open
? "" : " not"));
1599 } else if (!serverid_exists(&op
->server_id
)) {
1600 struct server_id_buf idbuf
;
1601 DEBUG(10, ("smbXsrv_open_cleanup[global: 0x%08x] "
1602 "server[%s] does not exist\n",
1604 server_id_str_buf(op
->server_id
, &idbuf
)));
1612 status
= dbwrap_record_delete(rec
);
1613 if (!NT_STATUS_IS_OK(status
)) {
1614 DEBUG(1, ("smbXsrv_open_cleanup[global: 0x%08x] "
1615 "failed to delete record"
1616 "from %s: %s\n", global_id
,
1617 dbwrap_name(smbXsrv_open_global_db_ctx
),
1618 nt_errstr(status
)));
1622 DEBUG(10, ("smbXsrv_open_cleanup[global: 0x%08x] "
1623 "delete record from %s\n",
1625 dbwrap_name(smbXsrv_open_global_db_ctx
)));