2 Unix SMB/CIFS implementation.
4 Copyright (C) Stefan (metze) Metzmacher 2005
5 Copyright (C) Guenther Deschner 2008
6 Copyright (C) Michael Adam 2008
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/>.
24 #include "libnet/libnet_dssync.h"
25 #include "rpc_client/cli_pipe.h"
26 #include "../libcli/drsuapi/drsuapi.h"
27 #include "../librpc/gen_ndr/ndr_drsuapi_c.h"
29 /****************************************************************
30 ****************************************************************/
32 static int libnet_dssync_free_context(struct dssync_context
*ctx
)
35 struct dcerpc_binding_handle
*b
;
41 if (is_valid_policy_hnd(&ctx
->bind_handle
) && ctx
->cli
) {
42 b
= ctx
->cli
->binding_handle
;
43 dcerpc_drsuapi_DsUnbind(b
, ctx
, &ctx
->bind_handle
, &result
);
49 /****************************************************************
50 ****************************************************************/
52 NTSTATUS
libnet_dssync_init_context(TALLOC_CTX
*mem_ctx
,
53 struct dssync_context
**ctx_p
)
55 struct dssync_context
*ctx
;
57 ctx
= talloc_zero(mem_ctx
, struct dssync_context
);
58 NT_STATUS_HAVE_NO_MEMORY(ctx
);
60 talloc_set_destructor(ctx
, libnet_dssync_free_context
);
61 ctx
->clean_old_entries
= false;
68 /****************************************************************
69 ****************************************************************/
71 static void parse_obj_identifier(struct drsuapi_DsReplicaObjectIdentifier
*id
,
80 if (id
->sid
.num_auths
> 0) {
81 *rid
= id
->sid
.sub_auths
[id
->sid
.num_auths
- 1];
85 /****************************************************************
86 ****************************************************************/
88 static void libnet_dssync_decrypt_attributes(TALLOC_CTX
*mem_ctx
,
89 DATA_BLOB
*session_key
,
90 struct drsuapi_DsReplicaObjectListItemEx
*cur
)
92 for (; cur
; cur
= cur
->next_object
) {
97 parse_obj_identifier(cur
->object
.identifier
, &rid
);
99 for (i
=0; i
< cur
->object
.attribute_ctr
.num_attributes
; i
++) {
101 struct drsuapi_DsReplicaAttribute
*attr
;
103 attr
= &cur
->object
.attribute_ctr
.attributes
[i
];
105 if (attr
->value_ctr
.num_values
< 1) {
109 if (!attr
->value_ctr
.values
[0].blob
) {
113 drsuapi_decrypt_attribute(mem_ctx
,
121 /****************************************************************
122 ****************************************************************/
124 static NTSTATUS
libnet_dssync_bind(TALLOC_CTX
*mem_ctx
,
125 struct dssync_context
*ctx
)
130 struct GUID bind_guid
;
131 struct drsuapi_DsBindInfoCtr bind_info
;
132 struct drsuapi_DsBindInfo28 info28
;
133 struct dcerpc_binding_handle
*b
= ctx
->cli
->binding_handle
;
137 GUID_from_string(DRSUAPI_DS_BIND_GUID
, &bind_guid
);
139 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_BASE
;
140 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_ASYNC_REPLICATION
;
141 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_REMOVEAPI
;
142 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_MOVEREQ_V2
;
143 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GETCHG_COMPRESS
;
144 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V1
;
145 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_RESTORE_USN_OPTIMIZATION
;
146 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_KCC_EXECUTE
;
147 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRY_V2
;
148 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_LINKED_VALUE_REPLICATION
;
149 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V2
;
150 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_INSTANCE_TYPE_NOT_REQ_ON_MOD
;
151 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_CRYPTO_BIND
;
152 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GET_REPL_INFO
;
153 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_STRONG_ENCRYPTION
;
154 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V01
;
155 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_TRANSITIVE_MEMBERSHIP
;
156 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_ADD_SID_HISTORY
;
157 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_POST_BETA3
;
158 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GET_MEMBERSHIPS2
;
159 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V6
;
160 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_NONDOMAIN_NCS
;
161 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8
;
162 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V5
;
163 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V6
;
164 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRYREPLY_V3
;
165 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V7
;
166 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_VERIFY_OBJECT
;
167 info28
.site_guid
= GUID_zero();
169 info28
.repl_epoch
= 0;
171 bind_info
.length
= 28;
172 bind_info
.info
.info28
= info28
;
174 status
= dcerpc_drsuapi_DsBind(b
, mem_ctx
,
180 if (!NT_STATUS_IS_OK(status
)) {
184 if (!W_ERROR_IS_OK(werr
)) {
185 return werror_to_ntstatus(werr
);
188 ZERO_STRUCT(ctx
->remote_info28
);
189 switch (bind_info
.length
) {
191 struct drsuapi_DsBindInfo24
*info24
;
192 info24
= &bind_info
.info
.info24
;
193 ctx
->remote_info28
.site_guid
= info24
->site_guid
;
194 ctx
->remote_info28
.supported_extensions
= info24
->supported_extensions
;
195 ctx
->remote_info28
.pid
= info24
->pid
;
196 ctx
->remote_info28
.repl_epoch
= 0;
200 ctx
->remote_info28
= bind_info
.info
.info28
;
204 struct drsuapi_DsBindInfo32
*info32
;
205 info32
= &bind_info
.info
.info32
;
206 ctx
->remote_info28
.site_guid
= info32
->site_guid
;
207 ctx
->remote_info28
.supported_extensions
= info32
->supported_extensions
;
208 ctx
->remote_info28
.pid
= info32
->pid
;
209 ctx
->remote_info28
.repl_epoch
= info32
->repl_epoch
;
213 struct drsuapi_DsBindInfo48
*info48
;
214 info48
= &bind_info
.info
.info48
;
215 ctx
->remote_info28
.site_guid
= info48
->site_guid
;
216 ctx
->remote_info28
.supported_extensions
= info48
->supported_extensions
;
217 ctx
->remote_info28
.pid
= info48
->pid
;
218 ctx
->remote_info28
.repl_epoch
= info48
->repl_epoch
;
222 struct drsuapi_DsBindInfo52
*info52
;
223 info52
= &bind_info
.info
.info52
;
224 ctx
->remote_info28
.site_guid
= info52
->site_guid
;
225 ctx
->remote_info28
.supported_extensions
= info52
->supported_extensions
;
226 ctx
->remote_info28
.pid
= info52
->pid
;
227 ctx
->remote_info28
.repl_epoch
= info52
->repl_epoch
;
231 DEBUG(1, ("Warning: invalid info length in bind info: %d\n",
239 /****************************************************************
240 ****************************************************************/
242 static NTSTATUS
libnet_dssync_lookup_nc(TALLOC_CTX
*mem_ctx
,
243 struct dssync_context
*ctx
)
248 union drsuapi_DsNameRequest req
;
250 struct drsuapi_DsNameString names
[1];
251 union drsuapi_DsNameCtr ctr
;
252 struct dcerpc_binding_handle
*b
= ctx
->cli
->binding_handle
;
254 names
[0].str
= talloc_asprintf(mem_ctx
, "%s\\", ctx
->domain_name
);
255 NT_STATUS_HAVE_NO_MEMORY(names
[0].str
);
257 req
.req1
.codepage
= 1252; /* german */
258 req
.req1
.language
= 0x00000407; /* german */
260 req
.req1
.names
= names
;
261 req
.req1
.format_flags
= DRSUAPI_DS_NAME_FLAG_NO_FLAGS
;
262 req
.req1
.format_offered
= DRSUAPI_DS_NAME_FORMAT_UNKNOWN
;
263 req
.req1
.format_desired
= DRSUAPI_DS_NAME_FORMAT_FQDN_1779
;
265 status
= dcerpc_drsuapi_DsCrackNames(b
, mem_ctx
,
272 if (!NT_STATUS_IS_OK(status
)) {
273 ctx
->error_message
= talloc_asprintf(ctx
,
274 "Failed to lookup DN for domain name: %s",
275 get_friendly_nt_error_msg(status
));
279 if (!W_ERROR_IS_OK(werr
)) {
280 ctx
->error_message
= talloc_asprintf(ctx
,
281 "Failed to lookup DN for domain name: %s",
282 get_friendly_werror_msg(werr
));
283 return werror_to_ntstatus(werr
);
286 if (ctr
.ctr1
->count
!= 1) {
287 return NT_STATUS_UNSUCCESSFUL
;
290 if (ctr
.ctr1
->array
[0].status
!= DRSUAPI_DS_NAME_STATUS_OK
) {
291 return NT_STATUS_UNSUCCESSFUL
;
294 ctx
->nc_dn
= talloc_strdup(mem_ctx
, ctr
.ctr1
->array
[0].result_name
);
295 NT_STATUS_HAVE_NO_MEMORY(ctx
->nc_dn
);
297 if (!ctx
->dns_domain_name
) {
298 ctx
->dns_domain_name
= talloc_strdup_upper(mem_ctx
,
299 ctr
.ctr1
->array
[0].dns_domain_name
);
300 NT_STATUS_HAVE_NO_MEMORY(ctx
->dns_domain_name
);
306 /****************************************************************
307 ****************************************************************/
309 static NTSTATUS
libnet_dssync_init(TALLOC_CTX
*mem_ctx
,
310 struct dssync_context
*ctx
)
314 status
= libnet_dssync_bind(mem_ctx
, ctx
);
315 if (!NT_STATUS_IS_OK(status
)) {
320 status
= libnet_dssync_lookup_nc(mem_ctx
, ctx
);
326 /****************************************************************
327 ****************************************************************/
329 static NTSTATUS
libnet_dssync_build_request(TALLOC_CTX
*mem_ctx
,
330 struct dssync_context
*ctx
,
332 struct replUpToDateVectorBlob
*utdv
,
334 union drsuapi_DsGetNCChangesRequest
*preq
)
339 union drsuapi_DsGetNCChangesRequest req
;
340 enum drsuapi_DsExtendedOperation extended_op
;
341 struct drsuapi_DsReplicaObjectIdentifier
*nc
= NULL
;
342 struct drsuapi_DsReplicaCursorCtrEx
*cursors
= NULL
;
344 uint32_t replica_flags
= DRSUAPI_DRS_WRIT_REP
|
345 DRSUAPI_DRS_INIT_SYNC
|
346 DRSUAPI_DRS_PER_SYNC
|
347 DRSUAPI_DRS_GET_ANC
|
348 DRSUAPI_DRS_NEVER_SYNCED
;
352 if (ctx
->remote_info28
.supported_extensions
353 & DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8
)
360 nc
= talloc_zero(mem_ctx
, struct drsuapi_DsReplicaObjectIdentifier
);
362 status
= NT_STATUS_NO_MEMORY
;
366 nc
->guid
= GUID_zero();
367 nc
->sid
= (struct dom_sid
) {0};
369 if (!ctx
->single_object_replication
&&
370 !ctx
->force_full_replication
&& utdv
)
372 cursors
= talloc_zero(mem_ctx
,
373 struct drsuapi_DsReplicaCursorCtrEx
);
375 status
= NT_STATUS_NO_MEMORY
;
379 switch (utdv
->version
) {
381 cursors
->count
= utdv
->ctr
.ctr1
.count
;
382 cursors
->cursors
= utdv
->ctr
.ctr1
.cursors
;
385 cursors
->count
= utdv
->ctr
.ctr2
.count
;
386 cursors
->cursors
= talloc_array(cursors
,
387 struct drsuapi_DsReplicaCursor
,
389 if (!cursors
->cursors
) {
390 status
= NT_STATUS_NO_MEMORY
;
393 for (count
= 0; count
< cursors
->count
; count
++) {
394 cursors
->cursors
[count
].source_dsa_invocation_id
=
395 utdv
->ctr
.ctr2
.cursors
[count
].source_dsa_invocation_id
;
396 cursors
->cursors
[count
].highest_usn
=
397 utdv
->ctr
.ctr2
.cursors
[count
].highest_usn
;
403 if (ctx
->single_object_replication
) {
404 extended_op
= DRSUAPI_EXOP_REPL_OBJ
;
406 extended_op
= DRSUAPI_EXOP_NONE
;
410 req
.req8
.naming_context
= nc
;
411 req
.req8
.replica_flags
= replica_flags
;
412 req
.req8
.max_object_count
= 402;
413 req
.req8
.max_ndr_size
= 402116;
414 req
.req8
.uptodateness_vector
= cursors
;
415 req
.req8
.extended_op
= extended_op
;
416 } else if (level
== 5) {
417 req
.req5
.naming_context
= nc
;
418 req
.req5
.replica_flags
= replica_flags
;
419 req
.req5
.max_object_count
= 402;
420 req
.req5
.max_ndr_size
= 402116;
421 req
.req5
.uptodateness_vector
= cursors
;
422 req
.req5
.extended_op
= extended_op
;
424 status
= NT_STATUS_INVALID_PARAMETER
;
440 TALLOC_FREE(cursors
);
444 static NTSTATUS
libnet_dssync_getncchanges(TALLOC_CTX
*mem_ctx
,
445 struct dssync_context
*ctx
,
447 union drsuapi_DsGetNCChangesRequest
*req
,
448 struct replUpToDateVectorBlob
**pnew_utdv
)
452 union drsuapi_DsGetNCChangesCtr ctr
;
453 struct drsuapi_DsGetNCChangesCtr1
*ctr1
= NULL
;
454 struct drsuapi_DsGetNCChangesCtr6
*ctr6
= NULL
;
455 struct replUpToDateVectorBlob
*new_utdv
= NULL
;
456 uint32_t level_out
= 0;
457 uint32_t out_level
= 0;
460 struct dcerpc_binding_handle
*b
= ctx
->cli
->binding_handle
;
462 if (!ctx
->single_object_replication
) {
463 new_utdv
= talloc_zero(mem_ctx
, struct replUpToDateVectorBlob
);
465 status
= NT_STATUS_NO_MEMORY
;
470 status
= dcerpc_binding_handle_auth_session_key(
471 b
, mem_ctx
, &ctx
->session_key
);
472 if (!NT_STATUS_IS_OK(status
)) {
473 ctx
->error_message
= talloc_asprintf(ctx
,
474 "Failed to get Session Key: %s",
479 for (y
=0, last_query
= false; !last_query
; y
++) {
480 struct drsuapi_DsReplicaObjectListItemEx
*first_object
= NULL
;
481 struct drsuapi_DsReplicaOIDMapping_Ctr
*mapping_ctr
= NULL
;
482 uint32_t linked_attributes_count
= 0;
483 struct drsuapi_DsReplicaLinkedAttribute
*linked_attributes
= NULL
;
486 DEBUG(1,("start[%d] tmp_higest_usn: %llu , highest_usn: %llu\n",y
,
487 (long long)req
->req8
.highwatermark
.tmp_highest_usn
,
488 (long long)req
->req8
.highwatermark
.highest_usn
));
489 } else if (level
== 5) {
490 DEBUG(1,("start[%d] tmp_higest_usn: %llu , highest_usn: %llu\n",y
,
491 (long long)req
->req5
.highwatermark
.tmp_highest_usn
,
492 (long long)req
->req5
.highwatermark
.highest_usn
));
495 status
= dcerpc_drsuapi_DsGetNCChanges(b
, mem_ctx
,
502 if (!NT_STATUS_IS_OK(status
)) {
503 ctx
->error_message
= talloc_asprintf(ctx
,
504 "Failed to get NC Changes: %s",
505 get_friendly_nt_error_msg(status
));
509 if (!W_ERROR_IS_OK(werr
)) {
510 status
= werror_to_ntstatus(werr
);
511 ctx
->error_message
= talloc_asprintf(ctx
,
512 "Failed to get NC Changes: %s",
513 get_friendly_werror_msg(werr
));
517 if (level_out
== 1) {
520 } else if (level_out
== 2 && ctr
.ctr2
.mszip1
.ts
) {
522 ctr1
= &ctr
.ctr2
.mszip1
.ts
->ctr1
;
523 } else if (level_out
== 6) {
526 } else if (level_out
== 7
527 && ctr
.ctr7
.level
== 6
528 && ctr
.ctr7
.type
== DRSUAPI_COMPRESSION_TYPE_MSZIP
529 && ctr
.ctr7
.ctr
.mszip6
.ts
) {
531 ctr6
= &ctr
.ctr7
.ctr
.mszip6
.ts
->ctr6
;
532 } else if (level_out
== 7
533 && ctr
.ctr7
.level
== 6
534 && ctr
.ctr7
.type
== DRSUAPI_COMPRESSION_TYPE_WIN2K3_LZ77_DIRECT2
535 && ctr
.ctr7
.ctr
.xpress6
.ts
) {
537 ctr6
= &ctr
.ctr7
.ctr
.xpress6
.ts
->ctr6
;
540 if (out_level
== 1) {
541 DEBUG(1,("end[%d] tmp_highest_usn: %llu , highest_usn: %llu\n",y
,
542 (long long)ctr1
->new_highwatermark
.tmp_highest_usn
,
543 (long long)ctr1
->new_highwatermark
.highest_usn
));
545 first_object
= ctr1
->first_object
;
546 mapping_ctr
= &ctr1
->mapping_ctr
;
548 if (ctr1
->more_data
) {
549 req
->req5
.highwatermark
= ctr1
->new_highwatermark
;
552 if (ctr1
->uptodateness_vector
&&
553 !ctx
->single_object_replication
)
555 new_utdv
->version
= 1;
556 new_utdv
->ctr
.ctr1
.count
=
557 ctr1
->uptodateness_vector
->count
;
558 new_utdv
->ctr
.ctr1
.cursors
=
559 ctr1
->uptodateness_vector
->cursors
;
562 } else if (out_level
== 6) {
563 DEBUG(1,("end[%d] tmp_highest_usn: %llu , highest_usn: %llu\n",y
,
564 (long long)ctr6
->new_highwatermark
.tmp_highest_usn
,
565 (long long)ctr6
->new_highwatermark
.highest_usn
));
567 first_object
= ctr6
->first_object
;
568 mapping_ctr
= &ctr6
->mapping_ctr
;
570 linked_attributes
= ctr6
->linked_attributes
;
571 linked_attributes_count
= ctr6
->linked_attributes_count
;
573 if (ctr6
->more_data
) {
574 req
->req8
.highwatermark
= ctr6
->new_highwatermark
;
577 if (ctr6
->uptodateness_vector
&&
578 !ctx
->single_object_replication
)
580 new_utdv
->version
= 2;
581 new_utdv
->ctr
.ctr2
.count
=
582 ctr6
->uptodateness_vector
->count
;
583 new_utdv
->ctr
.ctr2
.cursors
=
584 ctr6
->uptodateness_vector
->cursors
;
589 libnet_dssync_decrypt_attributes(mem_ctx
,
593 if (ctx
->ops
->process_objects
) {
594 status
= ctx
->ops
->process_objects(ctx
, mem_ctx
,
597 if (!NT_STATUS_IS_OK(status
)) {
598 ctx
->error_message
= talloc_asprintf(ctx
,
599 "Failed to call processing function: %s",
605 if (linked_attributes_count
== 0) {
609 if (ctx
->ops
->process_links
) {
610 status
= ctx
->ops
->process_links(ctx
, mem_ctx
,
611 linked_attributes_count
,
614 if (!NT_STATUS_IS_OK(status
)) {
615 ctx
->error_message
= talloc_asprintf(ctx
,
616 "Failed to call processing function: %s",
623 *pnew_utdv
= new_utdv
;
629 static NTSTATUS
libnet_dssync_process(TALLOC_CTX
*mem_ctx
,
630 struct dssync_context
*ctx
)
635 union drsuapi_DsGetNCChangesRequest req
;
636 struct replUpToDateVectorBlob
*old_utdv
= NULL
;
637 struct replUpToDateVectorBlob
*pnew_utdv
= NULL
;
642 if (ctx
->ops
->startup
) {
643 status
= ctx
->ops
->startup(ctx
, mem_ctx
, &old_utdv
);
644 if (!NT_STATUS_IS_OK(status
)) {
645 ctx
->error_message
= talloc_asprintf(ctx
,
646 "Failed to call startup operation: %s",
652 if (ctx
->single_object_replication
&& ctx
->object_dns
) {
653 dns
= ctx
->object_dns
;
654 dn_count
= ctx
->object_count
;
660 status
= NT_STATUS_OK
;
662 for (count
=0; count
< dn_count
; count
++) {
663 status
= libnet_dssync_build_request(mem_ctx
, ctx
,
667 if (!NT_STATUS_IS_OK(status
)) {
671 status
= libnet_dssync_getncchanges(mem_ctx
, ctx
, level
, &req
,
673 if (!NT_STATUS_IS_OK(status
)) {
674 if (!ctx
->error_message
) {
675 ctx
->error_message
= talloc_asprintf(ctx
,
676 "Failed to call DsGetNCCHanges: %s",
683 if (ctx
->ops
->finish
) {
684 status
= ctx
->ops
->finish(ctx
, mem_ctx
, pnew_utdv
);
685 if (!NT_STATUS_IS_OK(status
)) {
686 ctx
->error_message
= talloc_asprintf(ctx
,
687 "Failed to call finishing operation: %s",
697 /****************************************************************
698 ****************************************************************/
700 NTSTATUS
libnet_dssync(TALLOC_CTX
*mem_ctx
,
701 struct dssync_context
*ctx
)
706 tmp_ctx
= talloc_new(mem_ctx
);
708 return NT_STATUS_NO_MEMORY
;
711 status
= libnet_dssync_init(tmp_ctx
, ctx
);
712 if (!NT_STATUS_IS_OK(status
)) {
716 status
= libnet_dssync_process(tmp_ctx
, ctx
);
717 if (!NT_STATUS_IS_OK(status
)) {
722 TALLOC_FREE(tmp_ctx
);