python:tests: Store keys as bytes rather than as lists of ints
[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 "lib/util/server_id.h"
24 #include "smbd/smbd.h"
25 #include "smbd/globals.h"
26 #include "dbwrap/dbwrap.h"
27 #include "dbwrap/dbwrap_rbt.h"
28 #include "dbwrap/dbwrap_open.h"
29 #include "messages.h"
30 #include "lib/util/util_tdb.h"
31 #include "librpc/gen_ndr/ndr_smbXsrv.h"
32 #include "serverid.h"
33 #include "source3/include/util_tdb.h"
34
35 struct smbXsrv_tcon_table {
36         struct {
37                 struct db_context *db_ctx;
38                 uint32_t lowest_id;
39                 uint32_t highest_id;
40                 uint32_t max_tcons;
41                 uint32_t num_tcons;
42         } local;
43         struct {
44                 struct db_context *db_ctx;
45         } global;
46 };
47
48 static struct db_context *smbXsrv_tcon_global_db_ctx = NULL;
49
50 NTSTATUS smbXsrv_tcon_global_init(void)
51 {
52         char *global_path = NULL;
53         struct db_context *db_ctx = NULL;
54
55         if (smbXsrv_tcon_global_db_ctx != NULL) {
56                 return NT_STATUS_OK;
57         }
58
59         global_path = lock_path(talloc_tos(), "smbXsrv_tcon_global.tdb");
60         if (global_path == NULL) {
61                 return NT_STATUS_NO_MEMORY;
62         }
63
64         db_ctx = db_open(NULL, global_path,
65                          SMBD_VOLATILE_TDB_HASH_SIZE,
66                          SMBD_VOLATILE_TDB_FLAGS,
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                           tdb_data_dbg(key));
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                           tdb_data_dbg(key));
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                          tdb_data_dbg(key),
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                          tdb_data_dbg(key),
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                          tdb_data_dbg(key),
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                          tdb_data_dbg(key),
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                          tdb_data_dbg(key),
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                           tdb_data_dbg(key)));
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                         .version = SMBXSRV_VERSION_0,
834                         .info.info0 = tcon,
835                 };
836
837                 DEBUG(10,("smbXsrv_tcon_create: global_id (0x%08x) stored\n",
838                          tcon->global->tcon_global_id));
839                 NDR_PRINT_DEBUG(smbXsrv_tconB, &tcon_blob);
840         }
841
842         *_tcon = tcon;
843         return NT_STATUS_OK;
844 }
845
846 NTSTATUS smbXsrv_tcon_update(struct smbXsrv_tcon *tcon)
847 {
848         struct smbXsrv_tcon_table *table = tcon->table;
849         NTSTATUS status;
850
851         if (tcon->global->db_rec != NULL) {
852                 DEBUG(0, ("smbXsrv_tcon_update(0x%08x): "
853                           "Called with db_rec != NULL'\n",
854                           tcon->global->tcon_global_id));
855                 return NT_STATUS_INTERNAL_ERROR;
856         }
857
858         tcon->global->db_rec = smbXsrv_tcon_global_fetch_locked(
859                                                 table->global.db_ctx,
860                                                 tcon->global->tcon_global_id,
861                                                 tcon->global /* TALLOC_CTX */);
862         if (tcon->global->db_rec == NULL) {
863                 return NT_STATUS_INTERNAL_DB_ERROR;
864         }
865
866         status = smbXsrv_tcon_global_store(tcon->global);
867         if (!NT_STATUS_IS_OK(status)) {
868                 DEBUG(0,("smbXsrv_tcon_update: "
869                          "global_id (0x%08x) store failed - %s\n",
870                          tcon->global->tcon_global_id,
871                          nt_errstr(status)));
872                 return status;
873         }
874
875         if (DEBUGLVL(10)) {
876                 struct smbXsrv_tconB tcon_blob = {
877                         .version = SMBXSRV_VERSION_0,
878                         .info.info0 = tcon,
879                 };
880
881                 DEBUG(10,("smbXsrv_tcon_update: global_id (0x%08x) stored\n",
882                           tcon->global->tcon_global_id));
883                 NDR_PRINT_DEBUG(smbXsrv_tconB, &tcon_blob);
884         }
885
886         return NT_STATUS_OK;
887 }
888
889 NTSTATUS smbXsrv_tcon_disconnect(struct smbXsrv_tcon *tcon, uint64_t vuid)
890 {
891         struct smbXsrv_tcon_table *table;
892         struct db_record *local_rec = NULL;
893         struct db_record *global_rec = NULL;
894         NTSTATUS status;
895         NTSTATUS error = NT_STATUS_OK;
896
897         if (tcon->table == NULL) {
898                 return NT_STATUS_OK;
899         }
900
901         table = tcon->table;
902         tcon->table = NULL;
903
904         if (tcon->compat) {
905                 bool ok;
906
907                 ok = chdir_current_service(tcon->compat);
908                 if (!ok) {
909                         status = NT_STATUS_INTERNAL_ERROR;
910                         DEBUG(0, ("smbXsrv_tcon_disconnect(0x%08x, '%s'): "
911                                   "chdir_current_service() failed: %s\n",
912                                   tcon->global->tcon_global_id,
913                                   tcon->global->share_name,
914                                   nt_errstr(status)));
915                         /*
916                          * We must call close_cnum() on
917                          * error, as the caller is going
918                          * to free tcon and tcon->compat
919                          * so we must ensure tcon->compat is
920                          * removed from the linked list
921                          * conn->sconn->connections.
922                          */
923                         close_cnum(tcon->compat, vuid, ERROR_CLOSE);
924                         tcon->compat = NULL;
925                         return status;
926                 }
927
928                 close_cnum(tcon->compat, vuid, SHUTDOWN_CLOSE);
929                 tcon->compat = NULL;
930         }
931
932         tcon->status = NT_STATUS_NETWORK_NAME_DELETED;
933
934         global_rec = tcon->global->db_rec;
935         tcon->global->db_rec = NULL;
936         if (global_rec == NULL) {
937                 global_rec = smbXsrv_tcon_global_fetch_locked(
938                                                 table->global.db_ctx,
939                                                 tcon->global->tcon_global_id,
940                                                 tcon->global /* TALLOC_CTX */);
941                 if (global_rec == NULL) {
942                         error = NT_STATUS_INTERNAL_ERROR;
943                 }
944         }
945
946         if (global_rec != NULL) {
947                 status = dbwrap_record_delete(global_rec);
948                 if (!NT_STATUS_IS_OK(status)) {
949                         TDB_DATA key = dbwrap_record_get_key(global_rec);
950
951                         DEBUG(0, ("smbXsrv_tcon_disconnect(0x%08x, '%s'): "
952                                   "failed to delete global key '%s': %s\n",
953                                   tcon->global->tcon_global_id,
954                                   tcon->global->share_name,
955                                   tdb_data_dbg(key),
956                                   nt_errstr(status)));
957                         error = status;
958                 }
959         }
960         TALLOC_FREE(global_rec);
961
962         local_rec = tcon->db_rec;
963         if (local_rec == NULL) {
964                 local_rec = smbXsrv_tcon_local_fetch_locked(table->local.db_ctx,
965                                                         tcon->local_id,
966                                                         tcon /* TALLOC_CTX */);
967                 if (local_rec == NULL) {
968                         error = NT_STATUS_INTERNAL_ERROR;
969                 }
970         }
971
972         if (local_rec != NULL) {
973                 status = dbwrap_record_delete(local_rec);
974                 if (!NT_STATUS_IS_OK(status)) {
975                         TDB_DATA key = dbwrap_record_get_key(local_rec);
976
977                         DEBUG(0, ("smbXsrv_tcon_disconnect(0x%08x, '%s'): "
978                                   "failed to delete local key '%s': %s\n",
979                                   tcon->global->tcon_global_id,
980                                   tcon->global->share_name,
981                                   tdb_data_dbg(key),
982                                   nt_errstr(status)));
983                         error = status;
984                 }
985                 table->local.num_tcons -= 1;
986         }
987         if (tcon->db_rec == NULL) {
988                 TALLOC_FREE(local_rec);
989         }
990         tcon->db_rec = NULL;
991
992         return error;
993 }
994
995 struct smbXsrv_tcon_disconnect_all_state {
996         uint64_t vuid;
997         NTSTATUS first_status;
998         int errors;
999 };
1000
1001 static int smbXsrv_tcon_disconnect_all_callback(struct db_record *local_rec,
1002                                                 void *private_data);
1003
1004 static NTSTATUS smbXsrv_tcon_disconnect_all(struct smbXsrv_tcon_table *table,
1005                                             uint64_t vuid)
1006 {
1007         struct smbXsrv_tcon_disconnect_all_state state;
1008         NTSTATUS status;
1009         int count = 0;
1010
1011         if (table == NULL) {
1012                 return NT_STATUS_OK;
1013         }
1014
1015         ZERO_STRUCT(state);
1016         state.vuid = vuid;
1017
1018         status = dbwrap_traverse(table->local.db_ctx,
1019                                  smbXsrv_tcon_disconnect_all_callback,
1020                                  &state, &count);
1021         if (!NT_STATUS_IS_OK(status)) {
1022                 DEBUG(0, ("smbXsrv_tcon_disconnect_all: "
1023                           "dbwrap_traverse() failed: %s\n",
1024                           nt_errstr(status)));
1025                 return status;
1026         }
1027
1028         if (!NT_STATUS_IS_OK(state.first_status)) {
1029                 DEBUG(0, ("smbXsrv_tcon_disconnect_all: "
1030                           "count[%d] errors[%d] first[%s]\n",
1031                           count, state.errors,
1032                           nt_errstr(state.first_status)));
1033                 return state.first_status;
1034         }
1035
1036         return NT_STATUS_OK;
1037 }
1038
1039 static int smbXsrv_tcon_disconnect_all_callback(struct db_record *local_rec,
1040                                                 void *private_data)
1041 {
1042         struct smbXsrv_tcon_disconnect_all_state *state =
1043                 (struct smbXsrv_tcon_disconnect_all_state *)private_data;
1044         TDB_DATA val;
1045         void *ptr = NULL;
1046         struct smbXsrv_tcon *tcon = NULL;
1047         uint64_t vuid;
1048         NTSTATUS status;
1049
1050         val = dbwrap_record_get_value(local_rec);
1051         if (val.dsize != sizeof(ptr)) {
1052                 status = NT_STATUS_INTERNAL_ERROR;
1053                 if (NT_STATUS_IS_OK(state->first_status)) {
1054                         state->first_status = status;
1055                 }
1056                 state->errors++;
1057                 return 0;
1058         }
1059
1060         memcpy(&ptr, val.dptr, val.dsize);
1061         tcon = talloc_get_type_abort(ptr, struct smbXsrv_tcon);
1062
1063         vuid = state->vuid;
1064         if (vuid == 0 && tcon->compat) {
1065                 vuid = tcon->compat->vuid;
1066         }
1067
1068         tcon->db_rec = local_rec;
1069         status = smbXsrv_tcon_disconnect(tcon, vuid);
1070         tcon->db_rec = NULL;
1071         if (!NT_STATUS_IS_OK(status)) {
1072                 if (NT_STATUS_IS_OK(state->first_status)) {
1073                         state->first_status = status;
1074                 }
1075                 state->errors++;
1076                 return 0;
1077         }
1078
1079         return 0;
1080 }
1081
1082 NTSTATUS smb1srv_tcon_table_init(struct smbXsrv_connection *conn)
1083 {
1084         struct smbXsrv_client *client = conn->client;
1085
1086         /*
1087          * Allow a range from 1..65534 with 65534 values.
1088          */
1089         client->tcon_table = talloc_zero(client, struct smbXsrv_tcon_table);
1090         if (client->tcon_table == NULL) {
1091                 return NT_STATUS_NO_MEMORY;
1092         }
1093
1094         return smbXsrv_tcon_table_init(client, client->tcon_table,
1095                                        1, UINT16_MAX - 1,
1096                                        UINT16_MAX - 1);
1097 }
1098
1099 NTSTATUS smb1srv_tcon_create(struct smbXsrv_connection *conn,
1100                              NTTIME now,
1101                              struct smbXsrv_tcon **_tcon)
1102 {
1103         struct server_id id = messaging_server_id(conn->client->msg_ctx);
1104
1105         return smbXsrv_tcon_create(conn->client->tcon_table,
1106                                    conn->protocol,
1107                                    id, now, _tcon);
1108 }
1109
1110 NTSTATUS smb1srv_tcon_lookup(struct smbXsrv_connection *conn,
1111                              uint16_t tree_id, NTTIME now,
1112                              struct smbXsrv_tcon **tcon)
1113 {
1114         uint32_t local_id = tree_id;
1115
1116         return smbXsrv_tcon_local_lookup(conn->client->tcon_table,
1117                                          local_id, now, tcon);
1118 }
1119
1120 NTSTATUS smb1srv_tcon_disconnect_all(struct smbXsrv_client *client)
1121 {
1122
1123         /*
1124          * We do not pass a vuid here,
1125          * which means the vuid is taken from
1126          * the tcon->compat->vuid.
1127          *
1128          * NOTE: that tcon->compat->vuid may point to
1129          * a none existing vuid (or the wrong one)
1130          * as the tcon can exist without a session
1131          * in SMB1.
1132          *
1133          * This matches the old behavior of
1134          * conn_close_all(), but we should think
1135          * about how to fix this in future.
1136          */
1137         return smbXsrv_tcon_disconnect_all(client->tcon_table, 0);
1138 }
1139
1140 NTSTATUS smb2srv_tcon_table_init(struct smbXsrv_session *session)
1141 {
1142         /*
1143          * Allow a range from 1..4294967294 with 65534 (same as SMB1) values.
1144          */
1145         session->tcon_table = talloc_zero(session, struct smbXsrv_tcon_table);
1146         if (session->tcon_table == NULL) {
1147                 return NT_STATUS_NO_MEMORY;
1148         }
1149
1150         return smbXsrv_tcon_table_init(session, session->tcon_table,
1151                                        1, UINT32_MAX - 1,
1152                                        UINT16_MAX - 1);
1153 }
1154
1155 NTSTATUS smb2srv_tcon_create(struct smbXsrv_session *session,
1156                              NTTIME now,
1157                              struct smbXsrv_tcon **_tcon)
1158 {
1159         struct server_id id = messaging_server_id(session->client->msg_ctx);
1160
1161         return smbXsrv_tcon_create(session->tcon_table,
1162                                    PROTOCOL_SMB2_02,
1163                                    id, now, _tcon);
1164 }
1165
1166 NTSTATUS smb2srv_tcon_lookup(struct smbXsrv_session *session,
1167                              uint32_t tree_id, NTTIME now,
1168                              struct smbXsrv_tcon **tcon)
1169 {
1170         uint32_t local_id = tree_id;
1171
1172         return smbXsrv_tcon_local_lookup(session->tcon_table,
1173                                          local_id, now, tcon);
1174 }
1175
1176 NTSTATUS smb2srv_tcon_disconnect_all(struct smbXsrv_session *session)
1177 {
1178         uint64_t vuid = session->global->session_wire_id;
1179
1180         return smbXsrv_tcon_disconnect_all(session->tcon_table, vuid);
1181 }
1182
1183 struct smbXsrv_tcon_global_traverse_state {
1184         int (*fn)(struct smbXsrv_tcon_global0 *, void *);
1185         void *private_data;
1186 };
1187
1188 static int smbXsrv_tcon_global_traverse_fn(struct db_record *rec, void *data)
1189 {
1190         int ret = -1;
1191         struct smbXsrv_tcon_global_traverse_state *state =
1192                 (struct smbXsrv_tcon_global_traverse_state*)data;
1193         TDB_DATA key = dbwrap_record_get_key(rec);
1194         TDB_DATA val = dbwrap_record_get_value(rec);
1195         DATA_BLOB blob = data_blob_const(val.dptr, val.dsize);
1196         struct smbXsrv_tcon_globalB global_blob;
1197         enum ndr_err_code ndr_err;
1198         TALLOC_CTX *frame = talloc_stackframe();
1199
1200         ndr_err = ndr_pull_struct_blob(&blob, frame, &global_blob,
1201                         (ndr_pull_flags_fn_t)ndr_pull_smbXsrv_tcon_globalB);
1202         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1203                 DEBUG(1,("Invalid record in smbXsrv_tcon_global.tdb:"
1204                          "key '%s' ndr_pull_struct_blob - %s\n",
1205                          tdb_data_dbg(key),
1206                          ndr_errstr(ndr_err)));
1207                 goto done;
1208         }
1209
1210         if (global_blob.version != SMBXSRV_VERSION_0) {
1211                 DEBUG(1,("Invalid record in smbXsrv_tcon_global.tdb:"
1212                          "key '%s' unsupported version - %d\n",
1213                          tdb_data_dbg(key),
1214                          (int)global_blob.version));
1215                 goto done;
1216         }
1217
1218         if (global_blob.info.info0 == NULL) {
1219                 DEBUG(1,("Invalid record in smbXsrv_tcon_global.tdb:"
1220                          "key '%s' info0 NULL pointer\n",
1221                          tdb_data_dbg(key)));
1222                 goto done;
1223         }
1224
1225         global_blob.info.info0->db_rec = rec;
1226         ret = state->fn(global_blob.info.info0, state->private_data);
1227 done:
1228         TALLOC_FREE(frame);
1229         return ret;
1230 }
1231
1232 NTSTATUS smbXsrv_tcon_global_traverse(
1233                         int (*fn)(struct smbXsrv_tcon_global0 *, void *),
1234                         void *private_data)
1235 {
1236         NTSTATUS status;
1237         int count = 0;
1238         struct smbXsrv_tcon_global_traverse_state state = {
1239                 .fn = fn,
1240                 .private_data = private_data,
1241         };
1242
1243         become_root();
1244         status = smbXsrv_tcon_global_init();
1245         if (!NT_STATUS_IS_OK(status)) {
1246                 unbecome_root();
1247                 DEBUG(0, ("Failed to initialize tcon_global: %s\n",
1248                           nt_errstr(status)));
1249                 return status;
1250         }
1251
1252         status = dbwrap_traverse_read(smbXsrv_tcon_global_db_ctx,
1253                                       smbXsrv_tcon_global_traverse_fn,
1254                                       &state,
1255                                       &count);
1256         unbecome_root();
1257
1258         return status;
1259 }