b5d355e2f40e434ea98b02d70ea31f14355ccb8a
[bbaumbach/samba-autobuild/.git] / source4 / utils / net / drs / net_drs_showrepl.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    Implements functions offered by repadmin.exe tool under Windows
5
6    Copyright (C) Kamen Mazdrashki <kamen.mazdrashki@postpath.com> 2010
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "includes.h"
23 #include "utils/net/net.h"
24 #include "net_drs.h"
25 #include "lib/ldb/include/ldb.h"
26 #include "dsdb/samdb/samdb.h"
27 #include "lib/util/util_ldb.h"
28
29
30 /**
31  * Parses NTDS Settings DN to find out:
32  *  - DC name
33  *  - Site name
34  *  - Domain DNS name
35  */
36 static bool net_drs_parse_ntds_dn(struct ldb_dn *ntds_dn,
37                                   TALLOC_CTX *mem_ctx,
38                                   const char **_dc_name,
39                                   const char **_site_name,
40                                   const char **_domain_dns_name)
41 {
42         struct ldb_dn *dn = NULL;
43         const struct ldb_val *val;
44
45         dn = ldb_dn_copy(mem_ctx, ntds_dn);
46         NET_DRS_NOMEM_GOTO(dn, failed);
47
48         /* remove NTDS Settings component */
49         ldb_dn_remove_child_components(dn, 1);
50         if (_dc_name) {
51                 val = ldb_dn_get_rdn_val(dn);
52                 *_dc_name = talloc_strdup(mem_ctx, (const char *)val->data);
53         }
54
55         /* remove DC and Servers components */
56         ldb_dn_remove_child_components(dn, 2);
57         if (_site_name) {
58                 val = ldb_dn_get_rdn_val(dn);
59                 *_site_name = talloc_strdup(mem_ctx, (const char *)val->data);
60         }
61
62         if (_domain_dns_name) {
63                 char *pstr;
64                 char *dns_name;
65
66                 dns_name = ldb_dn_canonical_string(mem_ctx, dn);
67                 NET_DRS_NOMEM_GOTO(dns_name, failed);
68
69                 pstr = strchr(dns_name, '/');
70                 if (pstr) {
71                         *pstr = '\0';
72                 }
73
74                 *_domain_dns_name = dns_name;
75         }
76
77         talloc_free(dn);
78         return true;
79
80 failed:
81         talloc_free(dn);
82         return false;
83 }
84
85 static char * net_drs_dc_canonical_string(struct ldb_dn *ntds_dn, TALLOC_CTX *mem_ctx)
86 {
87         const char *dc_name;
88         const char *site_name;
89         char *canonical_name;
90
91         if (!net_drs_parse_ntds_dn(ntds_dn, mem_ctx, &dc_name, &site_name, NULL)) {
92                 return NULL;
93         }
94
95         canonical_name = talloc_asprintf(mem_ctx, "%s\\%s", site_name, dc_name);
96
97         talloc_free(discard_const(dc_name));
98         talloc_free(discard_const(site_name));
99
100         return canonical_name;
101 }
102
103 /**
104  * Prints DC information for showrepl command
105  */
106 static bool net_drs_showrepl_print_dc_info(struct net_drs_context *drs_ctx)
107 {
108         int ret;
109         const char *dc_name;
110         const char *site_name;
111         struct ldb_dn *dn;
112         struct ldb_message **ntds_msgs;
113         struct ldb_message **site_msgs;
114         uint32_t options;
115         struct GUID guid;
116         TALLOC_CTX *mem_ctx;
117         const char *ntds_attr[] = {"options", "objectGuid", "invocationId", NULL};
118         const char *site_ntds_attr[] = {"options", "whenChanged", NULL};
119
120         mem_ctx = talloc_new(drs_ctx);
121
122         /* Get NTDS Settings DN string for the DC */
123         dn = samdb_result_dn(drs_ctx->ldap.ldb, mem_ctx,
124                              drs_ctx->ldap.rootdse, "dsServiceName", NULL);
125         NET_DRS_CHECK_GOTO(dn, failed, "No dsServiceName value in RootDSE!\n");
126
127         /* parse NTDS Settings DN */
128         if (!net_drs_parse_ntds_dn(dn, mem_ctx, &dc_name, &site_name, NULL)) {
129                 d_printf("Unexptected: Failed to parse %s DN!\n",
130                          ldb_dn_get_linearized(dn));
131                 goto failed;
132         }
133
134         /* Query DC record for DSA's NTDS Settings DN */
135         ret = gendb_search_dn(drs_ctx->ldap.ldb, mem_ctx, dn, &ntds_msgs, ntds_attr);
136         if (ret != 1) {
137                 d_printf("Error while fetching %s, Possible error: %s\n",
138                          ldb_dn_get_linearized(dn),
139                          ldb_errstring(drs_ctx->ldap.ldb));
140                 goto failed;
141         }
142
143         /* find out NTDS Site Settings DN */
144         if (!ldb_dn_remove_child_components(dn, 3)) {
145                 d_printf("Unexpected: ldb_dn_remove_child_components() failed!\n");
146                 goto failed;
147         }
148         if (!ldb_dn_add_child_fmt(dn, "CN=%s", "NTDS Site Settings")) {
149                 d_printf("Unexpected: ldb_dn_add_child_fmt() failed!\n");
150                 goto failed;
151         }
152         /* Query Site record for DSA's NTDS Settings DN */
153         ret = gendb_search_dn(drs_ctx->ldap.ldb, mem_ctx, dn, &site_msgs, site_ntds_attr);
154         if (ret != 1) {
155                 d_printf("Error while fetching %s, Possible error: %s\n",
156                          ldb_dn_get_linearized(dn),
157                          ldb_errstring(drs_ctx->ldap.ldb));
158                 goto failed;
159         }
160
161         /* Site-name\DC-name */
162         d_printf("%s\\%s\n", site_name, dc_name);
163         /* DSA Options */
164         options = samdb_result_uint(ntds_msgs[0], "options", 0);
165         if (options) {
166                 /* TODO: Print options as string in IS_GC... etc form */
167                 d_printf("DSA Options: 0x%08X\n", options);
168         } else {
169                 d_printf("DSA Options: (none)\n");
170         }
171         /* Site Options */
172         options = samdb_result_uint(site_msgs[0], "options", 0);
173         if (options) {
174                 /* TODO: Print options in string */
175                 d_printf("DSA Options: 0x%08X\n", options);
176         } else {
177                 d_printf("Site Options: (none)\n");
178         }
179         /* DSA GUID */
180         guid = samdb_result_guid(ntds_msgs[0], "objectGUID");
181         d_printf("DSA object GUID: %s\n", GUID_string(mem_ctx, &guid));
182         /* DSA invocationId */
183         guid = samdb_result_guid(ntds_msgs[0], "invocationId");
184         d_printf("DSA invocationID: %s\n", GUID_string(mem_ctx, &guid));
185
186         talloc_free(mem_ctx);
187         return true;
188
189 failed:
190         talloc_free(mem_ctx);
191         return false;
192 }
193
194 /**
195  * Convenience function to call DsReplicaGetInfo
196  */
197 static bool net_drs_exec_DsReplicaGetInfo(struct net_drs_context *drs_ctx,
198                                           enum drsuapi_DsReplicaInfoType info_type,
199                                           union drsuapi_DsReplicaInfo *_replica_info)
200 {
201         NTSTATUS status;
202         struct drsuapi_DsReplicaGetInfo r;
203         union drsuapi_DsReplicaGetInfoRequest req;
204         enum drsuapi_DsReplicaInfoType info_type_got;
205         struct net_drs_connection *drs_conn = drs_ctx->drs_conn;
206
207         ZERO_STRUCT(req);
208         req.req1.info_type = info_type;
209
210         r.in.bind_handle        = &drs_conn->bind_handle;
211         r.in.level              = 1;
212         r.in.req                = &req;
213         r.out.info              = _replica_info;
214         r.out.info_type         = &info_type_got;
215
216         status = dcerpc_drsuapi_DsReplicaGetInfo_r(drs_conn->drs_handle, drs_ctx, &r);
217         if (!NT_STATUS_IS_OK(status)) {
218                 const char *errstr = nt_errstr(status);
219                 d_printf("DsReplicaGetInfo failed - %s.\n", errstr);
220                 return false;
221         } else if (!W_ERROR_IS_OK(r.out.result)) {
222                 d_printf("DsReplicaGetInfo failed - %s.\n", win_errstr(r.out.result));
223                 return false;
224         }
225
226         if (info_type != info_type_got) {
227                 d_printf("DsReplicaGetInfo: Error requested info %d, got info %d",
228                          info_type, info_type_got);
229                 return false;
230         }
231
232         return true;
233 }
234
235 /**
236  * Return transport type string for given transport object DN.
237  * Currently always return 'RPC'.
238  *
239  * TODO: Implement getting transport type for all kind of transports
240  */
241 static const char *
242 net_drs_transport_type_str(struct net_drs_context *drs_ctx, const char *transport_obj_dn)
243 {
244         return "RPC";
245 }
246
247 /**
248  * Prints most of the info we got about
249  * a replication partner
250  */
251 static bool net_drs_showrepl_print_heighbor(struct net_drs_context *drs_ctx,
252                                             struct drsuapi_DsReplicaNeighbour *neighbor)
253 {
254         struct ldb_dn *ntds_dn;
255         TALLOC_CTX *mem_ctx;
256
257         mem_ctx = talloc_new(drs_ctx);
258
259         ntds_dn = ldb_dn_new(drs_ctx, drs_ctx->ldap.ldb, neighbor->source_dsa_obj_dn);
260         NET_DRS_NOMEM_GOTO(ntds_dn, failed);
261
262         d_printf("%s\n", neighbor->naming_context_dn);
263         /* TODO: Determine connection type */
264         d_printf("\t%s via %s\n",
265                  net_drs_dc_canonical_string(ntds_dn, mem_ctx),
266                  net_drs_transport_type_str(drs_ctx, neighbor->transport_obj_dn));
267         d_printf("\t\tDSA object GUID: %s\n", GUID_string(mem_ctx, &neighbor->source_dsa_obj_guid));
268         if (W_ERROR_IS_OK(neighbor->result_last_attempt)) {
269                 d_printf("\t\tLast attempt @ %s was successful.\n",
270                          nt_time_string(mem_ctx, neighbor->last_attempt));
271         } else {
272                 d_printf("\t\tLast attempt @ %s failed, result %d (%s):\n",
273                          nt_time_string(mem_ctx, neighbor->last_attempt),
274                          W_ERROR_V(neighbor->result_last_attempt),
275                          win_errstr(neighbor->result_last_attempt));
276                 d_printf("\t\t\t%s\n", get_friendly_werror_msg(neighbor->result_last_attempt));
277         }
278         d_printf("\t\t%d consecutive failure(s).\n", neighbor->consecutive_sync_failures);
279         d_printf("\t\tLast success @ %s\n", nt_time_string(mem_ctx, neighbor->last_success));
280
281         talloc_free(mem_ctx);
282         return true;
283
284 failed:
285         talloc_free(mem_ctx);
286         return false;
287 }
288
289 /**
290  * Prints list of all servers that target DC
291  * replicates from
292  */
293 static bool net_drs_showrepl_print_inbound_neihbors(struct net_drs_context *drs_ctx)
294 {
295         int i;
296         bool bret;
297         struct drsuapi_DsReplicaNeighbourCtr *reps_from;
298         union drsuapi_DsReplicaInfo replica_info;
299
300         d_printf("\n==== INBOUND NEIGHBORS ====\n");
301
302         bret = net_drs_exec_DsReplicaGetInfo(drs_ctx,
303                                              DRSUAPI_DS_REPLICA_INFO_NEIGHBORS, &replica_info);
304         if (!bret) {
305                 d_printf("DsReplicaGetInfo() failed for DRSUAPI_DS_REPLICA_INFO_KCC_DSA_CONNECT_FAILURES");
306                 return false;
307         }
308         reps_from = replica_info.neighbours;
309
310         for (i = 0; i < reps_from->count; i++) {
311                 d_printf("\n");
312                 net_drs_showrepl_print_heighbor(drs_ctx, &reps_from->array[i]);
313         }
314
315         return true;
316 }
317
318 /**
319  * Prints list of all servers that target DC
320  * notifies for changes
321  */
322 static bool net_drs_showrepl_print_outbound_neihbors(struct net_drs_context *drs_ctx)
323 {
324         int i;
325         bool bret;
326         struct drsuapi_DsReplicaNeighbourCtr *reps_to;
327         union drsuapi_DsReplicaInfo replica_info;
328
329         d_printf("\n==== OUTBOUND NEIGHBORS ====\n");
330
331         bret = net_drs_exec_DsReplicaGetInfo(drs_ctx,
332                                              DRSUAPI_DS_REPLICA_INFO_REPSTO, &replica_info);
333         if (!bret) {
334                 d_printf("DsReplicaGetInfo() failed for DRSUAPI_DS_REPLICA_INFO_KCC_DSA_CONNECT_FAILURES");
335                 return false;
336         }
337         reps_to = replica_info.repsto;
338
339         for (i = 0; i < reps_to->count; i++) {
340                 d_printf("\n");
341                 net_drs_showrepl_print_heighbor(drs_ctx, &reps_to->array[i]);
342         }
343
344         return true;
345 }
346
347 /**
348  * Prints all connections under
349  * NTDS Settings for target DC.
350  *
351  * NOTE: All connections are printed
352  * no matter what their status is
353  */
354 static bool net_drs_showrepl_print_connection_objects(struct net_drs_context *drs_ctx)
355 {
356         int i;
357         int conn_count;
358         struct ldb_message **conn_msgs;
359         struct ldb_dn *dn;
360         uint32_t options;
361         const char *dc_dns_name;
362         TALLOC_CTX *mem_ctx;
363         const char *conn_attr[] = {
364                         "name",
365                         "enabledConnection",
366                         "fromServer",
367                         "mS-DS-ReplicatesNCReason",
368                         "options",
369                         "schedule",
370                         "transportType",
371                         "whenChanged",
372                         "whenCreated",
373                         NULL
374         };
375
376         mem_ctx = talloc_new(drs_ctx);
377
378         d_printf("\n==== KCC CONNECTION OBJECTS ====\n");
379
380         /* Get NTDS Settings DN string for the DC */
381         dn = samdb_result_dn(drs_ctx->ldap.ldb, mem_ctx,
382                              drs_ctx->ldap.rootdse, "dsServiceName", NULL);
383         NET_DRS_CHECK_GOTO(dn, failed, "No dsServiceName value in RootDSE!\n");
384
385         /* DNS host name for target DC */
386         dc_dns_name = samdb_result_string(drs_ctx->ldap.rootdse , "dnsHostName", NULL);
387         NET_DRS_CHECK_GOTO(dc_dns_name, failed, "No dsServiceName value in dnsHostName!\n");
388
389         /* Enum. Connection objects under NTDS Settings */
390         conn_count = gendb_search(drs_ctx->ldap.ldb, mem_ctx, dn,
391                                   &conn_msgs, conn_attr, "(objectClass=nTDSConnection)");
392         if (conn_count == -1) {
393                 d_printf("Error searching Connections for %s, Possible error: %s\n",
394                          ldb_dn_get_linearized(dn),
395                          ldb_errstring(drs_ctx->ldap.ldb));
396                 goto failed;
397         }
398
399         for (i = 0; i < conn_count; i++) {
400                 int k;
401                 const char *transport_type;
402                 struct ldb_message_element *msg_elem;
403                 struct ldb_message *conn_msg = conn_msgs[i];
404
405                 d_printf("Connection --\n");
406                 d_printf("\tConnection name : %s\n",
407                          samdb_result_string(conn_msg, "name", NULL));
408                 d_printf("\tEnabled         : %s\n",
409                          samdb_result_string(conn_msg, "enabledConnection", "TRUE"));
410                 d_printf("\tServer DNS name : %s\n", dc_dns_name);
411                 d_printf("\tServer DN  name : %s\n",
412                          samdb_result_string(conn_msg, "fromServer", NULL));
413                 transport_type = samdb_result_string(conn_msg, "transportType", NULL);
414                 d_printf("\t\tTransportType: %s\n",
415                          net_drs_transport_type_str(drs_ctx, transport_type));
416                 /* TODO: print Connection options in friendly format */
417                 options = samdb_result_uint(conn_msg, "options", 0);
418                 d_printf("\t\toptions:  0x%08X\n", options);
419
420                 /* print replicated NCs for this connection */
421                 msg_elem = ldb_msg_find_element(conn_msg, "mS-DS-ReplicatesNCReason");
422                 if (!msg_elem) {
423                         d_printf("Warning: No NC replicated for Connection!\n");
424                         continue;
425                 }
426                 for (k = 0; k < msg_elem->num_values; k++) {
427                         struct dsdb_dn *bin_dn;
428
429                         bin_dn = dsdb_dn_parse(mem_ctx, drs_ctx->ldap.ldb,
430                                                &msg_elem->values[k], DSDB_SYNTAX_BINARY_DN);
431                         if (!bin_dn) {
432                                 d_printf("Unexpected: Failed to parse DN - %s\n",
433                                          msg_elem->values[k].data);
434                         }
435                         d_printf("\t\tReplicatesNC: %s\n", ldb_dn_get_linearized(bin_dn->dn));
436                         /* TODO: print Reason flags in friendly format */
437                         options = RIVAL(bin_dn->extra_part.data, 0);
438                         d_printf("\t\tReason: 0x%08X\n", options);
439                         d_printf("\t\t\tReplica link has been added.\n");
440                 }
441         }
442
443         talloc_free(mem_ctx);
444         return true;
445
446 failed:
447         talloc_free(mem_ctx);
448         return false;
449 }
450
451 /**
452  * Prints all DC's connections failure.
453  *
454  * NOTE: Still don't know exactly what
455  * this information means
456  */
457 static bool net_drs_showrepl_print_connect_failures(struct net_drs_context *drs_ctx)
458 {
459         int i;
460         bool bret;
461         struct ldb_dn *ntds_dn;
462         struct drsuapi_DsReplicaKccDsaFailure *failure;
463         struct drsuapi_DsReplicaKccDsaFailuresCtr *connect_failures;
464         union drsuapi_DsReplicaInfo replica_info;
465         TALLOC_CTX *mem_ctx;
466
467         d_printf("\n==== CONNECION FAILURES ====\n");
468
469         bret = net_drs_exec_DsReplicaGetInfo(drs_ctx,
470                                              DRSUAPI_DS_REPLICA_INFO_KCC_DSA_CONNECT_FAILURES,
471                                              &replica_info);
472         if (!bret) {
473                 d_printf("DsReplicaGetInfo() failed for DRSUAPI_DS_REPLICA_INFO_KCC_DSA_CONNECT_FAILURES");
474                 return false;
475         }
476         connect_failures = replica_info.connectfailures;
477
478         mem_ctx = talloc_new(drs_ctx);
479
480         for (i = 0; i < connect_failures->count; i++) {
481                 failure = &connect_failures->array[i];
482
483                 ntds_dn = ldb_dn_new(mem_ctx, drs_ctx->ldap.ldb, failure->dsa_obj_dn);
484                 d_printf("Source: %s\n", net_drs_dc_canonical_string(ntds_dn, mem_ctx));
485                 d_printf("******* %d CONSECUTIVE FAILURES since %s\n",
486                          failure->num_failures,
487                          nt_time_string(mem_ctx, failure->first_failure));
488                 d_printf("Last error: %d (%s):",
489                          W_ERROR_V(failure->last_result),
490                          win_errstr(failure->last_result));
491                 d_printf("\t\t\t%s\n", get_friendly_werror_msg(failure->last_result));
492         }
493
494         talloc_free(mem_ctx);
495         return true;
496 }
497
498 /**
499  * Prints all DC's link failures
500  */
501 static bool net_drs_showrepl_print_link_failures(struct net_drs_context *drs_ctx)
502 {
503         int i;
504         bool bret;
505         struct ldb_dn *ntds_dn;
506         struct drsuapi_DsReplicaKccDsaFailure *failure;
507         struct drsuapi_DsReplicaKccDsaFailuresCtr *link_failures;
508         union drsuapi_DsReplicaInfo replica_info;
509         TALLOC_CTX *mem_ctx;
510
511         d_printf("\n==== LINK FAILURES ====\n");
512
513         bret = net_drs_exec_DsReplicaGetInfo(drs_ctx,
514                                              DRSUAPI_DS_REPLICA_INFO_KCC_DSA_LINK_FAILURES, &replica_info);
515         if (!bret) {
516                 d_printf("DsReplicaGetInfo() failed for DRSUAPI_DS_REPLICA_INFO_KCC_DSA_CONNECT_FAILURES");
517                 return false;
518         }
519         link_failures = replica_info.linkfailures;
520
521         mem_ctx = talloc_new(drs_ctx);
522
523         for (i = 0; i < link_failures->count; i++) {
524                 failure = &link_failures->array[i];
525
526                 ntds_dn = ldb_dn_new(mem_ctx, drs_ctx->ldap.ldb, failure->dsa_obj_dn);
527                 d_printf("Source: %s\n", net_drs_dc_canonical_string(ntds_dn, mem_ctx));
528                 d_printf("******* %d CONSECUTIVE FAILURES since %s\n",
529                          failure->num_failures,
530                          nt_time_string(mem_ctx, failure->first_failure));
531                 d_printf("Last error: %d (%s):\n",
532                          W_ERROR_V(failure->last_result),
533                          win_errstr(failure->last_result));
534                 d_printf("\t\t\t%s\n", get_friendly_werror_msg(failure->last_result));
535         }
536
537         talloc_free(mem_ctx);
538         return true;
539 }
540
541 /**
542  * 'net drs showrepl' command entry point
543  */
544 int net_drs_showrepl_cmd(struct net_context *ctx, int argc, const char **argv)
545 {
546         const char *dc_name;
547         struct net_drs_context *drs_ctx = NULL;
548
549         /* only one arg expected */
550         if (argc != 1) {
551                 return net_drs_showrepl_usage(ctx, argc, argv);
552         }
553
554         dc_name = argv[0];
555
556         if (!net_drs_create_context(ctx, dc_name, &drs_ctx)) {
557                 goto failed;
558         }
559
560         /* Print DC and Site info */
561         if (!net_drs_showrepl_print_dc_info(drs_ctx)) {
562                 goto failed;
563         }
564
565         /* INBOUND Neighbors */
566         if (!net_drs_showrepl_print_inbound_neihbors(drs_ctx)) {
567                 goto failed;
568         }
569
570         /* OUTBOUND Neighbors */
571         if (!net_drs_showrepl_print_outbound_neihbors(drs_ctx)) {
572                 goto failed;
573         }
574
575         /* Connection objects for DC */
576         if (!net_drs_showrepl_print_connection_objects(drs_ctx)) {
577                 goto failed;
578         }
579
580         /* Connection failures */
581         if (!net_drs_showrepl_print_connect_failures(drs_ctx)) {
582                 goto failed;
583         }
584
585         /* Link failures */
586         if (!net_drs_showrepl_print_link_failures(drs_ctx)) {
587                 goto failed;
588         }
589
590         talloc_free(drs_ctx);
591         return 0;
592
593 failed:
594         talloc_free(drs_ctx);
595         return -1;
596 }
597
598 /**
599  * 'net drs showrepl' usage
600  */
601 int net_drs_showrepl_usage(struct net_context *ctx, int argc, const char **argv)
602 {
603         d_printf("net drs showrepl <DC_NAME>\n");
604         return 0;
605 }