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