s4-dsdb: Fix the case for attribute name msDS-hasMasterNCs
[Samba.git] / source4 / dsdb / repl / drepl_partitions.c
blob3aa715a92d20bd59ed30ac53248199064f53fc85
1 /*
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/>.
22 #include "includes.h"
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"
35 #include "dsdb/common/util.h"
38 load the partitions list based on replicated NC attributes in our
39 NTDSDSA object
41 WERROR dreplsrv_load_partitions(struct dreplsrv_service *s)
43 WERROR status;
44 static const char *attrs[] = { "hasMasterNCs", "msDS-hasMasterNCs", "hasPartialReplicaNCs", "msDS-HasFullReplicaNCs", NULL };
45 unsigned int a;
46 int ret;
47 TALLOC_CTX *tmp_ctx;
48 struct ldb_result *res;
49 struct ldb_message_element *el;
50 struct ldb_dn *ntds_dn;
52 tmp_ctx = talloc_new(s);
53 W_ERROR_HAVE_NO_MEMORY(tmp_ctx);
55 ntds_dn = samdb_ntds_settings_dn(s->samdb);
56 if (!ntds_dn) {
57 DEBUG(1,(__location__ ": Unable to find ntds_dn: %s\n", ldb_errstring(s->samdb)));
58 talloc_free(tmp_ctx);
59 return WERR_DS_DRA_INTERNAL_ERROR;
62 ret = dsdb_search_dn(s->samdb, tmp_ctx, &res, ntds_dn, attrs, DSDB_SEARCH_SHOW_EXTENDED_DN);
63 if (ret != LDB_SUCCESS) {
64 DEBUG(1,("Searching for hasMasterNCs in NTDS DN failed: %s\n", ldb_errstring(s->samdb)));
65 talloc_free(tmp_ctx);
66 return WERR_DS_DRA_INTERNAL_ERROR;
69 for (a=0; attrs[a]; a++) {
70 int i;
72 el = ldb_msg_find_element(res->msgs[0], attrs[a]);
73 if (el == NULL) {
74 continue;
76 for (i=0; i<el->num_values; i++) {
77 struct ldb_dn *pdn;
78 struct dreplsrv_partition *p, *tp;
79 bool found;
81 pdn = ldb_dn_from_ldb_val(tmp_ctx, s->samdb, &el->values[i]);
82 if (pdn == NULL) {
83 talloc_free(tmp_ctx);
84 return WERR_DS_DRA_INTERNAL_ERROR;
86 if (!ldb_dn_validate(pdn)) {
87 return WERR_DS_DRA_INTERNAL_ERROR;
90 p = talloc_zero(s, struct dreplsrv_partition);
91 W_ERROR_HAVE_NO_MEMORY(p);
93 p->dn = talloc_steal(p, pdn);
94 p->service = s;
96 if (strcasecmp(attrs[a], "hasPartialReplicaNCs") == 0) {
97 p->partial_replica = true;
98 } else if (strcasecmp(attrs[a], "msDS-HasFullReplicaNCs") == 0) {
99 p->rodc_replica = true;
102 /* Do not add partitions more than once */
103 found = false;
104 for (tp = s->partitions; tp; tp = tp->next) {
105 if (ldb_dn_compare(tp->dn, p->dn) == 0) {
106 found = true;
107 break;
110 if (found) {
111 talloc_free(p);
112 continue;
115 DLIST_ADD(s->partitions, p);
116 DEBUG(2, ("dreplsrv_partition[%s] loaded\n", ldb_dn_get_linearized(p->dn)));
120 talloc_free(tmp_ctx);
122 status = dreplsrv_refresh_partitions(s);
123 W_ERROR_NOT_OK_RETURN(status);
125 return WERR_OK;
129 Check if particular SPN exists for an account
131 static bool dreplsrv_spn_exists(struct ldb_context *samdb, struct ldb_dn *ntds_dn,
132 const char *principal_name)
134 TALLOC_CTX *tmp_ctx;
135 const char *attrs[] = { "serverReference", NULL };
136 const char *attrs_empty[] = { NULL };
137 int ret;
138 struct ldb_result *res;
139 struct ldb_dn *account_dn;
141 tmp_ctx = talloc_new(samdb);
143 ret = dsdb_search_dn(samdb, tmp_ctx, &res, ntds_dn, attrs, 0);
144 if (ret != LDB_SUCCESS) {
145 talloc_free(tmp_ctx);
146 return false;
149 account_dn = ldb_msg_find_attr_as_dn(samdb, tmp_ctx, res->msgs[0], "serverReference");
150 if (account_dn == NULL) {
151 talloc_free(tmp_ctx);
152 return false;
155 talloc_free(res);
157 ret = dsdb_search(samdb, tmp_ctx, &res, account_dn, LDB_SCOPE_BASE, attrs_empty,
158 0, "servicePrincipalName=%s",
159 ldb_binary_encode_string(tmp_ctx, principal_name));
160 if (ret != LDB_SUCCESS || res->count != 1) {
161 talloc_free(tmp_ctx);
162 return false;
165 talloc_free(tmp_ctx);
166 return true;
170 work out the principal to use for DRS replication connections
172 NTSTATUS dreplsrv_get_target_principal(struct dreplsrv_service *s,
173 TALLOC_CTX *mem_ctx,
174 const struct repsFromTo1 *rft,
175 const char **target_principal)
177 TALLOC_CTX *tmp_ctx;
178 struct ldb_result *res;
179 const char *attrs_server[] = { "dNSHostName", NULL };
180 const char *attrs_ntds[] = { "msDS-HasDomainNCs", "hasMasterNCs", NULL };
181 int ret;
182 const char *hostname, *dnsdomain=NULL;
183 struct ldb_dn *ntds_dn, *server_dn;
184 struct ldb_dn *forest_dn, *nc_dn;
186 *target_principal = NULL;
188 tmp_ctx = talloc_new(mem_ctx);
190 /* we need to find their hostname */
191 ret = dsdb_find_dn_by_guid(s->samdb, tmp_ctx, &rft->source_dsa_obj_guid, &ntds_dn);
192 if (ret != LDB_SUCCESS) {
193 talloc_free(tmp_ctx);
194 /* its OK for their NTDSDSA DN not to be in our database */
195 return NT_STATUS_OK;
198 server_dn = ldb_dn_copy(tmp_ctx, ntds_dn);
199 if (server_dn == NULL) {
200 talloc_free(tmp_ctx);
201 return NT_STATUS_OK;
204 /* strip off the NTDS Settings */
205 if (!ldb_dn_remove_child_components(server_dn, 1)) {
206 talloc_free(tmp_ctx);
207 return NT_STATUS_OK;
210 ret = dsdb_search_dn(s->samdb, tmp_ctx, &res, server_dn, attrs_server, 0);
211 if (ret != LDB_SUCCESS) {
212 talloc_free(tmp_ctx);
213 /* its OK for their server DN not to be in our database */
214 return NT_STATUS_OK;
217 forest_dn = ldb_get_root_basedn(s->samdb);
218 if (forest_dn == NULL) {
219 talloc_free(tmp_ctx);
220 return NT_STATUS_OK;
223 hostname = ldb_msg_find_attr_as_string(res->msgs[0], "dNSHostName", NULL);
224 if (hostname != 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 (dreplsrv_spn_exists(s->samdb, ntds_dn, local_principal)) {
238 *target_principal = local_principal;
239 talloc_free(tmp_ctx);
240 return NT_STATUS_OK;
243 talloc_free(local_principal);
247 if we can't find the dNSHostName then we will try for the
248 E3514235-4B06-11D1-AB04-00C04FC2DCD2/${NTDSGUID}/${DNSDOMAIN}
249 SPN. To use that we need the DNS domain name of the target
250 DC. We find that by first looking for the msDS-HasDomainNCs
251 in the NTDSDSA object of the DC, and if we don't find that,
252 then we look for the hasMasterNCs attribute, and eliminate
253 the known schema and configuruation DNs. Despite how
254 bizarre this seems, Hongwei tells us that this is in fact
255 what windows does to find the SPN!!
257 ret = dsdb_search_dn(s->samdb, tmp_ctx, &res, ntds_dn, attrs_ntds, 0);
258 if (ret != LDB_SUCCESS) {
259 talloc_free(tmp_ctx);
260 return NT_STATUS_OK;
263 nc_dn = ldb_msg_find_attr_as_dn(s->samdb, tmp_ctx, res->msgs[0], "msDS-HasDomainNCs");
264 if (nc_dn != NULL) {
265 dnsdomain = samdb_dn_to_dns_domain(tmp_ctx, nc_dn);
268 if (dnsdomain == NULL) {
269 struct ldb_message_element *el;
270 int i;
271 el = ldb_msg_find_element(res->msgs[0], "hasMasterNCs");
272 for (i=0; el && i<el->num_values; i++) {
273 nc_dn = ldb_dn_from_ldb_val(tmp_ctx, s->samdb, &el->values[i]);
274 if (nc_dn == NULL ||
275 ldb_dn_compare(ldb_get_config_basedn(s->samdb), nc_dn) == 0 ||
276 ldb_dn_compare(ldb_get_schema_basedn(s->samdb), nc_dn) == 0) {
277 continue;
279 /* it must be a domain DN, get the equivalent
280 DNS domain name */
281 dnsdomain = samdb_dn_to_dns_domain(tmp_ctx, nc_dn);
282 break;
286 if (dnsdomain != NULL) {
287 *target_principal = talloc_asprintf(mem_ctx,
288 "E3514235-4B06-11D1-AB04-00C04FC2DCD2/%s/%s",
289 GUID_string(tmp_ctx, &rft->source_dsa_obj_guid),
290 dnsdomain);
293 talloc_free(tmp_ctx);
294 return NT_STATUS_OK;
298 WERROR dreplsrv_out_connection_attach(struct dreplsrv_service *s,
299 const struct repsFromTo1 *rft,
300 struct dreplsrv_out_connection **_conn)
302 struct dreplsrv_out_connection *cur, *conn = NULL;
303 const char *hostname;
305 if (!rft->other_info) {
306 return WERR_FOOBAR;
309 if (!rft->other_info->dns_name) {
310 return WERR_FOOBAR;
313 hostname = rft->other_info->dns_name;
315 for (cur = s->connections; cur; cur = cur->next) {
316 if (strcmp(cur->binding->host, hostname) == 0) {
317 conn = cur;
318 break;
322 if (!conn) {
323 NTSTATUS nt_status;
324 char *binding_str;
326 conn = talloc_zero(s, struct dreplsrv_out_connection);
327 W_ERROR_HAVE_NO_MEMORY(conn);
329 conn->service = s;
331 binding_str = talloc_asprintf(conn, "ncacn_ip_tcp:%s[krb5,seal]",
332 hostname);
333 W_ERROR_HAVE_NO_MEMORY(binding_str);
334 nt_status = dcerpc_parse_binding(conn, binding_str, &conn->binding);
335 talloc_free(binding_str);
336 if (!NT_STATUS_IS_OK(nt_status)) {
337 return ntstatus_to_werror(nt_status);
340 /* use the GC principal for DRS replication */
341 nt_status = dreplsrv_get_target_principal(s, conn->binding,
342 rft, &conn->binding->target_principal);
343 if (!NT_STATUS_IS_OK(nt_status)) {
344 return ntstatus_to_werror(nt_status);
347 DLIST_ADD_END(s->connections, conn, struct dreplsrv_out_connection *);
349 DEBUG(4,("dreplsrv_out_connection_attach(%s): create\n", conn->binding->host));
350 } else {
351 DEBUG(4,("dreplsrv_out_connection_attach(%s): attach\n", conn->binding->host));
354 *_conn = conn;
355 return WERR_OK;
359 find an existing source dsa in a list
361 static struct dreplsrv_partition_source_dsa *dreplsrv_find_source_dsa(struct dreplsrv_partition_source_dsa *list,
362 struct GUID *guid)
364 struct dreplsrv_partition_source_dsa *s;
365 for (s=list; s; s=s->next) {
366 if (GUID_compare(&s->repsFrom1->source_dsa_obj_guid, guid) == 0) {
367 return s;
370 return NULL;
375 static WERROR dreplsrv_partition_add_source_dsa(struct dreplsrv_service *s,
376 struct dreplsrv_partition *p,
377 struct dreplsrv_partition_source_dsa **listp,
378 struct dreplsrv_partition_source_dsa *check_list,
379 const struct ldb_val *val)
381 WERROR status;
382 enum ndr_err_code ndr_err;
383 struct dreplsrv_partition_source_dsa *source, *s2;
385 source = talloc_zero(p, struct dreplsrv_partition_source_dsa);
386 W_ERROR_HAVE_NO_MEMORY(source);
388 ndr_err = ndr_pull_struct_blob(val, source,
389 &source->_repsFromBlob,
390 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
391 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
392 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
393 talloc_free(source);
394 return ntstatus_to_werror(nt_status);
396 /* NDR_PRINT_DEBUG(repsFromToBlob, &source->_repsFromBlob); */
397 if (source->_repsFromBlob.version != 1) {
398 talloc_free(source);
399 return WERR_DS_DRA_INTERNAL_ERROR;
402 source->partition = p;
403 source->repsFrom1 = &source->_repsFromBlob.ctr.ctr1;
405 status = dreplsrv_out_connection_attach(s, source->repsFrom1, &source->conn);
406 W_ERROR_NOT_OK_RETURN(status);
408 if (check_list &&
409 dreplsrv_find_source_dsa(check_list, &source->repsFrom1->source_dsa_obj_guid)) {
410 /* its in the check list, don't add it again */
411 talloc_free(source);
412 return WERR_OK;
415 /* re-use an existing source if found */
416 for (s2=*listp; s2; s2=s2->next) {
417 if (GUID_compare(&s2->repsFrom1->source_dsa_obj_guid,
418 &source->repsFrom1->source_dsa_obj_guid) == 0) {
419 talloc_free(s2->repsFrom1->other_info);
420 *s2->repsFrom1 = *source->repsFrom1;
421 talloc_steal(s2, s2->repsFrom1->other_info);
422 talloc_free(source);
423 return WERR_OK;
427 DLIST_ADD_END(*listp, source, struct dreplsrv_partition_source_dsa *);
428 return WERR_OK;
431 WERROR dreplsrv_partition_find_for_nc(struct dreplsrv_service *s,
432 struct GUID *nc_guid,
433 struct dom_sid *nc_sid,
434 const char *nc_dn_str,
435 struct dreplsrv_partition **_p)
437 struct dreplsrv_partition *p;
438 bool valid_sid, valid_guid;
439 struct dom_sid null_sid;
440 ZERO_STRUCT(null_sid);
442 SMB_ASSERT(_p);
444 valid_sid = nc_sid && !dom_sid_equal(&null_sid, nc_sid);
445 valid_guid = nc_guid && !GUID_all_zero(nc_guid);
447 if (!valid_sid && !valid_guid && !nc_dn_str) {
448 return WERR_DS_DRA_INVALID_PARAMETER;
451 for (p = s->partitions; p; p = p->next) {
452 if ((valid_guid && GUID_equal(&p->nc.guid, nc_guid))
453 || strequal(p->nc.dn, nc_dn_str)
454 || (valid_sid && dom_sid_equal(&p->nc.sid, nc_sid)))
456 /* fill in he right guid and sid if possible */
457 if (nc_guid && !valid_guid) {
458 dsdb_get_extended_dn_guid(p->dn, nc_guid, "GUID");
460 if (nc_sid && !valid_sid) {
461 dsdb_get_extended_dn_sid(p->dn, nc_sid, "SID");
463 *_p = p;
464 return WERR_OK;
468 return WERR_DS_DRA_BAD_NC;
471 WERROR dreplsrv_partition_source_dsa_by_guid(struct dreplsrv_partition *p,
472 const struct GUID *dsa_guid,
473 struct dreplsrv_partition_source_dsa **_dsa)
475 struct dreplsrv_partition_source_dsa *dsa;
477 SMB_ASSERT(dsa_guid != NULL);
478 SMB_ASSERT(!GUID_all_zero(dsa_guid));
479 SMB_ASSERT(_dsa);
481 for (dsa = p->sources; dsa; dsa = dsa->next) {
482 if (GUID_equal(dsa_guid, &dsa->repsFrom1->source_dsa_obj_guid)) {
483 *_dsa = dsa;
484 return WERR_OK;
488 return WERR_DS_DRA_NO_REPLICA;
491 WERROR dreplsrv_partition_source_dsa_by_dns(const struct dreplsrv_partition *p,
492 const char *dsa_dns,
493 struct dreplsrv_partition_source_dsa **_dsa)
495 struct dreplsrv_partition_source_dsa *dsa;
497 SMB_ASSERT(dsa_dns != NULL);
498 SMB_ASSERT(_dsa);
500 for (dsa = p->sources; dsa; dsa = dsa->next) {
501 if (strequal(dsa_dns, dsa->repsFrom1->other_info->dns_name)) {
502 *_dsa = dsa;
503 return WERR_OK;
507 return WERR_DS_DRA_NO_REPLICA;
512 create a temporary dsa structure for a replication. This is needed
513 for the initial replication of a new partition, such as when a new
514 domain NC is created and we are a global catalog server
516 WERROR dreplsrv_partition_source_dsa_temporary(struct dreplsrv_partition *p,
517 TALLOC_CTX *mem_ctx,
518 const struct GUID *dsa_guid,
519 struct dreplsrv_partition_source_dsa **_dsa)
521 struct dreplsrv_partition_source_dsa *dsa;
522 WERROR werr;
524 dsa = talloc_zero(mem_ctx, struct dreplsrv_partition_source_dsa);
525 W_ERROR_HAVE_NO_MEMORY(dsa);
527 dsa->partition = p;
528 dsa->repsFrom1 = &dsa->_repsFromBlob.ctr.ctr1;
529 dsa->repsFrom1->replica_flags = 0;
530 dsa->repsFrom1->source_dsa_obj_guid = *dsa_guid;
532 dsa->repsFrom1->other_info = talloc_zero(dsa, struct repsFromTo1OtherInfo);
533 W_ERROR_HAVE_NO_MEMORY(dsa->repsFrom1->other_info);
535 dsa->repsFrom1->other_info->dns_name = samdb_ntds_msdcs_dns_name(p->service->samdb,
536 dsa->repsFrom1->other_info, dsa_guid);
537 W_ERROR_HAVE_NO_MEMORY(dsa->repsFrom1->other_info->dns_name);
539 werr = dreplsrv_out_connection_attach(p->service, dsa->repsFrom1, &dsa->conn);
540 if (!W_ERROR_IS_OK(werr)) {
541 DEBUG(0,(__location__ ": Failed to attach connection to %s\n",
542 ldb_dn_get_linearized(p->dn)));
543 talloc_free(dsa);
544 return werr;
547 *_dsa = dsa;
549 return WERR_OK;
553 static WERROR dreplsrv_refresh_partition(struct dreplsrv_service *s,
554 struct dreplsrv_partition *p)
556 WERROR status;
557 NTSTATUS ntstatus;
558 struct ldb_message_element *orf_el = NULL;
559 struct ldb_result *r = NULL;
560 unsigned int i;
561 int ret;
562 TALLOC_CTX *mem_ctx = talloc_new(p);
563 static const char *attrs[] = {
564 "repsFrom",
565 "repsTo",
566 NULL
568 struct ldb_dn *dn;
570 DEBUG(4, ("dreplsrv_refresh_partition(%s)\n",
571 ldb_dn_get_linearized(p->dn)));
573 ret = dsdb_search_dn(s->samdb, mem_ctx, &r, p->dn, attrs, DSDB_SEARCH_SHOW_EXTENDED_DN);
574 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
575 /* we haven't replicated the partition yet, but we
576 * can fill in the guid, sid etc from the partition DN */
577 dn = p->dn;
578 } else if (ret != LDB_SUCCESS) {
579 talloc_free(mem_ctx);
580 return WERR_FOOBAR;
581 } else {
582 dn = r->msgs[0]->dn;
585 talloc_free(discard_const(p->nc.dn));
586 ZERO_STRUCT(p->nc);
587 p->nc.dn = ldb_dn_alloc_linearized(p, dn);
588 W_ERROR_HAVE_NO_MEMORY(p->nc.dn);
589 ntstatus = dsdb_get_extended_dn_guid(dn, &p->nc.guid, "GUID");
590 if (!NT_STATUS_IS_OK(ntstatus)) {
591 DEBUG(0,(__location__ ": unable to get GUID for %s: %s\n",
592 p->nc.dn, nt_errstr(ntstatus)));
593 talloc_free(mem_ctx);
594 return WERR_DS_DRA_INTERNAL_ERROR;
596 dsdb_get_extended_dn_sid(dn, &p->nc.sid, "SID");
598 talloc_free(p->uptodatevector.cursors);
599 talloc_free(p->uptodatevector_ex.cursors);
600 ZERO_STRUCT(p->uptodatevector);
601 ZERO_STRUCT(p->uptodatevector_ex);
603 ret = dsdb_load_udv_v2(s->samdb, p->dn, p, &p->uptodatevector.cursors, &p->uptodatevector.count);
604 if (ret != LDB_SUCCESS) {
605 DEBUG(4,(__location__ ": no UDV available for %s\n", ldb_dn_get_linearized(p->dn)));
608 status = WERR_OK;
610 if (r != NULL && (orf_el = ldb_msg_find_element(r->msgs[0], "repsFrom"))) {
611 for (i=0; i < orf_el->num_values; i++) {
612 status = dreplsrv_partition_add_source_dsa(s, p, &p->sources,
613 NULL, &orf_el->values[i]);
614 W_ERROR_NOT_OK_GOTO_DONE(status);
618 if (r != NULL && (orf_el = ldb_msg_find_element(r->msgs[0], "repsTo"))) {
619 for (i=0; i < orf_el->num_values; i++) {
620 status = dreplsrv_partition_add_source_dsa(s, p, &p->notifies,
621 p->sources, &orf_el->values[i]);
622 W_ERROR_NOT_OK_GOTO_DONE(status);
626 done:
627 talloc_free(mem_ctx);
628 return status;
631 WERROR dreplsrv_refresh_partitions(struct dreplsrv_service *s)
633 WERROR status;
634 struct dreplsrv_partition *p;
636 for (p = s->partitions; p; p = p->next) {
637 status = dreplsrv_refresh_partition(s, p);
638 W_ERROR_NOT_OK_RETURN(status);
641 return WERR_OK;