2 Unix SMB/CIFS implementation.
4 Implements functions offered by repadmin.exe tool under Windows
6 Copyright (C) Kamen Mazdrashki <kamen.mazdrashki@postpath.com> 2010
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.
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.
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/>.
23 #include "samba_tool/samba_tool.h"
24 #include "samba_tool/drs/drs.h"
25 #include "lib/ldb/include/ldb.h"
26 #include "dsdb/samdb/samdb.h"
27 #include "lib/util/util_ldb.h"
31 * Parses NTDS Settings DN to find out:
36 static bool net_drs_parse_ntds_dn(struct ldb_dn
*ntds_dn
,
38 const char **_dc_name
,
39 const char **_site_name
,
40 const char **_domain_dns_name
)
42 struct ldb_dn
*dn
= NULL
;
43 const struct ldb_val
*val
;
45 dn
= ldb_dn_copy(mem_ctx
, ntds_dn
);
46 NET_DRS_NOMEM_GOTO(dn
, failed
);
48 /* remove NTDS Settings component */
49 if (!ldb_dn_remove_child_components(dn
, 1)) {
53 val
= ldb_dn_get_rdn_val(dn
);
54 *_dc_name
= talloc_strdup(mem_ctx
, (const char *)val
->data
);
57 /* remove DC and Servers components */
58 ldb_dn_remove_child_components(dn
, 2);
60 val
= ldb_dn_get_rdn_val(dn
);
61 *_site_name
= talloc_strdup(mem_ctx
, (const char *)val
->data
);
64 if (_domain_dns_name
) {
68 dns_name
= ldb_dn_canonical_string(mem_ctx
, dn
);
69 NET_DRS_NOMEM_GOTO(dns_name
, failed
);
71 pstr
= strchr(dns_name
, '/');
76 *_domain_dns_name
= dns_name
;
87 static const char * net_drs_dc_canonical_string(struct ldb_dn
*ntds_dn
, TALLOC_CTX
*mem_ctx
)
90 const char *site_name
;
93 if (ldb_dn_is_null(ntds_dn
)) {
97 if (!net_drs_parse_ntds_dn(ntds_dn
, mem_ctx
, &dc_name
, &site_name
, NULL
)) {
101 canonical_name
= talloc_asprintf(mem_ctx
, "%s\\%s", site_name
, dc_name
);
103 talloc_free(discard_const(dc_name
));
104 talloc_free(discard_const(site_name
));
106 return canonical_name
;
110 * Prints DC information for showrepl command
112 static bool net_drs_showrepl_print_dc_info(struct net_drs_context
*drs_ctx
)
116 const char *site_name
;
118 struct ldb_message
**ntds_msgs
;
119 struct ldb_message
**site_msgs
;
123 const char *ntds_attr
[] = {"options", "objectGuid", "invocationId", NULL
};
124 const char *site_ntds_attr
[] = {"options", "whenChanged", NULL
};
126 mem_ctx
= talloc_new(drs_ctx
);
128 /* Get NTDS Settings DN string for the DC */
129 dn
= samdb_result_dn(drs_ctx
->ldap
.ldb
, mem_ctx
,
130 drs_ctx
->ldap
.rootdse
, "dsServiceName", NULL
);
131 NET_DRS_CHECK_GOTO(dn
, failed
, "No dsServiceName value in RootDSE!\n");
133 /* parse NTDS Settings DN */
134 if (!net_drs_parse_ntds_dn(dn
, mem_ctx
, &dc_name
, &site_name
, NULL
)) {
135 d_printf("Unexpected: Failed to parse %s DN!\n",
136 ldb_dn_get_linearized(dn
));
140 /* Query DC record for DSA's NTDS Settings DN */
141 ret
= gendb_search_dn(drs_ctx
->ldap
.ldb
, mem_ctx
, dn
, &ntds_msgs
, ntds_attr
);
143 d_printf("Error while fetching %s, Possible error: %s\n",
144 ldb_dn_get_linearized(dn
),
145 ldb_errstring(drs_ctx
->ldap
.ldb
));
149 /* find out NTDS Site Settings DN */
150 if (!ldb_dn_remove_child_components(dn
, 3)) {
151 d_printf("Unexpected: ldb_dn_remove_child_components() failed!\n");
154 if (!ldb_dn_add_child_fmt(dn
, "CN=%s", "NTDS Site Settings")) {
155 d_printf("Unexpected: ldb_dn_add_child_fmt() failed!\n");
158 /* Query Site record for DSA's NTDS Settings DN */
159 ret
= gendb_search_dn(drs_ctx
->ldap
.ldb
, mem_ctx
, dn
, &site_msgs
, site_ntds_attr
);
161 d_printf("Error while fetching %s, Possible error: %s\n",
162 ldb_dn_get_linearized(dn
),
163 ldb_errstring(drs_ctx
->ldap
.ldb
));
167 /* Site-name\DC-name */
168 d_printf("%s\\%s\n", site_name
, dc_name
);
170 options
= ldb_msg_find_attr_as_uint(ntds_msgs
[0], "options", 0);
172 /* TODO: Print options as string in IS_GC... etc form */
173 d_printf("DSA Options: 0x%08X\n", options
);
175 d_printf("DSA Options: (none)\n");
178 options
= ldb_msg_find_attr_as_uint(site_msgs
[0], "options", 0);
180 /* TODO: Print options in string */
181 d_printf("DSA Options: 0x%08X\n", options
);
183 d_printf("Site Options: (none)\n");
186 guid
= samdb_result_guid(ntds_msgs
[0], "objectGUID");
187 d_printf("DSA object GUID: %s\n", GUID_string(mem_ctx
, &guid
));
188 /* DSA invocationId */
189 guid
= samdb_result_guid(ntds_msgs
[0], "invocationId");
190 d_printf("DSA invocationID: %s\n", GUID_string(mem_ctx
, &guid
));
192 talloc_free(mem_ctx
);
196 talloc_free(mem_ctx
);
201 * Convenience function to call DsReplicaGetInfo
203 static bool net_drs_exec_DsReplicaGetInfo(struct net_drs_context
*drs_ctx
,
204 enum drsuapi_DsReplicaInfoType info_type
,
205 union drsuapi_DsReplicaInfo
*_replica_info
)
208 struct drsuapi_DsReplicaGetInfo r
;
209 union drsuapi_DsReplicaGetInfoRequest req
;
210 enum drsuapi_DsReplicaInfoType info_type_got
;
211 struct net_drs_connection
*drs_conn
= drs_ctx
->drs_conn
;
214 req
.req1
.info_type
= info_type
;
216 r
.in
.bind_handle
= &drs_conn
->bind_handle
;
219 r
.out
.info
= _replica_info
;
220 r
.out
.info_type
= &info_type_got
;
222 status
= dcerpc_drsuapi_DsReplicaGetInfo_r(drs_conn
->drs_handle
, drs_ctx
, &r
);
223 if (!NT_STATUS_IS_OK(status
)) {
224 const char *errstr
= nt_errstr(status
);
225 d_printf("DsReplicaGetInfo failed - %s.\n", errstr
);
227 } else if (!W_ERROR_IS_OK(r
.out
.result
)) {
228 d_printf("DsReplicaGetInfo failed - %s.\n", win_errstr(r
.out
.result
));
232 if (info_type
!= info_type_got
) {
233 d_printf("DsReplicaGetInfo: Error requested info %d, got info %d.\n",
234 info_type
, info_type_got
);
242 * Return transport type string for given transport object DN.
243 * Currently always return 'RPC'.
245 * TODO: Implement getting transport type for all kind of transports
248 net_drs_transport_type_str(struct net_drs_context
*drs_ctx
, const char *transport_obj_dn
)
254 * Prints most of the info we got about
255 * a replication partner
257 static bool net_drs_showrepl_print_neighbor(struct net_drs_context
*drs_ctx
,
258 struct drsuapi_DsReplicaNeighbour
*neighbor
)
260 struct ldb_dn
*ntds_dn
;
263 mem_ctx
= talloc_new(drs_ctx
);
265 ntds_dn
= ldb_dn_new(drs_ctx
, drs_ctx
->ldap
.ldb
, neighbor
->source_dsa_obj_dn
);
266 NET_DRS_NOMEM_GOTO(ntds_dn
, failed
);
268 d_printf("%s\n", neighbor
->naming_context_dn
);
269 /* TODO: Determine connection type */
270 d_printf("\t%s via %s\n",
271 net_drs_dc_canonical_string(ntds_dn
, mem_ctx
),
272 net_drs_transport_type_str(drs_ctx
, neighbor
->transport_obj_dn
));
273 d_printf("\t\tDSA object GUID: %s\n", GUID_string(mem_ctx
, &neighbor
->source_dsa_obj_guid
));
274 if (W_ERROR_IS_OK(neighbor
->result_last_attempt
)) {
275 d_printf("\t\tLast attempt @ %s was successful.\n",
276 nt_time_string(mem_ctx
, neighbor
->last_attempt
));
278 d_printf("\t\tLast attempt @ %s failed, result %d (%s):\n",
279 nt_time_string(mem_ctx
, neighbor
->last_attempt
),
280 W_ERROR_V(neighbor
->result_last_attempt
),
281 win_errstr(neighbor
->result_last_attempt
));
282 d_printf("\t\t\t%s\n", get_friendly_werror_msg(neighbor
->result_last_attempt
));
284 d_printf("\t\t%d consecutive failure(s).\n", neighbor
->consecutive_sync_failures
);
285 d_printf("\t\tLast success @ %s\n", nt_time_string(mem_ctx
, neighbor
->last_success
));
287 talloc_free(mem_ctx
);
291 talloc_free(mem_ctx
);
296 * Prints list of all servers that target DC
299 static bool net_drs_showrepl_print_inbound_neihbors(struct net_drs_context
*drs_ctx
)
303 struct drsuapi_DsReplicaNeighbourCtr
*reps_from
;
304 union drsuapi_DsReplicaInfo replica_info
;
306 d_printf("\n==== INBOUND NEIGHBORS ====\n");
308 bret
= net_drs_exec_DsReplicaGetInfo(drs_ctx
,
309 DRSUAPI_DS_REPLICA_INFO_NEIGHBORS
, &replica_info
);
311 d_printf("DsReplicaGetInfo() failed for DRSUAPI_DS_REPLICA_INFO_KCC_DSA_CONNECT_FAILURES.\n");
314 reps_from
= replica_info
.neighbours
;
316 for (i
= 0; i
< reps_from
->count
; i
++) {
318 net_drs_showrepl_print_neighbor(drs_ctx
, &reps_from
->array
[i
]);
325 * Prints list of all servers that target DC
326 * notifies for changes
328 static bool net_drs_showrepl_print_outbound_neihbors(struct net_drs_context
*drs_ctx
)
332 struct drsuapi_DsReplicaNeighbourCtr
*reps_to
;
333 union drsuapi_DsReplicaInfo replica_info
;
335 d_printf("\n==== OUTBOUND NEIGHBORS ====\n");
337 bret
= net_drs_exec_DsReplicaGetInfo(drs_ctx
,
338 DRSUAPI_DS_REPLICA_INFO_REPSTO
, &replica_info
);
340 d_printf("DsReplicaGetInfo() failed for DRSUAPI_DS_REPLICA_INFO_KCC_DSA_CONNECT_FAILURES.\n");
343 reps_to
= replica_info
.repsto
;
345 for (i
= 0; i
< reps_to
->count
; i
++) {
347 net_drs_showrepl_print_neighbor(drs_ctx
, &reps_to
->array
[i
]);
354 * Prints all connections under
355 * NTDS Settings for target DC.
357 * NOTE: All connections are printed
358 * no matter what their status is
360 static bool net_drs_showrepl_print_connection_objects(struct net_drs_context
*drs_ctx
)
364 struct ldb_message
**conn_msgs
;
367 const char *dc_dns_name
;
369 const char *conn_attr
[] = {
373 "mS-DS-ReplicatesNCReason",
382 mem_ctx
= talloc_new(drs_ctx
);
384 d_printf("\n==== KCC CONNECTION OBJECTS ====\n");
386 /* Get NTDS Settings DN string for the DC */
387 dn
= samdb_result_dn(drs_ctx
->ldap
.ldb
, mem_ctx
,
388 drs_ctx
->ldap
.rootdse
, "dsServiceName", NULL
);
389 NET_DRS_CHECK_GOTO(dn
, failed
, "No dsServiceName value in RootDSE!\n");
391 /* DNS host name for target DC */
392 dc_dns_name
= ldb_msg_find_attr_as_string(drs_ctx
->ldap
.rootdse
, "dnsHostName", NULL
);
393 NET_DRS_CHECK_GOTO(dc_dns_name
, failed
, "No dsServiceName value in dnsHostName!\n");
395 /* Enum. Connection objects under NTDS Settings */
396 conn_count
= gendb_search(drs_ctx
->ldap
.ldb
, mem_ctx
, dn
,
397 &conn_msgs
, conn_attr
, "(objectClass=nTDSConnection)");
398 if (conn_count
== -1) {
399 d_printf("Error searching Connections for %s, Possible error: %s\n",
400 ldb_dn_get_linearized(dn
),
401 ldb_errstring(drs_ctx
->ldap
.ldb
));
405 for (i
= 0; i
< conn_count
; i
++) {
407 const char *transport_type
;
408 struct ldb_message_element
*msg_elem
;
409 struct ldb_message
*conn_msg
= conn_msgs
[i
];
411 d_printf("Connection --\n");
412 d_printf("\tConnection name : %s\n",
413 ldb_msg_find_attr_as_string(conn_msg
, "name", NULL
));
414 d_printf("\tEnabled : %s\n",
415 ldb_msg_find_attr_as_string(conn_msg
, "enabledConnection", "TRUE"));
416 d_printf("\tServer DNS name : %s\n", dc_dns_name
);
417 d_printf("\tServer DN name : %s\n",
418 ldb_msg_find_attr_as_string(conn_msg
, "fromServer", NULL
));
419 transport_type
= ldb_msg_find_attr_as_string(conn_msg
, "transportType", NULL
);
420 d_printf("\t\tTransportType: %s\n",
421 net_drs_transport_type_str(drs_ctx
, transport_type
));
422 /* TODO: print Connection options in friendly format */
423 options
= ldb_msg_find_attr_as_uint(conn_msg
, "options", 0);
424 d_printf("\t\toptions: 0x%08X\n", options
);
426 /* print replicated NCs for this connection */
427 msg_elem
= ldb_msg_find_element(conn_msg
, "mS-DS-ReplicatesNCReason");
429 d_printf("Warning: No NC replicated for Connection!\n");
432 for (k
= 0; k
< msg_elem
->num_values
; k
++) {
433 struct dsdb_dn
*bin_dn
;
435 bin_dn
= dsdb_dn_parse(mem_ctx
, drs_ctx
->ldap
.ldb
,
436 &msg_elem
->values
[k
], DSDB_SYNTAX_BINARY_DN
);
438 d_printf("Unexpected: Failed to parse DN - %s\n",
439 msg_elem
->values
[k
].data
);
441 d_printf("\t\tReplicatesNC: %s\n", ldb_dn_get_linearized(bin_dn
->dn
));
442 /* TODO: print Reason flags in friendly format */
443 options
= RIVAL(bin_dn
->extra_part
.data
, 0);
444 d_printf("\t\tReason: 0x%08X\n", options
);
445 d_printf("\t\t\tReplica link has been added.\n");
449 talloc_free(mem_ctx
);
453 talloc_free(mem_ctx
);
458 * Prints all DC's connections failure.
460 * NOTE: Still don't know exactly what
461 * this information means
463 static bool net_drs_showrepl_print_connect_failures(struct net_drs_context
*drs_ctx
)
467 struct ldb_dn
*ntds_dn
;
468 struct drsuapi_DsReplicaKccDsaFailure
*failure
;
469 struct drsuapi_DsReplicaKccDsaFailuresCtr
*connect_failures
;
470 union drsuapi_DsReplicaInfo replica_info
;
473 d_printf("\n==== CONNECION FAILURES ====\n");
475 bret
= net_drs_exec_DsReplicaGetInfo(drs_ctx
,
476 DRSUAPI_DS_REPLICA_INFO_KCC_DSA_CONNECT_FAILURES
,
479 d_printf("DsReplicaGetInfo() failed for DRSUAPI_DS_REPLICA_INFO_KCC_DSA_CONNECT_FAILURES.\n");
482 connect_failures
= replica_info
.connectfailures
;
484 mem_ctx
= talloc_new(drs_ctx
);
486 for (i
= 0; i
< connect_failures
->count
; i
++) {
487 failure
= &connect_failures
->array
[i
];
489 ntds_dn
= ldb_dn_new(mem_ctx
, drs_ctx
->ldap
.ldb
, failure
->dsa_obj_dn
);
490 d_printf("Source: %s\n", net_drs_dc_canonical_string(ntds_dn
, mem_ctx
));
491 d_printf("******* %d CONSECUTIVE FAILURES since %s\n",
492 failure
->num_failures
,
493 nt_time_string(mem_ctx
, failure
->first_failure
));
494 d_printf("Last error: %d (%s):\n",
495 W_ERROR_V(failure
->last_result
),
496 win_errstr(failure
->last_result
));
497 d_printf("\t\t\t%s\n", get_friendly_werror_msg(failure
->last_result
));
500 talloc_free(mem_ctx
);
505 * Prints all DC's link failures
507 static bool net_drs_showrepl_print_link_failures(struct net_drs_context
*drs_ctx
)
511 struct ldb_dn
*ntds_dn
;
512 struct drsuapi_DsReplicaKccDsaFailure
*failure
;
513 struct drsuapi_DsReplicaKccDsaFailuresCtr
*link_failures
;
514 union drsuapi_DsReplicaInfo replica_info
;
517 d_printf("\n==== LINK FAILURES ====\n");
519 bret
= net_drs_exec_DsReplicaGetInfo(drs_ctx
,
520 DRSUAPI_DS_REPLICA_INFO_KCC_DSA_LINK_FAILURES
, &replica_info
);
522 d_printf("DsReplicaGetInfo() failed for DRSUAPI_DS_REPLICA_INFO_KCC_DSA_CONNECT_FAILURES.\n");
525 link_failures
= replica_info
.linkfailures
;
527 mem_ctx
= talloc_new(drs_ctx
);
529 for (i
= 0; i
< link_failures
->count
; i
++) {
530 failure
= &link_failures
->array
[i
];
532 ntds_dn
= ldb_dn_new(mem_ctx
, drs_ctx
->ldap
.ldb
, failure
->dsa_obj_dn
);
533 d_printf("Source: %s\n", net_drs_dc_canonical_string(ntds_dn
, mem_ctx
));
534 d_printf("******* %d CONSECUTIVE FAILURES since %s\n",
535 failure
->num_failures
,
536 nt_time_string(mem_ctx
, failure
->first_failure
));
537 d_printf("Last error: %d (%s):\n",
538 W_ERROR_V(failure
->last_result
),
539 win_errstr(failure
->last_result
));
540 d_printf("\t\t\t%s\n", get_friendly_werror_msg(failure
->last_result
));
543 talloc_free(mem_ctx
);
548 * 'samba-tool drs showrepl' command entry point
550 int net_drs_showrepl_cmd(struct net_context
*ctx
, int argc
, const char **argv
)
553 struct net_drs_context
*drs_ctx
= NULL
;
555 /* only one arg expected */
557 return net_drs_showrepl_usage(ctx
, argc
, argv
);
562 if (!net_drs_create_context(ctx
, dc_name
, &drs_ctx
)) {
566 /* Print DC and Site info */
567 if (!net_drs_showrepl_print_dc_info(drs_ctx
)) {
571 /* INBOUND Neighbors */
572 if (!net_drs_showrepl_print_inbound_neihbors(drs_ctx
)) {
576 /* OUTBOUND Neighbors */
577 if (!net_drs_showrepl_print_outbound_neihbors(drs_ctx
)) {
581 /* Connection objects for DC */
582 if (!net_drs_showrepl_print_connection_objects(drs_ctx
)) {
586 /* Connection failures */
587 if (!net_drs_showrepl_print_connect_failures(drs_ctx
)) {
592 if (!net_drs_showrepl_print_link_failures(drs_ctx
)) {
596 talloc_free(drs_ctx
);
600 talloc_free(drs_ctx
);
605 * 'samba-tool drs showrepl' usage
607 int net_drs_showrepl_usage(struct net_context
*ctx
, int argc
, const char **argv
)
609 d_printf("samba-tool drs showrepl <DC_NAME>\n");