s4-drs: reduce verbosity of dreplsrv_out_connection_attach
[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 "lib/messaging/irpc.h"
28 #include "dsdb/repl/drepl_service.h"
29 #include "lib/ldb/include/ldb_errors.h"
30 #include "../lib/util/dlinklist.h"
31 #include "librpc/gen_ndr/ndr_misc.h"
32 #include "librpc/gen_ndr/ndr_drsuapi.h"
33 #include "librpc/gen_ndr/ndr_drsblobs.h"
34 #include "libcli/security/security.h"
35 #include "param/param.h"
36
37 WERROR dreplsrv_load_partitions(struct dreplsrv_service *s)
38 {
39         WERROR status;
40         static const char *attrs[] = { "namingContexts", 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
47         tmp_ctx = talloc_new(s);
48         W_ERROR_HAVE_NO_MEMORY(tmp_ctx);
49
50         ret = ldb_search(s->samdb, tmp_ctx, &res,
51                          ldb_dn_new(tmp_ctx, s->samdb, ""), LDB_SCOPE_BASE, attrs, NULL);
52         if (ret != LDB_SUCCESS) {
53                 DEBUG(1,("Searching for namingContexts in rootDSE failed: %s\n", ldb_errstring(s->samdb)));
54                 talloc_free(tmp_ctx);
55                 return WERR_DS_DRA_INTERNAL_ERROR;
56        }
57
58        el = ldb_msg_find_element(res->msgs[0], "namingContexts");
59        if (!el) {
60                DEBUG(1,("Finding namingContexts element in root_res failed: %s\n",
61                         ldb_errstring(s->samdb)));
62                talloc_free(tmp_ctx);
63                return WERR_DS_DRA_INTERNAL_ERROR;
64        }
65
66        for (i=0; i<el->num_values; i++) {
67                struct ldb_dn *pdn;
68                struct dreplsrv_partition *p;
69
70                pdn = ldb_dn_from_ldb_val(tmp_ctx, s->samdb, &el->values[i]);
71                if (pdn == NULL) {
72                        talloc_free(tmp_ctx);
73                        return WERR_DS_DRA_INTERNAL_ERROR;
74                }
75                if (!ldb_dn_validate(pdn)) {
76                        return WERR_DS_DRA_INTERNAL_ERROR;
77                }
78
79                p = talloc_zero(s, struct dreplsrv_partition);
80                W_ERROR_HAVE_NO_MEMORY(p);
81
82                p->dn = talloc_steal(p, pdn);
83
84                DLIST_ADD(s->partitions, p);
85
86                DEBUG(2, ("dreplsrv_partition[%s] loaded\n", ldb_dn_get_linearized(p->dn)));
87         }
88
89         talloc_free(tmp_ctx);
90
91         status = dreplsrv_refresh_partitions(s);
92         W_ERROR_NOT_OK_RETURN(status);
93
94         return WERR_OK;
95 }
96
97 /*
98   work out the principal to use for DRS replication connections
99  */
100 NTSTATUS dreplsrv_get_target_principal(struct dreplsrv_service *s,
101                                        TALLOC_CTX *mem_ctx,
102                                        const struct repsFromTo1 *rft,
103                                        const char **target_principal)
104 {
105         TALLOC_CTX *tmp_ctx;
106         struct ldb_result *res;
107         const char *attrs[] = { "dNSHostName", NULL };
108         int ret;
109         const char *hostname;
110         struct ldb_dn *dn;
111
112         *target_principal = NULL;
113
114         tmp_ctx = talloc_new(mem_ctx);
115
116         /* we need to find their hostname */
117         ret = dsdb_find_dn_by_guid(s->samdb, tmp_ctx, &rft->source_dsa_obj_guid, &dn);
118         if (ret != LDB_SUCCESS) {
119                 talloc_free(tmp_ctx);
120                 /* its OK for their NTDSDSA DN not to be in our database */
121                 return NT_STATUS_OK;
122         }
123
124         /* strip off the NTDS Settings */
125         if (!ldb_dn_remove_child_components(dn, 1)) {
126                 talloc_free(tmp_ctx);
127                 return NT_STATUS_OK;
128         }
129
130         ret = dsdb_search_dn(s->samdb, tmp_ctx, &res, dn, attrs, 0);
131         if (ret != LDB_SUCCESS) {
132                 talloc_free(tmp_ctx);
133                 /* its OK for their account DN not to be in our database */
134                 return NT_STATUS_OK;
135         }
136
137         hostname = ldb_msg_find_attr_as_string(res->msgs[0], "dNSHostName", NULL);
138         if (hostname == NULL) {
139                 talloc_free(tmp_ctx);
140                 /* its OK to not have a dnshostname */
141                 return NT_STATUS_OK;
142         }
143
144         /* All DCs have the GC/hostname/realm name, but if some of the
145          * preconditions are not satisfied, then we will fall back to
146          * the
147          * E3514235-4B06-11D1-AB04-00C04FC2DCD2/${NTDSGUID}/${DNSDOMAIN}
148          * name.  This means that if a AD server has a dnsHostName set
149          * on it's record, it must also have GC/hostname/realm
150          * servicePrincipalName */
151
152         *target_principal = talloc_asprintf(mem_ctx, "GC/%s/%s",
153                                             hostname,
154                                             lpcfg_dnsdomain(s->task->lp_ctx));
155         talloc_free(tmp_ctx);
156         return NT_STATUS_OK;
157 }
158
159
160 WERROR dreplsrv_out_connection_attach(struct dreplsrv_service *s,
161                                       const struct repsFromTo1 *rft,
162                                       struct dreplsrv_out_connection **_conn)
163 {
164         struct dreplsrv_out_connection *cur, *conn = NULL;
165         const char *hostname;
166
167         if (!rft->other_info) {
168                 return WERR_FOOBAR;
169         }
170
171         if (!rft->other_info->dns_name) {
172                 return WERR_FOOBAR;
173         }
174
175         hostname = rft->other_info->dns_name;
176
177         for (cur = s->connections; cur; cur = cur->next) {              
178                 if (strcmp(cur->binding->host, hostname) == 0) {
179                         conn = cur;
180                         break;
181                 }
182         }
183
184         if (!conn) {
185                 NTSTATUS nt_status;
186                 char *binding_str;
187
188                 conn = talloc_zero(s, struct dreplsrv_out_connection);
189                 W_ERROR_HAVE_NO_MEMORY(conn);
190
191                 conn->service   = s;
192
193                 binding_str = talloc_asprintf(conn, "ncacn_ip_tcp:%s[krb5,seal]",
194                                               hostname);
195                 W_ERROR_HAVE_NO_MEMORY(binding_str);
196                 nt_status = dcerpc_parse_binding(conn, binding_str, &conn->binding);
197                 talloc_free(binding_str);
198                 if (!NT_STATUS_IS_OK(nt_status)) {
199                         return ntstatus_to_werror(nt_status);
200                 }
201
202                 /* use the GC principal for DRS replication */
203                 nt_status = dreplsrv_get_target_principal(s, conn->binding,
204                                                           rft, &conn->binding->target_principal);
205                 if (!NT_STATUS_IS_OK(nt_status)) {
206                         return ntstatus_to_werror(nt_status);
207                 }
208
209                 DLIST_ADD_END(s->connections, conn, struct dreplsrv_out_connection *);
210
211                 DEBUG(4,("dreplsrv_out_connection_attach(%s): create\n", conn->binding->host));
212         } else {
213                 DEBUG(4,("dreplsrv_out_connection_attach(%s): attach\n", conn->binding->host));
214         }
215
216         *_conn = conn;
217         return WERR_OK;
218 }
219
220 /*
221   find an existing source dsa in a list
222  */
223 static struct dreplsrv_partition_source_dsa *dreplsrv_find_source_dsa(struct dreplsrv_partition_source_dsa *list,
224                                                                       struct GUID *guid)
225 {
226         struct dreplsrv_partition_source_dsa *s;
227         for (s=list; s; s=s->next) {
228                 if (GUID_compare(&s->repsFrom1->source_dsa_obj_guid, guid) == 0) {
229                         return s;
230                 }
231         }
232         return NULL;    
233 }
234
235
236
237 static WERROR dreplsrv_partition_add_source_dsa(struct dreplsrv_service *s,
238                                                 struct dreplsrv_partition *p,
239                                                 struct dreplsrv_partition_source_dsa **listp,
240                                                 struct dreplsrv_partition_source_dsa *check_list,
241                                                 const struct ldb_val *val)
242 {
243         WERROR status;
244         enum ndr_err_code ndr_err;
245         struct dreplsrv_partition_source_dsa *source, *s2;
246
247         source = talloc_zero(p, struct dreplsrv_partition_source_dsa);
248         W_ERROR_HAVE_NO_MEMORY(source);
249
250         ndr_err = ndr_pull_struct_blob(val, source, 
251                                        &source->_repsFromBlob,
252                                        (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
253         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
254                 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
255                 talloc_free(source);
256                 return ntstatus_to_werror(nt_status);
257         }
258         /* NDR_PRINT_DEBUG(repsFromToBlob, &source->_repsFromBlob); */
259         if (source->_repsFromBlob.version != 1) {
260                 talloc_free(source);
261                 return WERR_DS_DRA_INTERNAL_ERROR;
262         }
263
264         source->partition       = p;
265         source->repsFrom1       = &source->_repsFromBlob.ctr.ctr1;
266
267         status = dreplsrv_out_connection_attach(s, source->repsFrom1, &source->conn);
268         W_ERROR_NOT_OK_RETURN(status);
269
270         if (check_list && 
271             dreplsrv_find_source_dsa(check_list, &source->repsFrom1->source_dsa_obj_guid)) {
272                 /* its in the check list, don't add it again */
273                 talloc_free(source);
274                 return WERR_OK;
275         }
276
277         /* re-use an existing source if found */
278         for (s2=*listp; s2; s2=s2->next) {
279                 if (GUID_compare(&s2->repsFrom1->source_dsa_obj_guid, 
280                                  &source->repsFrom1->source_dsa_obj_guid) == 0) {
281                         talloc_free(s2->repsFrom1->other_info);
282                         *s2->repsFrom1 = *source->repsFrom1;
283                         talloc_steal(s2, s2->repsFrom1->other_info);
284                         talloc_free(source);
285                         return WERR_OK;
286                 }
287         }
288
289         DLIST_ADD_END(*listp, source, struct dreplsrv_partition_source_dsa *);
290         return WERR_OK;
291 }
292
293 WERROR dreplsrv_partition_find_for_nc(struct dreplsrv_service *s,
294                                       const struct GUID *nc_guid,
295                                       const struct dom_sid *nc_sid,
296                                       const char *nc_dn_str,
297                                       struct dreplsrv_partition **_p)
298 {
299         struct dreplsrv_partition *p;
300         bool valid_sid, valid_guid;
301         struct dom_sid null_sid;
302         ZERO_STRUCT(null_sid);
303
304         SMB_ASSERT(_p);
305
306         valid_sid  = nc_sid && !dom_sid_equal(&null_sid, nc_sid);
307         valid_guid = nc_guid && !GUID_all_zero(nc_guid);
308
309         if (!valid_sid && !valid_guid && !nc_dn_str) {
310                 return WERR_DS_DRA_INVALID_PARAMETER;
311         }
312
313         for (p = s->partitions; p; p = p->next) {
314                 if ((valid_guid && GUID_equal(&p->nc.guid, nc_guid))
315                     || strequal(p->nc.dn, nc_dn_str)
316                     || (valid_sid && dom_sid_equal(&p->nc.sid, nc_sid)))
317                 {
318                         *_p = p;
319                         return WERR_OK;
320                 }
321         }
322
323         return WERR_DS_DRA_BAD_NC;
324 }
325
326 WERROR dreplsrv_partition_source_dsa_by_guid(struct dreplsrv_partition *p,
327                                              const struct GUID *dsa_guid,
328                                              struct dreplsrv_partition_source_dsa **_dsa)
329 {
330         struct dreplsrv_partition_source_dsa *dsa;
331
332         SMB_ASSERT(dsa_guid != NULL);
333         SMB_ASSERT(!GUID_all_zero(dsa_guid));
334         SMB_ASSERT(_dsa);
335
336         for (dsa = p->sources; dsa; dsa = dsa->next) {
337                 if (GUID_equal(dsa_guid, &dsa->repsFrom1->source_dsa_obj_guid)) {
338                         *_dsa = dsa;
339                         return WERR_OK;
340                 }
341         }
342
343         return WERR_DS_DRA_NO_REPLICA;
344 }
345
346 WERROR dreplsrv_partition_source_dsa_by_dns(const struct dreplsrv_partition *p,
347                                             const char *dsa_dns,
348                                             struct dreplsrv_partition_source_dsa **_dsa)
349 {
350         struct dreplsrv_partition_source_dsa *dsa;
351
352         SMB_ASSERT(dsa_dns != NULL);
353         SMB_ASSERT(_dsa);
354
355         for (dsa = p->sources; dsa; dsa = dsa->next) {
356                 if (strequal(dsa_dns, dsa->repsFrom1->other_info->dns_name)) {
357                         *_dsa = dsa;
358                         return WERR_OK;
359                 }
360         }
361
362         return WERR_DS_DRA_NO_REPLICA;
363 }
364
365
366 static WERROR dreplsrv_refresh_partition(struct dreplsrv_service *s,
367                                          struct dreplsrv_partition *p)
368 {
369         WERROR status;
370         struct dom_sid *nc_sid;
371         struct ldb_message_element *orf_el = NULL;
372         struct ldb_result *r;
373         unsigned int i;
374         int ret;
375         TALLOC_CTX *mem_ctx = talloc_new(p);
376         static const char *attrs[] = {
377                 "objectSid",
378                 "objectGUID",
379                 "repsFrom",
380                 "repsTo",
381                 NULL
382         };
383
384         DEBUG(4, ("dreplsrv_refresh_partition(%s)\n",
385                 ldb_dn_get_linearized(p->dn)));
386
387         ret = ldb_search(s->samdb, mem_ctx, &r, p->dn, LDB_SCOPE_BASE, attrs,
388                          "(objectClass=*)");
389         if (ret != LDB_SUCCESS) {
390                 talloc_free(mem_ctx);
391                 return WERR_FOOBAR;
392         }
393         
394         talloc_free(discard_const(p->nc.dn));
395         ZERO_STRUCT(p->nc);
396         p->nc.dn        = ldb_dn_alloc_linearized(p, p->dn);
397         W_ERROR_HAVE_NO_MEMORY(p->nc.dn);
398         p->nc.guid      = samdb_result_guid(r->msgs[0], "objectGUID");
399         nc_sid          = samdb_result_dom_sid(p, r->msgs[0], "objectSid");
400         if (nc_sid) {
401                 p->nc.sid       = *nc_sid;
402                 talloc_free(nc_sid);
403         }
404
405         talloc_free(p->uptodatevector.cursors);
406         talloc_free(p->uptodatevector_ex.cursors);
407         ZERO_STRUCT(p->uptodatevector);
408         ZERO_STRUCT(p->uptodatevector_ex);
409
410         ret = dsdb_load_udv_v2(s->samdb, p->dn, p, &p->uptodatevector.cursors, &p->uptodatevector.count);
411         if (ret != LDB_SUCCESS) {
412                 DEBUG(4,(__location__ ": no UDV available for %s\n", ldb_dn_get_linearized(p->dn)));
413         }
414
415         orf_el = ldb_msg_find_element(r->msgs[0], "repsFrom");
416         if (orf_el) {
417                 for (i=0; i < orf_el->num_values; i++) {
418                         status = dreplsrv_partition_add_source_dsa(s, p, &p->sources, 
419                                                                    NULL, &orf_el->values[i]);
420                         W_ERROR_NOT_OK_RETURN(status);  
421                 }
422         }
423
424         orf_el = ldb_msg_find_element(r->msgs[0], "repsTo");
425         if (orf_el) {
426                 for (i=0; i < orf_el->num_values; i++) {
427                         status = dreplsrv_partition_add_source_dsa(s, p, &p->notifies, 
428                                                                    p->sources, &orf_el->values[i]);
429                         W_ERROR_NOT_OK_RETURN(status);  
430                 }
431         }
432
433         talloc_free(mem_ctx);
434
435         return WERR_OK;
436 }
437
438 WERROR dreplsrv_refresh_partitions(struct dreplsrv_service *s)
439 {
440         WERROR status;
441         struct dreplsrv_partition *p;
442
443         for (p = s->partitions; p; p = p->next) {
444                 status = dreplsrv_refresh_partition(s, p);
445                 W_ERROR_NOT_OK_RETURN(status);
446         }
447
448         return WERR_OK;
449 }