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"
25 #include "../libcli/drsuapi/drsuapi.h"
27 /****************************************************************
28 ****************************************************************/
30 static int libnet_dssync_free_context(struct dssync_context
*ctx
)
36 if (is_valid_policy_hnd(&ctx
->bind_handle
) && ctx
->cli
) {
37 rpccli_drsuapi_DsUnbind(ctx
->cli
, ctx
, &ctx
->bind_handle
, NULL
);
43 /****************************************************************
44 ****************************************************************/
46 NTSTATUS
libnet_dssync_init_context(TALLOC_CTX
*mem_ctx
,
47 struct dssync_context
**ctx_p
)
49 struct dssync_context
*ctx
;
51 ctx
= TALLOC_ZERO_P(mem_ctx
, struct dssync_context
);
52 NT_STATUS_HAVE_NO_MEMORY(ctx
);
54 talloc_set_destructor(ctx
, libnet_dssync_free_context
);
55 ctx
->clean_old_entries
= false;
62 /****************************************************************
63 ****************************************************************/
65 static void parse_obj_identifier(struct drsuapi_DsReplicaObjectIdentifier
*id
,
74 if (id
->sid
.num_auths
> 0) {
75 *rid
= id
->sid
.sub_auths
[id
->sid
.num_auths
- 1];
79 /****************************************************************
80 ****************************************************************/
82 static void libnet_dssync_decrypt_attributes(TALLOC_CTX
*mem_ctx
,
83 DATA_BLOB
*session_key
,
84 struct drsuapi_DsReplicaObjectListItemEx
*cur
)
86 for (; cur
; cur
= cur
->next_object
) {
91 parse_obj_identifier(cur
->object
.identifier
, &rid
);
93 for (i
=0; i
< cur
->object
.attribute_ctr
.num_attributes
; i
++) {
95 struct drsuapi_DsReplicaAttribute
*attr
;
97 attr
= &cur
->object
.attribute_ctr
.attributes
[i
];
99 if (attr
->value_ctr
.num_values
< 1) {
103 if (!attr
->value_ctr
.values
[0].blob
) {
107 drsuapi_decrypt_attribute(mem_ctx
,
114 /****************************************************************
115 ****************************************************************/
117 static NTSTATUS
libnet_dssync_bind(TALLOC_CTX
*mem_ctx
,
118 struct dssync_context
*ctx
)
123 struct GUID bind_guid
;
124 struct drsuapi_DsBindInfoCtr bind_info
;
125 struct drsuapi_DsBindInfo28 info28
;
129 GUID_from_string(DRSUAPI_DS_BIND_GUID
, &bind_guid
);
131 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_BASE
;
132 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_ASYNC_REPLICATION
;
133 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_REMOVEAPI
;
134 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_MOVEREQ_V2
;
135 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GETCHG_COMPRESS
;
136 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V1
;
137 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_RESTORE_USN_OPTIMIZATION
;
138 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_KCC_EXECUTE
;
139 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRY_V2
;
140 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_LINKED_VALUE_REPLICATION
;
141 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V2
;
142 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_INSTANCE_TYPE_NOT_REQ_ON_MOD
;
143 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_CRYPTO_BIND
;
144 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GET_REPL_INFO
;
145 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_STRONG_ENCRYPTION
;
146 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V01
;
147 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_TRANSITIVE_MEMBERSHIP
;
148 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_ADD_SID_HISTORY
;
149 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_POST_BETA3
;
150 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GET_MEMBERSHIPS2
;
151 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V6
;
152 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_NONDOMAIN_NCS
;
153 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8
;
154 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V5
;
155 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V6
;
156 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRYREPLY_V3
;
157 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V7
;
158 info28
.supported_extensions
|= DRSUAPI_SUPPORTED_EXTENSION_VERIFY_OBJECT
;
159 info28
.site_guid
= GUID_zero();
161 info28
.repl_epoch
= 0;
163 bind_info
.length
= 28;
164 bind_info
.info
.info28
= info28
;
166 status
= rpccli_drsuapi_DsBind(ctx
->cli
, mem_ctx
,
172 if (!NT_STATUS_IS_OK(status
)) {
176 if (!W_ERROR_IS_OK(werr
)) {
177 return werror_to_ntstatus(werr
);
180 ZERO_STRUCT(ctx
->remote_info28
);
181 switch (bind_info
.length
) {
183 struct drsuapi_DsBindInfo24
*info24
;
184 info24
= &bind_info
.info
.info24
;
185 ctx
->remote_info28
.site_guid
= info24
->site_guid
;
186 ctx
->remote_info28
.supported_extensions
= info24
->supported_extensions
;
187 ctx
->remote_info28
.pid
= info24
->pid
;
188 ctx
->remote_info28
.repl_epoch
= 0;
192 ctx
->remote_info28
= bind_info
.info
.info28
;
195 struct drsuapi_DsBindInfo48
*info48
;
196 info48
= &bind_info
.info
.info48
;
197 ctx
->remote_info28
.site_guid
= info48
->site_guid
;
198 ctx
->remote_info28
.supported_extensions
= info48
->supported_extensions
;
199 ctx
->remote_info28
.pid
= info48
->pid
;
200 ctx
->remote_info28
.repl_epoch
= info48
->repl_epoch
;
204 DEBUG(1, ("Warning: invalid info length in bind info: %d\n",
212 /****************************************************************
213 ****************************************************************/
215 static NTSTATUS
libnet_dssync_lookup_nc(TALLOC_CTX
*mem_ctx
,
216 struct dssync_context
*ctx
)
221 union drsuapi_DsNameRequest req
;
223 struct drsuapi_DsNameString names
[1];
224 union drsuapi_DsNameCtr ctr
;
226 names
[0].str
= talloc_asprintf(mem_ctx
, "%s\\", ctx
->domain_name
);
227 NT_STATUS_HAVE_NO_MEMORY(names
[0].str
);
229 req
.req1
.codepage
= 1252; /* german */
230 req
.req1
.language
= 0x00000407; /* german */
232 req
.req1
.names
= names
;
233 req
.req1
.format_flags
= DRSUAPI_DS_NAME_FLAG_NO_FLAGS
;
234 req
.req1
.format_offered
= DRSUAPI_DS_NAME_FORMAT_UNKNOWN
;
235 req
.req1
.format_desired
= DRSUAPI_DS_NAME_FORMAT_FQDN_1779
;
237 status
= rpccli_drsuapi_DsCrackNames(ctx
->cli
, mem_ctx
,
244 if (!NT_STATUS_IS_OK(status
)) {
245 ctx
->error_message
= talloc_asprintf(ctx
,
246 "Failed to lookup DN for domain name: %s",
247 get_friendly_werror_msg(werr
));
251 if (!W_ERROR_IS_OK(werr
)) {
252 return werror_to_ntstatus(werr
);
255 if (ctr
.ctr1
->count
!= 1) {
256 return NT_STATUS_UNSUCCESSFUL
;
259 if (ctr
.ctr1
->array
[0].status
!= DRSUAPI_DS_NAME_STATUS_OK
) {
260 return NT_STATUS_UNSUCCESSFUL
;
263 ctx
->nc_dn
= talloc_strdup(mem_ctx
, ctr
.ctr1
->array
[0].result_name
);
264 NT_STATUS_HAVE_NO_MEMORY(ctx
->nc_dn
);
266 if (!ctx
->dns_domain_name
) {
267 ctx
->dns_domain_name
= talloc_strdup_upper(mem_ctx
,
268 ctr
.ctr1
->array
[0].dns_domain_name
);
269 NT_STATUS_HAVE_NO_MEMORY(ctx
->dns_domain_name
);
275 /****************************************************************
276 ****************************************************************/
278 static NTSTATUS
libnet_dssync_init(TALLOC_CTX
*mem_ctx
,
279 struct dssync_context
*ctx
)
283 status
= libnet_dssync_bind(mem_ctx
, ctx
);
284 if (!NT_STATUS_IS_OK(status
)) {
289 status
= libnet_dssync_lookup_nc(mem_ctx
, ctx
);
295 /****************************************************************
296 ****************************************************************/
298 static NTSTATUS
libnet_dssync_build_request(TALLOC_CTX
*mem_ctx
,
299 struct dssync_context
*ctx
,
301 struct replUpToDateVectorBlob
*utdv
,
303 union drsuapi_DsGetNCChangesRequest
*preq
)
308 union drsuapi_DsGetNCChangesRequest req
;
309 struct dom_sid null_sid
;
310 enum drsuapi_DsExtendedOperation extended_op
;
311 struct drsuapi_DsReplicaObjectIdentifier
*nc
= NULL
;
312 struct drsuapi_DsReplicaCursorCtrEx
*cursors
= NULL
;
314 uint32_t replica_flags
= DRSUAPI_DS_REPLICA_NEIGHBOUR_WRITEABLE
|
315 DRSUAPI_DS_REPLICA_NEIGHBOUR_SYNC_ON_STARTUP
|
316 DRSUAPI_DS_REPLICA_NEIGHBOUR_DO_SCHEDULED_SYNCS
|
317 DRSUAPI_DS_REPLICA_NEIGHBOUR_RETURN_OBJECT_PARENTS
|
318 DRSUAPI_DS_REPLICA_NEIGHBOUR_NEVER_SYNCED
;
320 ZERO_STRUCT(null_sid
);
323 if (ctx
->remote_info28
.supported_extensions
324 & DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8
)
331 nc
= TALLOC_ZERO_P(mem_ctx
, struct drsuapi_DsReplicaObjectIdentifier
);
333 status
= NT_STATUS_NO_MEMORY
;
337 nc
->guid
= GUID_zero();
340 if (!ctx
->single_object_replication
&&
341 !ctx
->force_full_replication
&& utdv
)
343 cursors
= TALLOC_ZERO_P(mem_ctx
,
344 struct drsuapi_DsReplicaCursorCtrEx
);
346 status
= NT_STATUS_NO_MEMORY
;
350 switch (utdv
->version
) {
352 cursors
->count
= utdv
->ctr
.ctr1
.count
;
353 cursors
->cursors
= utdv
->ctr
.ctr1
.cursors
;
356 cursors
->count
= utdv
->ctr
.ctr2
.count
;
357 cursors
->cursors
= talloc_array(cursors
,
358 struct drsuapi_DsReplicaCursor
,
360 if (!cursors
->cursors
) {
361 status
= NT_STATUS_NO_MEMORY
;
364 for (count
= 0; count
< cursors
->count
; count
++) {
365 cursors
->cursors
[count
].source_dsa_invocation_id
=
366 utdv
->ctr
.ctr2
.cursors
[count
].source_dsa_invocation_id
;
367 cursors
->cursors
[count
].highest_usn
=
368 utdv
->ctr
.ctr2
.cursors
[count
].highest_usn
;
374 if (ctx
->single_object_replication
) {
375 extended_op
= DRSUAPI_EXOP_REPL_OBJ
;
377 extended_op
= DRSUAPI_EXOP_NONE
;
381 req
.req8
.naming_context
= nc
;
382 req
.req8
.replica_flags
= replica_flags
;
383 req
.req8
.max_object_count
= 402;
384 req
.req8
.max_ndr_size
= 402116;
385 req
.req8
.uptodateness_vector
= cursors
;
386 req
.req8
.extended_op
= extended_op
;
387 } else if (level
== 5) {
388 req
.req5
.naming_context
= nc
;
389 req
.req5
.replica_flags
= replica_flags
;
390 req
.req5
.max_object_count
= 402;
391 req
.req5
.max_ndr_size
= 402116;
392 req
.req5
.uptodateness_vector
= cursors
;
393 req
.req5
.extended_op
= extended_op
;
395 status
= NT_STATUS_INVALID_PARAMETER
;
411 TALLOC_FREE(cursors
);
415 static NTSTATUS
libnet_dssync_getncchanges(TALLOC_CTX
*mem_ctx
,
416 struct dssync_context
*ctx
,
418 union drsuapi_DsGetNCChangesRequest
*req
,
419 struct replUpToDateVectorBlob
**pnew_utdv
)
423 union drsuapi_DsGetNCChangesCtr ctr
;
424 struct drsuapi_DsGetNCChangesCtr1
*ctr1
= NULL
;
425 struct drsuapi_DsGetNCChangesCtr6
*ctr6
= NULL
;
426 struct replUpToDateVectorBlob
*new_utdv
= NULL
;
427 int32_t level_out
= 0;
428 int32_t out_level
= 0;
432 if (!ctx
->single_object_replication
) {
433 new_utdv
= TALLOC_ZERO_P(mem_ctx
, struct replUpToDateVectorBlob
);
435 status
= NT_STATUS_NO_MEMORY
;
440 for (y
=0, last_query
= false; !last_query
; y
++) {
441 struct drsuapi_DsReplicaObjectListItemEx
*first_object
= NULL
;
442 struct drsuapi_DsReplicaOIDMapping_Ctr
*mapping_ctr
= NULL
;
445 DEBUG(1,("start[%d] tmp_higest_usn: %llu , highest_usn: %llu\n",y
,
446 (long long)req
->req8
.highwatermark
.tmp_highest_usn
,
447 (long long)req
->req8
.highwatermark
.highest_usn
));
448 } else if (level
== 5) {
449 DEBUG(1,("start[%d] tmp_higest_usn: %llu , highest_usn: %llu\n",y
,
450 (long long)req
->req5
.highwatermark
.tmp_highest_usn
,
451 (long long)req
->req5
.highwatermark
.highest_usn
));
454 status
= rpccli_drsuapi_DsGetNCChanges(ctx
->cli
, mem_ctx
,
461 if (!NT_STATUS_IS_OK(status
)) {
462 ctx
->error_message
= talloc_asprintf(ctx
,
463 "Failed to get NC Changes: %s",
464 get_friendly_werror_msg(werr
));
468 if (!W_ERROR_IS_OK(werr
)) {
469 status
= werror_to_ntstatus(werr
);
473 if (level_out
== 1) {
476 } else if (level_out
== 2 && ctr
.ctr2
.mszip1
.ts
) {
478 ctr1
= &ctr
.ctr2
.mszip1
.ts
->ctr1
;
479 } else if (level_out
== 6) {
482 } else if (level_out
== 7
483 && ctr
.ctr7
.level
== 6
484 && ctr
.ctr7
.type
== DRSUAPI_COMPRESSION_TYPE_MSZIP
485 && ctr
.ctr7
.ctr
.mszip6
.ts
) {
487 ctr6
= &ctr
.ctr7
.ctr
.mszip6
.ts
->ctr6
;
488 } else if (level_out
== 7
489 && ctr
.ctr7
.level
== 6
490 && ctr
.ctr7
.type
== DRSUAPI_COMPRESSION_TYPE_XPRESS
491 && ctr
.ctr7
.ctr
.xpress6
.ts
) {
493 ctr6
= &ctr
.ctr7
.ctr
.xpress6
.ts
->ctr6
;
496 if (out_level
== 1) {
497 DEBUG(1,("end[%d] tmp_highest_usn: %llu , highest_usn: %llu\n",y
,
498 (long long)ctr1
->new_highwatermark
.tmp_highest_usn
,
499 (long long)ctr1
->new_highwatermark
.highest_usn
));
501 first_object
= ctr1
->first_object
;
502 mapping_ctr
= &ctr1
->mapping_ctr
;
504 if (ctr1
->more_data
) {
505 req
->req5
.highwatermark
= ctr1
->new_highwatermark
;
508 if (ctr1
->uptodateness_vector
&&
509 !ctx
->single_object_replication
)
511 new_utdv
->version
= 1;
512 new_utdv
->ctr
.ctr1
.count
=
513 ctr1
->uptodateness_vector
->count
;
514 new_utdv
->ctr
.ctr1
.cursors
=
515 ctr1
->uptodateness_vector
->cursors
;
518 } else if (out_level
== 6) {
519 DEBUG(1,("end[%d] tmp_highest_usn: %llu , highest_usn: %llu\n",y
,
520 (long long)ctr6
->new_highwatermark
.tmp_highest_usn
,
521 (long long)ctr6
->new_highwatermark
.highest_usn
));
523 first_object
= ctr6
->first_object
;
524 mapping_ctr
= &ctr6
->mapping_ctr
;
526 if (ctr6
->more_data
) {
527 req
->req8
.highwatermark
= ctr6
->new_highwatermark
;
530 if (ctr6
->uptodateness_vector
&&
531 !ctx
->single_object_replication
)
533 new_utdv
->version
= 2;
534 new_utdv
->ctr
.ctr2
.count
=
535 ctr6
->uptodateness_vector
->count
;
536 new_utdv
->ctr
.ctr2
.cursors
=
537 ctr6
->uptodateness_vector
->cursors
;
542 status
= cli_get_session_key(mem_ctx
, ctx
->cli
, &ctx
->session_key
);
543 if (!NT_STATUS_IS_OK(status
)) {
544 ctx
->error_message
= talloc_asprintf(ctx
,
545 "Failed to get Session Key: %s",
550 libnet_dssync_decrypt_attributes(mem_ctx
,
554 if (ctx
->ops
->process_objects
) {
555 status
= ctx
->ops
->process_objects(ctx
, mem_ctx
,
558 if (!NT_STATUS_IS_OK(status
)) {
559 ctx
->error_message
= talloc_asprintf(ctx
,
560 "Failed to call processing function: %s",
567 *pnew_utdv
= new_utdv
;
573 static NTSTATUS
libnet_dssync_process(TALLOC_CTX
*mem_ctx
,
574 struct dssync_context
*ctx
)
579 union drsuapi_DsGetNCChangesRequest req
;
580 struct replUpToDateVectorBlob
*old_utdv
= NULL
;
581 struct replUpToDateVectorBlob
*pnew_utdv
= NULL
;
586 if (ctx
->ops
->startup
) {
587 status
= ctx
->ops
->startup(ctx
, mem_ctx
, &old_utdv
);
588 if (!NT_STATUS_IS_OK(status
)) {
589 ctx
->error_message
= talloc_asprintf(ctx
,
590 "Failed to call startup operation: %s",
596 if (ctx
->single_object_replication
&& ctx
->object_dns
) {
597 dns
= ctx
->object_dns
;
598 dn_count
= ctx
->object_count
;
604 status
= NT_STATUS_OK
;
606 for (count
=0; count
< dn_count
; count
++) {
607 status
= libnet_dssync_build_request(mem_ctx
, ctx
,
611 if (!NT_STATUS_IS_OK(status
)) {
615 status
= libnet_dssync_getncchanges(mem_ctx
, ctx
, level
, &req
,
617 if (!NT_STATUS_IS_OK(status
)) {
618 ctx
->error_message
= talloc_asprintf(ctx
,
619 "Failed to call DsGetNCCHanges: %s",
625 if (ctx
->ops
->finish
) {
626 status
= ctx
->ops
->finish(ctx
, mem_ctx
, pnew_utdv
);
627 if (!NT_STATUS_IS_OK(status
)) {
628 ctx
->error_message
= talloc_asprintf(ctx
,
629 "Failed to call finishing operation: %s",
639 /****************************************************************
640 ****************************************************************/
642 NTSTATUS
libnet_dssync(TALLOC_CTX
*mem_ctx
,
643 struct dssync_context
*ctx
)
648 tmp_ctx
= talloc_new(mem_ctx
);
650 return NT_STATUS_NO_MEMORY
;
653 status
= libnet_dssync_init(tmp_ctx
, ctx
);
654 if (!NT_STATUS_IS_OK(status
)) {
658 status
= libnet_dssync_process(tmp_ctx
, ctx
);
659 if (!NT_STATUS_IS_OK(status
)) {
664 TALLOC_FREE(tmp_ctx
);