xref: /openbsd-src/usr.sbin/nsd/verify.c (revision fc405d53b73a2d73393cb97f684863d17b583e38)
1 /*
2  * verify.c -- running verifiers and serving the zone to be verified.
3  *
4  * Copyright (c) 2012-2020, NLnet Labs. All rights reserved.
5  *
6  * See LICENSE for the license.
7  *
8  */
9 
10 #include "config.h"
11 
12 #include <assert.h>
13 #include <ctype.h>
14 #include <errno.h>
15 #include <stdarg.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #ifdef HAVE_SYSLOG_H
20 #include <syslog.h>
21 #endif /* HAVE_SYSLOG_H */
22 #include <unistd.h>
23 #include <fcntl.h>
24 #include <sys/wait.h>
25 
26 #include "region-allocator.h"
27 #include "namedb.h"
28 #include "nsd.h"
29 #include "options.h"
30 #include "difffile.h"
31 #include "verify.h"
32 #include "popen3.h"
33 
34 struct zone *verify_next_zone(struct nsd *nsd, struct zone *zone)
35 {
36 	int verify;
37 	struct radnode *node;
38 
39 	if(zone != NULL) {
40 		node = radix_next(zone->node);
41 	} else {
42 		node = radix_first(nsd->db->zonetree);
43 	}
44 
45 	while(node != NULL) {
46 		zone = (struct zone *)node->elem;
47 		verify = zone->opts->pattern->verify_zone;
48 		if(verify == VERIFY_ZONE_INHERIT) {
49 			verify = nsd->options->verify_zones;
50 		}
51 		if(verify && zone->is_updated && !zone->is_checked) {
52 			return zone;
53 		}
54 		node = radix_next(node);
55 	}
56 
57 	return NULL;
58 }
59 
60 static inline ssize_t fill_buffer(struct verifier_stream *stream)
61 {
62 	ssize_t cnt = 0;
63 
64 	assert(stream);
65 	assert(stream->fd != -1);
66 	assert(stream->cnt <= LOGBUFSIZE);
67 	assert(stream->off <= stream->cnt);
68 
69 	// move data to start of buffer assuming all complete lines are printed
70 	if (stream->off) {
71 		size_t len = stream->cnt - stream->off;
72 		memmove(stream->buf, stream->buf + stream->off, len);
73 		stream->off = 0;
74 		stream->cnt = len;
75 		stream->buf[stream->cnt] = '\0'; // always null-terminate
76 	}
77 
78 	// read data if space is available
79 	cnt = read(stream->fd, stream->buf + stream->cnt, LOGBUFSIZE - stream->cnt);
80 	if (cnt > 0)
81 		stream->cnt += (size_t)cnt;
82 	assert(stream->cnt <= LOGBUFSIZE);
83 	assert(stream->off <= stream->cnt);
84 	stream->buf[stream->cnt] = '\0'; // always null-terminate
85 
86 	return cnt;
87 }
88 
89 static inline size_t print_line(struct verifier_stream *stream, int eof)
90 {
91 	char *eol = NULL;
92 	size_t len;
93 	const char *fmt;
94 
95 	if (stream->cnt == 0)
96 		return 0;
97 	assert(stream->off <= stream->cnt);
98 	if (stream->off == stream->cnt)
99 		return 0;
100 
101 	// try to locate natural line break
102 	assert(stream->buf[stream->cnt] == '\0');
103 	if ((eol = strchr(stream->buf + stream->off, '\n'))) {
104 		len = eol - (stream->buf + stream->off);
105 	} else {
106 		len = stream->cnt - stream->off;
107 	}
108 
109 	assert(len <= (stream->cnt - stream->off));
110 	// wait for buffer to contain a full line except on eof
111 	if (len < LOGLINELEN && !eol && !eof)
112 		return 0;
113 
114 	if (len > LOGLINELEN) {
115 		fmt = stream->cut ? ".. %.*s .." : "%.*s ..";
116 		len = LOGLINELEN; // remainder printed next iteration
117 		stream->cut = 1;
118 	} else {
119 		fmt = stream->cut ? ".. %.*s" : "%.*s";
120 		stream->cut = 0;
121 	}
122 	log_msg(stream->priority, fmt, len, stream->buf + stream->off);
123 
124 	stream->off += len + (eol != NULL);
125 	assert(stream->off <= stream->cnt);
126 	return len;
127 }
128 
129 /*
130  * Log verifier output on STDOUT and STDERR. Lines longer than LOGLINELEN are
131  * split over multiple lines. Line-breaks are indicated in the log with "...".
132  */
133 static void verify_handle_stream(int fd, short event, void *arg)
134 {
135 	int eof = 0;
136 	ssize_t cnt;
137 	struct verifier *verifier;
138 	struct verifier_stream *stream;
139 
140 	assert(event & EV_READ);
141 	assert(arg != NULL);
142 
143 	verifier = (struct verifier *)arg;
144 	if (fd == verifier->output_stream.fd) {
145 		stream = &verifier->output_stream;
146 	} else {
147 		assert(fd == verifier->error_stream.fd);
148 		stream = &verifier->error_stream;
149 	}
150 
151 	assert(stream);
152 	assert(stream->fd != -1);
153 
154 	do {
155 		cnt = fill_buffer(stream);
156 		eof = !cnt || (cnt < 0 && errno != EAGAIN && errno != EINTR);
157 		while (print_line(stream, eof)) ;
158 	} while (cnt > 0);
159 
160 	if(eof) {
161 		event_del(&stream->event);
162 		close(stream->fd);
163 		stream->fd = -1;
164 	}
165 }
166 
167 static void kill_verifier(struct verifier *verifier)
168 {
169 	assert(verifier != NULL);
170 	assert(verifier->zone != NULL);
171 
172 	if(kill(verifier->pid, SIGTERM) == -1) {
173 		log_msg(LOG_ERR, "verify: cannot kill verifier for "
174 		                 "zone %s (pid %d): %s",
175 		                 verifier->zone->opts->name,
176 		                 verifier->pid,
177 		                 strerror(errno));
178 	}
179 }
180 
181 static void close_stream(struct verifier *verifier, struct verifier_stream *stream)
182 {
183 	if (stream->fd == -1)
184 		return;
185 	verify_handle_stream(stream->fd, EV_READ, verifier);
186 	if (stream->fd == -1)
187 		return;
188 	event_del(&stream->event);
189 	close(stream->fd);
190 	stream->fd = -1;
191 }
192 
193 static void close_verifier(struct verifier *verifier)
194 {
195 	/* unregister events and close streams (in that order) */
196 	if(verifier->timeout.tv_sec > 0) {
197 		event_del(&verifier->timeout_event);
198 		verifier->timeout.tv_sec = 0;
199 		verifier->timeout.tv_usec = 0;
200 	}
201 
202 	if(verifier->zone_feed.fh != NULL) {
203 		event_del(&verifier->zone_feed.event);
204 		fclose(verifier->zone_feed.fh);
205 		verifier->zone_feed.fh = NULL;
206 		region_destroy(verifier->zone_feed.region);
207 	}
208 
209 	close_stream(verifier, &verifier->error_stream);
210 	close_stream(verifier, &verifier->output_stream);
211 
212 	verifier->zone->is_ok = verifier->was_ok;
213 	verifier->pid = -1;
214 	verifier->zone = NULL;
215 }
216 
217 /*
218  * Feed zone to verifier over STDIN as it becomes available.
219  */
220 static void verify_handle_feed(int fd, short event, void *arg)
221 {
222 	struct verifier *verifier;
223 	struct rr *rr;
224 
225 	(void)fd;
226 	assert(event == EV_WRITE);
227 	assert(arg != NULL);
228 
229 	verifier = (struct verifier *)arg;
230 	if((rr = zone_rr_iter_next(&verifier->zone_feed.rriter)) != NULL) {
231 		print_rr(verifier->zone_feed.fh,
232 		         verifier->zone_feed.rrprinter,
233 		         rr,
234 		         verifier->zone_feed.region,
235 		         verifier->zone_feed.buffer);
236 	} else {
237 		event_del(&verifier->zone_feed.event);
238 		fclose(verifier->zone_feed.fh);
239 		verifier->zone_feed.fh = NULL;
240 		region_destroy(verifier->zone_feed.region);
241 	}
242 }
243 
244 /*
245  * This handler will be called when a verifier-timeout alarm goes off. It just
246  * kills the verifier. server_verify_zones will make sure the zone will be
247  * considered bad.
248  */
249 void verify_handle_timeout(int fd, short event, void *arg)
250 {
251 	struct verifier *verifier;
252 
253 	(void)fd;
254 	assert(event & EV_TIMEOUT);
255 	assert(arg != NULL);
256 
257 	verifier = (struct verifier *)arg;
258 	verifier->zone->is_bad = 1;
259 
260 	log_msg(LOG_ERR, "verify: verifier for zone %s (pid %d) timed out",
261 	                 verifier->zone->opts->name, verifier->pid);
262 
263 	/* kill verifier, process reaped by exit handler */
264 	kill_verifier(verifier);
265 }
266 
267 void verify_handle_signal(int sig, short event, void *arg)
268 {
269 	char buf[1] = { '\0' };
270 	struct nsd *nsd;
271 
272 	assert(sig == SIGCHLD);
273 	assert(event & EV_SIGNAL);
274 	assert(arg != NULL);
275 
276 	nsd = (struct nsd *)arg;
277 	(void)write(nsd->verifier_pipe[1], buf, sizeof(buf));
278 }
279 
280 /*
281  * Reap process and update status of respective zone based on the exit code
282  * of a verifier. Everything from STDOUT and STDERR still available is read and
283  * written to the log as it might contain valuable information.
284  *
285  * NOTE: A timeout might have caused the verifier to be terminated.
286  */
287 void verify_handle_exit(int fd, short event, void *arg)
288 {
289 	int wstatus;
290 	pid_t pid;
291 	struct nsd *nsd;
292 	char buf[1];
293 
294 	assert(event & EV_READ);
295 	assert(arg != NULL);
296 
297 	nsd = (struct nsd *)arg;
298 
299 	if(read(fd, buf, sizeof(buf)) == -1) {
300 		if(errno != EAGAIN && errno != EINTR && errno != EWOULDBLOCK)
301 			log_msg(LOG_ERR, "verify_handle_exit: read failed: %s",
302 				strerror(errno));
303 	}
304 
305 	while(((pid = waitpid(-1, &wstatus, WNOHANG)) == -1 && errno == EINTR)
306 	    || (pid > 0))
307 	{
308 		struct verifier *verifier = NULL;
309 
310 		for(size_t i = 0; !verifier && i < nsd->verifier_limit; i++) {
311 			if(nsd->verifiers[i].zone != NULL &&
312 			   nsd->verifiers[i].pid == pid)
313 			{
314 				verifier = &nsd->verifiers[i];
315 			}
316 		}
317 
318 		if(verifier == NULL) {
319 			continue;
320 		}
321 
322 		if(!WIFEXITED(wstatus)) {
323 			log_msg(LOG_ERR, "verify: verifier for zone %s "
324 			                 "(pid %d) exited abnormally",
325 			                 verifier->zone->opts->name, pid);
326 		} else {
327 			int priority = LOG_INFO;
328 			int status = WEXITSTATUS(wstatus);
329 			if(status != 0) {
330 				priority = LOG_ERR;
331 				verifier->zone->is_bad = 1;
332 			}
333 			log_msg(priority, "verify: verifier for zone %s "
334 			                  "(pid %d) exited with %d",
335 			                  verifier->zone->opts->name, pid, status);
336 		}
337 
338 		close_verifier(verifier);
339 		nsd->verifier_count--;
340 	}
341 
342 	while(nsd->mode == NSD_RUN &&
343 	      nsd->verifier_count < nsd->verifier_limit &&
344 	      nsd->next_zone_to_verify != NULL)
345 	{
346 		verify_zone(nsd, nsd->next_zone_to_verify);
347 		nsd->next_zone_to_verify
348 			= verify_next_zone(nsd, nsd->next_zone_to_verify);
349 	}
350 
351 	if(nsd->next_zone_to_verify == NULL && nsd->verifier_count == 0) {
352 		event_base_loopexit(nsd->event_base, NULL);
353 		return;
354 	}
355 }
356 
357 /*
358  * A parent may be terminated (by the NSD_QUIT signal (nsdc stop command)).
359  * When a reload server process is running, the parent will then send a
360  * NSD_QUIT command to that server. This handler makes sure that this command
361  * is not neglected and that the reload server process will exit (gracefully).
362  */
363 void
364 verify_handle_command(int fd, short event, void *arg)
365 {
366 	struct nsd *nsd = (struct nsd *)arg;
367 	int len;
368 	sig_atomic_t mode;
369 
370 	assert(nsd != NULL);
371 	assert(event & (EV_READ
372 #ifdef EV_CLOSED
373 	| EV_CLOSED
374 #endif
375 	));
376 
377 	if((len = read(fd, &mode, sizeof(mode))) == -1) {
378 		log_msg(LOG_ERR, "verify: verify_handle_command: read: %s",
379 		                 strerror(errno));
380 		return;
381 	} else if(len == 0) {
382 		log_msg(LOG_INFO, "verify: command channel closed");
383 		mode = NSD_QUIT;
384 	} else if(mode != NSD_QUIT) {
385 		log_msg(LOG_ERR, "verify: bad command: %d", (int)mode);
386 		return;
387 	}
388 
389 	nsd->mode = mode;
390 
391 	if(nsd->verifier_count == 0) {
392 		event_base_loopexit(nsd->event_base, NULL);
393 		return; /* exit early if no verifiers are executing */
394 	}
395 
396 	/* kill verifiers, processes reaped elsewhere */
397 	for(size_t i = 0; i < nsd->verifier_limit; i++) {
398 		if(nsd->verifiers[i].zone != NULL) {
399 			kill_verifier(&nsd->verifiers[i]);
400 		}
401 	}
402 }
403 
404 /*
405  * A verifier is executed for the specified zone (if a verifier is configured
406  * and the zone has not been verified before). If one of the verifiers exits
407  * with non-zero, the zone is marked bad and nsd drops the zone update and
408  * reloads again.
409  */
410 void verify_zone(struct nsd *nsd, struct zone *zone)
411 {
412 	struct verifier *verifier = NULL;
413 	int32_t timeout;
414 	char **command;
415 	FILE *fin;
416 	int fdin, fderr, fdout, flags;
417 
418 	assert(nsd != NULL);
419 	assert(nsd->verifier_count < nsd->verifier_limit);
420 	assert(zone != NULL);
421 
422 	fin = NULL;
423 	fdin = fdout = fderr = -1;
424 
425 	/* search for available verifier slot */
426 	for(size_t i = 0; i < nsd->verifier_limit && !verifier; i++) {
427 		if(nsd->verifiers[i].zone == NULL) {
428 			verifier = &nsd->verifiers[i];
429 		}
430 	}
431 
432 	assert(verifier != NULL);
433 
434 	if(zone->opts->pattern->verifier != NULL) {
435 		command = zone->opts->pattern->verifier;
436 	} else if (nsd->options->verifier != NULL) {
437 		command = nsd->options->verifier;
438 	} else {
439 		log_msg(LOG_ERR, "verify: no verifier for zone %s",
440 		                 zone->opts->name);
441 		return;
442 	}
443 
444 	if(zone->opts->pattern->verifier_timeout
445 		!= VERIFIER_TIMEOUT_INHERIT)
446 	{
447 		timeout = zone->opts->pattern->verifier_timeout;
448 	} else {
449 		timeout = nsd->options->verifier_timeout;
450 	}
451 
452 	if(zone->opts->pattern->verifier_feed_zone
453 		!= VERIFIER_FEED_ZONE_INHERIT)
454 	{
455 		fdin = zone->opts->pattern->verifier_feed_zone ? -2 : -1;
456 	} else {
457 		fdin = nsd->options->verifier_feed_zone ? -2 : -1;
458 	}
459 
460 	assert(timeout >= 0);
461 
462 	setenv("VERIFY_ZONE", zone->opts->name, 1);
463 	setenv("VERIFY_ZONE_ON_STDIN", fdin == -2 ? "yes" : "no", 1);
464 
465 	verifier->pid = popen3(
466 		command, fdin == -2 ? &fdin : NULL, &fdout, &fderr);
467 	if(verifier->pid == -1) {
468 		log_msg(LOG_ERR, "verify: could not start verifier for zone "
469 				 "%s: %s", zone->opts->name, strerror(errno));
470 		goto fail_popen3;
471 	}
472 	flags = fcntl(fderr, F_GETFL, 0);
473 	if (fcntl(fderr, F_SETFL, flags | O_NONBLOCK) == -1) {
474 		log_msg(LOG_ERR, "verify: fcntl(stderr, ..., O_NONBLOCK) for "
475 		                 "zone %s: %s",
476 		                 zone->opts->name, strerror(errno));
477 		goto fail_fcntl;
478 	}
479 	flags = fcntl(fdout, F_GETFL, 0);
480 	if(fcntl(fdout, F_SETFL, flags | O_NONBLOCK) == -1) {
481 		log_msg(LOG_ERR, "verify: fcntl(stdout, ..., O_NONBLOCK) for "
482 		                 "zone %s: %s",
483 		                 zone->opts->name, strerror(errno));
484 		goto fail_fcntl;
485 	}
486 	if (fdin >= 0) {
487 		if ((fin = fdopen(fdin, "w")) == NULL) {
488 			log_msg(LOG_ERR, "verify: fdopen(stdin, ...) for "
489 			                 "zone %s: %s",
490 		                         zone->opts->name, strerror(errno));
491 			goto fail_fcntl;
492 		}
493 		/* write unbuffered */
494 		setbuf(fin, NULL);
495 	}
496 
497 	verifier->zone = zone;
498 	verifier->was_ok = zone->is_ok;
499 
500 	unsetenv("VERIFY_ZONE");
501 	unsetenv("VERIFY_ZONE_ON_STDIN");
502 
503 	verifier->error_stream.fd = fderr;
504 	verifier->error_stream.cnt = 0;
505 	verifier->error_stream.off = 0;
506 	verifier->error_stream.buf[0] = '\0';
507 	event_set(&verifier->error_stream.event,
508 	          verifier->error_stream.fd,
509 	          EV_READ|EV_PERSIST,
510 	          verify_handle_stream,
511 		  verifier);
512 	event_base_set(nsd->event_base, &verifier->error_stream.event);
513 	if(event_add(&verifier->error_stream.event, NULL) != 0) {
514 		log_msg(LOG_ERR, "verify: could not add error event for "
515 		                 "zone %s", zone->opts->name);
516 		goto fail_stderr;
517 	}
518 
519 	verifier->output_stream.fd = fdout;
520 	verifier->output_stream.cnt = 0;
521 	verifier->output_stream.off = 0;
522 	verifier->output_stream.buf[0] = '\0';
523 	event_set(&verifier->output_stream.event,
524 	          verifier->output_stream.fd,
525 	          EV_READ|EV_PERSIST,
526 	          verify_handle_stream,
527 	          verifier);
528 	event_base_set(nsd->event_base, &verifier->output_stream.event);
529 	if(event_add(&verifier->output_stream.event, NULL) != 0) {
530 		log_msg(LOG_ERR, "verify: could not add output event for "
531 		                 "zone %s", zone->opts->name);
532 		goto fail_stdout;
533 	}
534 
535 	if(fin != NULL) {
536 		verifier->zone_feed.fh = fin;
537 
538 		zone_rr_iter_init(&verifier->zone_feed.rriter, zone);
539 
540 		verifier->zone_feed.rrprinter
541 			= create_pretty_rr(nsd->server_region);
542 		verifier->zone_feed.region
543 			= region_create(xalloc, free);
544 		verifier->zone_feed.buffer
545 			= buffer_create(nsd->server_region, MAX_RDLENGTH);
546 
547 		event_set(&verifier->zone_feed.event,
548 		          fileno(verifier->zone_feed.fh),
549 			  EV_WRITE|EV_PERSIST,
550 			  &verify_handle_feed,
551 			  verifier);
552 		event_base_set(nsd->event_base, &verifier->zone_feed.event);
553 		if(event_add(&verifier->zone_feed.event, NULL) != 0) {
554 			log_msg(LOG_ERR, "verify: could not add input event "
555 			                 "for zone %s", zone->opts->name);
556 			goto fail_stdin;
557 		}
558 	}
559 
560 	if(timeout > 0) {
561 		verifier->timeout.tv_sec = timeout;
562 		verifier->timeout.tv_usec = 0;
563 		event_set(&verifier->timeout_event,
564 		          -1,
565 		          EV_TIMEOUT,
566 		          verify_handle_timeout,
567 		          verifier);
568 		event_base_set(nsd->event_base, &verifier->timeout_event);
569 		if(event_add(&verifier->timeout_event, &verifier->timeout) != 0) {
570 			log_msg(LOG_ERR, "verify: could not add timeout event "
571 			                 "for zone %s", zone->opts->name);
572 			goto fail_timeout;
573 		}
574 
575 		log_msg(LOG_INFO, "verify: started verifier for zone %s "
576 		                  "(pid %d), timeout is %d seconds",
577 		                  zone->opts->name, verifier->pid, timeout);
578 	} else {
579 		log_msg(LOG_INFO, "verify: started verifier for zone %s "
580 		                  "(pid %d)", zone->opts->name, verifier->pid);
581 	}
582 
583 	zone->is_ok = 1;
584 	nsd->verifier_count++;
585 	return;
586 
587 fail_timeout:
588 	verifier->timeout.tv_sec = 0;
589 	verifier->timeout.tv_usec = 0;
590 	if(fin != NULL) {
591 		event_del(&verifier->zone_feed.event);
592 	}
593 fail_stdin:
594 	verifier->zone_feed.fh = NULL;
595 	event_del(&verifier->output_stream.event);
596 fail_stdout:
597 	verifier->output_stream.fd = -1;
598 	event_del(&verifier->error_stream.event);
599 fail_stderr:
600 	verifier->error_stream.fd = -1;
601 fail_fcntl:
602 	kill_verifier(verifier);
603 	if(fin != NULL) {
604 		fclose(fin);
605 	} else if (fdin >= 0) {
606 		close(fdin);
607 	}
608 	close(fdout);
609 	close(fderr);
610 fail_popen3:
611 	zone->is_bad = 1;
612 	verifier->pid = -1;
613 	verifier->zone = NULL;
614 }
615