2 Unix SMB/CIFS implementation.
4 DsGetNCChanges replication test
6 Copyright (C) Stefan (metze) Metzmacher 2005
7 Copyright (C) Brad Henry 2005
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "lib/util/util_file.h"
25 #include "lib/cmdline/cmdline.h"
26 #include "librpc/gen_ndr/ndr_drsuapi_c.h"
27 #include "librpc/gen_ndr/ndr_drsblobs.h"
28 #include "libcli/cldap/cldap.h"
29 #include "torture/torture.h"
30 #include "../libcli/drsuapi/drsuapi.h"
31 #include "auth/gensec/gensec.h"
32 #include "param/param.h"
33 #include "dsdb/samdb/samdb.h"
34 #include "torture/rpc/torture_rpc.h"
35 #include "torture/drs/proto.h"
36 #include "lib/tsocket/tsocket.h"
37 #include "libcli/resolve/resolve.h"
38 #include "lib/util/util_paths.h"
42 struct DsSyncBindInfo
{
43 struct dcerpc_pipe
*drs_pipe
;
44 struct dcerpc_binding_handle
*drs_handle
;
45 struct drsuapi_DsBind req
;
46 struct GUID bind_guid
;
47 struct drsuapi_DsBindInfoCtr our_bind_info_ctr
;
48 struct drsuapi_DsBindInfo28 our_bind_info28
;
49 struct drsuapi_DsBindInfo28 peer_bind_info28
;
50 struct policy_handle bind_handle
;
53 struct DsSyncLDAPInfo
{
54 struct ldb_context
*ldb
;
58 struct dcerpc_binding
*drsuapi_binding
;
61 const char *dest_address
;
62 const char *domain_dn
;
63 const char *config_dn
;
64 const char *schema_dn
;
66 /* what we need to do as 'Administrator' */
68 struct cli_credentials
*credentials
;
69 struct DsSyncBindInfo drsuapi
;
70 struct DsSyncLDAPInfo ldap
;
73 /* what we need to do as the new dc machine account */
75 struct cli_credentials
*credentials
;
76 struct DsSyncBindInfo drsuapi
;
77 struct drsuapi_DsGetDCInfo2 dc_info2
;
78 struct GUID invocation_id
;
79 struct GUID object_guid
;
82 /* info about the old dc */
84 struct drsuapi_DsGetDomainControllerInfo dc_info
;
88 static struct DsSyncTest
*test_create_context(struct torture_context
*tctx
)
91 struct DsSyncTest
*ctx
;
92 struct drsuapi_DsBindInfo28
*our_bind_info28
;
93 struct drsuapi_DsBindInfoCtr
*our_bind_info_ctr
;
94 const char *binding
= torture_setting_string(tctx
, "binding", NULL
);
98 ctx
= talloc_zero(tctx
, struct DsSyncTest
);
99 if (!ctx
) return NULL
;
101 status
= dcerpc_parse_binding(ctx
, binding
, &ctx
->drsuapi_binding
);
102 if (!NT_STATUS_IS_OK(status
)) {
103 printf("Bad binding string %s\n", binding
);
106 status
= dcerpc_binding_set_flags(ctx
->drsuapi_binding
,
107 DCERPC_SIGN
| DCERPC_SEAL
, 0);
108 if (!NT_STATUS_IS_OK(status
)) {
109 printf("dcerpc_binding_set_flags - %s\n", nt_errstr(status
));
113 host
= dcerpc_binding_get_string_option(ctx
->drsuapi_binding
, "host");
115 ctx
->ldap_url
= talloc_asprintf(ctx
, "ldap://%s", host
);
117 make_nbt_name_server(&name
, host
);
119 /* do an initial name resolution to find its IP */
120 status
= resolve_name_ex(lpcfg_resolve_context(tctx
->lp_ctx
),
122 &ctx
->dest_address
, tctx
->ev
);
123 if (!NT_STATUS_IS_OK(status
)) {
124 printf("Failed to resolve %s - %s\n",
125 name
.name
, nt_errstr(status
));
130 ctx
->admin
.credentials
= samba_cmdline_get_creds();
132 our_bind_info28
= &ctx
->admin
.drsuapi
.our_bind_info28
;
133 our_bind_info28
->supported_extensions
= 0xFFFFFFFF;
134 our_bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRYREPLY_V3
;
135 our_bind_info28
->site_guid
= GUID_zero();
136 our_bind_info28
->pid
= 0;
137 our_bind_info28
->repl_epoch
= 1;
139 our_bind_info_ctr
= &ctx
->admin
.drsuapi
.our_bind_info_ctr
;
140 our_bind_info_ctr
->length
= 28;
141 our_bind_info_ctr
->info
.info28
= *our_bind_info28
;
143 GUID_from_string(DRSUAPI_DS_BIND_GUID
, &ctx
->admin
.drsuapi
.bind_guid
);
145 ctx
->admin
.drsuapi
.req
.in
.bind_guid
= &ctx
->admin
.drsuapi
.bind_guid
;
146 ctx
->admin
.drsuapi
.req
.in
.bind_info
= our_bind_info_ctr
;
147 ctx
->admin
.drsuapi
.req
.out
.bind_handle
= &ctx
->admin
.drsuapi
.bind_handle
;
150 ctx
->new_dc
.credentials
= samba_cmdline_get_creds();
152 our_bind_info28
= &ctx
->new_dc
.drsuapi
.our_bind_info28
;
153 our_bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_BASE
;
154 our_bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_ASYNC_REPLICATION
;
155 our_bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_REMOVEAPI
;
156 our_bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_MOVEREQ_V2
;
157 our_bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GETCHG_COMPRESS
;
158 our_bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V1
;
159 our_bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_RESTORE_USN_OPTIMIZATION
;
160 our_bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_KCC_EXECUTE
;
161 our_bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRY_V2
;
162 our_bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_LINKED_VALUE_REPLICATION
;
163 our_bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V2
;
164 our_bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_INSTANCE_TYPE_NOT_REQ_ON_MOD
;
165 our_bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_CRYPTO_BIND
;
166 our_bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GET_REPL_INFO
;
167 our_bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_STRONG_ENCRYPTION
;
168 our_bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V01
;
169 our_bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_TRANSITIVE_MEMBERSHIP
;
170 our_bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_ADD_SID_HISTORY
;
171 our_bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_POST_BETA3
;
172 our_bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GET_MEMBERSHIPS2
;
173 our_bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V6
;
174 our_bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_NONDOMAIN_NCS
;
175 our_bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8
;
176 our_bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V5
;
177 our_bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V6
;
178 our_bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRYREPLY_V3
;
179 our_bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V7
;
180 our_bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_VERIFY_OBJECT
;
181 if (lpcfg_parm_bool(tctx
->lp_ctx
, NULL
, "dssync", "xpress", false)) {
182 our_bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_XPRESS_COMPRESS
;
184 our_bind_info28
->site_guid
= GUID_zero();
185 our_bind_info28
->pid
= 0;
186 our_bind_info28
->repl_epoch
= 0;
188 our_bind_info_ctr
= &ctx
->new_dc
.drsuapi
.our_bind_info_ctr
;
189 our_bind_info_ctr
->length
= 28;
190 our_bind_info_ctr
->info
.info28
= *our_bind_info28
;
192 GUID_from_string(DRSUAPI_DS_BIND_GUID_W2K3
, &ctx
->new_dc
.drsuapi
.bind_guid
);
194 ctx
->new_dc
.drsuapi
.req
.in
.bind_guid
= &ctx
->new_dc
.drsuapi
.bind_guid
;
195 ctx
->new_dc
.drsuapi
.req
.in
.bind_info
= our_bind_info_ctr
;
196 ctx
->new_dc
.drsuapi
.req
.out
.bind_handle
= &ctx
->new_dc
.drsuapi
.bind_handle
;
198 ctx
->new_dc
.invocation_id
= ctx
->new_dc
.drsuapi
.bind_guid
;
205 static bool _test_DsBind(struct torture_context
*tctx
,
206 struct DsSyncTest
*ctx
, struct cli_credentials
*credentials
, struct DsSyncBindInfo
*b
)
211 status
= dcerpc_pipe_connect_b(ctx
,
212 &b
->drs_pipe
, ctx
->drsuapi_binding
,
214 credentials
, tctx
->ev
, tctx
->lp_ctx
);
216 if (!NT_STATUS_IS_OK(status
)) {
217 printf("Failed to connect to server as a BDC: %s\n", nt_errstr(status
));
220 b
->drs_handle
= b
->drs_pipe
->binding_handle
;
222 status
= dcerpc_drsuapi_DsBind_r(b
->drs_handle
, ctx
, &b
->req
);
223 if (!NT_STATUS_IS_OK(status
)) {
224 const char *errstr
= nt_errstr(status
);
225 printf("dcerpc_drsuapi_DsBind failed - %s\n", errstr
);
227 } else if (!W_ERROR_IS_OK(b
->req
.out
.result
)) {
228 printf("DsBind failed - %s\n", win_errstr(b
->req
.out
.result
));
232 ZERO_STRUCT(b
->peer_bind_info28
);
233 if (b
->req
.out
.bind_info
) {
234 switch (b
->req
.out
.bind_info
->length
) {
236 struct drsuapi_DsBindInfo24
*info24
;
237 info24
= &b
->req
.out
.bind_info
->info
.info24
;
238 b
->peer_bind_info28
.supported_extensions
= info24
->supported_extensions
;
239 b
->peer_bind_info28
.site_guid
= info24
->site_guid
;
240 b
->peer_bind_info28
.pid
= info24
->pid
;
241 b
->peer_bind_info28
.repl_epoch
= 0;
245 b
->peer_bind_info28
= b
->req
.out
.bind_info
->info
.info28
;
249 struct drsuapi_DsBindInfo32
*info32
;
250 info32
= &b
->req
.out
.bind_info
->info
.info32
;
251 b
->peer_bind_info28
.supported_extensions
= info32
->supported_extensions
;
252 b
->peer_bind_info28
.site_guid
= info32
->site_guid
;
253 b
->peer_bind_info28
.pid
= info32
->pid
;
254 b
->peer_bind_info28
.repl_epoch
= info32
->repl_epoch
;
258 struct drsuapi_DsBindInfo48
*info48
;
259 info48
= &b
->req
.out
.bind_info
->info
.info48
;
260 b
->peer_bind_info28
.supported_extensions
= info48
->supported_extensions
;
261 b
->peer_bind_info28
.site_guid
= info48
->site_guid
;
262 b
->peer_bind_info28
.pid
= info48
->pid
;
263 b
->peer_bind_info28
.repl_epoch
= info48
->repl_epoch
;
267 struct drsuapi_DsBindInfo52
*info52
;
268 info52
= &b
->req
.out
.bind_info
->info
.info52
;
269 b
->peer_bind_info28
.supported_extensions
= info52
->supported_extensions
;
270 b
->peer_bind_info28
.site_guid
= info52
->site_guid
;
271 b
->peer_bind_info28
.pid
= info52
->pid
;
272 b
->peer_bind_info28
.repl_epoch
= info52
->repl_epoch
;
276 printf("DsBind - warning: unknown BindInfo length: %u\n",
277 b
->req
.out
.bind_info
->length
);
284 static bool test_LDAPBind(struct torture_context
*tctx
, struct DsSyncTest
*ctx
,
285 struct cli_credentials
*credentials
, struct DsSyncLDAPInfo
*l
)
289 struct ldb_context
*ldb
;
291 const char *modules_option
[] = { "modules:paged_searches", NULL
};
292 ctx
->admin
.ldap
.ldb
= ldb
= ldb_init(ctx
, tctx
->ev
);
297 /* Despite us loading the schema from the AD server, we need
298 * the samba handlers to get the extended DN syntax stuff */
299 ret
= ldb_register_samba_handlers(ldb
);
300 if (ret
!= LDB_SUCCESS
) {
305 ldb_set_modules_dir(ldb
, modules_path(ldb
, "ldb"));
307 if (ldb_set_opaque(ldb
, "credentials", credentials
)) {
312 if (ldb_set_opaque(ldb
, "loadparm", tctx
->lp_ctx
)) {
317 ret
= ldb_connect(ldb
, ctx
->ldap_url
, 0, modules_option
);
318 if (ret
!= LDB_SUCCESS
) {
320 torture_assert_int_equal(tctx
, ret
, LDB_SUCCESS
, "Failed to make LDB connection to target");
323 printf("connected to LDAP: %s\n", ctx
->ldap_url
);
328 static bool test_GetInfo(struct torture_context
*tctx
, struct DsSyncTest
*ctx
)
330 struct ldb_context
*ldb
= ctx
->admin
.ldap
.ldb
;
332 /* We must have LDB connection ready by this time */
333 SMB_ASSERT(ldb
!= NULL
);
335 ctx
->domain_dn
= ldb_dn_get_linearized(ldb_get_default_basedn(ldb
));
336 torture_assert(tctx
, ctx
->domain_dn
!= NULL
, "Failed to get Domain DN");
338 ctx
->config_dn
= ldb_dn_get_linearized(ldb_get_config_basedn(ldb
));
339 torture_assert(tctx
, ctx
->config_dn
!= NULL
, "Failed to get Domain DN");
341 ctx
->schema_dn
= ldb_dn_get_linearized(ldb_get_schema_basedn(ldb
));
342 torture_assert(tctx
, ctx
->schema_dn
!= NULL
, "Failed to get Domain DN");
347 static bool test_analyse_objects(struct torture_context
*tctx
,
348 struct DsSyncTest
*ctx
,
349 const char *partition
,
350 const struct drsuapi_DsReplicaOIDMapping_Ctr
*mapping_ctr
,
351 uint32_t object_count
,
352 const struct drsuapi_DsReplicaObjectListItemEx
*first_object
,
353 const DATA_BLOB
*gensec_skey
)
355 static uint32_t object_id
;
356 const char *save_values_dir
;
357 const struct drsuapi_DsReplicaObjectListItemEx
*cur
;
358 struct ldb_context
*ldb
= ctx
->admin
.ldap
.ldb
;
359 struct ldb_dn
*deleted_dn
;
362 struct dsdb_extended_replicated_objects
*objs
;
363 struct ldb_extended_dn_control
*extended_dn_ctrl
;
364 struct dsdb_schema
*ldap_schema
;
365 struct ldb_dn
*partition_dn
= ldb_dn_new(tctx
, ldb
, partition
);
367 torture_assert_not_null(tctx
, partition_dn
, "Failed to parse partition DN as as DN");
369 /* load dsdb_schema using remote prefixMap */
371 drs_util_dsdb_schema_load_ldb(tctx
, ldb
, mapping_ctr
, false),
372 "drs_util_dsdb_schema_load_ldb() failed");
373 ldap_schema
= dsdb_get_schema(ldb
, NULL
);
375 status
= dsdb_replicated_objects_convert(ldb
,
386 torture_assert_werr_ok(tctx
, status
, "dsdb_extended_replicated_objects_convert() failed!");
388 extended_dn_ctrl
= talloc(objs
, struct ldb_extended_dn_control
);
389 extended_dn_ctrl
->type
= 1;
391 deleted_dn
= ldb_dn_new(objs
, ldb
, partition
);
392 ldb_dn_add_child_fmt(deleted_dn
, "CN=Deleted Objects");
394 for (i
=0; i
< objs
->num_objects
; i
++) {
395 struct ldb_request
*search_req
;
396 struct ldb_result
*res
;
397 struct ldb_message
*new_msg
, *drs_msg
, *ldap_msg
;
398 size_t num_attrs
= objs
->objects
[i
].msg
->num_elements
+1;
399 const char **attrs
= talloc_array(objs
, const char *, num_attrs
);
400 for (j
=0; j
< objs
->objects
[i
].msg
->num_elements
; j
++) {
401 attrs
[j
] = objs
->objects
[i
].msg
->elements
[j
].name
;
404 res
= talloc_zero(objs
, struct ldb_result
);
406 return LDB_ERR_OPERATIONS_ERROR
;
408 ret
= ldb_build_search_req(&search_req
, ldb
, objs
,
409 objs
->objects
[i
].msg
->dn
,
415 ldb_search_default_callback
,
417 if (ret
!= LDB_SUCCESS
) {
420 talloc_steal(search_req
, res
);
421 ret
= ldb_request_add_control(search_req
, LDB_CONTROL_SHOW_DELETED_OID
, true, NULL
);
422 if (ret
!= LDB_SUCCESS
) {
426 ret
= ldb_request_add_control(search_req
, LDB_CONTROL_EXTENDED_DN_OID
, true, extended_dn_ctrl
);
427 if (ret
!= LDB_SUCCESS
) {
431 ret
= ldb_request(ldb
, search_req
);
432 if (ret
== LDB_SUCCESS
) {
433 ret
= ldb_wait(search_req
->handle
, LDB_WAIT_ALL
);
436 torture_assert_int_equal(tctx
, ret
, LDB_SUCCESS
,
437 talloc_asprintf(tctx
,
438 "Could not re-fetch object just delivered over DRS: %s",
439 ldb_errstring(ldb
)));
440 torture_assert_int_equal(tctx
, res
->count
, 1, "Could not re-fetch object just delivered over DRS");
441 ldap_msg
= res
->msgs
[0];
442 for (j
=0; j
< ldap_msg
->num_elements
; j
++) {
443 ldap_msg
->elements
[j
].flags
= LDB_FLAG_MOD_ADD
;
444 /* For unknown reasons, there is no nTSecurityDescriptor on cn=deleted objects over LDAP, but there is over DRS! Skip it on both transports for now here so */
445 if ((ldb_attr_cmp(ldap_msg
->elements
[j
].name
, "nTSecurityDescriptor") == 0) &&
446 (ldb_dn_compare(ldap_msg
->dn
, deleted_dn
) == 0)) {
447 ldb_msg_remove_element(ldap_msg
, &ldap_msg
->elements
[j
]);
453 ret
= ldb_msg_normalize(ldb
, search_req
,
454 objs
->objects
[i
].msg
, &drs_msg
);
455 torture_assert(tctx
, ret
== LDB_SUCCESS
,
456 "ldb_msg_normalize() has failed");
458 for (j
=0; j
< drs_msg
->num_elements
; j
++) {
459 if (drs_msg
->elements
[j
].num_values
== 0) {
460 ldb_msg_remove_element(drs_msg
, &drs_msg
->elements
[j
]);
464 /* For unknown reasons, there is no nTSecurityDescriptor on cn=deleted objects over LDAP, but there is over DRS! */
465 } else if ((ldb_attr_cmp(drs_msg
->elements
[j
].name
, "nTSecurityDescriptor") == 0) &&
466 (ldb_dn_compare(drs_msg
->dn
, deleted_dn
) == 0)) {
467 ldb_msg_remove_element(drs_msg
, &drs_msg
->elements
[j
]);
470 } else if (ldb_attr_cmp(drs_msg
->elements
[j
].name
, "unicodePwd") == 0 ||
471 ldb_attr_cmp(drs_msg
->elements
[j
].name
, "dBCSPwd") == 0 ||
472 ldb_attr_cmp(drs_msg
->elements
[j
].name
, "userPassword") == 0 ||
473 ldb_attr_cmp(drs_msg
->elements
[j
].name
, "ntPwdHistory") == 0 ||
474 ldb_attr_cmp(drs_msg
->elements
[j
].name
, "lmPwdHistory") == 0 ||
475 ldb_attr_cmp(drs_msg
->elements
[j
].name
, "supplementalCredentials") == 0 ||
476 ldb_attr_cmp(drs_msg
->elements
[j
].name
, "priorValue") == 0 ||
477 ldb_attr_cmp(drs_msg
->elements
[j
].name
, "currentValue") == 0 ||
478 ldb_attr_cmp(drs_msg
->elements
[j
].name
, "trustAuthOutgoing") == 0 ||
479 ldb_attr_cmp(drs_msg
->elements
[j
].name
, "trustAuthIncoming") == 0 ||
480 ldb_attr_cmp(drs_msg
->elements
[j
].name
, "initialAuthOutgoing") == 0 ||
481 ldb_attr_cmp(drs_msg
->elements
[j
].name
, "initialAuthIncoming") == 0) {
483 /* These are not shown over LDAP, so we need to skip them for the comparison */
484 ldb_msg_remove_element(drs_msg
, &drs_msg
->elements
[j
]);
488 drs_msg
->elements
[j
].flags
= LDB_FLAG_MOD_ADD
;
493 ret
= ldb_msg_difference(ldb
, search_req
,
494 drs_msg
, ldap_msg
, &new_msg
);
495 torture_assert(tctx
, ret
== LDB_SUCCESS
, "ldb_msg_difference() has failed");
496 if (new_msg
->num_elements
!= 0) {
498 bool is_warning
= true;
500 struct ldb_message_element
*el
;
501 const struct dsdb_attribute
* a
;
502 struct ldb_ldif ldif
;
503 ldif
.changetype
= LDB_CHANGETYPE_MODIFY
;
505 s
= ldb_ldif_write_string(ldb
, new_msg
, &ldif
);
506 s
= talloc_asprintf(tctx
, "\n# Difference in between DRS and LDAP objects: \n%s", s
);
508 ret
= ldb_msg_difference(ldb
, search_req
,
509 ldap_msg
, drs_msg
, &ldif
.msg
);
510 torture_assert(tctx
, ret
== LDB_SUCCESS
, "ldb_msg_difference() has failed");
511 s
= talloc_asprintf_append(s
,
512 "\n# Difference in between LDAP and DRS objects: \n%s",
513 ldb_ldif_write_string(ldb
, new_msg
, &ldif
));
515 s
= talloc_asprintf_append(s
,
516 "# Should have no objects in 'difference' message. Diff elements: %d",
517 new_msg
->num_elements
);
520 * In case differences in messages are:
521 * 1. Attributes with different values, i.e. 'replace'
522 * 2. Those attributes are forward-link attributes
523 * then we just warn about those differences.
524 * It turns out windows doesn't send all of those values
525 * in replicated_object but in linked_attributes.
527 for (idx
= 0; idx
< new_msg
->num_elements
&& is_warning
; idx
++) {
528 el
= &new_msg
->elements
[idx
];
529 a
= dsdb_attribute_by_lDAPDisplayName(ldap_schema
,
531 if (LDB_FLAG_MOD_TYPE(el
->flags
) != LDB_FLAG_MOD_ADD
&&
532 LDB_FLAG_MOD_TYPE(el
->flags
) != LDB_FLAG_MOD_REPLACE
)
536 } else if (a
->linkID
& 1) {
541 torture_warning(tctx
, "%s", s
);
543 torture_fail(tctx
, s
);
547 /* search_req is used as a tmp talloc context in the above */
548 talloc_free(search_req
);
551 if (!lpcfg_parm_bool(tctx
->lp_ctx
, NULL
, "dssync", "print_pwd_blobs", false)) {
556 save_values_dir
= lpcfg_parm_string(tctx
->lp_ctx
, NULL
, "dssync", "save_pwd_blobs_dir");
558 for (cur
= first_object
; cur
; cur
= cur
->next_object
) {
560 bool dn_printed
= false;
562 if (!cur
->object
.identifier
) continue;
564 dn
= cur
->object
.identifier
->dn
;
566 for (i
=0; i
< cur
->object
.attribute_ctr
.num_attributes
; i
++) {
567 const char *name
= NULL
;
568 DATA_BLOB plain_data
;
569 struct drsuapi_DsReplicaAttribute
*attr
;
570 ndr_pull_flags_fn_t pull_fn
= NULL
;
571 ndr_print_fn_t print_fn
= NULL
;
573 attr
= &cur
->object
.attribute_ctr
.attributes
[i
];
575 switch (attr
->attid
) {
576 case DRSUAPI_ATTID_dBCSPwd
:
579 case DRSUAPI_ATTID_unicodePwd
:
582 case DRSUAPI_ATTID_ntPwdHistory
:
583 name
= "ntPwdHistory";
585 case DRSUAPI_ATTID_lmPwdHistory
:
586 name
= "lmPwdHistory";
588 case DRSUAPI_ATTID_supplementalCredentials
:
589 name
= "supplementalCredentials";
590 pull_fn
= (ndr_pull_flags_fn_t
)ndr_pull_supplementalCredentialsBlob
;
591 print_fn
= (ndr_print_fn_t
)ndr_print_supplementalCredentialsBlob
;
592 ptr
= talloc(ctx
, struct supplementalCredentialsBlob
);
594 case DRSUAPI_ATTID_priorValue
:
597 case DRSUAPI_ATTID_currentValue
:
598 name
= "currentValue";
600 case DRSUAPI_ATTID_trustAuthOutgoing
:
601 name
= "trustAuthOutgoing";
602 pull_fn
= (ndr_pull_flags_fn_t
)ndr_pull_trustAuthInOutBlob
;
603 print_fn
= (ndr_print_fn_t
)ndr_print_trustAuthInOutBlob
;
604 ptr
= talloc(ctx
, struct trustAuthInOutBlob
);
606 case DRSUAPI_ATTID_trustAuthIncoming
:
607 name
= "trustAuthIncoming";
608 pull_fn
= (ndr_pull_flags_fn_t
)ndr_pull_trustAuthInOutBlob
;
609 print_fn
= (ndr_print_fn_t
)ndr_print_trustAuthInOutBlob
;
610 ptr
= talloc(ctx
, struct trustAuthInOutBlob
);
612 case DRSUAPI_ATTID_initialAuthOutgoing
:
613 name
= "initialAuthOutgoing";
615 case DRSUAPI_ATTID_initialAuthIncoming
:
616 name
= "initialAuthIncoming";
622 if (attr
->value_ctr
.num_values
!= 1) continue;
624 if (!attr
->value_ctr
.values
[0].blob
) continue;
626 plain_data
= *attr
->value_ctr
.values
[0].blob
;
630 DEBUG(0,("DN[%u] %s\n", object_id
, dn
));
633 DEBUGADD(0,("ATTR: %s plain.length=%lu\n",
634 name
, (long)plain_data
.length
));
635 if (plain_data
.length
) {
636 enum ndr_err_code ndr_err
;
637 dump_data(0, plain_data
.data
, plain_data
.length
);
638 if (save_values_dir
) {
640 fname
= talloc_asprintf(ctx
, "%s/%s%02d",
645 ok
= file_save(fname
, plain_data
.data
, plain_data
.length
);
647 DEBUGADD(0,("Failed to save '%s'\n", fname
));
654 /* Can't use '_all' because of PIDL bugs with relative pointers */
655 ndr_err
= ndr_pull_struct_blob(&plain_data
, ptr
,
657 if (NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
658 (void)ndr_print_debug(1, print_fn
, name
, ptr
, __location__
, __func__
);
660 DEBUG(0, ("Failed to decode %s\n", name
));
671 static bool test_GetNCChanges(struct torture_context
*tctx
,
672 struct DsSyncTest
*ctx
,
673 const char *nc_dn_str
)
678 uint64_t highest_usn
= 0;
679 struct drsuapi_DsGetNCChanges r
;
680 union drsuapi_DsGetNCChangesRequest req
;
681 struct drsuapi_DsReplicaObjectIdentifier nc
;
682 struct drsuapi_DsGetNCChangesCtr1
*ctr1
= NULL
;
683 struct drsuapi_DsGetNCChangesCtr6
*ctr6
= NULL
;
684 uint32_t out_level
= 0;
685 struct dom_sid null_sid
;
686 DATA_BLOB gensec_skey
;
698 ZERO_STRUCT(null_sid
);
700 highest_usn
= lpcfg_parm_int(tctx
->lp_ctx
, NULL
, "dssync", "highest_usn", 0);
702 array
[0].level
= lpcfg_parm_int(tctx
->lp_ctx
, NULL
, "dssync", "get_nc_changes_level", array
[0].level
);
704 if (lpcfg_parm_bool(tctx
->lp_ctx
, NULL
, "dssync", "print_pwd_blobs", false)) {
705 const struct samr_Password
*nthash
;
706 nthash
= cli_credentials_get_nt_hash(ctx
->new_dc
.credentials
, ctx
);
708 dump_data_pw("CREDENTIALS nthash:", nthash
->hash
, sizeof(nthash
->hash
));
711 status
= dcerpc_binding_handle_auth_session_key(ctx
->new_dc
.drsuapi
.drs_handle
,
714 if (!NT_STATUS_IS_OK(status
)) {
715 printf("failed to get gensec session key: %s\n", nt_errstr(status
));
719 for (i
=0; i
< ARRAY_SIZE(array
); i
++) {
720 printf("Testing DsGetNCChanges level %d\n",
723 r
.in
.bind_handle
= &ctx
->new_dc
.drsuapi
.bind_handle
;
724 r
.in
.level
= array
[i
].level
;
726 switch (r
.in
.level
) {
728 nc
.guid
= GUID_zero();
733 r
.in
.req
->req5
.destination_dsa_guid
= ctx
->new_dc
.invocation_id
;
734 r
.in
.req
->req5
.source_dsa_invocation_id
= GUID_zero();
735 r
.in
.req
->req5
.naming_context
= &nc
;
736 r
.in
.req
->req5
.highwatermark
.tmp_highest_usn
= highest_usn
;
737 r
.in
.req
->req5
.highwatermark
.reserved_usn
= 0;
738 r
.in
.req
->req5
.highwatermark
.highest_usn
= highest_usn
;
739 r
.in
.req
->req5
.uptodateness_vector
= NULL
;
740 r
.in
.req
->req5
.replica_flags
= 0;
741 if (lpcfg_parm_bool(tctx
->lp_ctx
, NULL
, "dssync", "compression", false)) {
742 r
.in
.req
->req5
.replica_flags
|= DRSUAPI_DRS_USE_COMPRESSION
;
744 if (lpcfg_parm_bool(tctx
->lp_ctx
, NULL
, "dssync", "neighbour_writeable", true)) {
745 r
.in
.req
->req5
.replica_flags
|= DRSUAPI_DRS_WRIT_REP
;
747 r
.in
.req
->req5
.replica_flags
|= DRSUAPI_DRS_INIT_SYNC
748 | DRSUAPI_DRS_PER_SYNC
749 | DRSUAPI_DRS_GET_ANC
750 | DRSUAPI_DRS_NEVER_SYNCED
752 r
.in
.req
->req5
.max_object_count
= 133;
753 r
.in
.req
->req5
.max_ndr_size
= 1336770;
754 r
.in
.req
->req5
.extended_op
= DRSUAPI_EXOP_NONE
;
755 r
.in
.req
->req5
.fsmo_info
= 0;
759 nc
.guid
= GUID_zero();
762 /* nc.dn can be set to any other ad partition */
765 r
.in
.req
->req8
.destination_dsa_guid
= ctx
->new_dc
.invocation_id
;
766 r
.in
.req
->req8
.source_dsa_invocation_id
= GUID_zero();
767 r
.in
.req
->req8
.naming_context
= &nc
;
768 r
.in
.req
->req8
.highwatermark
.tmp_highest_usn
= highest_usn
;
769 r
.in
.req
->req8
.highwatermark
.reserved_usn
= 0;
770 r
.in
.req
->req8
.highwatermark
.highest_usn
= highest_usn
;
771 r
.in
.req
->req8
.uptodateness_vector
= NULL
;
772 r
.in
.req
->req8
.replica_flags
= 0;
773 if (lpcfg_parm_bool(tctx
->lp_ctx
, NULL
, "dssync", "compression", false)) {
774 r
.in
.req
->req8
.replica_flags
|= DRSUAPI_DRS_USE_COMPRESSION
;
776 if (lpcfg_parm_bool(tctx
->lp_ctx
, NULL
, "dssync", "neighbour_writeable", true)) {
777 r
.in
.req
->req8
.replica_flags
|= DRSUAPI_DRS_WRIT_REP
;
779 r
.in
.req
->req8
.replica_flags
|= DRSUAPI_DRS_INIT_SYNC
780 | DRSUAPI_DRS_PER_SYNC
781 | DRSUAPI_DRS_GET_ANC
782 | DRSUAPI_DRS_NEVER_SYNCED
784 r
.in
.req
->req8
.max_object_count
= 402;
785 r
.in
.req
->req8
.max_ndr_size
= 402116;
787 r
.in
.req
->req8
.extended_op
= DRSUAPI_EXOP_NONE
;
788 r
.in
.req
->req8
.fsmo_info
= 0;
789 r
.in
.req
->req8
.partial_attribute_set
= NULL
;
790 r
.in
.req
->req8
.partial_attribute_set_ex
= NULL
;
791 r
.in
.req
->req8
.mapping_ctr
.num_mappings
= 0;
792 r
.in
.req
->req8
.mapping_ctr
.mappings
= NULL
;
799 union drsuapi_DsGetNCChangesCtr ctr
;
803 r
.out
.level_out
= &_level
;
806 if (r
.in
.level
== 5) {
807 torture_comment(tctx
,
808 "start[%d] tmp_higest_usn: %llu , highest_usn: %llu\n",
810 (unsigned long long) r
.in
.req
->req5
.highwatermark
.tmp_highest_usn
,
811 (unsigned long long) r
.in
.req
->req5
.highwatermark
.highest_usn
);
814 if (r
.in
.level
== 8) {
815 torture_comment(tctx
,
816 "start[%d] tmp_higest_usn: %llu , highest_usn: %llu\n",
818 (unsigned long long) r
.in
.req
->req8
.highwatermark
.tmp_highest_usn
,
819 (unsigned long long) r
.in
.req
->req8
.highwatermark
.highest_usn
);
822 status
= dcerpc_drsuapi_DsGetNCChanges_r(ctx
->new_dc
.drsuapi
.drs_handle
, ctx
, &r
);
823 torture_drsuapi_assert_call(tctx
, ctx
->new_dc
.drsuapi
.drs_pipe
, status
,
824 &r
, "dcerpc_drsuapi_DsGetNCChanges");
826 if (ret
== true && *r
.out
.level_out
== 1) {
828 ctr1
= &r
.out
.ctr
->ctr1
;
829 } else if (ret
== true && *r
.out
.level_out
== 2 &&
830 r
.out
.ctr
->ctr2
.mszip1
.ts
) {
832 ctr1
= &r
.out
.ctr
->ctr2
.mszip1
.ts
->ctr1
;
835 if (out_level
== 1) {
836 torture_comment(tctx
,
837 "end[%d] tmp_highest_usn: %llu , highest_usn: %llu\n",
839 (unsigned long long) ctr1
->new_highwatermark
.tmp_highest_usn
,
840 (unsigned long long) ctr1
->new_highwatermark
.highest_usn
);
842 if (!test_analyse_objects(tctx
, ctx
, nc_dn_str
, &ctr1
->mapping_ctr
, ctr1
->object_count
,
843 ctr1
->first_object
, &gensec_skey
)) {
847 if (ctr1
->more_data
) {
848 r
.in
.req
->req5
.highwatermark
= ctr1
->new_highwatermark
;
853 if (ret
== true && *r
.out
.level_out
== 6) {
855 ctr6
= &r
.out
.ctr
->ctr6
;
856 } else if (ret
== true && *r
.out
.level_out
== 7
857 && r
.out
.ctr
->ctr7
.level
== 6
858 && r
.out
.ctr
->ctr7
.type
== DRSUAPI_COMPRESSION_TYPE_MSZIP
859 && r
.out
.ctr
->ctr7
.ctr
.mszip6
.ts
) {
861 ctr6
= &r
.out
.ctr
->ctr7
.ctr
.mszip6
.ts
->ctr6
;
862 } else if (ret
== true && *r
.out
.level_out
== 7
863 && r
.out
.ctr
->ctr7
.level
== 6
864 && r
.out
.ctr
->ctr7
.type
== DRSUAPI_COMPRESSION_TYPE_WIN2K3_LZ77_DIRECT2
865 && r
.out
.ctr
->ctr7
.ctr
.xpress6
.ts
) {
867 ctr6
= &r
.out
.ctr
->ctr7
.ctr
.xpress6
.ts
->ctr6
;
870 if (out_level
== 6) {
871 torture_comment(tctx
,
872 "end[%d] tmp_highest_usn: %llu , highest_usn: %llu\n",
874 (unsigned long long) ctr6
->new_highwatermark
.tmp_highest_usn
,
875 (unsigned long long) ctr6
->new_highwatermark
.highest_usn
);
877 if (!test_analyse_objects(tctx
, ctx
, nc_dn_str
, &ctr6
->mapping_ctr
, ctr6
->object_count
,
878 ctr6
->first_object
, &gensec_skey
)) {
882 if (ctr6
->more_data
) {
883 r
.in
.req
->req8
.highwatermark
= ctr6
->new_highwatermark
;
896 * Test DsGetNCChanges() DRSUAPI call against one
897 * or more Naming Contexts.
898 * Specific NC to test with may be supplied
899 * in lp_ctx configuration. If no NC is specified,
900 * it will test DsGetNCChanges() on all NCs on remote DC
902 static bool test_FetchData(struct torture_context
*tctx
, struct DsSyncTest
*ctx
)
906 const char *nc_dn_str
;
907 const char **nc_list
;
909 nc_list
= const_str_list(str_list_make_empty(ctx
));
910 torture_assert(tctx
, nc_list
, "Not enough memory!");
912 /* make a list of partitions to test with */
913 nc_dn_str
= lpcfg_parm_string(tctx
->lp_ctx
, NULL
, "dssync", "partition");
914 if (nc_dn_str
== NULL
) {
915 nc_list
= str_list_add_const(nc_list
, ctx
->domain_dn
);
916 nc_list
= str_list_add_const(nc_list
, ctx
->config_dn
);
917 nc_list
= str_list_add_const(nc_list
, ctx
->schema_dn
);
919 nc_list
= str_list_add_const(nc_list
, nc_dn_str
);
922 count
= str_list_length(nc_list
);
923 for (i
= 0; i
< count
&& ret
; i
++) {
924 torture_comment(tctx
, "\nNaming Context: %s\n", nc_list
[i
]);
925 ret
= test_GetNCChanges(tctx
, ctx
, nc_list
[i
]);
928 talloc_free(nc_list
);
933 static bool test_FetchNT4Data(struct torture_context
*tctx
,
934 struct DsSyncTest
*ctx
)
937 struct drsuapi_DsGetNT4ChangeLog r
;
938 union drsuapi_DsGetNT4ChangeLogRequest req
;
939 union drsuapi_DsGetNT4ChangeLogInfo info
;
940 uint32_t level_out
= 0;
946 r
.in
.bind_handle
= &ctx
->new_dc
.drsuapi
.bind_handle
;
949 r
.out
.level_out
= &level_out
;
951 req
.req1
.flags
= lpcfg_parm_int(tctx
->lp_ctx
, NULL
,
952 "dssync", "nt4changelog_flags",
953 DRSUAPI_NT4_CHANGELOG_GET_CHANGELOG
|
954 DRSUAPI_NT4_CHANGELOG_GET_SERIAL_NUMBERS
);
955 req
.req1
.preferred_maximum_length
= lpcfg_parm_int(tctx
->lp_ctx
, NULL
,
956 "dssync", "nt4changelog_preferred_len",
960 req
.req1
.restart_length
= cookie
.length
;
961 req
.req1
.restart_data
= cookie
.data
;
965 status
= dcerpc_drsuapi_DsGetNT4ChangeLog_r(ctx
->new_dc
.drsuapi
.drs_handle
, ctx
, &r
);
966 if (NT_STATUS_EQUAL(status
, NT_STATUS_NOT_IMPLEMENTED
)) {
968 "DsGetNT4ChangeLog not supported: NT_STATUS_NOT_IMPLEMENTED");
969 } else if (!NT_STATUS_IS_OK(status
)) {
970 const char *errstr
= nt_errstr(status
);
971 if (NT_STATUS_EQUAL(status
, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE
)) {
973 "DsGetNT4ChangeLog not supported: NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE");
976 talloc_asprintf(tctx
, "dcerpc_drsuapi_DsGetNT4ChangeLog failed - %s\n",
978 } else if (W_ERROR_EQUAL(r
.out
.result
, WERR_INVALID_DOMAIN_ROLE
)) {
980 "DsGetNT4ChangeLog not supported: WERR_INVALID_DOMAIN_ROLE");
981 } else if (!W_ERROR_IS_OK(r
.out
.result
)) {
983 talloc_asprintf(tctx
, "DsGetNT4ChangeLog failed - %s\n",
984 win_errstr(r
.out
.result
)));
985 } else if (*r
.out
.level_out
!= 1) {
987 talloc_asprintf(tctx
, "DsGetNT4ChangeLog unknown level - %u\n",
989 } else if (NT_STATUS_IS_OK(r
.out
.info
->info1
.status
)) {
990 } else if (NT_STATUS_EQUAL(r
.out
.info
->info1
.status
, STATUS_MORE_ENTRIES
)) {
991 cookie
.length
= r
.out
.info
->info1
.restart_length
;
992 cookie
.data
= r
.out
.info
->info1
.restart_data
;
996 talloc_asprintf(tctx
, "DsGetNT4ChangeLog failed - %s\n",
997 nt_errstr(r
.out
.info
->info1
.status
)));
1007 * DSSYNC test case setup
1009 static bool torture_dssync_tcase_setup(struct torture_context
*tctx
, void **data
)
1012 struct DsSyncTest
*ctx
;
1014 *data
= ctx
= test_create_context(tctx
);
1015 torture_assert(tctx
, ctx
, "test_create_context() failed");
1017 bret
= _test_DsBind(tctx
, ctx
, ctx
->admin
.credentials
, &ctx
->admin
.drsuapi
);
1018 torture_assert(tctx
, bret
, "_test_DsBind() failed");
1020 bret
= test_LDAPBind(tctx
, ctx
, ctx
->admin
.credentials
, &ctx
->admin
.ldap
);
1021 torture_assert(tctx
, bret
, "test_LDAPBind() failed");
1023 bret
= test_GetInfo(tctx
, ctx
);
1024 torture_assert(tctx
, bret
, "test_GetInfo() failed");
1026 bret
= _test_DsBind(tctx
, ctx
, ctx
->new_dc
.credentials
, &ctx
->new_dc
.drsuapi
);
1027 torture_assert(tctx
, bret
, "_test_DsBind() failed");
1033 * DSSYNC test case cleanup
1035 static bool torture_dssync_tcase_teardown(struct torture_context
*tctx
, void *data
)
1037 struct DsSyncTest
*ctx
;
1038 struct drsuapi_DsUnbind r
;
1039 struct policy_handle bind_handle
;
1041 ctx
= talloc_get_type(data
, struct DsSyncTest
);
1044 r
.out
.bind_handle
= &bind_handle
;
1046 /* Unbing admin handle */
1047 r
.in
.bind_handle
= &ctx
->admin
.drsuapi
.bind_handle
;
1048 dcerpc_drsuapi_DsUnbind_r(ctx
->admin
.drsuapi
.drs_handle
, ctx
, &r
);
1050 /* Unbing new_dc handle */
1051 r
.in
.bind_handle
= &ctx
->new_dc
.drsuapi
.bind_handle
;
1052 dcerpc_drsuapi_DsUnbind_r(ctx
->new_dc
.drsuapi
.drs_handle
, ctx
, &r
);
1060 * DSSYNC test case implementation
1062 void torture_drs_rpc_dssync_tcase(struct torture_suite
*suite
)
1064 typedef bool (*run_func
) (struct torture_context
*test
, void *tcase_data
);
1065 struct torture_tcase
*tcase
= torture_suite_add_tcase(suite
, "dssync");
1067 torture_tcase_set_fixture(tcase
,
1068 torture_dssync_tcase_setup
,
1069 torture_dssync_tcase_teardown
);
1071 torture_tcase_add_simple_test(tcase
, "DC_FetchData", (run_func
)test_FetchData
);
1072 torture_tcase_add_simple_test(tcase
, "FetchNT4Data", (run_func
)test_FetchNT4Data
);