s4-winbind: Use winbindd in the AD DC for fl2003dc and plugin_s4_dc
[Samba/wip.git] / source4 / dsdb / repl / drepl_partitions.c
blob8c85ef6f2588d04394754aa5c353b89cde3585c9
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, tmp_ctx);
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 *account_dn,
132 const char *principal_name)
134 TALLOC_CTX *tmp_ctx;
135 const char *attrs_empty[] = { NULL };
136 int ret;
137 struct ldb_result *res;
139 tmp_ctx = talloc_new(samdb);
141 ret = dsdb_search(samdb, tmp_ctx, &res, account_dn, LDB_SCOPE_BASE, attrs_empty,
142 0, "servicePrincipalName=%s",
143 ldb_binary_encode_string(tmp_ctx, principal_name));
144 if (ret != LDB_SUCCESS || res->count != 1) {
145 talloc_free(tmp_ctx);
146 return false;
149 talloc_free(tmp_ctx);
150 return true;
154 work out the principal to use for DRS replication connections
156 static NTSTATUS dreplsrv_get_target_principal(struct dreplsrv_service *s,
157 TALLOC_CTX *mem_ctx,
158 const struct repsFromTo1 *rft,
159 char **target_principal)
161 TALLOC_CTX *tmp_ctx;
162 struct ldb_result *res;
163 const char *attrs_server[] = { "dNSHostName", "serverReference", NULL };
164 const char *attrs_ntds[] = { "msDS-HasDomainNCs", "hasMasterNCs", NULL };
165 int ret;
166 const char *hostname, *dnsdomain=NULL;
167 struct ldb_dn *ntds_dn, *server_dn, *computer_dn;
168 struct ldb_dn *forest_dn, *nc_dn;
170 *target_principal = NULL;
172 tmp_ctx = talloc_new(mem_ctx);
174 /* we need to find their hostname */
175 ret = dsdb_find_dn_by_guid(s->samdb, tmp_ctx, &rft->source_dsa_obj_guid, 0, &ntds_dn);
176 if (ret != LDB_SUCCESS) {
177 talloc_free(tmp_ctx);
178 /* its OK for their NTDSDSA DN not to be in our database */
179 return NT_STATUS_OK;
182 server_dn = ldb_dn_copy(tmp_ctx, ntds_dn);
183 if (server_dn == NULL) {
184 talloc_free(tmp_ctx);
185 return NT_STATUS_OK;
188 /* strip off the NTDS Settings */
189 if (!ldb_dn_remove_child_components(server_dn, 1)) {
190 talloc_free(tmp_ctx);
191 return NT_STATUS_OK;
194 ret = dsdb_search_dn(s->samdb, tmp_ctx, &res, server_dn, attrs_server, 0);
195 if (ret != LDB_SUCCESS) {
196 talloc_free(tmp_ctx);
197 /* its OK for their server DN not to be in our database */
198 return NT_STATUS_OK;
201 forest_dn = ldb_get_root_basedn(s->samdb);
202 if (forest_dn == NULL) {
203 talloc_free(tmp_ctx);
204 return NT_STATUS_OK;
207 hostname = ldb_msg_find_attr_as_string(res->msgs[0], "dNSHostName", NULL);
208 computer_dn = ldb_msg_find_attr_as_dn(s->samdb, tmp_ctx, res->msgs[0], "serverReference");
209 if (hostname != NULL && computer_dn != NULL) {
210 char *local_principal;
213 if we have the dNSHostName attribute then we can use
214 the GC/hostname/realm SPN. All DCs should have this SPN
216 Windows DC may set up it's dNSHostName before setting up
217 GC/xx/xx SPN. So make sure it exists, before using it.
219 local_principal = talloc_asprintf(mem_ctx, "GC/%s/%s",
220 hostname,
221 samdb_dn_to_dns_domain(tmp_ctx, forest_dn));
222 if (dreplsrv_spn_exists(s->samdb, computer_dn, local_principal)) {
223 *target_principal = local_principal;
224 talloc_free(tmp_ctx);
225 return NT_STATUS_OK;
228 talloc_free(local_principal);
232 if we can't find the dNSHostName then we will try for the
233 E3514235-4B06-11D1-AB04-00C04FC2DCD2/${NTDSGUID}/${DNSDOMAIN}
234 SPN. To use that we need the DNS domain name of the target
235 DC. We find that by first looking for the msDS-HasDomainNCs
236 in the NTDSDSA object of the DC, and if we don't find that,
237 then we look for the hasMasterNCs attribute, and eliminate
238 the known schema and configuruation DNs. Despite how
239 bizarre this seems, Hongwei tells us that this is in fact
240 what windows does to find the SPN!!
242 ret = dsdb_search_dn(s->samdb, tmp_ctx, &res, ntds_dn, attrs_ntds, 0);
243 if (ret != LDB_SUCCESS) {
244 talloc_free(tmp_ctx);
245 return NT_STATUS_OK;
248 nc_dn = ldb_msg_find_attr_as_dn(s->samdb, tmp_ctx, res->msgs[0], "msDS-HasDomainNCs");
249 if (nc_dn != NULL) {
250 dnsdomain = samdb_dn_to_dns_domain(tmp_ctx, nc_dn);
253 if (dnsdomain == NULL) {
254 struct ldb_message_element *el;
255 int i;
256 el = ldb_msg_find_element(res->msgs[0], "hasMasterNCs");
257 for (i=0; el && i<el->num_values; i++) {
258 nc_dn = ldb_dn_from_ldb_val(tmp_ctx, s->samdb, &el->values[i]);
259 if (nc_dn == NULL ||
260 ldb_dn_compare(ldb_get_config_basedn(s->samdb), nc_dn) == 0 ||
261 ldb_dn_compare(ldb_get_schema_basedn(s->samdb), nc_dn) == 0) {
262 continue;
264 /* it must be a domain DN, get the equivalent
265 DNS domain name */
266 dnsdomain = samdb_dn_to_dns_domain(tmp_ctx, nc_dn);
267 break;
271 if (dnsdomain != NULL) {
272 *target_principal = talloc_asprintf(mem_ctx,
273 "E3514235-4B06-11D1-AB04-00C04FC2DCD2/%s/%s",
274 GUID_string(tmp_ctx, &rft->source_dsa_obj_guid),
275 dnsdomain);
278 talloc_free(tmp_ctx);
279 return NT_STATUS_OK;
283 WERROR dreplsrv_out_connection_attach(struct dreplsrv_service *s,
284 const struct repsFromTo1 *rft,
285 struct dreplsrv_out_connection **_conn)
287 struct dreplsrv_out_connection *cur, *conn = NULL;
288 const char *hostname;
290 if (!rft->other_info) {
291 return WERR_FOOBAR;
294 if (!rft->other_info->dns_name) {
295 return WERR_FOOBAR;
298 hostname = rft->other_info->dns_name;
300 for (cur = s->connections; cur; cur = cur->next) {
301 const char *host;
303 host = dcerpc_binding_get_string_option(cur->binding, "host");
304 if (host == NULL) {
305 continue;
308 if (strcmp(host, hostname) == 0) {
309 conn = cur;
310 break;
314 if (!conn) {
315 NTSTATUS nt_status;
316 char *binding_str;
317 char *target_principal = NULL;
319 conn = talloc_zero(s, struct dreplsrv_out_connection);
320 W_ERROR_HAVE_NO_MEMORY(conn);
322 conn->service = s;
324 binding_str = talloc_asprintf(conn, "ncacn_ip_tcp:%s[krb5,seal]",
325 hostname);
326 W_ERROR_HAVE_NO_MEMORY(binding_str);
327 nt_status = dcerpc_parse_binding(conn, binding_str, &conn->binding);
328 talloc_free(binding_str);
329 if (!NT_STATUS_IS_OK(nt_status)) {
330 return ntstatus_to_werror(nt_status);
333 /* use the GC principal for DRS replication */
334 nt_status = dreplsrv_get_target_principal(s, conn->binding,
335 rft, &target_principal);
336 if (!NT_STATUS_IS_OK(nt_status)) {
337 return ntstatus_to_werror(nt_status);
340 nt_status = dcerpc_binding_set_string_option(conn->binding,
341 "target_principal",
342 target_principal);
343 TALLOC_FREE(target_principal);
344 if (!NT_STATUS_IS_OK(nt_status)) {
345 return ntstatus_to_werror(nt_status);
348 DLIST_ADD_END(s->connections, conn, struct dreplsrv_out_connection *);
350 DEBUG(4,("dreplsrv_out_connection_attach(%s): create\n", hostname));
351 } else {
352 DEBUG(4,("dreplsrv_out_connection_attach(%s): attach\n", hostname));
355 *_conn = conn;
356 return WERR_OK;
360 find an existing source dsa in a list
362 static struct dreplsrv_partition_source_dsa *dreplsrv_find_source_dsa(struct dreplsrv_partition_source_dsa *list,
363 struct GUID *guid)
365 struct dreplsrv_partition_source_dsa *s;
366 for (s=list; s; s=s->next) {
367 if (GUID_equal(&s->repsFrom1->source_dsa_obj_guid, guid)) {
368 return s;
371 return NULL;
376 static WERROR dreplsrv_partition_add_source_dsa(struct dreplsrv_service *s,
377 struct dreplsrv_partition *p,
378 struct dreplsrv_partition_source_dsa **listp,
379 struct dreplsrv_partition_source_dsa *check_list,
380 const struct ldb_val *val)
382 WERROR status;
383 enum ndr_err_code ndr_err;
384 struct dreplsrv_partition_source_dsa *source, *s2;
386 source = talloc_zero(p, struct dreplsrv_partition_source_dsa);
387 W_ERROR_HAVE_NO_MEMORY(source);
389 ndr_err = ndr_pull_struct_blob(val, source,
390 &source->_repsFromBlob,
391 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
392 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
393 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
394 talloc_free(source);
395 return ntstatus_to_werror(nt_status);
397 /* NDR_PRINT_DEBUG(repsFromToBlob, &source->_repsFromBlob); */
398 if (source->_repsFromBlob.version != 1) {
399 talloc_free(source);
400 return WERR_DS_DRA_INTERNAL_ERROR;
403 source->partition = p;
404 source->repsFrom1 = &source->_repsFromBlob.ctr.ctr1;
406 status = dreplsrv_out_connection_attach(s, source->repsFrom1, &source->conn);
407 W_ERROR_NOT_OK_RETURN(status);
409 if (check_list &&
410 dreplsrv_find_source_dsa(check_list, &source->repsFrom1->source_dsa_obj_guid)) {
411 /* its in the check list, don't add it again */
412 talloc_free(source);
413 return WERR_OK;
416 /* re-use an existing source if found */
417 for (s2=*listp; s2; s2=s2->next) {
418 if (GUID_equal(&s2->repsFrom1->source_dsa_obj_guid,
419 &source->repsFrom1->source_dsa_obj_guid)) {
420 talloc_free(s2->repsFrom1->other_info);
421 *s2->repsFrom1 = *source->repsFrom1;
422 talloc_steal(s2, s2->repsFrom1->other_info);
423 talloc_free(source);
424 return WERR_OK;
428 DLIST_ADD_END(*listp, source, struct dreplsrv_partition_source_dsa *);
429 return WERR_OK;
433 * Find a partition when given a NC
434 * If the NC can't be found it will return BAD_NC
435 * Initial checks for invalid parameters have to be done beforehand
437 WERROR dreplsrv_partition_find_for_nc(struct dreplsrv_service *s,
438 struct GUID *nc_guid,
439 struct dom_sid *nc_sid,
440 const char *nc_dn_str,
441 struct dreplsrv_partition **_p)
443 struct dreplsrv_partition *p;
444 bool valid_sid, valid_guid;
445 struct dom_sid null_sid;
446 ZERO_STRUCT(null_sid);
448 SMB_ASSERT(_p);
450 valid_sid = nc_sid && !dom_sid_equal(&null_sid, nc_sid);
451 valid_guid = nc_guid && !GUID_all_zero(nc_guid);
453 if (!valid_sid && !valid_guid && (!nc_dn_str)) {
454 return WERR_DS_DRA_BAD_NC;
457 for (p = s->partitions; p; p = p->next) {
458 if ((valid_guid && GUID_equal(&p->nc.guid, nc_guid))
459 || strequal(p->nc.dn, nc_dn_str)
460 || (valid_sid && dom_sid_equal(&p->nc.sid, nc_sid)))
462 /* fill in he right guid and sid if possible */
463 if (nc_guid && !valid_guid) {
464 dsdb_get_extended_dn_guid(p->dn, nc_guid, "GUID");
466 if (nc_sid && !valid_sid) {
467 dsdb_get_extended_dn_sid(p->dn, nc_sid, "SID");
469 *_p = p;
470 return WERR_OK;
474 return WERR_DS_DRA_BAD_NC;
477 WERROR dreplsrv_partition_source_dsa_by_guid(struct dreplsrv_partition *p,
478 const struct GUID *dsa_guid,
479 struct dreplsrv_partition_source_dsa **_dsa)
481 struct dreplsrv_partition_source_dsa *dsa;
483 SMB_ASSERT(dsa_guid != NULL);
484 SMB_ASSERT(!GUID_all_zero(dsa_guid));
485 SMB_ASSERT(_dsa);
487 for (dsa = p->sources; dsa; dsa = dsa->next) {
488 if (GUID_equal(dsa_guid, &dsa->repsFrom1->source_dsa_obj_guid)) {
489 *_dsa = dsa;
490 return WERR_OK;
494 return WERR_DS_DRA_NO_REPLICA;
497 WERROR dreplsrv_partition_source_dsa_by_dns(const struct dreplsrv_partition *p,
498 const char *dsa_dns,
499 struct dreplsrv_partition_source_dsa **_dsa)
501 struct dreplsrv_partition_source_dsa *dsa;
503 SMB_ASSERT(dsa_dns != NULL);
504 SMB_ASSERT(_dsa);
506 for (dsa = p->sources; dsa; dsa = dsa->next) {
507 if (strequal(dsa_dns, dsa->repsFrom1->other_info->dns_name)) {
508 *_dsa = dsa;
509 return WERR_OK;
513 return WERR_DS_DRA_NO_REPLICA;
518 create a temporary dsa structure for a replication. This is needed
519 for the initial replication of a new partition, such as when a new
520 domain NC is created and we are a global catalog server
522 WERROR dreplsrv_partition_source_dsa_temporary(struct dreplsrv_partition *p,
523 TALLOC_CTX *mem_ctx,
524 const struct GUID *dsa_guid,
525 struct dreplsrv_partition_source_dsa **_dsa)
527 struct dreplsrv_partition_source_dsa *dsa;
528 WERROR werr;
530 dsa = talloc_zero(mem_ctx, struct dreplsrv_partition_source_dsa);
531 W_ERROR_HAVE_NO_MEMORY(dsa);
533 dsa->partition = p;
534 dsa->repsFrom1 = &dsa->_repsFromBlob.ctr.ctr1;
535 dsa->repsFrom1->replica_flags = 0;
536 dsa->repsFrom1->source_dsa_obj_guid = *dsa_guid;
538 dsa->repsFrom1->other_info = talloc_zero(dsa, struct repsFromTo1OtherInfo);
539 W_ERROR_HAVE_NO_MEMORY(dsa->repsFrom1->other_info);
541 dsa->repsFrom1->other_info->dns_name = samdb_ntds_msdcs_dns_name(p->service->samdb,
542 dsa->repsFrom1->other_info, dsa_guid);
543 W_ERROR_HAVE_NO_MEMORY(dsa->repsFrom1->other_info->dns_name);
545 werr = dreplsrv_out_connection_attach(p->service, dsa->repsFrom1, &dsa->conn);
546 if (!W_ERROR_IS_OK(werr)) {
547 DEBUG(0,(__location__ ": Failed to attach connection to %s\n",
548 ldb_dn_get_linearized(p->dn)));
549 talloc_free(dsa);
550 return werr;
553 *_dsa = dsa;
555 return WERR_OK;
559 static WERROR dreplsrv_refresh_partition(struct dreplsrv_service *s,
560 struct dreplsrv_partition *p)
562 WERROR status;
563 NTSTATUS ntstatus;
564 struct ldb_message_element *orf_el = NULL;
565 struct ldb_result *r = NULL;
566 unsigned int i;
567 int ret;
568 TALLOC_CTX *mem_ctx = talloc_new(p);
569 static const char *attrs[] = {
570 "repsFrom",
571 "repsTo",
572 NULL
574 struct ldb_dn *dn;
576 DEBUG(4, ("dreplsrv_refresh_partition(%s)\n",
577 ldb_dn_get_linearized(p->dn)));
579 ret = dsdb_search_dn(s->samdb, mem_ctx, &r, p->dn, attrs, DSDB_SEARCH_SHOW_EXTENDED_DN);
580 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
581 /* we haven't replicated the partition yet, but we
582 * can fill in the guid, sid etc from the partition DN */
583 dn = p->dn;
584 } else if (ret != LDB_SUCCESS) {
585 talloc_free(mem_ctx);
586 return WERR_FOOBAR;
587 } else {
588 dn = r->msgs[0]->dn;
591 talloc_free(discard_const(p->nc.dn));
592 ZERO_STRUCT(p->nc);
593 p->nc.dn = ldb_dn_alloc_linearized(p, dn);
594 W_ERROR_HAVE_NO_MEMORY(p->nc.dn);
595 ntstatus = dsdb_get_extended_dn_guid(dn, &p->nc.guid, "GUID");
596 if (!NT_STATUS_IS_OK(ntstatus)) {
597 DEBUG(0,(__location__ ": unable to get GUID for %s: %s\n",
598 p->nc.dn, nt_errstr(ntstatus)));
599 talloc_free(mem_ctx);
600 return WERR_DS_DRA_INTERNAL_ERROR;
602 dsdb_get_extended_dn_sid(dn, &p->nc.sid, "SID");
604 talloc_free(p->uptodatevector.cursors);
605 talloc_free(p->uptodatevector_ex.cursors);
606 ZERO_STRUCT(p->uptodatevector);
607 ZERO_STRUCT(p->uptodatevector_ex);
609 ret = dsdb_load_udv_v2(s->samdb, p->dn, p, &p->uptodatevector.cursors, &p->uptodatevector.count);
610 if (ret != LDB_SUCCESS) {
611 DEBUG(4,(__location__ ": no UDV available for %s\n", ldb_dn_get_linearized(p->dn)));
614 status = WERR_OK;
616 if (r != NULL && (orf_el = ldb_msg_find_element(r->msgs[0], "repsFrom"))) {
617 for (i=0; i < orf_el->num_values; i++) {
618 status = dreplsrv_partition_add_source_dsa(s, p, &p->sources,
619 NULL, &orf_el->values[i]);
620 W_ERROR_NOT_OK_GOTO_DONE(status);
624 if (r != NULL && (orf_el = ldb_msg_find_element(r->msgs[0], "repsTo"))) {
625 for (i=0; i < orf_el->num_values; i++) {
626 status = dreplsrv_partition_add_source_dsa(s, p, &p->notifies,
627 p->sources, &orf_el->values[i]);
628 W_ERROR_NOT_OK_GOTO_DONE(status);
632 done:
633 talloc_free(mem_ctx);
634 return status;
637 WERROR dreplsrv_refresh_partitions(struct dreplsrv_service *s)
639 WERROR status;
640 struct dreplsrv_partition *p;
642 for (p = s->partitions; p; p = p->next) {
643 status = dreplsrv_refresh_partition(s, p);
644 W_ERROR_NOT_OK_RETURN(status);
647 return WERR_OK;