1%{
2/*-
3 * Copyright (c) 2012 The FreeBSD Foundation
4 * All rights reserved.
5 *
6 * This software was developed by Pawel Jakub Dawidek under sponsorship from
7 * the FreeBSD Foundation.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31#include <config/config.h>
32
33#include <sys/types.h>
34#include <sys/queue.h>
35#include <sys/sysctl.h>
36
37#include <arpa/inet.h>
38
39#include <err.h>
40#include <errno.h>
41#include <stdio.h>
42#include <string.h>
43#include <sysexits.h>
44#include <unistd.h>
45#ifndef HAVE_STRLCPY
46#include <compat/strlcpy.h>
47#endif
48
49#include "auditdistd.h"
50#include "pjdlog.h"
51
52extern int depth;
53extern int lineno;
54
55extern FILE *yyin;
56extern char *yytext;
57
58static struct adist_config *lconfig;
59static struct adist_host *curhost;
60#define	SECTION_GLOBAL		0
61#define	SECTION_SENDER		1
62#define	SECTION_RECEIVER	2
63static int cursection;
64
65/* Sender section. */
66static char depth1_source[ADIST_ADDRSIZE];
67static int depth1_checksum;
68static int depth1_compression;
69/* Sender and receiver sections. */
70static char depth1_directory[PATH_MAX];
71
72static bool adjust_directory(char *path);
73static bool family_supported(int family);
74
75extern void yyrestart(FILE *);
76%}
77
78%token CB
79%token CERTFILE
80%token DIRECTORY
81%token FINGERPRINT
82%token HOST
83%token KEYFILE
84%token LISTEN
85%token NAME
86%token OB
87%token PASSWORD
88%token PIDFILE
89%token RECEIVER REMOTE
90%token SENDER SOURCE
91%token TIMEOUT
92
93/*
94%type <num> checksum_type
95%type <num> compression_type
96*/
97
98%union
99{
100	int num;
101	char *str;
102}
103
104%token <num> NUM
105%token <str> STR
106
107%%
108
109statements:
110	|
111	statements statement
112	;
113
114statement:
115	name_statement
116	|
117	pidfile_statement
118	|
119	timeout_statement
120	|
121	sender_statement
122	|
123	receiver_statement
124	;
125
126name_statement:	NAME STR
127	{
128		PJDLOG_RASSERT(depth == 0,
129		    "The name variable can only be specificed in the global section.");
130
131		if (lconfig->adc_name[0] != '\0') {
132			pjdlog_error("The name variable is specified twice.");
133			free($2);
134			return (1);
135		}
136		if (strlcpy(lconfig->adc_name, $2,
137		    sizeof(lconfig->adc_name)) >=
138		    sizeof(lconfig->adc_name)) {
139			pjdlog_error("The name value is too long.");
140			free($2);
141			return (1);
142		}
143		free($2);
144	}
145	;
146
147pidfile_statement:	PIDFILE STR
148	{
149		PJDLOG_RASSERT(depth == 0,
150		    "The pidfile variable can only be specificed in the global section.");
151
152		if (lconfig->adc_pidfile[0] != '\0') {
153			pjdlog_error("The pidfile variable is specified twice.");
154			free($2);
155			return (1);
156		}
157		if (strcmp($2, "none") != 0 && $2[0] != '/') {
158			pjdlog_error("The pidfile variable must be set to absolute pathname or \"none\".");
159			free($2);
160			return (1);
161		}
162		if (strlcpy(lconfig->adc_pidfile, $2,
163		    sizeof(lconfig->adc_pidfile)) >=
164		    sizeof(lconfig->adc_pidfile)) {
165			pjdlog_error("The pidfile value is too long.");
166			free($2);
167			return (1);
168		}
169		free($2);
170	}
171	;
172
173timeout_statement:	TIMEOUT NUM
174	{
175		PJDLOG_ASSERT(depth == 0);
176
177		lconfig->adc_timeout = $2;
178	}
179	;
180
181sender_statement:	SENDER sender_start sender_entries CB
182	{
183		PJDLOG_ASSERT(depth == 0);
184		PJDLOG_ASSERT(cursection == SECTION_SENDER);
185
186		/* Configure defaults. */
187		if (depth1_checksum == -1)
188			depth1_checksum = ADIST_CHECKSUM_NONE;
189		if (depth1_compression == -1)
190			depth1_compression = ADIST_COMPRESSION_NONE;
191		if (depth1_directory[0] == '\0') {
192			(void)strlcpy(depth1_directory, ADIST_DIRECTORY_SENDER,
193			    sizeof(depth1_directory));
194		}
195		/* Empty depth1_source is ok. */
196		TAILQ_FOREACH(curhost, &lconfig->adc_hosts, adh_next) {
197			if (curhost->adh_role != ADIST_ROLE_SENDER)
198				continue;
199			if (curhost->adh_checksum == -1)
200				curhost->adh_checksum = depth1_checksum;
201			if (curhost->adh_compression == -1)
202				curhost->adh_compression = depth1_compression;
203			if (curhost->adh_directory[0] == '\0') {
204				(void)strlcpy(curhost->adh_directory,
205				    depth1_directory,
206				    sizeof(curhost->adh_directory));
207			}
208			if (curhost->adh_localaddr[0] == '\0') {
209				(void)strlcpy(curhost->adh_localaddr,
210				    depth1_source,
211				    sizeof(curhost->adh_localaddr));
212			}
213		}
214		cursection = SECTION_GLOBAL;
215	}
216	;
217
218sender_start:	OB
219	{
220		PJDLOG_ASSERT(depth == 1);
221		PJDLOG_ASSERT(cursection == SECTION_GLOBAL);
222
223		cursection = SECTION_SENDER;
224		depth1_checksum = -1;
225		depth1_compression = -1;
226		depth1_source[0] = '\0';
227		depth1_directory[0] = '\0';
228
229#ifndef HAVE_AUDIT_SYSCALLS
230		pjdlog_error("Sender functionality is not available.");
231		return (1);
232#endif
233	}
234	;
235
236sender_entries:
237	|
238	sender_entries sender_entry
239	;
240
241sender_entry:
242	source_statement
243	|
244	directory_statement
245/*
246	|
247	checksum_statement
248	|
249	compression_statement
250*/
251	|
252	sender_host_statement
253	;
254
255receiver_statement:	RECEIVER receiver_start receiver_entries CB
256	{
257		PJDLOG_ASSERT(depth == 0);
258		PJDLOG_ASSERT(cursection == SECTION_RECEIVER);
259
260		/*
261		 * If not listen addresses were specified,
262		 * configure default ones.
263		 */
264		if (TAILQ_EMPTY(&lconfig->adc_listen)) {
265			struct adist_listen *lst;
266
267			if (family_supported(AF_INET)) {
268				lst = calloc(1, sizeof(*lst));
269				if (lst == NULL) {
270					pjdlog_error("Unable to allocate memory for listen address.");
271					return (1);
272				}
273				(void)strlcpy(lst->adl_addr,
274				    ADIST_LISTEN_TLS_TCP4,
275				    sizeof(lst->adl_addr));
276				TAILQ_INSERT_TAIL(&lconfig->adc_listen, lst, adl_next);
277			} else {
278				pjdlog_debug(1,
279				    "No IPv4 support in the kernel, not listening on IPv4 address.");
280			}
281			if (family_supported(AF_INET6)) {
282				lst = calloc(1, sizeof(*lst));
283				if (lst == NULL) {
284					pjdlog_error("Unable to allocate memory for listen address.");
285					return (1);
286				}
287				(void)strlcpy(lst->adl_addr,
288				    ADIST_LISTEN_TLS_TCP6,
289				    sizeof(lst->adl_addr));
290				TAILQ_INSERT_TAIL(&lconfig->adc_listen, lst, adl_next);
291			} else {
292				pjdlog_debug(1,
293				    "No IPv6 support in the kernel, not listening on IPv6 address.");
294			}
295			if (TAILQ_EMPTY(&lconfig->adc_listen)) {
296				pjdlog_error("No address to listen on.");
297				return (1);
298			}
299		}
300		/* Configure defaults. */
301		if (depth1_directory[0] == '\0') {
302			(void)strlcpy(depth1_directory,
303			    ADIST_DIRECTORY_RECEIVER,
304			    sizeof(depth1_directory));
305		}
306		TAILQ_FOREACH(curhost, &lconfig->adc_hosts, adh_next) {
307			if (curhost->adh_role != ADIST_ROLE_RECEIVER)
308				continue;
309			if (curhost->adh_directory[0] == '\0') {
310				if (snprintf(curhost->adh_directory,
311				    sizeof(curhost->adh_directory), "%s/%s",
312				    depth1_directory, curhost->adh_name) >=
313				    (ssize_t)sizeof(curhost->adh_directory)) {
314					pjdlog_error("Directory value is too long.");
315					return (1);
316				}
317			}
318		}
319		cursection = SECTION_GLOBAL;
320	}
321	;
322
323receiver_start:	OB
324	{
325		PJDLOG_ASSERT(depth == 1);
326		PJDLOG_ASSERT(cursection == SECTION_GLOBAL);
327
328		cursection = SECTION_RECEIVER;
329		depth1_directory[0] = '\0';
330	}
331	;
332
333receiver_entries:
334	|
335	receiver_entries receiver_entry
336	;
337
338receiver_entry:
339	listen_statement
340	|
341	directory_statement
342	|
343	certfile_statement
344	|
345	keyfile_statement
346	|
347	receiver_host_statement
348	;
349
350/*
351checksum_statement:	CHECKSUM checksum_type
352	{
353		PJDLOG_ASSERT(cursection == SECTION_SENDER);
354
355		switch (depth) {
356		case 1:
357			depth1_checksum = $2;
358			break;
359		case 2:
360			PJDLOG_ASSERT(curhost != NULL);
361			curhost->adh_checksum = $2;
362			break;
363		default:
364			PJDLOG_ABORT("checksum at wrong depth level");
365		}
366	}
367	;
368
369checksum_type:
370	NONE		{ $$ = ADIST_CHECKSUM_NONE; }
371	|
372	CRC32		{ $$ = ADIST_CHECKSUM_CRC32; }
373	|
374	SHA256		{ $$ = ADIST_CHECKSUM_SHA256; }
375	;
376
377compression_statement:	COMPRESSION compression_type
378	{
379		PJDLOG_ASSERT(cursection == SECTION_SENDER);
380
381		switch (depth) {
382		case 1:
383			depth1_compression = $2;
384			break;
385		case 2:
386			PJDLOG_ASSERT(curhost != NULL);
387			curhost->adh_compression = $2;
388			break;
389		default:
390			PJDLOG_ABORT("compression at wrong depth level");
391		}
392	}
393	;
394
395compression_type:
396	NONE		{ $$ = ADIST_COMPRESSION_NONE; }
397	|
398	LZF		{ $$ = ADIST_COMPRESSION_LZF; }
399	;
400*/
401
402directory_statement:	DIRECTORY STR
403	{
404		PJDLOG_ASSERT(cursection == SECTION_SENDER ||
405		    cursection == SECTION_RECEIVER);
406
407		switch (depth) {
408		case 1:
409			if (strlcpy(depth1_directory, $2,
410			    sizeof(depth1_directory)) >=
411			    sizeof(depth1_directory)) {
412				pjdlog_error("Directory value is too long.");
413				free($2);
414				return (1);
415			}
416			if (!adjust_directory(depth1_directory))
417				return (1);
418			break;
419		case 2:
420			if (cursection == SECTION_SENDER || $2[0] == '/') {
421				if (strlcpy(curhost->adh_directory, $2,
422				    sizeof(curhost->adh_directory)) >=
423				    sizeof(curhost->adh_directory)) {
424					pjdlog_error("Directory value is too long.");
425					free($2);
426					return (1);
427				}
428			} else /* if (cursection == SECTION_RECEIVER) */ {
429				if (depth1_directory[0] == '\0') {
430					pjdlog_error("Directory path must be absolute.");
431					free($2);
432					return (1);
433				}
434				if (snprintf(curhost->adh_directory,
435				    sizeof(curhost->adh_directory), "%s/%s",
436				    depth1_directory, $2) >=
437				    (ssize_t)sizeof(curhost->adh_directory)) {
438					pjdlog_error("Directory value is too long.");
439					free($2);
440					return (1);
441				}
442			}
443			break;
444		default:
445			PJDLOG_ABORT("directory at wrong depth level");
446		}
447		free($2);
448	}
449	;
450
451source_statement:	SOURCE STR
452	{
453		PJDLOG_RASSERT(cursection == SECTION_SENDER,
454		    "The source variable must be in sender section.");
455
456		switch (depth) {
457		case 1:
458			if (strlcpy(depth1_source, $2,
459			    sizeof(depth1_source)) >=
460			    sizeof(depth1_source)) {
461				pjdlog_error("Source value is too long.");
462				free($2);
463				return (1);
464			}
465			break;
466		case 2:
467			if (strlcpy(curhost->adh_localaddr, $2,
468			    sizeof(curhost->adh_localaddr)) >=
469			    sizeof(curhost->adh_localaddr)) {
470				pjdlog_error("Source value is too long.");
471				free($2);
472				return (1);
473			}
474			break;
475		}
476		free($2);
477	}
478	;
479
480fingerprint_statement:	FINGERPRINT STR
481	{
482		PJDLOG_ASSERT(cursection == SECTION_SENDER);
483		PJDLOG_ASSERT(depth == 2);
484
485		if (strncasecmp($2, "SHA256=", 7) != 0) {
486			pjdlog_error("Invalid fingerprint value.");
487			free($2);
488			return (1);
489		}
490		if (strlcpy(curhost->adh_fingerprint, $2,
491		    sizeof(curhost->adh_fingerprint)) >=
492		    sizeof(curhost->adh_fingerprint)) {
493			pjdlog_error("Fingerprint value is too long.");
494			free($2);
495			return (1);
496		}
497		free($2);
498	}
499	;
500
501password_statement:	PASSWORD STR
502	{
503		PJDLOG_ASSERT(cursection == SECTION_SENDER ||
504		    cursection == SECTION_RECEIVER);
505		PJDLOG_ASSERT(depth == 2);
506
507		if (strlcpy(curhost->adh_password, $2,
508		    sizeof(curhost->adh_password)) >=
509		    sizeof(curhost->adh_password)) {
510			pjdlog_error("Password value is too long.");
511			bzero($2, strlen($2));
512			free($2);
513			return (1);
514		}
515		bzero($2, strlen($2));
516		free($2);
517	}
518	;
519
520certfile_statement:	CERTFILE STR
521	{
522		PJDLOG_ASSERT(cursection == SECTION_RECEIVER);
523		PJDLOG_ASSERT(depth == 1);
524
525		if (strlcpy(lconfig->adc_certfile, $2,
526		    sizeof(lconfig->adc_certfile)) >=
527		    sizeof(lconfig->adc_certfile)) {
528			pjdlog_error("Certfile value is too long.");
529			free($2);
530			return (1);
531		}
532		free($2);
533	}
534	;
535
536keyfile_statement:	KEYFILE STR
537	{
538		PJDLOG_ASSERT(cursection == SECTION_RECEIVER);
539		PJDLOG_ASSERT(depth == 1);
540
541		if (strlcpy(lconfig->adc_keyfile, $2,
542		    sizeof(lconfig->adc_keyfile)) >=
543		    sizeof(lconfig->adc_keyfile)) {
544			pjdlog_error("Keyfile value is too long.");
545			free($2);
546			return (1);
547		}
548		free($2);
549	}
550	;
551
552listen_statement:	LISTEN STR
553	{
554		struct adist_listen *lst;
555
556		PJDLOG_ASSERT(depth == 1);
557		PJDLOG_ASSERT(cursection == SECTION_RECEIVER);
558
559		lst = calloc(1, sizeof(*lst));
560		if (lst == NULL) {
561			pjdlog_error("Unable to allocate memory for listen address.");
562			free($2);
563			return (1);
564		}
565		if (strlcpy(lst->adl_addr, $2, sizeof(lst->adl_addr)) >=
566		    sizeof(lst->adl_addr)) {
567			pjdlog_error("listen argument is too long.");
568			free($2);
569			free(lst);
570			return (1);
571		}
572		TAILQ_INSERT_TAIL(&lconfig->adc_listen, lst, adl_next);
573		free($2);
574	}
575	;
576
577sender_host_statement:	HOST host_start OB sender_host_entries CB
578	{
579		/* Put it onto host list. */
580		TAILQ_INSERT_TAIL(&lconfig->adc_hosts, curhost, adh_next);
581		curhost = NULL;
582	}
583	;
584
585receiver_host_statement:	HOST host_start OB receiver_host_entries CB
586	{
587		/* Put it onto host list. */
588		TAILQ_INSERT_TAIL(&lconfig->adc_hosts, curhost, adh_next);
589		curhost = NULL;
590	}
591	;
592
593host_start:	STR
594	{
595		/* Check if there is no duplicate entry. */
596		TAILQ_FOREACH(curhost, &lconfig->adc_hosts, adh_next) {
597			if (strcmp(curhost->adh_name, $1) != 0)
598				continue;
599			if (curhost->adh_role == ADIST_ROLE_SENDER &&
600			    cursection == SECTION_RECEIVER) {
601				continue;
602			}
603			if (curhost->adh_role == ADIST_ROLE_RECEIVER &&
604			    cursection == SECTION_SENDER) {
605				continue;
606			}
607			pjdlog_error("%s host %s is configured more than once.",
608			    curhost->adh_role == ADIST_ROLE_SENDER ?
609			    "Sender" : "Receiver", curhost->adh_name);
610			free($1);
611			return (1);
612		}
613
614		curhost = calloc(1, sizeof(*curhost));
615		if (curhost == NULL) {
616			pjdlog_error("Unable to allocate memory for host configuration.");
617			free($1);
618			return (1);
619		}
620		if (strlcpy(curhost->adh_name, $1, sizeof(curhost->adh_name)) >=
621		    sizeof(curhost->adh_name)) {
622			pjdlog_error("Host name is too long.");
623			free($1);
624			return (1);
625		}
626		free($1);
627		curhost->adh_role = cursection == SECTION_SENDER ?
628		    ADIST_ROLE_SENDER : ADIST_ROLE_RECEIVER;
629		curhost->adh_version = ADIST_VERSION;
630		curhost->adh_localaddr[0] = '\0';
631		curhost->adh_remoteaddr[0] = '\0';
632		curhost->adh_remote = NULL;
633		curhost->adh_directory[0] = '\0';
634		curhost->adh_password[0] = '\0';
635		curhost->adh_fingerprint[0] = '\0';
636		curhost->adh_worker_pid = 0;
637		curhost->adh_conn = NULL;
638	}
639	;
640
641sender_host_entries:
642	|
643	sender_host_entries sender_host_entry
644	;
645
646sender_host_entry:
647	source_statement
648	|
649	remote_statement
650	|
651	directory_statement
652	|
653	fingerprint_statement
654	|
655	password_statement
656/*
657	|
658	checksum_statement
659	|
660	compression_statement
661*/
662	;
663
664receiver_host_entries:
665	|
666	receiver_host_entries receiver_host_entry
667	;
668
669receiver_host_entry:
670	remote_statement
671	|
672	directory_statement
673	|
674	password_statement
675	;
676
677remote_statement:	REMOTE STR
678	{
679		PJDLOG_ASSERT(depth == 2);
680		PJDLOG_ASSERT(cursection == SECTION_SENDER ||
681		    cursection == SECTION_RECEIVER);
682
683		if (strlcpy(curhost->adh_remoteaddr, $2,
684		    sizeof(curhost->adh_remoteaddr)) >=
685		    sizeof(curhost->adh_remoteaddr)) {
686			pjdlog_error("Remote value is too long.");
687			free($2);
688			return (1);
689		}
690		free($2);
691	}
692	;
693
694%%
695
696static bool
697family_supported(int family)
698{
699	int sock;
700
701	sock = socket(family, SOCK_STREAM, 0);
702	if (sock == -1 && errno == EPROTONOSUPPORT)
703		return (false);
704	if (sock >= 0)
705		(void)close(sock);
706	return (true);
707}
708
709static bool
710adjust_directory(char *path)
711{
712	size_t len;
713
714	len = strlen(path);
715	for (;;) {
716		if (len == 0) {
717			pjdlog_error("Directory path is empty.");
718			return (false);
719		}
720		if (path[len - 1] != '/')
721			break;
722		len--;
723		path[len] = '\0';
724	}
725	if (path[0] != '/') {
726		pjdlog_error("Directory path must be absolute.");
727		return (false);
728	}
729	return (true);
730}
731
732static int
733my_name(char *name, size_t size)
734{
735	char buf[MAXHOSTNAMELEN];
736	char *pos;
737
738	if (gethostname(buf, sizeof(buf)) < 0) {
739		pjdlog_errno(LOG_ERR, "gethostname() failed");
740		return (-1);
741	}
742
743	/* First component of the host name. */
744	pos = strchr(buf, '.');
745	if (pos == NULL)
746		(void)strlcpy(name, buf, size);
747	else
748		(void)strlcpy(name, buf, MIN((size_t)(pos - buf + 1), size));
749
750	if (name[0] == '\0') {
751		pjdlog_error("Empty host name.");
752		return (-1);
753	}
754
755	return (0);
756}
757
758void
759yyerror(const char *str)
760{
761
762	pjdlog_error("Unable to parse configuration file at line %d near '%s': %s",
763	    lineno, yytext, str);
764}
765
766struct adist_config *
767yy_config_parse(const char *config, bool exitonerror)
768{
769	int ret;
770
771	curhost = NULL;
772	cursection = SECTION_GLOBAL;
773	depth = 0;
774	lineno = 0;
775
776	lconfig = calloc(1, sizeof(*lconfig));
777	if (lconfig == NULL) {
778		pjdlog_error("Unable to allocate memory for configuration.");
779		if (exitonerror)
780			exit(EX_TEMPFAIL);
781		return (NULL);
782	}
783	TAILQ_INIT(&lconfig->adc_hosts);
784	TAILQ_INIT(&lconfig->adc_listen);
785	lconfig->adc_name[0] = '\0';
786	lconfig->adc_timeout = -1;
787	lconfig->adc_pidfile[0] = '\0';
788	lconfig->adc_certfile[0] = '\0';
789	lconfig->adc_keyfile[0] = '\0';
790
791	yyin = fopen(config, "r");
792	if (yyin == NULL) {
793		pjdlog_errno(LOG_ERR, "Unable to open configuration file %s",
794		    config);
795		yy_config_free(lconfig);
796		if (exitonerror)
797			exit(EX_OSFILE);
798		return (NULL);
799	}
800	yyrestart(yyin);
801	ret = yyparse();
802	fclose(yyin);
803	if (ret != 0) {
804		yy_config_free(lconfig);
805		if (exitonerror)
806			exit(EX_CONFIG);
807		return (NULL);
808	}
809
810	/*
811	 * Let's see if everything is set up.
812	 */
813	if (lconfig->adc_name[0] == '\0' && my_name(lconfig->adc_name,
814	    sizeof(lconfig->adc_name)) == -1) {
815		yy_config_free(lconfig);
816		if (exitonerror)
817			exit(EX_CONFIG);
818		return (NULL);
819	}
820	if (lconfig->adc_timeout == -1)
821		lconfig->adc_timeout = ADIST_TIMEOUT;
822	if (lconfig->adc_pidfile[0] == '\0') {
823		(void)strlcpy(lconfig->adc_pidfile, ADIST_PIDFILE,
824		    sizeof(lconfig->adc_pidfile));
825	}
826	if (lconfig->adc_certfile[0] == '\0') {
827		(void)strlcpy(lconfig->adc_certfile, ADIST_CERTFILE,
828		    sizeof(lconfig->adc_certfile));
829	}
830	if (lconfig->adc_keyfile[0] == '\0') {
831		(void)strlcpy(lconfig->adc_keyfile, ADIST_KEYFILE,
832		    sizeof(lconfig->adc_keyfile));
833	}
834
835	return (lconfig);
836}
837
838void
839yy_config_free(struct adist_config *config)
840{
841	struct adist_host *adhost;
842	struct adist_listen *lst;
843
844	while ((lst = TAILQ_FIRST(&config->adc_listen)) != NULL) {
845		TAILQ_REMOVE(&config->adc_listen, lst, adl_next);
846		free(lst);
847	}
848	while ((adhost = TAILQ_FIRST(&config->adc_hosts)) != NULL) {
849		TAILQ_REMOVE(&config->adc_hosts, adhost, adh_next);
850		bzero(adhost, sizeof(*adhost));
851		free(adhost);
852	}
853	free(config);
854}
855