bee6a6e1d043897aa2f2f720b2aed441b0702e4b
[obnox/samba/samba-obnox.git] / source4 / dsdb / samdb / ldb_modules / dns_notify.c
1 /*
2    ldb database library
3
4    Copyright (C) Samuel Cabrero <samuelcabrero@kernevil.me> 2014
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 /*
21  *  Name: ldb
22  *
23  *  Component: ldb dns_notify module
24  *
25  *  Description: Notify the DNS server when zones are changed, either by direct
26  *               RPC management calls or DRS inbound replication.
27  *
28  *  Author: Samuel Cabrero <samuelcabrero@kernevil.me>
29  */
30
31 #include "includes.h"
32 #include "ldb_module.h"
33 #include "dsdb/samdb/ldb_modules/util.h"
34 #include "dsdb/samdb/samdb.h"
35 #include "dsdb/common/proto.h"
36 #include "librpc/gen_ndr/ndr_irpc.h"
37 #include "lib/messaging/irpc.h"
38 #include "librpc/gen_ndr/ndr_irpc_c.h"
39 #include "param/param.h"
40 #include "dlinklist.h"
41
42 struct dns_notify_watched_dn {
43         struct dns_notify_watched_dn *next, *prev;
44         struct ldb_dn *dn;
45 };
46
47 struct dns_notify_private {
48         struct dns_notify_watched_dn *watched;
49         bool reload_zones;
50 };
51
52 struct dns_notify_dnssrv_state {
53         struct imessaging_context *msg_ctx;
54         struct dnssrv_reload_dns_zones r;
55 };
56
57 static void dns_notify_dnssrv_done(struct tevent_req *req)
58 {
59         NTSTATUS status;
60         struct dns_notify_dnssrv_state *state;
61
62         state = tevent_req_callback_data(req, struct dns_notify_dnssrv_state);
63
64         status = dcerpc_dnssrv_reload_dns_zones_r_recv(req, state);
65         if (!NT_STATUS_IS_OK(status)) {
66                 DEBUG(1, ("%s: Error notifiying dns server: %s\n",
67                       __func__, nt_errstr(status)));
68         }
69         imessaging_cleanup(state->msg_ctx);
70
71         talloc_free(req);
72         talloc_free(state);
73 }
74
75 static void dns_notify_dnssrv_send(struct ldb_module *module)
76 {
77         struct ldb_context *ldb;
78         struct loadparm_context *lp_ctx;
79         struct dns_notify_dnssrv_state *state;
80         struct dcerpc_binding_handle *handle;
81         struct tevent_req *req;
82
83         ldb = ldb_module_get_ctx(module);
84
85         lp_ctx = ldb_get_opaque(ldb, "loadparm");
86         if (lp_ctx == NULL) {
87                 return;
88         }
89
90         state = talloc_zero(module, struct dns_notify_dnssrv_state);
91         if (state == NULL) {
92                 return;
93         }
94
95         /* Initialize messaging client */
96         state->msg_ctx = imessaging_client_init(state, lp_ctx,
97                                                 ldb_get_event_context(ldb));
98         if (state->msg_ctx == NULL) {
99                 ldb_asprintf_errstring(ldb, "Failed to generate client messaging context in %s",
100                                        lpcfg_imessaging_path(state, lp_ctx));
101                 talloc_free(state);
102                 return;
103         }
104
105         /* Get a handle to notify the DNS server */
106         handle = irpc_binding_handle_by_name(state, state->msg_ctx,
107                                              "dnssrv",
108                                              &ndr_table_irpc);
109         if (handle == NULL) {
110                 imessaging_cleanup(state->msg_ctx);
111                 talloc_free(state);
112                 return;
113         }
114
115         /* Send the notifications */
116         req = dcerpc_dnssrv_reload_dns_zones_r_send(state,
117                                                     ldb_get_event_context(ldb),
118                                                     handle,
119                                                     &state->r);
120         if (req == NULL) {
121                 imessaging_cleanup(state->msg_ctx);
122                 talloc_free(state);
123                 return;
124         }
125         tevent_req_set_callback(req, dns_notify_dnssrv_done, state);
126 }
127
128 static int dns_notify_add(struct ldb_module *module, struct ldb_request *req)
129 {
130         struct ldb_context *ldb;
131         struct dns_notify_private *data;
132         struct dns_notify_watched_dn *w;
133         struct dsdb_schema *schema;
134         const struct dsdb_class *objectclass;
135
136         if (ldb_dn_is_special(req->op.add.message->dn)) {
137                 return ldb_next_request(module, req);
138         }
139
140         if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
141                 return ldb_next_request(module, req);
142         }
143
144         ldb = ldb_module_get_ctx(module);
145         data = talloc_get_type(ldb_module_get_private(module),
146                                struct dns_notify_private);
147         if (data == NULL) {
148                 return ldb_operr(ldb);
149         }
150
151         for (w = data->watched; w; w = w->next) {
152                 if (ldb_dn_compare_base(w->dn, req->op.add.message->dn) == 0) {
153                         schema = dsdb_get_schema(ldb, req);
154                         if (schema == NULL) {
155                                 return ldb_operr(ldb);
156                         }
157
158                         objectclass = dsdb_get_structural_oc_from_msg(schema, req->op.add.message);
159                         if (objectclass == NULL) {
160                                 return ldb_operr(ldb);
161                         }
162
163                         if (ldb_attr_cmp(objectclass->lDAPDisplayName, "dnsZone") == 0) {
164                                 data->reload_zones = true;
165                                 break;
166                         }
167                 }
168         }
169
170         return ldb_next_request(module, req);
171 }
172
173 static int dns_notify_modify(struct ldb_module *module, struct ldb_request *req)
174 {
175         TALLOC_CTX *tmp_ctx;
176         struct ldb_context *ldb;
177         struct dns_notify_private *data;
178         struct dns_notify_watched_dn *w;
179         struct ldb_dn *dn;
180         struct ldb_result *res;
181         struct dsdb_schema *schema;
182         const struct dsdb_class *objectclass;
183         const char * const attrs[] = { "objectClass", NULL };
184         int ret;
185
186         if (ldb_dn_is_special(req->op.mod.message->dn)) {
187                 return ldb_next_request(module, req);
188         }
189
190         if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
191                 return ldb_next_request(module, req);
192         }
193
194         ldb = ldb_module_get_ctx(module);
195         data = talloc_get_type(ldb_module_get_private(module),
196                                struct dns_notify_private);
197         if (data == NULL) {
198                 return ldb_operr(ldb);
199         }
200
201         tmp_ctx = talloc_new(module);
202         if (tmp_ctx == NULL) {
203                 return ldb_oom(ldb);
204         }
205
206         for (w = data->watched; w; w = w->next) {
207                 if (ldb_dn_compare_base(w->dn, req->op.add.message->dn) == 0) {
208                         dn = ldb_dn_copy(tmp_ctx, req->op.mod.message->dn);
209
210                         ret = dsdb_module_search_dn(module, tmp_ctx, &res, dn, attrs,
211                                                     DSDB_FLAG_NEXT_MODULE |
212                                                     DSDB_SEARCH_SHOW_RECYCLED |
213                                                     DSDB_SEARCH_REVEAL_INTERNALS |
214                                                     DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT, req);
215                         if (ret != LDB_SUCCESS) {
216                                 ldb_asprintf_errstring(ldb_module_get_ctx(module),
217                                                        "%s: Failed to modify %s, because we failed to find it: %s\n",
218                                                        __func__,
219                                                        ldb_dn_get_linearized(dn),
220                                                        ldb_errstring(ldb_module_get_ctx(module)));
221                                 talloc_free(tmp_ctx);
222                                 return ret;
223                         }
224
225                         schema = dsdb_get_schema(ldb, req);
226                         if (schema == NULL) {
227                                 talloc_free(tmp_ctx);
228                                 return ldb_operr(ldb);
229                         }
230
231                         objectclass = dsdb_get_structural_oc_from_msg(schema, res->msgs[0]);
232                         if (objectclass == NULL) {
233                                 talloc_free(tmp_ctx);
234                                 return ldb_operr(ldb);
235                         }
236
237                         if (ldb_attr_cmp(objectclass->lDAPDisplayName, "dnsZone") == 0) {
238                                 data->reload_zones = true;
239                                 break;
240                         }
241                 }
242         }
243
244         talloc_free(tmp_ctx);
245         return ldb_next_request(module, req);
246 }
247
248 static int dns_notify_delete(struct ldb_module *module, struct ldb_request *req)
249 {
250         TALLOC_CTX *tmp_ctx;
251         struct ldb_context *ldb;
252         struct dns_notify_private *data;
253         struct dns_notify_watched_dn *w;
254         struct ldb_dn *old_dn;
255         struct ldb_result *res;
256         struct dsdb_schema *schema;
257         const struct dsdb_class *objectclass;
258         const char * const attrs[] = { "objectClass", NULL };
259         int ret;
260
261         if (ldb_dn_is_special(req->op.del.dn)) {
262                 return ldb_next_request(module, req);
263         }
264
265         if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
266                 return ldb_next_request(module, req);
267         }
268
269         ldb = ldb_module_get_ctx(module);
270         data = talloc_get_type(ldb_module_get_private(module),
271                                struct dns_notify_private);
272         if (data == NULL) {
273                 return ldb_operr(ldb);
274         }
275
276         tmp_ctx = talloc_new(module);
277         if (tmp_ctx == NULL) {
278                 return ldb_oom(ldb);
279         }
280
281         for (w = data->watched; w; w = w->next) {
282                 if (ldb_dn_compare_base(w->dn, req->op.add.message->dn) == 0) {
283                         old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn);
284                         ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, attrs,
285                                                     DSDB_FLAG_NEXT_MODULE |
286                                                     DSDB_SEARCH_SHOW_RECYCLED |
287                                                     DSDB_SEARCH_REVEAL_INTERNALS |
288                                                     DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT, req);
289                         if (ret != LDB_SUCCESS) {
290                                 ldb_asprintf_errstring(ldb_module_get_ctx(module),
291                                                        "%s: Failed to delete %s, because we failed to find it: %s\n",
292                                                        __func__,
293                                                        ldb_dn_get_linearized(old_dn),
294                                                        ldb_errstring(ldb_module_get_ctx(module)));
295                                 talloc_free(tmp_ctx);
296                                 return ret;
297                         }
298
299                         schema = dsdb_get_schema(ldb, req);
300                         if (schema == NULL) {
301                                 talloc_free(tmp_ctx);
302                                 return ldb_operr(ldb);
303                         }
304
305                         objectclass = dsdb_get_structural_oc_from_msg(schema, res->msgs[0]);
306                         if (objectclass == NULL) {
307                                 talloc_free(tmp_ctx);
308                                 return ldb_operr(ldb);
309                         }
310
311                         if (ldb_attr_cmp(objectclass->lDAPDisplayName, "dnsZone") == 0) {
312                                 data->reload_zones = true;
313                                 break;
314                         }
315                 }
316         }
317
318         talloc_free(tmp_ctx);
319         return ldb_next_request(module, req);
320 }
321
322 static int dns_notify_start_trans(struct ldb_module *module)
323 {
324         struct ldb_context *ldb;
325         struct dns_notify_private *data;
326
327         ldb = ldb_module_get_ctx(module);
328         data = talloc_get_type(ldb_module_get_private(module),
329                                struct dns_notify_private);
330         if (data == NULL) {
331                 return ldb_operr(ldb);
332         }
333
334         data->reload_zones = false;
335
336         return ldb_next_start_trans(module);
337 }
338
339 static int dns_notify_end_trans(struct ldb_module *module)
340 {
341         struct ldb_context *ldb;
342         struct dns_notify_private *data;
343         int ret;
344
345         ldb = ldb_module_get_ctx(module);
346         data = talloc_get_type(ldb_module_get_private(module),
347                                struct dns_notify_private);
348         if (data == NULL) {
349                 return ldb_operr(ldb);
350         }
351
352         ret = ldb_next_end_trans(module);
353         if (ret == LDB_SUCCESS) {
354                 if (data->reload_zones) {
355                         dns_notify_dnssrv_send(module);
356                 }
357         }
358
359         return ret;
360 }
361
362 static int dns_notify_del_trans(struct ldb_module *module)
363 {
364         struct ldb_context *ldb;
365         struct dns_notify_private *data;
366
367         ldb = ldb_module_get_ctx(module);
368         data = talloc_get_type(ldb_module_get_private(module),
369                                struct dns_notify_private);
370         if (data == NULL) {
371                 return ldb_operr(ldb);
372         }
373
374         data->reload_zones = false;
375
376         return ldb_next_del_trans(module);
377 }
378
379 static int dns_notify_init(struct ldb_module *module)
380 {
381         struct ldb_context *ldb;
382         struct dns_notify_private *data;
383         struct dns_notify_watched_dn *watched;
384         struct ldb_dn *domain_dn;
385         struct ldb_dn *forest_dn;
386
387         ldb = ldb_module_get_ctx(module);
388
389         data = talloc_zero(module, struct dns_notify_private);
390         if (data == NULL) {
391                 return ldb_oom(ldb);
392         }
393
394         domain_dn = ldb_get_default_basedn(ldb);
395         forest_dn = ldb_get_root_basedn(ldb);
396
397         /* Register hook on domain partition */
398         watched = talloc_zero(data, struct dns_notify_watched_dn);
399         if (watched == NULL) {
400                 talloc_free(data);
401                 return ldb_oom(ldb);
402         }
403         watched->dn = ldb_dn_new_fmt(watched, ldb,
404                                      "CN=MicrosoftDNS,CN=System,%s",
405                                      ldb_dn_get_linearized(domain_dn));
406         if (watched->dn == NULL) {
407                 talloc_free(data);
408                 return ldb_oom(ldb);
409         }
410         DLIST_ADD(data->watched, watched);
411
412         /* Check for DomainDnsZones partition and register hook */
413         watched = talloc_zero(data, struct dns_notify_watched_dn);
414         if (watched == NULL) {
415                 talloc_free(data);
416                 return ldb_oom(ldb);
417         }
418         watched->dn = ldb_dn_new_fmt(watched, ldb, "CN=MicrosoftDNS,DC=DomainDnsZones,%s", ldb_dn_get_linearized(forest_dn));
419         DLIST_ADD(data->watched, watched);
420
421         /* Check for ForestDnsZones partition and register hook */
422         watched = talloc_zero(data, struct dns_notify_watched_dn);
423         if (watched == NULL) {
424                 talloc_free(data);
425                 return ldb_oom(ldb);
426         }
427         watched->dn = ldb_dn_new_fmt(watched, ldb, "CN=MicrosoftDNS,DC=ForestDnsZones,%s", ldb_dn_get_linearized(forest_dn));
428         DLIST_ADD(data->watched, watched);
429
430         ldb_module_set_private(module, data);
431
432         return ldb_next_init(module);
433 }
434
435 static const struct ldb_module_ops ldb_dns_notify_module_ops = {
436         .name              = "dns_notify",
437         .init_context      = dns_notify_init,
438         .add               = dns_notify_add,
439         .modify            = dns_notify_modify,
440         .del               = dns_notify_delete,
441         .start_transaction = dns_notify_start_trans,
442         .end_transaction   = dns_notify_end_trans,
443         .del_transaction   = dns_notify_del_trans,
444 };
445
446 int ldb_dns_notify_module_init(const char *version)
447 {
448         LDB_MODULE_CHECK_VERSION(version);
449         return ldb_register_module(&ldb_dns_notify_module_ops);
450 }