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 "../libcli/drsuapi/drsuapi.h"
26 #include "../librpc/gen_ndr/cli_drsuapi.h"
28 /****************************************************************
29 ****************************************************************/
31 static int libnet_dssync_free_context(struct dssync_context
*ctx
)
37 if (is_valid_policy_hnd(&ctx
->bind_handle
) && ctx
->cli
) {
38 rpccli_drsuapi_DsUnbind(ctx
->cli
, ctx
, &ctx
->bind_handle
, NULL
);
44 /****************************************************************
45 ****************************************************************/
47 NTSTATUS
libnet_dssync_init_context(TALLOC_CTX
*mem_ctx
,
48 struct dssync_context
**ctx_p
)
50 struct dssync_context
*ctx
;
52 ctx
= TALLOC_ZERO_P(mem_ctx
, struct dssync_context
);
53 NT_STATUS_HAVE_NO_MEMORY(ctx
);
55 talloc_set_destructor(ctx
, libnet_dssync_free_context
);
56 ctx
->clean_old_entries
= false;
63 /****************************************************************
64 ****************************************************************/
66 static void parse_obj_identifier(struct drsuapi_DsReplicaObjectIdentifier
*id
,
75 if (id
->sid
.num_auths
> 0) {
76 *rid
= id
->sid
.sub_auths
[id
->sid
.num_auths
- 1];
80 /****************************************************************
81 ****************************************************************/
83 static void libnet_dssync_decrypt_attributes(TALLOC_CTX
*mem_ctx
,
84 DATA_BLOB
*session_key
,
85 struct drsuapi_DsReplicaObjectListItemEx
*cur
)
87 for (; cur
; cur
= cur
->next_object
) {
92 parse_obj_identifier(cur
->object
.identifier
, &rid
);
94 for (i
=0; i
< cur
->object
.attribute_ctr
.num_attributes
; i
++) {
96 struct drsuapi_DsReplicaAttribute
*attr
;
98 attr
= &cur
->object
.attribute_ctr
.attributes
[i
];
100 if (attr
->value_ctr
.num_values
< 1) {
104 if (!attr
->value_ctr
.values
[0].blob
) {
108 drsuapi_decrypt_attribute(mem_ctx
,
115 /****************************************************************
116 ****************************************************************/
118 static NTSTATUS
libnet_dssync_bind(TALLOC_CTX
*mem_ctx
,
119 struct dssync_context
*ctx
)
124 struct GUID bind_guid
;
125 struct drsuapi_DsBindInfoCtr bind_info
;
126 struct drsuapi_DsBindInfo28 info28
;
130 GUID_from_string(DRSUAPI_DS_BIND_GUID
, &bind_guid
);
132 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_BASE
;
133 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_ASYNC_REPLICATION
;
134 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_REMOVEAPI
;
135 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_MOVEREQ_V2
;
136 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GETCHG_COMPRESS
;
137 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V1
;
138 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_RESTORE_USN_OPTIMIZATION
;
139 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_KCC_EXECUTE
;
140 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRY_V2
;
141 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_LINKED_VALUE_REPLICATION
;
142 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V2
;
143 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_INSTANCE_TYPE_NOT_REQ_ON_MOD
;
144 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_CRYPTO_BIND
;
145 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GET_REPL_INFO
;
146 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_STRONG_ENCRYPTION
;
147 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V01
;
148 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_TRANSITIVE_MEMBERSHIP
;
149 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_ADD_SID_HISTORY
;
150 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_POST_BETA3
;
151 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GET_MEMBERSHIPS2
;
152 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V6
;
153 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_NONDOMAIN_NCS
;
154 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8
;
155 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V5
;
156 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V6
;
157 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRYREPLY_V3
;
158 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V7
;
159 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_VERIFY_OBJECT
;
160 info28
.site_guid
= GUID_zero();
162 info28
.repl_epoch
= 0;
164 bind_info
.length
= 28;
165 bind_info
.info
.info28
= info28
;
167 status
= rpccli_drsuapi_DsBind(ctx
->cli
, mem_ctx
,
173 if (!NT_STATUS_IS_OK(status
)) {
177 if (!W_ERROR_IS_OK(werr
)) {
178 return werror_to_ntstatus(werr
);
181 ZERO_STRUCT(ctx
->remote_info28
);
182 switch (bind_info
.length
) {
184 struct drsuapi_DsBindInfo24
*info24
;
185 info24
= &bind_info
.info
.info24
;
186 ctx
->remote_info28
.site_guid
= info24
->site_guid
;
187 ctx
->remote_info28
.supported_extensions
= info24
->supported_extensions
;
188 ctx
->remote_info28
.pid
= info24
->pid
;
189 ctx
->remote_info28
.repl_epoch
= 0;
193 ctx
->remote_info28
= bind_info
.info
.info28
;
196 struct drsuapi_DsBindInfo48
*info48
;
197 info48
= &bind_info
.info
.info48
;
198 ctx
->remote_info28
.site_guid
= info48
->site_guid
;
199 ctx
->remote_info28
.supported_extensions
= info48
->supported_extensions
;
200 ctx
->remote_info28
.pid
= info48
->pid
;
201 ctx
->remote_info28
.repl_epoch
= info48
->repl_epoch
;
205 DEBUG(1, ("Warning: invalid info length in bind info: %d\n",
213 /****************************************************************
214 ****************************************************************/
216 static NTSTATUS
libnet_dssync_lookup_nc(TALLOC_CTX
*mem_ctx
,
217 struct dssync_context
*ctx
)
222 union drsuapi_DsNameRequest req
;
224 struct drsuapi_DsNameString names
[1];
225 union drsuapi_DsNameCtr ctr
;
227 names
[0].str
= talloc_asprintf(mem_ctx
, "%s\\", ctx
->domain_name
);
228 NT_STATUS_HAVE_NO_MEMORY(names
[0].str
);
230 req
.req1
.codepage
= 1252; /* german */
231 req
.req1
.language
= 0x00000407; /* german */
233 req
.req1
.names
= names
;
234 req
.req1
.format_flags
= DRSUAPI_DS_NAME_FLAG_NO_FLAGS
;
235 req
.req1
.format_offered
= DRSUAPI_DS_NAME_FORMAT_UNKNOWN
;
236 req
.req1
.format_desired
= DRSUAPI_DS_NAME_FORMAT_FQDN_1779
;
238 status
= rpccli_drsuapi_DsCrackNames(ctx
->cli
, mem_ctx
,
245 if (!NT_STATUS_IS_OK(status
)) {
246 ctx
->error_message
= talloc_asprintf(ctx
,
247 "Failed to lookup DN for domain name: %s",
248 get_friendly_werror_msg(werr
));
252 if (!W_ERROR_IS_OK(werr
)) {
253 return werror_to_ntstatus(werr
);
256 if (ctr
.ctr1
->count
!= 1) {
257 return NT_STATUS_UNSUCCESSFUL
;
260 if (ctr
.ctr1
->array
[0].status
!= DRSUAPI_DS_NAME_STATUS_OK
) {
261 return NT_STATUS_UNSUCCESSFUL
;
264 ctx
->nc_dn
= talloc_strdup(mem_ctx
, ctr
.ctr1
->array
[0].result_name
);
265 NT_STATUS_HAVE_NO_MEMORY(ctx
->nc_dn
);
267 if (!ctx
->dns_domain_name
) {
268 ctx
->dns_domain_name
= talloc_strdup_upper(mem_ctx
,
269 ctr
.ctr1
->array
[0].dns_domain_name
);
270 NT_STATUS_HAVE_NO_MEMORY(ctx
->dns_domain_name
);
276 /****************************************************************
277 ****************************************************************/
279 static NTSTATUS
libnet_dssync_init(TALLOC_CTX
*mem_ctx
,
280 struct dssync_context
*ctx
)
284 status
= libnet_dssync_bind(mem_ctx
, ctx
);
285 if (!NT_STATUS_IS_OK(status
)) {
290 status
= libnet_dssync_lookup_nc(mem_ctx
, ctx
);
296 /****************************************************************
297 ****************************************************************/
299 static NTSTATUS
libnet_dssync_build_request(TALLOC_CTX
*mem_ctx
,
300 struct dssync_context
*ctx
,
302 struct replUpToDateVectorBlob
*utdv
,
304 union drsuapi_DsGetNCChangesRequest
*preq
)
309 union drsuapi_DsGetNCChangesRequest req
;
310 struct dom_sid null_sid
;
311 enum drsuapi_DsExtendedOperation extended_op
;
312 struct drsuapi_DsReplicaObjectIdentifier
*nc
= NULL
;
313 struct drsuapi_DsReplicaCursorCtrEx
*cursors
= NULL
;
315 uint32_t replica_flags
= DRSUAPI_DRS_WRIT_REP
|
316 DRSUAPI_DRS_INIT_SYNC
|
317 DRSUAPI_DRS_PER_SYNC
|
318 DRSUAPI_DRS_GET_ANC
|
319 DRSUAPI_DRS_NEVER_SYNCED
;
321 ZERO_STRUCT(null_sid
);
324 if (ctx
->remote_info28
.supported_extensions
325 & DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8
)
332 nc
= TALLOC_ZERO_P(mem_ctx
, struct drsuapi_DsReplicaObjectIdentifier
);
334 status
= NT_STATUS_NO_MEMORY
;
338 nc
->guid
= GUID_zero();
341 if (!ctx
->single_object_replication
&&
342 !ctx
->force_full_replication
&& utdv
)
344 cursors
= TALLOC_ZERO_P(mem_ctx
,
345 struct drsuapi_DsReplicaCursorCtrEx
);
347 status
= NT_STATUS_NO_MEMORY
;
351 switch (utdv
->version
) {
353 cursors
->count
= utdv
->ctr
.ctr1
.count
;
354 cursors
->cursors
= utdv
->ctr
.ctr1
.cursors
;
357 cursors
->count
= utdv
->ctr
.ctr2
.count
;
358 cursors
->cursors
= talloc_array(cursors
,
359 struct drsuapi_DsReplicaCursor
,
361 if (!cursors
->cursors
) {
362 status
= NT_STATUS_NO_MEMORY
;
365 for (count
= 0; count
< cursors
->count
; count
++) {
366 cursors
->cursors
[count
].source_dsa_invocation_id
=
367 utdv
->ctr
.ctr2
.cursors
[count
].source_dsa_invocation_id
;
368 cursors
->cursors
[count
].highest_usn
=
369 utdv
->ctr
.ctr2
.cursors
[count
].highest_usn
;
375 if (ctx
->single_object_replication
) {
376 extended_op
= DRSUAPI_EXOP_REPL_OBJ
;
378 extended_op
= DRSUAPI_EXOP_NONE
;
382 req
.req8
.naming_context
= nc
;
383 req
.req8
.replica_flags
= replica_flags
;
384 req
.req8
.max_object_count
= 402;
385 req
.req8
.max_ndr_size
= 402116;
386 req
.req8
.uptodateness_vector
= cursors
;
387 req
.req8
.extended_op
= extended_op
;
388 } else if (level
== 5) {
389 req
.req5
.naming_context
= nc
;
390 req
.req5
.replica_flags
= replica_flags
;
391 req
.req5
.max_object_count
= 402;
392 req
.req5
.max_ndr_size
= 402116;
393 req
.req5
.uptodateness_vector
= cursors
;
394 req
.req5
.extended_op
= extended_op
;
396 status
= NT_STATUS_INVALID_PARAMETER
;
412 TALLOC_FREE(cursors
);
416 static NTSTATUS
libnet_dssync_getncchanges(TALLOC_CTX
*mem_ctx
,
417 struct dssync_context
*ctx
,
419 union drsuapi_DsGetNCChangesRequest
*req
,
420 struct replUpToDateVectorBlob
**pnew_utdv
)
424 union drsuapi_DsGetNCChangesCtr ctr
;
425 struct drsuapi_DsGetNCChangesCtr1
*ctr1
= NULL
;
426 struct drsuapi_DsGetNCChangesCtr6
*ctr6
= NULL
;
427 struct replUpToDateVectorBlob
*new_utdv
= NULL
;
428 uint32_t level_out
= 0;
429 uint32_t out_level
= 0;
433 if (!ctx
->single_object_replication
) {
434 new_utdv
= TALLOC_ZERO_P(mem_ctx
, struct replUpToDateVectorBlob
);
436 status
= NT_STATUS_NO_MEMORY
;
441 for (y
=0, last_query
= false; !last_query
; y
++) {
442 struct drsuapi_DsReplicaObjectListItemEx
*first_object
= NULL
;
443 struct drsuapi_DsReplicaOIDMapping_Ctr
*mapping_ctr
= NULL
;
446 DEBUG(1,("start[%d] tmp_higest_usn: %llu , highest_usn: %llu\n",y
,
447 (long long)req
->req8
.highwatermark
.tmp_highest_usn
,
448 (long long)req
->req8
.highwatermark
.highest_usn
));
449 } else if (level
== 5) {
450 DEBUG(1,("start[%d] tmp_higest_usn: %llu , highest_usn: %llu\n",y
,
451 (long long)req
->req5
.highwatermark
.tmp_highest_usn
,
452 (long long)req
->req5
.highwatermark
.highest_usn
));
455 status
= rpccli_drsuapi_DsGetNCChanges(ctx
->cli
, mem_ctx
,
462 if (!NT_STATUS_IS_OK(status
)) {
463 ctx
->error_message
= talloc_asprintf(ctx
,
464 "Failed to get NC Changes: %s",
465 get_friendly_werror_msg(werr
));
469 if (!W_ERROR_IS_OK(werr
)) {
470 status
= werror_to_ntstatus(werr
);
474 if (level_out
== 1) {
477 } else if (level_out
== 2 && ctr
.ctr2
.mszip1
.ts
) {
479 ctr1
= &ctr
.ctr2
.mszip1
.ts
->ctr1
;
480 } else if (level_out
== 6) {
483 } else if (level_out
== 7
484 && ctr
.ctr7
.level
== 6
485 && ctr
.ctr7
.type
== DRSUAPI_COMPRESSION_TYPE_MSZIP
486 && ctr
.ctr7
.ctr
.mszip6
.ts
) {
488 ctr6
= &ctr
.ctr7
.ctr
.mszip6
.ts
->ctr6
;
489 } else if (level_out
== 7
490 && ctr
.ctr7
.level
== 6
491 && ctr
.ctr7
.type
== DRSUAPI_COMPRESSION_TYPE_XPRESS
492 && ctr
.ctr7
.ctr
.xpress6
.ts
) {
494 ctr6
= &ctr
.ctr7
.ctr
.xpress6
.ts
->ctr6
;
497 if (out_level
== 1) {
498 DEBUG(1,("end[%d] tmp_highest_usn: %llu , highest_usn: %llu\n",y
,
499 (long long)ctr1
->new_highwatermark
.tmp_highest_usn
,
500 (long long)ctr1
->new_highwatermark
.highest_usn
));
502 first_object
= ctr1
->first_object
;
503 mapping_ctr
= &ctr1
->mapping_ctr
;
505 if (ctr1
->more_data
) {
506 req
->req5
.highwatermark
= ctr1
->new_highwatermark
;
509 if (ctr1
->uptodateness_vector
&&
510 !ctx
->single_object_replication
)
512 new_utdv
->version
= 1;
513 new_utdv
->ctr
.ctr1
.count
=
514 ctr1
->uptodateness_vector
->count
;
515 new_utdv
->ctr
.ctr1
.cursors
=
516 ctr1
->uptodateness_vector
->cursors
;
519 } else if (out_level
== 6) {
520 DEBUG(1,("end[%d] tmp_highest_usn: %llu , highest_usn: %llu\n",y
,
521 (long long)ctr6
->new_highwatermark
.tmp_highest_usn
,
522 (long long)ctr6
->new_highwatermark
.highest_usn
));
524 first_object
= ctr6
->first_object
;
525 mapping_ctr
= &ctr6
->mapping_ctr
;
527 if (ctr6
->more_data
) {
528 req
->req8
.highwatermark
= ctr6
->new_highwatermark
;
531 if (ctr6
->uptodateness_vector
&&
532 !ctx
->single_object_replication
)
534 new_utdv
->version
= 2;
535 new_utdv
->ctr
.ctr2
.count
=
536 ctr6
->uptodateness_vector
->count
;
537 new_utdv
->ctr
.ctr2
.cursors
=
538 ctr6
->uptodateness_vector
->cursors
;
543 status
= cli_get_session_key(mem_ctx
, ctx
->cli
, &ctx
->session_key
);
544 if (!NT_STATUS_IS_OK(status
)) {
545 ctx
->error_message
= talloc_asprintf(ctx
,
546 "Failed to get Session Key: %s",
551 libnet_dssync_decrypt_attributes(mem_ctx
,
555 if (ctx
->ops
->process_objects
) {
556 status
= ctx
->ops
->process_objects(ctx
, mem_ctx
,
559 if (!NT_STATUS_IS_OK(status
)) {
560 ctx
->error_message
= talloc_asprintf(ctx
,
561 "Failed to call processing function: %s",
568 *pnew_utdv
= new_utdv
;
574 static NTSTATUS
libnet_dssync_process(TALLOC_CTX
*mem_ctx
,
575 struct dssync_context
*ctx
)
580 union drsuapi_DsGetNCChangesRequest req
;
581 struct replUpToDateVectorBlob
*old_utdv
= NULL
;
582 struct replUpToDateVectorBlob
*pnew_utdv
= NULL
;
587 if (ctx
->ops
->startup
) {
588 status
= ctx
->ops
->startup(ctx
, mem_ctx
, &old_utdv
);
589 if (!NT_STATUS_IS_OK(status
)) {
590 ctx
->error_message
= talloc_asprintf(ctx
,
591 "Failed to call startup operation: %s",
597 if (ctx
->single_object_replication
&& ctx
->object_dns
) {
598 dns
= ctx
->object_dns
;
599 dn_count
= ctx
->object_count
;
605 status
= NT_STATUS_OK
;
607 for (count
=0; count
< dn_count
; count
++) {
608 status
= libnet_dssync_build_request(mem_ctx
, ctx
,
612 if (!NT_STATUS_IS_OK(status
)) {
616 status
= libnet_dssync_getncchanges(mem_ctx
, ctx
, level
, &req
,
618 if (!NT_STATUS_IS_OK(status
)) {
619 ctx
->error_message
= talloc_asprintf(ctx
,
620 "Failed to call DsGetNCCHanges: %s",
626 if (ctx
->ops
->finish
) {
627 status
= ctx
->ops
->finish(ctx
, mem_ctx
, pnew_utdv
);
628 if (!NT_STATUS_IS_OK(status
)) {
629 ctx
->error_message
= talloc_asprintf(ctx
,
630 "Failed to call finishing operation: %s",
640 /****************************************************************
641 ****************************************************************/
643 NTSTATUS
libnet_dssync(TALLOC_CTX
*mem_ctx
,
644 struct dssync_context
*ctx
)
649 tmp_ctx
= talloc_new(mem_ctx
);
651 return NT_STATUS_NO_MEMORY
;
654 status
= libnet_dssync_init(tmp_ctx
, ctx
);
655 if (!NT_STATUS_IS_OK(status
)) {
659 status
= libnet_dssync_process(tmp_ctx
, ctx
);
660 if (!NT_STATUS_IS_OK(status
)) {
665 TALLOC_FREE(tmp_ctx
);