tdb: add overflow detection to tdb_expand_adjust()
[Samba/id10ts.git] / source3 / smbd / smbXsrv_tcon.c
blobb6e205857a7be8fca74e7153565ecefd4badacaa
1 /*
2 Unix SMB/CIFS implementation.
4 Copyright (C) Stefan Metzmacher 2011-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 "includes.h"
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 "messages.h"
29 #include "lib/util/util_tdb.h"
30 #include "librpc/gen_ndr/ndr_smbXsrv.h"
31 #include "serverid.h"
33 struct smbXsrv_tcon_table {
34 struct {
35 struct db_context *db_ctx;
36 uint32_t lowest_id;
37 uint32_t highest_id;
38 uint32_t max_tcons;
39 uint32_t num_tcons;
40 } local;
41 struct {
42 struct db_context *db_ctx;
43 } global;
46 static struct db_context *smbXsrv_tcon_global_db_ctx = NULL;
48 NTSTATUS smbXsrv_tcon_global_init(void)
50 const char *global_path = NULL;
51 struct db_context *db_ctx = NULL;
53 if (smbXsrv_tcon_global_db_ctx != NULL) {
54 return NT_STATUS_OK;
57 global_path = lock_path("smbXsrv_tcon_global.tdb");
59 db_ctx = db_open(NULL, global_path,
60 0, /* hash_size */
61 TDB_DEFAULT |
62 TDB_CLEAR_IF_FIRST |
63 TDB_INCOMPATIBLE_HASH,
64 O_RDWR | O_CREAT, 0600,
65 DBWRAP_LOCK_ORDER_1);
66 if (db_ctx == NULL) {
67 NTSTATUS status;
69 status = map_nt_error_from_unix_common(errno);
71 return status;
74 smbXsrv_tcon_global_db_ctx = db_ctx;
76 return NT_STATUS_OK;
80 * NOTE:
81 * We need to store the keys in big endian so that dbwrap_rbt's memcmp
82 * has the same result as integer comparison between the uint32_t
83 * values.
85 * TODO: implement string based key
88 #define SMBXSRV_TCON_GLOBAL_TDB_KEY_SIZE sizeof(uint32_t)
90 static TDB_DATA smbXsrv_tcon_global_id_to_key(uint32_t id,
91 uint8_t *key_buf)
93 TDB_DATA key;
95 RSIVAL(key_buf, 0, id);
97 key = make_tdb_data(key_buf, SMBXSRV_TCON_GLOBAL_TDB_KEY_SIZE);
99 return key;
102 #if 0
103 static NTSTATUS smbXsrv_tcon_global_key_to_id(TDB_DATA key, uint32_t *id)
105 if (id == NULL) {
106 return NT_STATUS_INVALID_PARAMETER;
109 if (key.dsize != SMBXSRV_TCON_GLOBAL_TDB_KEY_SIZE) {
110 return NT_STATUS_INTERNAL_DB_CORRUPTION;
113 *id = RIVAL(key.dptr, 0);
115 return NT_STATUS_OK;
117 #endif
119 #define SMBXSRV_TCON_LOCAL_TDB_KEY_SIZE sizeof(uint32_t)
121 static TDB_DATA smbXsrv_tcon_local_id_to_key(uint32_t id,
122 uint8_t *key_buf)
124 TDB_DATA key;
126 RSIVAL(key_buf, 0, id);
128 key = make_tdb_data(key_buf, SMBXSRV_TCON_LOCAL_TDB_KEY_SIZE);
130 return key;
133 static NTSTATUS smbXsrv_tcon_local_key_to_id(TDB_DATA key, uint32_t *id)
135 if (id == NULL) {
136 return NT_STATUS_INVALID_PARAMETER;
139 if (key.dsize != SMBXSRV_TCON_LOCAL_TDB_KEY_SIZE) {
140 return NT_STATUS_INTERNAL_DB_CORRUPTION;
143 *id = RIVAL(key.dptr, 0);
145 return NT_STATUS_OK;
148 static NTSTATUS smbXsrv_tcon_table_init(TALLOC_CTX *mem_ctx,
149 struct smbXsrv_tcon_table *table,
150 uint32_t lowest_id,
151 uint32_t highest_id,
152 uint32_t max_tcons)
154 NTSTATUS status;
155 uint64_t max_range;
157 if (lowest_id > highest_id) {
158 return NT_STATUS_INTERNAL_ERROR;
161 max_range = highest_id;
162 max_range -= lowest_id;
163 max_range += 1;
165 if (max_tcons > max_range) {
166 return NT_STATUS_INTERNAL_ERROR;
169 ZERO_STRUCTP(table);
170 table->local.db_ctx = db_open_rbt(table);
171 if (table->local.db_ctx == NULL) {
172 return NT_STATUS_NO_MEMORY;
174 table->local.lowest_id = lowest_id;
175 table->local.highest_id = highest_id;
176 table->local.max_tcons = max_tcons;
178 status = smbXsrv_tcon_global_init();
179 if (!NT_STATUS_IS_OK(status)) {
180 return status;
183 table->global.db_ctx = smbXsrv_tcon_global_db_ctx;
185 return NT_STATUS_OK;
188 struct smb1srv_tcon_local_allocate_state {
189 const uint32_t lowest_id;
190 const uint32_t highest_id;
191 uint32_t last_id;
192 uint32_t useable_id;
193 NTSTATUS status;
196 static int smb1srv_tcon_local_allocate_traverse(struct db_record *rec,
197 void *private_data)
199 struct smb1srv_tcon_local_allocate_state *state =
200 (struct smb1srv_tcon_local_allocate_state *)private_data;
201 TDB_DATA key = dbwrap_record_get_key(rec);
202 uint32_t id = 0;
203 NTSTATUS status;
205 status = smbXsrv_tcon_local_key_to_id(key, &id);
206 if (!NT_STATUS_IS_OK(status)) {
207 state->status = status;
208 return -1;
211 if (id <= state->last_id) {
212 state->status = NT_STATUS_INTERNAL_DB_CORRUPTION;
213 return -1;
215 state->last_id = id;
217 if (id > state->useable_id) {
218 state->status = NT_STATUS_OK;
219 return -1;
222 if (state->useable_id == state->highest_id) {
223 state->status = NT_STATUS_INSUFFICIENT_RESOURCES;
224 return -1;
227 state->useable_id +=1;
228 return 0;
231 static NTSTATUS smb1srv_tcon_local_allocate_id(struct db_context *db,
232 uint32_t lowest_id,
233 uint32_t highest_id,
234 TALLOC_CTX *mem_ctx,
235 struct db_record **_rec,
236 uint32_t *_id)
238 struct smb1srv_tcon_local_allocate_state state = {
239 .lowest_id = lowest_id,
240 .highest_id = highest_id,
241 .last_id = 0,
242 .useable_id = lowest_id,
243 .status = NT_STATUS_INTERNAL_ERROR,
245 uint32_t i;
246 uint32_t range;
247 NTSTATUS status;
248 int count = 0;
250 *_rec = NULL;
251 *_id = 0;
253 if (lowest_id > highest_id) {
254 return NT_STATUS_INSUFFICIENT_RESOURCES;
258 * first we try randomly
260 range = (highest_id - lowest_id) + 1;
262 for (i = 0; i < (range / 2); i++) {
263 uint32_t id;
264 uint8_t key_buf[SMBXSRV_TCON_LOCAL_TDB_KEY_SIZE];
265 TDB_DATA key;
266 TDB_DATA val;
267 struct db_record *rec = NULL;
269 id = generate_random() % range;
270 id += lowest_id;
272 if (id < lowest_id) {
273 id = lowest_id;
275 if (id > highest_id) {
276 id = highest_id;
279 key = smbXsrv_tcon_local_id_to_key(id, key_buf);
281 rec = dbwrap_fetch_locked(db, mem_ctx, key);
282 if (rec == NULL) {
283 return NT_STATUS_INSUFFICIENT_RESOURCES;
286 val = dbwrap_record_get_value(rec);
287 if (val.dsize != 0) {
288 TALLOC_FREE(rec);
289 continue;
292 *_rec = rec;
293 *_id = id;
294 return NT_STATUS_OK;
298 * if the range is almost full,
299 * we traverse the whole table
300 * (this relies on sorted behavior of dbwrap_rbt)
302 status = dbwrap_traverse_read(db, smb1srv_tcon_local_allocate_traverse,
303 &state, &count);
304 if (NT_STATUS_IS_OK(status)) {
305 if (NT_STATUS_IS_OK(state.status)) {
306 return NT_STATUS_INTERNAL_ERROR;
309 if (!NT_STATUS_EQUAL(state.status, NT_STATUS_INTERNAL_ERROR)) {
310 return state.status;
313 if (state.useable_id <= state.highest_id) {
314 state.status = NT_STATUS_OK;
315 } else {
316 return NT_STATUS_INSUFFICIENT_RESOURCES;
318 } else if (!NT_STATUS_EQUAL(status, NT_STATUS_INTERNAL_DB_CORRUPTION)) {
320 * Here we really expect NT_STATUS_INTERNAL_DB_CORRUPTION!
322 * If we get anything else it is an error, because it
323 * means we did not manage to find a free slot in
324 * the db.
326 return NT_STATUS_INSUFFICIENT_RESOURCES;
329 if (NT_STATUS_IS_OK(state.status)) {
330 uint32_t id;
331 uint8_t key_buf[SMBXSRV_TCON_LOCAL_TDB_KEY_SIZE];
332 TDB_DATA key;
333 TDB_DATA val;
334 struct db_record *rec = NULL;
336 id = state.useable_id;
338 key = smbXsrv_tcon_local_id_to_key(id, key_buf);
340 rec = dbwrap_fetch_locked(db, mem_ctx, key);
341 if (rec == NULL) {
342 return NT_STATUS_INSUFFICIENT_RESOURCES;
345 val = dbwrap_record_get_value(rec);
346 if (val.dsize != 0) {
347 TALLOC_FREE(rec);
348 return NT_STATUS_INTERNAL_DB_CORRUPTION;
351 *_rec = rec;
352 *_id = id;
353 return NT_STATUS_OK;
356 return state.status;
359 struct smbXsrv_tcon_local_fetch_state {
360 struct smbXsrv_tcon *tcon;
361 NTSTATUS status;
364 static void smbXsrv_tcon_local_fetch_parser(TDB_DATA key, TDB_DATA data,
365 void *private_data)
367 struct smbXsrv_tcon_local_fetch_state *state =
368 (struct smbXsrv_tcon_local_fetch_state *)private_data;
369 void *ptr;
371 if (data.dsize != sizeof(ptr)) {
372 state->status = NT_STATUS_INTERNAL_DB_ERROR;
373 return;
376 memcpy(&ptr, data.dptr, data.dsize);
377 state->tcon = talloc_get_type_abort(ptr, struct smbXsrv_tcon);
378 state->status = NT_STATUS_OK;
381 static NTSTATUS smbXsrv_tcon_local_lookup(struct smbXsrv_tcon_table *table,
382 uint32_t tcon_local_id,
383 NTTIME now,
384 struct smbXsrv_tcon **_tcon)
386 struct smbXsrv_tcon_local_fetch_state state = {
387 .tcon = NULL,
388 .status = NT_STATUS_INTERNAL_ERROR,
390 uint8_t key_buf[SMBXSRV_TCON_LOCAL_TDB_KEY_SIZE];
391 TDB_DATA key;
392 NTSTATUS status;
394 *_tcon = NULL;
396 if (tcon_local_id == 0) {
397 return NT_STATUS_NETWORK_NAME_DELETED;
400 if (table == NULL) {
401 /* this might happen before the end of negprot */
402 return NT_STATUS_NETWORK_NAME_DELETED;
405 if (table->local.db_ctx == NULL) {
406 return NT_STATUS_INTERNAL_ERROR;
409 key = smbXsrv_tcon_local_id_to_key(tcon_local_id, key_buf);
411 status = dbwrap_parse_record(table->local.db_ctx, key,
412 smbXsrv_tcon_local_fetch_parser,
413 &state);
414 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
415 return NT_STATUS_NETWORK_NAME_DELETED;
416 } else if (!NT_STATUS_IS_OK(status)) {
417 return status;
419 if (!NT_STATUS_IS_OK(state.status)) {
420 return state.status;
423 if (NT_STATUS_EQUAL(state.tcon->status, NT_STATUS_NETWORK_NAME_DELETED)) {
424 return NT_STATUS_NETWORK_NAME_DELETED;
427 state.tcon->idle_time = now;
429 *_tcon = state.tcon;
430 return state.tcon->status;
433 static int smbXsrv_tcon_global_destructor(struct smbXsrv_tcon_global0 *global)
435 return 0;
438 static void smbXsrv_tcon_global_verify_record(struct db_record *db_rec,
439 bool *is_free,
440 bool *was_free,
441 TALLOC_CTX *mem_ctx,
442 struct smbXsrv_tcon_global0 **_g);
444 static NTSTATUS smbXsrv_tcon_global_allocate(struct db_context *db,
445 TALLOC_CTX *mem_ctx,
446 struct smbXsrv_tcon_global0 **_global)
448 uint32_t i;
449 struct smbXsrv_tcon_global0 *global = NULL;
450 uint32_t last_free = 0;
451 const uint32_t min_tries = 3;
453 *_global = NULL;
455 global = talloc_zero(mem_ctx, struct smbXsrv_tcon_global0);
456 if (global == NULL) {
457 return NT_STATUS_NO_MEMORY;
459 talloc_set_destructor(global, smbXsrv_tcon_global_destructor);
462 * Here we just randomly try the whole 32-bit space
464 * We use just 32-bit, because we want to reuse the
465 * ID for SRVSVC.
467 for (i = 0; i < UINT32_MAX; i++) {
468 bool is_free = false;
469 bool was_free = false;
470 uint32_t id;
471 uint8_t key_buf[SMBXSRV_TCON_GLOBAL_TDB_KEY_SIZE];
472 TDB_DATA key;
474 if (i >= min_tries && last_free != 0) {
475 id = last_free;
476 } else {
477 id = generate_random();
479 if (id == 0) {
480 id++;
482 if (id == UINT32_MAX) {
483 id--;
486 key = smbXsrv_tcon_global_id_to_key(id, key_buf);
488 global->db_rec = dbwrap_fetch_locked(db, mem_ctx, key);
489 if (global->db_rec == NULL) {
490 talloc_free(global);
491 return NT_STATUS_INSUFFICIENT_RESOURCES;
494 smbXsrv_tcon_global_verify_record(global->db_rec,
495 &is_free,
496 &was_free,
497 NULL, NULL);
499 if (!is_free) {
500 TALLOC_FREE(global->db_rec);
501 continue;
504 if (!was_free && i < min_tries) {
506 * The session_id is free now,
507 * but was not free before.
509 * This happens if a smbd crashed
510 * and did not cleanup the record.
512 * If this is one of our first tries,
513 * then we try to find a real free one.
515 if (last_free == 0) {
516 last_free = id;
518 TALLOC_FREE(global->db_rec);
519 continue;
522 global->tcon_global_id = id;
524 *_global = global;
525 return NT_STATUS_OK;
528 /* should not be reached */
529 talloc_free(global);
530 return NT_STATUS_INTERNAL_ERROR;
533 static void smbXsrv_tcon_global_verify_record(struct db_record *db_rec,
534 bool *is_free,
535 bool *was_free,
536 TALLOC_CTX *mem_ctx,
537 struct smbXsrv_tcon_global0 **_g)
539 TDB_DATA key;
540 TDB_DATA val;
541 DATA_BLOB blob;
542 struct smbXsrv_tcon_globalB global_blob;
543 enum ndr_err_code ndr_err;
544 struct smbXsrv_tcon_global0 *global = NULL;
545 bool exists;
546 TALLOC_CTX *frame = talloc_stackframe();
548 *is_free = false;
550 if (was_free) {
551 *was_free = false;
553 if (_g) {
554 *_g = NULL;
557 key = dbwrap_record_get_key(db_rec);
559 val = dbwrap_record_get_value(db_rec);
560 if (val.dsize == 0) {
561 TALLOC_FREE(frame);
562 *is_free = true;
563 if (was_free) {
564 *was_free = true;
566 return;
569 blob = data_blob_const(val.dptr, val.dsize);
571 ndr_err = ndr_pull_struct_blob(&blob, frame, &global_blob,
572 (ndr_pull_flags_fn_t)ndr_pull_smbXsrv_tcon_globalB);
573 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
574 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
575 DEBUG(1,("smbXsrv_tcon_global_verify_record: "
576 "key '%s' ndr_pull_struct_blob - %s\n",
577 hex_encode_talloc(frame, key.dptr, key.dsize),
578 nt_errstr(status)));
579 TALLOC_FREE(frame);
580 return;
583 DEBUG(10,("smbXsrv_tcon_global_verify_record\n"));
584 if (DEBUGLVL(10)) {
585 NDR_PRINT_DEBUG(smbXsrv_tcon_globalB, &global_blob);
588 if (global_blob.version != SMBXSRV_VERSION_0) {
589 DEBUG(0,("smbXsrv_tcon_global_verify_record: "
590 "key '%s' use unsupported version %u\n",
591 hex_encode_talloc(frame, key.dptr, key.dsize),
592 global_blob.version));
593 NDR_PRINT_DEBUG(smbXsrv_tcon_globalB, &global_blob);
594 TALLOC_FREE(frame);
595 return;
598 global = global_blob.info.info0;
600 exists = serverid_exists(&global->server_id);
601 if (!exists) {
602 DEBUG(2,("smbXsrv_tcon_global_verify_record: "
603 "key '%s' server_id %s does not exist.\n",
604 hex_encode_talloc(frame, key.dptr, key.dsize),
605 server_id_str(frame, &global->server_id)));
606 if (DEBUGLVL(2)) {
607 NDR_PRINT_DEBUG(smbXsrv_tcon_globalB, &global_blob);
609 TALLOC_FREE(frame);
610 dbwrap_record_delete(db_rec);
611 *is_free = true;
612 return;
615 if (_g) {
616 *_g = talloc_move(mem_ctx, &global);
618 TALLOC_FREE(frame);
621 static NTSTATUS smbXsrv_tcon_global_store(struct smbXsrv_tcon_global0 *global)
623 struct smbXsrv_tcon_globalB global_blob;
624 DATA_BLOB blob = data_blob_null;
625 TDB_DATA key;
626 TDB_DATA val;
627 NTSTATUS status;
628 enum ndr_err_code ndr_err;
631 * TODO: if we use other versions than '0'
632 * we would add glue code here, that would be able to
633 * store the information in the old format.
636 if (global->db_rec == NULL) {
637 return NT_STATUS_INTERNAL_ERROR;
640 key = dbwrap_record_get_key(global->db_rec);
641 val = dbwrap_record_get_value(global->db_rec);
643 ZERO_STRUCT(global_blob);
644 global_blob.version = smbXsrv_version_global_current();
645 if (val.dsize >= 8) {
646 global_blob.seqnum = IVAL(val.dptr, 4);
648 global_blob.seqnum += 1;
649 global_blob.info.info0 = global;
651 ndr_err = ndr_push_struct_blob(&blob, global->db_rec, &global_blob,
652 (ndr_push_flags_fn_t)ndr_push_smbXsrv_tcon_globalB);
653 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
654 status = ndr_map_error2ntstatus(ndr_err);
655 DEBUG(1,("smbXsrv_tcon_global_store: key '%s' ndr_push - %s\n",
656 hex_encode_talloc(global->db_rec, key.dptr, key.dsize),
657 nt_errstr(status)));
658 TALLOC_FREE(global->db_rec);
659 return status;
662 val = make_tdb_data(blob.data, blob.length);
663 status = dbwrap_record_store(global->db_rec, val, TDB_REPLACE);
664 if (!NT_STATUS_IS_OK(status)) {
665 DEBUG(1,("smbXsrv_tcon_global_store: key '%s' store - %s\n",
666 hex_encode_talloc(global->db_rec, key.dptr, key.dsize),
667 nt_errstr(status)));
668 TALLOC_FREE(global->db_rec);
669 return status;
672 if (DEBUGLVL(10)) {
673 DEBUG(10,("smbXsrv_tcon_global_store: key '%s' stored\n",
674 hex_encode_talloc(global->db_rec, key.dptr, key.dsize)));
675 NDR_PRINT_DEBUG(smbXsrv_tcon_globalB, &global_blob);
678 TALLOC_FREE(global->db_rec);
680 return NT_STATUS_OK;
683 static int smbXsrv_tcon_destructor(struct smbXsrv_tcon *tcon)
685 NTSTATUS status;
687 status = smbXsrv_tcon_disconnect(tcon, 0);
688 if (!NT_STATUS_IS_OK(status)) {
689 DEBUG(0, ("smbXsrv_tcon_destructor: "
690 "smbXsrv_tcon_disconnect() failed - %s\n",
691 nt_errstr(status)));
694 TALLOC_FREE(tcon->global);
696 return 0;
699 static NTSTATUS smbXsrv_tcon_create(struct smbXsrv_connection *conn,
700 struct smbXsrv_tcon_table *table,
701 NTTIME now,
702 struct smbXsrv_tcon **_tcon)
704 struct db_record *local_rec = NULL;
705 struct smbXsrv_tcon *tcon = NULL;
706 void *ptr = NULL;
707 TDB_DATA val;
708 struct smbXsrv_tcon_global0 *global = NULL;
709 NTSTATUS status;
711 if (table->local.num_tcons >= table->local.max_tcons) {
712 return NT_STATUS_INSUFFICIENT_RESOURCES;
715 tcon = talloc_zero(table, struct smbXsrv_tcon);
716 if (tcon == NULL) {
717 return NT_STATUS_NO_MEMORY;
719 tcon->table = table;
720 tcon->status = NT_STATUS_INTERNAL_ERROR;
721 tcon->idle_time = now;
723 status = smbXsrv_tcon_global_allocate(table->global.db_ctx,
724 tcon, &global);
725 if (!NT_STATUS_IS_OK(status)) {
726 TALLOC_FREE(tcon);
727 return status;
729 tcon->global = global;
731 if (conn->protocol >= PROTOCOL_SMB2_02) {
732 uint64_t id = global->tcon_global_id;
733 uint8_t key_buf[SMBXSRV_TCON_LOCAL_TDB_KEY_SIZE];
734 TDB_DATA key;
736 global->tcon_wire_id = id;
738 tcon->local_id = global->tcon_global_id;
740 key = smbXsrv_tcon_local_id_to_key(tcon->local_id, key_buf);
742 local_rec = dbwrap_fetch_locked(table->local.db_ctx,
743 tcon, key);
744 if (local_rec == NULL) {
745 TALLOC_FREE(tcon);
746 return NT_STATUS_NO_MEMORY;
749 val = dbwrap_record_get_value(local_rec);
750 if (val.dsize != 0) {
751 TALLOC_FREE(tcon);
752 return NT_STATUS_INTERNAL_DB_CORRUPTION;
754 } else {
756 status = smb1srv_tcon_local_allocate_id(table->local.db_ctx,
757 table->local.lowest_id,
758 table->local.highest_id,
759 tcon,
760 &local_rec,
761 &tcon->local_id);
762 if (!NT_STATUS_IS_OK(status)) {
763 TALLOC_FREE(tcon);
764 return status;
767 global->tcon_wire_id = tcon->local_id;
770 global->creation_time = now;
772 global->server_id = messaging_server_id(conn->msg_ctx);
774 ptr = tcon;
775 val = make_tdb_data((uint8_t const *)&ptr, sizeof(ptr));
776 status = dbwrap_record_store(local_rec, val, TDB_REPLACE);
777 TALLOC_FREE(local_rec);
778 if (!NT_STATUS_IS_OK(status)) {
779 TALLOC_FREE(tcon);
780 return status;
782 table->local.num_tcons += 1;
784 talloc_set_destructor(tcon, smbXsrv_tcon_destructor);
786 status = smbXsrv_tcon_global_store(global);
787 if (!NT_STATUS_IS_OK(status)) {
788 DEBUG(0,("smbXsrv_tcon_create: "
789 "global_id (0x%08x) store failed - %s\n",
790 tcon->global->tcon_global_id,
791 nt_errstr(status)));
792 TALLOC_FREE(tcon);
793 return status;
796 if (DEBUGLVL(10)) {
797 struct smbXsrv_tconB tcon_blob;
799 ZERO_STRUCT(tcon_blob);
800 tcon_blob.version = SMBXSRV_VERSION_0;
801 tcon_blob.info.info0 = tcon;
803 DEBUG(10,("smbXsrv_tcon_create: global_id (0x%08x) stored\n",
804 tcon->global->tcon_global_id));
805 NDR_PRINT_DEBUG(smbXsrv_tconB, &tcon_blob);
808 *_tcon = tcon;
809 return NT_STATUS_OK;
812 NTSTATUS smbXsrv_tcon_update(struct smbXsrv_tcon *tcon)
814 struct smbXsrv_tcon_table *table = tcon->table;
815 NTSTATUS status;
816 uint8_t key_buf[SMBXSRV_TCON_GLOBAL_TDB_KEY_SIZE];
817 TDB_DATA key;
819 if (tcon->global->db_rec != NULL) {
820 DEBUG(0, ("smbXsrv_tcon_update(0x%08x): "
821 "Called with db_rec != NULL'\n",
822 tcon->global->tcon_global_id));
823 return NT_STATUS_INTERNAL_ERROR;
826 key = smbXsrv_tcon_global_id_to_key(tcon->global->tcon_global_id,
827 key_buf);
829 tcon->global->db_rec = dbwrap_fetch_locked(table->global.db_ctx,
830 tcon->global, key);
831 if (tcon->global->db_rec == NULL) {
832 DEBUG(0, ("smbXsrv_tcon_update(0x%08x): "
833 "Failed to lock global key '%s'\n",
834 tcon->global->tcon_global_id,
835 hex_encode_talloc(talloc_tos(), key.dptr,
836 key.dsize)));
837 return NT_STATUS_INTERNAL_DB_ERROR;
840 status = smbXsrv_tcon_global_store(tcon->global);
841 if (!NT_STATUS_IS_OK(status)) {
842 DEBUG(0,("smbXsrv_tcon_update: "
843 "global_id (0x%08x) store failed - %s\n",
844 tcon->global->tcon_global_id,
845 nt_errstr(status)));
846 return status;
849 if (DEBUGLVL(10)) {
850 struct smbXsrv_tconB tcon_blob;
852 ZERO_STRUCT(tcon_blob);
853 tcon_blob.version = SMBXSRV_VERSION_0;
854 tcon_blob.info.info0 = tcon;
856 DEBUG(10,("smbXsrv_tcon_update: global_id (0x%08x) stored\n",
857 tcon->global->tcon_global_id));
858 NDR_PRINT_DEBUG(smbXsrv_tconB, &tcon_blob);
861 return NT_STATUS_OK;
864 NTSTATUS smbXsrv_tcon_disconnect(struct smbXsrv_tcon *tcon, uint64_t vuid)
866 struct smbXsrv_tcon_table *table;
867 struct db_record *local_rec = NULL;
868 struct db_record *global_rec = NULL;
869 NTSTATUS status;
870 NTSTATUS error = NT_STATUS_OK;
872 if (tcon->table == NULL) {
873 return NT_STATUS_OK;
876 table = tcon->table;
877 tcon->table = NULL;
879 tcon->status = NT_STATUS_NETWORK_NAME_DELETED;
881 global_rec = tcon->global->db_rec;
882 tcon->global->db_rec = NULL;
883 if (global_rec == NULL) {
884 uint8_t key_buf[SMBXSRV_TCON_GLOBAL_TDB_KEY_SIZE];
885 TDB_DATA key;
887 key = smbXsrv_tcon_global_id_to_key(
888 tcon->global->tcon_global_id,
889 key_buf);
891 global_rec = dbwrap_fetch_locked(table->global.db_ctx,
892 tcon->global, key);
893 if (global_rec == NULL) {
894 DEBUG(0, ("smbXsrv_tcon_disconnect(0x%08x, '%s'): "
895 "Failed to lock global key '%s'\n",
896 tcon->global->tcon_global_id,
897 tcon->global->share_name,
898 hex_encode_talloc(global_rec, key.dptr,
899 key.dsize)));
900 error = NT_STATUS_INTERNAL_ERROR;
904 if (global_rec != NULL) {
905 status = dbwrap_record_delete(global_rec);
906 if (!NT_STATUS_IS_OK(status)) {
907 TDB_DATA key = dbwrap_record_get_key(global_rec);
909 DEBUG(0, ("smbXsrv_tcon_disconnect(0x%08x, '%s'): "
910 "failed to delete global key '%s': %s\n",
911 tcon->global->tcon_global_id,
912 tcon->global->share_name,
913 hex_encode_talloc(global_rec, key.dptr,
914 key.dsize),
915 nt_errstr(status)));
916 error = status;
919 TALLOC_FREE(global_rec);
921 local_rec = tcon->db_rec;
922 if (local_rec == NULL) {
923 uint8_t key_buf[SMBXSRV_TCON_LOCAL_TDB_KEY_SIZE];
924 TDB_DATA key;
926 key = smbXsrv_tcon_local_id_to_key(tcon->local_id, key_buf);
928 local_rec = dbwrap_fetch_locked(table->local.db_ctx,
929 tcon, key);
930 if (local_rec == NULL) {
931 DEBUG(0, ("smbXsrv_tcon_disconnect(0x%08x, '%s'): "
932 "Failed to lock local key '%s'\n",
933 tcon->global->tcon_global_id,
934 tcon->global->share_name,
935 hex_encode_talloc(local_rec, key.dptr,
936 key.dsize)));
937 error = NT_STATUS_INTERNAL_ERROR;
941 if (local_rec != NULL) {
942 status = dbwrap_record_delete(local_rec);
943 if (!NT_STATUS_IS_OK(status)) {
944 TDB_DATA key = dbwrap_record_get_key(local_rec);
946 DEBUG(0, ("smbXsrv_tcon_disconnect(0x%08x, '%s'): "
947 "failed to delete local key '%s': %s\n",
948 tcon->global->tcon_global_id,
949 tcon->global->share_name,
950 hex_encode_talloc(local_rec, key.dptr,
951 key.dsize),
952 nt_errstr(status)));
953 error = status;
955 table->local.num_tcons -= 1;
957 if (tcon->db_rec == NULL) {
958 TALLOC_FREE(local_rec);
960 tcon->db_rec = NULL;
962 if (tcon->compat) {
963 bool ok;
965 ok = set_current_service(tcon->compat, 0, true);
966 if (!ok) {
967 status = NT_STATUS_INTERNAL_ERROR;
968 DEBUG(0, ("smbXsrv_tcon_disconnect(0x%08x, '%s'): "
969 "set_current_service() failed: %s\n",
970 tcon->global->tcon_global_id,
971 tcon->global->share_name,
972 nt_errstr(status)));
973 tcon->compat = NULL;
974 return status;
977 close_cnum(tcon->compat, vuid);
978 tcon->compat = NULL;
981 return error;
984 struct smbXsrv_tcon_disconnect_all_state {
985 uint64_t vuid;
986 NTSTATUS first_status;
987 int errors;
990 static int smbXsrv_tcon_disconnect_all_callback(struct db_record *local_rec,
991 void *private_data);
993 static NTSTATUS smbXsrv_tcon_disconnect_all(struct smbXsrv_tcon_table *table,
994 uint64_t vuid)
996 struct smbXsrv_tcon_disconnect_all_state state;
997 NTSTATUS status;
998 int count = 0;
1000 if (table == NULL) {
1001 return NT_STATUS_OK;
1004 ZERO_STRUCT(state);
1005 state.vuid = vuid;
1007 status = dbwrap_traverse(table->local.db_ctx,
1008 smbXsrv_tcon_disconnect_all_callback,
1009 &state, &count);
1010 if (!NT_STATUS_IS_OK(status)) {
1011 DEBUG(0, ("smbXsrv_tcon_disconnect_all: "
1012 "dbwrap_traverse() failed: %s\n",
1013 nt_errstr(status)));
1014 return status;
1017 if (!NT_STATUS_IS_OK(state.first_status)) {
1018 DEBUG(0, ("smbXsrv_tcon_disconnect_all: "
1019 "count[%d] errors[%d] first[%s]\n",
1020 count, state.errors,
1021 nt_errstr(state.first_status)));
1022 return state.first_status;
1025 return NT_STATUS_OK;
1028 static int smbXsrv_tcon_disconnect_all_callback(struct db_record *local_rec,
1029 void *private_data)
1031 struct smbXsrv_tcon_disconnect_all_state *state =
1032 (struct smbXsrv_tcon_disconnect_all_state *)private_data;
1033 TDB_DATA val;
1034 void *ptr = NULL;
1035 struct smbXsrv_tcon *tcon = NULL;
1036 uint64_t vuid;
1037 NTSTATUS status;
1039 val = dbwrap_record_get_value(local_rec);
1040 if (val.dsize != sizeof(ptr)) {
1041 status = NT_STATUS_INTERNAL_ERROR;
1042 if (NT_STATUS_IS_OK(state->first_status)) {
1043 state->first_status = status;
1045 state->errors++;
1046 return 0;
1049 memcpy(&ptr, val.dptr, val.dsize);
1050 tcon = talloc_get_type_abort(ptr, struct smbXsrv_tcon);
1052 vuid = state->vuid;
1053 if (vuid == 0 && tcon->compat) {
1054 vuid = tcon->compat->vuid;
1057 tcon->db_rec = local_rec;
1058 status = smbXsrv_tcon_disconnect(tcon, vuid);
1059 if (!NT_STATUS_IS_OK(status)) {
1060 if (NT_STATUS_IS_OK(state->first_status)) {
1061 state->first_status = status;
1063 state->errors++;
1064 return 0;
1067 return 0;
1070 NTSTATUS smb1srv_tcon_table_init(struct smbXsrv_connection *conn)
1073 * Allow a range from 1..65534 with 65534 values.
1075 conn->tcon_table = talloc_zero(conn, struct smbXsrv_tcon_table);
1076 if (conn->tcon_table == NULL) {
1077 return NT_STATUS_NO_MEMORY;
1080 return smbXsrv_tcon_table_init(conn, conn->tcon_table,
1081 1, UINT16_MAX - 1,
1082 UINT16_MAX - 1);
1085 NTSTATUS smb1srv_tcon_create(struct smbXsrv_connection *conn,
1086 NTTIME now,
1087 struct smbXsrv_tcon **_tcon)
1089 return smbXsrv_tcon_create(conn, conn->tcon_table, now,
1090 _tcon);
1093 NTSTATUS smb1srv_tcon_lookup(struct smbXsrv_connection *conn,
1094 uint16_t tree_id, NTTIME now,
1095 struct smbXsrv_tcon **tcon)
1097 uint32_t local_id = tree_id;
1099 return smbXsrv_tcon_local_lookup(conn->tcon_table,
1100 local_id, now, tcon);
1103 NTSTATUS smb1srv_tcon_disconnect_all(struct smbXsrv_connection *conn)
1106 * We do not pass a vuid here,
1107 * which means the vuid is taken from
1108 * the tcon->compat->vuid.
1110 * NOTE: that tcon->compat->vuid may point to
1111 * a none existing vuid (or the wrong one)
1112 * as the tcon can exist without a session
1113 * in SMB1.
1115 * This matches the old behavior of
1116 * conn_close_all(), but we should think
1117 * about how to fix this in future.
1119 return smbXsrv_tcon_disconnect_all(conn->tcon_table, 0);
1122 NTSTATUS smb2srv_tcon_table_init(struct smbXsrv_session *session)
1125 * Allow a range from 1..4294967294 with 65534 (same as SMB1) values.
1127 session->tcon_table = talloc_zero(session, struct smbXsrv_tcon_table);
1128 if (session->tcon_table == NULL) {
1129 return NT_STATUS_NO_MEMORY;
1132 return smbXsrv_tcon_table_init(session, session->tcon_table,
1133 1, UINT32_MAX - 1,
1134 UINT16_MAX - 1);
1137 NTSTATUS smb2srv_tcon_create(struct smbXsrv_session *session,
1138 NTTIME now,
1139 struct smbXsrv_tcon **_tcon)
1141 return smbXsrv_tcon_create(session->connection, session->tcon_table,
1142 now, _tcon);
1145 NTSTATUS smb2srv_tcon_lookup(struct smbXsrv_session *session,
1146 uint32_t tree_id, NTTIME now,
1147 struct smbXsrv_tcon **tcon)
1149 uint32_t local_id = tree_id;
1151 return smbXsrv_tcon_local_lookup(session->tcon_table,
1152 local_id, now, tcon);
1155 NTSTATUS smb2srv_tcon_disconnect_all(struct smbXsrv_session *session)
1157 uint64_t vuid;
1159 if (session->compat) {
1160 vuid = session->compat->vuid;
1161 } else {
1162 vuid = 0;
1165 return smbXsrv_tcon_disconnect_all(session->tcon_table, vuid);
1168 struct smbXsrv_tcon_global_traverse_state {
1169 int (*fn)(struct smbXsrv_tcon_global0 *, void *);
1170 void *private_data;
1173 static int smbXsrv_tcon_global_traverse_fn(struct db_record *rec, void *data)
1175 int ret = -1;
1176 struct smbXsrv_tcon_global_traverse_state *state =
1177 (struct smbXsrv_tcon_global_traverse_state*)data;
1178 TDB_DATA key = dbwrap_record_get_key(rec);
1179 TDB_DATA val = dbwrap_record_get_value(rec);
1180 DATA_BLOB blob = data_blob_const(val.dptr, val.dsize);
1181 struct smbXsrv_tcon_globalB global_blob;
1182 enum ndr_err_code ndr_err;
1183 TALLOC_CTX *frame = talloc_stackframe();
1185 ndr_err = ndr_pull_struct_blob(&blob, frame, &global_blob,
1186 (ndr_pull_flags_fn_t)ndr_pull_smbXsrv_tcon_globalB);
1187 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1188 DEBUG(1,("Invalid record in smbXsrv_tcon_global.tdb:"
1189 "key '%s' ndr_pull_struct_blob - %s\n",
1190 hex_encode_talloc(frame, key.dptr, key.dsize),
1191 ndr_errstr(ndr_err)));
1192 goto done;
1195 if (global_blob.version != SMBXSRV_VERSION_0) {
1196 DEBUG(1,("Invalid record in smbXsrv_tcon_global.tdb:"
1197 "key '%s' unsuported version - %d\n",
1198 hex_encode_talloc(frame, key.dptr, key.dsize),
1199 (int)global_blob.version));
1200 goto done;
1203 global_blob.info.info0->db_rec = rec;
1204 ret = state->fn(global_blob.info.info0, state->private_data);
1205 done:
1206 TALLOC_FREE(frame);
1207 return ret;
1210 NTSTATUS smbXsrv_tcon_global_traverse(
1211 int (*fn)(struct smbXsrv_tcon_global0 *, void *),
1212 void *private_data)
1214 NTSTATUS status;
1215 int count = 0;
1216 struct smbXsrv_tcon_global_traverse_state state = {
1217 .fn = fn,
1218 .private_data = private_data,
1221 become_root();
1222 status = smbXsrv_tcon_global_init();
1223 if (!NT_STATUS_IS_OK(status)) {
1224 unbecome_root();
1225 DEBUG(0, ("Failed to initialize tcon_global: %s\n",
1226 nt_errstr(status)));
1227 return status;
1230 status = dbwrap_traverse_read(smbXsrv_tcon_global_db_ctx,
1231 smbXsrv_tcon_global_traverse_fn,
1232 &state,
1233 &count);
1234 unbecome_root();
1236 return status;