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