smbd: Fix crossing automounter mount points
[Samba.git] / source4 / dsdb / repl / drepl_partitions.c
blobc525329ee7c857e46b2e7b219e05cf4f4358b324
1 /*
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/>.
22 #include "includes.h"
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"
37 #undef DBGC_CLASS
38 #define DBGC_CLASS DBGC_DRS_REPL
40 #undef strcasecmp
43 load the partitions list based on replicated NC attributes in our
44 NTDSDSA object
46 WERROR dreplsrv_load_partitions(struct dreplsrv_service *s)
48 WERROR status;
49 static const char *attrs[] = { "hasMasterNCs", "msDS-hasMasterNCs", "hasPartialReplicaNCs", "msDS-HasFullReplicaNCs", NULL };
50 unsigned int a;
51 int ret;
52 TALLOC_CTX *tmp_ctx;
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);
61 if (!ntds_dn) {
62 DEBUG(1,(__location__ ": Unable to find ntds_dn: %s\n", ldb_errstring(s->samdb)));
63 talloc_free(tmp_ctx);
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)));
70 talloc_free(tmp_ctx);
71 return WERR_DS_DRA_INTERNAL_ERROR;
74 for (a=0; attrs[a]; a++) {
75 int i;
77 el = ldb_msg_find_element(res->msgs[0], attrs[a]);
78 if (el == NULL) {
79 continue;
81 for (i=0; i<el->num_values; i++) {
82 struct ldb_dn *pdn;
83 struct dreplsrv_partition *p, *tp;
84 bool found;
86 pdn = ldb_dn_from_ldb_val(tmp_ctx, s->samdb, &el->values[i]);
87 if (pdn == NULL) {
88 talloc_free(tmp_ctx);
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);
99 p->service = s;
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 */
108 found = false;
109 for (tp = s->partitions; tp; tp = tp->next) {
110 if (ldb_dn_compare(tp->dn, p->dn) == 0) {
111 found = true;
112 break;
115 if (found) {
116 talloc_free(p);
117 continue;
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);
130 return WERR_OK;
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)
139 TALLOC_CTX *tmp_ctx;
140 const char *attrs_empty[] = { NULL };
141 int ret;
142 struct ldb_result *res;
143 const char *principal_name_encoded = NULL;
145 tmp_ctx = talloc_new(samdb);
146 if (tmp_ctx == NULL) {
147 return false;
150 principal_name_encoded = ldb_binary_encode_string(tmp_ctx, principal_name);
151 if (principal_name_encoded == NULL) {
152 talloc_free(tmp_ctx);
153 return false;
156 ret = dsdb_search(samdb, tmp_ctx, &res, account_dn, LDB_SCOPE_BASE, attrs_empty,
157 0, "servicePrincipalName=%s",
158 principal_name_encoded);
159 if (ret != LDB_SUCCESS || res->count != 1) {
160 talloc_free(tmp_ctx);
161 return false;
164 talloc_free(tmp_ctx);
165 return true;
169 work out the principal to use for DRS replication connections
171 static NTSTATUS dreplsrv_get_target_principal(struct dreplsrv_service *s,
172 TALLOC_CTX *mem_ctx,
173 const struct repsFromTo1 *rft,
174 char **target_principal)
176 TALLOC_CTX *tmp_ctx;
177 struct ldb_result *res;
178 const char *attrs_server[] = { "dNSHostName", "serverReference", NULL };
179 const char *attrs_ntds[] = { "msDS-HasDomainNCs", "hasMasterNCs", NULL };
180 int ret;
181 const char *hostname, *dnsdomain=NULL;
182 struct ldb_dn *ntds_dn, *server_dn, *computer_dn;
183 struct ldb_dn *forest_dn, *nc_dn;
185 *target_principal = NULL;
187 tmp_ctx = talloc_new(mem_ctx);
189 /* we need to find their hostname */
190 ret = dsdb_find_dn_by_guid(s->samdb, tmp_ctx, &rft->source_dsa_obj_guid, 0, &ntds_dn);
191 if (ret != LDB_SUCCESS) {
192 talloc_free(tmp_ctx);
193 /* its OK for their NTDSDSA DN not to be in our database */
194 return NT_STATUS_OK;
197 server_dn = ldb_dn_copy(tmp_ctx, ntds_dn);
198 if (server_dn == NULL) {
199 talloc_free(tmp_ctx);
200 return NT_STATUS_OK;
203 /* strip off the NTDS Settings */
204 if (!ldb_dn_remove_child_components(server_dn, 1)) {
205 talloc_free(tmp_ctx);
206 return NT_STATUS_OK;
209 ret = dsdb_search_dn(s->samdb, tmp_ctx, &res, server_dn, attrs_server, 0);
210 if (ret != LDB_SUCCESS) {
211 talloc_free(tmp_ctx);
212 /* its OK for their server DN not to be in our database */
213 return NT_STATUS_OK;
216 forest_dn = ldb_get_root_basedn(s->samdb);
217 if (forest_dn == NULL) {
218 talloc_free(tmp_ctx);
219 return NT_STATUS_OK;
222 hostname = ldb_msg_find_attr_as_string(res->msgs[0], "dNSHostName", NULL);
223 computer_dn = ldb_msg_find_attr_as_dn(s->samdb, tmp_ctx, res->msgs[0], "serverReference");
224 if (hostname != NULL && computer_dn != NULL) {
225 char *local_principal;
228 if we have the dNSHostName attribute then we can use
229 the GC/hostname/realm SPN. All DCs should have this SPN
231 Windows DC may set up it's dNSHostName before setting up
232 GC/xx/xx SPN. So make sure it exists, before using it.
234 local_principal = talloc_asprintf(mem_ctx, "GC/%s/%s",
235 hostname,
236 samdb_dn_to_dns_domain(tmp_ctx, forest_dn));
237 if (local_principal == NULL) {
238 talloc_free(tmp_ctx);
239 return NT_STATUS_NO_MEMORY;
241 if (dreplsrv_spn_exists(s->samdb, computer_dn, local_principal)) {
242 *target_principal = local_principal;
243 talloc_free(tmp_ctx);
244 return NT_STATUS_OK;
247 talloc_free(local_principal);
251 if we can't find the dNSHostName then we will try for the
252 E3514235-4B06-11D1-AB04-00C04FC2DCD2/${NTDSGUID}/${DNSDOMAIN}
253 SPN. To use that we need the DNS domain name of the target
254 DC. We find that by first looking for the msDS-HasDomainNCs
255 in the NTDSDSA object of the DC, and if we don't find that,
256 then we look for the hasMasterNCs attribute, and eliminate
257 the known schema and configuruation DNs. Despite how
258 bizarre this seems, Hongwei tells us that this is in fact
259 what windows does to find the SPN!!
261 ret = dsdb_search_dn(s->samdb, tmp_ctx, &res, ntds_dn, attrs_ntds, 0);
262 if (ret != LDB_SUCCESS) {
263 talloc_free(tmp_ctx);
264 return NT_STATUS_OK;
267 nc_dn = ldb_msg_find_attr_as_dn(s->samdb, tmp_ctx, res->msgs[0], "msDS-HasDomainNCs");
268 if (nc_dn != NULL) {
269 dnsdomain = samdb_dn_to_dns_domain(tmp_ctx, nc_dn);
272 if (dnsdomain == NULL) {
273 struct ldb_message_element *el;
274 int i;
275 el = ldb_msg_find_element(res->msgs[0], "hasMasterNCs");
276 for (i=0; el && i<el->num_values; i++) {
277 nc_dn = ldb_dn_from_ldb_val(tmp_ctx, s->samdb, &el->values[i]);
278 if (nc_dn == NULL ||
279 ldb_dn_compare(ldb_get_config_basedn(s->samdb), nc_dn) == 0 ||
280 ldb_dn_compare(ldb_get_schema_basedn(s->samdb), nc_dn) == 0) {
281 continue;
283 /* it must be a domain DN, get the equivalent
284 DNS domain name */
285 dnsdomain = samdb_dn_to_dns_domain(tmp_ctx, nc_dn);
286 break;
290 if (dnsdomain != NULL) {
291 *target_principal = talloc_asprintf(mem_ctx,
292 "E3514235-4B06-11D1-AB04-00C04FC2DCD2/%s/%s@%s",
293 GUID_string(tmp_ctx, &rft->source_dsa_obj_guid),
294 dnsdomain, dnsdomain);
297 talloc_free(tmp_ctx);
298 return NT_STATUS_OK;
302 WERROR dreplsrv_out_connection_attach(struct dreplsrv_service *s,
303 const struct repsFromTo1 *rft,
304 struct dreplsrv_out_connection **_conn)
306 struct dreplsrv_out_connection *cur, *conn = NULL;
307 const char *hostname;
309 if (!rft->other_info) {
310 return WERR_FOOBAR;
313 if (!rft->other_info->dns_name) {
314 return WERR_FOOBAR;
317 hostname = rft->other_info->dns_name;
319 for (cur = s->connections; cur; cur = cur->next) {
320 const char *host;
322 host = dcerpc_binding_get_string_option(cur->binding, "host");
323 if (host == NULL) {
324 continue;
327 if (strcmp(host, hostname) == 0) {
328 conn = cur;
329 break;
333 if (!conn) {
334 NTSTATUS nt_status;
335 char *binding_str;
336 char *target_principal = NULL;
338 conn = talloc_zero(s, struct dreplsrv_out_connection);
339 W_ERROR_HAVE_NO_MEMORY(conn);
341 conn->service = s;
343 binding_str = talloc_asprintf(conn, "ncacn_ip_tcp:%s[krb5,seal]",
344 hostname);
345 W_ERROR_HAVE_NO_MEMORY(binding_str);
346 nt_status = dcerpc_parse_binding(conn, binding_str, &conn->binding);
347 talloc_free(binding_str);
348 if (!NT_STATUS_IS_OK(nt_status)) {
349 return ntstatus_to_werror(nt_status);
352 /* use the GC principal for DRS replication */
353 nt_status = dreplsrv_get_target_principal(s, conn->binding,
354 rft, &target_principal);
355 if (!NT_STATUS_IS_OK(nt_status)) {
356 return ntstatus_to_werror(nt_status);
359 nt_status = dcerpc_binding_set_string_option(conn->binding,
360 "target_principal",
361 target_principal);
362 TALLOC_FREE(target_principal);
363 if (!NT_STATUS_IS_OK(nt_status)) {
364 return ntstatus_to_werror(nt_status);
367 DLIST_ADD_END(s->connections, conn);
369 DEBUG(4,("dreplsrv_out_connection_attach(%s): create\n", hostname));
370 } else {
371 DEBUG(4,("dreplsrv_out_connection_attach(%s): attach\n", hostname));
374 *_conn = conn;
375 return WERR_OK;
379 find an existing source dsa in a list
381 static struct dreplsrv_partition_source_dsa *dreplsrv_find_source_dsa(struct dreplsrv_partition_source_dsa *list,
382 struct GUID *guid)
384 struct dreplsrv_partition_source_dsa *s;
385 for (s=list; s; s=s->next) {
386 if (GUID_equal(&s->repsFrom1->source_dsa_obj_guid, guid)) {
387 return s;
390 return NULL;
395 static WERROR dreplsrv_partition_add_source_dsa(struct dreplsrv_service *s,
396 struct dreplsrv_partition *p,
397 struct dreplsrv_partition_source_dsa **listp,
398 struct dreplsrv_partition_source_dsa *check_list,
399 const struct ldb_val *val)
401 WERROR status;
402 enum ndr_err_code ndr_err;
403 struct dreplsrv_partition_source_dsa *source, *s2;
405 source = talloc_zero(p, struct dreplsrv_partition_source_dsa);
406 W_ERROR_HAVE_NO_MEMORY(source);
408 ndr_err = ndr_pull_struct_blob(val, source,
409 &source->_repsFromBlob,
410 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
411 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
412 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
413 talloc_free(source);
414 return ntstatus_to_werror(nt_status);
416 /* NDR_PRINT_DEBUG(repsFromToBlob, &source->_repsFromBlob); */
417 if (source->_repsFromBlob.version != 1) {
418 talloc_free(source);
419 return WERR_DS_DRA_INTERNAL_ERROR;
422 source->partition = p;
423 source->repsFrom1 = &source->_repsFromBlob.ctr.ctr1;
425 status = dreplsrv_out_connection_attach(s, source->repsFrom1, &source->conn);
426 W_ERROR_NOT_OK_RETURN(status);
428 if (check_list &&
429 dreplsrv_find_source_dsa(check_list, &source->repsFrom1->source_dsa_obj_guid)) {
430 /* its in the check list, don't add it again */
431 talloc_free(source);
432 return WERR_OK;
435 /* re-use an existing source if found */
436 for (s2=*listp; s2; s2=s2->next) {
437 if (GUID_equal(&s2->repsFrom1->source_dsa_obj_guid,
438 &source->repsFrom1->source_dsa_obj_guid)) {
439 talloc_free(s2->repsFrom1->other_info);
440 *s2->repsFrom1 = *source->repsFrom1;
441 talloc_steal(s2, s2->repsFrom1->other_info);
442 talloc_free(source);
443 return WERR_OK;
447 DLIST_ADD_END(*listp, source);
448 return WERR_OK;
452 * Find a partition when given a NC
453 * If the NC can't be found it will return BAD_NC
454 * Initial checks for invalid parameters have to be done beforehand
456 WERROR dreplsrv_partition_find_for_nc(struct dreplsrv_service *s,
457 struct GUID *nc_guid,
458 struct dom_sid *nc_sid,
459 const char *nc_dn_str,
460 struct dreplsrv_partition **_p)
462 struct dreplsrv_partition *p;
463 bool valid_sid, valid_guid;
465 SMB_ASSERT(_p);
467 valid_sid = nc_sid && !is_null_sid(nc_sid);
468 valid_guid = nc_guid && !GUID_all_zero(nc_guid);
470 if (!valid_sid && !valid_guid && (!nc_dn_str)) {
471 return WERR_DS_DRA_BAD_NC;
474 for (p = s->partitions; p; p = p->next) {
475 if ((valid_guid && GUID_equal(&p->nc.guid, nc_guid))
476 || strequal(p->nc.dn, nc_dn_str)
477 || (valid_sid && dom_sid_equal(&p->nc.sid, nc_sid)))
479 /* fill in the right guid and sid if possible */
480 if (nc_guid && !valid_guid) {
481 dsdb_get_extended_dn_guid(p->dn, nc_guid, "GUID");
483 if (nc_sid && !valid_sid) {
484 dsdb_get_extended_dn_sid(p->dn, nc_sid, "SID");
486 *_p = p;
487 return WERR_OK;
491 return WERR_DS_DRA_BAD_NC;
494 WERROR dreplsrv_partition_source_dsa_by_guid(struct dreplsrv_partition *p,
495 const struct GUID *dsa_guid,
496 struct dreplsrv_partition_source_dsa **_dsa)
498 struct dreplsrv_partition_source_dsa *dsa;
500 SMB_ASSERT(dsa_guid != NULL);
501 SMB_ASSERT(!GUID_all_zero(dsa_guid));
502 SMB_ASSERT(_dsa);
504 for (dsa = p->sources; dsa; dsa = dsa->next) {
505 if (GUID_equal(dsa_guid, &dsa->repsFrom1->source_dsa_obj_guid)) {
506 *_dsa = dsa;
507 return WERR_OK;
511 return WERR_DS_DRA_NO_REPLICA;
514 WERROR dreplsrv_partition_source_dsa_by_dns(const struct dreplsrv_partition *p,
515 const char *dsa_dns,
516 struct dreplsrv_partition_source_dsa **_dsa)
518 struct dreplsrv_partition_source_dsa *dsa;
520 SMB_ASSERT(dsa_dns != NULL);
521 SMB_ASSERT(_dsa);
523 for (dsa = p->sources; dsa; dsa = dsa->next) {
524 if (strequal(dsa_dns, dsa->repsFrom1->other_info->dns_name)) {
525 *_dsa = dsa;
526 return WERR_OK;
530 return WERR_DS_DRA_NO_REPLICA;
535 create a temporary dsa structure for a replication. This is needed
536 for the initial replication of a new partition, such as when a new
537 domain NC is created and we are a global catalog server
539 WERROR dreplsrv_partition_source_dsa_temporary(struct dreplsrv_partition *p,
540 TALLOC_CTX *mem_ctx,
541 const struct GUID *dsa_guid,
542 struct dreplsrv_partition_source_dsa **_dsa)
544 struct dreplsrv_partition_source_dsa *dsa;
545 WERROR werr;
547 dsa = talloc_zero(mem_ctx, struct dreplsrv_partition_source_dsa);
548 W_ERROR_HAVE_NO_MEMORY(dsa);
550 dsa->partition = p;
551 dsa->repsFrom1 = &dsa->_repsFromBlob.ctr.ctr1;
552 dsa->repsFrom1->replica_flags = 0;
553 dsa->repsFrom1->source_dsa_obj_guid = *dsa_guid;
555 dsa->repsFrom1->other_info = talloc_zero(dsa, struct repsFromTo1OtherInfo);
556 W_ERROR_HAVE_NO_MEMORY(dsa->repsFrom1->other_info);
558 dsa->repsFrom1->other_info->dns_name = samdb_ntds_msdcs_dns_name(p->service->samdb,
559 dsa->repsFrom1->other_info, dsa_guid);
560 W_ERROR_HAVE_NO_MEMORY(dsa->repsFrom1->other_info->dns_name);
562 werr = dreplsrv_out_connection_attach(p->service, dsa->repsFrom1, &dsa->conn);
563 if (!W_ERROR_IS_OK(werr)) {
564 DEBUG(0,(__location__ ": Failed to attach connection to %s\n",
565 ldb_dn_get_linearized(p->dn)));
566 talloc_free(dsa);
567 return werr;
570 *_dsa = dsa;
572 return WERR_OK;
576 static WERROR dreplsrv_refresh_partition(struct dreplsrv_service *s,
577 struct dreplsrv_partition *p)
579 WERROR status;
580 NTSTATUS ntstatus;
581 struct ldb_message_element *orf_el = NULL;
582 struct ldb_result *r = NULL;
583 unsigned int i;
584 int ret;
585 TALLOC_CTX *mem_ctx = talloc_new(p);
586 static const char *attrs[] = {
587 "repsFrom",
588 "repsTo",
589 NULL
591 struct ldb_dn *dn;
593 DEBUG(4, ("dreplsrv_refresh_partition(%s)\n",
594 ldb_dn_get_linearized(p->dn)));
596 ret = dsdb_search_dn(s->samdb, mem_ctx, &r, p->dn, attrs, DSDB_SEARCH_SHOW_EXTENDED_DN);
597 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
598 /* we haven't replicated the partition yet, but we
599 * can fill in the guid, sid etc from the partition DN */
600 dn = p->dn;
601 } else if (ret != LDB_SUCCESS) {
602 talloc_free(mem_ctx);
603 return WERR_FOOBAR;
604 } else {
605 dn = r->msgs[0]->dn;
608 talloc_free(discard_const(p->nc.dn));
609 ZERO_STRUCT(p->nc);
610 p->nc.dn = ldb_dn_alloc_linearized(p, dn);
611 W_ERROR_HAVE_NO_MEMORY(p->nc.dn);
612 ntstatus = dsdb_get_extended_dn_guid(dn, &p->nc.guid, "GUID");
613 if (!NT_STATUS_IS_OK(ntstatus)) {
614 DEBUG(0,(__location__ ": unable to get GUID for %s: %s\n",
615 p->nc.dn, nt_errstr(ntstatus)));
616 talloc_free(mem_ctx);
617 return WERR_DS_DRA_INTERNAL_ERROR;
619 dsdb_get_extended_dn_sid(dn, &p->nc.sid, "SID");
621 talloc_free(p->uptodatevector.cursors);
622 talloc_free(p->uptodatevector_ex.cursors);
623 ZERO_STRUCT(p->uptodatevector);
624 ZERO_STRUCT(p->uptodatevector_ex);
626 ret = dsdb_load_udv_v2(s->samdb, p->dn, p, &p->uptodatevector.cursors, &p->uptodatevector.count);
627 if (ret != LDB_SUCCESS) {
628 DEBUG(4,(__location__ ": no UDV available for %s\n", ldb_dn_get_linearized(p->dn)));
631 status = WERR_OK;
633 if (r != NULL && (orf_el = ldb_msg_find_element(r->msgs[0], "repsFrom"))) {
634 for (i=0; i < orf_el->num_values; i++) {
635 status = dreplsrv_partition_add_source_dsa(s, p, &p->sources,
636 NULL, &orf_el->values[i]);
637 W_ERROR_NOT_OK_GOTO_DONE(status);
641 if (r != NULL && (orf_el = ldb_msg_find_element(r->msgs[0], "repsTo"))) {
642 for (i=0; i < orf_el->num_values; i++) {
643 status = dreplsrv_partition_add_source_dsa(s, p, &p->notifies,
644 p->sources, &orf_el->values[i]);
645 W_ERROR_NOT_OK_GOTO_DONE(status);
649 done:
650 talloc_free(mem_ctx);
651 return status;
654 WERROR dreplsrv_refresh_partitions(struct dreplsrv_service *s)
656 WERROR status;
657 struct dreplsrv_partition *p;
659 for (p = s->partitions; p; p = p->next) {
660 status = dreplsrv_refresh_partition(s, p);
661 W_ERROR_NOT_OK_RETURN(status);
664 return WERR_OK;