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