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.h"
26 /****************************************************************
27 ****************************************************************/
29 static int libnet_dssync_free_context(struct dssync_context
*ctx
)
35 if (is_valid_policy_hnd(&ctx
->bind_handle
) && ctx
->cli
) {
36 rpccli_drsuapi_DsUnbind(ctx
->cli
, ctx
, &ctx
->bind_handle
, NULL
);
42 /****************************************************************
43 ****************************************************************/
45 NTSTATUS
libnet_dssync_init_context(TALLOC_CTX
*mem_ctx
,
46 struct dssync_context
**ctx_p
)
48 struct dssync_context
*ctx
;
50 ctx
= TALLOC_ZERO_P(mem_ctx
, struct dssync_context
);
51 NT_STATUS_HAVE_NO_MEMORY(ctx
);
53 talloc_set_destructor(ctx
, libnet_dssync_free_context
);
54 ctx
->clean_old_entries
= false;
61 /****************************************************************
62 ****************************************************************/
64 static DATA_BLOB
*decrypt_attr_val(TALLOC_CTX
*mem_ctx
,
65 DATA_BLOB
*session_key
,
67 enum drsuapi_DsAttributeId id
,
73 ZERO_STRUCT(out_data
);
76 case DRSUAPI_ATTRIBUTE_dBCSPwd
:
77 case DRSUAPI_ATTRIBUTE_unicodePwd
:
78 case DRSUAPI_ATTRIBUTE_ntPwdHistory
:
79 case DRSUAPI_ATTRIBUTE_lmPwdHistory
:
82 case DRSUAPI_ATTRIBUTE_supplementalCredentials
:
83 case DRSUAPI_ATTRIBUTE_priorValue
:
84 case DRSUAPI_ATTRIBUTE_currentValue
:
85 case DRSUAPI_ATTRIBUTE_trustAuthOutgoing
:
86 case DRSUAPI_ATTRIBUTE_trustAuthIncoming
:
87 case DRSUAPI_ATTRIBUTE_initialAuthOutgoing
:
88 case DRSUAPI_ATTRIBUTE_initialAuthIncoming
:
94 out_data
= decrypt_drsuapi_blob(mem_ctx
, session_key
, rcrypt
,
97 if (out_data
.length
) {
98 return (DATA_BLOB
*)talloc_memdup(mem_ctx
, &out_data
, sizeof(DATA_BLOB
));
104 /****************************************************************
105 ****************************************************************/
107 static void parse_obj_identifier(struct drsuapi_DsReplicaObjectIdentifier
*id
,
116 if (id
->sid
.num_auths
> 0) {
117 *rid
= id
->sid
.sub_auths
[id
->sid
.num_auths
- 1];
121 /****************************************************************
122 ****************************************************************/
124 static void parse_obj_attribute(TALLOC_CTX
*mem_ctx
,
125 DATA_BLOB
*session_key
,
127 struct drsuapi_DsReplicaAttribute
*attr
)
131 for (i
=0; i
<attr
->value_ctr
.num_values
; i
++) {
133 DATA_BLOB
*plain_data
= NULL
;
135 plain_data
= decrypt_attr_val(mem_ctx
,
139 attr
->value_ctr
.values
[i
].blob
);
141 attr
->value_ctr
.values
[i
].blob
= plain_data
;
145 /****************************************************************
146 ****************************************************************/
148 static void libnet_dssync_decrypt_attributes(TALLOC_CTX
*mem_ctx
,
149 DATA_BLOB
*session_key
,
150 struct drsuapi_DsReplicaObjectListItemEx
*cur
)
152 for (; cur
; cur
= cur
->next_object
) {
157 parse_obj_identifier(cur
->object
.identifier
, &rid
);
159 for (i
=0; i
< cur
->object
.attribute_ctr
.num_attributes
; i
++) {
161 struct drsuapi_DsReplicaAttribute
*attr
;
163 attr
= &cur
->object
.attribute_ctr
.attributes
[i
];
165 if (attr
->value_ctr
.num_values
< 1) {
169 if (!attr
->value_ctr
.values
[0].blob
) {
173 parse_obj_attribute(mem_ctx
,
180 /****************************************************************
181 ****************************************************************/
183 static NTSTATUS
libnet_dssync_bind(TALLOC_CTX
*mem_ctx
,
184 struct dssync_context
*ctx
)
189 struct GUID bind_guid
;
190 struct drsuapi_DsBindInfoCtr bind_info
;
191 struct drsuapi_DsBindInfo28 info28
;
195 GUID_from_string(DRSUAPI_DS_BIND_GUID
, &bind_guid
);
197 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_BASE
;
198 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_ASYNC_REPLICATION
;
199 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_REMOVEAPI
;
200 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_MOVEREQ_V2
;
201 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GETCHG_COMPRESS
;
202 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V1
;
203 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_RESTORE_USN_OPTIMIZATION
;
204 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_KCC_EXECUTE
;
205 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRY_V2
;
206 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_LINKED_VALUE_REPLICATION
;
207 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V2
;
208 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_INSTANCE_TYPE_NOT_REQ_ON_MOD
;
209 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_CRYPTO_BIND
;
210 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GET_REPL_INFO
;
211 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_STRONG_ENCRYPTION
;
212 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V01
;
213 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_TRANSITIVE_MEMBERSHIP
;
214 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_ADD_SID_HISTORY
;
215 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_POST_BETA3
;
216 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GET_MEMBERSHIPS2
;
217 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V6
;
218 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_NONDOMAIN_NCS
;
219 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8
;
220 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V5
;
221 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V6
;
222 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRYREPLY_V3
;
223 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V7
;
224 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_VERIFY_OBJECT
;
225 info28
.site_guid
= GUID_zero();
227 info28
.repl_epoch
= 0;
229 bind_info
.length
= 28;
230 bind_info
.info
.info28
= info28
;
232 status
= rpccli_drsuapi_DsBind(ctx
->cli
, mem_ctx
,
238 if (!NT_STATUS_IS_OK(status
)) {
242 if (!W_ERROR_IS_OK(werr
)) {
243 return werror_to_ntstatus(werr
);
246 ZERO_STRUCT(ctx
->remote_info28
);
247 switch (bind_info
.length
) {
249 struct drsuapi_DsBindInfo24
*info24
;
250 info24
= &bind_info
.info
.info24
;
251 ctx
->remote_info28
.site_guid
= info24
->site_guid
;
252 ctx
->remote_info28
.supported_extensions
= info24
->supported_extensions
;
253 ctx
->remote_info28
.pid
= info24
->pid
;
254 ctx
->remote_info28
.repl_epoch
= 0;
258 ctx
->remote_info28
= bind_info
.info
.info28
;
261 struct drsuapi_DsBindInfo48
*info48
;
262 info48
= &bind_info
.info
.info48
;
263 ctx
->remote_info28
.site_guid
= info48
->site_guid
;
264 ctx
->remote_info28
.supported_extensions
= info48
->supported_extensions
;
265 ctx
->remote_info28
.pid
= info48
->pid
;
266 ctx
->remote_info28
.repl_epoch
= info48
->repl_epoch
;
270 DEBUG(1, ("Warning: invalid info length in bind info: %d\n",
278 /****************************************************************
279 ****************************************************************/
281 static NTSTATUS
libnet_dssync_lookup_nc(TALLOC_CTX
*mem_ctx
,
282 struct dssync_context
*ctx
)
287 union drsuapi_DsNameRequest req
;
289 struct drsuapi_DsNameString names
[1];
290 union drsuapi_DsNameCtr ctr
;
292 names
[0].str
= talloc_asprintf(mem_ctx
, "%s\\", ctx
->domain_name
);
293 NT_STATUS_HAVE_NO_MEMORY(names
[0].str
);
295 req
.req1
.codepage
= 1252; /* german */
296 req
.req1
.language
= 0x00000407; /* german */
298 req
.req1
.names
= names
;
299 req
.req1
.format_flags
= DRSUAPI_DS_NAME_FLAG_NO_FLAGS
;
300 req
.req1
.format_offered
= DRSUAPI_DS_NAME_FORMAT_UNKNOWN
;
301 req
.req1
.format_desired
= DRSUAPI_DS_NAME_FORMAT_FQDN_1779
;
303 status
= rpccli_drsuapi_DsCrackNames(ctx
->cli
, mem_ctx
,
310 if (!NT_STATUS_IS_OK(status
)) {
311 ctx
->error_message
= talloc_asprintf(ctx
,
312 "Failed to lookup DN for domain name: %s",
313 get_friendly_werror_msg(werr
));
317 if (!W_ERROR_IS_OK(werr
)) {
318 return werror_to_ntstatus(werr
);
321 if (ctr
.ctr1
->count
!= 1) {
322 return NT_STATUS_UNSUCCESSFUL
;
325 if (ctr
.ctr1
->array
[0].status
!= DRSUAPI_DS_NAME_STATUS_OK
) {
326 return NT_STATUS_UNSUCCESSFUL
;
329 ctx
->nc_dn
= talloc_strdup(mem_ctx
, ctr
.ctr1
->array
[0].result_name
);
330 NT_STATUS_HAVE_NO_MEMORY(ctx
->nc_dn
);
332 if (!ctx
->dns_domain_name
) {
333 ctx
->dns_domain_name
= talloc_strdup_upper(mem_ctx
,
334 ctr
.ctr1
->array
[0].dns_domain_name
);
335 NT_STATUS_HAVE_NO_MEMORY(ctx
->dns_domain_name
);
341 /****************************************************************
342 ****************************************************************/
344 static NTSTATUS
libnet_dssync_init(TALLOC_CTX
*mem_ctx
,
345 struct dssync_context
*ctx
)
349 status
= libnet_dssync_bind(mem_ctx
, ctx
);
350 if (!NT_STATUS_IS_OK(status
)) {
355 status
= libnet_dssync_lookup_nc(mem_ctx
, ctx
);
361 /****************************************************************
362 ****************************************************************/
364 static NTSTATUS
libnet_dssync_build_request(TALLOC_CTX
*mem_ctx
,
365 struct dssync_context
*ctx
,
367 struct replUpToDateVectorBlob
*utdv
,
369 union drsuapi_DsGetNCChangesRequest
*preq
)
374 union drsuapi_DsGetNCChangesRequest req
;
375 struct dom_sid null_sid
;
376 enum drsuapi_DsExtendedOperation extended_op
;
377 struct drsuapi_DsReplicaObjectIdentifier
*nc
= NULL
;
378 struct drsuapi_DsReplicaCursorCtrEx
*cursors
= NULL
;
380 uint32_t replica_flags
= DRSUAPI_DS_REPLICA_NEIGHBOUR_WRITEABLE
|
381 DRSUAPI_DS_REPLICA_NEIGHBOUR_SYNC_ON_STARTUP
|
382 DRSUAPI_DS_REPLICA_NEIGHBOUR_DO_SCHEDULED_SYNCS
|
383 DRSUAPI_DS_REPLICA_NEIGHBOUR_RETURN_OBJECT_PARENTS
|
384 DRSUAPI_DS_REPLICA_NEIGHBOUR_NEVER_SYNCED
;
386 ZERO_STRUCT(null_sid
);
389 if (ctx
->remote_info28
.supported_extensions
390 & DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8
)
397 nc
= TALLOC_ZERO_P(mem_ctx
, struct drsuapi_DsReplicaObjectIdentifier
);
399 status
= NT_STATUS_NO_MEMORY
;
403 nc
->guid
= GUID_zero();
406 if (!ctx
->single_object_replication
&&
407 !ctx
->force_full_replication
&& utdv
)
409 cursors
= TALLOC_ZERO_P(mem_ctx
,
410 struct drsuapi_DsReplicaCursorCtrEx
);
412 status
= NT_STATUS_NO_MEMORY
;
416 switch (utdv
->version
) {
418 cursors
->count
= utdv
->ctr
.ctr1
.count
;
419 cursors
->cursors
= utdv
->ctr
.ctr1
.cursors
;
422 cursors
->count
= utdv
->ctr
.ctr2
.count
;
423 cursors
->cursors
= talloc_array(cursors
,
424 struct drsuapi_DsReplicaCursor
,
426 if (!cursors
->cursors
) {
427 status
= NT_STATUS_NO_MEMORY
;
430 for (count
= 0; count
< cursors
->count
; count
++) {
431 cursors
->cursors
[count
].source_dsa_invocation_id
=
432 utdv
->ctr
.ctr2
.cursors
[count
].source_dsa_invocation_id
;
433 cursors
->cursors
[count
].highest_usn
=
434 utdv
->ctr
.ctr2
.cursors
[count
].highest_usn
;
440 if (ctx
->single_object_replication
) {
441 extended_op
= DRSUAPI_EXOP_REPL_OBJ
;
443 extended_op
= DRSUAPI_EXOP_NONE
;
447 req
.req8
.naming_context
= nc
;
448 req
.req8
.replica_flags
= replica_flags
;
449 req
.req8
.max_object_count
= 402;
450 req
.req8
.max_ndr_size
= 402116;
451 req
.req8
.uptodateness_vector
= cursors
;
452 req
.req8
.extended_op
= extended_op
;
453 } else if (level
== 5) {
454 req
.req5
.naming_context
= nc
;
455 req
.req5
.replica_flags
= replica_flags
;
456 req
.req5
.max_object_count
= 402;
457 req
.req5
.max_ndr_size
= 402116;
458 req
.req5
.uptodateness_vector
= cursors
;
459 req
.req5
.extended_op
= extended_op
;
461 status
= NT_STATUS_INVALID_PARAMETER
;
477 TALLOC_FREE(cursors
);
481 static NTSTATUS
libnet_dssync_getncchanges(TALLOC_CTX
*mem_ctx
,
482 struct dssync_context
*ctx
,
484 union drsuapi_DsGetNCChangesRequest
*req
,
485 struct replUpToDateVectorBlob
**pnew_utdv
)
489 union drsuapi_DsGetNCChangesCtr ctr
;
490 struct drsuapi_DsGetNCChangesCtr1
*ctr1
= NULL
;
491 struct drsuapi_DsGetNCChangesCtr6
*ctr6
= NULL
;
492 struct replUpToDateVectorBlob
*new_utdv
= NULL
;
493 int32_t level_out
= 0;
494 int32_t out_level
= 0;
498 if (!ctx
->single_object_replication
) {
499 new_utdv
= TALLOC_ZERO_P(mem_ctx
, struct replUpToDateVectorBlob
);
501 status
= NT_STATUS_NO_MEMORY
;
506 for (y
=0, last_query
= false; !last_query
; y
++) {
507 struct drsuapi_DsReplicaObjectListItemEx
*first_object
= NULL
;
508 struct drsuapi_DsReplicaOIDMapping_Ctr
*mapping_ctr
= NULL
;
511 DEBUG(1,("start[%d] tmp_higest_usn: %llu , highest_usn: %llu\n",y
,
512 (long long)req
->req8
.highwatermark
.tmp_highest_usn
,
513 (long long)req
->req8
.highwatermark
.highest_usn
));
514 } else if (level
== 5) {
515 DEBUG(1,("start[%d] tmp_higest_usn: %llu , highest_usn: %llu\n",y
,
516 (long long)req
->req5
.highwatermark
.tmp_highest_usn
,
517 (long long)req
->req5
.highwatermark
.highest_usn
));
520 status
= rpccli_drsuapi_DsGetNCChanges(ctx
->cli
, mem_ctx
,
527 if (!NT_STATUS_IS_OK(status
)) {
528 ctx
->error_message
= talloc_asprintf(ctx
,
529 "Failed to get NC Changes: %s",
530 get_friendly_werror_msg(werr
));
534 if (!W_ERROR_IS_OK(werr
)) {
535 status
= werror_to_ntstatus(werr
);
539 if (level_out
== 1) {
542 } else if (level_out
== 2 && ctr
.ctr2
.mszip1
.ts
) {
544 ctr1
= &ctr
.ctr2
.mszip1
.ts
->ctr1
;
545 } else if (level_out
== 6) {
548 } else if (level_out
== 7
549 && ctr
.ctr7
.level
== 6
550 && ctr
.ctr7
.type
== DRSUAPI_COMPRESSION_TYPE_MSZIP
551 && ctr
.ctr7
.ctr
.mszip6
.ts
) {
553 ctr6
= &ctr
.ctr7
.ctr
.mszip6
.ts
->ctr6
;
554 } else if (level_out
== 7
555 && ctr
.ctr7
.level
== 6
556 && ctr
.ctr7
.type
== DRSUAPI_COMPRESSION_TYPE_XPRESS
557 && ctr
.ctr7
.ctr
.xpress6
.ts
) {
559 ctr6
= &ctr
.ctr7
.ctr
.xpress6
.ts
->ctr6
;
562 if (out_level
== 1) {
563 DEBUG(1,("end[%d] tmp_highest_usn: %llu , highest_usn: %llu\n",y
,
564 (long long)ctr1
->new_highwatermark
.tmp_highest_usn
,
565 (long long)ctr1
->new_highwatermark
.highest_usn
));
567 first_object
= ctr1
->first_object
;
568 mapping_ctr
= &ctr1
->mapping_ctr
;
570 if (ctr1
->more_data
) {
571 req
->req5
.highwatermark
= ctr1
->new_highwatermark
;
574 if (ctr1
->uptodateness_vector
&&
575 !ctx
->single_object_replication
)
577 new_utdv
->version
= 1;
578 new_utdv
->ctr
.ctr1
.count
=
579 ctr1
->uptodateness_vector
->count
;
580 new_utdv
->ctr
.ctr1
.cursors
=
581 ctr1
->uptodateness_vector
->cursors
;
584 } else if (out_level
== 6) {
585 DEBUG(1,("end[%d] tmp_highest_usn: %llu , highest_usn: %llu\n",y
,
586 (long long)ctr6
->new_highwatermark
.tmp_highest_usn
,
587 (long long)ctr6
->new_highwatermark
.highest_usn
));
589 first_object
= ctr6
->first_object
;
590 mapping_ctr
= &ctr6
->mapping_ctr
;
592 if (ctr6
->more_data
) {
593 req
->req8
.highwatermark
= ctr6
->new_highwatermark
;
596 if (ctr6
->uptodateness_vector
&&
597 !ctx
->single_object_replication
)
599 new_utdv
->version
= 2;
600 new_utdv
->ctr
.ctr2
.count
=
601 ctr6
->uptodateness_vector
->count
;
602 new_utdv
->ctr
.ctr2
.cursors
=
603 ctr6
->uptodateness_vector
->cursors
;
608 status
= cli_get_session_key(mem_ctx
, ctx
->cli
, &ctx
->session_key
);
609 if (!NT_STATUS_IS_OK(status
)) {
610 ctx
->error_message
= talloc_asprintf(ctx
,
611 "Failed to get Session Key: %s",
616 libnet_dssync_decrypt_attributes(mem_ctx
,
620 if (ctx
->ops
->process_objects
) {
621 status
= ctx
->ops
->process_objects(ctx
, mem_ctx
,
624 if (!NT_STATUS_IS_OK(status
)) {
625 ctx
->error_message
= talloc_asprintf(ctx
,
626 "Failed to call processing function: %s",
633 *pnew_utdv
= new_utdv
;
639 static NTSTATUS
libnet_dssync_process(TALLOC_CTX
*mem_ctx
,
640 struct dssync_context
*ctx
)
645 union drsuapi_DsGetNCChangesRequest req
;
646 struct replUpToDateVectorBlob
*old_utdv
= NULL
;
647 struct replUpToDateVectorBlob
*pnew_utdv
= NULL
;
652 status
= ctx
->ops
->startup(ctx
, mem_ctx
, &old_utdv
);
653 if (!NT_STATUS_IS_OK(status
)) {
654 ctx
->error_message
= talloc_asprintf(ctx
,
655 "Failed to call startup operation: %s",
660 if (ctx
->single_object_replication
&& ctx
->object_dns
) {
661 dns
= ctx
->object_dns
;
662 dn_count
= ctx
->object_count
;
668 for (count
=0; count
< dn_count
; count
++) {
669 status
= libnet_dssync_build_request(mem_ctx
, ctx
,
673 if (!NT_STATUS_IS_OK(status
)) {
677 status
= libnet_dssync_getncchanges(mem_ctx
, ctx
, level
, &req
,
679 if (!NT_STATUS_IS_OK(status
)) {
680 ctx
->error_message
= talloc_asprintf(ctx
,
681 "Failed to call DsGetNCCHanges: %s",
687 status
= ctx
->ops
->finish(ctx
, mem_ctx
, pnew_utdv
);
688 if (!NT_STATUS_IS_OK(status
)) {
689 ctx
->error_message
= talloc_asprintf(ctx
,
690 "Failed to call finishing operation: %s",
699 /****************************************************************
700 ****************************************************************/
702 NTSTATUS
libnet_dssync(TALLOC_CTX
*mem_ctx
,
703 struct dssync_context
*ctx
)
708 tmp_ctx
= talloc_new(mem_ctx
);
710 return NT_STATUS_NO_MEMORY
;
713 status
= libnet_dssync_init(tmp_ctx
, ctx
);
714 if (!NT_STATUS_IS_OK(status
)) {
718 status
= libnet_dssync_process(tmp_ctx
, ctx
);
719 if (!NT_STATUS_IS_OK(status
)) {
724 TALLOC_FREE(tmp_ctx
);