2 * Samba Unix/Linux client library
3 * net witness commands to manage smb witness registrations
4 * Copyright (C) 2023 Stefan Metzmacher
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.
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.
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/>.
21 #include "utils/net.h"
24 #include "lib/util/util_tdb.h"
25 #include "source3/include/util_tdb.h"
26 #include "libcli/security/dom_sid.h"
27 #include "lib/dbwrap/dbwrap.h"
28 #include "lib/dbwrap/dbwrap_rbt.h"
29 #include "lib/dbwrap/dbwrap_open.h"
30 #include "lib/param/param.h"
31 #include "librpc/gen_ndr/ndr_rpcd_witness.h"
38 #include "audit_logging.h" /* various JSON helpers */
39 #endif /* HAVE_JANSSON */
43 static struct db_context
*net_witness_open_registration_db(void)
45 static struct db_context
*db
;
46 char *global_path
= NULL
;
52 global_path
= lock_path(talloc_tos(), "rpcd_witness_registration.tdb");
53 if (global_path
== NULL
) {
62 TDB_INCOMPATIBLE_HASH
,
67 TALLOC_FREE(global_path
);
75 struct net_witness_scan_registrations_action_state
{
76 bool (*prepare_fn
)(void *private_data
);
77 bool (*match_fn
)(void *private_data
, const struct rpcd_witness_registration
*rg
);
78 NTSTATUS (*process_fn
)(void *private_data
, const struct rpcd_witness_registration
*rg
);
82 struct net_witness_scan_registrations_regex
{
87 struct net_witness_scan_registrations_state
{
88 struct net_context
*c
;
89 struct net_witness_scan_registrations_regex net_name
;
90 struct net_witness_scan_registrations_regex share_name
;
91 struct net_witness_scan_registrations_regex ip_address
;
92 struct net_witness_scan_registrations_regex client_computer
;
93 struct json_object
*message_json
;
95 struct json_object filters_json
;
96 struct json_object registrations_json
;
98 const struct net_witness_scan_registrations_action_state
*action
;
102 static bool net_witness_scan_registrations_regex_init(
103 struct net_witness_scan_registrations_state
*state
,
104 struct net_witness_scan_registrations_regex
*r
,
105 const char *option
, const char *value
);
106 static bool net_witness_scan_registrations_regex_match(
107 struct net_witness_scan_registrations_regex
*r
,
108 const char *name
, const char *value
);
109 static void net_witness_scan_registrations_regex_free(
110 struct net_witness_scan_registrations_regex
*r
);
112 static bool net_witness_scan_registrations_match(
113 struct net_witness_scan_registrations_state
*state
,
114 const struct rpcd_witness_registration
*rg
)
116 if (state
->net_name
.valid
) {
119 match
= net_witness_scan_registrations_regex_match(
128 if (state
->share_name
.valid
) {
131 match
= net_witness_scan_registrations_regex_match(
140 if (state
->ip_address
.valid
) {
143 match
= net_witness_scan_registrations_regex_match(
152 if (state
->client_computer
.valid
) {
155 match
= net_witness_scan_registrations_regex_match(
156 &state
->client_computer
,
157 "client_computer_name",
158 rg
->client_computer_name
);
167 static bool net_witness_scan_registrations_regex_init(
168 struct net_witness_scan_registrations_state
*state
,
169 struct net_witness_scan_registrations_regex
*r
,
170 const char *option
, const char *value
)
173 struct net_context
*c
= state
->c
;
174 #endif /* HAVE_JANSSON */
183 ret
= regcomp(&r
->regex
, value
, REG_EXTENDED
|REG_ICASE
|REG_NOSUB
);
186 regerror(ret
, &r
->regex
, buf
, sizeof(buf
));
187 d_printf("regcomp(%s) failed for %s: "
188 "%d: %s\n", value
, option
, ret
, buf
);
194 ret
= json_add_string(&state
->filters_json
,
201 #endif /* HAVE_JANSSON */
207 static bool net_witness_scan_registrations_regex_match(
208 struct net_witness_scan_registrations_regex
*r
,
209 const char *name
, const char *value
)
219 * without a share name,
220 * we match against an empty
226 ret
= regexec(&r
->regex
, value
, 0, NULL
, 0);
227 if (ret
== REG_NOMATCH
) {
234 static void net_witness_scan_registrations_regex_free(
235 struct net_witness_scan_registrations_regex
*r
)
243 static bool net_witness_scan_registrations_init(
244 struct net_witness_scan_registrations_state
*state
)
246 struct net_context
*c
= state
->c
;
251 state
->filters_json
= json_new_object();
252 if (json_is_invalid(&state
->filters_json
)) {
256 if (c
->opt_witness_registration
!= NULL
) {
259 ret
= json_add_string(&state
->filters_json
,
260 "--witness-registration",
261 c
->opt_witness_registration
);
267 state
->registrations_json
= json_new_object();
268 if (json_is_invalid(&state
->registrations_json
)) {
271 #else /* not HAVE_JANSSON */
272 d_fprintf(stderr
, _("JSON support not available\n"));
274 #endif /* not HAVE_JANSSON */
277 ok
= net_witness_scan_registrations_regex_init(state
,
279 "--witness-net-name",
280 c
->opt_witness_net_name
);
285 ok
= net_witness_scan_registrations_regex_init(state
,
287 "--witness-share-name",
288 c
->opt_witness_share_name
);
293 ok
= net_witness_scan_registrations_regex_init(state
,
295 "--witness-ip-address",
296 c
->opt_witness_ip_address
);
301 ok
= net_witness_scan_registrations_regex_init(state
,
302 &state
->client_computer
,
303 "--witness-client-computer-name",
304 c
->opt_witness_client_computer_name
);
309 ok
= state
->action
->prepare_fn(state
->action
->private_data
);
315 d_printf("%-36s %-20s %-15s %-20s %s\n",
316 "Registration-UUID:",
320 "ClientComputerName");
321 d_printf("%-36s-%-20s-%-15s-%-20s-%s\n",
322 "------------------------------------",
323 "--------------------",
324 "------------------",
325 "--------------------",
326 "------------------");
332 static bool net_witness_scan_registrations_finish(
333 struct net_witness_scan_registrations_state
*state
)
336 struct net_context
*c
= state
->c
;
337 struct json_object root_json
= json_empty_object
;
338 TALLOC_CTX
*frame
= NULL
;
339 const char *json_str
= NULL
;
346 frame
= talloc_stackframe();
348 root_json
= json_new_object();
349 if (json_is_invalid(&root_json
)) {
354 ret
= json_add_object(&root_json
,
356 &state
->filters_json
);
358 json_free(&root_json
);
362 state
->filters_json
= json_empty_object
;
364 if (state
->message_json
!= NULL
) {
365 ret
= json_add_object(&root_json
,
367 state
->message_json
);
369 json_free(&root_json
);
373 *state
->message_json
= json_empty_object
;
376 ret
= json_add_object(&root_json
,
378 &state
->registrations_json
);
380 json_free(&root_json
);
384 state
->registrations_json
= json_empty_object
;
386 json_str
= json_to_string(frame
, &root_json
);
387 json_free(&root_json
);
388 if (json_str
== NULL
) {
393 d_printf("%s\n", json_str
);
396 #else /* not HAVE_JANSSON */
398 #endif /* not HAVE_JANSSON */
401 static void net_witness_scan_registrations_free(
402 struct net_witness_scan_registrations_state
*state
)
405 if (!json_is_invalid(&state
->filters_json
)) {
406 json_free(&state
->filters_json
);
408 if (!json_is_invalid(&state
->registrations_json
)) {
409 json_free(&state
->registrations_json
);
411 #endif /* HAVE_JANSSON */
413 net_witness_scan_registrations_regex_free(&state
->net_name
);
414 net_witness_scan_registrations_regex_free(&state
->share_name
);
415 net_witness_scan_registrations_regex_free(&state
->ip_address
);
416 net_witness_scan_registrations_regex_free(&state
->client_computer
);
420 static int dump_registration_json(struct json_object
*registrations_json
,
422 const struct rpcd_witness_registration
*rg
)
424 struct json_object jsobj
= json_empty_object
;
425 struct json_object flags_json
= json_empty_object
;
426 struct json_object context_json
= json_empty_object
;
427 struct json_object serverid_json
= json_empty_object
;
428 struct json_object auth_json
= json_empty_object
;
429 struct json_object connection_json
= json_empty_object
;
431 struct dom_sid_buf sid_buf
;
434 jsobj
= json_new_object();
435 if (json_is_invalid(&jsobj
)) {
436 d_fprintf(stderr
, _("error setting up JSON value\n"));
440 ret
= json_add_flags32(&jsobj
, "version", rg
->version
);
445 ret
= json_add_string(&jsobj
, "net_name", rg
->net_name
);
450 ret
= json_add_string(&jsobj
, "share_name", rg
->share_name
);
455 ret
= json_add_string(&jsobj
, "ip_address", rg
->ip_address
);
460 ret
= json_add_string(&jsobj
, "client_computer_name", rg
->client_computer_name
);
465 flags_json
= json_new_object();
466 if (json_is_invalid(&flags_json
)) {
470 ret
= json_add_bool(&flags_json
, "WITNESS_REGISTER_IP_NOTIFICATION",
471 (rg
->flags
& WITNESS_REGISTER_IP_NOTIFICATION
) ?
477 ret
= json_add_int(&flags_json
, "int", rg
->flags
);
482 ret
= json_add_flags32(&flags_json
, "hex", rg
->flags
);
487 ret
= json_add_object(&jsobj
, "flags", &flags_json
);
491 flags_json
= json_empty_object
;
493 ret
= json_add_int(&jsobj
, "timeout", rg
->timeout
);
498 context_json
= json_new_object();
499 if (json_is_invalid(&context_json
)) {
503 ret
= json_add_int(&context_json
, "handle_type", rg
->context_handle
.handle_type
);
508 ret
= json_add_guid(&context_json
, "uuid", &rg
->context_handle
.uuid
);
513 ret
= json_add_object(&jsobj
, "context_handle", &context_json
);
517 context_json
= json_empty_object
;
519 serverid_json
= json_new_object();
520 if (json_is_invalid(&serverid_json
)) {
524 ret
= json_add_int(&serverid_json
, "pid", rg
->server_id
.pid
);
529 ret
= json_add_int(&serverid_json
, "task_id", rg
->server_id
.task_id
);
534 ret
= json_add_int(&serverid_json
, "vnn", rg
->server_id
.vnn
);
539 ret
= json_add_int(&serverid_json
, "unique_id", rg
->server_id
.unique_id
);
544 ret
= json_add_object(&jsobj
, "server_id", &serverid_json
);
548 serverid_json
= json_empty_object
;
550 auth_json
= json_new_object();
551 if (json_is_invalid(&auth_json
)) {
555 ret
= json_add_string(&auth_json
, "account_name", rg
->account_name
);
560 ret
= json_add_string(&auth_json
, "domain_name", rg
->domain_name
);
565 ret
= json_add_string(&auth_json
,
567 dom_sid_str_buf(&rg
->account_sid
, &sid_buf
));
572 ret
= json_add_object(&jsobj
, "auth", &auth_json
);
576 auth_json
= json_empty_object
;
578 connection_json
= json_new_object();
579 if (json_is_invalid(&connection_json
)) {
583 ret
= json_add_string(&connection_json
, "local_address", rg
->local_address
);
588 ret
= json_add_string(&connection_json
, "remote_address", rg
->remote_address
);
593 ret
= json_add_object(&jsobj
, "connection", &connection_json
);
597 connection_json
= json_empty_object
;
599 nttime_to_timeval(&tv
, rg
->registration_time
);
600 ret
= json_add_time(&jsobj
, "registration_time", tv
);
605 ret
= json_add_object(registrations_json
, key_str
, &jsobj
);
609 jsobj
= json_empty_object
;
612 if (!json_is_invalid(&connection_json
)) {
613 json_free(&connection_json
);
615 if (!json_is_invalid(&auth_json
)) {
616 json_free(&auth_json
);
618 if (!json_is_invalid(&serverid_json
)) {
619 json_free(&serverid_json
);
621 if (!json_is_invalid(&context_json
)) {
622 json_free(&context_json
);
624 if (!json_is_invalid(&flags_json
)) {
625 json_free(&flags_json
);
627 if (!json_is_invalid(&jsobj
)) {
633 #endif /* HAVE_JANSSON */
635 static NTSTATUS
net_witness_scan_registrations_dump_rg(
636 struct net_witness_scan_registrations_state
*state
,
637 const struct rpcd_witness_registration
*rg
)
639 struct net_context
*c
= state
->c
;
640 struct GUID_txt_buf key_buf
;
641 const char *key_str
= GUID_buf_string(&rg
->context_handle
.uuid
, &key_buf
);
647 ret
= dump_registration_json(&state
->registrations_json
,
651 d_fprintf(stderr
, "dump_registration_json(%s) failed\n",
653 return NT_STATUS_INTERNAL_ERROR
;
655 #endif /* HAVE_JANSSON */
659 d_printf("%-36s %-20s %-15s %-20s %s\n",
662 rg
->share_name
? rg
->share_name
: "''",
664 rg
->client_computer_name
);
669 static void net_witness_scan_registrations_parser(TDB_DATA key
,
673 struct net_witness_scan_registrations_state
*state
=
674 (struct net_witness_scan_registrations_state
*)private_data
;
675 DATA_BLOB val_blob
= data_blob_const(val
.dptr
, val
.dsize
);
676 struct rpcd_witness_registration rg
;
677 enum ndr_err_code ndr_err
;
678 TALLOC_CTX
*frame
= NULL
;
681 if (val_blob
.length
== 0) {
685 frame
= talloc_stackframe();
687 ndr_err
= ndr_pull_struct_blob(&val_blob
, frame
, &rg
,
688 (ndr_pull_flags_fn_t
)ndr_pull_rpcd_witness_registration
);
689 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
690 DBG_WARNING("Invalid record in rpcd_witness_registration.tdb:"
691 "key '%s' ndr_pull_struct_blob - %s\n",
693 ndr_errstr(ndr_err
));
694 state
->error
= ndr_map_error2ntstatus(ndr_err
);
699 if (!serverid_exists(&rg
.server_id
)) {
704 if (CHECK_DEBUGLVL(DBGLVL_DEBUG
)) {
705 NDR_PRINT_DEBUG(rpcd_witness_registration
, &rg
);
708 match
= net_witness_scan_registrations_match(state
, &rg
);
709 if (!NT_STATUS_IS_OK(state
->error
)) {
718 match
= state
->action
->match_fn(state
->action
->private_data
, &rg
);
724 state
->error
= state
->action
->process_fn(state
->action
->private_data
, &rg
);
725 if (NT_STATUS_IS_OK(state
->error
)) {
726 state
->error
= net_witness_scan_registrations_dump_rg(state
,
732 static int net_witness_scan_registrations_traverse_cb(struct db_record
*rec
, void *private_data
)
734 struct net_witness_scan_registrations_state
*state
=
735 (struct net_witness_scan_registrations_state
*)private_data
;
736 TDB_DATA key
= dbwrap_record_get_key(rec
);
737 TDB_DATA val
= dbwrap_record_get_value(rec
);
739 net_witness_scan_registrations_parser(key
, val
, private_data
);
741 if (!NT_STATUS_IS_OK(state
->error
)) {
748 static int net_witness_scan_registrations(struct net_context
*c
,
749 struct json_object
*message_json
,
750 const struct net_witness_scan_registrations_action_state
*action
)
752 struct net_witness_scan_registrations_state state
= {
754 .message_json
= message_json
,
757 struct db_context
*db
= NULL
;
761 db
= net_witness_open_registration_db();
763 d_printf("net_witness_open_registration_db() failed\n");
767 ok
= net_witness_scan_registrations_init(&state
);
769 d_printf("net_witness_scan_registrations_init() failed\n");
773 if (c
->opt_witness_registration
!= NULL
) {
774 const char *key_str
= c
->opt_witness_registration
;
775 DATA_BLOB key_blob
= data_blob_string_const(key_str
);
776 TDB_DATA key
= make_tdb_data(key_blob
.data
, key_blob
.length
);
778 status
= dbwrap_parse_record(db
,
780 net_witness_scan_registrations_parser
,
782 if (NT_STATUS_EQUAL(status
, NT_STATUS_NOT_FOUND
)) {
783 status
= NT_STATUS_OK
;
785 if (!NT_STATUS_IS_OK(status
)) {
786 d_printf("dbwrap_parse_record(%s) failed: %s\n",
787 key_str
, nt_errstr(status
));
788 net_witness_scan_registrations_free(&state
);
791 if (!NT_STATUS_IS_OK(state
.error
)) {
792 d_printf("net_witness_scan_registrations_parser(%s) failed: %s\n",
793 key_str
, nt_errstr(state
.error
));
794 net_witness_scan_registrations_free(&state
);
798 status
= dbwrap_traverse_read(db
,
799 net_witness_scan_registrations_traverse_cb
,
802 if (!NT_STATUS_IS_OK(status
)) {
803 d_printf("dbwrap_traverse_read() failed\n");
804 net_witness_scan_registrations_free(&state
);
807 if (!NT_STATUS_IS_OK(state
.error
)) {
808 d_printf("net_witness_scan_registrations_traverse_cb() failed: %s\n",
809 nt_errstr(state
.error
));
810 net_witness_scan_registrations_free(&state
);
815 ok
= net_witness_scan_registrations_finish(&state
);
817 d_printf("net_witness_scan_registrations_finish() failed\n");
821 net_witness_scan_registrations_free(&state
);
825 struct net_witness_list_state
{
826 struct net_context
*c
;
829 static bool net_witness_list_prepare_fn(void *private_data
)
834 static bool net_witness_list_match_fn(void *private_data
,
835 const struct rpcd_witness_registration
*rg
)
840 static NTSTATUS
net_witness_list_process_fn(void *private_data
,
841 const struct rpcd_witness_registration
*rg
)
846 static void net_witness_filter_usage(void)
848 d_printf(" Note: Only supported with clustering=yes!\n\n");
849 d_printf(" Machine readable output can be generated with "
850 "the following option:\n"
854 d_printf(" The selection of registrations can be limited by "
855 "the following options:\n"
857 " --witness-registration=REGISTRATION_UUID\n"
858 " This does a direct lookup for REGISTRATION_UUID\n"
859 " instead of doing a database traversal.\n"
861 " The following options all take a "
862 "POSIX Extended Regular Expression,\n"
863 " which can further filter the selection of "
865 " These options are applied as logical AND, "
867 " allows specifying multiple strings using "
870 " --witness-net-name=REGEX\n"
871 " This specifies the 'server name' the client\n"
872 " registered for monitoring.\n"
874 " --witness-share-name=REGEX\n"
875 " This specifies the 'share name' the client\n"
876 " registered for monitoring.\n"
877 " Note that the share name is optional in the\n"
878 " registration, otherwise an empty string is \n"
881 " --witness-ip-address=REGEX\n"
882 " This specifies the ip address the client\n"
883 " registered for monitoring.\n"
885 " --witness-client-computer-name=REGEX\n"
886 " This specifies the client computer name the client\n"
887 " specified in the registration.\n"
888 " Note it is just a string chosen by the "
893 static void net_witness_list_usage(void)
899 _("List witness registrations "
900 "from rpcd_witness_registration.tdb"));
901 net_witness_filter_usage();
904 static int net_witness_list(struct net_context
*c
, int argc
, const char **argv
)
906 TALLOC_CTX
*frame
= talloc_stackframe();
907 struct net_witness_list_state state
= { .c
= c
, };
908 struct net_witness_scan_registrations_action_state action
= {
909 .prepare_fn
= net_witness_list_prepare_fn
,
910 .match_fn
= net_witness_list_match_fn
,
911 .process_fn
= net_witness_list_process_fn
,
912 .private_data
= &state
,
916 if (c
->display_usage
) {
917 net_witness_list_usage();
922 net_witness_list_usage();
926 if (!lp_clustering()) {
927 d_printf("ERROR: Only supported with clustering=yes!\n\n");
931 ret
= net_witness_scan_registrations(c
, NULL
, &action
);
933 d_printf("net_witness_scan_registrations() failed\n");
943 int net_witness(struct net_context
*c
, int argc
, const char **argv
)
945 struct functable func
[] = {
950 N_("List witness registrations "
951 "from rpcd_witness_registration.tdb"),
952 N_("net witness list\n"
953 " List witness registrations "
954 "from rpcd_witness_registration.tdb"),
956 {NULL
, NULL
, 0, NULL
, NULL
}
959 return net_run_function(c
, argc
, argv
, "net witness", func
);