s3/utils: Use sddl_decode_err_msg instead of sddl_decode
[samba.git] / source3 / utils / net_serverid.c
1 /*
2    Samba Unix/Linux SMB client library
3    net serverid commands
4    Copyright (C) Volker Lendecke 2010
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "includes.h"
21 #include "utils/net.h"
22 #include "lib/util/server_id.h"
23 #include "dbwrap/dbwrap.h"
24 #include "dbwrap/dbwrap_rbt.h"
25 #include "serverid.h"
26 #include "session.h"
27 #include "smbd/globals.h"
28 #include "smbd/smbXsrv_open.h"
29 #include "util_tdb.h"
30 #include "librpc/gen_ndr/ndr_open_files.h"
31
32 struct wipedbs_record_marker {
33         struct wipedbs_record_marker *prev, *next;
34         TDB_DATA key, val;
35         const char *desc;
36 };
37
38 struct wipedbs_server_data {
39         struct server_id server_id;
40         const char *server_id_str;
41         bool exists;
42         struct wipedbs_record_marker *session_records;
43         struct wipedbs_record_marker *tcon_records;
44         struct wipedbs_record_marker *open_records;
45 };
46
47 struct wipedbs_state {
48         struct db_context *id2server_data;
49         struct {
50                 struct {
51                         int total;
52                         int existing;
53                         int disconnected;
54                 } server;
55                 struct {
56                         int total;
57                         int disconnected;
58                         int todelete;
59                         int failure;
60                 } session, tcon, open;
61                 int open_timed_out;
62         } stat;
63         struct server_id *server_ids;
64         bool *server_exists;
65         int idx;
66         struct db_context *session_db;
67         struct db_context *tcon_db;
68         struct db_context *open_db;
69         struct timeval now;
70         bool testmode;
71         bool verbose;
72 };
73
74 static struct wipedbs_server_data *get_server_data(struct wipedbs_state *state,
75                                                    const struct server_id *id)
76 {
77         struct wipedbs_server_data *ret = NULL;
78         TDB_DATA key, val = tdb_null;
79         NTSTATUS status;
80
81         key = make_tdb_data((const void*)&id->unique_id, sizeof(id->unique_id));
82         status = dbwrap_fetch(state->id2server_data, talloc_tos(), key, &val);
83         if (NT_STATUS_IS_OK(status)) {
84                 ret = *(struct wipedbs_server_data**) val.dptr;
85                 TALLOC_FREE(val.dptr);
86         } else if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
87                 struct server_id_buf idbuf;
88
89                 server_id_str_buf(*id, &idbuf);
90
91                 ret = talloc_zero(state->id2server_data,
92                                   struct wipedbs_server_data);
93                 if (ret == NULL) {
94                         DEBUG(0, ("Failed to allocate server entry for %s\n",
95                                   idbuf.buf));
96                         goto done;
97                 }
98                 ret->server_id = *id;
99                 ret->server_id_str = talloc_strdup(ret, idbuf.buf);
100                 ret->exists = true;
101                 val = make_tdb_data((const void*)&ret, sizeof(ret));
102                 status = dbwrap_store(state->id2server_data,
103                                       key, val, TDB_INSERT);
104                 if (!NT_STATUS_IS_OK(status)) {
105                         DEBUG(0, ("Failed to store server entry for %s: %s\n",
106                                   idbuf.buf, nt_errstr(status)));
107                 }
108                 goto done;
109         } else {
110                 struct server_id_buf idbuf;
111                 DEBUG(0, ("Failed to fetch server entry for %s: %s\n",
112                           server_id_str_buf(*id, &idbuf), nt_errstr(status)));
113                 goto done;
114         }
115         if (!server_id_equal(id, &ret->server_id)) {
116                 struct server_id_buf idbuf1, idbuf2;
117                 DEBUG(0, ("uniq id collision for %s and %s\n",
118                           server_id_str_buf(*id, &idbuf1),
119                           server_id_str_buf(ret->server_id, &idbuf2)));
120                 smb_panic("server_id->unique_id not unique!");
121         }
122 done:
123         return ret;
124 }
125
126 static int wipedbs_traverse_sessions(struct smbXsrv_session_global0 *session,
127                                      void *wipedbs_state)
128 {
129         struct wipedbs_state *state =
130                 talloc_get_type_abort(wipedbs_state,
131                 struct wipedbs_state);
132         struct wipedbs_server_data *sd;
133         struct wipedbs_record_marker *rec;
134         TDB_DATA tmp;
135         int ret = -1;
136
137         assert(session->num_channels == 1);
138
139         state->stat.session.total++;
140
141         sd = get_server_data(state, &session->channels[0].server_id);
142         if (sd == NULL) {
143                 goto done;
144         }
145
146         if (server_id_is_disconnected(&sd->server_id)) {
147                 state->stat.session.disconnected++;
148         }
149
150         rec = talloc_zero(sd, struct wipedbs_record_marker);
151         if (rec == NULL) {
152                 DEBUG(0, ("Out of memory!\n"));
153                 goto done;
154         }
155
156         tmp = dbwrap_record_get_key(session->db_rec);
157         rec->key = tdb_data_talloc_copy(rec, tmp);
158         tmp = dbwrap_record_get_value(session->db_rec);
159         rec->val = tdb_data_talloc_copy(rec, tmp);
160
161         rec->desc = talloc_asprintf(
162                 rec, "session[global: %u wire: %llu]",
163                 session->session_global_id,
164                 (long long unsigned)session->session_wire_id);
165
166         if ((rec->key.dptr == NULL) || (rec->val.dptr == NULL) ||
167             (rec->desc == NULL))
168         {
169                 DEBUG(0, ("Out of memory!\n"));
170                 goto done;
171         }
172
173         state->session_db = dbwrap_record_get_db(session->db_rec);
174
175         DLIST_ADD(sd->session_records, rec);
176         ret = 0;
177 done:
178         return ret;
179 }
180
181 static int wipedbs_traverse_tcon(struct smbXsrv_tcon_global0 *tcon,
182                                  void *wipedbs_state)
183 {
184         struct wipedbs_state *state =
185                 talloc_get_type_abort(wipedbs_state,
186                 struct wipedbs_state);
187         struct wipedbs_server_data *sd;
188         struct wipedbs_record_marker *rec;
189         TDB_DATA tmp;
190         int ret = -1;
191
192         state->stat.tcon.total++;
193
194         sd = get_server_data(state, &tcon->server_id);
195         if (sd == NULL) {
196                 goto done;
197         }
198
199         if (server_id_is_disconnected(&sd->server_id)) {
200                 state->stat.tcon.disconnected++;
201         }
202
203         rec = talloc_zero(sd, struct wipedbs_record_marker);
204         if (rec == NULL) {
205                 DEBUG(0, ("Out of memory!\n"));
206                 goto done;
207         }
208
209         tmp = dbwrap_record_get_key(tcon->db_rec);
210         rec->key = tdb_data_talloc_copy(rec, tmp);
211         tmp = dbwrap_record_get_value(tcon->db_rec);
212         rec->val = tdb_data_talloc_copy(rec, tmp);
213
214         rec->desc = talloc_asprintf(
215                 rec, "tcon[global: %u wire: %u session: %u share: %s]",
216                 tcon->tcon_global_id, tcon->tcon_wire_id,
217                 tcon->session_global_id, tcon->share_name);
218
219         if ((rec->key.dptr == NULL) || (rec->val.dptr == NULL) ||
220             (rec->desc == NULL))
221         {
222                 DEBUG(0, ("Out of memory!\n"));
223                 goto done;
224         }
225
226         state->tcon_db = dbwrap_record_get_db(tcon->db_rec);
227
228         DLIST_ADD(sd->tcon_records, rec);
229         ret = 0;
230
231 done:
232         return ret;
233 }
234
235 static int wipedbs_traverse_open(struct db_record *db_rec,
236                                  struct smbXsrv_open_global0 *open,
237                                  void *wipedbs_state)
238 {
239         struct wipedbs_state *state =
240                 talloc_get_type_abort(wipedbs_state,
241                 struct wipedbs_state);
242         struct wipedbs_server_data *sd;
243         struct wipedbs_record_marker *rec;
244         TDB_DATA tmp;
245         int ret = -1;
246
247         state->stat.open.total++;
248
249         sd = get_server_data(state, &open->server_id);
250         if (sd == NULL) {
251                 goto done;
252         }
253
254         if (server_id_is_disconnected(&sd->server_id)) {
255                 struct timeval disconnect_time;
256                 int64_t tdiff;
257                 bool reached;
258
259                 state->stat.open.disconnected++;
260
261                 nttime_to_timeval(&disconnect_time, open->disconnect_time);
262                 tdiff = usec_time_diff(&state->now, &disconnect_time);
263                 reached = (tdiff >= INT64_C(1000)*open->durable_timeout_msec);
264
265                 if (state->verbose) {
266                         TALLOC_CTX *mem_ctx = talloc_new(talloc_tos());
267                         enum ndr_err_code ndr_err;
268                         struct vfs_default_durable_cookie cookie;
269
270                         ndr_err = ndr_pull_struct_blob(
271                                 &open->backend_cookie, mem_ctx, &cookie,
272                                 (ndr_pull_flags_fn_t)ndr_pull_vfs_default_durable_cookie);
273                         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
274                                 d_printf("ndr_pull_struct_blob failed\n");
275                                 ret = -1;
276                                 goto done;
277                         }
278
279                         d_printf("open[%s/%s id: 0x%" PRIx32 "] disconnected at "
280                                  "[%s] %us ago with timeout of %us "
281                                  "-%s reached\n",
282                                  cookie.servicepath, cookie.base_name,
283                                  open->open_global_id,
284                                  nt_time_string(mem_ctx, open->disconnect_time),
285                                  (unsigned)(tdiff/1000000),
286                                  open->durable_timeout_msec / 1000,
287                                  reached ? "" : " not");
288                         talloc_free(mem_ctx);
289                 }
290
291                 if (!reached) {
292                         ret = 0;
293                         goto done;
294                 }
295                 state->stat.open_timed_out++;
296         }
297
298         rec = talloc_zero(sd, struct wipedbs_record_marker);
299         if (rec == NULL) {
300                 DEBUG(0, ("Out of memory!\n"));
301                 goto done;
302         }
303
304         tmp = dbwrap_record_get_key(db_rec);
305         rec->key = tdb_data_talloc_copy(rec, tmp);
306         tmp = dbwrap_record_get_value(db_rec);
307         rec->val = tdb_data_talloc_copy(rec, tmp);
308
309         rec->desc = talloc_asprintf(
310                 rec, "open[global: %u persistent: %llu volatile: %llu]",
311                 open->open_global_id,
312                 (long long unsigned)open->open_persistent_id,
313                 (long long unsigned)open->open_volatile_id);
314
315         if ((rec->key.dptr == NULL) || (rec->val.dptr == NULL) ||
316             (rec->desc == NULL))
317         {
318                 DEBUG(0, ("Out of memory!\n"));
319                 goto done;
320         }
321
322         state->open_db = dbwrap_record_get_db(db_rec);
323
324         DLIST_ADD(sd->open_records, rec);
325         ret = 0;
326
327 done:
328         return ret;
329 }
330
331 static int wipedbs_traverse_nop(struct db_record *rec, void *private_data)
332 {
333         return 0;
334 }
335
336 static int wipedbs_traverse_fill_ids(struct db_record *rec, void *wipedbs_state)
337 {
338         struct wipedbs_state *state = talloc_get_type_abort(
339                 wipedbs_state, struct wipedbs_state);
340
341         TDB_DATA val = dbwrap_record_get_value(rec);
342
343         struct wipedbs_server_data *sd = talloc_get_type_abort(
344                 *(void**)val.dptr, struct wipedbs_server_data);
345
346         state->server_ids[state->idx] = sd->server_id;
347         state->idx++;
348         return 0;
349 }
350
351 static int wipedbs_traverse_set_exists(struct db_record *rec,
352                                        void *wipedbs_state)
353 {
354         struct wipedbs_state *state = talloc_get_type_abort(
355                 wipedbs_state, struct wipedbs_state);
356
357         TDB_DATA val = dbwrap_record_get_value(rec);
358
359         struct wipedbs_server_data *sd = talloc_get_type_abort(
360                 *(void**)val.dptr, struct wipedbs_server_data);
361
362         /* assume a stable traverse order for rbt */
363         SMB_ASSERT(server_id_equal(&state->server_ids[state->idx],
364                                    &sd->server_id));
365         sd->exists = state->server_exists[state->idx];
366
367         if (sd->exists) {
368                 state->stat.server.existing++;
369         }
370         if (server_id_is_disconnected(&sd->server_id)) {
371                 state->stat.server.disconnected++;
372         }
373
374         state->idx++;
375         return 0;
376 }
377
378 static bool serverids_exist(const struct server_id *ids, int num_ids,
379                             bool *results)
380 {
381         int i;
382
383         for (i=0; i<num_ids; i++) {
384                 results[i] = serverid_exists(&ids[i]);
385         }
386
387         return true;
388 }
389
390
391 static NTSTATUS wipedbs_check_server_exists(struct wipedbs_state *state)
392 {
393         NTSTATUS status;
394         bool ok;
395         int num_servers;
396
397         status = dbwrap_traverse_read(state->id2server_data,
398                                       wipedbs_traverse_nop, NULL, &num_servers);
399         if (!NT_STATUS_IS_OK(status)) {
400                 DEBUG(0, ("Failed to traverse temporary database\n"));
401                 goto done;
402         }
403         state->stat.server.total = num_servers;
404
405         state->server_ids = talloc_array(state, struct server_id, num_servers);
406         state->server_exists = talloc_array(state, bool, num_servers);
407         if (state->server_ids == NULL || state->server_exists == NULL) {
408                 DEBUG(0, ("Out of memory\n"));
409                 goto done;
410         }
411
412         state->idx = 0;
413         status = dbwrap_traverse_read(state->id2server_data,
414                                       wipedbs_traverse_fill_ids,
415                                       state, NULL);
416         if (!NT_STATUS_IS_OK(status)) {
417                 DEBUG(0, ("Failed to traverse temporary database\n"));
418                 goto done;
419         }
420
421         ok = serverids_exist(state->server_ids, num_servers, state->server_exists);
422         if (!ok) {
423                 DEBUG(0, ("Calling serverids_exist failed\n"));
424                 status = NT_STATUS_UNSUCCESSFUL;
425                 goto done;
426         }
427
428         state->idx = 0;
429         status = dbwrap_traverse_read(state->id2server_data,
430                                       wipedbs_traverse_set_exists, state, NULL);
431         if (!NT_STATUS_IS_OK(status)) {
432                 DEBUG(0, ("Failed to traverse temporary database\n"));
433                 goto done;
434         }
435 done:
436         TALLOC_FREE(state->server_ids);
437         TALLOC_FREE(state->server_exists);
438         return status;
439 }
440
441 struct wipedbs_delete_state {
442         struct wipedbs_record_marker *cur;
443         bool verbose;
444         bool dry_run;
445         size_t total;
446         size_t num;
447 };
448
449 static void wipedbs_delete_fn(
450         struct db_record *rec, TDB_DATA value, void *private_data)
451 {
452         struct db_context *db = dbwrap_record_get_db(rec);
453         struct wipedbs_delete_state *state = private_data;
454         struct wipedbs_record_marker *cur = state->cur;
455         NTSTATUS status = NT_STATUS_OK;
456
457         state->total += 1;
458
459         if (!tdb_data_equal(value, cur->val)) {
460                 DBG_ERR("Warning: record <%s> from %s changed,"
461                         "skip record!\n",
462                         cur->desc, dbwrap_name(db));
463                 return;
464         }
465
466         if (state->verbose) {
467                 d_printf("deleting %s\n", cur->desc);
468         }
469
470         if (!state->dry_run) {
471                 status = dbwrap_record_delete(rec);
472         }
473
474         if (!NT_STATUS_IS_OK(status)) {
475                 DBG_ERR("Failed to delete record <%s> from %s: %s\n",
476                         cur->desc,
477                         dbwrap_name(db),
478                         nt_errstr(status));
479                 return;
480         }
481
482         state->num += 1;
483 }
484
485 static int wipedbs_delete_records(struct db_context *db,
486                                   struct wipedbs_record_marker *records,
487                                   bool dry_run, bool verbose, int *count)
488 {
489         struct wipedbs_delete_state state = {
490                 .verbose = verbose, .dry_run = dry_run,
491         };
492
493         if (db == NULL) {
494                 return 0;
495         }
496
497         for (state.cur = records;
498              state.cur != NULL;
499              state.cur = state.cur->next) {
500
501                 NTSTATUS status = dbwrap_do_locked(
502                         db, state.cur->key, wipedbs_delete_fn, &state);
503
504                 if (!NT_STATUS_IS_OK(status)) {
505                         DBG_ERR("dbwrap_do_locked failed for record <%s> "
506                                 "from %s\n",
507                                 state.cur->desc,
508                                 dbwrap_name(db));
509                 }
510         }
511
512         if (verbose) {
513                 d_printf("Deleted %zu of %zu records from %s\n",
514                          state.num,
515                          state.total,
516                          dbwrap_name(db));
517         }
518
519         if (count) {
520                 *count += state.total;
521         }
522
523         return state.total - state.num;
524 }
525
526 static int wipedbs_traverse_server_data(struct db_record *rec,
527                                         void *wipedbs_state)
528 {
529         struct wipedbs_state *state = talloc_get_type_abort(
530                 wipedbs_state, struct wipedbs_state);
531         bool dry_run = state->testmode;
532         TDB_DATA val = dbwrap_record_get_value(rec);
533         int ret;
534         struct wipedbs_server_data *sd = talloc_get_type_abort(
535                 *(void**)val.dptr, struct wipedbs_server_data);
536
537         if (state->verbose) {
538                 d_printf("Server: '%s' %s\n", sd->server_id_str,
539                          sd->exists ?
540                          "exists" :
541                          "does not exist, cleaning up...");
542         }
543
544         if (sd->exists) {
545                 return 0;
546         }
547
548         ret = wipedbs_delete_records(state->session_db, sd->session_records,
549                                      dry_run, state->verbose,
550                                      &state->stat.session.todelete);
551         state->stat.session.failure += ret;
552
553         ret = wipedbs_delete_records(state->tcon_db, sd->tcon_records,
554                                      dry_run, state->verbose,
555                                      &state->stat.tcon.todelete);
556         state->stat.tcon.failure += ret;
557
558         ret = wipedbs_delete_records(state->open_db, sd->open_records,
559                                      dry_run, state->verbose,
560                                      &state->stat.open.todelete);
561         state->stat.open.failure += ret;
562
563         return 0;
564 }
565
566 static int net_serverid_wipedbs(struct net_context *c, int argc,
567                                 const char **argv)
568 {
569         int ret = -1;
570         NTSTATUS status;
571         struct wipedbs_state *state = talloc_zero(talloc_tos(),
572                                                   struct wipedbs_state);
573
574         if (c->display_usage) {
575                 d_printf("%s\n%s",
576                          _("Usage:"),
577                          _("net serverid wipedbs [--test] [--verbose]\n"));
578                 d_printf("%s\n%s",
579                          _("Example:"),
580                          _("net serverid wipedbs -v\n"));
581                 return -1;
582         }
583
584         state->now = timeval_current();
585         state->testmode = c->opt_testmode;
586         state->verbose = c->opt_verbose;
587
588         state->id2server_data = db_open_rbt(state);
589         if (state->id2server_data == NULL) {
590                 DEBUG(0, ("Failed to open temporary database\n"));
591                 goto done;
592         }
593
594         status = smbXsrv_session_global_traverse(wipedbs_traverse_sessions,
595                                                  state);
596         if (!NT_STATUS_IS_OK(status)) {
597                 goto done;
598         }
599
600         status = smbXsrv_tcon_global_traverse(wipedbs_traverse_tcon, state);
601         if (!NT_STATUS_IS_OK(status)) {
602                 goto done;
603         }
604
605         status = smbXsrv_open_global_traverse(wipedbs_traverse_open, state);
606         if (!NT_STATUS_IS_OK(status)) {
607                 goto done;
608         }
609
610         status = wipedbs_check_server_exists(state);
611         if (!NT_STATUS_IS_OK(status)) {
612                 goto done;
613         }
614
615         status = dbwrap_traverse_read(state->id2server_data,
616                                       wipedbs_traverse_server_data,
617                                       state, NULL);
618         if (!NT_STATUS_IS_OK(status)) {
619                 DEBUG(0, ("Failed to traverse db: %s\n", nt_errstr(status)));
620                 goto done;
621         }
622
623         d_printf("Found %d serverids, %d alive and %d disconnected\n",
624                  state->stat.server.total,
625                  state->stat.server.existing,
626                  state->stat.server.disconnected);
627         d_printf("Found %d sessions, %d alive and %d disconnected"
628                  ", cleaned up %d of %d entries\n",
629                  state->stat.session.total,
630                  state->stat.session.total - state->stat.session.todelete,
631                  state->stat.session.disconnected,
632                  state->stat.session.todelete - state->stat.session.failure,
633                  state->stat.session.todelete);
634         d_printf("Found %d tcons, %d alive and %d disconnected"
635                  ", cleaned up %d of %d entries\n",
636                  state->stat.tcon.total,
637                  state->stat.tcon.total - state->stat.tcon.todelete,
638                  state->stat.tcon.disconnected,
639                  state->stat.tcon.todelete - state->stat.tcon.failure,
640                  state->stat.tcon.todelete);
641         d_printf("Found %d opens, %d alive, %d disconnected and %d timed out"
642                  ", cleaned up %d of %d entries\n",
643                  state->stat.open.total,
644                  state->stat.open.total - state->stat.open.todelete
645                  - (state->stat.open.disconnected - state->stat.open_timed_out),
646                  state->stat.open.disconnected,
647                  state->stat.open_timed_out,
648                  state->stat.open.todelete - state->stat.open.failure,
649                  state->stat.open.todelete);
650
651         ret = 0;
652 done:
653         talloc_free(state);
654         return ret;
655 }
656
657 static int net_serverid_exists(struct net_context *c, int argc,
658                                const char **argv)
659 {
660         struct server_id pid;
661         bool ok;
662
663         if ((argc != 1) || (c->display_usage)) {
664                 d_printf("Usage:\n"
665                          "net serverid exists <serverid>\n");
666                 return -1;
667         }
668
669         pid = server_id_from_string(get_my_vnn(), argv[0]);
670         ok = serverid_exists(&pid);
671
672         if (ok) {
673                 d_printf("%s exists\n", argv[0]);
674         } else {
675                 d_printf("%s does not exist\n", argv[0]);
676         }
677
678         return 0;
679 }
680
681 int net_serverid(struct net_context *c, int argc, const char **argv)
682 {
683         struct functable func[] = {
684                 {
685                         "wipedbs",
686                         net_serverid_wipedbs,
687                         NET_TRANSPORT_LOCAL,
688                         N_("Clean dead entries from temporary databases"),
689                         N_("net serverid wipedbs\n"
690                            "    Clean dead entries from temporary databases")
691                 },
692                 {
693                         "exists",
694                         net_serverid_exists,
695                         NET_TRANSPORT_LOCAL,
696                         N_("Show existence of a serverid"),
697                         N_("net serverid exists <id>")
698                 },
699                 {NULL, NULL, 0, NULL, NULL}
700         };
701
702         return net_run_function(c, argc, argv, "net serverid", func);
703 }