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