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 "lib/messaging/irpc.h"
28 #include "dsdb/repl/drepl_service.h"
29 #include "lib/ldb/include/ldb_errors.h"
30 #include "../lib/util/dlinklist.h"
31 #include "librpc/gen_ndr/ndr_misc.h"
32 #include "librpc/gen_ndr/ndr_drsuapi.h"
33 #include "librpc/gen_ndr/ndr_drsblobs.h"
34 #include "libcli/security/security.h"
35 #include "param/param.h"
37 WERROR dreplsrv_load_partitions(struct dreplsrv_service *s)
40 static const char *attrs[] = { "namingContexts", NULL };
44 struct ldb_result *res;
45 struct ldb_message_element *el;
47 tmp_ctx = talloc_new(s);
48 W_ERROR_HAVE_NO_MEMORY(tmp_ctx);
50 ret = ldb_search(s->samdb, tmp_ctx, &res,
51 ldb_dn_new(tmp_ctx, s->samdb, ""), LDB_SCOPE_BASE, attrs, NULL);
52 if (ret != LDB_SUCCESS) {
53 DEBUG(1,("Searching for namingContexts in rootDSE failed: %s\n", ldb_errstring(s->samdb)));
55 return WERR_DS_DRA_INTERNAL_ERROR;
58 el = ldb_msg_find_element(res->msgs[0], "namingContexts");
60 DEBUG(1,("Finding namingContexts element in root_res failed: %s\n",
61 ldb_errstring(s->samdb)));
63 return WERR_DS_DRA_INTERNAL_ERROR;
66 for (i=0; i<el->num_values; i++) {
68 struct dreplsrv_partition *p;
70 pdn = ldb_dn_from_ldb_val(tmp_ctx, s->samdb, &el->values[i]);
73 return WERR_DS_DRA_INTERNAL_ERROR;
75 if (!ldb_dn_validate(pdn)) {
76 return WERR_DS_DRA_INTERNAL_ERROR;
79 p = talloc_zero(s, struct dreplsrv_partition);
80 W_ERROR_HAVE_NO_MEMORY(p);
82 p->dn = talloc_steal(p, pdn);
84 DLIST_ADD(s->partitions, p);
86 DEBUG(2, ("dreplsrv_partition[%s] loaded\n", ldb_dn_get_linearized(p->dn)));
91 status = dreplsrv_refresh_partitions(s);
92 W_ERROR_NOT_OK_RETURN(status);
98 work out the principal to use for DRS replication connections
100 NTSTATUS dreplsrv_get_target_principal(struct dreplsrv_service *s,
102 const struct repsFromTo1 *rft,
103 const char **target_principal)
106 struct ldb_result *res;
107 const char *attrs[] = { "dNSHostName", NULL };
109 const char *hostname;
112 *target_principal = NULL;
114 tmp_ctx = talloc_new(mem_ctx);
116 /* we need to find their hostname */
117 ret = dsdb_find_dn_by_guid(s->samdb, tmp_ctx, &rft->source_dsa_obj_guid, &dn);
118 if (ret != LDB_SUCCESS) {
119 talloc_free(tmp_ctx);
120 /* its OK for their NTDSDSA DN not to be in our database */
124 /* strip off the NTDS Settings */
125 if (!ldb_dn_remove_child_components(dn, 1)) {
126 talloc_free(tmp_ctx);
130 ret = dsdb_search_dn(s->samdb, tmp_ctx, &res, dn, attrs, 0);
131 if (ret != LDB_SUCCESS) {
132 talloc_free(tmp_ctx);
133 /* its OK for their account DN not to be in our database */
137 hostname = ldb_msg_find_attr_as_string(res->msgs[0], "dNSHostName", NULL);
138 if (hostname == NULL) {
139 talloc_free(tmp_ctx);
140 /* its OK to not have a dnshostname */
144 /* All DCs have the GC/hostname/realm name, but if some of the
145 * preconditions are not satisfied, then we will fall back to
147 * E3514235-4B06-11D1-AB04-00C04FC2DCD2/${NTDSGUID}/${DNSDOMAIN}
148 * name. This means that if a AD server has a dnsHostName set
149 * on it's record, it must also have GC/hostname/realm
150 * servicePrincipalName */
152 *target_principal = talloc_asprintf(mem_ctx, "GC/%s/%s",
154 lpcfg_dnsdomain(s->task->lp_ctx));
155 talloc_free(tmp_ctx);
160 WERROR dreplsrv_out_connection_attach(struct dreplsrv_service *s,
161 const struct repsFromTo1 *rft,
162 struct dreplsrv_out_connection **_conn)
164 struct dreplsrv_out_connection *cur, *conn = NULL;
165 const char *hostname;
167 if (!rft->other_info) {
171 if (!rft->other_info->dns_name) {
175 hostname = rft->other_info->dns_name;
177 for (cur = s->connections; cur; cur = cur->next) {
178 if (strcmp(cur->binding->host, hostname) == 0) {
188 conn = talloc_zero(s, struct dreplsrv_out_connection);
189 W_ERROR_HAVE_NO_MEMORY(conn);
193 binding_str = talloc_asprintf(conn, "ncacn_ip_tcp:%s[krb5,seal]",
195 W_ERROR_HAVE_NO_MEMORY(binding_str);
196 nt_status = dcerpc_parse_binding(conn, binding_str, &conn->binding);
197 talloc_free(binding_str);
198 if (!NT_STATUS_IS_OK(nt_status)) {
199 return ntstatus_to_werror(nt_status);
202 /* use the GC principal for DRS replication */
203 nt_status = dreplsrv_get_target_principal(s, conn->binding,
204 rft, &conn->binding->target_principal);
205 if (!NT_STATUS_IS_OK(nt_status)) {
206 return ntstatus_to_werror(nt_status);
209 DLIST_ADD_END(s->connections, conn, struct dreplsrv_out_connection *);
211 DEBUG(4,("dreplsrv_out_connection_attach(%s): create\n", conn->binding->host));
213 DEBUG(4,("dreplsrv_out_connection_attach(%s): attach\n", conn->binding->host));
221 find an existing source dsa in a list
223 static struct dreplsrv_partition_source_dsa *dreplsrv_find_source_dsa(struct dreplsrv_partition_source_dsa *list,
226 struct dreplsrv_partition_source_dsa *s;
227 for (s=list; s; s=s->next) {
228 if (GUID_compare(&s->repsFrom1->source_dsa_obj_guid, guid) == 0) {
237 static WERROR dreplsrv_partition_add_source_dsa(struct dreplsrv_service *s,
238 struct dreplsrv_partition *p,
239 struct dreplsrv_partition_source_dsa **listp,
240 struct dreplsrv_partition_source_dsa *check_list,
241 const struct ldb_val *val)
244 enum ndr_err_code ndr_err;
245 struct dreplsrv_partition_source_dsa *source, *s2;
247 source = talloc_zero(p, struct dreplsrv_partition_source_dsa);
248 W_ERROR_HAVE_NO_MEMORY(source);
250 ndr_err = ndr_pull_struct_blob(val, source,
251 &source->_repsFromBlob,
252 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
253 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
254 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
256 return ntstatus_to_werror(nt_status);
258 /* NDR_PRINT_DEBUG(repsFromToBlob, &source->_repsFromBlob); */
259 if (source->_repsFromBlob.version != 1) {
261 return WERR_DS_DRA_INTERNAL_ERROR;
264 source->partition = p;
265 source->repsFrom1 = &source->_repsFromBlob.ctr.ctr1;
267 status = dreplsrv_out_connection_attach(s, source->repsFrom1, &source->conn);
268 W_ERROR_NOT_OK_RETURN(status);
271 dreplsrv_find_source_dsa(check_list, &source->repsFrom1->source_dsa_obj_guid)) {
272 /* its in the check list, don't add it again */
277 /* re-use an existing source if found */
278 for (s2=*listp; s2; s2=s2->next) {
279 if (GUID_compare(&s2->repsFrom1->source_dsa_obj_guid,
280 &source->repsFrom1->source_dsa_obj_guid) == 0) {
281 talloc_free(s2->repsFrom1->other_info);
282 *s2->repsFrom1 = *source->repsFrom1;
283 talloc_steal(s2, s2->repsFrom1->other_info);
289 DLIST_ADD_END(*listp, source, struct dreplsrv_partition_source_dsa *);
293 WERROR dreplsrv_partition_find_for_nc(struct dreplsrv_service *s,
294 const struct GUID *nc_guid,
295 const struct dom_sid *nc_sid,
296 const char *nc_dn_str,
297 struct dreplsrv_partition **_p)
299 struct dreplsrv_partition *p;
300 bool valid_sid, valid_guid;
301 struct dom_sid null_sid;
302 ZERO_STRUCT(null_sid);
306 valid_sid = nc_sid && !dom_sid_equal(&null_sid, nc_sid);
307 valid_guid = nc_guid && !GUID_all_zero(nc_guid);
309 if (!valid_sid && !valid_guid && !nc_dn_str) {
310 return WERR_DS_DRA_INVALID_PARAMETER;
313 for (p = s->partitions; p; p = p->next) {
314 if ((valid_guid && GUID_equal(&p->nc.guid, nc_guid))
315 || strequal(p->nc.dn, nc_dn_str)
316 || (valid_sid && dom_sid_equal(&p->nc.sid, nc_sid)))
323 return WERR_DS_DRA_BAD_NC;
326 WERROR dreplsrv_partition_source_dsa_by_guid(struct dreplsrv_partition *p,
327 const struct GUID *dsa_guid,
328 struct dreplsrv_partition_source_dsa **_dsa)
330 struct dreplsrv_partition_source_dsa *dsa;
332 SMB_ASSERT(dsa_guid != NULL);
333 SMB_ASSERT(!GUID_all_zero(dsa_guid));
336 for (dsa = p->sources; dsa; dsa = dsa->next) {
337 if (GUID_equal(dsa_guid, &dsa->repsFrom1->source_dsa_obj_guid)) {
343 return WERR_DS_DRA_NO_REPLICA;
346 WERROR dreplsrv_partition_source_dsa_by_dns(const struct dreplsrv_partition *p,
348 struct dreplsrv_partition_source_dsa **_dsa)
350 struct dreplsrv_partition_source_dsa *dsa;
352 SMB_ASSERT(dsa_dns != NULL);
355 for (dsa = p->sources; dsa; dsa = dsa->next) {
356 if (strequal(dsa_dns, dsa->repsFrom1->other_info->dns_name)) {
362 return WERR_DS_DRA_NO_REPLICA;
366 static WERROR dreplsrv_refresh_partition(struct dreplsrv_service *s,
367 struct dreplsrv_partition *p)
370 struct dom_sid *nc_sid;
371 struct ldb_message_element *orf_el = NULL;
372 struct ldb_result *r;
375 TALLOC_CTX *mem_ctx = talloc_new(p);
376 static const char *attrs[] = {
384 DEBUG(4, ("dreplsrv_refresh_partition(%s)\n",
385 ldb_dn_get_linearized(p->dn)));
387 ret = ldb_search(s->samdb, mem_ctx, &r, p->dn, LDB_SCOPE_BASE, attrs,
389 if (ret != LDB_SUCCESS) {
390 talloc_free(mem_ctx);
394 talloc_free(discard_const(p->nc.dn));
396 p->nc.dn = ldb_dn_alloc_linearized(p, p->dn);
397 W_ERROR_HAVE_NO_MEMORY(p->nc.dn);
398 p->nc.guid = samdb_result_guid(r->msgs[0], "objectGUID");
399 nc_sid = samdb_result_dom_sid(p, r->msgs[0], "objectSid");
405 talloc_free(p->uptodatevector.cursors);
406 talloc_free(p->uptodatevector_ex.cursors);
407 ZERO_STRUCT(p->uptodatevector);
408 ZERO_STRUCT(p->uptodatevector_ex);
410 ret = dsdb_load_udv_v2(s->samdb, p->dn, p, &p->uptodatevector.cursors, &p->uptodatevector.count);
411 if (ret != LDB_SUCCESS) {
412 DEBUG(4,(__location__ ": no UDV available for %s\n", ldb_dn_get_linearized(p->dn)));
415 orf_el = ldb_msg_find_element(r->msgs[0], "repsFrom");
417 for (i=0; i < orf_el->num_values; i++) {
418 status = dreplsrv_partition_add_source_dsa(s, p, &p->sources,
419 NULL, &orf_el->values[i]);
420 W_ERROR_NOT_OK_RETURN(status);
424 orf_el = ldb_msg_find_element(r->msgs[0], "repsTo");
426 for (i=0; i < orf_el->num_values; i++) {
427 status = dreplsrv_partition_add_source_dsa(s, p, &p->notifies,
428 p->sources, &orf_el->values[i]);
429 W_ERROR_NOT_OK_RETURN(status);
433 talloc_free(mem_ctx);
438 WERROR dreplsrv_refresh_partitions(struct dreplsrv_service *s)
441 struct dreplsrv_partition *p;
443 for (p = s->partitions; p; p = p->next) {
444 status = dreplsrv_refresh_partition(s, p);
445 W_ERROR_NOT_OK_RETURN(status);