2 Unix SMB/CIFS mplementation.
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 "smbd/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"
36 WERROR
dreplsrv_load_partitions(struct dreplsrv_service
*s
)
39 static const char *attrs
[] = { "namingContexts", NULL
};
43 struct ldb_result
*res
;
44 struct ldb_message_element
*el
;
46 tmp_ctx
= talloc_new(s
);
47 W_ERROR_HAVE_NO_MEMORY(tmp_ctx
);
49 ret
= ldb_search(s
->samdb
, tmp_ctx
, &res
,
50 ldb_dn_new(tmp_ctx
, s
->samdb
, ""), LDB_SCOPE_BASE
, attrs
, NULL
);
51 if (ret
!= LDB_SUCCESS
) {
52 DEBUG(1,("Searching for namingContexts in rootDSE failed: %s\n", ldb_errstring(s
->samdb
)));
54 return WERR_DS_DRA_INTERNAL_ERROR
;
57 el
= ldb_msg_find_element(res
->msgs
[0], "namingContexts");
59 DEBUG(1,("Finding namingContexts element in root_res failed: %s\n",
60 ldb_errstring(s
->samdb
)));
62 return WERR_DS_DRA_INTERNAL_ERROR
;
65 for (i
=0; i
<el
->num_values
; i
++) {
67 struct dreplsrv_partition
*p
;
69 pdn
= ldb_dn_from_ldb_val(tmp_ctx
, s
->samdb
, &el
->values
[i
]);
72 return WERR_DS_DRA_INTERNAL_ERROR
;
74 if (!ldb_dn_validate(pdn
)) {
75 return WERR_DS_DRA_INTERNAL_ERROR
;
78 p
= talloc_zero(s
, struct dreplsrv_partition
);
79 W_ERROR_HAVE_NO_MEMORY(p
);
81 p
->dn
= talloc_steal(p
, pdn
);
83 DLIST_ADD(s
->partitions
, p
);
85 DEBUG(2, ("dreplsrv_partition[%s] loaded\n", ldb_dn_get_linearized(p
->dn
)));
90 status
= dreplsrv_refresh_partitions(s
);
91 W_ERROR_NOT_OK_RETURN(status
);
97 work out the principal to use for DRS replication connections
99 NTSTATUS
dreplsrv_get_target_principal(struct dreplsrv_service
*s
,
101 const struct repsFromTo1
*rft
,
102 const char **target_principal
)
105 struct ldb_result
*res
;
106 const char *attrs
[] = { "dNSHostName", NULL
};
108 const char *hostname
;
111 *target_principal
= NULL
;
113 tmp_ctx
= talloc_new(mem_ctx
);
115 /* we need to find their hostname */
116 ret
= dsdb_find_dn_by_guid(s
->samdb
, tmp_ctx
, &rft
->source_dsa_obj_guid
, &dn
);
117 if (ret
!= LDB_SUCCESS
) {
118 talloc_free(tmp_ctx
);
119 /* its OK for their NTDSDSA DN not to be in our database */
123 /* strip off the NTDS Settings */
124 if (!ldb_dn_remove_child_components(dn
, 1)) {
125 talloc_free(tmp_ctx
);
129 ret
= dsdb_search_dn(s
->samdb
, tmp_ctx
, &res
, dn
, attrs
, 0);
130 if (ret
!= LDB_SUCCESS
) {
131 talloc_free(tmp_ctx
);
132 /* its OK for their account DN not to be in our database */
136 hostname
= ldb_msg_find_attr_as_string(res
->msgs
[0], "dNSHostName", NULL
);
137 if (hostname
== NULL
) {
138 talloc_free(tmp_ctx
);
139 /* its OK to not have a dnshostname */
143 /* All DCs have the GC/hostname/realm name, but if some of the
144 * preconditions are not satisfied, then we will fall back to
146 * E3514235-4B06-11D1-AB04-00C04FC2DCD2/${NTDSGUID}/${DNSDOMAIN}
147 * name. This means that if a AD server has a dnsHostName set
148 * on it's record, it must also have GC/hostname/realm
149 * servicePrincipalName */
151 *target_principal
= talloc_asprintf(mem_ctx
, "GC/%s/%s",
153 lpcfg_dnsdomain(s
->task
->lp_ctx
));
154 talloc_free(tmp_ctx
);
159 WERROR
dreplsrv_out_connection_attach(struct dreplsrv_service
*s
,
160 const struct repsFromTo1
*rft
,
161 struct dreplsrv_out_connection
**_conn
)
163 struct dreplsrv_out_connection
*cur
, *conn
= NULL
;
164 const char *hostname
;
166 if (!rft
->other_info
) {
170 if (!rft
->other_info
->dns_name
) {
174 hostname
= rft
->other_info
->dns_name
;
176 for (cur
= s
->connections
; cur
; cur
= cur
->next
) {
177 if (strcmp(cur
->binding
->host
, hostname
) == 0) {
187 conn
= talloc_zero(s
, struct dreplsrv_out_connection
);
188 W_ERROR_HAVE_NO_MEMORY(conn
);
192 binding_str
= talloc_asprintf(conn
, "ncacn_ip_tcp:%s[krb5,seal]",
194 W_ERROR_HAVE_NO_MEMORY(binding_str
);
195 nt_status
= dcerpc_parse_binding(conn
, binding_str
, &conn
->binding
);
196 talloc_free(binding_str
);
197 if (!NT_STATUS_IS_OK(nt_status
)) {
198 return ntstatus_to_werror(nt_status
);
201 /* use the GC principal for DRS replication */
202 nt_status
= dreplsrv_get_target_principal(s
, conn
->binding
,
203 rft
, &conn
->binding
->target_principal
);
204 if (!NT_STATUS_IS_OK(nt_status
)) {
205 return ntstatus_to_werror(nt_status
);
208 DLIST_ADD_END(s
->connections
, conn
, struct dreplsrv_out_connection
*);
210 DEBUG(4,("dreplsrv_out_connection_attach(%s): create\n", conn
->binding
->host
));
212 DEBUG(4,("dreplsrv_out_connection_attach(%s): attach\n", conn
->binding
->host
));
220 find an existing source dsa in a list
222 static struct dreplsrv_partition_source_dsa
*dreplsrv_find_source_dsa(struct dreplsrv_partition_source_dsa
*list
,
225 struct dreplsrv_partition_source_dsa
*s
;
226 for (s
=list
; s
; s
=s
->next
) {
227 if (GUID_compare(&s
->repsFrom1
->source_dsa_obj_guid
, guid
) == 0) {
236 static WERROR
dreplsrv_partition_add_source_dsa(struct dreplsrv_service
*s
,
237 struct dreplsrv_partition
*p
,
238 struct dreplsrv_partition_source_dsa
**listp
,
239 struct dreplsrv_partition_source_dsa
*check_list
,
240 const struct ldb_val
*val
)
243 enum ndr_err_code ndr_err
;
244 struct dreplsrv_partition_source_dsa
*source
, *s2
;
246 source
= talloc_zero(p
, struct dreplsrv_partition_source_dsa
);
247 W_ERROR_HAVE_NO_MEMORY(source
);
249 ndr_err
= ndr_pull_struct_blob(val
, source
,
250 &source
->_repsFromBlob
,
251 (ndr_pull_flags_fn_t
)ndr_pull_repsFromToBlob
);
252 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
253 NTSTATUS nt_status
= ndr_map_error2ntstatus(ndr_err
);
255 return ntstatus_to_werror(nt_status
);
257 /* NDR_PRINT_DEBUG(repsFromToBlob, &source->_repsFromBlob); */
258 if (source
->_repsFromBlob
.version
!= 1) {
260 return WERR_DS_DRA_INTERNAL_ERROR
;
263 source
->partition
= p
;
264 source
->repsFrom1
= &source
->_repsFromBlob
.ctr
.ctr1
;
266 status
= dreplsrv_out_connection_attach(s
, source
->repsFrom1
, &source
->conn
);
267 W_ERROR_NOT_OK_RETURN(status
);
270 dreplsrv_find_source_dsa(check_list
, &source
->repsFrom1
->source_dsa_obj_guid
)) {
271 /* its in the check list, don't add it again */
276 /* re-use an existing source if found */
277 for (s2
=*listp
; s2
; s2
=s2
->next
) {
278 if (GUID_compare(&s2
->repsFrom1
->source_dsa_obj_guid
,
279 &source
->repsFrom1
->source_dsa_obj_guid
) == 0) {
280 talloc_free(s2
->repsFrom1
->other_info
);
281 *s2
->repsFrom1
= *source
->repsFrom1
;
282 talloc_steal(s2
, s2
->repsFrom1
->other_info
);
288 DLIST_ADD_END(*listp
, source
, struct dreplsrv_partition_source_dsa
*);
292 WERROR
dreplsrv_partition_find_for_nc(struct dreplsrv_service
*s
,
293 const struct GUID
*nc_guid
,
294 const struct dom_sid
*nc_sid
,
295 const char *nc_dn_str
,
296 struct dreplsrv_partition
**_p
)
298 struct dreplsrv_partition
*p
;
299 bool valid_sid
, valid_guid
;
300 struct dom_sid null_sid
;
301 ZERO_STRUCT(null_sid
);
305 valid_sid
= nc_sid
&& !dom_sid_equal(&null_sid
, nc_sid
);
306 valid_guid
= nc_guid
&& !GUID_all_zero(nc_guid
);
308 if (!valid_sid
&& !valid_guid
&& !nc_dn_str
) {
309 return WERR_DS_DRA_INVALID_PARAMETER
;
312 for (p
= s
->partitions
; p
; p
= p
->next
) {
313 if ((valid_guid
&& GUID_equal(&p
->nc
.guid
, nc_guid
))
314 || strequal(p
->nc
.dn
, nc_dn_str
)
315 || (valid_sid
&& dom_sid_equal(&p
->nc
.sid
, nc_sid
)))
322 return WERR_DS_DRA_BAD_NC
;
325 WERROR
dreplsrv_partition_source_dsa_by_guid(struct dreplsrv_partition
*p
,
326 const struct GUID
*dsa_guid
,
327 struct dreplsrv_partition_source_dsa
**_dsa
)
329 struct dreplsrv_partition_source_dsa
*dsa
;
331 SMB_ASSERT(dsa_guid
!= NULL
);
332 SMB_ASSERT(!GUID_all_zero(dsa_guid
));
335 for (dsa
= p
->sources
; dsa
; dsa
= dsa
->next
) {
336 if (GUID_equal(dsa_guid
, &dsa
->repsFrom1
->source_dsa_obj_guid
)) {
342 return WERR_DS_DRA_NO_REPLICA
;
345 WERROR
dreplsrv_partition_source_dsa_by_dns(const struct dreplsrv_partition
*p
,
347 struct dreplsrv_partition_source_dsa
**_dsa
)
349 struct dreplsrv_partition_source_dsa
*dsa
;
351 SMB_ASSERT(dsa_dns
!= NULL
);
354 for (dsa
= p
->sources
; dsa
; dsa
= dsa
->next
) {
355 if (strequal(dsa_dns
, dsa
->repsFrom1
->other_info
->dns_name
)) {
361 return WERR_DS_DRA_NO_REPLICA
;
365 static WERROR
dreplsrv_refresh_partition(struct dreplsrv_service
*s
,
366 struct dreplsrv_partition
*p
)
369 struct dom_sid
*nc_sid
;
370 struct ldb_message_element
*orf_el
= NULL
;
371 struct ldb_result
*r
;
374 TALLOC_CTX
*mem_ctx
= talloc_new(p
);
375 static const char *attrs
[] = {
383 DEBUG(4, ("dreplsrv_refresh_partition(%s)\n",
384 ldb_dn_get_linearized(p
->dn
)));
386 ret
= ldb_search(s
->samdb
, mem_ctx
, &r
, p
->dn
, LDB_SCOPE_BASE
, attrs
,
388 if (ret
!= LDB_SUCCESS
) {
389 talloc_free(mem_ctx
);
393 talloc_free(discard_const(p
->nc
.dn
));
395 p
->nc
.dn
= ldb_dn_alloc_linearized(p
, p
->dn
);
396 W_ERROR_HAVE_NO_MEMORY(p
->nc
.dn
);
397 p
->nc
.guid
= samdb_result_guid(r
->msgs
[0], "objectGUID");
398 nc_sid
= samdb_result_dom_sid(p
, r
->msgs
[0], "objectSid");
404 talloc_free(p
->uptodatevector
.cursors
);
405 talloc_free(p
->uptodatevector_ex
.cursors
);
406 ZERO_STRUCT(p
->uptodatevector
);
407 ZERO_STRUCT(p
->uptodatevector_ex
);
409 ret
= dsdb_load_udv_v2(s
->samdb
, p
->dn
, p
, &p
->uptodatevector
.cursors
, &p
->uptodatevector
.count
);
410 if (ret
!= LDB_SUCCESS
) {
411 DEBUG(4,(__location__
": no UDV available for %s\n", ldb_dn_get_linearized(p
->dn
)));
414 orf_el
= ldb_msg_find_element(r
->msgs
[0], "repsFrom");
416 for (i
=0; i
< orf_el
->num_values
; i
++) {
417 status
= dreplsrv_partition_add_source_dsa(s
, p
, &p
->sources
,
418 NULL
, &orf_el
->values
[i
]);
419 W_ERROR_NOT_OK_RETURN(status
);
423 orf_el
= ldb_msg_find_element(r
->msgs
[0], "repsTo");
425 for (i
=0; i
< orf_el
->num_values
; i
++) {
426 status
= dreplsrv_partition_add_source_dsa(s
, p
, &p
->notifies
,
427 p
->sources
, &orf_el
->values
[i
]);
428 W_ERROR_NOT_OK_RETURN(status
);
432 talloc_free(mem_ctx
);
437 WERROR
dreplsrv_refresh_partitions(struct dreplsrv_service
*s
)
440 struct dreplsrv_partition
*p
;
442 for (p
= s
->partitions
; p
; p
= p
->next
) {
443 status
= dreplsrv_refresh_partition(s
, p
);
444 W_ERROR_NOT_OK_RETURN(status
);