s4-dns: support Samba command line options to the dlz_bind.so module
[samba.git] / source4 / dns_server / dlz_bind9.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    bind9 dlz driver for Samba
5
6    Copyright (C) 2010 Andrew Tridgell
7
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.
12
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.
17
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/>.
20 */
21
22 #include "includes.h"
23 #include "talloc.h"
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 "lib/cmdline/popt_common.h"
30 #include "dlz_bind9.h"
31
32 struct dlz_bind9_data {
33         struct ldb_context *samdb;
34         struct tevent_context *ev_ctx;
35         struct loadparm_context *lp;
36
37         /* helper functions from the dlz_dlopen driver */
38         void (*log)(int level, const char *fmt, ...);
39         isc_result_t (*putrr)(dns_sdlzlookup_t *handle, const char *type,
40                               dns_ttl_t ttl, const char *data);
41         isc_result_t (*putnamedrr)(dns_sdlzlookup_t *handle, const char *name,
42                                    const char *type, dns_ttl_t ttl, const char *data);
43 };
44
45 /*
46   return the version of the API
47  */
48 _PUBLIC_ int dlz_version(unsigned int *flags)
49 {
50         return DLZ_DLOPEN_VERSION;
51 }
52
53 /*
54    remember a helper function from the bind9 dlz_dlopen driver
55  */
56 static void b9_add_helper(struct dlz_bind9_data *state, const char *helper_name, void *ptr)
57 {
58         if (strcmp(helper_name, "log") == 0) {
59                 state->log = ptr;
60         }
61         if (strcmp(helper_name, "putrr") == 0) {
62                 state->putrr = ptr;
63         }
64         if (strcmp(helper_name, "putnamedrr") == 0) {
65                 state->putnamedrr = ptr;
66         }
67 }
68
69 /*
70   format a record for bind9
71  */
72 static bool b9_format(struct dlz_bind9_data *state,
73                       TALLOC_CTX *mem_ctx,
74                       struct dnsp_DnssrvRpcRecord *rec,
75                       const char **type, const char **data)
76 {
77         switch (rec->wType) {
78         case DNS_TYPE_A:
79                 *type = "a";
80                 *data = rec->data.ipv4;
81                 break;
82
83         case DNS_TYPE_AAAA:
84                 *type = "aaaa";
85                 *data = rec->data.ipv6;
86                 break;
87
88         case DNS_TYPE_CNAME:
89                 *type = "cname";
90                 *data = rec->data.cname;
91                 break;
92
93         case DNS_TYPE_TXT:
94                 *type = "txt";
95                 *data = rec->data.txt;
96                 break;
97
98         case DNS_TYPE_PTR:
99                 *type = "ptr";
100                 *data = rec->data.ptr;
101                 break;
102
103         case DNS_TYPE_SRV:
104                 *type = "srv";
105                 *data = talloc_asprintf(mem_ctx, "%u %u %u %s",
106                                         rec->data.srv.wPriority,
107                                         rec->data.srv.wWeight,
108                                         rec->data.srv.wPort,
109                                         rec->data.srv.nameTarget);
110                 break;
111
112         case DNS_TYPE_MX:
113                 *type = "mx";
114                 *data = talloc_asprintf(mem_ctx, "%u %s",
115                                         rec->data.srv.wPriority,
116                                         rec->data.srv.nameTarget);
117                 break;
118
119         case DNS_TYPE_HINFO:
120                 *type = "hinfo";
121                 *data = talloc_asprintf(mem_ctx, "%s %s",
122                                         rec->data.hinfo.cpu,
123                                         rec->data.hinfo.os);
124                 break;
125
126         case DNS_TYPE_NS:
127                 *type = "ns";
128                 *data = rec->data.ns;
129                 break;
130
131         case DNS_TYPE_SOA:
132                 *type = "soa";
133                 *data = talloc_asprintf(mem_ctx, "%s %s %u %u %u %u %u",
134                                         rec->data.soa.mname,
135                                         rec->data.soa.rname,
136                                         rec->data.soa.serial,
137                                         rec->data.soa.refresh,
138                                         rec->data.soa.retry,
139                                         rec->data.soa.expire,
140                                         rec->data.soa.minimum);
141                 break;
142
143         default:
144                 state->log(ISC_LOG_ERROR, "samba b9_putrr: unhandled record type %u",
145                            rec->wType);
146                 return false;
147         }
148
149         return true;
150 }
151
152 /*
153   send a resource recond to bind9
154  */
155 static isc_result_t b9_putrr(struct dlz_bind9_data *state,
156                              void *handle, struct dnsp_DnssrvRpcRecord *rec,
157                              const char **types)
158 {
159         isc_result_t result;
160         const char *type, *data;
161         TALLOC_CTX *tmp_ctx = talloc_new(state);
162
163         if (!b9_format(state, tmp_ctx, rec, &type, &data)) {
164                 return ISC_R_FAILURE;
165         }
166
167         if (data == NULL) {
168                 talloc_free(tmp_ctx);
169                 return ISC_R_NOMEMORY;
170         }
171
172         if (types) {
173                 int i;
174                 for (i=0; types[i]; i++) {
175                         if (strcmp(types[i], type) == 0) break;
176                 }
177                 if (types[i] == NULL) {
178                         /* skip it */
179                         return ISC_R_SUCCESS;
180                 }
181         }
182
183         /* FIXME: why does dlz insist on all TTL values being the same
184            for the same name? */
185         result = state->putrr(handle, type, /* rec->dwTtlSeconds */ 900, data);
186         if (result != ISC_R_SUCCESS) {
187                 state->log(ISC_LOG_ERROR, "Failed to put rr");
188         }
189         talloc_free(tmp_ctx);
190         return result;
191 }
192
193
194 /*
195   send a named resource recond to bind9
196  */
197 static isc_result_t b9_putnamedrr(struct dlz_bind9_data *state,
198                                   void *handle, const char *name,
199                                   struct dnsp_DnssrvRpcRecord *rec)
200 {
201         isc_result_t result;
202         const char *type, *data;
203         TALLOC_CTX *tmp_ctx = talloc_new(state);
204
205         if (!b9_format(state, tmp_ctx, rec, &type, &data)) {
206                 return ISC_R_FAILURE;
207         }
208
209         if (data == NULL) {
210                 talloc_free(tmp_ctx);
211                 return ISC_R_NOMEMORY;
212         }
213
214         /* FIXME: why does dlz insist on all TTL values being the same
215            for the same name? */
216         result = state->putnamedrr(handle, name, type, /* rec->dwTtlSeconds */ 900, data);
217         if (result != ISC_R_SUCCESS) {
218                 state->log(ISC_LOG_ERROR, "Failed to put named rr '%s'", name);
219         }
220         talloc_free(tmp_ctx);
221         return result;
222 }
223
224
225 /*
226    parse options
227  */
228 static isc_result_t parse_options(struct dlz_bind9_data *state,
229                                   unsigned int argc, char *argv[])
230 {
231         int opt;
232         poptContext pc;
233         struct poptOption long_options[] = {
234                 POPT_COMMON_SAMBA
235                 { NULL }
236         };
237
238         pc = poptGetContext("dlz_bind9", argc, (const char **)argv, long_options,
239                             POPT_CONTEXT_KEEP_FIRST);
240
241         while ((opt = poptGetNextOpt(pc)) != -1) {
242                 switch (opt) {
243                 default:
244                         state->log(ISC_LOG_ERROR, "Invalid option %s: %s",
245                                    poptBadOption(pc, 0), poptStrerror(opt));
246                         return ISC_R_FAILURE;
247                 }
248         }
249
250         return ISC_R_SUCCESS;
251 }
252
253
254 /*
255   called to initialise the driver
256  */
257 _PUBLIC_ isc_result_t dlz_create(const char *dlzname,
258                                  unsigned int argc, char *argv[],
259                                  void *driverarg, void **dbdata, ...)
260 {
261         struct dlz_bind9_data *state;
262         const char *helper_name;
263         va_list ap;
264         isc_result_t result;
265         const char *url;
266         TALLOC_CTX *tmp_ctx;
267         int ret;
268         struct ldb_dn *dn;
269
270         state = talloc_zero(NULL, struct dlz_bind9_data);
271         if (state == NULL) {
272                 return ISC_R_NOMEMORY;
273         }
274
275         tmp_ctx = talloc_new(state);
276
277         /* fill in the helper functions */
278         va_start(ap, dbdata);
279         while ((helper_name = va_arg(ap, const char *)) != NULL) {
280                 b9_add_helper(state, helper_name, va_arg(ap, void*));
281         }
282         va_end(ap);
283
284         result = parse_options(state, argc, argv);
285         if (result != ISC_R_SUCCESS) {
286                 goto failed;
287         }
288
289         state->lp = loadparm_init_global(true);
290         if (state->lp == NULL) {
291                 result = ISC_R_NOMEMORY;
292                 goto failed;
293         }
294
295         state->ev_ctx = tevent_context_init(state);
296         if (state->ev_ctx == NULL) {
297                 result = ISC_R_NOMEMORY;
298                 goto failed;
299         }
300
301         state->samdb = ldb_init(state, state->ev_ctx);
302         if (state->samdb == NULL) {
303                 state->log(ISC_LOG_ERROR, "samba dlz_bind9: Failed to create ldb");
304                 result = ISC_R_FAILURE;
305                 goto failed;
306         }
307
308         url = talloc_asprintf(tmp_ctx, "ldapi://%s",
309                               private_path(tmp_ctx, state->lp, "ldap_priv/ldapi"));
310         if (url == NULL) {
311                 result = ISC_R_NOMEMORY;
312                 goto failed;
313         }
314
315         ret = ldb_connect(state->samdb, url, 0, NULL);
316         if (ret == -1) {
317                 state->log(ISC_LOG_ERROR, "samba dlz_bind9: Failed to connect to %s - %s",
318                            url, ldb_errstring(state->samdb));
319                 result = ISC_R_FAILURE;
320                 goto failed;
321         }
322
323         dn = ldb_get_default_basedn(state->samdb);
324         if (dn == NULL) {
325                 state->log(ISC_LOG_ERROR, "samba dlz_bind9: Unable to get basedn for %s - %s",
326                            url, ldb_errstring(state->samdb));
327                 result = ISC_R_FAILURE;
328                 goto failed;
329         }
330
331         state->log(ISC_LOG_INFO, "samba dlz_bind9: started for DN %s",
332                    ldb_dn_get_linearized(dn));
333
334         *dbdata = state;
335
336         talloc_free(tmp_ctx);
337         return ISC_R_SUCCESS;
338
339 failed:
340         talloc_free(state);
341         return result;
342 }
343
344 /*
345   shutdown the backend
346  */
347 _PUBLIC_ void dlz_destroy(void *driverarg, void *dbdata)
348 {
349         struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
350         state->log(ISC_LOG_INFO, "samba dlz_bind9: shutting down");
351         talloc_free(state);
352 }
353
354
355 /*
356   see if we handle a given zone
357  */
358 _PUBLIC_ isc_result_t dlz_findzonedb(void *driverarg, void *dbdata, const char *name)
359 {
360         struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
361         if (strcasecmp(lpcfg_dnsdomain(state->lp), name) == 0) {
362                 return ISC_R_SUCCESS;
363         }
364         return ISC_R_NOTFOUND;
365 }
366
367
368 /*
369   lookup one record
370  */
371 _PUBLIC_ isc_result_t dlz_lookup_types(struct dlz_bind9_data *state,
372                                        const char *zone, const char *name,
373                                        void *driverarg, dns_sdlzlookup_t *lookup,
374                                        const char **types)
375 {
376         struct ldb_dn *dn;
377         TALLOC_CTX *tmp_ctx = talloc_new(state);
378         const char *attrs[] = { "dnsRecord", NULL };
379         int ret, i;
380         struct ldb_result *res;
381         struct ldb_message_element *el;
382
383         dn = ldb_dn_copy(tmp_ctx, ldb_get_default_basedn(state->samdb));
384         if (dn == NULL) {
385                 talloc_free(tmp_ctx);
386                 return ISC_R_NOMEMORY;
387         }
388
389         if (!ldb_dn_add_child_fmt(dn, "DC=%s,DC=%s,CN=MicrosoftDNS,DC=DomainDnsZones",
390                                   name, zone)) {
391                 talloc_free(tmp_ctx);
392                 return ISC_R_NOMEMORY;
393         }
394
395         ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_BASE,
396                          attrs, "objectClass=dnsNode");
397         if (ret != LDB_SUCCESS) {
398                 talloc_free(tmp_ctx);
399                 return ISC_R_NOTFOUND;
400         }
401
402         el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
403         if (el == NULL || el->num_values == 0) {
404                 state->log(ISC_LOG_INFO, "failed to find %s",
405                            ldb_dn_get_linearized(dn));
406                 talloc_free(tmp_ctx);
407                 return ISC_R_NOTFOUND;
408         }
409
410         for (i=0; i<el->num_values; i++) {
411                 struct dnsp_DnssrvRpcRecord rec;
412                 enum ndr_err_code ndr_err;
413                 isc_result_t result;
414
415                 ndr_err = ndr_pull_struct_blob(&el->values[i], tmp_ctx, &rec,
416                                                (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
417                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
418                         state->log(ISC_LOG_ERROR, "samba dlz_bind9: failed to parse dnsRecord for %s",
419                                    ldb_dn_get_linearized(dn));
420                         talloc_free(tmp_ctx);
421                         return ISC_R_FAILURE;
422                 }
423
424                 result = b9_putrr(state, lookup, &rec, types);
425                 if (result != ISC_R_SUCCESS) {
426                         talloc_free(tmp_ctx);
427                         return result;
428                 }
429         }
430
431         talloc_free(tmp_ctx);
432         return ISC_R_SUCCESS;
433 }
434
435 /*
436   lookup one record
437  */
438 _PUBLIC_ isc_result_t dlz_lookup(const char *zone, const char *name, void *driverarg,
439                                  void *dbdata, dns_sdlzlookup_t *lookup)
440 {
441         struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
442         return dlz_lookup_types(state, zone, name, driverarg, lookup, NULL);
443 }
444
445
446 /*
447   see if a zone transfer is allowed
448  */
449 _PUBLIC_ isc_result_t dlz_allowzonexfr(void *driverarg, void *dbdata, const char *name,
450                                        const char *client)
451 {
452         struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
453
454         if (strcasecmp(lpcfg_dnsdomain(state->lp), name) == 0) {
455                 /* TODO: check an ACL here? client is the IP of the requester */
456                 state->log(ISC_LOG_INFO, "samba dlz_bind9: allowing zone transfer for '%s' by '%s'",
457                            name, client);
458                 return ISC_R_SUCCESS;
459         }
460         return ISC_R_NOTFOUND;
461 }
462
463 /*
464   perform a zone transfer
465  */
466 _PUBLIC_ isc_result_t dlz_allnodes(const char *zone, void *driverarg, void *dbdata,
467                                    dns_sdlzallnodes_t *allnodes)
468 {
469         struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
470         const char *attrs[] = { "dnsRecord", NULL };
471         int ret, i, j;
472         struct ldb_dn *dn;
473         struct ldb_result *res;
474         TALLOC_CTX *tmp_ctx = talloc_new(state);
475
476
477         dn = ldb_dn_copy(tmp_ctx, ldb_get_default_basedn(state->samdb));
478         if (dn == NULL) {
479                 talloc_free(tmp_ctx);
480                 return ISC_R_NOMEMORY;
481         }
482
483         if (!ldb_dn_add_child_fmt(dn, "DC=%s,CN=MicrosoftDNS,DC=DomainDnsZones", zone)) {
484                 talloc_free(tmp_ctx);
485                 return ISC_R_NOMEMORY;
486         }
487
488         ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_SUBTREE,
489                          attrs, "objectClass=dnsNode");
490         if (ret != LDB_SUCCESS) {
491                 talloc_free(tmp_ctx);
492                 return ISC_R_NOTFOUND;
493         }
494
495         for (i=0; i<res->count; i++) {
496                 struct ldb_message_element *el;
497                 TALLOC_CTX *el_ctx = talloc_new(tmp_ctx);
498                 const char *rdn, *name;
499                 const struct ldb_val *v;
500
501                 el = ldb_msg_find_element(res->msgs[i], "dnsRecord");
502                 if (el == NULL || el->num_values == 0) {
503                         state->log(ISC_LOG_INFO, "failed to find dnsRecord for %s",
504                                    ldb_dn_get_linearized(dn));
505                         talloc_free(el_ctx);
506                         continue;
507                 }
508
509                 v = ldb_dn_get_rdn_val(res->msgs[i]->dn);
510                 if (v == NULL) {
511                         state->log(ISC_LOG_INFO, "failed to find RDN for %s",
512                                    ldb_dn_get_linearized(dn));
513                         talloc_free(el_ctx);
514                         continue;
515                 }
516
517                 rdn = talloc_strndup(el_ctx, (char *)v->data, v->length);
518                 if (rdn == NULL) {
519                         talloc_free(tmp_ctx);
520                         return ISC_R_NOMEMORY;
521                 }
522
523                 if (strcmp(rdn, "@") == 0) {
524                         name = zone;
525                 } else {
526                         name = talloc_asprintf(el_ctx, "%s.%s", rdn, zone);
527                 }
528                 if (name == NULL) {
529                         talloc_free(tmp_ctx);
530                         return ISC_R_NOMEMORY;
531                 }
532
533                 for (j=0; j<el->num_values; j++) {
534                         struct dnsp_DnssrvRpcRecord rec;
535                         enum ndr_err_code ndr_err;
536                         isc_result_t result;
537
538                         ndr_err = ndr_pull_struct_blob(&el->values[j], el_ctx, &rec,
539                                                        (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
540                         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
541                                 state->log(ISC_LOG_ERROR, "samba dlz_bind9: failed to parse dnsRecord for %s",
542                                            ldb_dn_get_linearized(dn));
543                                 talloc_free(el_ctx);
544                                 continue;
545                         }
546
547                         result = b9_putnamedrr(state, allnodes, name, &rec);
548                         if (result != ISC_R_SUCCESS) {
549                                 talloc_free(el_ctx);
550                                 continue;
551                         }
552                 }
553         }
554
555         talloc_free(tmp_ctx);
556
557         return ISC_R_SUCCESS;
558 }