2 Unix SMB/CIFS Implementation.
3 DSDB replication service
5 Copyright (C) Stefan Metzmacher 2007
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 "dsdb/samdb/samdb.h"
24 #include "auth/auth.h"
25 #include "samba/service.h"
26 #include "lib/events/events.h"
27 #include "dsdb/repl/drepl_service.h"
28 #include <ldb_errors.h>
29 #include "../lib/util/dlinklist.h"
30 #include "librpc/gen_ndr/ndr_misc.h"
31 #include "librpc/gen_ndr/ndr_drsuapi.h"
32 #include "librpc/gen_ndr/ndr_drsblobs.h"
33 #include "libcli/security/security.h"
34 #include "param/param.h"
35 #include "dsdb/common/util.h"
38 #define DBGC_CLASS DBGC_DRS_REPL
43 load the partitions list based on replicated NC attributes in our
46 WERROR
dreplsrv_load_partitions(struct dreplsrv_service
*s
)
49 static const char *attrs
[] = { "hasMasterNCs", "msDS-hasMasterNCs", "hasPartialReplicaNCs", "msDS-HasFullReplicaNCs", NULL
};
53 struct ldb_result
*res
;
54 struct ldb_message_element
*el
;
55 struct ldb_dn
*ntds_dn
;
57 tmp_ctx
= talloc_new(s
);
58 W_ERROR_HAVE_NO_MEMORY(tmp_ctx
);
60 ntds_dn
= samdb_ntds_settings_dn(s
->samdb
, tmp_ctx
);
62 DEBUG(1,(__location__
": Unable to find ntds_dn: %s\n", ldb_errstring(s
->samdb
)));
64 return WERR_DS_DRA_INTERNAL_ERROR
;
67 ret
= dsdb_search_dn(s
->samdb
, tmp_ctx
, &res
, ntds_dn
, attrs
, DSDB_SEARCH_SHOW_EXTENDED_DN
);
68 if (ret
!= LDB_SUCCESS
) {
69 DEBUG(1,("Searching for hasMasterNCs in NTDS DN failed: %s\n", ldb_errstring(s
->samdb
)));
71 return WERR_DS_DRA_INTERNAL_ERROR
;
74 for (a
=0; attrs
[a
]; a
++) {
77 el
= ldb_msg_find_element(res
->msgs
[0], attrs
[a
]);
81 for (i
=0; i
<el
->num_values
; i
++) {
83 struct dreplsrv_partition
*p
, *tp
;
86 pdn
= ldb_dn_from_ldb_val(tmp_ctx
, s
->samdb
, &el
->values
[i
]);
89 return WERR_DS_DRA_INTERNAL_ERROR
;
91 if (!ldb_dn_validate(pdn
)) {
92 return WERR_DS_DRA_INTERNAL_ERROR
;
95 p
= talloc_zero(s
, struct dreplsrv_partition
);
96 W_ERROR_HAVE_NO_MEMORY(p
);
98 p
->dn
= talloc_steal(p
, pdn
);
101 if (strcasecmp(attrs
[a
], "hasPartialReplicaNCs") == 0) {
102 p
->partial_replica
= true;
103 } else if (strcasecmp(attrs
[a
], "msDS-HasFullReplicaNCs") == 0) {
104 p
->rodc_replica
= true;
107 /* Do not add partitions more than once */
109 for (tp
= s
->partitions
; tp
; tp
= tp
->next
) {
110 if (ldb_dn_compare(tp
->dn
, p
->dn
) == 0) {
120 DLIST_ADD(s
->partitions
, p
);
121 DEBUG(2, ("dreplsrv_partition[%s] loaded\n", ldb_dn_get_linearized(p
->dn
)));
125 talloc_free(tmp_ctx
);
127 status
= dreplsrv_refresh_partitions(s
);
128 W_ERROR_NOT_OK_RETURN(status
);
134 Check if particular SPN exists for an account
136 static bool dreplsrv_spn_exists(struct ldb_context
*samdb
, struct ldb_dn
*account_dn
,
137 const char *principal_name
)
140 const char *attrs_empty
[] = { NULL
};
142 struct ldb_result
*res
;
144 tmp_ctx
= talloc_new(samdb
);
146 ret
= dsdb_search(samdb
, tmp_ctx
, &res
, account_dn
, LDB_SCOPE_BASE
, attrs_empty
,
147 0, "servicePrincipalName=%s",
148 ldb_binary_encode_string(tmp_ctx
, principal_name
));
149 if (ret
!= LDB_SUCCESS
|| res
->count
!= 1) {
150 talloc_free(tmp_ctx
);
154 talloc_free(tmp_ctx
);
159 work out the principal to use for DRS replication connections
161 static NTSTATUS
dreplsrv_get_target_principal(struct dreplsrv_service
*s
,
163 const struct repsFromTo1
*rft
,
164 char **target_principal
)
167 struct ldb_result
*res
;
168 const char *attrs_server
[] = { "dNSHostName", "serverReference", NULL
};
169 const char *attrs_ntds
[] = { "msDS-HasDomainNCs", "hasMasterNCs", NULL
};
171 const char *hostname
, *dnsdomain
=NULL
;
172 struct ldb_dn
*ntds_dn
, *server_dn
, *computer_dn
;
173 struct ldb_dn
*forest_dn
, *nc_dn
;
175 *target_principal
= NULL
;
177 tmp_ctx
= talloc_new(mem_ctx
);
179 /* we need to find their hostname */
180 ret
= dsdb_find_dn_by_guid(s
->samdb
, tmp_ctx
, &rft
->source_dsa_obj_guid
, 0, &ntds_dn
);
181 if (ret
!= LDB_SUCCESS
) {
182 talloc_free(tmp_ctx
);
183 /* its OK for their NTDSDSA DN not to be in our database */
187 server_dn
= ldb_dn_copy(tmp_ctx
, ntds_dn
);
188 if (server_dn
== NULL
) {
189 talloc_free(tmp_ctx
);
193 /* strip off the NTDS Settings */
194 if (!ldb_dn_remove_child_components(server_dn
, 1)) {
195 talloc_free(tmp_ctx
);
199 ret
= dsdb_search_dn(s
->samdb
, tmp_ctx
, &res
, server_dn
, attrs_server
, 0);
200 if (ret
!= LDB_SUCCESS
) {
201 talloc_free(tmp_ctx
);
202 /* its OK for their server DN not to be in our database */
206 forest_dn
= ldb_get_root_basedn(s
->samdb
);
207 if (forest_dn
== NULL
) {
208 talloc_free(tmp_ctx
);
212 hostname
= ldb_msg_find_attr_as_string(res
->msgs
[0], "dNSHostName", NULL
);
213 computer_dn
= ldb_msg_find_attr_as_dn(s
->samdb
, tmp_ctx
, res
->msgs
[0], "serverReference");
214 if (hostname
!= NULL
&& computer_dn
!= NULL
) {
215 char *local_principal
;
218 if we have the dNSHostName attribute then we can use
219 the GC/hostname/realm SPN. All DCs should have this SPN
221 Windows DC may set up it's dNSHostName before setting up
222 GC/xx/xx SPN. So make sure it exists, before using it.
224 local_principal
= talloc_asprintf(mem_ctx
, "GC/%s/%s",
226 samdb_dn_to_dns_domain(tmp_ctx
, forest_dn
));
227 if (dreplsrv_spn_exists(s
->samdb
, computer_dn
, local_principal
)) {
228 *target_principal
= local_principal
;
229 talloc_free(tmp_ctx
);
233 talloc_free(local_principal
);
237 if we can't find the dNSHostName then we will try for the
238 E3514235-4B06-11D1-AB04-00C04FC2DCD2/${NTDSGUID}/${DNSDOMAIN}
239 SPN. To use that we need the DNS domain name of the target
240 DC. We find that by first looking for the msDS-HasDomainNCs
241 in the NTDSDSA object of the DC, and if we don't find that,
242 then we look for the hasMasterNCs attribute, and eliminate
243 the known schema and configuruation DNs. Despite how
244 bizarre this seems, Hongwei tells us that this is in fact
245 what windows does to find the SPN!!
247 ret
= dsdb_search_dn(s
->samdb
, tmp_ctx
, &res
, ntds_dn
, attrs_ntds
, 0);
248 if (ret
!= LDB_SUCCESS
) {
249 talloc_free(tmp_ctx
);
253 nc_dn
= ldb_msg_find_attr_as_dn(s
->samdb
, tmp_ctx
, res
->msgs
[0], "msDS-HasDomainNCs");
255 dnsdomain
= samdb_dn_to_dns_domain(tmp_ctx
, nc_dn
);
258 if (dnsdomain
== NULL
) {
259 struct ldb_message_element
*el
;
261 el
= ldb_msg_find_element(res
->msgs
[0], "hasMasterNCs");
262 for (i
=0; el
&& i
<el
->num_values
; i
++) {
263 nc_dn
= ldb_dn_from_ldb_val(tmp_ctx
, s
->samdb
, &el
->values
[i
]);
265 ldb_dn_compare(ldb_get_config_basedn(s
->samdb
), nc_dn
) == 0 ||
266 ldb_dn_compare(ldb_get_schema_basedn(s
->samdb
), nc_dn
) == 0) {
269 /* it must be a domain DN, get the equivalent
271 dnsdomain
= samdb_dn_to_dns_domain(tmp_ctx
, nc_dn
);
276 if (dnsdomain
!= NULL
) {
277 *target_principal
= talloc_asprintf(mem_ctx
,
278 "E3514235-4B06-11D1-AB04-00C04FC2DCD2/%s/%s@%s",
279 GUID_string(tmp_ctx
, &rft
->source_dsa_obj_guid
),
280 dnsdomain
, dnsdomain
);
283 talloc_free(tmp_ctx
);
288 WERROR
dreplsrv_out_connection_attach(struct dreplsrv_service
*s
,
289 const struct repsFromTo1
*rft
,
290 struct dreplsrv_out_connection
**_conn
)
292 struct dreplsrv_out_connection
*cur
, *conn
= NULL
;
293 const char *hostname
;
295 if (!rft
->other_info
) {
299 if (!rft
->other_info
->dns_name
) {
303 hostname
= rft
->other_info
->dns_name
;
305 for (cur
= s
->connections
; cur
; cur
= cur
->next
) {
308 host
= dcerpc_binding_get_string_option(cur
->binding
, "host");
313 if (strcmp(host
, hostname
) == 0) {
322 char *target_principal
= NULL
;
324 conn
= talloc_zero(s
, struct dreplsrv_out_connection
);
325 W_ERROR_HAVE_NO_MEMORY(conn
);
329 binding_str
= talloc_asprintf(conn
, "ncacn_ip_tcp:%s[krb5,seal]",
331 W_ERROR_HAVE_NO_MEMORY(binding_str
);
332 nt_status
= dcerpc_parse_binding(conn
, binding_str
, &conn
->binding
);
333 talloc_free(binding_str
);
334 if (!NT_STATUS_IS_OK(nt_status
)) {
335 return ntstatus_to_werror(nt_status
);
338 /* use the GC principal for DRS replication */
339 nt_status
= dreplsrv_get_target_principal(s
, conn
->binding
,
340 rft
, &target_principal
);
341 if (!NT_STATUS_IS_OK(nt_status
)) {
342 return ntstatus_to_werror(nt_status
);
345 nt_status
= dcerpc_binding_set_string_option(conn
->binding
,
348 TALLOC_FREE(target_principal
);
349 if (!NT_STATUS_IS_OK(nt_status
)) {
350 return ntstatus_to_werror(nt_status
);
353 DLIST_ADD_END(s
->connections
, conn
);
355 DEBUG(4,("dreplsrv_out_connection_attach(%s): create\n", hostname
));
357 DEBUG(4,("dreplsrv_out_connection_attach(%s): attach\n", hostname
));
365 find an existing source dsa in a list
367 static struct dreplsrv_partition_source_dsa
*dreplsrv_find_source_dsa(struct dreplsrv_partition_source_dsa
*list
,
370 struct dreplsrv_partition_source_dsa
*s
;
371 for (s
=list
; s
; s
=s
->next
) {
372 if (GUID_equal(&s
->repsFrom1
->source_dsa_obj_guid
, guid
)) {
381 static WERROR
dreplsrv_partition_add_source_dsa(struct dreplsrv_service
*s
,
382 struct dreplsrv_partition
*p
,
383 struct dreplsrv_partition_source_dsa
**listp
,
384 struct dreplsrv_partition_source_dsa
*check_list
,
385 const struct ldb_val
*val
)
388 enum ndr_err_code ndr_err
;
389 struct dreplsrv_partition_source_dsa
*source
, *s2
;
391 source
= talloc_zero(p
, struct dreplsrv_partition_source_dsa
);
392 W_ERROR_HAVE_NO_MEMORY(source
);
394 ndr_err
= ndr_pull_struct_blob(val
, source
,
395 &source
->_repsFromBlob
,
396 (ndr_pull_flags_fn_t
)ndr_pull_repsFromToBlob
);
397 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
398 NTSTATUS nt_status
= ndr_map_error2ntstatus(ndr_err
);
400 return ntstatus_to_werror(nt_status
);
402 /* NDR_PRINT_DEBUG(repsFromToBlob, &source->_repsFromBlob); */
403 if (source
->_repsFromBlob
.version
!= 1) {
405 return WERR_DS_DRA_INTERNAL_ERROR
;
408 source
->partition
= p
;
409 source
->repsFrom1
= &source
->_repsFromBlob
.ctr
.ctr1
;
411 status
= dreplsrv_out_connection_attach(s
, source
->repsFrom1
, &source
->conn
);
412 W_ERROR_NOT_OK_RETURN(status
);
415 dreplsrv_find_source_dsa(check_list
, &source
->repsFrom1
->source_dsa_obj_guid
)) {
416 /* its in the check list, don't add it again */
421 /* re-use an existing source if found */
422 for (s2
=*listp
; s2
; s2
=s2
->next
) {
423 if (GUID_equal(&s2
->repsFrom1
->source_dsa_obj_guid
,
424 &source
->repsFrom1
->source_dsa_obj_guid
)) {
425 talloc_free(s2
->repsFrom1
->other_info
);
426 *s2
->repsFrom1
= *source
->repsFrom1
;
427 talloc_steal(s2
, s2
->repsFrom1
->other_info
);
433 DLIST_ADD_END(*listp
, source
);
438 * Find a partition when given a NC
439 * If the NC can't be found it will return BAD_NC
440 * Initial checks for invalid parameters have to be done beforehand
442 WERROR
dreplsrv_partition_find_for_nc(struct dreplsrv_service
*s
,
443 struct GUID
*nc_guid
,
444 struct dom_sid
*nc_sid
,
445 const char *nc_dn_str
,
446 struct dreplsrv_partition
**_p
)
448 struct dreplsrv_partition
*p
;
449 bool valid_sid
, valid_guid
;
453 valid_sid
= nc_sid
&& !is_null_sid(nc_sid
);
454 valid_guid
= nc_guid
&& !GUID_all_zero(nc_guid
);
456 if (!valid_sid
&& !valid_guid
&& (!nc_dn_str
)) {
457 return WERR_DS_DRA_BAD_NC
;
460 for (p
= s
->partitions
; p
; p
= p
->next
) {
461 if ((valid_guid
&& GUID_equal(&p
->nc
.guid
, nc_guid
))
462 || strequal(p
->nc
.dn
, nc_dn_str
)
463 || (valid_sid
&& dom_sid_equal(&p
->nc
.sid
, nc_sid
)))
465 /* fill in he right guid and sid if possible */
466 if (nc_guid
&& !valid_guid
) {
467 dsdb_get_extended_dn_guid(p
->dn
, nc_guid
, "GUID");
469 if (nc_sid
&& !valid_sid
) {
470 dsdb_get_extended_dn_sid(p
->dn
, nc_sid
, "SID");
477 return WERR_DS_DRA_BAD_NC
;
480 WERROR
dreplsrv_partition_source_dsa_by_guid(struct dreplsrv_partition
*p
,
481 const struct GUID
*dsa_guid
,
482 struct dreplsrv_partition_source_dsa
**_dsa
)
484 struct dreplsrv_partition_source_dsa
*dsa
;
486 SMB_ASSERT(dsa_guid
!= NULL
);
487 SMB_ASSERT(!GUID_all_zero(dsa_guid
));
490 for (dsa
= p
->sources
; dsa
; dsa
= dsa
->next
) {
491 if (GUID_equal(dsa_guid
, &dsa
->repsFrom1
->source_dsa_obj_guid
)) {
497 return WERR_DS_DRA_NO_REPLICA
;
500 WERROR
dreplsrv_partition_source_dsa_by_dns(const struct dreplsrv_partition
*p
,
502 struct dreplsrv_partition_source_dsa
**_dsa
)
504 struct dreplsrv_partition_source_dsa
*dsa
;
506 SMB_ASSERT(dsa_dns
!= NULL
);
509 for (dsa
= p
->sources
; dsa
; dsa
= dsa
->next
) {
510 if (strequal(dsa_dns
, dsa
->repsFrom1
->other_info
->dns_name
)) {
516 return WERR_DS_DRA_NO_REPLICA
;
521 create a temporary dsa structure for a replication. This is needed
522 for the initial replication of a new partition, such as when a new
523 domain NC is created and we are a global catalog server
525 WERROR
dreplsrv_partition_source_dsa_temporary(struct dreplsrv_partition
*p
,
527 const struct GUID
*dsa_guid
,
528 struct dreplsrv_partition_source_dsa
**_dsa
)
530 struct dreplsrv_partition_source_dsa
*dsa
;
533 dsa
= talloc_zero(mem_ctx
, struct dreplsrv_partition_source_dsa
);
534 W_ERROR_HAVE_NO_MEMORY(dsa
);
537 dsa
->repsFrom1
= &dsa
->_repsFromBlob
.ctr
.ctr1
;
538 dsa
->repsFrom1
->replica_flags
= 0;
539 dsa
->repsFrom1
->source_dsa_obj_guid
= *dsa_guid
;
541 dsa
->repsFrom1
->other_info
= talloc_zero(dsa
, struct repsFromTo1OtherInfo
);
542 W_ERROR_HAVE_NO_MEMORY(dsa
->repsFrom1
->other_info
);
544 dsa
->repsFrom1
->other_info
->dns_name
= samdb_ntds_msdcs_dns_name(p
->service
->samdb
,
545 dsa
->repsFrom1
->other_info
, dsa_guid
);
546 W_ERROR_HAVE_NO_MEMORY(dsa
->repsFrom1
->other_info
->dns_name
);
548 werr
= dreplsrv_out_connection_attach(p
->service
, dsa
->repsFrom1
, &dsa
->conn
);
549 if (!W_ERROR_IS_OK(werr
)) {
550 DEBUG(0,(__location__
": Failed to attach connection to %s\n",
551 ldb_dn_get_linearized(p
->dn
)));
562 static WERROR
dreplsrv_refresh_partition(struct dreplsrv_service
*s
,
563 struct dreplsrv_partition
*p
)
567 struct ldb_message_element
*orf_el
= NULL
;
568 struct ldb_result
*r
= NULL
;
571 TALLOC_CTX
*mem_ctx
= talloc_new(p
);
572 static const char *attrs
[] = {
579 DEBUG(4, ("dreplsrv_refresh_partition(%s)\n",
580 ldb_dn_get_linearized(p
->dn
)));
582 ret
= dsdb_search_dn(s
->samdb
, mem_ctx
, &r
, p
->dn
, attrs
, DSDB_SEARCH_SHOW_EXTENDED_DN
);
583 if (ret
== LDB_ERR_NO_SUCH_OBJECT
) {
584 /* we haven't replicated the partition yet, but we
585 * can fill in the guid, sid etc from the partition DN */
587 } else if (ret
!= LDB_SUCCESS
) {
588 talloc_free(mem_ctx
);
594 talloc_free(discard_const(p
->nc
.dn
));
596 p
->nc
.dn
= ldb_dn_alloc_linearized(p
, dn
);
597 W_ERROR_HAVE_NO_MEMORY(p
->nc
.dn
);
598 ntstatus
= dsdb_get_extended_dn_guid(dn
, &p
->nc
.guid
, "GUID");
599 if (!NT_STATUS_IS_OK(ntstatus
)) {
600 DEBUG(0,(__location__
": unable to get GUID for %s: %s\n",
601 p
->nc
.dn
, nt_errstr(ntstatus
)));
602 talloc_free(mem_ctx
);
603 return WERR_DS_DRA_INTERNAL_ERROR
;
605 dsdb_get_extended_dn_sid(dn
, &p
->nc
.sid
, "SID");
607 talloc_free(p
->uptodatevector
.cursors
);
608 talloc_free(p
->uptodatevector_ex
.cursors
);
609 ZERO_STRUCT(p
->uptodatevector
);
610 ZERO_STRUCT(p
->uptodatevector_ex
);
612 ret
= dsdb_load_udv_v2(s
->samdb
, p
->dn
, p
, &p
->uptodatevector
.cursors
, &p
->uptodatevector
.count
);
613 if (ret
!= LDB_SUCCESS
) {
614 DEBUG(4,(__location__
": no UDV available for %s\n", ldb_dn_get_linearized(p
->dn
)));
619 if (r
!= NULL
&& (orf_el
= ldb_msg_find_element(r
->msgs
[0], "repsFrom"))) {
620 for (i
=0; i
< orf_el
->num_values
; i
++) {
621 status
= dreplsrv_partition_add_source_dsa(s
, p
, &p
->sources
,
622 NULL
, &orf_el
->values
[i
]);
623 W_ERROR_NOT_OK_GOTO_DONE(status
);
627 if (r
!= NULL
&& (orf_el
= ldb_msg_find_element(r
->msgs
[0], "repsTo"))) {
628 for (i
=0; i
< orf_el
->num_values
; i
++) {
629 status
= dreplsrv_partition_add_source_dsa(s
, p
, &p
->notifies
,
630 p
->sources
, &orf_el
->values
[i
]);
631 W_ERROR_NOT_OK_GOTO_DONE(status
);
636 talloc_free(mem_ctx
);
640 WERROR
dreplsrv_refresh_partitions(struct dreplsrv_service
*s
)
643 struct dreplsrv_partition
*p
;
645 for (p
= s
->partitions
; p
; p
= p
->next
) {
646 status
= dreplsrv_refresh_partition(s
, p
);
647 W_ERROR_NOT_OK_RETURN(status
);