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"
32 #include <ccan/hash/hash.h>
35 struct smbXsrv_open_table
{
37 struct db_context
*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 const 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");
61 db_ctx
= db_open(NULL
, global_path
,
65 TDB_INCOMPATIBLE_HASH
,
66 O_RDWR
| O_CREAT
, 0600,
72 status
= map_nt_error_from_unix_common(errno
);
77 smbXsrv_open_global_db_ctx
= db_ctx
;
84 * We need to store the keys in big endian so that dbwrap_rbt's memcmp
85 * has the same result as integer comparison between the uint32_t
88 * TODO: implement string based key
91 #define SMBXSRV_OPEN_GLOBAL_TDB_KEY_SIZE sizeof(uint32_t)
93 static TDB_DATA
smbXsrv_open_global_id_to_key(uint32_t id
,
98 RSIVAL(key_buf
, 0, id
);
100 key
= make_tdb_data(key_buf
, SMBXSRV_OPEN_GLOBAL_TDB_KEY_SIZE
);
106 static NTSTATUS
smbXsrv_open_global_key_to_id(TDB_DATA key
, uint32_t *id
)
109 return NT_STATUS_INVALID_PARAMETER
;
112 if (key
.dsize
!= SMBXSRV_OPEN_GLOBAL_TDB_KEY_SIZE
) {
113 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
116 *id
= RIVAL(key
.dptr
, 0);
122 #define SMBXSRV_OPEN_LOCAL_TDB_KEY_SIZE sizeof(uint32_t)
124 static TDB_DATA
smbXsrv_open_local_id_to_key(uint32_t id
,
129 RSIVAL(key_buf
, 0, id
);
131 key
= make_tdb_data(key_buf
, SMBXSRV_OPEN_LOCAL_TDB_KEY_SIZE
);
136 static NTSTATUS
smbXsrv_open_local_key_to_id(TDB_DATA key
, uint32_t *id
)
139 return NT_STATUS_INVALID_PARAMETER
;
142 if (key
.dsize
!= SMBXSRV_OPEN_LOCAL_TDB_KEY_SIZE
) {
143 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
146 *id
= RIVAL(key
.dptr
, 0);
151 static NTSTATUS
smbXsrv_open_table_init(struct smbXsrv_connection
*conn
,
156 struct smbXsrv_open_table
*table
;
160 if (lowest_id
> highest_id
) {
161 return NT_STATUS_INTERNAL_ERROR
;
164 max_range
= highest_id
;
165 max_range
-= lowest_id
;
168 if (max_opens
> max_range
) {
169 return NT_STATUS_INTERNAL_ERROR
;
172 table
= talloc_zero(conn
, struct smbXsrv_open_table
);
174 return NT_STATUS_NO_MEMORY
;
177 table
->local
.db_ctx
= db_open_rbt(table
);
178 if (table
->local
.db_ctx
== NULL
) {
180 return NT_STATUS_NO_MEMORY
;
182 table
->local
.lowest_id
= lowest_id
;
183 table
->local
.highest_id
= highest_id
;
184 table
->local
.max_opens
= max_opens
;
186 status
= smbXsrv_open_global_init();
187 if (!NT_STATUS_IS_OK(status
)) {
192 table
->global
.db_ctx
= smbXsrv_open_global_db_ctx
;
194 conn
->open_table
= table
;
198 struct smbXsrv_open_local_allocate_state
{
199 const uint32_t lowest_id
;
200 const uint32_t highest_id
;
206 static int smbXsrv_open_local_allocate_traverse(struct db_record
*rec
,
209 struct smbXsrv_open_local_allocate_state
*state
=
210 (struct smbXsrv_open_local_allocate_state
*)private_data
;
211 TDB_DATA key
= dbwrap_record_get_key(rec
);
215 status
= smbXsrv_open_local_key_to_id(key
, &id
);
216 if (!NT_STATUS_IS_OK(status
)) {
217 state
->status
= status
;
221 if (id
<= state
->last_id
) {
222 state
->status
= NT_STATUS_INTERNAL_DB_CORRUPTION
;
227 if (id
> state
->useable_id
) {
228 state
->status
= NT_STATUS_OK
;
232 if (state
->useable_id
== state
->highest_id
) {
233 state
->status
= NT_STATUS_INSUFFICIENT_RESOURCES
;
237 state
->useable_id
+=1;
241 static NTSTATUS
smbXsrv_open_local_allocate_id(struct db_context
*db
,
245 struct db_record
**_rec
,
248 struct smbXsrv_open_local_allocate_state state
= {
249 .lowest_id
= lowest_id
,
250 .highest_id
= highest_id
,
252 .useable_id
= lowest_id
,
253 .status
= NT_STATUS_INTERNAL_ERROR
,
263 if (lowest_id
> highest_id
) {
264 return NT_STATUS_INSUFFICIENT_RESOURCES
;
268 * first we try randomly
270 range
= (highest_id
- lowest_id
) + 1;
272 for (i
= 0; i
< (range
/ 2); i
++) {
274 uint8_t key_buf
[SMBXSRV_OPEN_LOCAL_TDB_KEY_SIZE
];
277 struct db_record
*rec
= NULL
;
279 id
= generate_random() % range
;
282 if (id
< lowest_id
) {
285 if (id
> highest_id
) {
289 key
= smbXsrv_open_local_id_to_key(id
, key_buf
);
291 rec
= dbwrap_fetch_locked(db
, mem_ctx
, key
);
293 return NT_STATUS_INSUFFICIENT_RESOURCES
;
296 val
= dbwrap_record_get_value(rec
);
297 if (val
.dsize
!= 0) {
308 * if the range is almost full,
309 * we traverse the whole table
310 * (this relies on sorted behavior of dbwrap_rbt)
312 status
= dbwrap_traverse_read(db
, smbXsrv_open_local_allocate_traverse
,
314 if (NT_STATUS_IS_OK(status
)) {
315 if (NT_STATUS_IS_OK(state
.status
)) {
316 return NT_STATUS_INTERNAL_ERROR
;
319 if (!NT_STATUS_EQUAL(state
.status
, NT_STATUS_INTERNAL_ERROR
)) {
323 if (state
.useable_id
<= state
.highest_id
) {
324 state
.status
= NT_STATUS_OK
;
326 return NT_STATUS_INSUFFICIENT_RESOURCES
;
328 } else if (!NT_STATUS_EQUAL(status
, NT_STATUS_INTERNAL_DB_CORRUPTION
)) {
330 * Here we really expect NT_STATUS_INTERNAL_DB_CORRUPTION!
332 * If we get anything else it is an error, because it
333 * means we did not manage to find a free slot in
336 return NT_STATUS_INSUFFICIENT_RESOURCES
;
339 if (NT_STATUS_IS_OK(state
.status
)) {
341 uint8_t key_buf
[SMBXSRV_OPEN_LOCAL_TDB_KEY_SIZE
];
344 struct db_record
*rec
= NULL
;
346 id
= state
.useable_id
;
348 key
= smbXsrv_open_local_id_to_key(id
, key_buf
);
350 rec
= dbwrap_fetch_locked(db
, mem_ctx
, key
);
352 return NT_STATUS_INSUFFICIENT_RESOURCES
;
355 val
= dbwrap_record_get_value(rec
);
356 if (val
.dsize
!= 0) {
358 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
369 struct smbXsrv_open_local_fetch_state
{
370 struct smbXsrv_open
*op
;
374 static void smbXsrv_open_local_fetch_parser(TDB_DATA key
, TDB_DATA data
,
377 struct smbXsrv_open_local_fetch_state
*state
=
378 (struct smbXsrv_open_local_fetch_state
*)private_data
;
381 if (data
.dsize
!= sizeof(ptr
)) {
382 state
->status
= NT_STATUS_INTERNAL_DB_ERROR
;
386 memcpy(&ptr
, data
.dptr
, data
.dsize
);
387 state
->op
= talloc_get_type_abort(ptr
, struct smbXsrv_open
);
388 state
->status
= NT_STATUS_OK
;
391 static NTSTATUS
smbXsrv_open_local_lookup(struct smbXsrv_open_table
*table
,
392 uint32_t open_local_id
,
393 uint32_t open_global_id
,
395 struct smbXsrv_open
**_open
)
397 struct smbXsrv_open_local_fetch_state state
= {
399 .status
= NT_STATUS_INTERNAL_ERROR
,
401 uint8_t key_buf
[SMBXSRV_OPEN_LOCAL_TDB_KEY_SIZE
];
407 if (open_local_id
== 0) {
408 return NT_STATUS_FILE_CLOSED
;
412 /* this might happen before the end of negprot */
413 return NT_STATUS_FILE_CLOSED
;
416 if (table
->local
.db_ctx
== NULL
) {
417 return NT_STATUS_INTERNAL_ERROR
;
420 key
= smbXsrv_open_local_id_to_key(open_local_id
, key_buf
);
422 status
= dbwrap_parse_record(table
->local
.db_ctx
, key
,
423 smbXsrv_open_local_fetch_parser
,
425 if (NT_STATUS_EQUAL(status
, NT_STATUS_NOT_FOUND
)) {
426 return NT_STATUS_FILE_CLOSED
;
427 } else if (!NT_STATUS_IS_OK(status
)) {
430 if (!NT_STATUS_IS_OK(state
.status
)) {
434 if (NT_STATUS_EQUAL(state
.op
->status
, NT_STATUS_FILE_CLOSED
)) {
435 return NT_STATUS_FILE_CLOSED
;
438 if (open_global_id
== 0) {
439 /* make the global check a no-op for SMB1 */
440 open_global_id
= state
.op
->global
->open_global_id
;
443 if (state
.op
->global
->open_global_id
!= open_global_id
) {
444 return NT_STATUS_FILE_CLOSED
;
447 state
.op
->idle_time
= now
;
450 return state
.op
->status
;
453 static int smbXsrv_open_global_destructor(struct smbXsrv_open_global0
*global
)
458 static void smbXsrv_open_global_verify_record(struct db_record
*db_rec
,
462 struct smbXsrv_open_global0
**_g
);
464 static NTSTATUS
smbXsrv_open_global_allocate(struct db_context
*db
,
466 struct smbXsrv_open_global0
**_global
)
469 struct smbXsrv_open_global0
*global
= NULL
;
470 uint32_t last_free
= 0;
471 const uint32_t min_tries
= 3;
475 global
= talloc_zero(mem_ctx
, struct smbXsrv_open_global0
);
476 if (global
== NULL
) {
477 return NT_STATUS_NO_MEMORY
;
479 talloc_set_destructor(global
, smbXsrv_open_global_destructor
);
482 * Here we just randomly try the whole 32-bit space
484 * We use just 32-bit, because we want to reuse the
487 for (i
= 0; i
< UINT32_MAX
; i
++) {
488 bool is_free
= false;
489 bool was_free
= false;
491 uint8_t key_buf
[SMBXSRV_OPEN_GLOBAL_TDB_KEY_SIZE
];
494 if (i
>= min_tries
&& last_free
!= 0) {
497 id
= generate_random();
502 if (id
== UINT32_MAX
) {
506 key
= smbXsrv_open_global_id_to_key(id
, key_buf
);
508 global
->db_rec
= dbwrap_fetch_locked(db
, mem_ctx
, key
);
509 if (global
->db_rec
== NULL
) {
511 return NT_STATUS_INSUFFICIENT_RESOURCES
;
514 smbXsrv_open_global_verify_record(global
->db_rec
,
520 TALLOC_FREE(global
->db_rec
);
524 if (!was_free
&& i
< min_tries
) {
526 * The session_id is free now,
527 * but was not free before.
529 * This happens if a smbd crashed
530 * and did not cleanup the record.
532 * If this is one of our first tries,
533 * then we try to find a real free one.
535 if (last_free
== 0) {
538 TALLOC_FREE(global
->db_rec
);
542 global
->open_global_id
= id
;
548 /* should not be reached */
550 return NT_STATUS_INTERNAL_ERROR
;
553 static void smbXsrv_open_global_verify_record(struct db_record
*db_rec
,
557 struct smbXsrv_open_global0
**_g
)
562 struct smbXsrv_open_globalB global_blob
;
563 enum ndr_err_code ndr_err
;
564 struct smbXsrv_open_global0
*global
= NULL
;
566 TALLOC_CTX
*frame
= talloc_stackframe();
577 key
= dbwrap_record_get_key(db_rec
);
579 val
= dbwrap_record_get_value(db_rec
);
580 if (val
.dsize
== 0) {
589 blob
= data_blob_const(val
.dptr
, val
.dsize
);
591 ndr_err
= ndr_pull_struct_blob(&blob
, frame
, &global_blob
,
592 (ndr_pull_flags_fn_t
)ndr_pull_smbXsrv_open_globalB
);
593 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
594 NTSTATUS status
= ndr_map_error2ntstatus(ndr_err
);
595 DEBUG(1,("smbXsrv_open_global_verify_record: "
596 "key '%s' ndr_pull_struct_blob - %s\n",
597 hex_encode_talloc(frame
, key
.dptr
, key
.dsize
),
603 DEBUG(10,("smbXsrv_open_global_verify_record\n"));
604 if (CHECK_DEBUGLVL(10)) {
605 NDR_PRINT_DEBUG(smbXsrv_open_globalB
, &global_blob
);
608 if (global_blob
.version
!= SMBXSRV_VERSION_0
) {
609 DEBUG(0,("smbXsrv_open_global_verify_record: "
610 "key '%s' use unsupported version %u\n",
611 hex_encode_talloc(frame
, key
.dptr
, key
.dsize
),
612 global_blob
.version
));
613 NDR_PRINT_DEBUG(smbXsrv_open_globalB
, &global_blob
);
618 global
= global_blob
.info
.info0
;
620 if (server_id_is_disconnected(&global
->server_id
)) {
623 exists
= serverid_exists(&global
->server_id
);
626 DEBUG(2,("smbXsrv_open_global_verify_record: "
627 "key '%s' server_id %s does not exist.\n",
628 hex_encode_talloc(frame
, key
.dptr
, key
.dsize
),
629 server_id_str(frame
, &global
->server_id
)));
630 if (CHECK_DEBUGLVL(2)) {
631 NDR_PRINT_DEBUG(smbXsrv_open_globalB
, &global_blob
);
634 dbwrap_record_delete(db_rec
);
640 *_g
= talloc_move(mem_ctx
, &global
);
645 static NTSTATUS
smbXsrv_open_global_store(struct smbXsrv_open_global0
*global
)
647 struct smbXsrv_open_globalB global_blob
;
648 DATA_BLOB blob
= data_blob_null
;
652 enum ndr_err_code ndr_err
;
655 * TODO: if we use other versions than '0'
656 * we would add glue code here, that would be able to
657 * store the information in the old format.
660 if (global
->db_rec
== NULL
) {
661 return NT_STATUS_INTERNAL_ERROR
;
664 key
= dbwrap_record_get_key(global
->db_rec
);
665 val
= dbwrap_record_get_value(global
->db_rec
);
667 ZERO_STRUCT(global_blob
);
668 global_blob
.version
= smbXsrv_version_global_current();
669 if (val
.dsize
>= 8) {
670 global_blob
.seqnum
= IVAL(val
.dptr
, 4);
672 global_blob
.seqnum
+= 1;
673 global_blob
.info
.info0
= global
;
675 ndr_err
= ndr_push_struct_blob(&blob
, global
->db_rec
, &global_blob
,
676 (ndr_push_flags_fn_t
)ndr_push_smbXsrv_open_globalB
);
677 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
678 status
= ndr_map_error2ntstatus(ndr_err
);
679 DEBUG(1,("smbXsrv_open_global_store: key '%s' ndr_push - %s\n",
680 hex_encode_talloc(global
->db_rec
, key
.dptr
, key
.dsize
),
682 TALLOC_FREE(global
->db_rec
);
686 val
= make_tdb_data(blob
.data
, blob
.length
);
687 status
= dbwrap_record_store(global
->db_rec
, val
, TDB_REPLACE
);
688 if (!NT_STATUS_IS_OK(status
)) {
689 DEBUG(1,("smbXsrv_open_global_store: key '%s' store - %s\n",
690 hex_encode_talloc(global
->db_rec
, key
.dptr
, key
.dsize
),
692 TALLOC_FREE(global
->db_rec
);
696 if (CHECK_DEBUGLVL(10)) {
697 DEBUG(10,("smbXsrv_open_global_store: key '%s' stored\n",
698 hex_encode_talloc(global
->db_rec
, key
.dptr
, key
.dsize
)));
699 NDR_PRINT_DEBUG(smbXsrv_open_globalB
, &global_blob
);
702 TALLOC_FREE(global
->db_rec
);
707 static NTSTATUS
smbXsrv_open_global_lookup(struct smbXsrv_open_table
*table
,
708 uint32_t open_global_id
,
710 struct smbXsrv_open_global0
**_global
)
713 uint8_t key_buf
[SMBXSRV_OPEN_GLOBAL_TDB_KEY_SIZE
];
714 struct db_record
*global_rec
= NULL
;
715 bool is_free
= false;
719 if (table
->global
.db_ctx
== NULL
) {
720 return NT_STATUS_INTERNAL_ERROR
;
723 key
= smbXsrv_open_global_id_to_key(open_global_id
, key_buf
);
725 global_rec
= dbwrap_fetch_locked(table
->global
.db_ctx
, mem_ctx
, key
);
726 if (global_rec
== NULL
) {
727 DEBUG(0, ("smbXsrv_open_global_lookup(0x%08x): "
728 "Failed to lock global key '%s'\n",
730 hex_encode_talloc(talloc_tos(), key
.dptr
,
732 return NT_STATUS_INTERNAL_DB_ERROR
;
735 smbXsrv_open_global_verify_record(global_rec
,
741 talloc_free(global_rec
);
742 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
745 (*_global
)->db_rec
= talloc_move(*_global
, &global_rec
);
747 talloc_set_destructor(*_global
, smbXsrv_open_global_destructor
);
752 static int smbXsrv_open_destructor(struct smbXsrv_open
*op
)
756 status
= smbXsrv_open_close(op
, 0);
757 if (!NT_STATUS_IS_OK(status
)) {
758 DEBUG(0, ("smbXsrv_open_destructor: "
759 "smbXsrv_open_close() failed - %s\n",
763 TALLOC_FREE(op
->global
);
768 NTSTATUS
smbXsrv_open_create(struct smbXsrv_connection
*conn
,
769 struct auth_session_info
*session_info
,
771 struct smbXsrv_open
**_open
)
773 struct smbXsrv_open_table
*table
= conn
->open_table
;
774 struct db_record
*local_rec
= NULL
;
775 struct smbXsrv_open
*op
= NULL
;
778 struct smbXsrv_open_global0
*global
= NULL
;
780 struct dom_sid
*current_sid
= NULL
;
781 struct security_token
*current_token
= NULL
;
783 if (session_info
== NULL
) {
784 return NT_STATUS_INVALID_HANDLE
;
786 current_token
= session_info
->security_token
;
788 if (current_token
== NULL
) {
789 return NT_STATUS_INVALID_HANDLE
;
792 if (current_token
->num_sids
> PRIMARY_USER_SID_INDEX
) {
793 current_sid
= ¤t_token
->sids
[PRIMARY_USER_SID_INDEX
];
796 if (current_sid
== NULL
) {
797 return NT_STATUS_INVALID_HANDLE
;
800 if (table
->local
.num_opens
>= table
->local
.max_opens
) {
801 return NT_STATUS_INSUFFICIENT_RESOURCES
;
804 op
= talloc_zero(table
, struct smbXsrv_open
);
806 return NT_STATUS_NO_MEMORY
;
809 op
->status
= NT_STATUS_OK
; /* TODO: start with INTERNAL_ERROR */
812 status
= smbXsrv_open_global_allocate(table
->global
.db_ctx
,
814 if (!NT_STATUS_IS_OK(status
)) {
820 status
= smbXsrv_open_local_allocate_id(table
->local
.db_ctx
,
821 table
->local
.lowest_id
,
822 table
->local
.highest_id
,
826 if (!NT_STATUS_IS_OK(status
)) {
831 global
->open_persistent_id
= global
->open_global_id
;
832 global
->open_volatile_id
= op
->local_id
;
834 global
->server_id
= messaging_server_id(conn
->msg_ctx
);
835 global
->open_time
= now
;
836 global
->open_owner
= *current_sid
;
837 if (conn
->protocol
>= PROTOCOL_SMB2_10
) {
838 global
->client_guid
= conn
->smb2
.client
.guid
;
842 val
= make_tdb_data((uint8_t const *)&ptr
, sizeof(ptr
));
843 status
= dbwrap_record_store(local_rec
, val
, TDB_REPLACE
);
844 TALLOC_FREE(local_rec
);
845 if (!NT_STATUS_IS_OK(status
)) {
849 table
->local
.num_opens
+= 1;
851 talloc_set_destructor(op
, smbXsrv_open_destructor
);
853 status
= smbXsrv_open_global_store(global
);
854 if (!NT_STATUS_IS_OK(status
)) {
855 DEBUG(0,("smbXsrv_open_create: "
856 "global_id (0x%08x) store failed - %s\n",
857 op
->global
->open_global_id
,
863 if (CHECK_DEBUGLVL(10)) {
864 struct smbXsrv_openB open_blob
;
866 ZERO_STRUCT(open_blob
);
867 open_blob
.version
= SMBXSRV_VERSION_0
;
868 open_blob
.info
.info0
= op
;
870 DEBUG(10,("smbXsrv_open_create: global_id (0x%08x) stored\n",
871 op
->global
->open_global_id
));
872 NDR_PRINT_DEBUG(smbXsrv_openB
, &open_blob
);
879 uint32_t smbXsrv_open_hash(struct smbXsrv_open
*_open
)
884 SBVAL(buf
, 0, _open
->global
->open_persistent_id
);
885 SBVAL(buf
, 8, _open
->global
->open_volatile_id
);
886 SBVAL(buf
, 16, _open
->global
->open_time
);
888 ret
= hash(buf
, sizeof(buf
), 0);
897 NTSTATUS
smbXsrv_open_update(struct smbXsrv_open
*op
)
899 struct smbXsrv_open_table
*table
= op
->table
;
901 uint8_t key_buf
[SMBXSRV_OPEN_GLOBAL_TDB_KEY_SIZE
];
904 if (op
->global
->db_rec
!= NULL
) {
905 DEBUG(0, ("smbXsrv_open_update(0x%08x): "
906 "Called with db_rec != NULL'\n",
907 op
->global
->open_global_id
));
908 return NT_STATUS_INTERNAL_ERROR
;
911 key
= smbXsrv_open_global_id_to_key(op
->global
->open_global_id
,
914 op
->global
->db_rec
= dbwrap_fetch_locked(table
->global
.db_ctx
,
916 if (op
->global
->db_rec
== NULL
) {
917 DEBUG(0, ("smbXsrv_open_update(0x%08x): "
918 "Failed to lock global key '%s'\n",
919 op
->global
->open_global_id
,
920 hex_encode_talloc(talloc_tos(), key
.dptr
,
922 return NT_STATUS_INTERNAL_DB_ERROR
;
925 status
= smbXsrv_open_global_store(op
->global
);
926 if (!NT_STATUS_IS_OK(status
)) {
927 DEBUG(0,("smbXsrv_open_update: "
928 "global_id (0x%08x) store failed - %s\n",
929 op
->global
->open_global_id
,
934 if (CHECK_DEBUGLVL(10)) {
935 struct smbXsrv_openB open_blob
;
937 ZERO_STRUCT(open_blob
);
938 open_blob
.version
= SMBXSRV_VERSION_0
;
939 open_blob
.info
.info0
= op
;
941 DEBUG(10,("smbXsrv_open_update: global_id (0x%08x) stored\n",
942 op
->global
->open_global_id
));
943 NDR_PRINT_DEBUG(smbXsrv_openB
, &open_blob
);
949 NTSTATUS
smbXsrv_open_close(struct smbXsrv_open
*op
, NTTIME now
)
951 struct smbXsrv_open_table
*table
;
952 struct db_record
*local_rec
= NULL
;
953 struct db_record
*global_rec
= NULL
;
955 NTSTATUS error
= NT_STATUS_OK
;
957 if (op
->table
== NULL
) {
964 op
->status
= NT_STATUS_FILE_CLOSED
;
965 op
->global
->disconnect_time
= now
;
966 server_id_set_disconnected(&op
->global
->server_id
);
968 global_rec
= op
->global
->db_rec
;
969 op
->global
->db_rec
= NULL
;
970 if (global_rec
== NULL
) {
971 uint8_t key_buf
[SMBXSRV_OPEN_GLOBAL_TDB_KEY_SIZE
];
974 key
= smbXsrv_open_global_id_to_key(
975 op
->global
->open_global_id
,
978 global_rec
= dbwrap_fetch_locked(table
->global
.db_ctx
,
980 if (global_rec
== NULL
) {
981 DEBUG(0, ("smbXsrv_open_close(0x%08x): "
982 "Failed to lock global key '%s'\n",
983 op
->global
->open_global_id
,
984 hex_encode_talloc(global_rec
, key
.dptr
,
986 error
= NT_STATUS_INTERNAL_ERROR
;
990 if (global_rec
!= NULL
&& op
->global
->durable
) {
992 * If it is a durable open we need to update the global part
993 * instead of deleting it
995 op
->global
->db_rec
= global_rec
;
996 status
= smbXsrv_open_global_store(op
->global
);
997 if (NT_STATUS_IS_OK(status
)) {
999 * smbXsrv_open_global_store does the free
1000 * of op->global->db_rec
1004 if (!NT_STATUS_IS_OK(status
)) {
1005 DEBUG(0,("smbXsrv_open_close(0x%08x)"
1006 "smbXsrv_open_global_store() failed - %s\n",
1007 op
->global
->open_global_id
,
1008 nt_errstr(status
)));
1012 if (NT_STATUS_IS_OK(status
) && CHECK_DEBUGLVL(10)) {
1013 struct smbXsrv_openB open_blob
;
1015 ZERO_STRUCT(open_blob
);
1016 open_blob
.version
= SMBXSRV_VERSION_0
;
1017 open_blob
.info
.info0
= op
;
1019 DEBUG(10,("smbXsrv_open_close(0x%08x): "
1020 "stored disconnect\n",
1021 op
->global
->open_global_id
));
1022 NDR_PRINT_DEBUG(smbXsrv_openB
, &open_blob
);
1026 if (global_rec
!= NULL
) {
1027 status
= dbwrap_record_delete(global_rec
);
1028 if (!NT_STATUS_IS_OK(status
)) {
1029 TDB_DATA key
= dbwrap_record_get_key(global_rec
);
1031 DEBUG(0, ("smbXsrv_open_close(0x%08x): "
1032 "failed to delete global key '%s': %s\n",
1033 op
->global
->open_global_id
,
1034 hex_encode_talloc(global_rec
, key
.dptr
,
1036 nt_errstr(status
)));
1040 TALLOC_FREE(global_rec
);
1042 local_rec
= op
->db_rec
;
1043 if (local_rec
== NULL
) {
1044 uint8_t key_buf
[SMBXSRV_OPEN_LOCAL_TDB_KEY_SIZE
];
1047 key
= smbXsrv_open_local_id_to_key(op
->local_id
, key_buf
);
1049 local_rec
= dbwrap_fetch_locked(table
->local
.db_ctx
,
1051 if (local_rec
== NULL
) {
1052 DEBUG(0, ("smbXsrv_open_close(0x%08x): "
1053 "Failed to lock local key '%s'\n",
1054 op
->global
->open_global_id
,
1055 hex_encode_talloc(local_rec
, key
.dptr
,
1057 error
= NT_STATUS_INTERNAL_ERROR
;
1061 if (local_rec
!= NULL
) {
1062 status
= dbwrap_record_delete(local_rec
);
1063 if (!NT_STATUS_IS_OK(status
)) {
1064 TDB_DATA key
= dbwrap_record_get_key(local_rec
);
1066 DEBUG(0, ("smbXsrv_open_close(0x%08x): "
1067 "failed to delete local key '%s': %s\n",
1068 op
->global
->open_global_id
,
1069 hex_encode_talloc(local_rec
, key
.dptr
,
1071 nt_errstr(status
)));
1074 table
->local
.num_opens
-= 1;
1076 if (op
->db_rec
== NULL
) {
1077 TALLOC_FREE(local_rec
);
1082 op
->compat
->op
= NULL
;
1083 file_free(NULL
, op
->compat
);
1090 NTSTATUS
smb1srv_open_table_init(struct smbXsrv_connection
*conn
)
1095 * Allow a range from 1..65534.
1097 * With real_max_open_files possible ids,
1098 * truncated to the SMB1 limit of 16-bit.
1100 * 0 and 0xFFFF are no valid ids.
1102 max_opens
= conn
->sconn
->real_max_open_files
;
1103 max_opens
= MIN(max_opens
, UINT16_MAX
- 1);
1105 return smbXsrv_open_table_init(conn
, 1, UINT16_MAX
- 1, max_opens
);
1108 NTSTATUS
smb1srv_open_lookup(struct smbXsrv_connection
*conn
,
1109 uint16_t fnum
, NTTIME now
,
1110 struct smbXsrv_open
**_open
)
1112 struct smbXsrv_open_table
*table
= conn
->open_table
;
1113 uint32_t local_id
= fnum
;
1114 uint32_t global_id
= 0;
1116 return smbXsrv_open_local_lookup(table
, local_id
, global_id
, now
, _open
);
1119 NTSTATUS
smb2srv_open_table_init(struct smbXsrv_connection
*conn
)
1124 * Allow a range from 1..4294967294.
1126 * With real_max_open_files possible ids,
1127 * truncated to 16-bit (the same as SMB1 for now).
1129 * 0 and 0xFFFFFFFF are no valid ids.
1131 * The usage of conn->sconn->real_max_open_files
1132 * is the reason that we use one open table per
1133 * transport connection (as we still have a 1:1 mapping
1134 * between process and transport connection).
1136 max_opens
= conn
->sconn
->real_max_open_files
;
1137 max_opens
= MIN(max_opens
, UINT16_MAX
- 1);
1139 return smbXsrv_open_table_init(conn
, 1, UINT32_MAX
- 1, max_opens
);
1142 NTSTATUS
smb2srv_open_lookup(struct smbXsrv_connection
*conn
,
1143 uint64_t persistent_id
,
1144 uint64_t volatile_id
,
1146 struct smbXsrv_open
**_open
)
1148 struct smbXsrv_open_table
*table
= conn
->open_table
;
1149 uint32_t local_id
= volatile_id
& UINT32_MAX
;
1150 uint64_t local_zeros
= volatile_id
& 0xFFFFFFFF00000000LLU
;
1151 uint32_t global_id
= persistent_id
& UINT32_MAX
;
1152 uint64_t global_zeros
= persistent_id
& 0xFFFFFFFF00000000LLU
;
1154 if (local_zeros
!= 0) {
1155 return NT_STATUS_FILE_CLOSED
;
1158 if (global_zeros
!= 0) {
1159 return NT_STATUS_FILE_CLOSED
;
1162 if (global_id
== 0) {
1163 return NT_STATUS_FILE_CLOSED
;
1166 return smbXsrv_open_local_lookup(table
, local_id
, global_id
, now
, _open
);
1169 NTSTATUS
smb2srv_open_recreate(struct smbXsrv_connection
*conn
,
1170 struct auth_session_info
*session_info
,
1171 uint64_t persistent_id
,
1172 const struct GUID
*create_guid
,
1174 struct smbXsrv_open
**_open
)
1176 struct smbXsrv_open_table
*table
= conn
->open_table
;
1177 struct db_record
*local_rec
= NULL
;
1178 struct smbXsrv_open
*op
= NULL
;
1181 uint32_t global_id
= persistent_id
& UINT32_MAX
;
1182 uint64_t global_zeros
= persistent_id
& 0xFFFFFFFF00000000LLU
;
1184 struct security_token
*current_token
= NULL
;
1186 if (session_info
== NULL
) {
1187 return NT_STATUS_INVALID_HANDLE
;
1189 current_token
= session_info
->security_token
;
1191 if (current_token
== NULL
) {
1192 return NT_STATUS_INVALID_HANDLE
;
1195 if (global_zeros
!= 0) {
1196 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
1199 op
= talloc_zero(table
, struct smbXsrv_open
);
1201 return NT_STATUS_NO_MEMORY
;
1205 status
= smbXsrv_open_global_lookup(table
, global_id
, op
, &op
->global
);
1206 if (!NT_STATUS_IS_OK(status
)) {
1212 * If the provided create_guid is NULL, this means that
1213 * the reconnect request was a v1 request. In that case
1214 * we should skipt the create GUID verification, since
1215 * it is valid to v1-reconnect a v2-opened handle.
1217 if ((create_guid
!= NULL
) &&
1218 !GUID_equal(&op
->global
->create_guid
, create_guid
))
1221 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
1224 if (!security_token_is_sid(current_token
, &op
->global
->open_owner
)) {
1226 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
1229 if (!op
->global
->durable
) {
1231 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
1234 if (table
->local
.num_opens
>= table
->local
.max_opens
) {
1236 return NT_STATUS_INSUFFICIENT_RESOURCES
;
1239 status
= smbXsrv_open_local_allocate_id(table
->local
.db_ctx
,
1240 table
->local
.lowest_id
,
1241 table
->local
.highest_id
,
1245 if (!NT_STATUS_IS_OK(status
)) {
1250 op
->idle_time
= now
;
1251 op
->status
= NT_STATUS_FILE_CLOSED
;
1253 op
->global
->open_volatile_id
= op
->local_id
;
1254 op
->global
->server_id
= messaging_server_id(conn
->msg_ctx
);
1257 val
= make_tdb_data((uint8_t const *)&ptr
, sizeof(ptr
));
1258 status
= dbwrap_record_store(local_rec
, val
, TDB_REPLACE
);
1259 TALLOC_FREE(local_rec
);
1260 if (!NT_STATUS_IS_OK(status
)) {
1264 table
->local
.num_opens
+= 1;
1266 talloc_set_destructor(op
, smbXsrv_open_destructor
);
1268 status
= smbXsrv_open_global_store(op
->global
);
1269 if (!NT_STATUS_IS_OK(status
)) {
1274 if (CHECK_DEBUGLVL(10)) {
1275 struct smbXsrv_openB open_blob
;
1277 ZERO_STRUCT(open_blob
);
1278 open_blob
.version
= 0;
1279 open_blob
.info
.info0
= op
;
1281 DEBUG(10,("smbXsrv_open_recreate: global_id (0x%08x) stored\n",
1282 op
->global
->open_global_id
));
1283 NDR_PRINT_DEBUG(smbXsrv_openB
, &open_blob
);
1287 return NT_STATUS_OK
;
1291 static NTSTATUS
smbXsrv_open_global_parse_record(TALLOC_CTX
*mem_ctx
,
1292 struct db_record
*rec
,
1293 struct smbXsrv_open_global0
**global
)
1295 TDB_DATA key
= dbwrap_record_get_key(rec
);
1296 TDB_DATA val
= dbwrap_record_get_value(rec
);
1297 DATA_BLOB blob
= data_blob_const(val
.dptr
, val
.dsize
);
1298 struct smbXsrv_open_globalB global_blob
;
1299 enum ndr_err_code ndr_err
;
1301 TALLOC_CTX
*frame
= talloc_stackframe();
1303 ndr_err
= ndr_pull_struct_blob(&blob
, frame
, &global_blob
,
1304 (ndr_pull_flags_fn_t
)ndr_pull_smbXsrv_open_globalB
);
1305 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1306 DEBUG(1,("Invalid record in smbXsrv_open_global.tdb:"
1307 "key '%s' ndr_pull_struct_blob - %s\n",
1308 hex_encode_talloc(frame
, key
.dptr
, key
.dsize
),
1309 ndr_errstr(ndr_err
)));
1310 status
= ndr_map_error2ntstatus(ndr_err
);
1314 if (global_blob
.version
!= SMBXSRV_VERSION_0
) {
1315 status
= NT_STATUS_INTERNAL_DB_CORRUPTION
;
1316 DEBUG(1,("Invalid record in smbXsrv_open_global.tdb:"
1317 "key '%s' unsuported version - %d - %s\n",
1318 hex_encode_talloc(frame
, key
.dptr
, key
.dsize
),
1319 (int)global_blob
.version
,
1320 nt_errstr(status
)));
1324 *global
= talloc_move(mem_ctx
, &global_blob
.info
.info0
);
1325 status
= NT_STATUS_OK
;
1331 struct smbXsrv_open_global_traverse_state
{
1332 int (*fn
)(struct smbXsrv_open_global0
*, void *);
1336 static int smbXsrv_open_global_traverse_fn(struct db_record
*rec
, void *data
)
1338 struct smbXsrv_open_global_traverse_state
*state
=
1339 (struct smbXsrv_open_global_traverse_state
*)data
;
1340 struct smbXsrv_open_global0
*global
= NULL
;
1344 status
= smbXsrv_open_global_parse_record(talloc_tos(), rec
, &global
);
1345 if (!NT_STATUS_IS_OK(status
)) {
1349 global
->db_rec
= rec
;
1350 ret
= state
->fn(global
, state
->private_data
);
1351 talloc_free(global
);
1355 NTSTATUS
smbXsrv_open_global_traverse(
1356 int (*fn
)(struct smbXsrv_open_global0
*, void *),
1362 struct smbXsrv_open_global_traverse_state state
= {
1364 .private_data
= private_data
,
1368 status
= smbXsrv_open_global_init();
1369 if (!NT_STATUS_IS_OK(status
)) {
1371 DEBUG(0, ("Failed to initialize open_global: %s\n",
1372 nt_errstr(status
)));
1376 status
= dbwrap_traverse_read(smbXsrv_open_global_db_ctx
,
1377 smbXsrv_open_global_traverse_fn
,
1385 NTSTATUS
smbXsrv_open_cleanup(uint64_t persistent_id
)
1387 NTSTATUS status
= NT_STATUS_OK
;
1388 TALLOC_CTX
*frame
= talloc_stackframe();
1389 struct smbXsrv_open_global0
*op
= NULL
;
1390 uint8_t key_buf
[SMBXSRV_OPEN_GLOBAL_TDB_KEY_SIZE
];
1393 struct db_record
*rec
;
1394 bool delete_open
= false;
1395 uint32_t global_id
= persistent_id
& UINT32_MAX
;
1397 key
= smbXsrv_open_global_id_to_key(global_id
, key_buf
);
1398 rec
= dbwrap_fetch_locked(smbXsrv_open_global_db_ctx
, frame
, key
);
1400 status
= NT_STATUS_NOT_FOUND
;
1401 DEBUG(1, ("smbXsrv_open_cleanup[global: 0x%08x] "
1402 "failed to fetch record from %s - %s\n",
1403 global_id
, dbwrap_name(smbXsrv_open_global_db_ctx
),
1404 nt_errstr(status
)));
1408 val
= dbwrap_record_get_value(rec
);
1409 if (val
.dsize
== 0) {
1410 DEBUG(10, ("smbXsrv_open_cleanup[global: 0x%08x] "
1411 "empty record in %s, skipping...\n",
1412 global_id
, dbwrap_name(smbXsrv_open_global_db_ctx
)));
1416 status
= smbXsrv_open_global_parse_record(talloc_tos(), rec
, &op
);
1417 if (!NT_STATUS_IS_OK(status
)) {
1418 DEBUG(1, ("smbXsrv_open_cleanup[global: 0x%08x] "
1419 "failed to read record: %s\n",
1420 global_id
, nt_errstr(status
)));
1424 if (server_id_is_disconnected(&op
->server_id
)) {
1425 struct timeval now
, disconnect_time
;
1427 now
= timeval_current();
1428 nttime_to_timeval(&disconnect_time
, op
->disconnect_time
);
1429 tdiff
= usec_time_diff(&now
, &disconnect_time
);
1430 delete_open
= (tdiff
>= 1000*op
->durable_timeout_msec
);
1432 DEBUG(10, ("smbXsrv_open_cleanup[global: 0x%08x] "
1433 "disconnected at [%s] %us ago with "
1434 "timeout of %us -%s reached\n",
1436 nt_time_string(frame
, op
->disconnect_time
),
1437 (unsigned)(tdiff
/1000000),
1438 op
->durable_timeout_msec
/ 1000,
1439 delete_open
? "" : " not"));
1440 } else if (!serverid_exists(&op
->server_id
)) {
1441 DEBUG(10, ("smbXsrv_open_cleanup[global: 0x%08x] "
1442 "server[%s] does not exist\n",
1443 global_id
, server_id_str(frame
, &op
->server_id
)));
1451 status
= dbwrap_record_delete(rec
);
1452 if (!NT_STATUS_IS_OK(status
)) {
1453 DEBUG(1, ("smbXsrv_open_cleanup[global: 0x%08x] "
1454 "failed to delete record"
1455 "from %s: %s\n", global_id
,
1456 dbwrap_name(smbXsrv_open_global_db_ctx
),
1457 nt_errstr(status
)));
1461 DEBUG(10, ("smbXsrv_open_cleanup[global: 0x%08x] "
1462 "delete record from %s\n",
1464 dbwrap_name(smbXsrv_open_global_db_ctx
)));