2 Unix SMB/CIFS implementation.
4 Copyright (C) Stefan Metzmacher 2012
5 Copyright (C) Michael Adam 2012
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "smbd/smbd.h"
23 #include "smbd/globals.h"
24 #include "dbwrap/dbwrap.h"
25 #include "dbwrap/dbwrap_rbt.h"
26 #include "dbwrap/dbwrap_open.h"
29 #include "../lib/tsocket/tsocket.h"
30 #include "../libcli/security/security.h"
32 #include "lib/util/util_tdb.h"
33 #include "librpc/gen_ndr/ndr_smbXsrv.h"
35 static struct db_context *smbXsrv_open_global_db_ctx = NULL;
37 NTSTATUS smbXsrv_open_global_init(void)
39 const char *global_path = NULL;
40 struct db_context *db_ctx = NULL;
42 if (smbXsrv_open_global_db_ctx != NULL) {
46 global_path = lock_path("smbXsrv_open_global.tdb");
48 db_ctx = db_open(NULL, global_path,
52 TDB_INCOMPATIBLE_HASH,
53 O_RDWR | O_CREAT, 0600,
54 DBWRAP_LOCK_ORDER_1 /* TODO - correct lock order */);
58 status = map_nt_error_from_unix_common(errno);
63 smbXsrv_open_global_db_ctx = db_ctx;
68 static NTSTATUS smbXsrv_open_table_init(struct smbXsrv_connection *conn,
72 struct smbXsrv_open_table *table = &conn->open_table;
76 table->local.db_ctx = db_open_rbt(conn);
77 if (table->local.db_ctx == NULL) {
78 return NT_STATUS_NO_MEMORY;
80 table->local.lowest_id = lowest_id;
81 table->local.highest_id = highest_id;
83 status = smbXsrv_open_global_init();
84 if (!NT_STATUS_IS_OK(status)) {
88 table->global.db_ctx = smbXsrv_open_global_db_ctx;
93 struct smbXsrv_open_local_allocate_state {
94 const uint32_t lowest_id;
95 const uint32_t highest_id;
101 static int smbXsrv_open_local_allocate_traverse(struct db_record *rec,
104 struct smbXsrv_open_local_allocate_state *state =
105 (struct smbXsrv_open_local_allocate_state *)private_data;
106 TDB_DATA key = dbwrap_record_get_key(rec);
109 if (key.dsize != sizeof(uint32_t)) {
111 state->status = NT_STATUS_INTERNAL_DB_CORRUPTION;
117 * We need big endian so that dbwrap_rbt's memcmp
118 * has the same result as integer comparison between the uint32_t
121 * TODO: implement string based key
123 id = RIVAL(key.dptr, 0);
125 if (id <= state->last_id) {
127 state->status = NT_STATUS_INTERNAL_DB_CORRUPTION;
132 if (id > state->useable_id) {
133 state->status = NT_STATUS_OK;
137 state->useable_id +=1;
141 static NTSTATUS smbXsrv_open_local_allocate_id(struct db_context *db,
145 struct db_record **_rec,
148 struct smbXsrv_open_local_allocate_state state = {
149 .lowest_id = lowest_id,
150 .highest_id = highest_id,
152 .useable_id = lowest_id,
153 .status = NT_STATUS_INTERNAL_ERROR,
163 if (lowest_id > highest_id) {
164 return NT_STATUS_INSUFFICIENT_RESOURCES;
167 range = (highest_id - lowest_id) + 1;
169 for (i = 0; i < range; i++) {
171 uint8_t key_buf[sizeof(uint32_t)];
174 struct db_record *rec = NULL;
176 id = generate_random() % range;
179 if (id < lowest_id) {
182 if (id > highest_id) {
186 RSIVAL(key_buf, 0, id);
187 key = make_tdb_data(key_buf, sizeof(key_buf));
189 rec = dbwrap_fetch_locked(db, mem_ctx, key);
191 return NT_STATUS_INSUFFICIENT_RESOURCES;
194 val = dbwrap_record_get_value(rec);
195 if (val.dsize != 0) {
205 status = dbwrap_traverse_read(db, smbXsrv_open_local_allocate_traverse,
207 if (!NT_STATUS_EQUAL(status, NT_STATUS_INTERNAL_DB_CORRUPTION)) {
209 * Here we really expect NT_STATUS_INTERNAL_DB_CORRUPTION!
211 * If we get anything else it is an error, because it
212 * means we did not manage to find a free slot in
215 return NT_STATUS_INSUFFICIENT_RESOURCES;
218 if (NT_STATUS_IS_OK(state.status)) {
220 uint8_t key_buf[sizeof(uint32_t)];
223 struct db_record *rec = NULL;
225 id = state.useable_id;
227 RSIVAL(key_buf, 0, id);
228 key = make_tdb_data(key_buf, sizeof(key_buf));
230 rec = dbwrap_fetch_locked(db, mem_ctx, key);
232 return NT_STATUS_INSUFFICIENT_RESOURCES;
235 val = dbwrap_record_get_value(rec);
236 if (val.dsize != 0) {
238 return NT_STATUS_INTERNAL_DB_CORRUPTION;
249 struct smbXsrv_open_local_fetch_state {
250 struct smbXsrv_open *op;
254 static void smbXsrv_open_local_fetch_parser(TDB_DATA key, TDB_DATA data,
257 struct smbXsrv_open_local_fetch_state *state =
258 (struct smbXsrv_open_local_fetch_state *)private_data;
261 if (data.dsize != sizeof(ptr)) {
262 state->status = NT_STATUS_INTERNAL_DB_ERROR;
266 memcpy(&ptr, data.dptr, data.dsize);
267 state->op = talloc_get_type_abort(ptr, struct smbXsrv_open);
268 state->status = NT_STATUS_OK;
271 static NTSTATUS smbXsrv_open_local_lookup(struct smbXsrv_open_table *table,
272 uint32_t open_local_id,
274 struct smbXsrv_open **_open)
276 struct smbXsrv_open_local_fetch_state state = {
278 .status = NT_STATUS_INTERNAL_ERROR,
280 uint8_t key_buf[sizeof(uint32_t)];
286 if (table->local.db_ctx == NULL) {
287 return NT_STATUS_INTERNAL_ERROR;
290 RSIVAL(key_buf, 0, open_local_id);
291 key = make_tdb_data(key_buf, sizeof(key_buf));
293 status = dbwrap_parse_record(table->local.db_ctx, key,
294 smbXsrv_open_local_fetch_parser,
296 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
297 return NT_STATUS_USER_SESSION_DELETED;
298 } else if (!NT_STATUS_IS_OK(status)) {
301 if (!NT_STATUS_IS_OK(state.status)) {
309 static int smbXsrv_open_global_destructor(struct smbXsrv_open_global0 *global)
314 static NTSTATUS smbXsrv_open_global_allocate(struct db_context *db,
316 struct smbXsrv_open_global0 **_global)
319 struct smbXsrv_open_global0 *global = NULL;
323 global = talloc_zero(mem_ctx, struct smbXsrv_open_global0);
324 if (global == NULL) {
325 return NT_STATUS_NO_MEMORY;
327 talloc_set_destructor(global, smbXsrv_open_global_destructor);
329 for (i = 0; i < UINT32_MAX; i++) {
331 uint8_t key_buf[sizeof(uint32_t)];
335 id = generate_random();
339 if (id == UINT32_MAX) {
343 RSIVAL(key_buf, 0, id);
344 key = make_tdb_data(key_buf, sizeof(key_buf));
346 global->db_rec = dbwrap_fetch_locked(db, mem_ctx, key);
347 if (global->db_rec == NULL) {
349 return NT_STATUS_INSUFFICIENT_RESOURCES;
352 val = dbwrap_record_get_value(global->db_rec);
353 if (val.dsize != 0) {
354 TALLOC_FREE(global->db_rec);
358 global->open_global_id = id;
364 /* should not be reached */
366 return NT_STATUS_INTERNAL_ERROR;
369 static NTSTATUS smbXsrv_open_global_store(struct smbXsrv_connection *sconn,
370 struct smbXsrv_open_global0 *global)
372 struct smbXsrv_open_globalB global_blob;
373 DATA_BLOB blob = data_blob_null;
376 enum ndr_err_code ndr_err;
379 * TODO: if we use other versions than '0'
380 * we would add glue code here, that would be able to
381 * store the information in the old format.
384 if (global->db_rec == NULL) {
385 return NT_STATUS_INTERNAL_ERROR;
388 val = dbwrap_record_get_value(global->db_rec);
390 ZERO_STRUCT(global_blob);
391 global_blob.version = 0;
392 if (val.dsize >= 8) {
393 global_blob.seqnum = IVAL(val.dptr, 4);
395 global_blob.seqnum += 1;
396 global_blob.info.info0 = global;
398 DEBUG(0,("%s:%s:\n", __location__, __FUNCTION__));
399 NDR_PRINT_DEBUG(smbXsrv_open_globalB, &global_blob);
401 ndr_err = ndr_push_struct_blob(&blob, global->db_rec, &global_blob,
402 (ndr_push_flags_fn_t)ndr_push_smbXsrv_open_globalB);
403 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
404 //status = ndr_err_code;
405 TALLOC_FREE(global->db_rec);
410 val = make_tdb_data(blob.data, blob.length);
411 status = dbwrap_record_store(global->db_rec, val, TDB_REPLACE);
412 TALLOC_FREE(global->db_rec);
413 if (!NT_STATUS_IS_OK(status)) {
420 struct smbXsrv_open_global_fetch_state {
422 struct smbXsrv_open_global0 *op;
426 static void smbXsrv_open_global_fetch_parser(TDB_DATA key, TDB_DATA data,
429 struct smbXsrv_open_global_fetch_state *state =
430 (struct smbXsrv_open_global_fetch_state *)private_data;
431 DATA_BLOB blob = data_blob_const(data.dptr, data.dsize);
432 struct smbXsrv_open_globalB global_blob;
433 enum ndr_err_code ndr_err;
435 ndr_err = ndr_pull_struct_blob(&blob, state->mem_ctx, &global_blob,
436 (ndr_pull_flags_fn_t)ndr_pull_smbXsrv_open_globalB);
437 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
438 //state->status = map;
442 DEBUG(0,("%s:%s:\n", __location__, __FUNCTION__));
443 NDR_PRINT_DEBUG(smbXsrv_open_globalB, &global_blob);
445 state->op = global_blob.info.info0;
447 state->status = NT_STATUS_OK;
450 static NTSTATUS smbXsrv_open_global_lookup(struct smbXsrv_open_table *table,
451 uint32_t open_global_id,
453 struct smbXsrv_open_global0 **_open)
455 struct smbXsrv_open_global_fetch_state state = {
458 .status = NT_STATUS_INTERNAL_ERROR,
461 uint8_t key_buf[sizeof(uint32_t)];
466 DEBUG(0,("%s:%s:\n", __location__, __FUNCTION__));
468 if (table->global.db_ctx == NULL) {
469 DEBUG(0,("%s:%s:\n", __location__, __FUNCTION__));
470 return NT_STATUS_INTERNAL_ERROR;
473 /* TODO: key as string */
474 RSIVAL(key_buf, 0, open_global_id);
475 key = make_tdb_data(key_buf, sizeof(key_buf));
477 status = dbwrap_parse_record(table->global.db_ctx, key,
478 smbXsrv_open_global_fetch_parser, &state);
479 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
480 DEBUG(0,("%s:%s:\n", __location__, __FUNCTION__));
481 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
482 } else if (!NT_STATUS_IS_OK(status)) {
483 DEBUG(0,("%s:%s:\n", __location__, __FUNCTION__));
486 if (!NT_STATUS_IS_OK(state.status)) {
487 DEBUG(0,("%s:%s:\n", __location__, __FUNCTION__));
492 DEBUG(0,("%s:%s:\n", __location__, __FUNCTION__));
496 static int smbXsrv_open_destructor(struct smbXsrv_open *op)
498 struct smbXsrv_open_table *table;
499 struct db_record *local_rec = NULL;
500 struct db_record *global_rec = NULL;
503 if (op->connection == NULL) {
507 table = &op->connection->open_table;
508 op->connection = NULL;
510 local_rec = op->db_rec;
512 if (local_rec == NULL) {
513 uint8_t key_buf[sizeof(uint32_t)];
516 RSIVAL(key_buf, 0, op->local_id);
517 key = make_tdb_data(key_buf, sizeof(key_buf));
519 local_rec = dbwrap_fetch_locked(table->local.db_ctx,
523 if (local_rec != NULL) {
524 status = dbwrap_record_delete(local_rec);
527 global_rec = op->global->db_rec;
528 op->global->db_rec = NULL;
529 if (global_rec == NULL && !op->global->durable) {
530 uint8_t key_buf[sizeof(uint32_t)];
533 RSIVAL(key_buf, 0, op->global->open_global_id);
534 key = make_tdb_data(key_buf, sizeof(key_buf));
536 global_rec = dbwrap_fetch_locked(table->global.db_ctx,
540 if (global_rec != NULL) {
541 status = dbwrap_record_delete(global_rec);
543 TALLOC_FREE(op->global);
548 NTSTATUS smbXsrv_open_create(struct smbXsrv_connection *conn,
550 struct smbXsrv_open **_open)
552 struct smbXsrv_open_table *table = &conn->open_table;
553 uint32_t max_opens = table->local.highest_id - table->local.lowest_id;
554 struct db_record *local_rec = NULL;
555 struct smbXsrv_open *op = NULL;
558 struct smbXsrv_open_global0 *global = NULL;
561 //system("sleep 999999");
563 if (table->local.num_opens >= max_opens) {
564 return NT_STATUS_INSUFFICIENT_RESOURCES;
565 // TODO smb1 return NT_STATUS_TOO_MANY_SESSIONS;
568 op = talloc_zero(conn, struct smbXsrv_open);
570 return NT_STATUS_NO_MEMORY;
572 op->connection = conn;
574 status = smbXsrv_open_global_allocate(table->global.db_ctx,
576 if (!NT_STATUS_IS_OK(status)) {
582 talloc_set_destructor(op, smbXsrv_open_destructor);
584 status = smbXsrv_open_local_allocate_id(table->local.db_ctx,
585 table->local.lowest_id,
586 table->local.highest_id,
590 if (!NT_STATUS_IS_OK(status)) {
595 uint64_t id = global->open_global_id;
597 global->open_persistent_id = id;
598 global->open_persistent_id |= (id << 32) & 0xFFFFFFFF00000000ULL;
601 uint64_t id = op->local_id;
603 global->open_volatile_id = id;
604 //global->open_volatile_id |= (id << 32) & 0xFFFFFFFF00000000ULL;
608 val = make_tdb_data((uint8_t const *)&ptr, sizeof(ptr));
609 status = dbwrap_record_store(local_rec, val, TDB_REPLACE);
610 TALLOC_FREE(local_rec);
611 if (!NT_STATUS_IS_OK(status)) {
616 global->open_time = now;
617 global->open_path = "TODO...";
619 status = smbXsrv_open_global_store(conn, global);
620 if (!NT_STATUS_IS_OK(status)) {
626 struct smbXsrv_openB open_blob;
628 ZERO_STRUCT(open_blob);
629 open_blob.version = 0;
630 open_blob.info.info0 = op;
632 DEBUG(0,("%s:%s:\n", __location__, __FUNCTION__));
633 NDR_PRINT_DEBUG(smbXsrv_openB, &open_blob);
640 NTSTATUS smbXsrv_open_update(struct smbXsrv_open *op)
642 struct smbXsrv_open_table *table = &op->connection->open_table;
644 uint8_t key_buf[sizeof(uint32_t)];
647 if (op->global->db_rec != NULL) {
648 return NT_STATUS_INTERNAL_ERROR;
651 RSIVAL(key_buf, 0, op->global->open_global_id);
652 key = make_tdb_data(key_buf, sizeof(key_buf));
654 op->global->db_rec = dbwrap_fetch_locked(table->global.db_ctx,
656 if (op->global->db_rec == NULL) {
657 // TODO proper return code?
658 return NT_STATUS_NO_MEMORY;
661 status = smbXsrv_open_global_store(op->connection,
663 if (!NT_STATUS_IS_OK(status)) {
669 struct smbXsrv_openB open_blob;
671 ZERO_STRUCT(open_blob);
672 open_blob.version = 0;
673 open_blob.info.info0 = op;
675 DEBUG(0,("%s:%s:\n", __location__, __FUNCTION__));
676 NDR_PRINT_DEBUG(smbXsrv_openB, &open_blob);
682 NTSTATUS smb1srv_open_table_init(struct smbXsrv_connection *conn)
684 return smbXsrv_open_table_init(conn, 1,
685 conn->sconn->real_max_open_files);
688 NTSTATUS smb1srv_open_lookup(struct smbXsrv_open_table *table,
689 uint16_t fnum, NTTIME now,
690 struct smbXsrv_open **_open)
692 uint32_t local_id = fnum;
694 return smbXsrv_open_local_lookup(table, local_id, now, _open);
697 NTSTATUS smb2srv_open_table_init(struct smbXsrv_connection *conn)
699 return smbXsrv_open_table_init(conn, 1,
700 conn->sconn->real_max_open_files);
703 NTSTATUS smb2srv_open_lookup(struct smbXsrv_open_table *table,
704 uint64_t volatile_id, NTTIME now,
705 struct smbXsrv_open **_open)
707 uint32_t local_id = volatile_id & UINT32_MAX;
709 return smbXsrv_open_local_lookup(table, local_id, now, _open);
712 NTSTATUS smb2srv_open_global_lookup(struct smbXsrv_open_table *table,
713 uint64_t persistent_id,
715 struct smbXsrv_open_global0 **_open)
717 uint32_t global_id = persistent_id & UINT32_MAX;
719 return smbXsrv_open_global_lookup(table, global_id, mem_ctx, _open);