1 /*
2    Unix SMB/CIFS mplementation.
3    DSDB replication service
4 
5    Copyright (C) Stefan Metzmacher 2007
6 
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.
11 
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.
16 
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/>.
19 
20 */
21 
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"
36 
37 #undef DBGC_CLASS
38 #define DBGC_CLASS            DBGC_DRS_REPL
39 
40 /*
41   load the partitions list based on replicated NC attributes in our
42   NTDSDSA object
43  */
dreplsrv_load_partitions(struct dreplsrv_service * s)44 WERROR dreplsrv_load_partitions(struct dreplsrv_service *s)
45 {
46 	WERROR status;
47 	static const char *attrs[] = { "hasMasterNCs", "msDS-hasMasterNCs", "hasPartialReplicaNCs", "msDS-HasFullReplicaNCs", NULL };
48 	unsigned int a;
49 	int ret;
50 	TALLOC_CTX *tmp_ctx;
51 	struct ldb_result *res;
52 	struct ldb_message_element *el;
53 	struct ldb_dn *ntds_dn;
54 
55 	tmp_ctx = talloc_new(s);
56 	W_ERROR_HAVE_NO_MEMORY(tmp_ctx);
57 
58 	ntds_dn = samdb_ntds_settings_dn(s->samdb, tmp_ctx);
59 	if (!ntds_dn) {
60 		DEBUG(1,(__location__ ": Unable to find ntds_dn: %s\n", ldb_errstring(s->samdb)));
61 		talloc_free(tmp_ctx);
62 		return WERR_DS_DRA_INTERNAL_ERROR;
63 	}
64 
65 	ret = dsdb_search_dn(s->samdb, tmp_ctx, &res, ntds_dn, attrs, DSDB_SEARCH_SHOW_EXTENDED_DN);
66 	if (ret != LDB_SUCCESS) {
67 		DEBUG(1,("Searching for hasMasterNCs in NTDS DN failed: %s\n", ldb_errstring(s->samdb)));
68 		talloc_free(tmp_ctx);
69 		return WERR_DS_DRA_INTERNAL_ERROR;
70 	}
71 
72 	for (a=0; attrs[a]; a++) {
73 		int i;
74 
75 		el = ldb_msg_find_element(res->msgs[0], attrs[a]);
76 		if (el == NULL) {
77 			continue;
78 		}
79 		for (i=0; i<el->num_values; i++) {
80 			struct ldb_dn *pdn;
81 			struct dreplsrv_partition *p, *tp;
82 			bool found;
83 
84 			pdn = ldb_dn_from_ldb_val(tmp_ctx, s->samdb, &el->values[i]);
85 			if (pdn == NULL) {
86 				talloc_free(tmp_ctx);
87 				return WERR_DS_DRA_INTERNAL_ERROR;
88 			}
89 			if (!ldb_dn_validate(pdn)) {
90 				return WERR_DS_DRA_INTERNAL_ERROR;
91 			}
92 
93 			p = talloc_zero(s, struct dreplsrv_partition);
94 			W_ERROR_HAVE_NO_MEMORY(p);
95 
96 			p->dn = talloc_steal(p, pdn);
97 			p->service = s;
98 
99 			if (strcasecmp(attrs[a], "hasPartialReplicaNCs") == 0) {
100 				p->partial_replica = true;
101 			} else if (strcasecmp(attrs[a], "msDS-HasFullReplicaNCs") == 0) {
102 				p->rodc_replica = true;
103 			}
104 
105 			/* Do not add partitions more than once */
106 			found = false;
107 			for (tp = s->partitions; tp; tp = tp->next) {
108 				if (ldb_dn_compare(tp->dn, p->dn) == 0) {
109 					found = true;
110 					break;
111 				}
112 			}
113 			if (found) {
114 				talloc_free(p);
115 				continue;
116 			}
117 
118 			DLIST_ADD(s->partitions, p);
119 			DEBUG(2, ("dreplsrv_partition[%s] loaded\n", ldb_dn_get_linearized(p->dn)));
120 		}
121 	}
122 
123 	talloc_free(tmp_ctx);
124 
125 	status = dreplsrv_refresh_partitions(s);
126 	W_ERROR_NOT_OK_RETURN(status);
127 
128 	return WERR_OK;
129 }
130 
131 /*
132   Check if particular SPN exists for an account
133  */
dreplsrv_spn_exists(struct ldb_context * samdb,struct ldb_dn * account_dn,const char * principal_name)134 static bool dreplsrv_spn_exists(struct ldb_context *samdb, struct ldb_dn *account_dn,
135 				const char *principal_name)
136 {
137 	TALLOC_CTX *tmp_ctx;
138 	const char *attrs_empty[] = { NULL };
139 	int ret;
140 	struct ldb_result *res;
141 
142 	tmp_ctx = talloc_new(samdb);
143 
144 	ret = dsdb_search(samdb, tmp_ctx, &res, account_dn, LDB_SCOPE_BASE, attrs_empty,
145 			0, "servicePrincipalName=%s",
146 			ldb_binary_encode_string(tmp_ctx, principal_name));
147 	if (ret != LDB_SUCCESS || res->count != 1) {
148 		talloc_free(tmp_ctx);
149 		return false;
150 	}
151 
152 	talloc_free(tmp_ctx);
153 	return true;
154 }
155 
156 /*
157   work out the principal to use for DRS replication connections
158  */
dreplsrv_get_target_principal(struct dreplsrv_service * s,TALLOC_CTX * mem_ctx,const struct repsFromTo1 * rft,char ** target_principal)159 static NTSTATUS dreplsrv_get_target_principal(struct dreplsrv_service *s,
160 					      TALLOC_CTX *mem_ctx,
161 					      const struct repsFromTo1 *rft,
162 					      char **target_principal)
163 {
164 	TALLOC_CTX *tmp_ctx;
165 	struct ldb_result *res;
166 	const char *attrs_server[] = { "dNSHostName", "serverReference", NULL };
167 	const char *attrs_ntds[] = { "msDS-HasDomainNCs", "hasMasterNCs", NULL };
168 	int ret;
169 	const char *hostname, *dnsdomain=NULL;
170 	struct ldb_dn *ntds_dn, *server_dn, *computer_dn;
171 	struct ldb_dn *forest_dn, *nc_dn;
172 
173 	*target_principal = NULL;
174 
175 	tmp_ctx = talloc_new(mem_ctx);
176 
177 	/* we need to find their hostname */
178 	ret = dsdb_find_dn_by_guid(s->samdb, tmp_ctx, &rft->source_dsa_obj_guid, 0, &ntds_dn);
179 	if (ret != LDB_SUCCESS) {
180 		talloc_free(tmp_ctx);
181 		/* its OK for their NTDSDSA DN not to be in our database */
182 		return NT_STATUS_OK;
183 	}
184 
185 	server_dn = ldb_dn_copy(tmp_ctx, ntds_dn);
186 	if (server_dn == NULL) {
187 		talloc_free(tmp_ctx);
188 		return NT_STATUS_OK;
189 	}
190 
191 	/* strip off the NTDS Settings */
192 	if (!ldb_dn_remove_child_components(server_dn, 1)) {
193 		talloc_free(tmp_ctx);
194 		return NT_STATUS_OK;
195 	}
196 
197 	ret = dsdb_search_dn(s->samdb, tmp_ctx, &res, server_dn, attrs_server, 0);
198 	if (ret != LDB_SUCCESS) {
199 		talloc_free(tmp_ctx);
200 		/* its OK for their server DN not to be in our database */
201 		return NT_STATUS_OK;
202 	}
203 
204 	forest_dn = ldb_get_root_basedn(s->samdb);
205 	if (forest_dn == NULL) {
206 		talloc_free(tmp_ctx);
207 		return NT_STATUS_OK;
208 	}
209 
210 	hostname = ldb_msg_find_attr_as_string(res->msgs[0], "dNSHostName", NULL);
211 	computer_dn = ldb_msg_find_attr_as_dn(s->samdb, tmp_ctx, res->msgs[0], "serverReference");
212 	if (hostname != NULL && computer_dn != NULL) {
213 		char *local_principal;
214 
215 		/*
216 		  if we have the dNSHostName attribute then we can use
217 		  the GC/hostname/realm SPN. All DCs should have this SPN
218 
219 		  Windows DC may set up it's dNSHostName before setting up
220 		  GC/xx/xx SPN. So make sure it exists, before using it.
221 		 */
222 		local_principal = talloc_asprintf(mem_ctx, "GC/%s/%s",
223 						    hostname,
224 						    samdb_dn_to_dns_domain(tmp_ctx, forest_dn));
225 		if (dreplsrv_spn_exists(s->samdb, computer_dn, local_principal)) {
226 			*target_principal = local_principal;
227 			talloc_free(tmp_ctx);
228 			return NT_STATUS_OK;
229 		}
230 
231 		talloc_free(local_principal);
232 	}
233 
234 	/*
235 	   if we can't find the dNSHostName then we will try for the
236 	   E3514235-4B06-11D1-AB04-00C04FC2DCD2/${NTDSGUID}/${DNSDOMAIN}
237 	   SPN. To use that we need the DNS domain name of the target
238 	   DC. We find that by first looking for the msDS-HasDomainNCs
239 	   in the NTDSDSA object of the DC, and if we don't find that,
240 	   then we look for the hasMasterNCs attribute, and eliminate
241 	   the known schema and configuruation DNs. Despite how
242 	   bizarre this seems, Hongwei tells us that this is in fact
243 	   what windows does to find the SPN!!
244 	*/
245 	ret = dsdb_search_dn(s->samdb, tmp_ctx, &res, ntds_dn, attrs_ntds, 0);
246 	if (ret != LDB_SUCCESS) {
247 		talloc_free(tmp_ctx);
248 		return NT_STATUS_OK;
249 	}
250 
251 	nc_dn = ldb_msg_find_attr_as_dn(s->samdb, tmp_ctx, res->msgs[0], "msDS-HasDomainNCs");
252 	if (nc_dn != NULL) {
253 		dnsdomain = samdb_dn_to_dns_domain(tmp_ctx, nc_dn);
254 	}
255 
256 	if (dnsdomain == NULL) {
257 		struct ldb_message_element *el;
258 		int i;
259 		el = ldb_msg_find_element(res->msgs[0], "hasMasterNCs");
260 		for (i=0; el && i<el->num_values; i++) {
261 			nc_dn = ldb_dn_from_ldb_val(tmp_ctx, s->samdb, &el->values[i]);
262 			if (nc_dn == NULL ||
263 			    ldb_dn_compare(ldb_get_config_basedn(s->samdb), nc_dn) == 0 ||
264 			    ldb_dn_compare(ldb_get_schema_basedn(s->samdb), nc_dn) == 0) {
265 				continue;
266 			}
267 			/* it must be a domain DN, get the equivalent
268 			   DNS domain name */
269 			dnsdomain = samdb_dn_to_dns_domain(tmp_ctx, nc_dn);
270 			break;
271 		}
272 	}
273 
274 	if (dnsdomain != NULL) {
275 		*target_principal = talloc_asprintf(mem_ctx,
276 						    "E3514235-4B06-11D1-AB04-00C04FC2DCD2/%s/%s@%s",
277 						    GUID_string(tmp_ctx, &rft->source_dsa_obj_guid),
278 						    dnsdomain, dnsdomain);
279 	}
280 
281 	talloc_free(tmp_ctx);
282 	return NT_STATUS_OK;
283 }
284 
285 
dreplsrv_out_connection_attach(struct dreplsrv_service * s,const struct repsFromTo1 * rft,struct dreplsrv_out_connection ** _conn)286 WERROR dreplsrv_out_connection_attach(struct dreplsrv_service *s,
287 				      const struct repsFromTo1 *rft,
288 				      struct dreplsrv_out_connection **_conn)
289 {
290 	struct dreplsrv_out_connection *cur, *conn = NULL;
291 	const char *hostname;
292 
293 	if (!rft->other_info) {
294 		return WERR_FOOBAR;
295 	}
296 
297 	if (!rft->other_info->dns_name) {
298 		return WERR_FOOBAR;
299 	}
300 
301 	hostname = rft->other_info->dns_name;
302 
303 	for (cur = s->connections; cur; cur = cur->next) {
304 		const char *host;
305 
306 		host = dcerpc_binding_get_string_option(cur->binding, "host");
307 		if (host == NULL) {
308 			continue;
309 		}
310 
311 		if (strcmp(host, hostname) == 0) {
312 			conn = cur;
313 			break;
314 		}
315 	}
316 
317 	if (!conn) {
318 		NTSTATUS nt_status;
319 		char *binding_str;
320 		char *target_principal = NULL;
321 
322 		conn = talloc_zero(s, struct dreplsrv_out_connection);
323 		W_ERROR_HAVE_NO_MEMORY(conn);
324 
325 		conn->service	= s;
326 
327 		binding_str = talloc_asprintf(conn, "ncacn_ip_tcp:%s[krb5,seal]",
328 					      hostname);
329 		W_ERROR_HAVE_NO_MEMORY(binding_str);
330 		nt_status = dcerpc_parse_binding(conn, binding_str, &conn->binding);
331 		talloc_free(binding_str);
332 		if (!NT_STATUS_IS_OK(nt_status)) {
333 			return ntstatus_to_werror(nt_status);
334 		}
335 
336 		/* use the GC principal for DRS replication */
337 		nt_status = dreplsrv_get_target_principal(s, conn->binding,
338 							  rft, &target_principal);
339 		if (!NT_STATUS_IS_OK(nt_status)) {
340 			return ntstatus_to_werror(nt_status);
341 		}
342 
343 		nt_status = dcerpc_binding_set_string_option(conn->binding,
344 							     "target_principal",
345 							     target_principal);
346 		TALLOC_FREE(target_principal);
347 		if (!NT_STATUS_IS_OK(nt_status)) {
348 			return ntstatus_to_werror(nt_status);
349 		}
350 
351 		DLIST_ADD_END(s->connections, conn);
352 
353 		DEBUG(4,("dreplsrv_out_connection_attach(%s): create\n", hostname));
354 	} else {
355 		DEBUG(4,("dreplsrv_out_connection_attach(%s): attach\n", hostname));
356 	}
357 
358 	*_conn = conn;
359 	return WERR_OK;
360 }
361 
362 /*
363   find an existing source dsa in a list
364  */
dreplsrv_find_source_dsa(struct dreplsrv_partition_source_dsa * list,struct GUID * guid)365 static struct dreplsrv_partition_source_dsa *dreplsrv_find_source_dsa(struct dreplsrv_partition_source_dsa *list,
366 								      struct GUID *guid)
367 {
368 	struct dreplsrv_partition_source_dsa *s;
369 	for (s=list; s; s=s->next) {
370 		if (GUID_equal(&s->repsFrom1->source_dsa_obj_guid, guid)) {
371 			return s;
372 		}
373 	}
374 	return NULL;
375 }
376 
377 
378 
dreplsrv_partition_add_source_dsa(struct dreplsrv_service * s,struct dreplsrv_partition * p,struct dreplsrv_partition_source_dsa ** listp,struct dreplsrv_partition_source_dsa * check_list,const struct ldb_val * val)379 static WERROR dreplsrv_partition_add_source_dsa(struct dreplsrv_service *s,
380 						struct dreplsrv_partition *p,
381 						struct dreplsrv_partition_source_dsa **listp,
382 						struct dreplsrv_partition_source_dsa *check_list,
383 						const struct ldb_val *val)
384 {
385 	WERROR status;
386 	enum ndr_err_code ndr_err;
387 	struct dreplsrv_partition_source_dsa *source, *s2;
388 
389 	source = talloc_zero(p, struct dreplsrv_partition_source_dsa);
390 	W_ERROR_HAVE_NO_MEMORY(source);
391 
392 	ndr_err = ndr_pull_struct_blob(val, source,
393 				       &source->_repsFromBlob,
394 				       (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
395 	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
396 		NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
397 		talloc_free(source);
398 		return ntstatus_to_werror(nt_status);
399 	}
400 	/* NDR_PRINT_DEBUG(repsFromToBlob, &source->_repsFromBlob); */
401 	if (source->_repsFromBlob.version != 1) {
402 		talloc_free(source);
403 		return WERR_DS_DRA_INTERNAL_ERROR;
404 	}
405 
406 	source->partition	= p;
407 	source->repsFrom1	= &source->_repsFromBlob.ctr.ctr1;
408 
409 	status = dreplsrv_out_connection_attach(s, source->repsFrom1, &source->conn);
410 	W_ERROR_NOT_OK_RETURN(status);
411 
412 	if (check_list &&
413 	    dreplsrv_find_source_dsa(check_list, &source->repsFrom1->source_dsa_obj_guid)) {
414 		/* its in the check list, don't add it again */
415 		talloc_free(source);
416 		return WERR_OK;
417 	}
418 
419 	/* re-use an existing source if found */
420 	for (s2=*listp; s2; s2=s2->next) {
421 		if (GUID_equal(&s2->repsFrom1->source_dsa_obj_guid,
422 				 &source->repsFrom1->source_dsa_obj_guid)) {
423 			talloc_free(s2->repsFrom1->other_info);
424 			*s2->repsFrom1 = *source->repsFrom1;
425 			talloc_steal(s2, s2->repsFrom1->other_info);
426 			talloc_free(source);
427 			return WERR_OK;
428 		}
429 	}
430 
431 	DLIST_ADD_END(*listp, source);
432 	return WERR_OK;
433 }
434 
435 /**
436  * Find a partition when given a NC
437  * If the NC can't be found it will return BAD_NC
438  * Initial checks for invalid parameters have to be done beforehand
439  */
dreplsrv_partition_find_for_nc(struct dreplsrv_service * s,struct GUID * nc_guid,struct dom_sid * nc_sid,const char * nc_dn_str,struct dreplsrv_partition ** _p)440 WERROR dreplsrv_partition_find_for_nc(struct dreplsrv_service *s,
441 				      struct GUID *nc_guid,
442 				      struct dom_sid *nc_sid,
443 				      const char *nc_dn_str,
444 				      struct dreplsrv_partition **_p)
445 {
446 	struct dreplsrv_partition *p;
447 	bool valid_sid, valid_guid;
448 
449 	SMB_ASSERT(_p);
450 
451 	valid_sid  = nc_sid && !is_null_sid(nc_sid);
452 	valid_guid = nc_guid && !GUID_all_zero(nc_guid);
453 
454 	if (!valid_sid && !valid_guid && (!nc_dn_str)) {
455 		return WERR_DS_DRA_BAD_NC;
456 	}
457 
458 	for (p = s->partitions; p; p = p->next) {
459 		if ((valid_guid && GUID_equal(&p->nc.guid, nc_guid))
460 		    || strequal(p->nc.dn, nc_dn_str)
461 		    || (valid_sid && dom_sid_equal(&p->nc.sid, nc_sid)))
462 		{
463 			/* fill in he right guid and sid if possible */
464 			if (nc_guid && !valid_guid) {
465 				dsdb_get_extended_dn_guid(p->dn, nc_guid, "GUID");
466 			}
467 			if (nc_sid && !valid_sid) {
468 				dsdb_get_extended_dn_sid(p->dn, nc_sid, "SID");
469 			}
470 			*_p = p;
471 			return WERR_OK;
472 		}
473 	}
474 
475 	return WERR_DS_DRA_BAD_NC;
476 }
477 
dreplsrv_partition_source_dsa_by_guid(struct dreplsrv_partition * p,const struct GUID * dsa_guid,struct dreplsrv_partition_source_dsa ** _dsa)478 WERROR dreplsrv_partition_source_dsa_by_guid(struct dreplsrv_partition *p,
479 					     const struct GUID *dsa_guid,
480 					     struct dreplsrv_partition_source_dsa **_dsa)
481 {
482 	struct dreplsrv_partition_source_dsa *dsa;
483 
484 	SMB_ASSERT(dsa_guid != NULL);
485 	SMB_ASSERT(!GUID_all_zero(dsa_guid));
486 	SMB_ASSERT(_dsa);
487 
488 	for (dsa = p->sources; dsa; dsa = dsa->next) {
489 		if (GUID_equal(dsa_guid, &dsa->repsFrom1->source_dsa_obj_guid)) {
490 			*_dsa = dsa;
491 			return WERR_OK;
492 		}
493 	}
494 
495 	return WERR_DS_DRA_NO_REPLICA;
496 }
497 
dreplsrv_partition_source_dsa_by_dns(const struct dreplsrv_partition * p,const char * dsa_dns,struct dreplsrv_partition_source_dsa ** _dsa)498 WERROR dreplsrv_partition_source_dsa_by_dns(const struct dreplsrv_partition *p,
499 					    const char *dsa_dns,
500 					    struct dreplsrv_partition_source_dsa **_dsa)
501 {
502 	struct dreplsrv_partition_source_dsa *dsa;
503 
504 	SMB_ASSERT(dsa_dns != NULL);
505 	SMB_ASSERT(_dsa);
506 
507 	for (dsa = p->sources; dsa; dsa = dsa->next) {
508 		if (strequal(dsa_dns, dsa->repsFrom1->other_info->dns_name)) {
509 			*_dsa = dsa;
510 			return WERR_OK;
511 		}
512 	}
513 
514 	return WERR_DS_DRA_NO_REPLICA;
515 }
516 
517 
518 /*
519   create a temporary dsa structure for a replication. This is needed
520   for the initial replication of a new partition, such as when a new
521   domain NC is created and we are a global catalog server
522  */
dreplsrv_partition_source_dsa_temporary(struct dreplsrv_partition * p,TALLOC_CTX * mem_ctx,const struct GUID * dsa_guid,struct dreplsrv_partition_source_dsa ** _dsa)523 WERROR dreplsrv_partition_source_dsa_temporary(struct dreplsrv_partition *p,
524 					       TALLOC_CTX *mem_ctx,
525 					       const struct GUID *dsa_guid,
526 					       struct dreplsrv_partition_source_dsa **_dsa)
527 {
528 	struct dreplsrv_partition_source_dsa *dsa;
529 	WERROR werr;
530 
531 	dsa = talloc_zero(mem_ctx, struct dreplsrv_partition_source_dsa);
532 	W_ERROR_HAVE_NO_MEMORY(dsa);
533 
534 	dsa->partition = p;
535 	dsa->repsFrom1 = &dsa->_repsFromBlob.ctr.ctr1;
536 	dsa->repsFrom1->replica_flags = 0;
537 	dsa->repsFrom1->source_dsa_obj_guid = *dsa_guid;
538 
539 	dsa->repsFrom1->other_info = talloc_zero(dsa, struct repsFromTo1OtherInfo);
540 	W_ERROR_HAVE_NO_MEMORY(dsa->repsFrom1->other_info);
541 
542 	dsa->repsFrom1->other_info->dns_name = samdb_ntds_msdcs_dns_name(p->service->samdb,
543 									 dsa->repsFrom1->other_info, dsa_guid);
544 	W_ERROR_HAVE_NO_MEMORY(dsa->repsFrom1->other_info->dns_name);
545 
546 	werr = dreplsrv_out_connection_attach(p->service, dsa->repsFrom1, &dsa->conn);
547 	if (!W_ERROR_IS_OK(werr)) {
548 		DEBUG(0,(__location__ ": Failed to attach connection to %s\n",
549 			 ldb_dn_get_linearized(p->dn)));
550 		talloc_free(dsa);
551 		return werr;
552 	}
553 
554 	*_dsa = dsa;
555 
556 	return WERR_OK;
557 }
558 
559 
dreplsrv_refresh_partition(struct dreplsrv_service * s,struct dreplsrv_partition * p)560 static WERROR dreplsrv_refresh_partition(struct dreplsrv_service *s,
561 					 struct dreplsrv_partition *p)
562 {
563 	WERROR status;
564 	NTSTATUS ntstatus;
565 	struct ldb_message_element *orf_el = NULL;
566 	struct ldb_result *r = NULL;
567 	unsigned int i;
568 	int ret;
569 	TALLOC_CTX *mem_ctx = talloc_new(p);
570 	static const char *attrs[] = {
571 		"repsFrom",
572 		"repsTo",
573 		NULL
574 	};
575 	struct ldb_dn *dn;
576 
577 	DEBUG(4, ("dreplsrv_refresh_partition(%s)\n",
578 		ldb_dn_get_linearized(p->dn)));
579 
580 	ret = dsdb_search_dn(s->samdb, mem_ctx, &r, p->dn, attrs, DSDB_SEARCH_SHOW_EXTENDED_DN);
581 	if (ret == LDB_ERR_NO_SUCH_OBJECT) {
582 		/* we haven't replicated the partition yet, but we
583 		 * can fill in the guid, sid etc from the partition DN */
584 		dn = p->dn;
585 	} else if (ret != LDB_SUCCESS) {
586 		talloc_free(mem_ctx);
587 		return WERR_FOOBAR;
588 	} else {
589 		dn = r->msgs[0]->dn;
590 	}
591 
592 	talloc_free(discard_const(p->nc.dn));
593 	ZERO_STRUCT(p->nc);
594 	p->nc.dn	= ldb_dn_alloc_linearized(p, dn);
595 	W_ERROR_HAVE_NO_MEMORY(p->nc.dn);
596 	ntstatus = dsdb_get_extended_dn_guid(dn, &p->nc.guid, "GUID");
597 	if (!NT_STATUS_IS_OK(ntstatus)) {
598 		DEBUG(0,(__location__ ": unable to get GUID for %s: %s\n",
599 			 p->nc.dn, nt_errstr(ntstatus)));
600 		talloc_free(mem_ctx);
601 		return WERR_DS_DRA_INTERNAL_ERROR;
602 	}
603 	dsdb_get_extended_dn_sid(dn, &p->nc.sid, "SID");
604 
605 	talloc_free(p->uptodatevector.cursors);
606 	talloc_free(p->uptodatevector_ex.cursors);
607 	ZERO_STRUCT(p->uptodatevector);
608 	ZERO_STRUCT(p->uptodatevector_ex);
609 
610 	ret = dsdb_load_udv_v2(s->samdb, p->dn, p, &p->uptodatevector.cursors, &p->uptodatevector.count);
611 	if (ret != LDB_SUCCESS) {
612 		DEBUG(4,(__location__ ": no UDV available for %s\n", ldb_dn_get_linearized(p->dn)));
613 	}
614 
615 	status = WERR_OK;
616 
617 	if (r != NULL && (orf_el = ldb_msg_find_element(r->msgs[0], "repsFrom"))) {
618 		for (i=0; i < orf_el->num_values; i++) {
619 			status = dreplsrv_partition_add_source_dsa(s, p, &p->sources,
620 								   NULL, &orf_el->values[i]);
621 			W_ERROR_NOT_OK_GOTO_DONE(status);
622 		}
623 	}
624 
625 	if (r != NULL && (orf_el = ldb_msg_find_element(r->msgs[0], "repsTo"))) {
626 		for (i=0; i < orf_el->num_values; i++) {
627 			status = dreplsrv_partition_add_source_dsa(s, p, &p->notifies,
628 								   p->sources, &orf_el->values[i]);
629 			W_ERROR_NOT_OK_GOTO_DONE(status);
630 		}
631 	}
632 
633 done:
634 	talloc_free(mem_ctx);
635 	return status;
636 }
637 
dreplsrv_refresh_partitions(struct dreplsrv_service * s)638 WERROR dreplsrv_refresh_partitions(struct dreplsrv_service *s)
639 {
640 	WERROR status;
641 	struct dreplsrv_partition *p;
642 
643 	for (p = s->partitions; p; p = p->next) {
644 		status = dreplsrv_refresh_partition(s, p);
645 		W_ERROR_NOT_OK_RETURN(status);
646 	}
647 
648 	return WERR_OK;
649 }
650