2 Unix SMB/CIFS implementation.
4 bind9 dlz driver for Samba
6 Copyright (C) 2010 Andrew Tridgell
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "param/param.h"
25 #include "lib/events/events.h"
26 #include "dsdb/samdb/samdb.h"
27 #include "dsdb/common/util.h"
28 #include "auth/session.h"
29 #include "auth/gensec/gensec.h"
30 #include "gen_ndr/ndr_dnsp.h"
31 #include "lib/cmdline/popt_common.h"
32 #include "lib/cmdline/popt_credentials.h"
33 #include "ldb_module.h"
34 #include "dlz_bind9.h"
36 struct dlz_bind9_data {
37 struct ldb_context *samdb;
38 struct tevent_context *ev_ctx;
39 struct loadparm_context *lp;
41 /* helper functions from the dlz_dlopen driver */
42 void (*log)(int level, const char *fmt, ...);
43 isc_result_t (*putrr)(dns_sdlzlookup_t *handle, const char *type,
44 dns_ttl_t ttl, const char *data);
45 isc_result_t (*putnamedrr)(dns_sdlzlookup_t *handle, const char *name,
46 const char *type, dns_ttl_t ttl, const char *data);
50 return the version of the API
52 _PUBLIC_ int dlz_version(unsigned int *flags)
54 return DLZ_DLOPEN_VERSION;
58 remember a helper function from the bind9 dlz_dlopen driver
60 static void b9_add_helper(struct dlz_bind9_data *state, const char *helper_name, void *ptr)
62 if (strcmp(helper_name, "log") == 0) {
65 if (strcmp(helper_name, "putrr") == 0) {
68 if (strcmp(helper_name, "putnamedrr") == 0) {
69 state->putnamedrr = ptr;
74 format a record for bind9
76 static bool b9_format(struct dlz_bind9_data *state,
78 struct dnsp_DnssrvRpcRecord *rec,
79 const char **type, const char **data)
84 *data = rec->data.ipv4;
89 *data = rec->data.ipv6;
94 *data = rec->data.cname;
99 *data = rec->data.txt;
104 *data = rec->data.ptr;
109 *data = talloc_asprintf(mem_ctx, "%u %u %u %s",
110 rec->data.srv.wPriority,
111 rec->data.srv.wWeight,
113 rec->data.srv.nameTarget);
118 *data = talloc_asprintf(mem_ctx, "%u %s",
119 rec->data.srv.wPriority,
120 rec->data.srv.nameTarget);
125 *data = talloc_asprintf(mem_ctx, "%s %s",
132 *data = rec->data.ns;
137 *data = talloc_asprintf(mem_ctx, "%s %s %u %u %u %u %u",
140 rec->data.soa.serial,
141 rec->data.soa.refresh,
143 rec->data.soa.expire,
144 rec->data.soa.minimum);
148 state->log(ISC_LOG_ERROR, "samba b9_putrr: unhandled record type %u",
157 send a resource recond to bind9
159 static isc_result_t b9_putrr(struct dlz_bind9_data *state,
160 void *handle, struct dnsp_DnssrvRpcRecord *rec,
164 const char *type, *data;
165 TALLOC_CTX *tmp_ctx = talloc_new(state);
167 if (!b9_format(state, tmp_ctx, rec, &type, &data)) {
168 return ISC_R_FAILURE;
172 talloc_free(tmp_ctx);
173 return ISC_R_NOMEMORY;
178 for (i=0; types[i]; i++) {
179 if (strcmp(types[i], type) == 0) break;
181 if (types[i] == NULL) {
183 return ISC_R_SUCCESS;
187 result = state->putrr(handle, type, rec->dwTtlSeconds, data);
188 if (result != ISC_R_SUCCESS) {
189 state->log(ISC_LOG_ERROR, "Failed to put rr");
191 talloc_free(tmp_ctx);
197 send a named resource recond to bind9
199 static isc_result_t b9_putnamedrr(struct dlz_bind9_data *state,
200 void *handle, const char *name,
201 struct dnsp_DnssrvRpcRecord *rec)
204 const char *type, *data;
205 TALLOC_CTX *tmp_ctx = talloc_new(state);
207 if (!b9_format(state, tmp_ctx, rec, &type, &data)) {
208 return ISC_R_FAILURE;
212 talloc_free(tmp_ctx);
213 return ISC_R_NOMEMORY;
216 result = state->putnamedrr(handle, name, type, rec->dwTtlSeconds, data);
217 if (result != ISC_R_SUCCESS) {
218 state->log(ISC_LOG_ERROR, "Failed to put named rr '%s'", name);
220 talloc_free(tmp_ctx);
231 static isc_result_t parse_options(struct dlz_bind9_data *state,
232 unsigned int argc, char *argv[],
233 struct b9_options *options)
237 struct poptOption long_options[] = {
238 { "url", 'H', POPT_ARG_STRING, &options->url, 0, "database URL", "URL" },
241 struct poptOption **popt_options;
244 popt_options = ldb_module_popt_options(state->samdb);
245 (*popt_options) = long_options;
247 ret = ldb_modules_hook(state->samdb, LDB_MODULE_HOOK_CMDLINE_OPTIONS);
248 if (ret != LDB_SUCCESS) {
249 state->log(ISC_LOG_ERROR, "dlz samba: failed cmdline hook");
250 return ISC_R_FAILURE;
253 pc = poptGetContext("dlz_bind9", argc, (const char **)argv, *popt_options,
254 POPT_CONTEXT_KEEP_FIRST);
256 while ((opt = poptGetNextOpt(pc)) != -1) {
259 state->log(ISC_LOG_ERROR, "dlz samba: Invalid option %s: %s",
260 poptBadOption(pc, 0), poptStrerror(opt));
261 return ISC_R_FAILURE;
265 ret = ldb_modules_hook(state->samdb, LDB_MODULE_HOOK_CMDLINE_PRECONNECT);
266 if (ret != LDB_SUCCESS) {
267 state->log(ISC_LOG_ERROR, "dlz samba: failed cmdline preconnect");
268 return ISC_R_FAILURE;
271 return ISC_R_SUCCESS;
276 called to initialise the driver
278 _PUBLIC_ isc_result_t dlz_create(const char *dlzname,
279 unsigned int argc, char *argv[],
280 void *driverarg, void **dbdata, ...)
282 struct dlz_bind9_data *state;
283 const char *helper_name;
289 struct b9_options options;
291 ZERO_STRUCT(options);
293 state = talloc_zero(NULL, struct dlz_bind9_data);
295 return ISC_R_NOMEMORY;
298 tmp_ctx = talloc_new(state);
300 /* fill in the helper functions */
301 va_start(ap, dbdata);
302 while ((helper_name = va_arg(ap, const char *)) != NULL) {
303 b9_add_helper(state, helper_name, va_arg(ap, void*));
307 state->ev_ctx = s4_event_context_init(state);
308 if (state->ev_ctx == NULL) {
309 result = ISC_R_NOMEMORY;
313 state->samdb = ldb_init(state, state->ev_ctx);
314 if (state->samdb == NULL) {
315 state->log(ISC_LOG_ERROR, "samba dlz_bind9: Failed to create ldb");
316 result = ISC_R_FAILURE;
320 result = parse_options(state, argc, argv, &options);
321 if (result != ISC_R_SUCCESS) {
325 state->lp = loadparm_init_global(true);
326 if (state->lp == NULL) {
327 result = ISC_R_NOMEMORY;
331 if (options.url == NULL) {
332 options.url = talloc_asprintf(tmp_ctx, "ldapi://%s",
333 private_path(tmp_ctx, state->lp, "ldap_priv/ldapi"));
334 if (options.url == NULL) {
335 result = ISC_R_NOMEMORY;
340 ret = ldb_connect(state->samdb, options.url, 0, NULL);
342 state->log(ISC_LOG_ERROR, "samba dlz_bind9: Failed to connect to %s - %s",
343 options.url, ldb_errstring(state->samdb));
344 result = ISC_R_FAILURE;
348 ret = ldb_modules_hook(state->samdb, LDB_MODULE_HOOK_CMDLINE_POSTCONNECT);
349 if (ret != LDB_SUCCESS) {
350 state->log(ISC_LOG_ERROR, "samba dlz_bind9: Failed postconnect for %s - %s",
351 options.url, ldb_errstring(state->samdb));
352 result = ISC_R_FAILURE;
356 dn = ldb_get_default_basedn(state->samdb);
358 state->log(ISC_LOG_ERROR, "samba dlz_bind9: Unable to get basedn for %s - %s",
359 options.url, ldb_errstring(state->samdb));
360 result = ISC_R_FAILURE;
364 state->log(ISC_LOG_INFO, "samba dlz_bind9: started for DN %s",
365 ldb_dn_get_linearized(dn));
369 talloc_free(tmp_ctx);
370 return ISC_R_SUCCESS;
380 _PUBLIC_ void dlz_destroy(void *driverarg, void *dbdata)
382 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
383 state->log(ISC_LOG_INFO, "samba dlz_bind9: shutting down");
389 see if we handle a given zone
391 _PUBLIC_ isc_result_t dlz_findzonedb(void *driverarg, void *dbdata, const char *name)
393 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
394 if (strcasecmp(lpcfg_dnsdomain(state->lp), name) == 0) {
395 return ISC_R_SUCCESS;
397 return ISC_R_NOTFOUND;
404 static isc_result_t dlz_lookup_types(struct dlz_bind9_data *state,
405 const char *zone, const char *name,
406 void *driverarg, dns_sdlzlookup_t *lookup,
410 TALLOC_CTX *tmp_ctx = talloc_new(state);
411 const char *attrs[] = { "dnsRecord", NULL };
413 struct ldb_result *res;
414 struct ldb_message_element *el;
416 dn = ldb_dn_copy(tmp_ctx, ldb_get_default_basedn(state->samdb));
418 talloc_free(tmp_ctx);
419 return ISC_R_NOMEMORY;
422 if (!ldb_dn_add_child_fmt(dn, "DC=%s,DC=%s,CN=MicrosoftDNS,DC=DomainDnsZones",
424 talloc_free(tmp_ctx);
425 return ISC_R_NOMEMORY;
428 ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_BASE,
429 attrs, "objectClass=dnsNode");
430 if (ret != LDB_SUCCESS) {
431 talloc_free(tmp_ctx);
432 return ISC_R_NOTFOUND;
435 el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
436 if (el == NULL || el->num_values == 0) {
437 state->log(ISC_LOG_INFO, "failed to find %s",
438 ldb_dn_get_linearized(dn));
439 talloc_free(tmp_ctx);
440 return ISC_R_NOTFOUND;
443 for (i=0; i<el->num_values; i++) {
444 struct dnsp_DnssrvRpcRecord rec;
445 enum ndr_err_code ndr_err;
448 ndr_err = ndr_pull_struct_blob(&el->values[i], tmp_ctx, &rec,
449 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
450 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
451 state->log(ISC_LOG_ERROR, "samba dlz_bind9: failed to parse dnsRecord for %s",
452 ldb_dn_get_linearized(dn));
453 talloc_free(tmp_ctx);
454 return ISC_R_FAILURE;
457 result = b9_putrr(state, lookup, &rec, types);
458 if (result != ISC_R_SUCCESS) {
459 talloc_free(tmp_ctx);
464 talloc_free(tmp_ctx);
465 return ISC_R_SUCCESS;
471 _PUBLIC_ isc_result_t dlz_lookup(const char *zone, const char *name, void *driverarg,
472 void *dbdata, dns_sdlzlookup_t *lookup)
474 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
475 return dlz_lookup_types(state, zone, name, driverarg, lookup, NULL);
480 see if a zone transfer is allowed
482 _PUBLIC_ isc_result_t dlz_allowzonexfr(void *driverarg, void *dbdata, const char *name,
485 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
487 if (strcasecmp(lpcfg_dnsdomain(state->lp), name) == 0) {
488 /* TODO: check an ACL here? client is the IP of the requester */
489 state->log(ISC_LOG_INFO, "samba dlz_bind9: allowing zone transfer for '%s' by '%s'",
491 return ISC_R_SUCCESS;
493 return ISC_R_NOTFOUND;
497 perform a zone transfer
499 _PUBLIC_ isc_result_t dlz_allnodes(const char *zone, void *driverarg, void *dbdata,
500 dns_sdlzallnodes_t *allnodes)
502 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
503 const char *attrs[] = { "dnsRecord", NULL };
506 struct ldb_result *res;
507 TALLOC_CTX *tmp_ctx = talloc_new(state);
510 dn = ldb_dn_copy(tmp_ctx, ldb_get_default_basedn(state->samdb));
512 talloc_free(tmp_ctx);
513 return ISC_R_NOMEMORY;
516 if (!ldb_dn_add_child_fmt(dn, "DC=%s,CN=MicrosoftDNS,DC=DomainDnsZones", zone)) {
517 talloc_free(tmp_ctx);
518 return ISC_R_NOMEMORY;
521 ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_SUBTREE,
522 attrs, "objectClass=dnsNode");
523 if (ret != LDB_SUCCESS) {
524 talloc_free(tmp_ctx);
525 return ISC_R_NOTFOUND;
528 for (i=0; i<res->count; i++) {
529 struct ldb_message_element *el;
530 TALLOC_CTX *el_ctx = talloc_new(tmp_ctx);
531 const char *rdn, *name;
532 const struct ldb_val *v;
534 el = ldb_msg_find_element(res->msgs[i], "dnsRecord");
535 if (el == NULL || el->num_values == 0) {
536 state->log(ISC_LOG_INFO, "failed to find dnsRecord for %s",
537 ldb_dn_get_linearized(dn));
542 v = ldb_dn_get_rdn_val(res->msgs[i]->dn);
544 state->log(ISC_LOG_INFO, "failed to find RDN for %s",
545 ldb_dn_get_linearized(dn));
550 rdn = talloc_strndup(el_ctx, (char *)v->data, v->length);
552 talloc_free(tmp_ctx);
553 return ISC_R_NOMEMORY;
556 if (strcmp(rdn, "@") == 0) {
559 name = talloc_asprintf(el_ctx, "%s.%s", rdn, zone);
562 talloc_free(tmp_ctx);
563 return ISC_R_NOMEMORY;
566 for (j=0; j<el->num_values; j++) {
567 struct dnsp_DnssrvRpcRecord rec;
568 enum ndr_err_code ndr_err;
571 ndr_err = ndr_pull_struct_blob(&el->values[j], el_ctx, &rec,
572 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
573 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
574 state->log(ISC_LOG_ERROR, "samba dlz_bind9: failed to parse dnsRecord for %s",
575 ldb_dn_get_linearized(dn));
580 result = b9_putnamedrr(state, allnodes, name, &rec);
581 if (result != ISC_R_SUCCESS) {
588 talloc_free(tmp_ctx);
590 return ISC_R_SUCCESS;