ddd03f610906598e3ff794fa7fa0a7e05f11e046
[samba.git] / source3 / smbd / smbXsrv_tcon.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    Copyright (C) Stefan Metzmacher 2011-2012
5    Copyright (C) Michael Adam 2012
6
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.
11
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.
16
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/>.
19 */
20
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"
32
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;
44 };
45
46 static struct db_context *smbXsrv_tcon_global_db_ctx = NULL;
47
48 NTSTATUS smbXsrv_tcon_global_init(void)
49 {
50         char *global_path = NULL;
51         struct db_context *db_ctx = NULL;
52
53         if (smbXsrv_tcon_global_db_ctx != NULL) {
54                 return NT_STATUS_OK;
55         }
56
57         global_path = lock_path("smbXsrv_tcon_global.tdb");
58         if (global_path == NULL) {
59                 return NT_STATUS_NO_MEMORY;
60         }
61
62         db_ctx = db_open(NULL, global_path,
63                          0, /* hash_size */
64                          TDB_DEFAULT |
65                          TDB_CLEAR_IF_FIRST |
66                          TDB_INCOMPATIBLE_HASH,
67                          O_RDWR | O_CREAT, 0600,
68                          DBWRAP_LOCK_ORDER_1,
69                          DBWRAP_FLAG_NONE);
70         TALLOC_FREE(global_path);
71         if (db_ctx == NULL) {
72                 NTSTATUS status;
73
74                 status = map_nt_error_from_unix_common(errno);
75
76                 return status;
77         }
78
79         smbXsrv_tcon_global_db_ctx = db_ctx;
80
81         return NT_STATUS_OK;
82 }
83
84 /*
85  * NOTE:
86  * We need to store the keys in big endian so that dbwrap_rbt's memcmp
87  * has the same result as integer comparison between the uint32_t
88  * values.
89  *
90  * TODO: implement string based key
91  */
92
93 #define SMBXSRV_TCON_GLOBAL_TDB_KEY_SIZE sizeof(uint32_t)
94
95 static TDB_DATA smbXsrv_tcon_global_id_to_key(uint32_t id,
96                                               uint8_t *key_buf)
97 {
98         TDB_DATA key;
99
100         RSIVAL(key_buf, 0, id);
101
102         key = make_tdb_data(key_buf, SMBXSRV_TCON_GLOBAL_TDB_KEY_SIZE);
103
104         return key;
105 }
106
107 #if 0
108 static NTSTATUS smbXsrv_tcon_global_key_to_id(TDB_DATA key, uint32_t *id)
109 {
110         if (id == NULL) {
111                 return NT_STATUS_INVALID_PARAMETER;
112         }
113
114         if (key.dsize != SMBXSRV_TCON_GLOBAL_TDB_KEY_SIZE) {
115                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
116         }
117
118         *id = RIVAL(key.dptr, 0);
119
120         return NT_STATUS_OK;
121 }
122 #endif
123
124 #define SMBXSRV_TCON_LOCAL_TDB_KEY_SIZE sizeof(uint32_t)
125
126 static TDB_DATA smbXsrv_tcon_local_id_to_key(uint32_t id,
127                                              uint8_t *key_buf)
128 {
129         TDB_DATA key;
130
131         RSIVAL(key_buf, 0, id);
132
133         key = make_tdb_data(key_buf, SMBXSRV_TCON_LOCAL_TDB_KEY_SIZE);
134
135         return key;
136 }
137
138 static NTSTATUS smbXsrv_tcon_local_key_to_id(TDB_DATA key, uint32_t *id)
139 {
140         if (id == NULL) {
141                 return NT_STATUS_INVALID_PARAMETER;
142         }
143
144         if (key.dsize != SMBXSRV_TCON_LOCAL_TDB_KEY_SIZE) {
145                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
146         }
147
148         *id = RIVAL(key.dptr, 0);
149
150         return NT_STATUS_OK;
151 }
152
153 static struct db_record *smbXsrv_tcon_global_fetch_locked(
154                         struct db_context *db,
155                         uint32_t id,
156                         TALLOC_CTX *mem_ctx)
157 {
158         TDB_DATA key;
159         uint8_t key_buf[SMBXSRV_TCON_GLOBAL_TDB_KEY_SIZE];
160         struct db_record *rec = NULL;
161
162         key = smbXsrv_tcon_global_id_to_key(id, key_buf);
163
164         rec = dbwrap_fetch_locked(db, mem_ctx, key);
165
166         if (rec == NULL) {
167                 DBG_DEBUG("Failed to lock global id 0x%08x, key '%s'\n", id,
168                           hex_encode_talloc(talloc_tos(), key.dptr, key.dsize));
169         }
170
171         return rec;
172 }
173
174 static struct db_record *smbXsrv_tcon_local_fetch_locked(
175                         struct db_context *db,
176                         uint32_t id,
177                         TALLOC_CTX *mem_ctx)
178 {
179         TDB_DATA key;
180         uint8_t key_buf[SMBXSRV_TCON_LOCAL_TDB_KEY_SIZE];
181         struct db_record *rec = NULL;
182
183         key = smbXsrv_tcon_local_id_to_key(id, key_buf);
184
185         rec = dbwrap_fetch_locked(db, mem_ctx, key);
186
187         if (rec == NULL) {
188                 DBG_DEBUG("Failed to lock local id 0x%08x, key '%s'\n", id,
189                           hex_encode_talloc(talloc_tos(), key.dptr, key.dsize));
190         }
191
192         return rec;
193 }
194
195 static NTSTATUS smbXsrv_tcon_table_init(TALLOC_CTX *mem_ctx,
196                                         struct smbXsrv_tcon_table *table,
197                                         uint32_t lowest_id,
198                                         uint32_t highest_id,
199                                         uint32_t max_tcons)
200 {
201         NTSTATUS status;
202         uint64_t max_range;
203
204         if (lowest_id > highest_id) {
205                 return NT_STATUS_INTERNAL_ERROR;
206         }
207
208         max_range = highest_id;
209         max_range -= lowest_id;
210         max_range += 1;
211
212         if (max_tcons > max_range) {
213                 return NT_STATUS_INTERNAL_ERROR;
214         }
215
216         ZERO_STRUCTP(table);
217         table->local.db_ctx = db_open_rbt(table);
218         if (table->local.db_ctx == NULL) {
219                 return NT_STATUS_NO_MEMORY;
220         }
221         table->local.lowest_id = lowest_id;
222         table->local.highest_id = highest_id;
223         table->local.max_tcons = max_tcons;
224
225         status = smbXsrv_tcon_global_init();
226         if (!NT_STATUS_IS_OK(status)) {
227                 return status;
228         }
229
230         table->global.db_ctx = smbXsrv_tcon_global_db_ctx;
231
232         return NT_STATUS_OK;
233 }
234
235 struct smb1srv_tcon_local_allocate_state {
236         const uint32_t lowest_id;
237         const uint32_t highest_id;
238         uint32_t last_id;
239         uint32_t useable_id;
240         NTSTATUS status;
241 };
242
243 static int smb1srv_tcon_local_allocate_traverse(struct db_record *rec,
244                                                    void *private_data)
245 {
246         struct smb1srv_tcon_local_allocate_state *state =
247                 (struct smb1srv_tcon_local_allocate_state *)private_data;
248         TDB_DATA key = dbwrap_record_get_key(rec);
249         uint32_t id = 0;
250         NTSTATUS status;
251
252         status = smbXsrv_tcon_local_key_to_id(key, &id);
253         if (!NT_STATUS_IS_OK(status)) {
254                 state->status = status;
255                 return -1;
256         }
257
258         if (id <= state->last_id) {
259                 state->status = NT_STATUS_INTERNAL_DB_CORRUPTION;
260                 return -1;
261         }
262         state->last_id = id;
263
264         if (id > state->useable_id) {
265                 state->status = NT_STATUS_OK;
266                 return -1;
267         }
268
269         if (state->useable_id == state->highest_id) {
270                 state->status = NT_STATUS_INSUFFICIENT_RESOURCES;
271                 return -1;
272         }
273
274         state->useable_id +=1;
275         return 0;
276 }
277
278 static NTSTATUS smb1srv_tcon_local_allocate_id(struct db_context *db,
279                                                uint32_t lowest_id,
280                                                uint32_t highest_id,
281                                                TALLOC_CTX *mem_ctx,
282                                                struct db_record **_rec,
283                                                uint32_t *_id)
284 {
285         struct smb1srv_tcon_local_allocate_state state = {
286                 .lowest_id = lowest_id,
287                 .highest_id = highest_id,
288                 .last_id = 0,
289                 .useable_id = lowest_id,
290                 .status = NT_STATUS_INTERNAL_ERROR,
291         };
292         uint32_t i;
293         uint32_t range;
294         NTSTATUS status;
295         int count = 0;
296
297         *_rec = NULL;
298         *_id = 0;
299
300         if (lowest_id > highest_id) {
301                 return NT_STATUS_INSUFFICIENT_RESOURCES;
302         }
303
304         /*
305          * first we try randomly
306          */
307         range = (highest_id - lowest_id) + 1;
308
309         for (i = 0; i < (range / 2); i++) {
310                 uint32_t id;
311                 TDB_DATA val;
312                 struct db_record *rec = NULL;
313
314                 id = generate_random() % range;
315                 id += lowest_id;
316
317                 if (id < lowest_id) {
318                         id = lowest_id;
319                 }
320                 if (id > highest_id) {
321                         id = highest_id;
322                 }
323
324                 rec = smbXsrv_tcon_local_fetch_locked(db, id, mem_ctx);
325                 if (rec == NULL) {
326                         return NT_STATUS_INSUFFICIENT_RESOURCES;
327                 }
328
329                 val = dbwrap_record_get_value(rec);
330                 if (val.dsize != 0) {
331                         TALLOC_FREE(rec);
332                         continue;
333                 }
334
335                 *_rec = rec;
336                 *_id = id;
337                 return NT_STATUS_OK;
338         }
339
340         /*
341          * if the range is almost full,
342          * we traverse the whole table
343          * (this relies on sorted behavior of dbwrap_rbt)
344          */
345         status = dbwrap_traverse_read(db, smb1srv_tcon_local_allocate_traverse,
346                                       &state, &count);
347         if (NT_STATUS_IS_OK(status)) {
348                 if (NT_STATUS_IS_OK(state.status)) {
349                         return NT_STATUS_INTERNAL_ERROR;
350                 }
351
352                 if (!NT_STATUS_EQUAL(state.status, NT_STATUS_INTERNAL_ERROR)) {
353                         return state.status;
354                 }
355
356                 if (state.useable_id <= state.highest_id) {
357                         state.status = NT_STATUS_OK;
358                 } else {
359                         return NT_STATUS_INSUFFICIENT_RESOURCES;
360                 }
361         } else if (!NT_STATUS_EQUAL(status, NT_STATUS_INTERNAL_DB_CORRUPTION)) {
362                 /*
363                  * Here we really expect NT_STATUS_INTERNAL_DB_CORRUPTION!
364                  *
365                  * If we get anything else it is an error, because it
366                  * means we did not manage to find a free slot in
367                  * the db.
368                  */
369                 return NT_STATUS_INSUFFICIENT_RESOURCES;
370         }
371
372         if (NT_STATUS_IS_OK(state.status)) {
373                 uint32_t id;
374                 TDB_DATA val;
375                 struct db_record *rec = NULL;
376
377                 id = state.useable_id;
378
379                 rec = smbXsrv_tcon_local_fetch_locked(db, id, mem_ctx);
380                 if (rec == NULL) {
381                         return NT_STATUS_INSUFFICIENT_RESOURCES;
382                 }
383
384                 val = dbwrap_record_get_value(rec);
385                 if (val.dsize != 0) {
386                         TALLOC_FREE(rec);
387                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
388                 }
389
390                 *_rec = rec;
391                 *_id = id;
392                 return NT_STATUS_OK;
393         }
394
395         return state.status;
396 }
397
398 struct smbXsrv_tcon_local_fetch_state {
399         struct smbXsrv_tcon *tcon;
400         NTSTATUS status;
401 };
402
403 static void smbXsrv_tcon_local_fetch_parser(TDB_DATA key, TDB_DATA data,
404                                             void *private_data)
405 {
406         struct smbXsrv_tcon_local_fetch_state *state =
407                 (struct smbXsrv_tcon_local_fetch_state *)private_data;
408         void *ptr;
409
410         if (data.dsize != sizeof(ptr)) {
411                 state->status = NT_STATUS_INTERNAL_DB_ERROR;
412                 return;
413         }
414
415         memcpy(&ptr, data.dptr, data.dsize);
416         state->tcon = talloc_get_type_abort(ptr, struct smbXsrv_tcon);
417         state->status = NT_STATUS_OK;
418 }
419
420 static NTSTATUS smbXsrv_tcon_local_lookup(struct smbXsrv_tcon_table *table,
421                                           uint32_t tcon_local_id,
422                                           NTTIME now,
423                                           struct smbXsrv_tcon **_tcon)
424 {
425         struct smbXsrv_tcon_local_fetch_state state = {
426                 .tcon = NULL,
427                 .status = NT_STATUS_INTERNAL_ERROR,
428         };
429         uint8_t key_buf[SMBXSRV_TCON_LOCAL_TDB_KEY_SIZE];
430         TDB_DATA key;
431         NTSTATUS status;
432
433         *_tcon = NULL;
434
435         if (tcon_local_id == 0) {
436                 return NT_STATUS_NETWORK_NAME_DELETED;
437         }
438
439         if (table == NULL) {
440                 /* this might happen before the end of negprot */
441                 return NT_STATUS_NETWORK_NAME_DELETED;
442         }
443
444         if (table->local.db_ctx == NULL) {
445                 return NT_STATUS_INTERNAL_ERROR;
446         }
447
448         key = smbXsrv_tcon_local_id_to_key(tcon_local_id, key_buf);
449
450         status = dbwrap_parse_record(table->local.db_ctx, key,
451                                      smbXsrv_tcon_local_fetch_parser,
452                                      &state);
453         if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
454                 return NT_STATUS_NETWORK_NAME_DELETED;
455         } else if (!NT_STATUS_IS_OK(status)) {
456                 return status;
457         }
458         if (!NT_STATUS_IS_OK(state.status)) {
459                 return state.status;
460         }
461
462         if (NT_STATUS_EQUAL(state.tcon->status, NT_STATUS_NETWORK_NAME_DELETED)) {
463                 return NT_STATUS_NETWORK_NAME_DELETED;
464         }
465
466         state.tcon->idle_time = now;
467
468         *_tcon = state.tcon;
469         return state.tcon->status;
470 }
471
472 static int smbXsrv_tcon_global_destructor(struct smbXsrv_tcon_global0 *global)
473 {
474         return 0;
475 }
476
477 static void smbXsrv_tcon_global_verify_record(struct db_record *db_rec,
478                                         bool *is_free,
479                                         bool *was_free,
480                                         TALLOC_CTX *mem_ctx,
481                                         struct smbXsrv_tcon_global0 **_g);
482
483 static NTSTATUS smbXsrv_tcon_global_allocate(struct db_context *db,
484                                         TALLOC_CTX *mem_ctx,
485                                         struct smbXsrv_tcon_global0 **_global)
486 {
487         uint32_t i;
488         struct smbXsrv_tcon_global0 *global = NULL;
489         uint32_t last_free = 0;
490         const uint32_t min_tries = 3;
491
492         *_global = NULL;
493
494         global = talloc_zero(mem_ctx, struct smbXsrv_tcon_global0);
495         if (global == NULL) {
496                 return NT_STATUS_NO_MEMORY;
497         }
498         talloc_set_destructor(global, smbXsrv_tcon_global_destructor);
499
500         /*
501          * Here we just randomly try the whole 32-bit space
502          *
503          * We use just 32-bit, because we want to reuse the
504          * ID for SRVSVC.
505          */
506         for (i = 0; i < UINT32_MAX; i++) {
507                 bool is_free = false;
508                 bool was_free = false;
509                 uint32_t id;
510
511                 if (i >= min_tries && last_free != 0) {
512                         id = last_free;
513                 } else {
514                         id = generate_random();
515                 }
516                 if (id == 0) {
517                         id++;
518                 }
519                 if (id == UINT32_MAX) {
520                         id--;
521                 }
522
523                 global->db_rec = smbXsrv_tcon_global_fetch_locked(db, id,
524                                                                   mem_ctx);
525                 if (global->db_rec == NULL) {
526                         talloc_free(global);
527                         return NT_STATUS_INSUFFICIENT_RESOURCES;
528                 }
529
530                 smbXsrv_tcon_global_verify_record(global->db_rec,
531                                                   &is_free,
532                                                   &was_free,
533                                                   NULL, NULL);
534
535                 if (!is_free) {
536                         TALLOC_FREE(global->db_rec);
537                         continue;
538                 }
539
540                 if (!was_free && i < min_tries) {
541                         /*
542                          * The session_id is free now,
543                          * but was not free before.
544                          *
545                          * This happens if a smbd crashed
546                          * and did not cleanup the record.
547                          *
548                          * If this is one of our first tries,
549                          * then we try to find a real free one.
550                          */
551                         if (last_free == 0) {
552                                 last_free = id;
553                         }
554                         TALLOC_FREE(global->db_rec);
555                         continue;
556                 }
557
558                 global->tcon_global_id = id;
559
560                 *_global = global;
561                 return NT_STATUS_OK;
562         }
563
564         /* should not be reached */
565         talloc_free(global);
566         return NT_STATUS_INTERNAL_ERROR;
567 }
568
569 static void smbXsrv_tcon_global_verify_record(struct db_record *db_rec,
570                                         bool *is_free,
571                                         bool *was_free,
572                                         TALLOC_CTX *mem_ctx,
573                                         struct smbXsrv_tcon_global0 **_g)
574 {
575         TDB_DATA key;
576         TDB_DATA val;
577         DATA_BLOB blob;
578         struct smbXsrv_tcon_globalB global_blob;
579         enum ndr_err_code ndr_err;
580         struct smbXsrv_tcon_global0 *global = NULL;
581         bool exists;
582         TALLOC_CTX *frame = talloc_stackframe();
583
584         *is_free = false;
585
586         if (was_free) {
587                 *was_free = false;
588         }
589         if (_g) {
590                 *_g = NULL;
591         }
592
593         key = dbwrap_record_get_key(db_rec);
594
595         val = dbwrap_record_get_value(db_rec);
596         if (val.dsize == 0) {
597                 TALLOC_FREE(frame);
598                 *is_free = true;
599                 if (was_free) {
600                         *was_free = true;
601                 }
602                 return;
603         }
604
605         blob = data_blob_const(val.dptr, val.dsize);
606
607         ndr_err = ndr_pull_struct_blob(&blob, frame, &global_blob,
608                         (ndr_pull_flags_fn_t)ndr_pull_smbXsrv_tcon_globalB);
609         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
610                 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
611                 DEBUG(1,("smbXsrv_tcon_global_verify_record: "
612                          "key '%s' ndr_pull_struct_blob - %s\n",
613                          hex_encode_talloc(frame, key.dptr, key.dsize),
614                          nt_errstr(status)));
615                 TALLOC_FREE(frame);
616                 return;
617         }
618
619         DEBUG(10,("smbXsrv_tcon_global_verify_record\n"));
620         if (DEBUGLVL(10)) {
621                 NDR_PRINT_DEBUG(smbXsrv_tcon_globalB, &global_blob);
622         }
623
624         if (global_blob.version != SMBXSRV_VERSION_0) {
625                 DEBUG(0,("smbXsrv_tcon_global_verify_record: "
626                          "key '%s' use unsupported version %u\n",
627                          hex_encode_talloc(frame, key.dptr, key.dsize),
628                          global_blob.version));
629                 NDR_PRINT_DEBUG(smbXsrv_tcon_globalB, &global_blob);
630                 TALLOC_FREE(frame);
631                 return;
632         }
633
634         global = global_blob.info.info0;
635
636         exists = serverid_exists(&global->server_id);
637         if (!exists) {
638                 struct server_id_buf idbuf;
639                 DEBUG(2,("smbXsrv_tcon_global_verify_record: "
640                          "key '%s' server_id %s does not exist.\n",
641                          hex_encode_talloc(frame, key.dptr, key.dsize),
642                          server_id_str_buf(global->server_id, &idbuf)));
643                 if (DEBUGLVL(2)) {
644                         NDR_PRINT_DEBUG(smbXsrv_tcon_globalB, &global_blob);
645                 }
646                 TALLOC_FREE(frame);
647                 dbwrap_record_delete(db_rec);
648                 *is_free = true;
649                 return;
650         }
651
652         if (_g) {
653                 *_g = talloc_move(mem_ctx, &global);
654         }
655         TALLOC_FREE(frame);
656 }
657
658 static NTSTATUS smbXsrv_tcon_global_store(struct smbXsrv_tcon_global0 *global)
659 {
660         struct smbXsrv_tcon_globalB global_blob;
661         DATA_BLOB blob = data_blob_null;
662         TDB_DATA key;
663         TDB_DATA val;
664         NTSTATUS status;
665         enum ndr_err_code ndr_err;
666
667         /*
668          * TODO: if we use other versions than '0'
669          * we would add glue code here, that would be able to
670          * store the information in the old format.
671          */
672
673         if (global->db_rec == NULL) {
674                 return NT_STATUS_INTERNAL_ERROR;
675         }
676
677         key = dbwrap_record_get_key(global->db_rec);
678         val = dbwrap_record_get_value(global->db_rec);
679
680         ZERO_STRUCT(global_blob);
681         global_blob.version = smbXsrv_version_global_current();
682         if (val.dsize >= 8) {
683                 global_blob.seqnum = IVAL(val.dptr, 4);
684         }
685         global_blob.seqnum += 1;
686         global_blob.info.info0 = global;
687
688         ndr_err = ndr_push_struct_blob(&blob, global->db_rec, &global_blob,
689                         (ndr_push_flags_fn_t)ndr_push_smbXsrv_tcon_globalB);
690         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
691                 status = ndr_map_error2ntstatus(ndr_err);
692                 DEBUG(1,("smbXsrv_tcon_global_store: key '%s' ndr_push - %s\n",
693                          hex_encode_talloc(global->db_rec, key.dptr, key.dsize),
694                          nt_errstr(status)));
695                 TALLOC_FREE(global->db_rec);
696                 return status;
697         }
698
699         val = make_tdb_data(blob.data, blob.length);
700         status = dbwrap_record_store(global->db_rec, val, TDB_REPLACE);
701         if (!NT_STATUS_IS_OK(status)) {
702                 DEBUG(1,("smbXsrv_tcon_global_store: key '%s' store - %s\n",
703                          hex_encode_talloc(global->db_rec, key.dptr, key.dsize),
704                          nt_errstr(status)));
705                 TALLOC_FREE(global->db_rec);
706                 return status;
707         }
708
709         if (DEBUGLVL(10)) {
710                 DEBUG(10,("smbXsrv_tcon_global_store: key '%s' stored\n",
711                          hex_encode_talloc(global->db_rec, key.dptr, key.dsize)));
712                 NDR_PRINT_DEBUG(smbXsrv_tcon_globalB, &global_blob);
713         }
714
715         TALLOC_FREE(global->db_rec);
716
717         return NT_STATUS_OK;
718 }
719
720 static int smbXsrv_tcon_destructor(struct smbXsrv_tcon *tcon)
721 {
722         NTSTATUS status;
723
724         status = smbXsrv_tcon_disconnect(tcon, 0);
725         if (!NT_STATUS_IS_OK(status)) {
726                 DEBUG(0, ("smbXsrv_tcon_destructor: "
727                           "smbXsrv_tcon_disconnect() failed - %s\n",
728                           nt_errstr(status)));
729         }
730
731         TALLOC_FREE(tcon->global);
732
733         return 0;
734 }
735
736 static NTSTATUS smbXsrv_tcon_create(struct smbXsrv_tcon_table *table,
737                                     enum protocol_types protocol,
738                                     struct server_id server_id,
739                                     NTTIME now,
740                                     struct smbXsrv_tcon **_tcon)
741 {
742         struct db_record *local_rec = NULL;
743         struct smbXsrv_tcon *tcon = NULL;
744         void *ptr = NULL;
745         TDB_DATA val;
746         struct smbXsrv_tcon_global0 *global = NULL;
747         NTSTATUS status;
748
749         if (table->local.num_tcons >= table->local.max_tcons) {
750                 return NT_STATUS_INSUFFICIENT_RESOURCES;
751         }
752
753         tcon = talloc_zero(table, struct smbXsrv_tcon);
754         if (tcon == NULL) {
755                 return NT_STATUS_NO_MEMORY;
756         }
757         tcon->table = table;
758         tcon->status = NT_STATUS_INTERNAL_ERROR;
759         tcon->idle_time = now;
760
761         status = smbXsrv_tcon_global_allocate(table->global.db_ctx,
762                                               tcon, &global);
763         if (!NT_STATUS_IS_OK(status)) {
764                 TALLOC_FREE(tcon);
765                 return status;
766         }
767         tcon->global = global;
768
769         if (protocol >= PROTOCOL_SMB2_02) {
770                 uint64_t id = global->tcon_global_id;
771
772                 global->tcon_wire_id = id;
773
774                 tcon->local_id = global->tcon_global_id;
775
776                 local_rec = smbXsrv_tcon_local_fetch_locked(table->local.db_ctx,
777                                                         tcon->local_id,
778                                                         tcon /* TALLOC_CTX */);
779                 if (local_rec == NULL) {
780                         TALLOC_FREE(tcon);
781                         return NT_STATUS_NO_MEMORY;
782                 }
783
784                 val = dbwrap_record_get_value(local_rec);
785                 if (val.dsize != 0) {
786                         TALLOC_FREE(tcon);
787                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
788                 }
789         } else {
790
791                 status = smb1srv_tcon_local_allocate_id(table->local.db_ctx,
792                                                         table->local.lowest_id,
793                                                         table->local.highest_id,
794                                                         tcon,
795                                                         &local_rec,
796                                                         &tcon->local_id);
797                 if (!NT_STATUS_IS_OK(status)) {
798                         TALLOC_FREE(tcon);
799                         return status;
800                 }
801
802                 global->tcon_wire_id = tcon->local_id;
803         }
804
805         global->creation_time = now;
806
807         global->server_id = server_id;
808
809         ptr = tcon;
810         val = make_tdb_data((uint8_t const *)&ptr, sizeof(ptr));
811         status = dbwrap_record_store(local_rec, val, TDB_REPLACE);
812         TALLOC_FREE(local_rec);
813         if (!NT_STATUS_IS_OK(status)) {
814                 TALLOC_FREE(tcon);
815                 return status;
816         }
817         table->local.num_tcons += 1;
818
819         talloc_set_destructor(tcon, smbXsrv_tcon_destructor);
820
821         status = smbXsrv_tcon_global_store(global);
822         if (!NT_STATUS_IS_OK(status)) {
823                 DEBUG(0,("smbXsrv_tcon_create: "
824                          "global_id (0x%08x) store failed - %s\n",
825                          tcon->global->tcon_global_id,
826                          nt_errstr(status)));
827                 TALLOC_FREE(tcon);
828                 return status;
829         }
830
831         if (DEBUGLVL(10)) {
832                 struct smbXsrv_tconB tcon_blob;
833
834                 ZERO_STRUCT(tcon_blob);
835                 tcon_blob.version = SMBXSRV_VERSION_0;
836                 tcon_blob.info.info0 = tcon;
837
838                 DEBUG(10,("smbXsrv_tcon_create: global_id (0x%08x) stored\n",
839                          tcon->global->tcon_global_id));
840                 NDR_PRINT_DEBUG(smbXsrv_tconB, &tcon_blob);
841         }
842
843         *_tcon = tcon;
844         return NT_STATUS_OK;
845 }
846
847 NTSTATUS smbXsrv_tcon_update(struct smbXsrv_tcon *tcon)
848 {
849         struct smbXsrv_tcon_table *table = tcon->table;
850         NTSTATUS status;
851
852         if (tcon->global->db_rec != NULL) {
853                 DEBUG(0, ("smbXsrv_tcon_update(0x%08x): "
854                           "Called with db_rec != NULL'\n",
855                           tcon->global->tcon_global_id));
856                 return NT_STATUS_INTERNAL_ERROR;
857         }
858
859         tcon->global->db_rec = smbXsrv_tcon_global_fetch_locked(
860                                                 table->global.db_ctx,
861                                                 tcon->global->tcon_global_id,
862                                                 tcon->global /* TALLOC_CTX */);
863         if (tcon->global->db_rec == NULL) {
864                 return NT_STATUS_INTERNAL_DB_ERROR;
865         }
866
867         status = smbXsrv_tcon_global_store(tcon->global);
868         if (!NT_STATUS_IS_OK(status)) {
869                 DEBUG(0,("smbXsrv_tcon_update: "
870                          "global_id (0x%08x) store failed - %s\n",
871                          tcon->global->tcon_global_id,
872                          nt_errstr(status)));
873                 return status;
874         }
875
876         if (DEBUGLVL(10)) {
877                 struct smbXsrv_tconB tcon_blob;
878
879                 ZERO_STRUCT(tcon_blob);
880                 tcon_blob.version = SMBXSRV_VERSION_0;
881                 tcon_blob.info.info0 = tcon;
882
883                 DEBUG(10,("smbXsrv_tcon_update: global_id (0x%08x) stored\n",
884                           tcon->global->tcon_global_id));
885                 NDR_PRINT_DEBUG(smbXsrv_tconB, &tcon_blob);
886         }
887
888         return NT_STATUS_OK;
889 }
890
891 NTSTATUS smbXsrv_tcon_disconnect(struct smbXsrv_tcon *tcon, uint64_t vuid)
892 {
893         struct smbXsrv_tcon_table *table;
894         struct db_record *local_rec = NULL;
895         struct db_record *global_rec = NULL;
896         NTSTATUS status;
897         NTSTATUS error = NT_STATUS_OK;
898
899         if (tcon->table == NULL) {
900                 return NT_STATUS_OK;
901         }
902
903         table = tcon->table;
904         tcon->table = NULL;
905
906         tcon->status = NT_STATUS_NETWORK_NAME_DELETED;
907
908         global_rec = tcon->global->db_rec;
909         tcon->global->db_rec = NULL;
910         if (global_rec == NULL) {
911                 global_rec = smbXsrv_tcon_global_fetch_locked(
912                                                 table->global.db_ctx,
913                                                 tcon->global->tcon_global_id,
914                                                 tcon->global /* TALLOC_CTX */);
915                 if (global_rec == NULL) {
916                         error = NT_STATUS_INTERNAL_ERROR;
917                 }
918         }
919
920         if (global_rec != NULL) {
921                 status = dbwrap_record_delete(global_rec);
922                 if (!NT_STATUS_IS_OK(status)) {
923                         TDB_DATA key = dbwrap_record_get_key(global_rec);
924
925                         DEBUG(0, ("smbXsrv_tcon_disconnect(0x%08x, '%s'): "
926                                   "failed to delete global key '%s': %s\n",
927                                   tcon->global->tcon_global_id,
928                                   tcon->global->share_name,
929                                   hex_encode_talloc(global_rec, key.dptr,
930                                                     key.dsize),
931                                   nt_errstr(status)));
932                         error = status;
933                 }
934         }
935         TALLOC_FREE(global_rec);
936
937         local_rec = tcon->db_rec;
938         if (local_rec == NULL) {
939                 local_rec = smbXsrv_tcon_local_fetch_locked(table->local.db_ctx,
940                                                         tcon->local_id,
941                                                         tcon /* TALLOC_CTX */);
942                 if (local_rec == NULL) {
943                         error = NT_STATUS_INTERNAL_ERROR;
944                 }
945         }
946
947         if (local_rec != NULL) {
948                 status = dbwrap_record_delete(local_rec);
949                 if (!NT_STATUS_IS_OK(status)) {
950                         TDB_DATA key = dbwrap_record_get_key(local_rec);
951
952                         DEBUG(0, ("smbXsrv_tcon_disconnect(0x%08x, '%s'): "
953                                   "failed to delete local key '%s': %s\n",
954                                   tcon->global->tcon_global_id,
955                                   tcon->global->share_name,
956                                   hex_encode_talloc(local_rec, key.dptr,
957                                                     key.dsize),
958                                   nt_errstr(status)));
959                         error = status;
960                 }
961                 table->local.num_tcons -= 1;
962         }
963         if (tcon->db_rec == NULL) {
964                 TALLOC_FREE(local_rec);
965         }
966         tcon->db_rec = NULL;
967
968         if (tcon->compat) {
969                 bool ok;
970
971                 ok = set_current_service(tcon->compat, 0, true);
972                 if (!ok) {
973                         status = NT_STATUS_INTERNAL_ERROR;
974                         DEBUG(0, ("smbXsrv_tcon_disconnect(0x%08x, '%s'): "
975                                   "set_current_service() failed: %s\n",
976                                   tcon->global->tcon_global_id,
977                                   tcon->global->share_name,
978                                   nt_errstr(status)));
979                         tcon->compat = NULL;
980                         return status;
981                 }
982
983                 close_cnum(tcon->compat, vuid);
984                 tcon->compat = NULL;
985         }
986
987         return error;
988 }
989
990 struct smbXsrv_tcon_disconnect_all_state {
991         uint64_t vuid;
992         NTSTATUS first_status;
993         int errors;
994 };
995
996 static int smbXsrv_tcon_disconnect_all_callback(struct db_record *local_rec,
997                                                 void *private_data);
998
999 static NTSTATUS smbXsrv_tcon_disconnect_all(struct smbXsrv_tcon_table *table,
1000                                             uint64_t vuid)
1001 {
1002         struct smbXsrv_tcon_disconnect_all_state state;
1003         NTSTATUS status;
1004         int count = 0;
1005
1006         if (table == NULL) {
1007                 return NT_STATUS_OK;
1008         }
1009
1010         ZERO_STRUCT(state);
1011         state.vuid = vuid;
1012
1013         status = dbwrap_traverse(table->local.db_ctx,
1014                                  smbXsrv_tcon_disconnect_all_callback,
1015                                  &state, &count);
1016         if (!NT_STATUS_IS_OK(status)) {
1017                 DEBUG(0, ("smbXsrv_tcon_disconnect_all: "
1018                           "dbwrap_traverse() failed: %s\n",
1019                           nt_errstr(status)));
1020                 return status;
1021         }
1022
1023         if (!NT_STATUS_IS_OK(state.first_status)) {
1024                 DEBUG(0, ("smbXsrv_tcon_disconnect_all: "
1025                           "count[%d] errors[%d] first[%s]\n",
1026                           count, state.errors,
1027                           nt_errstr(state.first_status)));
1028                 return state.first_status;
1029         }
1030
1031         return NT_STATUS_OK;
1032 }
1033
1034 static int smbXsrv_tcon_disconnect_all_callback(struct db_record *local_rec,
1035                                                 void *private_data)
1036 {
1037         struct smbXsrv_tcon_disconnect_all_state *state =
1038                 (struct smbXsrv_tcon_disconnect_all_state *)private_data;
1039         TDB_DATA val;
1040         void *ptr = NULL;
1041         struct smbXsrv_tcon *tcon = NULL;
1042         uint64_t vuid;
1043         NTSTATUS status;
1044
1045         val = dbwrap_record_get_value(local_rec);
1046         if (val.dsize != sizeof(ptr)) {
1047                 status = NT_STATUS_INTERNAL_ERROR;
1048                 if (NT_STATUS_IS_OK(state->first_status)) {
1049                         state->first_status = status;
1050                 }
1051                 state->errors++;
1052                 return 0;
1053         }
1054
1055         memcpy(&ptr, val.dptr, val.dsize);
1056         tcon = talloc_get_type_abort(ptr, struct smbXsrv_tcon);
1057
1058         vuid = state->vuid;
1059         if (vuid == 0 && tcon->compat) {
1060                 vuid = tcon->compat->vuid;
1061         }
1062
1063         tcon->db_rec = local_rec;
1064         status = smbXsrv_tcon_disconnect(tcon, vuid);
1065         if (!NT_STATUS_IS_OK(status)) {
1066                 if (NT_STATUS_IS_OK(state->first_status)) {
1067                         state->first_status = status;
1068                 }
1069                 state->errors++;
1070                 return 0;
1071         }
1072
1073         return 0;
1074 }
1075
1076 NTSTATUS smb1srv_tcon_table_init(struct smbXsrv_connection *conn)
1077 {
1078         struct smbXsrv_client *client = conn->client;
1079
1080         /*
1081          * Allow a range from 1..65534 with 65534 values.
1082          */
1083         client->tcon_table = talloc_zero(client, struct smbXsrv_tcon_table);
1084         if (client->tcon_table == NULL) {
1085                 return NT_STATUS_NO_MEMORY;
1086         }
1087
1088         return smbXsrv_tcon_table_init(client, client->tcon_table,
1089                                        1, UINT16_MAX - 1,
1090                                        UINT16_MAX - 1);
1091 }
1092
1093 NTSTATUS smb1srv_tcon_create(struct smbXsrv_connection *conn,
1094                              NTTIME now,
1095                              struct smbXsrv_tcon **_tcon)
1096 {
1097         struct server_id id = messaging_server_id(conn->msg_ctx);
1098
1099         return smbXsrv_tcon_create(conn->client->tcon_table,
1100                                    conn->protocol,
1101                                    id, now, _tcon);
1102 }
1103
1104 NTSTATUS smb1srv_tcon_lookup(struct smbXsrv_connection *conn,
1105                              uint16_t tree_id, NTTIME now,
1106                              struct smbXsrv_tcon **tcon)
1107 {
1108         uint32_t local_id = tree_id;
1109
1110         return smbXsrv_tcon_local_lookup(conn->client->tcon_table,
1111                                          local_id, now, tcon);
1112 }
1113
1114 NTSTATUS smb1srv_tcon_disconnect_all(struct smbXsrv_connection *conn)
1115 {
1116         struct smbXsrv_client *client = conn->client;
1117
1118         /*
1119          * We do not pass a vuid here,
1120          * which means the vuid is taken from
1121          * the tcon->compat->vuid.
1122          *
1123          * NOTE: that tcon->compat->vuid may point to
1124          * a none existing vuid (or the wrong one)
1125          * as the tcon can exist without a session
1126          * in SMB1.
1127          *
1128          * This matches the old behavior of
1129          * conn_close_all(), but we should think
1130          * about how to fix this in future.
1131          */
1132         return smbXsrv_tcon_disconnect_all(client->tcon_table, 0);
1133 }
1134
1135 NTSTATUS smb2srv_tcon_table_init(struct smbXsrv_session *session)
1136 {
1137         /*
1138          * Allow a range from 1..4294967294 with 65534 (same as SMB1) values.
1139          */
1140         session->tcon_table = talloc_zero(session, struct smbXsrv_tcon_table);
1141         if (session->tcon_table == NULL) {
1142                 return NT_STATUS_NO_MEMORY;
1143         }
1144
1145         return smbXsrv_tcon_table_init(session, session->tcon_table,
1146                                        1, UINT32_MAX - 1,
1147                                        UINT16_MAX - 1);
1148 }
1149
1150 NTSTATUS smb2srv_tcon_create(struct smbXsrv_session *session,
1151                              NTTIME now,
1152                              struct smbXsrv_tcon **_tcon)
1153 {
1154         struct server_id id = messaging_server_id(session->client->msg_ctx);
1155
1156         return smbXsrv_tcon_create(session->tcon_table,
1157                                    PROTOCOL_SMB2_02,
1158                                    id, now, _tcon);
1159 }
1160
1161 NTSTATUS smb2srv_tcon_lookup(struct smbXsrv_session *session,
1162                              uint32_t tree_id, NTTIME now,
1163                              struct smbXsrv_tcon **tcon)
1164 {
1165         uint32_t local_id = tree_id;
1166
1167         return smbXsrv_tcon_local_lookup(session->tcon_table,
1168                                          local_id, now, tcon);
1169 }
1170
1171 NTSTATUS smb2srv_tcon_disconnect_all(struct smbXsrv_session *session)
1172 {
1173         uint64_t vuid;
1174
1175         if (session->compat) {
1176                 vuid = session->compat->vuid;
1177         } else {
1178                 vuid = 0;
1179         }
1180
1181         return smbXsrv_tcon_disconnect_all(session->tcon_table, vuid);
1182 }
1183
1184 struct smbXsrv_tcon_global_traverse_state {
1185         int (*fn)(struct smbXsrv_tcon_global0 *, void *);
1186         void *private_data;
1187 };
1188
1189 static int smbXsrv_tcon_global_traverse_fn(struct db_record *rec, void *data)
1190 {
1191         int ret = -1;
1192         struct smbXsrv_tcon_global_traverse_state *state =
1193                 (struct smbXsrv_tcon_global_traverse_state*)data;
1194         TDB_DATA key = dbwrap_record_get_key(rec);
1195         TDB_DATA val = dbwrap_record_get_value(rec);
1196         DATA_BLOB blob = data_blob_const(val.dptr, val.dsize);
1197         struct smbXsrv_tcon_globalB global_blob;
1198         enum ndr_err_code ndr_err;
1199         TALLOC_CTX *frame = talloc_stackframe();
1200
1201         ndr_err = ndr_pull_struct_blob(&blob, frame, &global_blob,
1202                         (ndr_pull_flags_fn_t)ndr_pull_smbXsrv_tcon_globalB);
1203         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1204                 DEBUG(1,("Invalid record in smbXsrv_tcon_global.tdb:"
1205                          "key '%s' ndr_pull_struct_blob - %s\n",
1206                          hex_encode_talloc(frame, key.dptr, key.dsize),
1207                          ndr_errstr(ndr_err)));
1208                 goto done;
1209         }
1210
1211         if (global_blob.version != SMBXSRV_VERSION_0) {
1212                 DEBUG(1,("Invalid record in smbXsrv_tcon_global.tdb:"
1213                          "key '%s' unsuported version - %d\n",
1214                          hex_encode_talloc(frame, key.dptr, key.dsize),
1215                          (int)global_blob.version));
1216                 goto done;
1217         }
1218
1219         global_blob.info.info0->db_rec = rec;
1220         ret = state->fn(global_blob.info.info0, state->private_data);
1221 done:
1222         TALLOC_FREE(frame);
1223         return ret;
1224 }
1225
1226 NTSTATUS smbXsrv_tcon_global_traverse(
1227                         int (*fn)(struct smbXsrv_tcon_global0 *, void *),
1228                         void *private_data)
1229 {
1230         NTSTATUS status;
1231         int count = 0;
1232         struct smbXsrv_tcon_global_traverse_state state = {
1233                 .fn = fn,
1234                 .private_data = private_data,
1235         };
1236
1237         become_root();
1238         status = smbXsrv_tcon_global_init();
1239         if (!NT_STATUS_IS_OK(status)) {
1240                 unbecome_root();
1241                 DEBUG(0, ("Failed to initialize tcon_global: %s\n",
1242                           nt_errstr(status)));
1243                 return status;
1244         }
1245
1246         status = dbwrap_traverse_read(smbXsrv_tcon_global_db_ctx,
1247                                       smbXsrv_tcon_global_traverse_fn,
1248                                       &state,
1249                                       &count);
1250         unbecome_root();
1251
1252         return status;
1253 }