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 smb1srv_open_local_allocate_state {
94 const uint32_t lowest_id;
95 const uint32_t highest_id;
101 static int smb1srv_open_local_allocate_traverse(struct db_record *rec,
104 struct smb1srv_open_local_allocate_state *state =
105 (struct smb1srv_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 smb1srv_open_local_allocate_id(struct db_context *db,
145 struct db_record **_rec,
148 struct smb1srv_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, smb1srv_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 *open;
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->open = 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();
336 if (id >= UINT16_MAX) {
337 id = id & UINT16_MAX;
342 if (id == UINT32_MAX) {
346 RSIVAL(key_buf, 0, id);
347 key = make_tdb_data(key_buf, sizeof(key_buf));
349 global->db_rec = dbwrap_fetch_locked(db, mem_ctx, key);
350 if (global->db_rec == NULL) {
352 return NT_STATUS_INSUFFICIENT_RESOURCES;
355 val = dbwrap_record_get_value(global->db_rec);
356 if (val.dsize != 0) {
357 TALLOC_FREE(global->db_rec);
361 global->open_global_id = id;
367 /* should not be reached */
369 return NT_STATUS_INTERNAL_ERROR;
372 static NTSTATUS smbXsrv_open_global_store(struct smbXsrv_connection *sconn,
373 struct smbXsrv_open_global0 *global)
375 struct smbXsrv_open_globalB global_blob;
376 DATA_BLOB blob = data_blob_null;
379 enum ndr_err_code ndr_err;
382 * TODO: if we use other versions than '0'
383 * we would add glue code here, that would be able to
384 * store the information in the old format.
387 if (global->db_rec == NULL) {
388 return NT_STATUS_INTERNAL_ERROR;
391 val = dbwrap_record_get_value(global->db_rec);
393 ZERO_STRUCT(global_blob);
394 global_blob.version = 0;
395 if (val.dsize >= 8) {
396 global_blob.seqnum = IVAL(val.dptr, 4);
398 global_blob.seqnum += 1;
399 global_blob.info.info0 = global;
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);
409 val = make_tdb_data(blob.data, blob.length);
410 status = dbwrap_record_store(global->db_rec, val, TDB_REPLACE);
411 TALLOC_FREE(global->db_rec);
412 if (!NT_STATUS_IS_OK(status)) {
419 struct smbXsrv_open_global_fetch_state {
421 struct smbXsrv_open_global *open;
425 static void smbXsrv_open_global_fetch_parser(TDB_DATA key, TDB_DATA data,
428 struct smbXsrv_open_global_fetch_state *state =
429 (struct smbXsrv_open_global_fetch_state *)private_data;
431 state->status = NT_STATUS_NOT_IMPLEMENTED;
434 static NTSTATUS smbXsrv_open_global_lookup(struct smbXsrv_open_table *table,
435 uint32_t open_global_id,
437 struct smbXsrv_open_global **_open)
439 struct smbXsrv_open_global_fetch_state state = {
442 .status = NT_STATUS_INTERNAL_ERROR,
445 uint8_t key_buf[sizeof(uint32_t)];
450 if (table->global.db_ctx == NULL) {
451 return NT_STATUS_INTERNAL_ERROR;
454 /* TODO: key as string */
455 RSIVAL(key_buf, 0, open_global_id);
456 key = make_tdb_data(key_buf, sizeof(key_buf));
458 status = dbwrap_parse_record(table->global.db_ctx, key,
459 smbXsrv_open_global_fetch_parser, &state);
460 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
461 return NT_STATUS_USER_SESSION_DELETED;
462 } else if (!NT_STATUS_IS_OK(status)) {
465 if (!NT_STATUS_IS_OK(state.status)) {
473 static int smbXsrv_open_destructor(struct smbXsrv_open *open)
475 struct smbXsrv_open_table *table;
476 struct db_record *local_rec = NULL;
477 struct db_record *global_rec = NULL;
480 if (open->connection == NULL) {
484 table = &open->connection->open_table;
485 open->connection = NULL;
487 local_rec = open->db_rec;
489 if (local_rec == NULL) {
490 uint8_t key_buf[sizeof(uint32_t)];
493 RSIVAL(key_buf, 0, open->local_id);
494 key = make_tdb_data(key_buf, sizeof(key_buf));
496 local_rec = dbwrap_fetch_locked(table->local.db_ctx,
500 if (local_rec != NULL) {
501 status = dbwrap_record_delete(local_rec);
504 global_rec = open->global->db_rec;
505 open->global->db_rec = NULL;
506 if (global_rec == NULL) {
507 uint8_t key_buf[sizeof(uint32_t)];
510 RSIVAL(key_buf, 0, open->global->open_global_id);
511 key = make_tdb_data(key_buf, sizeof(key_buf));
513 global_rec = dbwrap_fetch_locked(table->global.db_ctx,
517 if (global_rec != NULL) {
518 status = dbwrap_record_delete(global_rec);
520 TALLOC_FREE(open->global);
525 NTSTATUS smbXsrv_open_create(struct smbXsrv_connection *conn,
527 struct smbXsrv_open **_open)
529 struct smbXsrv_open_table *table = &conn->open_table;
530 uint32_t max_opens = table->local.highest_id - table->local.lowest_id;
531 struct db_record *local_rec = NULL;
532 struct smbXsrv_open *open = NULL;
535 struct smbXsrv_open_global0 *global = NULL;
538 //system("sleep 999999");
540 if (table->local.num_opens >= max_opens) {
541 return NT_STATUS_INSUFFICIENT_RESOURCES;
542 // TODO smb1 return NT_STATUS_TOO_MANY_SESSIONS;
545 open = talloc_zero(conn, struct smbXsrv_open);
547 return NT_STATUS_NO_MEMORY;
549 open->connection = conn;
551 status = smbXsrv_open_global_allocate(table->global.db_ctx,
553 if (!NT_STATUS_IS_OK(status)) {
557 open->global = global;
559 talloc_set_destructor(open, smbXsrv_open_destructor);
561 if (conn->protocol >= PROTOCOL_SMB2_02) {
562 uint64_t id = global->open_global_id;
563 uint8_t key_buf[sizeof(uint32_t)];
566 global->open_persistent_id = id;
567 //global->open_persistent_id |= (id << 32) & 0xFFFFFFFF00000000ULL;
569 open->local_id = global->open_global_id;
571 RSIVAL(key_buf, 0, open->local_id);
572 key = make_tdb_data(key_buf, sizeof(key_buf));
574 local_rec = dbwrap_fetch_locked(table->local.db_ctx,
576 if (local_rec == NULL) {
577 return NT_STATUS_NO_MEMORY;
580 val = dbwrap_record_get_value(local_rec);
581 if (val.dsize != 0) {
582 return NT_STATUS_INTERNAL_DB_CORRUPTION;
586 status = smb1srv_open_local_allocate_id(table->local.db_ctx,
587 table->local.lowest_id,
588 table->local.highest_id,
592 if (!NT_STATUS_IS_OK(status)) {
596 global->open_persistent_id = open->local_id;
600 val = make_tdb_data((uint8_t const *)&ptr, sizeof(ptr));
601 status = dbwrap_record_store(local_rec, val, TDB_REPLACE);
602 TALLOC_FREE(local_rec);
603 if (!NT_STATUS_IS_OK(status)) {
608 global->creation_time = now;
610 status = smbXsrv_open_global_store(conn, global);
611 if (!NT_STATUS_IS_OK(status)) {
617 struct smbXsrv_openB open_blob;
619 ZERO_STRUCT(open_blob);
620 open_blob.version = 0;
621 open_blob.info.info0 = open;
623 NDR_PRINT_DEBUG(smbXsrv_openB, &open_blob);
630 NTSTATUS smbXsrv_open_update(struct smbXsrv_open *open)
632 struct smbXsrv_open_table *table = &open->connection->open_table;
634 uint8_t key_buf[sizeof(uint32_t)];
637 if (open->global->db_rec != NULL) {
638 return NT_STATUS_INTERNAL_ERROR;
641 RSIVAL(key_buf, 0, open->global->open_global_id);
642 key = make_tdb_data(key_buf, sizeof(key_buf));
644 open->global->db_rec = dbwrap_fetch_locked(table->global.db_ctx,
646 if (open->global->db_rec == NULL) {
647 // TODO proper return code?
648 return NT_STATUS_NO_MEMORY;
651 status = smbXsrv_open_global_store(open->connection,
653 if (!NT_STATUS_IS_OK(status)) {
661 NTSTATUS smb1srv_open_table_init(struct smbXsrv_connection *conn)
664 * Allow a range from 100..65534.
666 return smbXsrv_open_table_init(conn, 100, UINT16_MAX - 1);
669 NTSTATUS smb1srv_open_lookup(struct smbXsrv_open_table *table,
670 uint16_t vuid, NTTIME now,
671 struct smbXsrv_open **open)
673 uint32_t local_id = vuid;
675 return smbXsrv_open_local_lookup(table, local_id, now, open);
678 NTSTATUS smb2srv_open_table_init(struct smbXsrv_connection *conn)
681 * For now use the same range as SMB1.
683 * Allow a range from 100..65534.
685 return smbXsrv_open_table_init(conn, 100, UINT16_MAX - 1);
688 NTSTATUS smb2srv_open_lookup(struct smbXsrv_open_table *table,
689 uint64_t open_id, NTTIME now,
690 struct smbXsrv_open **open)
692 uint32_t local_id = open_id & UINT32_MAX;
694 return smbXsrv_open_local_lookup(table, local_id, now, open);