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/>.
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_session_global_db_ctx = NULL;
37 NTSTATUS smbXsrv_session_global_init(void)
39 const char *global_path = NULL;
40 struct db_context *db_ctx = NULL;
42 if (smbXsrv_session_global_db_ctx != NULL) {
47 * This contains secret information like session keys!
49 global_path = lock_path("smbXsrv_session_global.tdb");
51 db_ctx = db_open(NULL, global_path,
55 TDB_INCOMPATIBLE_HASH,
56 O_RDWR | O_CREAT, 0600,
61 status = map_nt_error_from_unix_common(errno);
66 smbXsrv_session_global_db_ctx = db_ctx;
71 static NTSTATUS smbXsrv_session_table_init(struct smbXsrv_connection *conn,
75 struct smbXsrv_session_table *table = &conn->session_table;
79 table->local.db_ctx = db_open_rbt(conn);
80 if (table->local.db_ctx == NULL) {
81 return NT_STATUS_NO_MEMORY;
83 table->local.lowest_id = lowest_id;
84 table->local.highest_id = highest_id;
86 status = smbXsrv_session_global_init();
87 if (!NT_STATUS_IS_OK(status)) {
91 table->global.db_ctx = smbXsrv_session_global_db_ctx;
96 struct smb1srv_session_local_allocate_state {
97 const uint32_t lowest_id;
98 const uint32_t highest_id;
104 static int smb1srv_session_local_allocate_traverse(struct db_record *rec,
107 struct smb1srv_session_local_allocate_state *state =
108 (struct smb1srv_session_local_allocate_state *)private_data;
109 TDB_DATA key = dbwrap_record_get_key(rec);
112 if (key.dsize != sizeof(uint32_t)) {
114 state->status = NT_STATUS_INTERNAL_DB_CORRUPTION;
120 * We need big endian so that dbwrap_rbt's memcmp
121 * has the same result as integer comparison between the uint32_t
124 * TODO: implement string based key
126 id = RIVAL(key.dptr, 0);
128 if (id <= state->last_id) {
130 state->status = NT_STATUS_INTERNAL_DB_CORRUPTION;
135 if (id > state->useable_id) {
136 state->status = NT_STATUS_OK;
140 state->useable_id +=1;
144 static NTSTATUS smb1srv_session_local_allocate_id(struct db_context *db,
148 struct db_record **_rec,
151 struct smb1srv_session_local_allocate_state state = {
152 .lowest_id = lowest_id,
153 .highest_id = highest_id,
155 .useable_id = lowest_id,
156 .status = NT_STATUS_INTERNAL_ERROR,
166 if (lowest_id > highest_id) {
167 return NT_STATUS_INSUFFICIENT_RESOURCES;
170 range = (highest_id - lowest_id) + 1;
172 for (i = 0; i < range; i++) {
174 uint8_t key_buf[sizeof(uint32_t)];
177 struct db_record *rec = NULL;
179 id = generate_random() % range;
182 if (id < lowest_id) {
185 if (id > highest_id) {
189 RSIVAL(key_buf, 0, id);
190 key = make_tdb_data(key_buf, sizeof(key_buf));
192 rec = dbwrap_fetch_locked(db, mem_ctx, key);
194 return NT_STATUS_INSUFFICIENT_RESOURCES;
197 val = dbwrap_record_get_value(rec);
198 if (val.dsize != 0) {
208 status = dbwrap_traverse_read(db, smb1srv_session_local_allocate_traverse,
210 if (!NT_STATUS_EQUAL(status, NT_STATUS_INTERNAL_DB_CORRUPTION)) {
212 * Here we really expect NT_STATUS_INTERNAL_DB_CORRUPTION!
214 * If we get anything else it is an error, because it
215 * means we did not manage to find a free slot in
218 return NT_STATUS_INSUFFICIENT_RESOURCES;
221 if (NT_STATUS_IS_OK(state.status)) {
223 uint8_t key_buf[sizeof(uint32_t)];
226 struct db_record *rec = NULL;
228 id = state.useable_id;
230 RSIVAL(key_buf, 0, id);
231 key = make_tdb_data(key_buf, sizeof(key_buf));
233 rec = dbwrap_fetch_locked(db, mem_ctx, key);
235 return NT_STATUS_INSUFFICIENT_RESOURCES;
238 val = dbwrap_record_get_value(rec);
239 if (val.dsize != 0) {
241 return NT_STATUS_INTERNAL_DB_CORRUPTION;
252 struct smbXsrv_session_local_fetch_state {
253 struct smbXsrv_session *session;
257 static void smbXsrv_session_local_fetch_parser(TDB_DATA key, TDB_DATA data,
260 struct smbXsrv_session_local_fetch_state *state =
261 (struct smbXsrv_session_local_fetch_state *)private_data;
264 if (data.dsize != sizeof(ptr)) {
265 state->status = NT_STATUS_INTERNAL_DB_ERROR;
269 memcpy(&ptr, data.dptr, data.dsize);
270 state->session = talloc_get_type_abort(ptr, struct smbXsrv_session);
271 state->status = NT_STATUS_OK;
274 static NTSTATUS smbXsrv_session_local_lookup(struct smbXsrv_session_table *table,
275 uint32_t session_local_id,
277 struct smbXsrv_session **_session)
279 struct smbXsrv_session_local_fetch_state state = {
281 .status = NT_STATUS_INTERNAL_ERROR,
283 uint8_t key_buf[sizeof(uint32_t)];
289 if (table->local.db_ctx == NULL) {
290 return NT_STATUS_INTERNAL_ERROR;
293 RSIVAL(key_buf, 0, session_local_id);
294 key = make_tdb_data(key_buf, sizeof(key_buf));
296 status = dbwrap_parse_record(table->local.db_ctx, key,
297 smbXsrv_session_local_fetch_parser,
299 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
300 return NT_STATUS_USER_SESSION_DELETED;
301 } else if (!NT_STATUS_IS_OK(status)) {
304 if (!NT_STATUS_IS_OK(state.status)) {
308 if (!NT_STATUS_IS_OK(state.session->status)) {
309 *_session = state.session;
310 return state.session->status;
313 if (now > state.session->global->expiration_time) {
314 state.session->status = NT_STATUS_NETWORK_SESSION_EXPIRED;
317 *_session = state.session;
318 return state.session->status;
321 static int smbXsrv_session_global_destructor(struct smbXsrv_session_global0 *global)
326 static NTSTATUS smbXsrv_session_global_allocate(struct db_context *db,
328 struct smbXsrv_session_global0 **_global)
331 struct smbXsrv_session_global0 *global = NULL;
335 global = talloc_zero(mem_ctx, struct smbXsrv_session_global0);
336 if (global == NULL) {
337 return NT_STATUS_NO_MEMORY;
339 talloc_set_destructor(global, smbXsrv_session_global_destructor);
341 for (i = 0; i < UINT32_MAX; i++) {
343 uint8_t key_buf[sizeof(uint32_t)];
347 id = generate_random();
348 if (id >= UINT16_MAX) {
349 id = id & UINT16_MAX;
354 if (id == UINT32_MAX) {
358 RSIVAL(key_buf, 0, id);
359 key = make_tdb_data(key_buf, sizeof(key_buf));
361 global->db_rec = dbwrap_fetch_locked(db, mem_ctx, key);
362 if (global->db_rec == NULL) {
364 return NT_STATUS_INSUFFICIENT_RESOURCES;
367 val = dbwrap_record_get_value(global->db_rec);
368 if (val.dsize != 0) {
369 TALLOC_FREE(global->db_rec);
373 global->session_global_id = id;
379 /* should not be reached */
381 return NT_STATUS_INTERNAL_ERROR;
384 static NTSTATUS smbXsrv_session_global_store(struct smbXsrv_connection *sconn,
385 struct smbXsrv_session_global0 *global)
387 struct smbXsrv_session_globalB global_blob;
388 DATA_BLOB blob = data_blob_null;
391 enum ndr_err_code ndr_err;
394 * TODO: if we use other versions than '0'
395 * we would add glue code here, that would be able to
396 * store the information in the old format.
399 if (global->db_rec == NULL) {
400 return NT_STATUS_INTERNAL_ERROR;
403 val = dbwrap_record_get_value(global->db_rec);
405 ZERO_STRUCT(global_blob);
406 global_blob.version = 0;
407 if (val.dsize >= 8) {
408 global_blob.seqnum = IVAL(val.dptr, 4);
410 global_blob.seqnum += 1;
411 global_blob.info.info0 = global;
413 ndr_err = ndr_push_struct_blob(&blob, global->db_rec, &global_blob,
414 (ndr_push_flags_fn_t)ndr_push_smbXsrv_session_globalB);
415 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
416 //status = ndr_err_code;
417 TALLOC_FREE(global->db_rec);
421 val = make_tdb_data(blob.data, blob.length);
422 status = dbwrap_record_store(global->db_rec, val, TDB_REPLACE);
423 TALLOC_FREE(global->db_rec);
424 if (!NT_STATUS_IS_OK(status)) {
431 struct smbXsrv_session_global_fetch_state {
433 struct smbXsrv_session_global *session;
437 static void smbXsrv_session_global_fetch_parser(TDB_DATA key, TDB_DATA data,
440 struct smbXsrv_session_global_fetch_state *state =
441 (struct smbXsrv_session_global_fetch_state *)private_data;
443 state->status = NT_STATUS_NOT_IMPLEMENTED;
446 static NTSTATUS smbXsrv_session_global_lookup(struct smbXsrv_session_table *table,
447 uint32_t session_global_id,
449 struct smbXsrv_session_global **_session)
451 struct smbXsrv_session_global_fetch_state state = {
454 .status = NT_STATUS_INTERNAL_ERROR,
457 uint8_t key_buf[sizeof(uint32_t)];
462 if (table->global.db_ctx == NULL) {
463 return NT_STATUS_INTERNAL_ERROR;
466 /* TODO: key as string */
467 RSIVAL(key_buf, 0, session_global_id);
468 key = make_tdb_data(key_buf, sizeof(key_buf));
470 status = dbwrap_parse_record(table->global.db_ctx, key,
471 smbXsrv_session_global_fetch_parser, &state);
472 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
473 return NT_STATUS_USER_SESSION_DELETED;
474 } else if (!NT_STATUS_IS_OK(status)) {
477 if (!NT_STATUS_IS_OK(state.status)) {
481 *_session = state.session;
485 static int smbXsrv_session_destructor(struct smbXsrv_session *session)
487 struct smbXsrv_session_table *table;
488 struct db_record *local_rec = NULL;
489 struct db_record *global_rec = NULL;
492 if (session->connection == NULL) {
496 table = &session->connection->session_table;
497 session->connection = NULL;
499 local_rec = session->db_rec;
500 session->db_rec = NULL;
501 if (local_rec == NULL) {
502 uint8_t key_buf[sizeof(uint32_t)];
505 RSIVAL(key_buf, 0, session->local_id);
506 key = make_tdb_data(key_buf, sizeof(key_buf));
508 local_rec = dbwrap_fetch_locked(table->local.db_ctx,
512 if (local_rec != NULL) {
513 status = dbwrap_record_delete(local_rec);
516 global_rec = session->global->db_rec;
517 session->global->db_rec = NULL;
518 if (global_rec == NULL) {
519 uint8_t key_buf[sizeof(uint32_t)];
522 RSIVAL(key_buf, 0, session->global->session_global_id);
523 key = make_tdb_data(key_buf, sizeof(key_buf));
525 global_rec = dbwrap_fetch_locked(table->global.db_ctx,
526 session->global, key);
529 if (global_rec != NULL) {
530 status = dbwrap_record_delete(global_rec);
532 TALLOC_FREE(session->global);
537 NTSTATUS smbXsrv_session_create(struct smbXsrv_connection *conn,
539 struct smbXsrv_session **_session)
541 struct smbXsrv_session_table *table = &conn->session_table;
542 uint32_t max_sessions = table->local.highest_id - table->local.lowest_id;
543 struct db_record *local_rec = NULL;
544 struct smbXsrv_session *session = NULL;
547 struct smbXsrv_session_global0 *global = NULL;
548 struct smbXsrv_channel_global0 *channels = NULL;
551 //system("sleep 999999");
553 if (table->local.num_sessions >= max_sessions) {
554 return NT_STATUS_INSUFFICIENT_RESOURCES;
555 // TODO smb1 return NT_STATUS_TOO_MANY_SESSIONS;
558 session = talloc_zero(conn, struct smbXsrv_session);
559 if (session == NULL) {
560 return NT_STATUS_NO_MEMORY;
562 session->status = NT_STATUS_MORE_PROCESSING_REQUIRED;
563 session->connection = conn;
565 status = smbXsrv_session_global_allocate(table->global.db_ctx,
568 if (!NT_STATUS_IS_OK(status)) {
569 talloc_free(session);
572 session->global = global;
574 talloc_set_destructor(session, smbXsrv_session_destructor);
576 if (conn->protocol >= PROTOCOL_SMB2_02) {
577 uint64_t id = global->session_global_id;
578 uint8_t key_buf[sizeof(uint32_t)];
581 global->session_wire_id = id;
582 //global->session_wire_id |= (id << 32) & 0xFFFFFFFF00000000ULL;
584 session->local_id = global->session_global_id;
586 RSIVAL(key_buf, 0, session->local_id);
587 key = make_tdb_data(key_buf, sizeof(key_buf));
589 local_rec = dbwrap_fetch_locked(table->local.db_ctx,
591 if (local_rec == NULL) {
592 return NT_STATUS_NO_MEMORY;
595 val = dbwrap_record_get_value(local_rec);
596 if (val.dsize != 0) {
597 return NT_STATUS_INTERNAL_DB_CORRUPTION;
601 status = smb1srv_session_local_allocate_id(table->local.db_ctx,
602 table->local.lowest_id,
603 table->local.highest_id,
607 if (!NT_STATUS_IS_OK(status)) {
611 global->session_wire_id = session->local_id;
615 val = make_tdb_data((uint8_t const *)&ptr, sizeof(ptr));
616 status = dbwrap_record_store(local_rec, val, TDB_REPLACE);
617 TALLOC_FREE(local_rec);
618 if (!NT_STATUS_IS_OK(status)) {
619 talloc_free(session);
623 global->creation_time = now;
624 global->expiration_time = UINT64_MAX;//NTTIME_INFINITY;
626 global->num_channels = 1;
627 channels = talloc_zero_array(global,
628 struct smbXsrv_channel_global0,
629 global->num_channels);
630 if (channels == NULL) {
631 talloc_free(session);
632 return NT_STATUS_NO_MEMORY;
634 global->channels = channels;
636 channels[0].server_id = messaging_server_id(conn->msg_ctx);
637 channels[0].local_address = tsocket_address_string(conn->local_address,
639 if (channels[0].local_address == NULL) {
640 talloc_free(session);
641 return NT_STATUS_NO_MEMORY;
643 channels[0].remote_address = tsocket_address_string(conn->remote_address,
645 if (channels[0].remote_address == NULL) {
646 talloc_free(session);
647 return NT_STATUS_NO_MEMORY;
649 channels[0].remote_name = talloc_strdup(channels, conn->remote_hostname);
650 if (channels[0].remote_name == NULL) {
651 talloc_free(session);
652 return NT_STATUS_NO_MEMORY;
654 channels[0].signing_key = data_blob_null;
656 status = smbXsrv_session_global_store(conn,
658 if (!NT_STATUS_IS_OK(status)) {
659 talloc_free(session);
664 struct smbXsrv_sessionB session_blob;
666 ZERO_STRUCT(session_blob);
667 session_blob.version = 0;
668 session_blob.info.info0 = session;
670 NDR_PRINT_DEBUG(smbXsrv_sessionB, &session_blob);
677 NTSTATUS smbXsrv_session_update(struct smbXsrv_session *session)
679 struct smbXsrv_session_table *table = &session->connection->session_table;
681 uint8_t key_buf[sizeof(uint32_t)];
684 if (session->global->db_rec != NULL) {
685 return NT_STATUS_INTERNAL_ERROR;
688 RSIVAL(key_buf, 0, session->global->session_global_id);
689 key = make_tdb_data(key_buf, sizeof(key_buf));
691 session->global->db_rec = dbwrap_fetch_locked(table->global.db_ctx,
692 session->global, key);
693 if (session->global->db_rec == NULL) {
694 // TODO proper return code?
695 return NT_STATUS_NO_MEMORY;
698 status = smbXsrv_session_global_store(session->connection,
700 if (!NT_STATUS_IS_OK(status)) {
708 NTSTATUS smb1srv_session_table_init(struct smbXsrv_connection *conn)
711 * Allow a range from 100..65534.
713 return smbXsrv_session_table_init(conn, 100, UINT16_MAX - 1);
716 NTSTATUS smb1srv_session_lookup(struct smbXsrv_session_table *table,
717 uint16_t vuid, NTTIME now,
718 struct smbXsrv_session **session)
720 uint32_t local_id = vuid;
722 return smbXsrv_session_local_lookup(table, local_id, now, session);
725 NTSTATUS smb2srv_session_table_init(struct smbXsrv_connection *conn)
728 * For now use the same range as SMB1.
730 * Allow a range from 100..65534.
732 return smbXsrv_session_table_init(conn, 100, UINT16_MAX - 1);
735 NTSTATUS smb2srv_session_lookup(struct smbXsrv_session_table *table,
736 uint64_t session_id, NTTIME now,
737 struct smbXsrv_session **session)
739 uint32_t local_id = session_id & UINT32_MAX;
741 return smbXsrv_session_local_lookup(table, local_id, now, session);