smbd: Simplify smbXsrv_open_global_verify_record()
[Samba.git] / source3 / smbd / smbXsrv_open.c
blob1e1423fa0cb67f67e541f924d92a269d9db99627
1 /*
2 Unix SMB/CIFS implementation.
4 Copyright (C) Stefan Metzmacher 2012
5 Copyright (C) Michael Adam 2012
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "smbXsrv_open.h"
22 #include "includes.h"
23 #include "system/filesys.h"
24 #include "lib/util/server_id.h"
25 #include "smbd/smbd.h"
26 #include "smbd/globals.h"
27 #include "dbwrap/dbwrap.h"
28 #include "dbwrap/dbwrap_rbt.h"
29 #include "dbwrap/dbwrap_open.h"
30 #include "../libcli/security/security.h"
31 #include "messages.h"
32 #include "lib/util/util_tdb.h"
33 #include "librpc/gen_ndr/ndr_smbXsrv.h"
34 #include "serverid.h"
35 #include "source3/include/util_tdb.h"
36 #include "lib/util/idtree_random.h"
38 struct smbXsrv_open_table {
39 struct {
40 struct idr_context *idr;
41 struct db_context *replay_cache_db_ctx;
42 uint32_t lowest_id;
43 uint32_t highest_id;
44 uint32_t max_opens;
45 uint32_t num_opens;
46 } local;
47 struct {
48 struct db_context *db_ctx;
49 } global;
52 static struct db_context *smbXsrv_open_global_db_ctx = NULL;
54 NTSTATUS smbXsrv_open_global_init(void)
56 char *global_path = NULL;
57 struct db_context *db_ctx = NULL;
59 if (smbXsrv_open_global_db_ctx != NULL) {
60 return NT_STATUS_OK;
63 global_path = lock_path(talloc_tos(), "smbXsrv_open_global.tdb");
64 if (global_path == NULL) {
65 return NT_STATUS_NO_MEMORY;
68 db_ctx = db_open(NULL, global_path,
69 SMBD_VOLATILE_TDB_HASH_SIZE,
70 SMBD_VOLATILE_TDB_FLAGS,
71 O_RDWR | O_CREAT, 0600,
72 DBWRAP_LOCK_ORDER_1,
73 DBWRAP_FLAG_NONE);
74 TALLOC_FREE(global_path);
75 if (db_ctx == NULL) {
76 NTSTATUS status;
78 status = map_nt_error_from_unix_common(errno);
80 return status;
83 smbXsrv_open_global_db_ctx = db_ctx;
85 return NT_STATUS_OK;
89 * NOTE:
90 * We need to store the keys in big endian so that dbwrap_rbt's memcmp
91 * has the same result as integer comparison between the uint32_t
92 * values.
94 * TODO: implement string based key
97 struct smbXsrv_open_global_key_buf { uint8_t buf[sizeof(uint32_t)]; };
99 static TDB_DATA smbXsrv_open_global_id_to_key(
100 uint32_t id, struct smbXsrv_open_global_key_buf *key_buf)
102 RSIVAL(key_buf->buf, 0, id);
104 return (TDB_DATA) {
105 .dptr = key_buf->buf,
106 .dsize = sizeof(key_buf->buf),
110 static struct db_record *smbXsrv_open_global_fetch_locked(
111 struct db_context *db,
112 uint32_t id,
113 TALLOC_CTX *mem_ctx)
115 struct smbXsrv_open_global_key_buf key_buf;
116 TDB_DATA key = smbXsrv_open_global_id_to_key(id, &key_buf);
117 struct db_record *rec = NULL;
120 rec = dbwrap_fetch_locked(db, mem_ctx, key);
122 if (rec == NULL) {
123 DBG_DEBUG("Failed to lock global id 0x%08x, key '%s'\n", id,
124 tdb_data_dbg(key));
127 return rec;
130 static NTSTATUS smbXsrv_open_table_init(struct smbXsrv_connection *conn,
131 uint32_t lowest_id,
132 uint32_t highest_id,
133 uint32_t max_opens)
135 struct smbXsrv_client *client = conn->client;
136 struct smbXsrv_open_table *table;
137 NTSTATUS status;
138 uint64_t max_range;
140 if (lowest_id > highest_id) {
141 return NT_STATUS_INTERNAL_ERROR;
144 max_range = highest_id;
145 max_range -= lowest_id;
146 max_range += 1;
148 if (max_opens > max_range) {
149 return NT_STATUS_INTERNAL_ERROR;
152 table = talloc_zero(client, struct smbXsrv_open_table);
153 if (table == NULL) {
154 return NT_STATUS_NO_MEMORY;
157 table->local.idr = idr_init(table);
158 if (table->local.idr == NULL) {
159 TALLOC_FREE(table);
160 return NT_STATUS_NO_MEMORY;
162 table->local.replay_cache_db_ctx = db_open_rbt(table);
163 if (table->local.replay_cache_db_ctx == NULL) {
164 TALLOC_FREE(table);
165 return NT_STATUS_NO_MEMORY;
167 table->local.lowest_id = lowest_id;
168 table->local.highest_id = highest_id;
169 table->local.max_opens = max_opens;
171 status = smbXsrv_open_global_init();
172 if (!NT_STATUS_IS_OK(status)) {
173 TALLOC_FREE(table);
174 return status;
177 table->global.db_ctx = smbXsrv_open_global_db_ctx;
179 client->open_table = table;
180 return NT_STATUS_OK;
183 static NTSTATUS smbXsrv_open_local_lookup(struct smbXsrv_open_table *table,
184 uint32_t open_local_id,
185 uint32_t open_global_id,
186 NTTIME now,
187 struct smbXsrv_open **_open)
189 struct smbXsrv_open *op = NULL;
191 *_open = NULL;
193 if (open_local_id == 0) {
194 return NT_STATUS_FILE_CLOSED;
197 if (table == NULL) {
198 /* this might happen before the end of negprot */
199 return NT_STATUS_FILE_CLOSED;
202 if (table->local.idr == NULL) {
203 return NT_STATUS_INTERNAL_ERROR;
206 op = idr_find(table->local.idr, open_local_id);
207 if (op == NULL) {
208 return NT_STATUS_FILE_CLOSED;
211 if (open_global_id == 0) {
212 /* make the global check a no-op for SMB1 */
213 open_global_id = op->global->open_global_id;
216 if (op->global->open_global_id != open_global_id) {
217 return NT_STATUS_FILE_CLOSED;
220 if (now != 0) {
221 op->idle_time = now;
224 *_open = op;
225 return NT_STATUS_OK;
228 static NTSTATUS smbXsrv_open_global_verify_record(
229 TDB_DATA key,
230 TDB_DATA val,
231 TALLOC_CTX *mem_ctx,
232 struct smbXsrv_open_global0 **_global0);
234 static NTSTATUS smbXsrv_open_global_allocate(
235 struct db_context *db, struct smbXsrv_open_global0 *global)
237 uint32_t i;
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
245 * ID for SRVSVC.
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;
250 TDB_DATA key, val;
251 uint32_t id;
252 NTSTATUS status;
254 if (i >= min_tries && last_free != 0) {
255 id = last_free;
256 } else {
257 id = generate_random();
259 if (id == 0) {
260 id++;
262 if (id == UINT32_MAX) {
263 id--;
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;
282 return NT_STATUS_OK;
285 TALLOC_FREE(tmp_global0);
287 if (NT_STATUS_EQUAL(status, NT_STATUS_FATAL_APP_EXIT)) {
289 * smbd crashed
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",
296 nt_errstr(status));
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
304 * quickly.
306 last_free = id;
310 if (!NT_STATUS_IS_OK(status)) {
311 DBG_WARNING("smbXsrv_open_global_verify_record() "
312 "failed for %s: %s, ignoring\n",
313 tdb_data_dbg(key),
314 nt_errstr(status));
317 TALLOC_FREE(global->db_rec);
320 /* should not be reached */
321 return NT_STATUS_INTERNAL_ERROR;
324 static NTSTATUS smbXsrv_open_global_verify_record(
325 TDB_DATA key,
326 TDB_DATA val,
327 TALLOC_CTX *mem_ctx,
328 struct smbXsrv_open_global0 **_global0)
330 DATA_BLOB blob = { .data = val.dptr, .length = val.dsize, };
331 struct smbXsrv_open_globalB global_blob;
332 enum ndr_err_code ndr_err;
333 struct smbXsrv_open_global0 *global0 = NULL;
334 struct server_id_buf buf;
336 if (val.dsize == 0) {
337 return NT_STATUS_NOT_FOUND;
340 ndr_err = ndr_pull_struct_blob(
341 &blob,
342 mem_ctx,
343 &global_blob,
344 (ndr_pull_flags_fn_t)ndr_pull_smbXsrv_open_globalB);
345 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
346 DBG_WARNING("key '%s' ndr_pull_struct_blob - %s\n",
347 tdb_data_dbg(key),
348 ndr_map_error2string(ndr_err));
349 return ndr_map_error2ntstatus(ndr_err);
352 DBG_DEBUG("\n");
353 if (CHECK_DEBUGLVL(10)) {
354 NDR_PRINT_DEBUG(smbXsrv_open_globalB, &global_blob);
357 if (global_blob.version != SMBXSRV_VERSION_0) {
358 DBG_ERR("key '%s' use unsupported version %u\n",
359 tdb_data_dbg(key),
360 global_blob.version);
361 NDR_PRINT_DEBUG(smbXsrv_open_globalB, &global_blob);
362 return NT_STATUS_INTERNAL_ERROR;
365 global0 = global_blob.info.info0;
366 *_global0 = global0;
368 if (server_id_is_disconnected(&global0->server_id)) {
369 return NT_STATUS_OK;
371 if (serverid_exists(&global0->server_id)) {
372 return NT_STATUS_OK;
375 DBG_WARNING("smbd %s did not clean up record %s\n",
376 server_id_str_buf(global0->server_id, &buf),
377 tdb_data_dbg(key));
379 return NT_STATUS_FATAL_APP_EXIT;
382 static NTSTATUS smbXsrv_open_global_store(struct smbXsrv_open_global0 *global)
384 struct smbXsrv_open_globalB global_blob;
385 DATA_BLOB blob = data_blob_null;
386 TDB_DATA key;
387 TDB_DATA val;
388 NTSTATUS status;
389 enum ndr_err_code ndr_err;
392 * TODO: if we use other versions than '0'
393 * we would add glue code here, that would be able to
394 * store the information in the old format.
397 key = dbwrap_record_get_key(global->db_rec);
398 val = dbwrap_record_get_value(global->db_rec);
400 global_blob = (struct smbXsrv_open_globalB) {
401 .version = smbXsrv_version_global_current(),
404 if (val.dsize >= 8) {
405 global_blob.seqnum = IVAL(val.dptr, 4);
407 global_blob.seqnum += 1;
408 global_blob.info.info0 = global;
410 ndr_err = ndr_push_struct_blob(&blob, talloc_tos(), &global_blob,
411 (ndr_push_flags_fn_t)ndr_push_smbXsrv_open_globalB);
412 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
413 DBG_WARNING("key '%s' ndr_push - %s\n",
414 tdb_data_dbg(key),
415 ndr_map_error2string(ndr_err));
416 TALLOC_FREE(global->db_rec);
417 return ndr_map_error2ntstatus(ndr_err);
420 val = make_tdb_data(blob.data, blob.length);
421 status = dbwrap_record_store(global->db_rec, val, TDB_REPLACE);
422 TALLOC_FREE(blob.data);
423 if (!NT_STATUS_IS_OK(status)) {
424 DBG_WARNING("key '%s' store - %s\n",
425 tdb_data_dbg(key),
426 nt_errstr(status));
427 TALLOC_FREE(global->db_rec);
428 return status;
431 if (CHECK_DEBUGLVL(10)) {
432 DBG_DEBUG("key '%s' stored\n", tdb_data_dbg(key));
433 NDR_PRINT_DEBUG(smbXsrv_open_globalB, &global_blob);
436 TALLOC_FREE(global->db_rec);
438 return NT_STATUS_OK;
441 static NTSTATUS smbXsrv_open_global_lookup(struct smbXsrv_open_table *table,
442 uint32_t open_global_id,
443 TALLOC_CTX *mem_ctx,
444 struct smbXsrv_open_global0 **_global)
446 struct smbXsrv_open_global_key_buf key_buf;
447 TDB_DATA key = smbXsrv_open_global_id_to_key(open_global_id, &key_buf);
448 TDB_DATA val;
449 struct db_record *global_rec = NULL;
450 NTSTATUS status;
452 *_global = NULL;
454 if (table->global.db_ctx == NULL) {
455 return NT_STATUS_INTERNAL_ERROR;
458 global_rec = dbwrap_fetch_locked(table->global.db_ctx, mem_ctx, key);
459 if (global_rec == NULL) {
460 return NT_STATUS_INTERNAL_DB_ERROR;
462 val = dbwrap_record_get_value(global_rec);
464 status = smbXsrv_open_global_verify_record(key, val, mem_ctx, _global);
465 if (NT_STATUS_IS_OK(status)) {
466 (*_global)->db_rec = talloc_move(*_global, &global_rec);
467 return NT_STATUS_OK;
470 TALLOC_FREE(global_rec);
472 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
473 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
476 return status;
479 static int smbXsrv_open_destructor(struct smbXsrv_open *op)
481 NTSTATUS status;
483 status = smbXsrv_open_close(op, 0);
484 if (!NT_STATUS_IS_OK(status)) {
485 DEBUG(0, ("smbXsrv_open_destructor: "
486 "smbXsrv_open_close() failed - %s\n",
487 nt_errstr(status)));
490 TALLOC_FREE(op->global);
492 return 0;
495 NTSTATUS smbXsrv_open_create(struct smbXsrv_connection *conn,
496 struct auth_session_info *session_info,
497 NTTIME now,
498 struct smbXsrv_open **_open)
500 struct smbXsrv_open_table *table = conn->client->open_table;
501 struct smbXsrv_open *op = NULL;
502 struct smbXsrv_open_global0 *global = NULL;
503 NTSTATUS status;
504 struct dom_sid *current_sid = NULL;
505 struct security_token *current_token = NULL;
506 int local_id;
508 if (session_info == NULL) {
509 return NT_STATUS_INVALID_HANDLE;
511 current_token = session_info->security_token;
513 if (current_token == NULL) {
514 return NT_STATUS_INVALID_HANDLE;
517 if (current_token->num_sids > PRIMARY_USER_SID_INDEX) {
518 current_sid = &current_token->sids[PRIMARY_USER_SID_INDEX];
521 if (current_sid == NULL) {
522 return NT_STATUS_INVALID_HANDLE;
525 if (table->local.num_opens >= table->local.max_opens) {
526 return NT_STATUS_INSUFFICIENT_RESOURCES;
529 op = talloc_zero(table, struct smbXsrv_open);
530 if (op == NULL) {
531 return NT_STATUS_NO_MEMORY;
533 op->table = table;
534 op->status = NT_STATUS_OK; /* TODO: start with INTERNAL_ERROR */
535 op->idle_time = now;
537 global = talloc_zero(op, struct smbXsrv_open_global0);
538 if (global == NULL) {
539 TALLOC_FREE(op);
540 return NT_STATUS_NO_MEMORY;
542 op->global = global;
545 * We mark every slot as invalid using 0xFF.
546 * Valid values are masked with 0xF.
548 memset(global->lock_sequence_array, 0xFF,
549 sizeof(global->lock_sequence_array));
551 status = smbXsrv_open_global_allocate(table->global.db_ctx, global);
552 if (!NT_STATUS_IS_OK(status)) {
553 TALLOC_FREE(op);
554 return status;
557 local_id = idr_get_new_random(
558 table->local.idr,
560 table->local.lowest_id,
561 table->local.highest_id);
562 if (local_id == -1) {
563 TALLOC_FREE(op);
564 return NT_STATUS_INSUFFICIENT_RESOURCES;
566 op->local_id = local_id;
568 global->open_persistent_id = global->open_global_id;
569 global->open_volatile_id = op->local_id;
571 global->server_id = messaging_server_id(conn->client->msg_ctx);
572 global->open_time = now;
573 global->open_owner = *current_sid;
574 if (conn->protocol >= PROTOCOL_SMB2_10) {
575 global->client_guid = conn->smb2.client.guid;
578 table->local.num_opens += 1;
580 talloc_set_destructor(op, smbXsrv_open_destructor);
582 status = smbXsrv_open_global_store(global);
583 if (!NT_STATUS_IS_OK(status)) {
584 DEBUG(0,("smbXsrv_open_create: "
585 "global_id (0x%08x) store failed - %s\n",
586 op->global->open_global_id,
587 nt_errstr(status)));
588 TALLOC_FREE(op);
589 return status;
592 if (CHECK_DEBUGLVL(10)) {
593 struct smbXsrv_openB open_blob = {
594 .version = SMBXSRV_VERSION_0,
595 .info.info0 = op,
598 DEBUG(10,("smbXsrv_open_create: global_id (0x%08x) stored\n",
599 op->global->open_global_id));
600 NDR_PRINT_DEBUG(smbXsrv_openB, &open_blob);
603 *_open = op;
604 return NT_STATUS_OK;
607 static NTSTATUS smbXsrv_open_set_replay_cache(struct smbXsrv_open *op)
609 struct GUID *create_guid;
610 struct GUID_txt_buf buf;
611 char *guid_string;
612 struct db_context *db = op->table->local.replay_cache_db_ctx;
613 struct smbXsrv_open_replay_cache rc = {
614 .idle_time = op->idle_time,
615 .local_id = op->local_id,
617 uint8_t data[SMBXSRV_OPEN_REPLAY_CACHE_FIXED_SIZE] = { 0 };
618 DATA_BLOB blob = { .data = data, .length = sizeof(data), };
619 enum ndr_err_code ndr_err;
620 NTSTATUS status;
621 TDB_DATA val;
623 if (!(op->flags & SMBXSRV_OPEN_NEED_REPLAY_CACHE)) {
624 return NT_STATUS_OK;
627 if (op->flags & SMBXSRV_OPEN_HAVE_REPLAY_CACHE) {
628 return NT_STATUS_OK;
631 create_guid = &op->global->create_guid;
632 guid_string = GUID_buf_string(create_guid, &buf);
634 ndr_err = ndr_push_struct_into_fixed_blob(&blob, &rc,
635 (ndr_push_flags_fn_t)ndr_push_smbXsrv_open_replay_cache);
636 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
637 status = ndr_map_error2ntstatus(ndr_err);
638 return status;
640 val = make_tdb_data(blob.data, blob.length);
642 status = dbwrap_store_bystring(db, guid_string, val, TDB_REPLACE);
644 if (NT_STATUS_IS_OK(status)) {
645 op->flags |= SMBXSRV_OPEN_HAVE_REPLAY_CACHE;
646 op->flags &= ~SMBXSRV_OPEN_NEED_REPLAY_CACHE;
649 return status;
652 NTSTATUS smbXsrv_open_purge_replay_cache(struct smbXsrv_client *client,
653 const struct GUID *create_guid)
655 struct GUID_txt_buf buf;
656 char *guid_string;
657 struct db_context *db;
659 if (client->open_table == NULL) {
660 return NT_STATUS_OK;
663 db = client->open_table->local.replay_cache_db_ctx;
665 guid_string = GUID_buf_string(create_guid, &buf);
666 if (guid_string == NULL) {
667 return NT_STATUS_INVALID_PARAMETER;
670 return dbwrap_purge_bystring(db, guid_string);
673 static NTSTATUS smbXsrv_open_clear_replay_cache(struct smbXsrv_open *op)
675 struct GUID *create_guid;
676 struct GUID_txt_buf buf;
677 char *guid_string;
678 struct db_context *db;
679 NTSTATUS status;
681 if (op->table == NULL) {
682 return NT_STATUS_OK;
685 db = op->table->local.replay_cache_db_ctx;
687 if (!(op->flags & SMBXSRV_OPEN_HAVE_REPLAY_CACHE)) {
688 return NT_STATUS_OK;
691 create_guid = &op->global->create_guid;
692 if (GUID_all_zero(create_guid)) {
693 return NT_STATUS_OK;
696 guid_string = GUID_buf_string(create_guid, &buf);
697 if (guid_string == NULL) {
698 return NT_STATUS_INVALID_PARAMETER;
701 status = dbwrap_purge_bystring(db, guid_string);
703 if (NT_STATUS_IS_OK(status)) {
704 op->flags &= ~SMBXSRV_OPEN_HAVE_REPLAY_CACHE;
707 return status;
710 NTSTATUS smbXsrv_open_update(struct smbXsrv_open *op)
712 struct smbXsrv_open_table *table = op->table;
713 NTSTATUS status;
715 if (op->global->db_rec != NULL) {
716 DEBUG(0, ("smbXsrv_open_update(0x%08x): "
717 "Called with db_rec != NULL'\n",
718 op->global->open_global_id));
719 return NT_STATUS_INTERNAL_ERROR;
722 op->global->db_rec = smbXsrv_open_global_fetch_locked(
723 table->global.db_ctx,
724 op->global->open_global_id,
725 op->global /* TALLOC_CTX */);
726 if (op->global->db_rec == NULL) {
727 return NT_STATUS_INTERNAL_DB_ERROR;
730 status = smbXsrv_open_global_store(op->global);
731 if (!NT_STATUS_IS_OK(status)) {
732 DEBUG(0,("smbXsrv_open_update: "
733 "global_id (0x%08x) store failed - %s\n",
734 op->global->open_global_id,
735 nt_errstr(status)));
736 return status;
739 status = smbXsrv_open_set_replay_cache(op);
740 if (!NT_STATUS_IS_OK(status)) {
741 DBG_ERR("smbXsrv_open_set_replay_cache failed: %s\n",
742 nt_errstr(status));
743 return status;
746 if (CHECK_DEBUGLVL(10)) {
747 struct smbXsrv_openB open_blob = {
748 .version = SMBXSRV_VERSION_0,
749 .info.info0 = op,
752 DEBUG(10,("smbXsrv_open_update: global_id (0x%08x) stored\n",
753 op->global->open_global_id));
754 NDR_PRINT_DEBUG(smbXsrv_openB, &open_blob);
757 return NT_STATUS_OK;
760 NTSTATUS smbXsrv_open_close(struct smbXsrv_open *op, NTTIME now)
762 struct smbXsrv_open_table *table;
763 struct db_record *global_rec = NULL;
764 NTSTATUS status;
765 NTSTATUS error = NT_STATUS_OK;
766 int ret;
768 error = smbXsrv_open_clear_replay_cache(op);
769 if (!NT_STATUS_IS_OK(error)) {
770 DBG_ERR("smbXsrv_open_clear_replay_cache failed: %s\n",
771 nt_errstr(error));
774 if (op->table == NULL) {
775 return error;
778 table = op->table;
779 op->table = NULL;
781 op->status = NT_STATUS_FILE_CLOSED;
782 op->global->disconnect_time = now;
783 server_id_set_disconnected(&op->global->server_id);
785 global_rec = op->global->db_rec;
786 op->global->db_rec = NULL;
787 if (global_rec == NULL) {
788 global_rec = smbXsrv_open_global_fetch_locked(
789 table->global.db_ctx,
790 op->global->open_global_id,
791 op->global /* TALLOC_CTX */);
792 if (global_rec == NULL) {
793 error = NT_STATUS_INTERNAL_ERROR;
797 if (global_rec != NULL && op->global->durable) {
799 * If it is a durable open we need to update the global part
800 * instead of deleting it
802 op->global->db_rec = global_rec;
803 status = smbXsrv_open_global_store(op->global);
804 if (NT_STATUS_IS_OK(status)) {
806 * smbXsrv_open_global_store does the free
807 * of op->global->db_rec
809 global_rec = NULL;
811 if (!NT_STATUS_IS_OK(status)) {
812 DEBUG(0,("smbXsrv_open_close(0x%08x)"
813 "smbXsrv_open_global_store() failed - %s\n",
814 op->global->open_global_id,
815 nt_errstr(status)));
816 error = status;
819 if (NT_STATUS_IS_OK(status) && CHECK_DEBUGLVL(10)) {
820 struct smbXsrv_openB open_blob = {
821 .version = SMBXSRV_VERSION_0,
822 .info.info0 = op,
825 DEBUG(10,("smbXsrv_open_close(0x%08x): "
826 "stored disconnect\n",
827 op->global->open_global_id));
828 NDR_PRINT_DEBUG(smbXsrv_openB, &open_blob);
832 if (global_rec != NULL) {
833 status = dbwrap_record_delete(global_rec);
834 if (!NT_STATUS_IS_OK(status)) {
835 TDB_DATA key = dbwrap_record_get_key(global_rec);
837 DEBUG(0, ("smbXsrv_open_close(0x%08x): "
838 "failed to delete global key '%s': %s\n",
839 op->global->open_global_id,
840 tdb_data_dbg(key),
841 nt_errstr(status)));
842 error = status;
845 TALLOC_FREE(global_rec);
847 ret = idr_remove(table->local.idr, op->local_id);
848 SMB_ASSERT(ret == 0);
850 table->local.num_opens -= 1;
852 if (op->compat) {
853 op->compat->op = NULL;
854 file_free(NULL, op->compat);
855 op->compat = NULL;
858 return error;
861 NTSTATUS smb1srv_open_table_init(struct smbXsrv_connection *conn)
863 uint32_t max_opens;
866 * Allow a range from 1..65534.
868 * With real_max_open_files possible ids,
869 * truncated to the SMB1 limit of 16-bit.
871 * 0 and 0xFFFF are no valid ids.
873 max_opens = conn->client->sconn->real_max_open_files;
874 max_opens = MIN(max_opens, UINT16_MAX - 1);
876 return smbXsrv_open_table_init(conn, 1, UINT16_MAX - 1, max_opens);
879 NTSTATUS smb1srv_open_lookup(struct smbXsrv_connection *conn,
880 uint16_t fnum, NTTIME now,
881 struct smbXsrv_open **_open)
883 struct smbXsrv_open_table *table = conn->client->open_table;
884 uint32_t local_id = fnum;
885 uint32_t global_id = 0;
887 return smbXsrv_open_local_lookup(table, local_id, global_id, now, _open);
890 NTSTATUS smb2srv_open_table_init(struct smbXsrv_connection *conn)
892 uint32_t max_opens;
893 uint32_t highest_id;
896 * Allow a range from 1..4294967294.
898 * With real_max_open_files possible ids,
899 * truncated to 16-bit (the same as SMB1 for now).
901 * 0 and 0xFFFFFFFF are no valid ids.
903 * The usage of conn->sconn->real_max_open_files
904 * is the reason that we use one open table per
905 * transport connection (as we still have a 1:1 mapping
906 * between process and transport connection).
908 max_opens = conn->client->sconn->real_max_open_files;
909 max_opens = MIN(max_opens, UINT16_MAX - 1);
912 * idtree uses "int" for local IDs. Limit the maximum ID to
913 * what "int" can hold.
915 highest_id = UINT32_MAX-1;
916 highest_id = MIN(highest_id, INT_MAX);
918 return smbXsrv_open_table_init(conn, 1, highest_id, max_opens);
921 NTSTATUS smb2srv_open_lookup(struct smbXsrv_connection *conn,
922 uint64_t persistent_id,
923 uint64_t volatile_id,
924 NTTIME now,
925 struct smbXsrv_open **_open)
927 struct smbXsrv_open_table *table = conn->client->open_table;
928 uint32_t local_id = volatile_id & UINT32_MAX;
929 uint64_t local_zeros = volatile_id & 0xFFFFFFFF00000000LLU;
930 uint32_t global_id = persistent_id & UINT32_MAX;
931 uint64_t global_zeros = persistent_id & 0xFFFFFFFF00000000LLU;
932 NTSTATUS status;
934 if (local_zeros != 0) {
935 return NT_STATUS_FILE_CLOSED;
938 if (global_zeros != 0) {
939 return NT_STATUS_FILE_CLOSED;
942 if (global_id == 0) {
943 return NT_STATUS_FILE_CLOSED;
946 status = smbXsrv_open_local_lookup(table, local_id, global_id, now,
947 _open);
948 if (!NT_STATUS_IS_OK(status)) {
949 return status;
953 * Clear the replay cache for this create_guid if it exists:
954 * This is based on the assumption that this lookup will be
955 * triggered by a client request using the file-id for lookup.
956 * Hence the client has proven that it has in fact seen the
957 * reply to its initial create call. So subsequent create replays
958 * should be treated as invalid. Hence the index for create_guid
959 * lookup needs to be removed.
961 status = smbXsrv_open_clear_replay_cache(*_open);
963 return status;
967 * This checks or marks the replay cache, we have the following
968 * cases:
970 * 1. There is no record in the cache
971 * => we add the passes caller_req_guid as holder_req_guid
972 * together with local_id as 0.
973 * => We return STATUS_FWP_RESERVED in order to indicate
974 * that the caller holds the current reservation
976 * 2. There is a record in the cache and holder_req_guid
977 * is already the same as caller_req_guid and local_id is 0
978 * => We return STATUS_FWP_RESERVED in order to indicate
979 * that the caller holds the current reservation
981 * 3. There is a record in the cache with a holder_req_guid
982 * other than caller_req_guid (and local_id is 0):
983 * => We return NT_STATUS_FILE_NOT_AVAILABLE to indicate
984 * the original request is still pending
986 * 4. There is a record in the cache with a zero holder_req_guid
987 * and a valid local_id:
988 * => We lookup the existing open by local_id
989 * => We return NT_STATUS_OK together with the smbXsrv_open
992 * With NT_STATUS_OK the caller can continue the replay processing.
994 * With STATUS_FWP_RESERVED the caller should continue the normal
995 * open processing:
996 * - On success:
997 * - smbXsrv_open_update()/smbXsrv_open_set_replay_cache()
998 * will convert the record to a zero holder_req_guid
999 * with a valid local_id.
1000 * - On failure:
1001 * - smbXsrv_open_purge_replay_cache() should cleanup
1002 * the reservation.
1004 * All other values should be returned to the client,
1005 * while NT_STATUS_FILE_NOT_AVAILABLE will trigger the
1006 * retry loop on the client.
1008 NTSTATUS smb2srv_open_lookup_replay_cache(struct smbXsrv_connection *conn,
1009 struct GUID caller_req_guid,
1010 struct GUID create_guid,
1011 const char *name,
1012 NTTIME now,
1013 struct smbXsrv_open **_open)
1015 TALLOC_CTX *frame = talloc_stackframe();
1016 NTSTATUS status;
1017 struct smbXsrv_open_table *table = conn->client->open_table;
1018 struct db_context *db = table->local.replay_cache_db_ctx;
1019 struct GUID_txt_buf tmp_guid_buf;
1020 struct GUID_txt_buf _create_guid_buf;
1021 const char *create_guid_str = GUID_buf_string(&create_guid, &_create_guid_buf);
1022 TDB_DATA create_guid_key = string_term_tdb_data(create_guid_str);
1023 struct db_record *db_rec = NULL;
1024 struct smbXsrv_open *op = NULL;
1025 struct smbXsrv_open_replay_cache rc = {
1026 .holder_req_guid = caller_req_guid,
1027 .idle_time = now,
1028 .local_id = 0,
1030 enum ndr_err_code ndr_err;
1031 DATA_BLOB blob = data_blob_null;
1032 TDB_DATA val;
1034 *_open = NULL;
1036 db_rec = dbwrap_fetch_locked(db, frame, create_guid_key);
1037 if (db_rec == NULL) {
1038 TALLOC_FREE(frame);
1039 return NT_STATUS_INTERNAL_DB_ERROR;
1042 val = dbwrap_record_get_value(db_rec);
1043 if (val.dsize == 0) {
1044 uint8_t data[SMBXSRV_OPEN_REPLAY_CACHE_FIXED_SIZE];
1046 blob = data_blob_const(data, ARRAY_SIZE(data));
1047 ndr_err = ndr_push_struct_into_fixed_blob(&blob, &rc,
1048 (ndr_push_flags_fn_t)ndr_push_smbXsrv_open_replay_cache);
1049 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1050 status = ndr_map_error2ntstatus(ndr_err);
1051 TALLOC_FREE(frame);
1052 return status;
1055 val = make_tdb_data(blob.data, blob.length);
1056 status = dbwrap_record_store(db_rec, val, TDB_REPLACE);
1057 if (!NT_STATUS_IS_OK(status)) {
1058 TALLOC_FREE(frame);
1059 return status;
1063 * We're the new holder
1065 *_open = NULL;
1066 TALLOC_FREE(frame);
1067 return NT_STATUS_FWP_RESERVED;
1070 if (val.dsize != SMBXSRV_OPEN_REPLAY_CACHE_FIXED_SIZE) {
1071 TALLOC_FREE(frame);
1072 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1075 blob = data_blob_const(val.dptr, val.dsize);
1076 ndr_err = ndr_pull_struct_blob_all_noalloc(&blob, &rc,
1077 (ndr_pull_flags_fn_t)ndr_pull_smbXsrv_open_replay_cache);
1078 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1079 status = ndr_map_error2ntstatus(ndr_err);
1080 TALLOC_FREE(frame);
1081 return status;
1083 if (rc.local_id != 0) {
1084 if (GUID_equal(&rc.holder_req_guid, &caller_req_guid)) {
1086 * This should not happen
1088 status = NT_STATUS_INTERNAL_ERROR;
1089 DBG_ERR("caller %s already holds local_id %u for create %s [%s] - %s\n",
1090 GUID_buf_string(&caller_req_guid, &tmp_guid_buf),
1091 (unsigned)rc.local_id,
1092 create_guid_str,
1093 name,
1094 nt_errstr(status));
1096 TALLOC_FREE(frame);
1097 return status;
1100 status = smbXsrv_open_local_lookup(table,
1101 rc.local_id,
1102 0, /* global_id */
1103 now,
1104 &op);
1105 if (!NT_STATUS_IS_OK(status)) {
1106 DBG_ERR("holder %s stale for local_id %u for create %s [%s] - %s\n",
1107 GUID_buf_string(&rc.holder_req_guid, &tmp_guid_buf),
1108 (unsigned)rc.local_id,
1109 create_guid_str,
1110 name,
1111 nt_errstr(status));
1113 TALLOC_FREE(frame);
1114 return status;
1118 * We found an open the caller can reuse.
1120 SMB_ASSERT(op != NULL);
1121 *_open = op;
1122 TALLOC_FREE(frame);
1123 return NT_STATUS_OK;
1126 if (GUID_equal(&rc.holder_req_guid, &caller_req_guid)) {
1128 * We're still the holder
1130 *_open = NULL;
1131 TALLOC_FREE(frame);
1132 return NT_STATUS_FWP_RESERVED;
1136 * The original request (or a former replay) is still
1137 * pending, ask the client to retry by sending FILE_NOT_AVAILABLE.
1139 status = NT_STATUS_FILE_NOT_AVAILABLE;
1140 DBG_DEBUG("holder %s still pending for create %s [%s] - %s\n",
1141 GUID_buf_string(&rc.holder_req_guid, &tmp_guid_buf),
1142 create_guid_str,
1143 name,
1144 nt_errstr(status));
1145 TALLOC_FREE(frame);
1146 return status;
1149 NTSTATUS smb2srv_open_recreate(struct smbXsrv_connection *conn,
1150 struct auth_session_info *session_info,
1151 uint64_t persistent_id,
1152 const struct GUID *create_guid,
1153 NTTIME now,
1154 struct smbXsrv_open **_open)
1156 struct smbXsrv_open_table *table = conn->client->open_table;
1157 struct smbXsrv_open *op = NULL;
1158 uint32_t global_id;
1159 NTSTATUS status;
1160 struct security_token *current_token = NULL;
1161 int local_id;
1163 if (session_info == NULL) {
1164 DEBUG(10, ("session_info=NULL\n"));
1165 return NT_STATUS_INVALID_HANDLE;
1167 current_token = session_info->security_token;
1169 if (current_token == NULL) {
1170 DEBUG(10, ("current_token=NULL\n"));
1171 return NT_STATUS_INVALID_HANDLE;
1174 if ((persistent_id & 0xFFFFFFFF00000000LLU) != 0) {
1176 * We only use 32 bit for the persistent ID
1178 DBG_DEBUG("persistent_id=%"PRIx64"\n", persistent_id);
1179 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1181 global_id = persistent_id & UINT32_MAX; /* truncate to 32 bit */
1183 op = talloc_zero(table, struct smbXsrv_open);
1184 if (op == NULL) {
1185 return NT_STATUS_NO_MEMORY;
1187 op->table = table;
1189 status = smbXsrv_open_global_lookup(table, global_id, op, &op->global);
1190 if (!NT_STATUS_IS_OK(status)) {
1191 TALLOC_FREE(op);
1192 DEBUG(10, ("smbXsrv_open_global_lookup returned %s\n",
1193 nt_errstr(status)));
1194 return status;
1198 * If the provided create_guid is NULL, this means that
1199 * the reconnect request was a v1 request. In that case
1200 * we should skip the create GUID verification, since
1201 * it is valid to v1-reconnect a v2-opened handle.
1203 if ((create_guid != NULL) &&
1204 !GUID_equal(&op->global->create_guid, create_guid))
1206 TALLOC_FREE(op);
1207 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1210 if (!security_token_is_sid(current_token, &op->global->open_owner)) {
1211 TALLOC_FREE(op);
1212 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1215 if (!op->global->durable) {
1216 TALLOC_FREE(op);
1217 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1220 if (table->local.num_opens >= table->local.max_opens) {
1221 TALLOC_FREE(op);
1222 return NT_STATUS_INSUFFICIENT_RESOURCES;
1225 local_id = idr_get_new_random(
1226 table->local.idr,
1228 table->local.lowest_id,
1229 table->local.highest_id);
1230 if (local_id == -1) {
1231 TALLOC_FREE(op);
1232 return NT_STATUS_INSUFFICIENT_RESOURCES;
1235 op->local_id = local_id;
1237 op->idle_time = now;
1238 op->status = NT_STATUS_FILE_CLOSED;
1240 op->global->open_volatile_id = op->local_id;
1241 op->global->server_id = messaging_server_id(conn->client->msg_ctx);
1243 table->local.num_opens += 1;
1245 talloc_set_destructor(op, smbXsrv_open_destructor);
1247 status = smbXsrv_open_global_store(op->global);
1248 if (!NT_STATUS_IS_OK(status)) {
1249 TALLOC_FREE(op);
1250 return status;
1253 if (CHECK_DEBUGLVL(10)) {
1254 struct smbXsrv_openB open_blob = {
1255 .info.info0 = op,
1258 DEBUG(10,("smbXsrv_open_recreate: global_id (0x%08x) stored\n",
1259 op->global->open_global_id));
1260 NDR_PRINT_DEBUG(smbXsrv_openB, &open_blob);
1263 *_open = op;
1264 return NT_STATUS_OK;
1268 static NTSTATUS smbXsrv_open_global_parse_record(TALLOC_CTX *mem_ctx,
1269 struct db_record *rec,
1270 struct smbXsrv_open_global0 **global)
1272 TDB_DATA key = dbwrap_record_get_key(rec);
1273 TDB_DATA val = dbwrap_record_get_value(rec);
1274 DATA_BLOB blob = data_blob_const(val.dptr, val.dsize);
1275 struct smbXsrv_open_globalB global_blob;
1276 enum ndr_err_code ndr_err;
1277 NTSTATUS status;
1278 TALLOC_CTX *frame = talloc_stackframe();
1280 ndr_err = ndr_pull_struct_blob(&blob, frame, &global_blob,
1281 (ndr_pull_flags_fn_t)ndr_pull_smbXsrv_open_globalB);
1282 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1283 DEBUG(1,("Invalid record in smbXsrv_open_global.tdb:"
1284 "key '%s' ndr_pull_struct_blob - %s\n",
1285 tdb_data_dbg(key),
1286 ndr_errstr(ndr_err)));
1287 status = ndr_map_error2ntstatus(ndr_err);
1288 goto done;
1291 if (global_blob.version != SMBXSRV_VERSION_0) {
1292 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
1293 DEBUG(1,("Invalid record in smbXsrv_open_global.tdb:"
1294 "key '%s' unsupported version - %d - %s\n",
1295 tdb_data_dbg(key),
1296 (int)global_blob.version,
1297 nt_errstr(status)));
1298 goto done;
1301 if (global_blob.info.info0 == NULL) {
1302 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
1303 DEBUG(1,("Invalid record in smbXsrv_tcon_global.tdb:"
1304 "key '%s' info0 NULL pointer - %s\n",
1305 tdb_data_dbg(key),
1306 nt_errstr(status)));
1307 goto done;
1310 *global = talloc_move(mem_ctx, &global_blob.info.info0);
1311 status = NT_STATUS_OK;
1312 done:
1313 talloc_free(frame);
1314 return status;
1317 struct smbXsrv_open_global_traverse_state {
1318 int (*fn)(struct smbXsrv_open_global0 *, void *);
1319 void *private_data;
1322 static int smbXsrv_open_global_traverse_fn(struct db_record *rec, void *data)
1324 struct smbXsrv_open_global_traverse_state *state =
1325 (struct smbXsrv_open_global_traverse_state*)data;
1326 struct smbXsrv_open_global0 *global = NULL;
1327 NTSTATUS status;
1328 int ret = -1;
1330 status = smbXsrv_open_global_parse_record(talloc_tos(), rec, &global);
1331 if (!NT_STATUS_IS_OK(status)) {
1332 return -1;
1335 global->db_rec = rec;
1336 ret = state->fn(global, state->private_data);
1337 talloc_free(global);
1338 return ret;
1341 NTSTATUS smbXsrv_open_global_traverse(
1342 int (*fn)(struct smbXsrv_open_global0 *, void *),
1343 void *private_data)
1346 NTSTATUS status;
1347 int count = 0;
1348 struct smbXsrv_open_global_traverse_state state = {
1349 .fn = fn,
1350 .private_data = private_data,
1353 become_root();
1354 status = smbXsrv_open_global_init();
1355 if (!NT_STATUS_IS_OK(status)) {
1356 unbecome_root();
1357 DEBUG(0, ("Failed to initialize open_global: %s\n",
1358 nt_errstr(status)));
1359 return status;
1362 status = dbwrap_traverse_read(smbXsrv_open_global_db_ctx,
1363 smbXsrv_open_global_traverse_fn,
1364 &state,
1365 &count);
1366 unbecome_root();
1368 return status;
1371 NTSTATUS smbXsrv_open_cleanup(uint64_t persistent_id)
1373 NTSTATUS status = NT_STATUS_OK;
1374 TALLOC_CTX *frame = talloc_stackframe();
1375 struct smbXsrv_open_global0 *op = NULL;
1376 TDB_DATA val;
1377 struct db_record *rec;
1378 bool delete_open = false;
1379 uint32_t global_id = persistent_id & UINT32_MAX;
1381 rec = smbXsrv_open_global_fetch_locked(smbXsrv_open_global_db_ctx,
1382 global_id,
1383 frame);
1384 if (rec == NULL) {
1385 status = NT_STATUS_NOT_FOUND;
1386 goto done;
1389 val = dbwrap_record_get_value(rec);
1390 if (val.dsize == 0) {
1391 DEBUG(10, ("smbXsrv_open_cleanup[global: 0x%08x] "
1392 "empty record in %s, skipping...\n",
1393 global_id, dbwrap_name(smbXsrv_open_global_db_ctx)));
1394 goto done;
1397 status = smbXsrv_open_global_parse_record(talloc_tos(), rec, &op);
1398 if (!NT_STATUS_IS_OK(status)) {
1399 DEBUG(1, ("smbXsrv_open_cleanup[global: 0x%08x] "
1400 "failed to read record: %s\n",
1401 global_id, nt_errstr(status)));
1402 goto done;
1405 if (server_id_is_disconnected(&op->server_id)) {
1406 struct timeval now, disconnect_time;
1407 int64_t tdiff;
1408 now = timeval_current();
1409 nttime_to_timeval(&disconnect_time, op->disconnect_time);
1410 tdiff = usec_time_diff(&now, &disconnect_time);
1411 delete_open = (tdiff >= 1000*op->durable_timeout_msec);
1413 DEBUG(10, ("smbXsrv_open_cleanup[global: 0x%08x] "
1414 "disconnected at [%s] %us ago with "
1415 "timeout of %us -%s reached\n",
1416 global_id,
1417 nt_time_string(frame, op->disconnect_time),
1418 (unsigned)(tdiff/1000000),
1419 op->durable_timeout_msec / 1000,
1420 delete_open ? "" : " not"));
1421 } else if (!serverid_exists(&op->server_id)) {
1422 struct server_id_buf idbuf;
1423 DEBUG(10, ("smbXsrv_open_cleanup[global: 0x%08x] "
1424 "server[%s] does not exist\n",
1425 global_id,
1426 server_id_str_buf(op->server_id, &idbuf)));
1427 delete_open = true;
1430 if (!delete_open) {
1431 goto done;
1434 status = dbwrap_record_delete(rec);
1435 if (!NT_STATUS_IS_OK(status)) {
1436 DEBUG(1, ("smbXsrv_open_cleanup[global: 0x%08x] "
1437 "failed to delete record"
1438 "from %s: %s\n", global_id,
1439 dbwrap_name(smbXsrv_open_global_db_ctx),
1440 nt_errstr(status)));
1441 goto done;
1444 DEBUG(10, ("smbXsrv_open_cleanup[global: 0x%08x] "
1445 "delete record from %s\n",
1446 global_id,
1447 dbwrap_name(smbXsrv_open_global_db_ctx)));
1449 done:
1450 talloc_free(frame);
1451 return status;