Merge branch 'master' of ssh://git.samba.org/data/git/samba
[samba.git] / source4 / dsdb / dns / dns_update.c
1 /*
2    Unix SMB/CIFS mplementation.
3
4    DNS update service
5
6    Copyright (C) Andrew Tridgell 2009
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
23 /*
24   this module auto-creates the named.conf.update file, which tells
25   bind9 what KRB5 principals it should accept for updates to our zone
26
27   It also uses the samba_dnsupdate script to auto-create the right DNS
28   names for ourselves as a DC in the domain, using TSIG-GSS
29  */
30
31 #include "includes.h"
32 #include "dsdb/samdb/samdb.h"
33 #include "auth/auth.h"
34 #include "smbd/service.h"
35 #include "lib/messaging/irpc.h"
36 #include "param/param.h"
37 #include "system/filesys.h"
38 #include "libcli/composite/composite.h"
39
40 struct dnsupdate_service {
41         struct task_server *task;
42         struct auth_session_info *system_session_info;
43         struct ldb_context *samdb;
44
45         /* status for periodic config file update */
46         struct {
47                 uint32_t interval;
48                 struct tevent_timer *te;
49                 struct tevent_req *subreq;
50                 NTSTATUS status;
51         } confupdate;
52
53         /* status for periodic DNS name check */
54         struct {
55                 uint32_t interval;
56                 struct tevent_timer *te;
57                 struct tevent_req *subreq;
58                 struct tevent_req *spnreq;
59                 NTSTATUS status;
60         } nameupdate;
61 };
62
63 /*
64   called when rndc reload has finished
65  */
66 static void dnsupdate_rndc_done(struct tevent_req *subreq)
67 {
68         struct dnsupdate_service *service = tevent_req_callback_data(subreq,
69                                             struct dnsupdate_service);
70         int ret;
71         int sys_errno;
72
73         service->confupdate.subreq = NULL;
74
75         ret = samba_runcmd_recv(subreq, &sys_errno);
76         TALLOC_FREE(subreq);
77         if (ret != 0) {
78                 service->confupdate.status = map_nt_error_from_unix(sys_errno);
79         } else {
80                 service->confupdate.status = NT_STATUS_OK;
81         }
82
83         if (!NT_STATUS_IS_OK(service->confupdate.status)) {
84                 DEBUG(0,(__location__ ": Failed rndc update - %s\n",
85                          nt_errstr(service->confupdate.status)));
86         } else {
87                 DEBUG(3,("Completed rndc reload OK\n"));
88         }
89 }
90
91 /*
92   called every 'dnsupdate:conf interval' seconds
93  */
94 static void dnsupdate_rebuild(struct dnsupdate_service *service)
95 {
96         int ret;
97         size_t size;
98         struct ldb_result *res;
99         const char *tmp_path, *path, *path_static;
100         char *static_policies;
101         int fd;
102         unsigned int i;
103         const char *attrs[] = { "sAMAccountName", NULL };
104         const char *realm = lpcfg_realm(service->task->lp_ctx);
105         TALLOC_CTX *tmp_ctx = talloc_new(service);
106         const char * const *rndc_command = lpcfg_rndc_command(service->task->lp_ctx);
107
108         /* abort any pending script run */
109         TALLOC_FREE(service->confupdate.subreq);
110
111         ret = ldb_search(service->samdb, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE,
112                          attrs, "(&(primaryGroupID=%u)(objectClass=computer))",
113                          DOMAIN_RID_DCS);
114         if (ret != LDB_SUCCESS) {
115                 DEBUG(0,(__location__ ": Unable to find DCs list - %s", ldb_errstring(service->samdb)));
116                 talloc_free(tmp_ctx);
117                 return;
118         }
119
120         path = lpcfg_parm_string(service->task->lp_ctx, NULL, "dnsupdate", "path");
121         if (path == NULL) {
122                 path = private_path(tmp_ctx, service->task->lp_ctx, "named.conf.update");
123         }
124
125         path_static = lpcfg_parm_string(service->task->lp_ctx, NULL, "dnsupdate", "extra_static_grant_rules");
126         if (path_static == NULL) {
127                 path_static = private_path(tmp_ctx, service->task->lp_ctx, "named.conf.update.static");
128         }
129
130         tmp_path = talloc_asprintf(tmp_ctx, "%s.tmp", path);
131         if (path == NULL || tmp_path == NULL || path_static == NULL ) {
132                 DEBUG(0,(__location__ ": Unable to get paths\n"));
133                 talloc_free(tmp_ctx);
134                 return;
135         }
136
137         static_policies = file_load(path_static, &size, 0, tmp_ctx);
138
139         unlink(tmp_path);
140         fd = open(tmp_path, O_CREAT|O_TRUNC|O_WRONLY, 0444);
141         if (fd == -1) {
142                 DEBUG(1,(__location__ ": Unable to open %s - %s\n", tmp_path, strerror(errno)));
143                 talloc_free(tmp_ctx);
144                 return;
145         }
146
147         dprintf(fd, "/* this file is auto-generated - do not edit */\n");
148         dprintf(fd, "update-policy {\n");
149         if( static_policies != NULL ) {
150                 dprintf(fd, "/* Start of static entries */\n");
151                 dprintf(fd, "%s\n",static_policies);
152                 dprintf(fd, "/* End of static entries */\n");
153         }
154         dprintf(fd, "\tgrant %s ms-self * A AAAA;\n", realm);
155         dprintf(fd, "\tgrant administrator@%s wildcard * A AAAA SRV CNAME TXT;\n", realm);
156
157         for (i=0; i<res->count; i++) {
158                 const char *acctname;
159                 acctname = ldb_msg_find_attr_as_string(res->msgs[i],
160                                                        "sAMAccountName", NULL);
161                 if (!acctname) continue;
162                 dprintf(fd, "\tgrant %s@%s wildcard * A AAAA SRV CNAME;\n",
163                         acctname, realm);
164         }
165         dprintf(fd, "};\n");
166         close(fd);
167
168
169         if (NT_STATUS_IS_OK(service->confupdate.status) &&
170             file_compare(tmp_path, path) == true) {
171                 unlink(tmp_path);
172                 talloc_free(tmp_ctx);
173                 return;
174         }
175
176         if (rename(tmp_path, path) != 0) {
177                 DEBUG(0,(__location__ ": Failed to rename %s to %s - %s\n",
178                          tmp_path, path, strerror(errno)));
179                 talloc_free(tmp_ctx);
180                 return;
181         }
182
183         DEBUG(2,("Loading new DNS update grant rules\n"));
184         service->confupdate.subreq = samba_runcmd_send(service,
185                                                        service->task->event_ctx,
186                                                        timeval_current_ofs(10, 0),
187                                                        2, 0,
188                                                        rndc_command,
189                                                        "reload", NULL);
190         if (service->confupdate.subreq == NULL) {
191                 DEBUG(0,(__location__ ": samba_runcmd_send() failed with no memory\n"));
192                 talloc_free(tmp_ctx);
193                 return;
194         }
195         tevent_req_set_callback(service->confupdate.subreq,
196                                 dnsupdate_rndc_done,
197                                 service);
198
199         talloc_free(tmp_ctx);
200 }
201
202 static NTSTATUS dnsupdate_confupdate_schedule(struct dnsupdate_service *service);
203
204 /*
205   called every 'dnsupdate:conf interval' seconds
206  */
207 static void dnsupdate_confupdate_handler_te(struct tevent_context *ev, struct tevent_timer *te,
208                                           struct timeval t, void *ptr)
209 {
210         struct dnsupdate_service *service = talloc_get_type(ptr, struct dnsupdate_service);
211
212         dnsupdate_rebuild(service);
213         dnsupdate_confupdate_schedule(service);
214 }
215
216
217 static NTSTATUS dnsupdate_confupdate_schedule(struct dnsupdate_service *service)
218 {
219         service->confupdate.te = tevent_add_timer(service->task->event_ctx, service,
220                                                 timeval_current_ofs(service->confupdate.interval, 0),
221                                                 dnsupdate_confupdate_handler_te, service);
222         NT_STATUS_HAVE_NO_MEMORY(service->confupdate.te);
223         return NT_STATUS_OK;
224 }
225
226
227 /*
228   called when dns update script has finished
229  */
230 static void dnsupdate_nameupdate_done(struct tevent_req *subreq)
231 {
232         struct dnsupdate_service *service = tevent_req_callback_data(subreq,
233                                             struct dnsupdate_service);
234         int ret;
235         int sys_errno;
236
237         service->nameupdate.subreq = NULL;
238
239         ret = samba_runcmd_recv(subreq, &sys_errno);
240         TALLOC_FREE(subreq);
241         if (ret != 0) {
242                 service->nameupdate.status = map_nt_error_from_unix(sys_errno);
243         } else {
244                 service->nameupdate.status = NT_STATUS_OK;
245         }
246
247         if (!NT_STATUS_IS_OK(service->nameupdate.status)) {
248                 DEBUG(0,(__location__ ": Failed DNS update - %s\n",
249                          nt_errstr(service->nameupdate.status)));
250         } else {
251                 DEBUG(3,("Completed DNS update check OK\n"));
252         }
253 }
254
255
256 /*
257   called when spn update script has finished
258  */
259 static void dnsupdate_spnupdate_done(struct tevent_req *subreq)
260 {
261         struct dnsupdate_service *service = tevent_req_callback_data(subreq,
262                                             struct dnsupdate_service);
263         int ret;
264         int sys_errno;
265
266         service->nameupdate.spnreq = NULL;
267
268         ret = samba_runcmd_recv(subreq, &sys_errno);
269         TALLOC_FREE(subreq);
270         if (ret != 0) {
271                 service->nameupdate.status = map_nt_error_from_unix(sys_errno);
272         } else {
273                 service->nameupdate.status = NT_STATUS_OK;
274         }
275
276         if (!NT_STATUS_IS_OK(service->nameupdate.status)) {
277                 DEBUG(0,(__location__ ": Failed SPN update - %s\n",
278                          nt_errstr(service->nameupdate.status)));
279         } else {
280                 DEBUG(3,("Completed SPN update check OK\n"));
281         }
282 }
283
284 /*
285   called every 'dnsupdate:name interval' seconds
286  */
287 static void dnsupdate_check_names(struct dnsupdate_service *service)
288 {
289         const char * const *dns_update_command = lpcfg_dns_update_command(service->task->lp_ctx);
290         const char * const *spn_update_command = lpcfg_spn_update_command(service->task->lp_ctx);
291
292         /* kill any existing child */
293         TALLOC_FREE(service->nameupdate.subreq);
294
295         DEBUG(3,("Calling DNS name update script\n"));
296         service->nameupdate.subreq = samba_runcmd_send(service,
297                                                        service->task->event_ctx,
298                                                        timeval_current_ofs(10, 0),
299                                                        2, 0,
300                                                        dns_update_command,
301                                                        NULL);
302         if (service->nameupdate.subreq == NULL) {
303                 DEBUG(0,(__location__ ": samba_runcmd_send() failed with no memory\n"));
304                 return;
305         }
306         tevent_req_set_callback(service->nameupdate.subreq,
307                                 dnsupdate_nameupdate_done,
308                                 service);
309
310         DEBUG(3,("Calling SPN name update script\n"));
311         service->nameupdate.spnreq = samba_runcmd_send(service,
312                                                        service->task->event_ctx,
313                                                        timeval_current_ofs(10, 0),
314                                                        2, 0,
315                                                        spn_update_command,
316                                                        NULL);
317         if (service->nameupdate.spnreq == NULL) {
318                 DEBUG(0,(__location__ ": samba_runcmd_send() failed with no memory\n"));
319                 return;
320         }
321         tevent_req_set_callback(service->nameupdate.spnreq,
322                                 dnsupdate_spnupdate_done,
323                                 service);
324 }
325
326 static NTSTATUS dnsupdate_nameupdate_schedule(struct dnsupdate_service *service);
327
328 /*
329   called every 'dnsupdate:name interval' seconds
330  */
331 static void dnsupdate_nameupdate_handler_te(struct tevent_context *ev, struct tevent_timer *te,
332                                             struct timeval t, void *ptr)
333 {
334         struct dnsupdate_service *service = talloc_get_type(ptr, struct dnsupdate_service);
335
336         dnsupdate_check_names(service);
337         dnsupdate_nameupdate_schedule(service);
338 }
339
340
341 static NTSTATUS dnsupdate_nameupdate_schedule(struct dnsupdate_service *service)
342 {
343         service->nameupdate.te = tevent_add_timer(service->task->event_ctx, service,
344                                                   timeval_current_ofs(service->nameupdate.interval, 0),
345                                                   dnsupdate_nameupdate_handler_te, service);
346         NT_STATUS_HAVE_NO_MEMORY(service->nameupdate.te);
347         return NT_STATUS_OK;
348 }
349
350 /*
351   startup the dns update task
352 */
353 static void dnsupdate_task_init(struct task_server *task)
354 {
355         NTSTATUS status;
356         struct dnsupdate_service *service;
357
358         if (lpcfg_server_role(task->lp_ctx) != ROLE_DOMAIN_CONTROLLER) {
359                 /* not useful for non-DC */
360                 return;
361         }
362
363         task_server_set_title(task, "task[dnsupdate]");
364
365         service = talloc_zero(task, struct dnsupdate_service);
366         if (!service) {
367                 task_server_terminate(task, "dnsupdate_task_init: out of memory", true);
368                 return;
369         }
370         service->task           = task;
371         task->private_data      = service;
372
373         service->system_session_info = system_session(service->task->lp_ctx);
374         if (!service->system_session_info) {
375                 task_server_terminate(task,
376                                       "dnsupdate: Failed to obtain server credentials\n",
377                                       true);
378                 return;
379         }
380
381         service->samdb = samdb_connect(service, service->task->event_ctx, task->lp_ctx,
382                                        service->system_session_info);
383         if (!service->samdb) {
384                 task_server_terminate(task, "dnsupdate: Failed to connect to local samdb\n",
385                                       true);
386                 return;
387         }
388
389         service->confupdate.interval    = lpcfg_parm_int(task->lp_ctx, NULL,
390                                                       "dnsupdate", "config interval", 60); /* in seconds */
391
392         service->nameupdate.interval    = lpcfg_parm_int(task->lp_ctx, NULL,
393                                                       "dnsupdate", "name interval", 600); /* in seconds */
394
395         dnsupdate_rebuild(service);
396         status = dnsupdate_confupdate_schedule(service);
397         if (!NT_STATUS_IS_OK(status)) {
398                 task_server_terminate(task, talloc_asprintf(task,
399                                       "dnsupdate: Failed to confupdate schedule: %s\n",
400                                                             nt_errstr(status)), true);
401                 return;
402         }
403
404         dnsupdate_check_names(service);
405         status = dnsupdate_nameupdate_schedule(service);
406         if (!NT_STATUS_IS_OK(status)) {
407                 task_server_terminate(task, talloc_asprintf(task,
408                                       "dnsupdate: Failed to nameupdate schedule: %s\n",
409                                                             nt_errstr(status)), true);
410                 return;
411         }
412
413         irpc_add_name(task->msg_ctx, "dnsupdate");
414
415         /* create the intial file */
416         dnsupdate_rebuild(service);
417
418 }
419
420 /*
421   register ourselves as a available server
422 */
423 NTSTATUS server_service_dnsupdate_init(void)
424 {
425         return register_server_service("dnsupdate", dnsupdate_task_init);
426 }