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 "dsdb/samdb/samdb.h"
26 #include "dsdb/common/util.h"
27 #include "auth/session.h"
28 #include "gen_ndr/ndr_dnsp.h"
29 #include "dlz_bind9.h"
31 struct dlz_bind9_data {
32 struct ldb_context *samdb;
33 struct tevent_context *ev_ctx;
34 struct loadparm_context *lp;
36 /* helper functions from the dlz_dlopen driver */
37 void (*log)(int level, const char *fmt, ...);
38 isc_result_t (*putrr)(dns_sdlzlookup_t *handle, const char *type,
39 dns_ttl_t ttl, const char *data);
40 isc_result_t (*putnamedrr)(dns_sdlzlookup_t *handle, const char *name,
41 const char *type, dns_ttl_t ttl, const char *data);
45 return the version of the API
47 _PUBLIC_ int dlz_version(void)
49 return DLZ_DLOPEN_VERSION;
53 remember a helper function from the bind9 dlz_dlopen driver
55 static void b9_add_helper(struct dlz_bind9_data *state, const char *helper_name, void *ptr)
57 if (strcmp(helper_name, "log") == 0) {
60 if (strcmp(helper_name, "putrr") == 0) {
63 if (strcmp(helper_name, "putnamedrr") == 0) {
64 state->putnamedrr = ptr;
69 format a record for bind9
71 static bool b9_format(struct dlz_bind9_data *state,
73 struct dnsp_DnssrvRpcRecord *rec,
74 const char **type, const char **data)
79 *data = rec->data.ipv4;
84 *data = rec->data.ipv6;
89 *data = rec->data.cname;
94 *data = rec->data.txt;
99 *data = rec->data.ptr;
104 *data = talloc_asprintf(mem_ctx, "%u %u %u %s",
105 rec->data.srv.wPriority,
106 rec->data.srv.wWeight,
108 rec->data.srv.nameTarget);
113 *data = talloc_asprintf(mem_ctx, "%u %s",
114 rec->data.srv.wPriority,
115 rec->data.srv.nameTarget);
120 *data = talloc_asprintf(mem_ctx, "%s %s",
127 *data = rec->data.ns;
132 *data = talloc_asprintf(mem_ctx, "%s %s %u %u %u %u %u",
135 rec->data.soa.serial,
136 rec->data.soa.refresh,
138 rec->data.soa.expire,
139 rec->data.soa.minimum);
143 state->log(ISC_LOG_ERROR, "samba b9_putrr: unhandled record type %u",
152 send a resource recond to bind9
154 static isc_result_t b9_putrr(struct dlz_bind9_data *state,
155 void *handle, struct dnsp_DnssrvRpcRecord *rec,
159 const char *type, *data;
160 TALLOC_CTX *tmp_ctx = talloc_new(state);
162 if (!b9_format(state, tmp_ctx, rec, &type, &data)) {
163 return ISC_R_FAILURE;
167 talloc_free(tmp_ctx);
168 return ISC_R_NOMEMORY;
173 for (i=0; types[i]; i++) {
174 if (strcmp(types[i], type) == 0) break;
176 if (types[i] == NULL) {
178 return ISC_R_SUCCESS;
182 /* FIXME: why does dlz insist on all TTL values being the same
183 for the same name? */
184 result = state->putrr(handle, type, /* rec->dwTtlSeconds */ 900, data);
185 if (result != ISC_R_SUCCESS) {
186 state->log(ISC_LOG_ERROR, "Failed to put rr");
188 talloc_free(tmp_ctx);
194 send a named resource recond to bind9
196 static isc_result_t b9_putnamedrr(struct dlz_bind9_data *state,
197 void *handle, const char *name,
198 struct dnsp_DnssrvRpcRecord *rec)
201 const char *type, *data;
202 TALLOC_CTX *tmp_ctx = talloc_new(state);
204 if (!b9_format(state, tmp_ctx, rec, &type, &data)) {
205 return ISC_R_FAILURE;
209 talloc_free(tmp_ctx);
210 return ISC_R_NOMEMORY;
213 /* FIXME: why does dlz insist on all TTL values being the same
214 for the same name? */
215 result = state->putnamedrr(handle, name, type, /* rec->dwTtlSeconds */ 900, data);
216 if (result != ISC_R_SUCCESS) {
217 state->log(ISC_LOG_ERROR, "Failed to put named rr '%s'", name);
219 talloc_free(tmp_ctx);
225 called to initialise the driver
227 _PUBLIC_ isc_result_t dlz_create(const char *dlzname,
228 unsigned int argc, char *argv[],
229 void *driverarg, void **dbdata, ...)
231 struct dlz_bind9_data *state;
232 const char *helper_name;
240 state = talloc_zero(NULL, struct dlz_bind9_data);
242 return ISC_R_NOMEMORY;
245 tmp_ctx = talloc_new(state);
247 /* fill in the helper functions */
248 va_start(ap, dbdata);
249 while ((helper_name = va_arg(ap, const char *)) != NULL) {
250 b9_add_helper(state, helper_name, va_arg(ap, void*));
254 state->lp = loadparm_init_global(true);
255 if (state->lp == NULL) {
256 result = ISC_R_NOMEMORY;
260 state->ev_ctx = tevent_context_init(state);
261 if (state->ev_ctx == NULL) {
262 result = ISC_R_NOMEMORY;
266 state->samdb = ldb_init(state, state->ev_ctx);
267 if (state->samdb == NULL) {
268 state->log(ISC_LOG_ERROR, "samba dlz_bind9: Failed to create ldb");
269 result = ISC_R_FAILURE;
273 url = talloc_asprintf(tmp_ctx, "ldapi://%s",
274 private_path(tmp_ctx, state->lp, "ldap_priv/ldapi"));
276 result = ISC_R_NOMEMORY;
280 ret = ldb_connect(state->samdb, url, 0, NULL);
282 state->log(ISC_LOG_ERROR, "samba dlz_bind9: Failed to connect to %s - %s",
283 url, ldb_errstring(state->samdb));
284 result = ISC_R_FAILURE;
288 dn = ldb_get_default_basedn(state->samdb);
290 state->log(ISC_LOG_ERROR, "samba dlz_bind9: Unable to get basedn for %s - %s",
291 url, ldb_errstring(state->samdb));
292 result = ISC_R_FAILURE;
296 state->log(ISC_LOG_INFO, "samba dlz_bind9: started for DN %s",
297 ldb_dn_get_linearized(dn));
301 talloc_free(tmp_ctx);
302 return ISC_R_SUCCESS;
312 _PUBLIC_ void dlz_destroy(void *driverarg, void *dbdata)
314 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
315 state->log(ISC_LOG_INFO, "samba dlz_bind9: shutting down");
321 see if we handle a given zone
323 _PUBLIC_ isc_result_t dlz_findzonedb(void *driverarg, void *dbdata, const char *name)
325 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
326 if (strcasecmp(lpcfg_dnsdomain(state->lp), name) == 0) {
327 return ISC_R_SUCCESS;
329 return ISC_R_NOTFOUND;
336 _PUBLIC_ isc_result_t dlz_lookup_types(struct dlz_bind9_data *state,
337 const char *zone, const char *name,
338 void *driverarg, dns_sdlzlookup_t *lookup,
342 TALLOC_CTX *tmp_ctx = talloc_new(state);
343 const char *attrs[] = { "dnsRecord", NULL };
345 struct ldb_result *res;
346 struct ldb_message_element *el;
348 dn = ldb_dn_copy(tmp_ctx, ldb_get_default_basedn(state->samdb));
350 talloc_free(tmp_ctx);
351 return ISC_R_NOMEMORY;
354 if (!ldb_dn_add_child_fmt(dn, "DC=%s,DC=%s,CN=MicrosoftDNS,DC=DomainDnsZones",
356 talloc_free(tmp_ctx);
357 return ISC_R_NOMEMORY;
360 ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_BASE,
361 attrs, "objectClass=dnsNode");
362 if (ret != LDB_SUCCESS) {
363 talloc_free(tmp_ctx);
364 return ISC_R_NOTFOUND;
367 el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
368 if (el == NULL || el->num_values == 0) {
369 state->log(ISC_LOG_INFO, "failed to find %s",
370 ldb_dn_get_linearized(dn));
371 talloc_free(tmp_ctx);
372 return ISC_R_NOTFOUND;
375 for (i=0; i<el->num_values; i++) {
376 struct dnsp_DnssrvRpcRecord rec;
377 enum ndr_err_code ndr_err;
380 ndr_err = ndr_pull_struct_blob(&el->values[i], tmp_ctx, &rec,
381 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
382 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
383 state->log(ISC_LOG_ERROR, "samba dlz_bind9: failed to parse dnsRecord for %s",
384 ldb_dn_get_linearized(dn));
385 talloc_free(tmp_ctx);
386 return ISC_R_FAILURE;
389 result = b9_putrr(state, lookup, &rec, types);
390 if (result != ISC_R_SUCCESS) {
391 talloc_free(tmp_ctx);
396 talloc_free(tmp_ctx);
397 return ISC_R_SUCCESS;
403 _PUBLIC_ isc_result_t dlz_lookup(const char *zone, const char *name, void *driverarg,
404 void *dbdata, dns_sdlzlookup_t *lookup)
406 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
407 return dlz_lookup_types(state, zone, name, driverarg, lookup, NULL);
412 see if a zone transfer is allowed
414 _PUBLIC_ isc_result_t dlz_allowzonexfr(void *driverarg, void *dbdata, const char *name,
417 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
419 if (strcasecmp(lpcfg_dnsdomain(state->lp), name) == 0) {
420 /* TODO: check an ACL here? client is the IP of the requester */
421 state->log(ISC_LOG_INFO, "samba dlz_bind9: allowing zone transfer for '%s' by '%s'",
423 return ISC_R_SUCCESS;
425 return ISC_R_NOTFOUND;
429 perform a zone transfer
431 _PUBLIC_ isc_result_t dlz_allnodes(const char *zone, void *driverarg, void *dbdata,
432 dns_sdlzallnodes_t *allnodes)
434 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
435 const char *attrs[] = { "dnsRecord", NULL };
438 struct ldb_result *res;
439 TALLOC_CTX *tmp_ctx = talloc_new(state);
442 dn = ldb_dn_copy(tmp_ctx, ldb_get_default_basedn(state->samdb));
444 talloc_free(tmp_ctx);
445 return ISC_R_NOMEMORY;
448 if (!ldb_dn_add_child_fmt(dn, "DC=%s,CN=MicrosoftDNS,DC=DomainDnsZones", zone)) {
449 talloc_free(tmp_ctx);
450 return ISC_R_NOMEMORY;
453 ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_SUBTREE,
454 attrs, "objectClass=dnsNode");
455 if (ret != LDB_SUCCESS) {
456 talloc_free(tmp_ctx);
457 return ISC_R_NOTFOUND;
460 for (i=0; i<res->count; i++) {
461 struct ldb_message_element *el;
462 TALLOC_CTX *el_ctx = talloc_new(tmp_ctx);
463 const char *rdn, *name;
464 const struct ldb_val *v;
466 el = ldb_msg_find_element(res->msgs[i], "dnsRecord");
467 if (el == NULL || el->num_values == 0) {
468 state->log(ISC_LOG_INFO, "failed to find dnsRecord for %s",
469 ldb_dn_get_linearized(dn));
474 v = ldb_dn_get_rdn_val(res->msgs[i]->dn);
476 state->log(ISC_LOG_INFO, "failed to find RDN for %s",
477 ldb_dn_get_linearized(dn));
482 rdn = talloc_strndup(el_ctx, (char *)v->data, v->length);
484 talloc_free(tmp_ctx);
485 return ISC_R_NOMEMORY;
488 if (strcmp(rdn, "@") == 0) {
491 name = talloc_asprintf(el_ctx, "%s.%s", rdn, zone);
494 talloc_free(tmp_ctx);
495 return ISC_R_NOMEMORY;
498 for (j=0; j<el->num_values; j++) {
499 struct dnsp_DnssrvRpcRecord rec;
500 enum ndr_err_code ndr_err;
503 ndr_err = ndr_pull_struct_blob(&el->values[j], el_ctx, &rec,
504 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
505 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
506 state->log(ISC_LOG_ERROR, "samba dlz_bind9: failed to parse dnsRecord for %s",
507 ldb_dn_get_linearized(dn));
512 result = b9_putnamedrr(state, allnodes, name, &rec);
513 if (result != ISC_R_SUCCESS) {
520 talloc_free(tmp_ctx);
522 return ISC_R_SUCCESS;