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