2 Unix SMB/CIFS implementation.
6 Copyright (C) Andrew Tridgell 2003
7 Copyright (C) Stefan (metze) Metzmacher 2004
8 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2006
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
25 #include "lib/cmdline/cmdline.h"
26 #include "librpc/gen_ndr/ndr_drsuapi_c.h"
27 #include "torture/rpc/torture_rpc.h"
28 #include "libcli/security/dom_sid.h"
29 #include "param/param.h"
31 #define TEST_MACHINE_NAME "torturetest"
33 static bool test_DsBind(struct dcerpc_pipe
*p
,
34 struct torture_context
*tctx
,
35 struct policy_handle
*bind_handle
,
36 struct drsuapi_DsBindInfo28
*srv_info28
)
39 struct drsuapi_DsBind r
;
40 struct GUID bind_guid
;
41 struct drsuapi_DsBindInfo28
*bind_info28
;
42 struct drsuapi_DsBindInfoCtr bind_info_ctr
;
44 ZERO_STRUCT(bind_info_ctr
);
45 bind_info_ctr
.length
= 28;
47 bind_info28
= &bind_info_ctr
.info
.info28
;
48 bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_BASE
;
49 bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_ASYNC_REPLICATION
;
50 bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_REMOVEAPI
;
51 bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_MOVEREQ_V2
;
52 bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GETCHG_COMPRESS
;
53 bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V1
;
54 bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_RESTORE_USN_OPTIMIZATION
;
55 bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_KCC_EXECUTE
;
56 bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRY_V2
;
57 bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_LINKED_VALUE_REPLICATION
;
58 bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V2
;
59 bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_INSTANCE_TYPE_NOT_REQ_ON_MOD
;
60 bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_CRYPTO_BIND
;
61 bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GET_REPL_INFO
;
62 bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_STRONG_ENCRYPTION
;
63 bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V01
;
64 bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_TRANSITIVE_MEMBERSHIP
;
65 bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_ADD_SID_HISTORY
;
66 bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_POST_BETA3
;
67 bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GET_MEMBERSHIPS2
;
68 bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V6
;
69 bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_NONDOMAIN_NCS
;
70 bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8
;
71 bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V5
;
72 bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V6
;
73 bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRYREPLY_V3
;
74 bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V7
;
75 bind_info28
->supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_VERIFY_OBJECT
;
77 GUID_from_string(DRSUAPI_DS_BIND_GUID
, &bind_guid
);
79 r
.in
.bind_guid
= &bind_guid
;
80 r
.in
.bind_info
= &bind_info_ctr
;
81 r
.out
.bind_handle
= bind_handle
;
83 torture_comment(tctx
, "Testing DsBind\n");
85 status
= dcerpc_drsuapi_DsBind_r(p
->binding_handle
, tctx
, &r
);
86 torture_drsuapi_assert_call(tctx
, p
, status
, &r
, "dcerpc_drsuapi_DsBind");
88 if (srv_info28
!= NULL
) {
89 *srv_info28
= r
.out
.bind_info
->info
.info28
;
95 static bool test_DsGetDomainControllerInfo(struct torture_context
*tctx
,
96 struct DsPrivate
*priv
)
99 struct dcerpc_pipe
*p
= priv
->drs_pipe
;
100 struct drsuapi_DsGetDomainControllerInfo r
;
101 union drsuapi_DsGetDCInfoCtr ctr
;
102 union drsuapi_DsGetDCInfoRequest req
;
103 int32_t level_out
= 0;
112 .name
= torture_join_dom_netbios_name(priv
->join
),
116 .name
= torture_join_dom_dns_name(priv
->join
),
120 .name
= "__UNKNOWN_DOMAIN__",
121 .expected
= WERR_DS_OBJ_NOT_FOUND
124 .name
= "unknown.domain.samba.example.com",
125 .expected
= WERR_DS_OBJ_NOT_FOUND
128 int levels
[] = {1, 2};
131 for (i
=0; i
< ARRAY_SIZE(levels
); i
++) {
132 for (j
=0; j
< ARRAY_SIZE(names
); j
++) {
134 r
.in
.bind_handle
= &priv
->bind_handle
;
138 r
.in
.req
->req1
.domain_name
= names
[j
].name
;
139 r
.in
.req
->req1
.level
= level
;
142 r
.out
.level_out
= &level_out
;
144 torture_comment(tctx
,
145 "Testing DsGetDomainControllerInfo level %d on domainname '%s'\n",
146 r
.in
.req
->req1
.level
, r
.in
.req
->req1
.domain_name
);
148 status
= dcerpc_drsuapi_DsGetDomainControllerInfo_r(p
->binding_handle
, tctx
, &r
);
149 torture_assert_ntstatus_ok(tctx
, status
,
150 "dcerpc_drsuapi_DsGetDomainControllerInfo with dns domain failed");
151 torture_assert_werr_equal(tctx
,
152 r
.out
.result
, names
[j
].expected
,
153 "DsGetDomainControllerInfo level with dns domain failed");
155 if (!W_ERROR_IS_OK(r
.out
.result
)) {
156 /* If this was an error, we can't read the result structure */
160 torture_assert_int_equal(tctx
,
161 r
.in
.req
->req1
.level
, *r
.out
.level_out
,
162 "dcerpc_drsuapi_DsGetDomainControllerInfo in/out level differs");
166 for (k
=0; k
< r
.out
.ctr
->ctr1
.count
; k
++) {
167 if (strcasecmp_m(r
.out
.ctr
->ctr1
.array
[k
].netbios_name
,
168 torture_join_netbios_name(priv
->join
)) == 0) {
175 for (k
=0; k
< r
.out
.ctr
->ctr2
.count
; k
++) {
176 if (strcasecmp_m(r
.out
.ctr
->ctr2
.array
[k
].netbios_name
,
177 torture_join_netbios_name(priv
->join
)) == 0) {
179 priv
->dcinfo
= r
.out
.ctr
->ctr2
.array
[k
];
185 torture_assert(tctx
, found
,
186 "dcerpc_drsuapi_DsGetDomainControllerInfo: Failed to find the domain controller we just created during the join");
190 r
.in
.bind_handle
= &priv
->bind_handle
;
194 r
.out
.level_out
= &level_out
;
196 r
.in
.req
->req1
.domain_name
= "__UNKNOWN_DOMAIN__"; /* This is clearly ignored for this level */
197 r
.in
.req
->req1
.level
= -1;
199 torture_comment(tctx
, "Testing DsGetDomainControllerInfo level %d on domainname '%s'\n",
200 r
.in
.req
->req1
.level
, r
.in
.req
->req1
.domain_name
);
202 status
= dcerpc_drsuapi_DsGetDomainControllerInfo_r(p
->binding_handle
, tctx
, &r
);
204 torture_assert_ntstatus_ok(tctx
, status
,
205 "dcerpc_drsuapi_DsGetDomainControllerInfo with dns domain failed");
206 torture_assert_werr_ok(tctx
, r
.out
.result
,
207 "DsGetDomainControllerInfo with dns domain failed");
210 const char *dc_account
= talloc_asprintf(tctx
, "%s\\%s$",
211 torture_join_dom_netbios_name(priv
->join
),
212 priv
->dcinfo
.netbios_name
);
213 torture_comment(tctx
, "%s: Enum active LDAP sessions searching for %s\n", __func__
, dc_account
);
214 for (k
=0; k
< r
.out
.ctr
->ctr01
.count
; k
++) {
215 if (strcasecmp_m(r
.out
.ctr
->ctr01
.array
[k
].client_account
,
221 torture_assert(tctx
, found
,
222 "dcerpc_drsuapi_DsGetDomainControllerInfo level: Failed to find the domain controller in last logon records");
229 static bool test_DsWriteAccountSpn(struct torture_context
*tctx
,
230 struct DsPrivate
*priv
)
233 struct dcerpc_pipe
*p
= priv
->drs_pipe
;
234 struct drsuapi_DsWriteAccountSpn r
;
235 union drsuapi_DsWriteAccountSpnRequest req
;
236 struct drsuapi_DsNameString names
[2];
237 union drsuapi_DsWriteAccountSpnResult res
;
240 r
.in
.bind_handle
= &priv
->bind_handle
;
244 torture_comment(tctx
, "Testing DsWriteAccountSpn\n");
246 r
.in
.req
->req1
.operation
= DRSUAPI_DS_SPN_OPERATION_ADD
;
247 r
.in
.req
->req1
.unknown1
= 0;
248 r
.in
.req
->req1
.object_dn
= priv
->dcinfo
.computer_dn
;
249 r
.in
.req
->req1
.count
= 2;
250 r
.in
.req
->req1
.spn_names
= names
;
251 names
[0].str
= talloc_asprintf(tctx
, "smbtortureSPN/%s",priv
->dcinfo
.netbios_name
);
252 names
[1].str
= talloc_asprintf(tctx
, "smbtortureSPN/%s",priv
->dcinfo
.dns_name
);
255 r
.out
.level_out
= &level_out
;
257 status
= dcerpc_drsuapi_DsWriteAccountSpn_r(p
->binding_handle
, tctx
, &r
);
258 torture_drsuapi_assert_call(tctx
, p
, status
, &r
, "dcerpc_drsuapi_DsWriteAccountSpn");
260 r
.in
.req
->req1
.operation
= DRSUAPI_DS_SPN_OPERATION_DELETE
;
261 r
.in
.req
->req1
.unknown1
= 0;
263 status
= dcerpc_drsuapi_DsWriteAccountSpn_r(p
->binding_handle
, tctx
, &r
);
264 torture_drsuapi_assert_call(tctx
, p
, status
, &r
, "dcerpc_drsuapi_DsWriteAccountSpn");
269 static bool test_DsReplicaGetInfo(struct torture_context
*tctx
,
270 struct DsPrivate
*priv
)
273 struct dcerpc_pipe
*p
= priv
->drs_pipe
;
274 struct drsuapi_DsReplicaGetInfo r
;
275 union drsuapi_DsReplicaGetInfoRequest req
;
276 union drsuapi_DsReplicaInfo info
;
277 enum drsuapi_DsReplicaInfoType info_type
;
285 DRSUAPI_DS_REPLICA_GET_INFO
,
286 DRSUAPI_DS_REPLICA_INFO_NEIGHBORS
,
289 DRSUAPI_DS_REPLICA_GET_INFO
,
290 DRSUAPI_DS_REPLICA_INFO_CURSORS
,
293 DRSUAPI_DS_REPLICA_GET_INFO
,
294 DRSUAPI_DS_REPLICA_INFO_OBJ_METADATA
,
297 DRSUAPI_DS_REPLICA_GET_INFO
,
298 DRSUAPI_DS_REPLICA_INFO_KCC_DSA_CONNECT_FAILURES
,
301 DRSUAPI_DS_REPLICA_GET_INFO
,
302 DRSUAPI_DS_REPLICA_INFO_KCC_DSA_LINK_FAILURES
,
305 DRSUAPI_DS_REPLICA_GET_INFO
,
306 DRSUAPI_DS_REPLICA_INFO_PENDING_OPS
,
309 DRSUAPI_DS_REPLICA_GET_INFO2
,
310 DRSUAPI_DS_REPLICA_INFO_ATTRIBUTE_VALUE_METADATA
,
313 DRSUAPI_DS_REPLICA_GET_INFO2
,
314 DRSUAPI_DS_REPLICA_INFO_CURSORS2
,
317 DRSUAPI_DS_REPLICA_GET_INFO2
,
318 DRSUAPI_DS_REPLICA_INFO_CURSORS3
,
321 DRSUAPI_DS_REPLICA_GET_INFO2
,
322 DRSUAPI_DS_REPLICA_INFO_OBJ_METADATA2
,
325 DRSUAPI_DS_REPLICA_GET_INFO2
,
326 DRSUAPI_DS_REPLICA_INFO_ATTRIBUTE_VALUE_METADATA2
,
329 DRSUAPI_DS_REPLICA_GET_INFO2
,
330 DRSUAPI_DS_REPLICA_INFO_REPSTO
,
333 DRSUAPI_DS_REPLICA_GET_INFO2
,
334 DRSUAPI_DS_REPLICA_INFO_CLIENT_CONTEXTS
,
337 DRSUAPI_DS_REPLICA_GET_INFO2
,
338 DRSUAPI_DS_REPLICA_INFO_UPTODATE_VECTOR_V1
,
341 DRSUAPI_DS_REPLICA_GET_INFO2
,
342 DRSUAPI_DS_REPLICA_INFO_SERVER_OUTGOING_CALLS
,
347 if (torture_setting_bool(tctx
, "samba4", false)) {
348 torture_comment(tctx
, "skipping DsReplicaGetInfo test against Samba4\n");
352 r
.in
.bind_handle
= &priv
->bind_handle
;
355 for (i
=0; i
< ARRAY_SIZE(array
); i
++) {
356 const char *object_dn
;
358 torture_comment(tctx
, "Testing DsReplicaGetInfo level %d infotype %d\n",
359 array
[i
].level
, array
[i
].infotype
);
361 object_dn
= (array
[i
].obj_dn
? array
[i
].obj_dn
: priv
->domain_obj_dn
);
363 r
.in
.level
= array
[i
].level
;
365 case DRSUAPI_DS_REPLICA_GET_INFO
:
366 r
.in
.req
->req1
.info_type
= array
[i
].infotype
;
367 r
.in
.req
->req1
.object_dn
= object_dn
;
368 ZERO_STRUCT(r
.in
.req
->req1
.source_dsa_guid
);
370 case DRSUAPI_DS_REPLICA_GET_INFO2
:
371 r
.in
.req
->req2
.info_type
= array
[i
].infotype
;
372 r
.in
.req
->req2
.object_dn
= object_dn
;
373 ZERO_STRUCT(r
.in
.req
->req2
.source_dsa_guid
);
374 r
.in
.req
->req2
.flags
= 0;
375 r
.in
.req
->req2
.attribute_name
= NULL
;
376 r
.in
.req
->req2
.value_dn_str
= NULL
;
377 r
.in
.req
->req2
.enumeration_context
= 0;
382 r
.out
.info_type
= &info_type
;
384 status
= dcerpc_drsuapi_DsReplicaGetInfo_r(p
->binding_handle
, tctx
, &r
);
385 torture_drsuapi_assert_call(tctx
, p
, status
, &r
, "dcerpc_drsuapi_DsReplicaGetInfo");
386 if (NT_STATUS_EQUAL(status
, NT_STATUS_RPC_ENUM_VALUE_OUT_OF_RANGE
)) {
387 torture_comment(tctx
,
388 "DsReplicaGetInfo level %d and/or infotype %d not supported by server\n",
389 array
[i
].level
, array
[i
].infotype
);
391 torture_drsuapi_assert_call(tctx
, p
, status
, &r
, "dcerpc_drsuapi_DsReplicaGetInfo");
398 static bool test_DsReplicaSync(struct torture_context
*tctx
,
399 struct DsPrivate
*priv
)
402 struct dcerpc_pipe
*p
= priv
->drs_pipe
;
404 struct drsuapi_DsReplicaSync r
;
405 union drsuapi_DsReplicaSyncRequest sync_req
;
406 struct drsuapi_DsReplicaObjectIdentifier nc
;
407 struct dom_sid null_sid
;
416 if (!torture_setting_bool(tctx
, "dangerous", false)) {
417 torture_comment(tctx
, "DsReplicaSync disabled - enable dangerous tests to use\n");
421 if (torture_setting_bool(tctx
, "samba4", false)) {
422 torture_comment(tctx
, "skipping DsReplicaSync test against Samba4\n");
426 ZERO_STRUCT(null_sid
);
428 r
.in
.bind_handle
= &priv
->bind_handle
;
430 for (i
=0; i
< ARRAY_SIZE(array
); i
++) {
431 torture_comment(tctx
, "Testing DsReplicaSync level %d\n",
434 r
.in
.level
= array
[i
].level
;
437 nc
.guid
= GUID_zero();
439 nc
.dn
= priv
->domain_obj_dn
?priv
->domain_obj_dn
:"";
441 sync_req
.req1
.naming_context
= &nc
;
442 sync_req
.req1
.source_dsa_guid
= priv
->dcinfo
.ntds_guid
;
443 sync_req
.req1
.source_dsa_dns
= NULL
;
444 sync_req
.req1
.options
= 16;
446 r
.in
.req
= &sync_req
;
450 status
= dcerpc_drsuapi_DsReplicaSync_r(p
->binding_handle
, tctx
, &r
);
451 torture_drsuapi_assert_call(tctx
, p
, status
, &r
, "dcerpc_drsuapi_DsReplicaSync");
457 static bool test_DsReplicaUpdateRefs(struct torture_context
*tctx
,
458 struct DsPrivate
*priv
)
461 struct dcerpc_pipe
*p
= priv
->drs_pipe
;
462 struct drsuapi_DsReplicaUpdateRefs r
;
463 struct drsuapi_DsReplicaObjectIdentifier nc
;
464 struct GUID dest_dsa_guid
;
465 const char *dest_dsa_guid_str
;
466 struct dom_sid null_sid
;
468 ZERO_STRUCT(null_sid
);
469 dest_dsa_guid
= GUID_random();
470 dest_dsa_guid_str
= GUID_string(tctx
, &dest_dsa_guid
);
472 r
.in
.bind_handle
= &priv
->bind_handle
;
473 r
.in
.level
= 1; /* Only version 1 is defined presently */
476 nc
.guid
= priv
->domain_obj_dn
? GUID_zero():priv
->domain_guid
;
478 nc
.dn
= priv
->domain_obj_dn
? priv
->domain_obj_dn
: "";
480 /* default setup for request */
481 r
.in
.req
.req1
.naming_context
= &nc
;
482 r
.in
.req
.req1
.dest_dsa_dns_name
= talloc_asprintf(tctx
, "%s._msdn.%s",
484 priv
->domain_dns_name
);
485 r
.in
.req
.req1
.dest_dsa_guid
= dest_dsa_guid
;
487 /* 1. deleting replica dest should fail */
488 torture_comment(tctx
, "delete: %s\n", r
.in
.req
.req1
.dest_dsa_dns_name
);
489 r
.in
.req
.req1
.options
= DRSUAPI_DRS_DEL_REF
;
490 status
= dcerpc_drsuapi_DsReplicaUpdateRefs_r(p
->binding_handle
, tctx
, &r
);
491 torture_drsuapi_assert_call_werr(tctx
, p
,
492 status
, WERR_DS_DRA_REF_NOT_FOUND
, &r
,
493 "dcerpc_drsuapi_DsReplicaUpdateRefs");
495 /* 2. hopefully adding random replica dest should succeed */
496 torture_comment(tctx
, "add : %s\n", r
.in
.req
.req1
.dest_dsa_dns_name
);
497 r
.in
.req
.req1
.options
= DRSUAPI_DRS_ADD_REF
;
498 status
= dcerpc_drsuapi_DsReplicaUpdateRefs_r(p
->binding_handle
, tctx
, &r
);
499 torture_drsuapi_assert_call_werr(tctx
, p
,
501 "dcerpc_drsuapi_DsReplicaUpdateRefs");
503 /* 3. try adding same replica dest - should fail */
504 torture_comment(tctx
, "add : %s\n", r
.in
.req
.req1
.dest_dsa_dns_name
);
505 r
.in
.req
.req1
.options
= DRSUAPI_DRS_ADD_REF
;
506 status
= dcerpc_drsuapi_DsReplicaUpdateRefs_r(p
->binding_handle
, tctx
, &r
);
507 torture_drsuapi_assert_call_werr(tctx
, p
,
508 status
, WERR_DS_DRA_REF_ALREADY_EXISTS
, &r
,
509 "dcerpc_drsuapi_DsReplicaUpdateRefs");
511 /* 4. try resetting same replica dest - should succeed */
512 torture_comment(tctx
, "reset : %s\n", r
.in
.req
.req1
.dest_dsa_dns_name
);
513 r
.in
.req
.req1
.options
= DRSUAPI_DRS_DEL_REF
| DRSUAPI_DRS_ADD_REF
;
514 status
= dcerpc_drsuapi_DsReplicaUpdateRefs_r(p
->binding_handle
, tctx
, &r
);
515 torture_drsuapi_assert_call_werr(tctx
, p
,
517 "dcerpc_drsuapi_DsReplicaUpdateRefs");
519 /* 5. delete random replicate added at step 2. */
520 torture_comment(tctx
, "delete : %s\n", r
.in
.req
.req1
.dest_dsa_dns_name
);
521 r
.in
.req
.req1
.options
= DRSUAPI_DRS_DEL_REF
;
522 status
= dcerpc_drsuapi_DsReplicaUpdateRefs_r(p
->binding_handle
, tctx
, &r
);
523 torture_drsuapi_assert_call_werr(tctx
, p
,
525 "dcerpc_drsuapi_DsReplicaUpdateRefs");
527 /* 6. try replace on non-existing replica dest - should succeed */
528 torture_comment(tctx
, "replace: %s\n", r
.in
.req
.req1
.dest_dsa_dns_name
);
529 r
.in
.req
.req1
.options
= DRSUAPI_DRS_DEL_REF
| DRSUAPI_DRS_ADD_REF
;
530 status
= dcerpc_drsuapi_DsReplicaUpdateRefs_r(p
->binding_handle
, tctx
, &r
);
531 torture_drsuapi_assert_call_werr(tctx
, p
,
533 "dcerpc_drsuapi_DsReplicaUpdateRefs");
535 /* 7. delete random replicate added at step 6. */
536 torture_comment(tctx
, "delete : %s\n", r
.in
.req
.req1
.dest_dsa_dns_name
);
537 r
.in
.req
.req1
.options
= DRSUAPI_DRS_DEL_REF
;
538 status
= dcerpc_drsuapi_DsReplicaUpdateRefs_r(p
->binding_handle
, tctx
, &r
);
539 torture_drsuapi_assert_call_werr(tctx
, p
,
541 "dcerpc_drsuapi_DsReplicaUpdateRefs");
546 static bool test_DsGetNCChanges(struct torture_context
*tctx
,
547 struct DsPrivate
*priv
)
550 struct dcerpc_pipe
*p
= priv
->drs_pipe
;
552 struct drsuapi_DsGetNCChanges r
;
553 union drsuapi_DsGetNCChangesRequest req
;
554 union drsuapi_DsGetNCChangesCtr ctr
;
555 struct drsuapi_DsReplicaObjectIdentifier nc
;
556 struct dom_sid null_sid
;
569 if (torture_setting_bool(tctx
, "samba4", false)) {
570 torture_comment(tctx
, "skipping DsGetNCChanges test against Samba4\n");
574 ZERO_STRUCT(null_sid
);
576 for (i
=0; i
< ARRAY_SIZE(array
); i
++) {
577 torture_comment(tctx
,
578 "Testing DsGetNCChanges level %d\n",
581 r
.in
.bind_handle
= &priv
->bind_handle
;
582 r
.in
.level
= array
[i
].level
;
583 r
.out
.level_out
= &level_out
;
586 switch (r
.in
.level
) {
588 nc
.guid
= GUID_zero();
590 nc
.dn
= priv
->domain_obj_dn
? priv
->domain_obj_dn
: "";
593 r
.in
.req
->req5
.destination_dsa_guid
= GUID_random();
594 r
.in
.req
->req5
.source_dsa_invocation_id
= GUID_zero();
595 r
.in
.req
->req5
.naming_context
= &nc
;
596 r
.in
.req
->req5
.highwatermark
.tmp_highest_usn
= 0;
597 r
.in
.req
->req5
.highwatermark
.reserved_usn
= 0;
598 r
.in
.req
->req5
.highwatermark
.highest_usn
= 0;
599 r
.in
.req
->req5
.uptodateness_vector
= NULL
;
600 r
.in
.req
->req5
.replica_flags
= 0;
601 if (lpcfg_parm_bool(tctx
->lp_ctx
, NULL
, "drsuapi", "compression", false)) {
602 r
.in
.req
->req5
.replica_flags
|= DRSUAPI_DRS_USE_COMPRESSION
;
604 r
.in
.req
->req5
.max_object_count
= 0;
605 r
.in
.req
->req5
.max_ndr_size
= 0;
606 r
.in
.req
->req5
.extended_op
= DRSUAPI_EXOP_NONE
;
607 r
.in
.req
->req5
.fsmo_info
= 0;
611 nc
.guid
= GUID_zero();
613 nc
.dn
= priv
->domain_obj_dn
? priv
->domain_obj_dn
: "";
616 r
.in
.req
->req8
.destination_dsa_guid
= GUID_random();
617 r
.in
.req
->req8
.source_dsa_invocation_id
= GUID_zero();
618 r
.in
.req
->req8
.naming_context
= &nc
;
619 r
.in
.req
->req8
.highwatermark
.tmp_highest_usn
= 0;
620 r
.in
.req
->req8
.highwatermark
.reserved_usn
= 0;
621 r
.in
.req
->req8
.highwatermark
.highest_usn
= 0;
622 r
.in
.req
->req8
.uptodateness_vector
= NULL
;
623 r
.in
.req
->req8
.replica_flags
= 0;
624 if (lpcfg_parm_bool(tctx
->lp_ctx
, NULL
, "drsuapi", "compression", false)) {
625 r
.in
.req
->req8
.replica_flags
|= DRSUAPI_DRS_USE_COMPRESSION
;
627 if (lpcfg_parm_bool(tctx
->lp_ctx
, NULL
, "drsuapi", "neighbour_writeable", true)) {
628 r
.in
.req
->req8
.replica_flags
|= DRSUAPI_DRS_WRIT_REP
;
630 r
.in
.req
->req8
.replica_flags
|= DRSUAPI_DRS_INIT_SYNC
631 | DRSUAPI_DRS_PER_SYNC
632 | DRSUAPI_DRS_GET_ANC
633 | DRSUAPI_DRS_NEVER_SYNCED
635 r
.in
.req
->req8
.max_object_count
= 402;
636 r
.in
.req
->req8
.max_ndr_size
= 402116;
637 r
.in
.req
->req8
.extended_op
= DRSUAPI_EXOP_NONE
;
638 r
.in
.req
->req8
.fsmo_info
= 0;
639 r
.in
.req
->req8
.partial_attribute_set
= NULL
;
640 r
.in
.req
->req8
.partial_attribute_set_ex
= NULL
;
641 r
.in
.req
->req8
.mapping_ctr
.num_mappings
= 0;
642 r
.in
.req
->req8
.mapping_ctr
.mappings
= NULL
;
647 status
= dcerpc_drsuapi_DsGetNCChanges_r(p
->binding_handle
, tctx
, &r
);
648 torture_drsuapi_assert_call(tctx
, p
, status
, &r
, "dcerpc_drsuapi_DsGetNCChanges");
654 bool test_QuerySitesByCost(struct torture_context
*tctx
,
655 struct DsPrivate
*priv
)
658 struct dcerpc_pipe
*p
= priv
->drs_pipe
;
659 struct drsuapi_QuerySitesByCost r
;
660 union drsuapi_QuerySitesByCostRequest req
;
662 const char *my_site
= "Default-First-Site-Name";
663 const char *remote_site1
= "smbtorture-nonexisting-site1";
664 const char *remote_site2
= "smbtorture-nonexisting-site2";
666 req
.req1
.site_from
= talloc_strdup(tctx
, my_site
);
667 req
.req1
.num_req
= 2;
668 req
.req1
.site_to
= talloc_zero_array(tctx
, const char *, 2);
669 req
.req1
.site_to
[0] = talloc_strdup(tctx
, remote_site1
);
670 req
.req1
.site_to
[1] = talloc_strdup(tctx
, remote_site2
);
673 r
.in
.bind_handle
= &priv
->bind_handle
;
677 status
= dcerpc_drsuapi_QuerySitesByCost_r(p
->binding_handle
, tctx
, &r
);
678 torture_drsuapi_assert_call(tctx
, p
, status
, &r
, "dcerpc_drsuapi_QuerySitesByCost");
680 if (W_ERROR_IS_OK(r
.out
.result
)) {
681 torture_assert_werr_equal(tctx
,
682 r
.out
.ctr
->ctr1
.info
[0].error_code
, WERR_DS_OBJ_NOT_FOUND
,
683 "dcerpc_drsuapi_QuerySitesByCost");
684 torture_assert_werr_equal(tctx
,
685 r
.out
.ctr
->ctr1
.info
[1].error_code
, WERR_DS_OBJ_NOT_FOUND
,
686 "dcerpc_drsuapi_QuerySitesByCost expected error_code WERR_DS_OBJ_NOT_FOUND");
688 torture_assert_int_equal(tctx
,
689 r
.out
.ctr
->ctr1
.info
[0].site_cost
, -1,
690 "dcerpc_drsuapi_QuerySitesByCost");
691 torture_assert_int_equal(tctx
,
692 r
.out
.ctr
->ctr1
.info
[1].site_cost
, -1,
693 "dcerpc_drsuapi_QuerySitesByCost exptected site cost");
701 bool test_DsUnbind(struct dcerpc_pipe
*p
,
702 struct torture_context
*tctx
,
703 struct DsPrivate
*priv
)
706 struct drsuapi_DsUnbind r
;
708 r
.in
.bind_handle
= &priv
->bind_handle
;
709 r
.out
.bind_handle
= &priv
->bind_handle
;
711 torture_comment(tctx
, "Testing DsUnbind\n");
713 status
= dcerpc_drsuapi_DsUnbind_r(p
->binding_handle
, tctx
, &r
);
714 torture_drsuapi_assert_call(tctx
, p
, status
, &r
, "dcerpc_drsuapi_DsUnbind");
721 * Helper func to collect DC information for testing purposes.
722 * This function is almost identical to test_DsGetDomainControllerInfo
724 bool torture_rpc_drsuapi_get_dcinfo(struct torture_context
*torture
,
725 struct DsPrivate
*priv
)
728 int32_t level_out
= 0;
729 struct drsuapi_DsGetDomainControllerInfo r
;
730 union drsuapi_DsGetDCInfoCtr ctr
;
732 const char *names
[] = {
733 torture_join_dom_netbios_name(priv
->join
),
734 torture_join_dom_dns_name(priv
->join
)};
736 for (j
=0; j
< ARRAY_SIZE(names
); j
++) {
737 union drsuapi_DsGetDCInfoRequest req
;
738 struct dcerpc_binding_handle
*b
= priv
->drs_pipe
->binding_handle
;
739 r
.in
.bind_handle
= &priv
->bind_handle
;
743 r
.in
.req
->req1
.domain_name
= names
[j
];
744 r
.in
.req
->req1
.level
= 2;
747 r
.out
.level_out
= &level_out
;
749 status
= dcerpc_drsuapi_DsGetDomainControllerInfo_r(b
, torture
, &r
);
750 if (!NT_STATUS_IS_OK(status
)) {
753 if (!W_ERROR_IS_OK(r
.out
.result
)) {
754 /* If this was an error, we can't read the result structure */
758 for (k
=0; k
< r
.out
.ctr
->ctr2
.count
; k
++) {
759 if (strcasecmp_m(r
.out
.ctr
->ctr2
.array
[k
].netbios_name
,
760 torture_join_netbios_name(priv
->join
)) == 0) {
761 priv
->dcinfo
= r
.out
.ctr
->ctr2
.array
[k
];
771 * Common test case setup function to be used
772 * in DRS suit of test when appropriate
774 bool torture_drsuapi_tcase_setup_common(struct torture_context
*tctx
, struct DsPrivate
*priv
)
777 int rnd
= rand() % 1000;
778 char *name
= talloc_asprintf(tctx
, "%s%d", TEST_MACHINE_NAME
, rnd
);
780 torture_assert(tctx
, priv
, "Invalid argument");
782 priv
->admin_credentials
= samba_cmdline_get_creds();
784 torture_comment(tctx
, "Create DRSUAPI pipe\n");
785 status
= torture_rpc_connection(tctx
,
788 torture_assert(tctx
, NT_STATUS_IS_OK(status
), "Unable to connect to DRSUAPI pipe");
790 torture_comment(tctx
, "About to join domain with name %s\n", name
);
791 priv
->join
= torture_join_domain(tctx
, name
, ACB_SVRTRUST
,
792 &priv
->dc_credentials
);
793 torture_assert(tctx
, priv
->join
, "Failed to join as BDC");
795 if (!test_DsBind(priv
->drs_pipe
, tctx
,
797 &priv
->srv_bind_info
))
800 torture_drsuapi_tcase_teardown_common(tctx
, priv
);
801 torture_fail(tctx
, "Failed execute test_DsBind()");
804 /* try collect some information for testing */
805 torture_rpc_drsuapi_get_dcinfo(tctx
, priv
);
811 * Common test case teardown function to be used
812 * in DRS suit of test when appropriate
814 bool torture_drsuapi_tcase_teardown_common(struct torture_context
*tctx
, struct DsPrivate
*priv
)
817 torture_leave_domain(tctx
, priv
->join
);
824 * Test case setup for DRSUAPI test case
826 static bool torture_drsuapi_tcase_setup(struct torture_context
*tctx
, void **data
)
828 struct DsPrivate
*priv
;
830 *data
= priv
= talloc_zero(tctx
, struct DsPrivate
);
832 return torture_drsuapi_tcase_setup_common(tctx
, priv
);
836 * Test case tear-down for DRSUAPI test case
838 static bool torture_drsuapi_tcase_teardown(struct torture_context
*tctx
, void *data
)
841 struct DsPrivate
*priv
= talloc_get_type(data
, struct DsPrivate
);
843 ret
= torture_drsuapi_tcase_teardown_common(tctx
, priv
);
849 static bool __test_DsBind_assoc_group(struct torture_context
*tctx
,
850 const char *testname
,
851 struct DsPrivate
*priv
,
852 struct cli_credentials
*creds
)
856 struct drsuapi_DsCrackNames r
;
857 union drsuapi_DsNameRequest req
;
859 union drsuapi_DsNameCtr ctr
;
860 struct drsuapi_DsNameString names
[1];
861 const char *dom_sid
= NULL
;
862 struct dcerpc_pipe
*p1
= NULL
;
863 struct dcerpc_pipe
*p2
= NULL
;
864 TALLOC_CTX
*mem_ctx
= priv
;
865 struct dcerpc_binding
*binding
= NULL
;
866 struct policy_handle ds_bind_handle
= { .handle_type
= 0, };
868 torture_comment(tctx
, "%s: starting...\n", testname
);
870 torture_assert_ntstatus_ok(tctx
,
871 torture_rpc_binding(tctx
, &binding
),
872 "torture_rpc_binding");
874 torture_assert_ntstatus_ok(tctx
,
875 dcerpc_pipe_connect_b(tctx
,
884 torture_assert_ntstatus_ok(tctx
,
885 dcerpc_pipe_connect_b(tctx
,
894 torture_assert(tctx
, test_DsBind(p1
, tctx
, &ds_bind_handle
, NULL
), "DsBind");
897 r
.in
.bind_handle
= &ds_bind_handle
;
900 r
.in
.req
->req1
.codepage
= 1252; /* german */
901 r
.in
.req
->req1
.language
= 0x00000407; /* german */
902 r
.in
.req
->req1
.count
= 1;
903 r
.in
.req
->req1
.names
= names
;
904 r
.in
.req
->req1
.format_flags
= DRSUAPI_DS_NAME_FLAG_NO_FLAGS
;
906 r
.in
.req
->req1
.format_offered
= DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY
;
907 r
.in
.req
->req1
.format_desired
= DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT
;
909 r
.out
.level_out
= &level_out
;
912 dom_sid
= dom_sid_string(mem_ctx
, torture_join_sid(priv
->join
));
914 names
[0].str
= dom_sid
;
916 torture_comment(tctx
, "Testing DsCrackNames on p1 with name '%s'"
917 " offered format: %d desired format:%d\n",
919 r
.in
.req
->req1
.format_offered
,
920 r
.in
.req
->req1
.format_desired
);
921 status
= dcerpc_drsuapi_DsCrackNames_r(p1
->binding_handle
, mem_ctx
, &r
);
922 if (!NT_STATUS_IS_OK(status
)) {
923 const char *errstr
= nt_errstr(status
);
924 err_msg
= talloc_asprintf(mem_ctx
, "dcerpc_drsuapi_DsCrackNames failed - %s", errstr
);
925 torture_fail(tctx
, err_msg
);
926 } else if (!W_ERROR_IS_OK(r
.out
.result
)) {
927 err_msg
= talloc_asprintf(mem_ctx
, "DsCrackNames failed - %s", win_errstr(r
.out
.result
));
928 torture_fail(tctx
, err_msg
);
929 } else if (r
.out
.ctr
->ctr1
->array
[0].status
!= DRSUAPI_DS_NAME_STATUS_OK
) {
930 err_msg
= talloc_asprintf(mem_ctx
, "DsCrackNames failed on name - %d",
931 r
.out
.ctr
->ctr1
->array
[0].status
);
932 torture_fail(tctx
, err_msg
);
935 torture_comment(tctx
, "Testing DsCrackNames on p2 with name '%s'"
936 " offered format: %d desired format:%d\n",
938 r
.in
.req
->req1
.format_offered
,
939 r
.in
.req
->req1
.format_desired
);
940 status
= dcerpc_drsuapi_DsCrackNames_r(p2
->binding_handle
, mem_ctx
, &r
);
941 if (!NT_STATUS_IS_OK(status
)) {
942 const char *errstr
= nt_errstr(status
);
943 err_msg
= talloc_asprintf(mem_ctx
, "dcerpc_drsuapi_DsCrackNames failed - %s", errstr
);
944 torture_fail(tctx
, err_msg
);
945 } else if (!W_ERROR_IS_OK(r
.out
.result
)) {
946 err_msg
= talloc_asprintf(mem_ctx
, "DsCrackNames failed - %s", win_errstr(r
.out
.result
));
947 torture_fail(tctx
, err_msg
);
948 } else if (r
.out
.ctr
->ctr1
->array
[0].status
!= DRSUAPI_DS_NAME_STATUS_OK
) {
949 err_msg
= talloc_asprintf(mem_ctx
, "DsCrackNames failed on name - %d",
950 r
.out
.ctr
->ctr1
->array
[0].status
);
951 torture_fail(tctx
, err_msg
);
956 torture_comment(tctx
, "Testing DsCrackNames on p2 (with p1 closed) with name '%s'"
957 " offered format: %d desired format:%d\n",
959 r
.in
.req
->req1
.format_offered
,
960 r
.in
.req
->req1
.format_desired
);
961 status
= dcerpc_drsuapi_DsCrackNames_r(p2
->binding_handle
, mem_ctx
, &r
);
962 if (!NT_STATUS_IS_OK(status
)) {
963 const char *errstr
= nt_errstr(status
);
964 err_msg
= talloc_asprintf(mem_ctx
, "dcerpc_drsuapi_DsCrackNames failed - %s", errstr
);
965 torture_fail(tctx
, err_msg
);
966 } else if (!W_ERROR_IS_OK(r
.out
.result
)) {
967 err_msg
= talloc_asprintf(mem_ctx
, "DsCrackNames failed - %s", win_errstr(r
.out
.result
));
968 torture_fail(tctx
, err_msg
);
969 } else if (r
.out
.ctr
->ctr1
->array
[0].status
!= DRSUAPI_DS_NAME_STATUS_OK
) {
970 err_msg
= talloc_asprintf(mem_ctx
, "DsCrackNames failed on name - %d",
971 r
.out
.ctr
->ctr1
->array
[0].status
);
972 torture_fail(tctx
, err_msg
);
975 torture_comment(tctx
, "%s: ... finished\n", testname
);
979 static bool test_DsBindAssocGroupAdmin(struct torture_context
*tctx
,
980 struct DsPrivate
*priv
,
981 struct cli_credentials
*creds
)
983 return __test_DsBind_assoc_group(tctx
, __func__
, priv
,
984 priv
->admin_credentials
);
987 static bool test_DsBindAssocGroupDC(struct torture_context
*tctx
,
988 struct DsPrivate
*priv
,
989 struct cli_credentials
*creds
)
991 return __test_DsBind_assoc_group(tctx
, __func__
, priv
,
992 priv
->dc_credentials
);
995 static bool test_DsBindAssocGroupWS(struct torture_context
*tctx
,
996 struct DsPrivate
*priv
,
997 struct cli_credentials
*creds
)
999 struct test_join
*wks_join
= NULL
;
1000 struct cli_credentials
*wks_credentials
= NULL
;
1001 int rnd
= rand() % 1000;
1002 char *wks_name
= talloc_asprintf(tctx
, "WKS%s%d", TEST_MACHINE_NAME
, rnd
);
1005 torture_comment(tctx
, "%s: About to join workstation with name %s\n",
1006 __func__
, wks_name
);
1007 wks_join
= torture_join_domain(tctx
, wks_name
, ACB_WSTRUST
,
1009 torture_assert(tctx
, wks_join
, "Failed to join as WORKSTATION");
1010 ret
= __test_DsBind_assoc_group(tctx
, __func__
, priv
,
1012 torture_leave_domain(tctx
, wks_join
);
1017 * DRSUAPI test case implementation
1019 void torture_rpc_drsuapi_tcase(struct torture_suite
*suite
)
1021 typedef bool (*run_func
) (struct torture_context
*test
, void *tcase_data
);
1023 struct torture_tcase
*tcase
= torture_suite_add_tcase(suite
, "drsuapi");
1025 torture_tcase_set_fixture(tcase
, torture_drsuapi_tcase_setup
,
1026 torture_drsuapi_tcase_teardown
);
1029 test
= torture_tcase_add_simple_test(tcase
, "QuerySitesByCost", (run_func
)test_QuerySitesByCost
);
1032 torture_tcase_add_simple_test(tcase
, "DsGetDomainControllerInfo", (run_func
)test_DsGetDomainControllerInfo
);
1034 torture_tcase_add_simple_test(tcase
, "DsCrackNames", (run_func
)test_DsCrackNames
);
1036 torture_tcase_add_simple_test(tcase
, "DsWriteAccountSpn", (run_func
)test_DsWriteAccountSpn
);
1038 torture_tcase_add_simple_test(tcase
, "DsReplicaGetInfo", (run_func
)test_DsReplicaGetInfo
);
1040 torture_tcase_add_simple_test(tcase
, "DsReplicaSync", (run_func
)test_DsReplicaSync
);
1042 torture_tcase_add_simple_test(tcase
, "DsReplicaUpdateRefs", (run_func
)test_DsReplicaUpdateRefs
);
1044 torture_tcase_add_simple_test(tcase
, "DsGetNCChanges", (run_func
)test_DsGetNCChanges
);
1046 torture_tcase_add_simple_test(tcase
, "DsBindAssocGroupAdmin", (run_func
)test_DsBindAssocGroupAdmin
);
1047 torture_tcase_add_simple_test(tcase
, "DsBindAssocGroupDC", (run_func
)test_DsBindAssocGroupDC
);
1048 torture_tcase_add_simple_test(tcase
, "DsBindAssocGroupWS", (run_func
)test_DsBindAssocGroupWS
);