2 Unix SMB/CIFS implementation.
4 Copyright (C) Stefan (metze) Metzmacher 2005
5 Copyright (C) Guenther Deschner 2008
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "libnet/libnet.h"
25 /****************************************************************
26 ****************************************************************/
28 static int libnet_dssync_free_context(struct dssync_context
*ctx
)
34 if (is_valid_policy_hnd(&ctx
->bind_handle
) && ctx
->cli
) {
35 rpccli_drsuapi_DsUnbind(ctx
->cli
, ctx
, &ctx
->bind_handle
, NULL
);
41 /****************************************************************
42 ****************************************************************/
44 NTSTATUS
libnet_dssync_init_context(TALLOC_CTX
*mem_ctx
,
45 struct dssync_context
**ctx_p
)
47 struct dssync_context
*ctx
;
49 ctx
= TALLOC_ZERO_P(mem_ctx
, struct dssync_context
);
50 NT_STATUS_HAVE_NO_MEMORY(ctx
);
52 talloc_set_destructor(ctx
, libnet_dssync_free_context
);
59 /****************************************************************
60 ****************************************************************/
62 static DATA_BLOB
*decrypt_attr_val(TALLOC_CTX
*mem_ctx
,
63 DATA_BLOB
*session_key
,
65 enum drsuapi_DsAttributeId id
,
71 ZERO_STRUCT(out_data
);
74 case DRSUAPI_ATTRIBUTE_dBCSPwd
:
75 case DRSUAPI_ATTRIBUTE_unicodePwd
:
76 case DRSUAPI_ATTRIBUTE_ntPwdHistory
:
77 case DRSUAPI_ATTRIBUTE_lmPwdHistory
:
80 case DRSUAPI_ATTRIBUTE_supplementalCredentials
:
81 case DRSUAPI_ATTRIBUTE_priorValue
:
82 case DRSUAPI_ATTRIBUTE_currentValue
:
83 case DRSUAPI_ATTRIBUTE_trustAuthOutgoing
:
84 case DRSUAPI_ATTRIBUTE_trustAuthIncoming
:
85 case DRSUAPI_ATTRIBUTE_initialAuthOutgoing
:
86 case DRSUAPI_ATTRIBUTE_initialAuthIncoming
:
92 out_data
= decrypt_drsuapi_blob(mem_ctx
, session_key
, rcrypt
,
95 if (out_data
.length
) {
96 return (DATA_BLOB
*)talloc_memdup(mem_ctx
, &out_data
, sizeof(DATA_BLOB
));
102 /****************************************************************
103 ****************************************************************/
105 static void parse_obj_identifier(struct drsuapi_DsReplicaObjectIdentifier
*id
,
114 if (id
->sid
.num_auths
> 0) {
115 *rid
= id
->sid
.sub_auths
[id
->sid
.num_auths
- 1];
119 /****************************************************************
120 ****************************************************************/
122 static void parse_obj_attribute(TALLOC_CTX
*mem_ctx
,
123 DATA_BLOB
*session_key
,
125 struct drsuapi_DsReplicaAttribute
*attr
)
129 for (i
=0; i
<attr
->value_ctr
.num_values
; i
++) {
131 DATA_BLOB
*plain_data
= NULL
;
133 plain_data
= decrypt_attr_val(mem_ctx
,
137 attr
->value_ctr
.values
[i
].blob
);
139 attr
->value_ctr
.values
[i
].blob
= plain_data
;
143 /****************************************************************
144 ****************************************************************/
146 static void libnet_dssync_decrypt_attributes(TALLOC_CTX
*mem_ctx
,
147 DATA_BLOB
*session_key
,
148 struct drsuapi_DsReplicaObjectListItemEx
*cur
)
150 for (; cur
; cur
= cur
->next_object
) {
155 parse_obj_identifier(cur
->object
.identifier
, &rid
);
157 for (i
=0; i
< cur
->object
.attribute_ctr
.num_attributes
; i
++) {
159 struct drsuapi_DsReplicaAttribute
*attr
;
161 attr
= &cur
->object
.attribute_ctr
.attributes
[i
];
163 if (attr
->value_ctr
.num_values
< 1) {
167 if (!attr
->value_ctr
.values
[0].blob
) {
171 parse_obj_attribute(mem_ctx
,
178 /****************************************************************
179 ****************************************************************/
181 static NTSTATUS
libnet_dssync_bind(TALLOC_CTX
*mem_ctx
,
182 struct dssync_context
*ctx
)
187 struct GUID bind_guid
;
188 struct drsuapi_DsBindInfoCtr bind_info
;
189 struct drsuapi_DsBindInfo28 info28
;
193 GUID_from_string(DRSUAPI_DS_BIND_GUID
, &bind_guid
);
195 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_BASE
;
196 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_ASYNC_REPLICATION
;
197 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_REMOVEAPI
;
198 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_MOVEREQ_V2
;
199 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GETCHG_COMPRESS
;
200 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V1
;
201 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_RESTORE_USN_OPTIMIZATION
;
202 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_KCC_EXECUTE
;
203 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRY_V2
;
204 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_LINKED_VALUE_REPLICATION
;
205 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V2
;
206 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_INSTANCE_TYPE_NOT_REQ_ON_MOD
;
207 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_CRYPTO_BIND
;
208 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GET_REPL_INFO
;
209 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_STRONG_ENCRYPTION
;
210 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V01
;
211 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_TRANSITIVE_MEMBERSHIP
;
212 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_ADD_SID_HISTORY
;
213 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_POST_BETA3
;
214 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GET_MEMBERSHIPS2
;
215 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V6
;
216 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_NONDOMAIN_NCS
;
217 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8
;
218 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V5
;
219 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V6
;
220 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRYREPLY_V3
;
221 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V7
;
222 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_VERIFY_OBJECT
;
223 info28
.site_guid
= GUID_zero();
225 info28
.repl_epoch
= 0;
227 bind_info
.length
= 28;
228 bind_info
.info
.info28
= info28
;
230 status
= rpccli_drsuapi_DsBind(ctx
->cli
, mem_ctx
,
236 if (!NT_STATUS_IS_OK(status
)) {
240 if (!W_ERROR_IS_OK(werr
)) {
241 return werror_to_ntstatus(werr
);
244 ZERO_STRUCT(ctx
->remote_info28
);
245 switch (bind_info
.length
) {
247 struct drsuapi_DsBindInfo24
*info24
;
248 info24
= &bind_info
.info
.info24
;
249 ctx
->remote_info28
.site_guid
= info24
->site_guid
;
250 ctx
->remote_info28
.supported_extensions
= info24
->supported_extensions
;
251 ctx
->remote_info28
.pid
= info24
->pid
;
252 ctx
->remote_info28
.repl_epoch
= 0;
256 ctx
->remote_info28
= bind_info
.info
.info28
;
259 struct drsuapi_DsBindInfo48
*info48
;
260 info48
= &bind_info
.info
.info48
;
261 ctx
->remote_info28
.site_guid
= info48
->site_guid
;
262 ctx
->remote_info28
.supported_extensions
= info48
->supported_extensions
;
263 ctx
->remote_info28
.pid
= info48
->pid
;
264 ctx
->remote_info28
.repl_epoch
= info48
->repl_epoch
;
268 DEBUG(1, ("Warning: invalid info length in bind info: %d\n",
276 /****************************************************************
277 ****************************************************************/
279 static NTSTATUS
libnet_dssync_lookup_nc(TALLOC_CTX
*mem_ctx
,
280 struct dssync_context
*ctx
)
285 union drsuapi_DsNameRequest req
;
287 struct drsuapi_DsNameString names
[1];
288 union drsuapi_DsNameCtr ctr
;
290 names
[0].str
= talloc_asprintf(mem_ctx
, "%s\\", ctx
->domain_name
);
291 NT_STATUS_HAVE_NO_MEMORY(names
[0].str
);
293 req
.req1
.codepage
= 1252; /* german */
294 req
.req1
.language
= 0x00000407; /* german */
296 req
.req1
.names
= names
;
297 req
.req1
.format_flags
= DRSUAPI_DS_NAME_FLAG_NO_FLAGS
;
298 req
.req1
.format_offered
= DRSUAPI_DS_NAME_FORMAT_UKNOWN
;
299 req
.req1
.format_desired
= DRSUAPI_DS_NAME_FORMAT_FQDN_1779
;
301 status
= rpccli_drsuapi_DsCrackNames(ctx
->cli
, mem_ctx
,
308 if (!NT_STATUS_IS_OK(status
)) {
309 ctx
->error_message
= talloc_asprintf(mem_ctx
,
310 "Failed to lookup DN for domain name: %s",
311 get_friendly_werror_msg(werr
));
315 if (!W_ERROR_IS_OK(werr
)) {
316 return werror_to_ntstatus(werr
);
319 if (ctr
.ctr1
->count
!= 1) {
320 return NT_STATUS_UNSUCCESSFUL
;
323 if (ctr
.ctr1
->array
[0].status
!= DRSUAPI_DS_NAME_STATUS_OK
) {
324 return NT_STATUS_UNSUCCESSFUL
;
327 ctx
->nc_dn
= talloc_strdup(mem_ctx
, ctr
.ctr1
->array
[0].result_name
);
328 NT_STATUS_HAVE_NO_MEMORY(ctx
->nc_dn
);
330 if (!ctx
->dns_domain_name
) {
331 ctx
->dns_domain_name
= talloc_strdup_upper(mem_ctx
,
332 ctr
.ctr1
->array
[0].dns_domain_name
);
333 NT_STATUS_HAVE_NO_MEMORY(ctx
->dns_domain_name
);
339 /****************************************************************
340 ****************************************************************/
342 static NTSTATUS
libnet_dssync_init(TALLOC_CTX
*mem_ctx
,
343 struct dssync_context
*ctx
)
347 status
= libnet_dssync_bind(mem_ctx
, ctx
);
348 if (!NT_STATUS_IS_OK(status
)) {
353 status
= libnet_dssync_lookup_nc(mem_ctx
, ctx
);
359 /****************************************************************
360 ****************************************************************/
362 static NTSTATUS
libnet_dssync_build_request(TALLOC_CTX
*mem_ctx
,
363 struct dssync_context
*ctx
,
365 struct replUpToDateVectorBlob
*utdv
,
367 union drsuapi_DsGetNCChangesRequest
*preq
)
371 union drsuapi_DsGetNCChangesRequest req
;
372 struct dom_sid null_sid
;
373 enum drsuapi_DsExtendedOperation extended_op
;
374 struct drsuapi_DsReplicaObjectIdentifier
*nc
= NULL
;
375 struct drsuapi_DsReplicaCursorCtrEx
*cursors
= NULL
;
377 uint32_t replica_flags
= DRSUAPI_DS_REPLICA_NEIGHBOUR_WRITEABLE
|
378 DRSUAPI_DS_REPLICA_NEIGHBOUR_SYNC_ON_STARTUP
|
379 DRSUAPI_DS_REPLICA_NEIGHBOUR_DO_SCHEDULED_SYNCS
|
380 DRSUAPI_DS_REPLICA_NEIGHBOUR_RETURN_OBJECT_PARENTS
|
381 DRSUAPI_DS_REPLICA_NEIGHBOUR_NEVER_SYNCED
;
383 ZERO_STRUCT(null_sid
);
386 nc
= TALLOC_ZERO_P(mem_ctx
, struct drsuapi_DsReplicaObjectIdentifier
);
388 status
= NT_STATUS_NO_MEMORY
;
392 nc
->guid
= GUID_zero();
395 if (!ctx
->repl_nodiff
&& utdv
) {
396 cursors
= TALLOC_ZERO_P(mem_ctx
,
397 struct drsuapi_DsReplicaCursorCtrEx
);
399 status
= NT_STATUS_NO_MEMORY
;
403 switch (utdv
->version
) {
405 cursors
->count
= utdv
->ctr
.ctr1
.count
;
406 cursors
->cursors
= utdv
->ctr
.ctr1
.cursors
;
409 cursors
->count
= utdv
->ctr
.ctr2
.count
;
410 cursors
->cursors
= talloc_array(cursors
,
411 struct drsuapi_DsReplicaCursor
,
413 if (!cursors
->cursors
) {
414 status
= NT_STATUS_NO_MEMORY
;
417 for (count
= 0; count
< cursors
->count
; count
++) {
418 cursors
->cursors
[count
].source_dsa_invocation_id
=
419 utdv
->ctr
.ctr2
.cursors
[count
].source_dsa_invocation_id
;
420 cursors
->cursors
[count
].highest_usn
=
421 utdv
->ctr
.ctr2
.cursors
[count
].highest_usn
;
428 extended_op
= DRSUAPI_EXOP_REPL_OBJ
;
430 extended_op
= DRSUAPI_EXOP_NONE
;
434 req
.req8
.naming_context
= nc
;
435 req
.req8
.replica_flags
= replica_flags
;
436 req
.req8
.max_object_count
= 402;
437 req
.req8
.max_ndr_size
= 402116;
438 req
.req8
.uptodateness_vector
= cursors
;
439 req
.req8
.extended_op
= extended_op
;
440 } else if (level
== 5) {
441 req
.req5
.naming_context
= nc
;
442 req
.req5
.replica_flags
= replica_flags
;
443 req
.req5
.max_object_count
= 402;
444 req
.req5
.max_ndr_size
= 402116;
445 req
.req5
.uptodateness_vector
= cursors
;
446 req
.req5
.extended_op
= extended_op
;
448 status
= NT_STATUS_INVALID_PARAMETER
;
460 TALLOC_FREE(cursors
);
464 static NTSTATUS
libnet_dssync_process(TALLOC_CTX
*mem_ctx
,
465 struct dssync_context
*ctx
)
471 int32_t level_out
= 0;
472 union drsuapi_DsGetNCChangesRequest req
;
473 union drsuapi_DsGetNCChangesCtr ctr
;
475 struct drsuapi_DsGetNCChangesCtr1
*ctr1
= NULL
;
476 struct drsuapi_DsGetNCChangesCtr6
*ctr6
= NULL
;
477 struct replUpToDateVectorBlob
*old_utdv
= NULL
;
478 struct replUpToDateVectorBlob new_utdv
;
479 struct replUpToDateVectorBlob
*pnew_utdv
= NULL
;
480 int32_t out_level
= 0;
484 status
= ctx
->ops
->startup(ctx
, mem_ctx
, &old_utdv
);
485 if (!NT_STATUS_IS_OK(status
)) {
486 ctx
->error_message
= talloc_asprintf(mem_ctx
,
487 "Failed to call startup operation: %s",
492 if (ctx
->remote_info28
.supported_extensions
493 & DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8
)
500 if (ctx
->single
&& ctx
->object_dn
) {
506 status
= libnet_dssync_build_request(mem_ctx
, ctx
, dn
, old_utdv
, level
,
508 if (!NT_STATUS_IS_OK(status
)) {
509 ctx
->error_message
= talloc_asprintf(mem_ctx
,
510 "Failed to build DsGetNCChanges request: %s",
516 pnew_utdv
= &new_utdv
;
521 bool last_query
= true;
524 DEBUG(1,("start[%d] tmp_higest_usn: %llu , highest_usn: %llu\n",y
,
525 (long long)req
.req8
.highwatermark
.tmp_highest_usn
,
526 (long long)req
.req8
.highwatermark
.highest_usn
));
527 } else if (level
== 5) {
528 DEBUG(1,("start[%d] tmp_higest_usn: %llu , highest_usn: %llu\n",y
,
529 (long long)req
.req5
.highwatermark
.tmp_highest_usn
,
530 (long long)req
.req5
.highwatermark
.highest_usn
));
533 status
= rpccli_drsuapi_DsGetNCChanges(ctx
->cli
, mem_ctx
,
540 if (!NT_STATUS_IS_OK(status
)) {
541 ctx
->error_message
= talloc_asprintf(mem_ctx
,
542 "Failed to get NC Changes: %s",
543 get_friendly_werror_msg(werr
));
547 if (!W_ERROR_IS_OK(werr
)) {
548 status
= werror_to_ntstatus(werr
);
552 if (level_out
== 1) {
555 } else if (level_out
== 2) {
557 ctr1
= ctr
.ctr2
.ctr
.mszip1
.ctr1
;
560 status
= cli_get_session_key(mem_ctx
, ctx
->cli
, &ctx
->session_key
);
561 if (!NT_STATUS_IS_OK(status
)) {
562 ctx
->error_message
= talloc_asprintf(mem_ctx
,
563 "Failed to get Session Key: %s",
568 if (out_level
== 1) {
569 DEBUG(1,("end[%d] tmp_highest_usn: %llu , highest_usn: %llu\n",y
,
570 (long long)ctr1
->new_highwatermark
.tmp_highest_usn
,
571 (long long)ctr1
->new_highwatermark
.highest_usn
));
573 libnet_dssync_decrypt_attributes(mem_ctx
,
577 if (ctr1
->more_data
) {
578 req
.req5
.highwatermark
= ctr1
->new_highwatermark
;
582 if (ctx
->ops
->process_objects
) {
583 status
= ctx
->ops
->process_objects(ctx
, mem_ctx
,
586 if (!NT_STATUS_IS_OK(status
)) {
587 ctx
->error_message
= talloc_asprintf(mem_ctx
,
588 "Failed to call processing function: %s",
598 ZERO_STRUCT(new_utdv
);
599 new_utdv
.version
= 1;
600 if (ctr1
->uptodateness_vector
) {
601 new_utdv
.ctr
.ctr1
.count
= ctr1
->uptodateness_vector
->count
;
602 new_utdv
.ctr
.ctr1
.cursors
= ctr1
->uptodateness_vector
->cursors
;
606 if (level_out
== 6) {
609 } else if (level_out
== 7
610 && ctr
.ctr7
.level
== 6
611 && ctr
.ctr7
.type
== DRSUAPI_COMPRESSION_TYPE_MSZIP
) {
613 ctr6
= ctr
.ctr7
.ctr
.mszip6
.ctr6
;
616 if (out_level
== 6) {
617 DEBUG(1,("end[%d] tmp_highest_usn: %llu , highest_usn: %llu\n",y
,
618 (long long)ctr6
->new_highwatermark
.tmp_highest_usn
,
619 (long long)ctr6
->new_highwatermark
.highest_usn
));
621 libnet_dssync_decrypt_attributes(mem_ctx
,
625 if (ctr6
->more_data
) {
626 req
.req8
.highwatermark
= ctr6
->new_highwatermark
;
630 if (ctx
->ops
->process_objects
) {
631 status
= ctx
->ops
->process_objects(ctx
, mem_ctx
,
634 if (!NT_STATUS_IS_OK(status
)) {
635 ctx
->error_message
= talloc_asprintf(mem_ctx
,
636 "Failed to call processing function: %s",
646 ZERO_STRUCT(new_utdv
);
647 new_utdv
.version
= 2;
648 if (ctr6
->uptodateness_vector
) {
649 new_utdv
.ctr
.ctr2
.count
= ctr6
->uptodateness_vector
->count
;
650 new_utdv
.ctr
.ctr2
.cursors
= ctr6
->uptodateness_vector
->cursors
;
654 status
= ctx
->ops
->finish(ctx
, mem_ctx
, pnew_utdv
);
655 if (!NT_STATUS_IS_OK(status
)) {
656 ctx
->error_message
= talloc_asprintf(mem_ctx
,
657 "Failed to call finishing operation: %s",
669 /****************************************************************
670 ****************************************************************/
672 NTSTATUS
libnet_dssync(TALLOC_CTX
*mem_ctx
,
673 struct dssync_context
*ctx
)
677 status
= libnet_dssync_init(mem_ctx
, ctx
);
678 if (!NT_STATUS_IS_OK(status
)) {
682 status
= libnet_dssync_process(mem_ctx
, ctx
);
683 if (!NT_STATUS_IS_OK(status
)) {