Line data Source code
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 "util/dlinklist.h"
41 :
42 : #undef strcasecmp
43 :
44 : struct dns_notify_watched_dn {
45 : struct dns_notify_watched_dn *next, *prev;
46 : struct ldb_dn *dn;
47 : };
48 :
49 : struct dns_notify_private {
50 : struct dns_notify_watched_dn *watched;
51 : bool reload_zones;
52 : };
53 :
54 : struct dns_notify_dnssrv_state {
55 : struct imessaging_context *msg_ctx;
56 : struct dnssrv_reload_dns_zones r;
57 : };
58 :
59 0 : static void dns_notify_dnssrv_done(struct tevent_req *req)
60 : {
61 0 : NTSTATUS status;
62 0 : struct dns_notify_dnssrv_state *state;
63 :
64 0 : state = tevent_req_callback_data(req, struct dns_notify_dnssrv_state);
65 :
66 0 : status = dcerpc_dnssrv_reload_dns_zones_r_recv(req, state);
67 0 : if (!NT_STATUS_IS_OK(status)) {
68 0 : DEBUG(1, ("%s: Error notifying dns server: %s\n",
69 : __func__, nt_errstr(status)));
70 : }
71 0 : imessaging_cleanup(state->msg_ctx);
72 :
73 0 : talloc_free(req);
74 0 : talloc_free(state);
75 0 : }
76 :
77 1479 : static void dns_notify_dnssrv_send(struct ldb_module *module)
78 : {
79 27 : struct ldb_context *ldb;
80 27 : struct loadparm_context *lp_ctx;
81 27 : struct dns_notify_dnssrv_state *state;
82 27 : struct dcerpc_binding_handle *handle;
83 27 : struct tevent_req *req;
84 :
85 1479 : ldb = ldb_module_get_ctx(module);
86 :
87 1479 : lp_ctx = ldb_get_opaque(ldb, "loadparm");
88 1479 : if (lp_ctx == NULL) {
89 0 : return;
90 : }
91 :
92 1479 : state = talloc_zero(module, struct dns_notify_dnssrv_state);
93 1479 : if (state == NULL) {
94 0 : return;
95 : }
96 :
97 : /* Initialize messaging client */
98 1479 : state->msg_ctx = imessaging_client_init(state, lp_ctx,
99 : ldb_get_event_context(ldb));
100 1479 : if (state->msg_ctx == NULL) {
101 8 : ldb_asprintf_errstring(ldb, "Failed to generate client messaging context in %s",
102 : lpcfg_imessaging_path(state, lp_ctx));
103 8 : talloc_free(state);
104 8 : return;
105 : }
106 :
107 : /* Get a handle to notify the DNS server */
108 1471 : handle = irpc_binding_handle_by_name(state, state->msg_ctx,
109 : "dnssrv",
110 : &ndr_table_irpc);
111 1471 : if (handle == NULL) {
112 249 : imessaging_cleanup(state->msg_ctx);
113 249 : talloc_free(state);
114 249 : return;
115 : }
116 :
117 : /* Send the notifications */
118 1222 : req = dcerpc_dnssrv_reload_dns_zones_r_send(state,
119 : ldb_get_event_context(ldb),
120 : handle,
121 : &state->r);
122 1222 : if (req == NULL) {
123 0 : imessaging_cleanup(state->msg_ctx);
124 0 : talloc_free(state);
125 0 : return;
126 : }
127 1222 : tevent_req_set_callback(req, dns_notify_dnssrv_done, state);
128 : }
129 :
130 924119 : static int dns_notify_add(struct ldb_module *module, struct ldb_request *req)
131 : {
132 83787 : struct ldb_context *ldb;
133 83787 : struct dns_notify_private *data;
134 83787 : struct dns_notify_watched_dn *w;
135 83787 : struct dsdb_schema *schema;
136 83787 : const struct dsdb_class *objectclass;
137 :
138 924119 : if (ldb_dn_is_special(req->op.add.message->dn)) {
139 1517 : return ldb_next_request(module, req);
140 : }
141 :
142 922602 : if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
143 271837 : return ldb_next_request(module, req);
144 : }
145 :
146 650765 : ldb = ldb_module_get_ctx(module);
147 650765 : data = talloc_get_type(ldb_module_get_private(module),
148 : struct dns_notify_private);
149 650765 : if (data == NULL) {
150 0 : return ldb_operr(ldb);
151 : }
152 :
153 2600702 : for (w = data->watched; w; w = w->next) {
154 1951112 : if (ldb_dn_compare_base(w->dn, req->op.add.message->dn) == 0) {
155 15178 : schema = dsdb_get_schema(ldb, req);
156 15178 : if (schema == NULL) {
157 0 : return ldb_operr(ldb);
158 : }
159 :
160 15178 : objectclass = dsdb_get_structural_oc_from_msg(schema, req->op.add.message);
161 15178 : if (objectclass == NULL) {
162 0 : return ldb_operr(ldb);
163 : }
164 :
165 15178 : if (ldb_attr_cmp(objectclass->lDAPDisplayName, "dnsZone") == 0) {
166 1175 : data->reload_zones = true;
167 1175 : break;
168 : }
169 : }
170 : }
171 :
172 650765 : return ldb_next_request(module, req);
173 : }
174 :
175 1281725 : static int dns_notify_modify(struct ldb_module *module, struct ldb_request *req)
176 : {
177 30575 : TALLOC_CTX *tmp_ctx;
178 30575 : struct ldb_context *ldb;
179 30575 : struct dns_notify_private *data;
180 30575 : struct dns_notify_watched_dn *w;
181 30575 : struct ldb_dn *dn;
182 30575 : struct ldb_result *res;
183 30575 : struct dsdb_schema *schema;
184 30575 : const struct dsdb_class *objectclass;
185 1281725 : const char * const attrs[] = { "objectClass", NULL };
186 30575 : int ret;
187 :
188 1281725 : if (ldb_dn_is_special(req->op.mod.message->dn)) {
189 227832 : return ldb_next_request(module, req);
190 : }
191 :
192 1053893 : if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
193 19184 : return ldb_next_request(module, req);
194 : }
195 :
196 1034709 : ldb = ldb_module_get_ctx(module);
197 1034709 : data = talloc_get_type(ldb_module_get_private(module),
198 : struct dns_notify_private);
199 1034709 : if (data == NULL) {
200 0 : return ldb_operr(ldb);
201 : }
202 :
203 1034709 : tmp_ctx = talloc_new(module);
204 1034709 : if (tmp_ctx == NULL) {
205 0 : return ldb_oom(ldb);
206 : }
207 :
208 4137066 : for (w = data->watched; w; w = w->next) {
209 3103259 : if (ldb_dn_compare_base(w->dn, req->op.add.message->dn) == 0) {
210 10166 : dn = ldb_dn_copy(tmp_ctx, req->op.mod.message->dn);
211 :
212 10166 : ret = dsdb_module_search_dn(module, tmp_ctx, &res, dn, attrs,
213 : DSDB_FLAG_NEXT_MODULE |
214 : DSDB_SEARCH_SHOW_RECYCLED |
215 : DSDB_SEARCH_REVEAL_INTERNALS |
216 : DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT, req);
217 10166 : if (ret != LDB_SUCCESS) {
218 : /*
219 : * We want the give the caller the
220 : * error from trying the actual
221 : * request, below
222 : */
223 0 : break;
224 : }
225 :
226 10166 : schema = dsdb_get_schema(ldb, req);
227 10166 : if (schema == NULL) {
228 0 : talloc_free(tmp_ctx);
229 0 : return ldb_operr(ldb);
230 : }
231 :
232 10166 : objectclass = dsdb_get_structural_oc_from_msg(schema, res->msgs[0]);
233 10166 : if (objectclass == NULL) {
234 0 : talloc_free(tmp_ctx);
235 0 : return ldb_operr(ldb);
236 : }
237 :
238 10166 : if (ldb_attr_cmp(objectclass->lDAPDisplayName, "dnsZone") == 0) {
239 902 : data->reload_zones = true;
240 902 : break;
241 : }
242 : }
243 : }
244 :
245 1034709 : talloc_free(tmp_ctx);
246 1034709 : return ldb_next_request(module, req);
247 : }
248 :
249 17 : static int dns_notify_delete(struct ldb_module *module, struct ldb_request *req)
250 : {
251 9 : TALLOC_CTX *tmp_ctx;
252 9 : struct ldb_context *ldb;
253 9 : struct dns_notify_private *data;
254 9 : struct dns_notify_watched_dn *w;
255 9 : struct ldb_dn *old_dn;
256 9 : struct ldb_result *res;
257 9 : struct dsdb_schema *schema;
258 9 : const struct dsdb_class *objectclass;
259 17 : const char * const attrs[] = { "objectClass", NULL };
260 9 : int ret;
261 :
262 17 : if (ldb_dn_is_special(req->op.del.dn)) {
263 1 : return ldb_next_request(module, req);
264 : }
265 :
266 16 : if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
267 16 : return ldb_next_request(module, req);
268 : }
269 :
270 0 : ldb = ldb_module_get_ctx(module);
271 0 : data = talloc_get_type(ldb_module_get_private(module),
272 : struct dns_notify_private);
273 0 : if (data == NULL) {
274 0 : return ldb_operr(ldb);
275 : }
276 :
277 0 : tmp_ctx = talloc_new(module);
278 0 : if (tmp_ctx == NULL) {
279 0 : return ldb_oom(ldb);
280 : }
281 :
282 0 : for (w = data->watched; w; w = w->next) {
283 0 : if (ldb_dn_compare_base(w->dn, req->op.add.message->dn) == 0) {
284 0 : old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn);
285 0 : ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, attrs,
286 : DSDB_FLAG_NEXT_MODULE |
287 : DSDB_SEARCH_SHOW_RECYCLED |
288 : DSDB_SEARCH_REVEAL_INTERNALS |
289 : DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT, req);
290 0 : if (ret != LDB_SUCCESS) {
291 : /*
292 : * We want the give the caller the
293 : * error from trying the actual
294 : * request, below
295 : */
296 0 : break;
297 : }
298 :
299 0 : schema = dsdb_get_schema(ldb, req);
300 0 : if (schema == NULL) {
301 0 : talloc_free(tmp_ctx);
302 0 : return ldb_operr(ldb);
303 : }
304 :
305 0 : objectclass = dsdb_get_structural_oc_from_msg(schema, res->msgs[0]);
306 0 : if (objectclass == NULL) {
307 0 : talloc_free(tmp_ctx);
308 0 : return ldb_operr(ldb);
309 : }
310 :
311 0 : if (ldb_attr_cmp(objectclass->lDAPDisplayName, "dnsZone") == 0) {
312 0 : data->reload_zones = true;
313 0 : break;
314 : }
315 : }
316 : }
317 :
318 0 : talloc_free(tmp_ctx);
319 0 : return ldb_next_request(module, req);
320 : }
321 :
322 349735 : static int dns_notify_start_trans(struct ldb_module *module)
323 : {
324 2179 : struct ldb_context *ldb;
325 2179 : struct dns_notify_private *data;
326 :
327 349735 : ldb = ldb_module_get_ctx(module);
328 349735 : data = talloc_get_type(ldb_module_get_private(module),
329 : struct dns_notify_private);
330 349735 : if (data == NULL) {
331 0 : return ldb_operr(ldb);
332 : }
333 :
334 349735 : data->reload_zones = false;
335 :
336 349735 : return ldb_next_start_trans(module);
337 : }
338 :
339 304240 : static int dns_notify_end_trans(struct ldb_module *module)
340 : {
341 2174 : struct ldb_context *ldb;
342 2174 : struct dns_notify_private *data;
343 2174 : int ret;
344 :
345 304240 : ldb = ldb_module_get_ctx(module);
346 304240 : data = talloc_get_type(ldb_module_get_private(module),
347 : struct dns_notify_private);
348 304240 : if (data == NULL) {
349 0 : return ldb_operr(ldb);
350 : }
351 :
352 304240 : ret = ldb_next_end_trans(module);
353 304240 : if (ret == LDB_SUCCESS) {
354 304240 : if (data->reload_zones) {
355 1479 : dns_notify_dnssrv_send(module);
356 : }
357 : }
358 :
359 302066 : return ret;
360 : }
361 :
362 45493 : static int dns_notify_del_trans(struct ldb_module *module)
363 : {
364 4 : struct ldb_context *ldb;
365 4 : struct dns_notify_private *data;
366 :
367 45493 : ldb = ldb_module_get_ctx(module);
368 45493 : data = talloc_get_type(ldb_module_get_private(module),
369 : struct dns_notify_private);
370 45493 : if (data == NULL) {
371 0 : return ldb_operr(ldb);
372 : }
373 :
374 45493 : data->reload_zones = false;
375 :
376 45493 : return ldb_next_del_trans(module);
377 : }
378 :
379 182004 : static int dns_notify_init(struct ldb_module *module)
380 : {
381 6016 : struct ldb_context *ldb;
382 6016 : struct dns_notify_private *data;
383 6016 : struct dns_notify_watched_dn *watched;
384 6016 : struct ldb_dn *domain_dn;
385 6016 : struct ldb_dn *forest_dn;
386 :
387 182004 : ldb = ldb_module_get_ctx(module);
388 :
389 182004 : data = talloc_zero(module, struct dns_notify_private);
390 182004 : if (data == NULL) {
391 0 : return ldb_oom(ldb);
392 : }
393 :
394 182004 : domain_dn = ldb_get_default_basedn(ldb);
395 182004 : forest_dn = ldb_get_root_basedn(ldb);
396 :
397 : /* Register hook on domain partition */
398 182004 : watched = talloc_zero(data, struct dns_notify_watched_dn);
399 182004 : if (watched == NULL) {
400 0 : talloc_free(data);
401 0 : return ldb_oom(ldb);
402 : }
403 182004 : watched->dn = ldb_dn_new_fmt(watched, ldb,
404 : "CN=MicrosoftDNS,CN=System,%s",
405 : ldb_dn_get_linearized(domain_dn));
406 182004 : if (watched->dn == NULL) {
407 0 : talloc_free(data);
408 0 : return ldb_oom(ldb);
409 : }
410 182004 : DLIST_ADD(data->watched, watched);
411 :
412 : /* Check for DomainDnsZones partition and register hook */
413 182004 : watched = talloc_zero(data, struct dns_notify_watched_dn);
414 182004 : if (watched == NULL) {
415 0 : talloc_free(data);
416 0 : return ldb_oom(ldb);
417 : }
418 182004 : watched->dn = ldb_dn_new_fmt(watched, ldb, "CN=MicrosoftDNS,DC=DomainDnsZones,%s", ldb_dn_get_linearized(forest_dn));
419 182004 : DLIST_ADD(data->watched, watched);
420 :
421 : /* Check for ForestDnsZones partition and register hook */
422 182004 : watched = talloc_zero(data, struct dns_notify_watched_dn);
423 182004 : if (watched == NULL) {
424 0 : talloc_free(data);
425 0 : return ldb_oom(ldb);
426 : }
427 182004 : watched->dn = ldb_dn_new_fmt(watched, ldb, "CN=MicrosoftDNS,DC=ForestDnsZones,%s", ldb_dn_get_linearized(forest_dn));
428 182004 : DLIST_ADD(data->watched, watched);
429 :
430 182004 : ldb_module_set_private(module, data);
431 :
432 182004 : 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 6040 : int ldb_dns_notify_module_init(const char *version)
447 : {
448 6040 : LDB_MODULE_CHECK_VERSION(version);
449 6040 : return ldb_register_module(&ldb_dns_notify_module_ops);
450 : }
|