1243730Srwatson/*-
2243730Srwatson * Copyright (c) 2012 The FreeBSD Foundation
3243730Srwatson * All rights reserved.
4243730Srwatson *
5243730Srwatson * This software was developed by Pawel Jakub Dawidek under sponsorship from
6243730Srwatson * the FreeBSD Foundation.
7243730Srwatson *
8243730Srwatson * Redistribution and use in source and binary forms, with or without
9243730Srwatson * modification, are permitted provided that the following conditions
10243730Srwatson * are met:
11243730Srwatson * 1. Redistributions of source code must retain the above copyright
12243730Srwatson *    notice, this list of conditions and the following disclaimer.
13243730Srwatson * 2. Redistributions in binary form must reproduce the above copyright
14243730Srwatson *    notice, this list of conditions and the following disclaimer in the
15243730Srwatson *    documentation and/or other materials provided with the distribution.
16243730Srwatson *
17243730Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
18243730Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19243730Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20243730Srwatson * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
21243730Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22243730Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23243730Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24243730Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25243730Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26243730Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27243730Srwatson * SUCH DAMAGE.
28243730Srwatson */
29243730Srwatson
30243734Srwatson#include <config/config.h>
31243730Srwatson
32243730Srwatson#include <sys/param.h>
33243730Srwatson#if defined(HAVE_SYS_ENDIAN_H) && defined(HAVE_BSWAP)
34243730Srwatson#include <sys/endian.h>
35243730Srwatson#else /* !HAVE_SYS_ENDIAN_H || !HAVE_BSWAP */
36243730Srwatson#ifdef HAVE_MACHINE_ENDIAN_H
37243730Srwatson#include <machine/endian.h>
38243730Srwatson#else /* !HAVE_MACHINE_ENDIAN_H */
39243730Srwatson#ifdef HAVE_ENDIAN_H
40243730Srwatson#include <endian.h>
41243730Srwatson#else /* !HAVE_ENDIAN_H */
42243730Srwatson#error "No supported endian.h"
43243730Srwatson#endif /* !HAVE_ENDIAN_H */
44243730Srwatson#endif /* !HAVE_MACHINE_ENDIAN_H */
45243730Srwatson#include <compat/endian.h>
46243730Srwatson#endif /* !HAVE_SYS_ENDIAN_H || !HAVE_BSWAP */
47243730Srwatson#include <sys/queue.h>
48243730Srwatson#include <sys/wait.h>
49243730Srwatson
50243730Srwatson#include <ctype.h>
51243730Srwatson#include <err.h>
52243730Srwatson#include <errno.h>
53243730Srwatson#include <fcntl.h>
54243730Srwatson#ifdef HAVE_LIBUTIL_H
55243730Srwatson#include <libutil.h>
56243730Srwatson#endif
57243730Srwatson#include <signal.h>
58243730Srwatson#include <stdio.h>
59243730Srwatson#include <stdlib.h>
60243730Srwatson#include <string.h>
61243730Srwatson#include <strings.h>
62243730Srwatson#include <unistd.h>
63243730Srwatson
64243730Srwatson#include <openssl/hmac.h>
65243730Srwatson
66243730Srwatson#ifndef HAVE_PIDFILE_OPEN
67243730Srwatson#include <compat/pidfile.h>
68243730Srwatson#endif
69243730Srwatson#ifndef HAVE_STRLCPY
70243730Srwatson#include <compat/strlcpy.h>
71243730Srwatson#endif
72243730Srwatson#ifndef HAVE_SIGTIMEDWAIT
73243730Srwatson#include "sigtimedwait.h"
74243730Srwatson#endif
75243730Srwatson
76243730Srwatson#include "auditdistd.h"
77243734Srwatson#include "pjdlog.h"
78243730Srwatson#include "proto.h"
79243730Srwatson#include "subr.h"
80243730Srwatson#include "synch.h"
81243730Srwatson
82243730Srwatson/* Path to configuration file. */
83243730Srwatsonconst char *cfgpath = ADIST_CONFIG;
84243730Srwatson/* Auditdistd configuration. */
85243730Srwatsonstatic struct adist_config *adcfg;
86243730Srwatson/* Was SIGINT or SIGTERM signal received? */
87243730Srwatsonbool sigexit_received = false;
88243730Srwatson/* PID file handle. */
89243730Srwatsonstruct pidfh *pfh;
90243730Srwatson
91243730Srwatson/* How often check for hooks running for too long. */
92243730Srwatson#define	SIGNALS_CHECK_INTERVAL	5
93243730Srwatson
94243730Srwatsonstatic void
95243730Srwatsonusage(void)
96243730Srwatson{
97243730Srwatson
98243730Srwatson	errx(EX_USAGE, "[-dFhl] [-c config] [-P pidfile]");
99243730Srwatson}
100243730Srwatson
101243730Srwatsonvoid
102243730Srwatsondescriptors_cleanup(struct adist_host *adhost)
103243730Srwatson{
104243730Srwatson	struct adist_host *adh;
105243730Srwatson	struct adist_listen *lst;
106243730Srwatson
107243730Srwatson	TAILQ_FOREACH(adh, &adcfg->adc_hosts, adh_next) {
108243730Srwatson		if (adh == adhost)
109243730Srwatson			continue;
110243730Srwatson		if (adh->adh_remote != NULL) {
111243730Srwatson			proto_close(adh->adh_remote);
112243730Srwatson			adh->adh_remote = NULL;
113243730Srwatson		}
114243730Srwatson	}
115243730Srwatson	TAILQ_FOREACH(lst, &adcfg->adc_listen, adl_next) {
116243730Srwatson		if (lst->adl_conn != NULL)
117243730Srwatson			proto_close(lst->adl_conn);
118243730Srwatson	}
119243730Srwatson	(void)pidfile_close(pfh);
120243730Srwatson	pjdlog_fini();
121243730Srwatson}
122243730Srwatson
123243730Srwatsonstatic void
124243730Srwatsonchild_cleanup(struct adist_host *adhost)
125243730Srwatson{
126243730Srwatson
127243730Srwatson	if (adhost->adh_conn != NULL) {
128243730Srwatson		PJDLOG_ASSERT(adhost->adh_role == ADIST_ROLE_SENDER);
129243730Srwatson		proto_close(adhost->adh_conn);
130243730Srwatson		adhost->adh_conn = NULL;
131243730Srwatson	}
132243730Srwatson	adhost->adh_worker_pid = 0;
133243730Srwatson}
134243730Srwatson
135243730Srwatsonstatic void
136243730Srwatsonchild_exit_log(const char *type, unsigned int pid, int status)
137243730Srwatson{
138243730Srwatson
139243730Srwatson	if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
140243730Srwatson		pjdlog_debug(1, "%s process exited gracefully (pid=%u).",
141243730Srwatson		    type, pid);
142243730Srwatson	} else if (WIFSIGNALED(status)) {
143243730Srwatson		pjdlog_error("%s process killed (pid=%u, signal=%d).",
144243730Srwatson		    type, pid, WTERMSIG(status));
145243730Srwatson	} else {
146243730Srwatson		pjdlog_error("%s process exited ungracefully (pid=%u, exitcode=%d).",
147243730Srwatson		    type, pid, WIFEXITED(status) ? WEXITSTATUS(status) : -1);
148243730Srwatson	}
149243730Srwatson}
150243730Srwatson
151243730Srwatsonstatic void
152243730Srwatsonchild_exit(void)
153243730Srwatson{
154243730Srwatson	struct adist_host *adhost;
155243730Srwatson	bool restart;
156243730Srwatson	int status;
157243730Srwatson	pid_t pid;
158243730Srwatson
159243730Srwatson	restart = false;
160243730Srwatson	while ((pid = wait3(&status, WNOHANG, NULL)) > 0) {
161243730Srwatson		/* Find host related to the process that just exited. */
162243730Srwatson		TAILQ_FOREACH(adhost, &adcfg->adc_hosts, adh_next) {
163243730Srwatson			if (pid == adhost->adh_worker_pid)
164243730Srwatson				break;
165243730Srwatson		}
166243730Srwatson		if (adhost == NULL) {
167243730Srwatson			child_exit_log("Sandbox", pid, status);
168243730Srwatson		} else {
169243730Srwatson			if (adhost->adh_role == ADIST_ROLE_SENDER)
170243730Srwatson				restart = true;
171243730Srwatson			pjdlog_prefix_set("[%s] (%s) ", adhost->adh_name,
172243730Srwatson			    role2str(adhost->adh_role));
173243730Srwatson			child_exit_log("Worker", pid, status);
174243730Srwatson			child_cleanup(adhost);
175243730Srwatson			pjdlog_prefix_set("%s", "");
176243730Srwatson		}
177243730Srwatson	}
178243730Srwatson	if (!restart)
179243730Srwatson		return;
180243730Srwatson	/* We have some sender processes to restart. */
181243730Srwatson	sleep(1);
182243730Srwatson	TAILQ_FOREACH(adhost, &adcfg->adc_hosts, adh_next) {
183243730Srwatson		if (adhost->adh_role != ADIST_ROLE_SENDER)
184243730Srwatson			continue;
185243730Srwatson		if (adhost->adh_worker_pid != 0)
186243730Srwatson			continue;
187243730Srwatson		pjdlog_prefix_set("[%s] (%s) ", adhost->adh_name,
188243730Srwatson		    role2str(adhost->adh_role));
189243730Srwatson		pjdlog_info("Restarting sender process.");
190243730Srwatson		adist_sender(adcfg, adhost);
191243730Srwatson		pjdlog_prefix_set("%s", "");
192243730Srwatson	}
193243730Srwatson}
194243730Srwatson
195243730Srwatson/* TODO */
196243730Srwatsonstatic void
197243730Srwatsonadist_reload(void)
198243730Srwatson{
199243730Srwatson
200243730Srwatson	pjdlog_info("Reloading configuration is not yet implemented.");
201243730Srwatson}
202243730Srwatson
203243730Srwatsonstatic void
204243730Srwatsonterminate_workers(void)
205243730Srwatson{
206243730Srwatson	struct adist_host *adhost;
207243730Srwatson
208243730Srwatson	pjdlog_info("Termination signal received, exiting.");
209243730Srwatson	TAILQ_FOREACH(adhost, &adcfg->adc_hosts, adh_next) {
210243730Srwatson		if (adhost->adh_worker_pid == 0)
211243730Srwatson			continue;
212243730Srwatson		pjdlog_info("Terminating worker process (adhost=%s, role=%s, pid=%u).",
213243730Srwatson		    adhost->adh_name, role2str(adhost->adh_role),
214243730Srwatson		    adhost->adh_worker_pid);
215243730Srwatson		if (kill(adhost->adh_worker_pid, SIGTERM) == 0)
216243730Srwatson			continue;
217243730Srwatson		pjdlog_errno(LOG_WARNING,
218243730Srwatson		    "Unable to send signal to worker process (adhost=%s, role=%s, pid=%u).",
219243730Srwatson		    adhost->adh_name, role2str(adhost->adh_role),
220243730Srwatson		    adhost->adh_worker_pid);
221243730Srwatson	}
222243730Srwatson}
223243730Srwatson
224243730Srwatsonstatic void
225243730Srwatsonlisten_accept(struct adist_listen *lst)
226243730Srwatson{
227243730Srwatson	unsigned char rnd[32], hash[32], resp[32];
228243730Srwatson	struct adist_host *adhost;
229243730Srwatson	struct proto_conn *conn;
230243730Srwatson	char adname[ADIST_HOSTSIZE];
231243730Srwatson	char laddr[256], raddr[256];
232243730Srwatson	char welcome[8];
233243730Srwatson	int status, version;
234243730Srwatson	pid_t pid;
235243730Srwatson
236243730Srwatson	proto_local_address(lst->adl_conn, laddr, sizeof(laddr));
237243730Srwatson	pjdlog_debug(1, "Accepting connection to %s.", laddr);
238243730Srwatson
239243730Srwatson	if (proto_accept(lst->adl_conn, &conn) == -1) {
240243730Srwatson		pjdlog_errno(LOG_ERR, "Unable to accept connection to %s",
241243730Srwatson		    laddr);
242243730Srwatson		return;
243243730Srwatson	}
244243730Srwatson
245243730Srwatson	proto_local_address(conn, laddr, sizeof(laddr));
246243730Srwatson	proto_remote_address(conn, raddr, sizeof(raddr));
247243730Srwatson	pjdlog_info("Connection from %s to %s.", raddr, laddr);
248243730Srwatson
249243730Srwatson	/* Error in setting timeout is not critical, but why should it fail? */
250243730Srwatson	if (proto_timeout(conn, ADIST_TIMEOUT) < 0)
251243730Srwatson		pjdlog_errno(LOG_WARNING, "Unable to set connection timeout");
252243730Srwatson
253243730Srwatson	/*
254243730Srwatson	 * Before receiving any data see if remote host is known.
255243730Srwatson	 */
256243730Srwatson	TAILQ_FOREACH(adhost, &adcfg->adc_hosts, adh_next) {
257243730Srwatson		if (adhost->adh_role != ADIST_ROLE_RECEIVER)
258243730Srwatson			continue;
259243730Srwatson		if (!proto_address_match(conn, adhost->adh_remoteaddr))
260243730Srwatson			continue;
261243730Srwatson		break;
262243730Srwatson	}
263243730Srwatson	if (adhost == NULL) {
264243730Srwatson		pjdlog_error("Client %s is not known.", raddr);
265243730Srwatson		goto close;
266243730Srwatson	}
267243730Srwatson	/* Ok, remote host is known. */
268243730Srwatson
269243730Srwatson	/* Exchange welcome message, which include version number. */
270243730Srwatson	bzero(welcome, sizeof(welcome));
271243730Srwatson	if (proto_recv(conn, welcome, sizeof(welcome)) == -1) {
272243730Srwatson		pjdlog_errno(LOG_WARNING,
273243730Srwatson		    "Unable to receive welcome message from %s",
274243730Srwatson		    adhost->adh_remoteaddr);
275243730Srwatson		goto close;
276243730Srwatson	}
277243730Srwatson	if (strncmp(welcome, "ADIST", 5) != 0 || !isdigit(welcome[5]) ||
278243730Srwatson	    !isdigit(welcome[6]) || welcome[7] != '\0') {
279243730Srwatson		pjdlog_warning("Invalid welcome message from %s.",
280243730Srwatson		    adhost->adh_remoteaddr);
281243730Srwatson		goto close;
282243730Srwatson	}
283243730Srwatson
284243730Srwatson	version = MIN(ADIST_VERSION, atoi(welcome + 5));
285243730Srwatson
286243730Srwatson	(void)snprintf(welcome, sizeof(welcome), "ADIST%02d", version);
287243730Srwatson	if (proto_send(conn, welcome, sizeof(welcome)) == -1) {
288243730Srwatson		pjdlog_errno(LOG_WARNING,
289243730Srwatson		    "Unable to send welcome message to %s",
290243730Srwatson		    adhost->adh_remoteaddr);
291243730Srwatson		goto close;
292243730Srwatson	}
293243730Srwatson
294243730Srwatson	if (proto_recv(conn, adname, sizeof(adhost->adh_name)) < 0) {
295243730Srwatson		pjdlog_errno(LOG_ERR, "Unable to receive hostname from %s",
296243730Srwatson		    raddr);
297243730Srwatson		goto close;
298243730Srwatson	}
299243730Srwatson
300243730Srwatson	/* Find host now that we have hostname. */
301243730Srwatson	TAILQ_FOREACH(adhost, &adcfg->adc_hosts, adh_next) {
302243730Srwatson		if (adhost->adh_role != ADIST_ROLE_RECEIVER)
303243730Srwatson			continue;
304243730Srwatson		if (!proto_address_match(conn, adhost->adh_remoteaddr))
305243730Srwatson			continue;
306243730Srwatson		if (strcmp(adhost->adh_name, adname) != 0)
307243730Srwatson			continue;
308243730Srwatson		break;
309243730Srwatson	}
310243730Srwatson	if (adhost == NULL) {
311243730Srwatson		pjdlog_error("No configuration for host %s from address %s.",
312243730Srwatson		    adname, raddr);
313243730Srwatson		goto close;
314243730Srwatson	}
315243730Srwatson
316243730Srwatson	adhost->adh_version = version;
317243730Srwatson	pjdlog_debug(1, "Version %d negotiated with %s.", adhost->adh_version,
318243730Srwatson	    adhost->adh_remoteaddr);
319243730Srwatson
320243730Srwatson	/* Now that we know host name setup log prefix. */
321243730Srwatson	pjdlog_prefix_set("[%s] (%s) ", adhost->adh_name,
322243730Srwatson	    role2str(adhost->adh_role));
323243730Srwatson
324243730Srwatson	if (adist_random(rnd, sizeof(rnd)) == -1) {
325243730Srwatson		pjdlog_error("Unable to generate challenge.");
326243730Srwatson		goto close;
327243730Srwatson	}
328243730Srwatson	pjdlog_debug(1, "Challenge generated.");
329243730Srwatson
330243730Srwatson	if (proto_send(conn, rnd, sizeof(rnd)) == -1) {
331243730Srwatson		pjdlog_errno(LOG_ERR, "Unable to send challenge to %s",
332243730Srwatson		    adhost->adh_remoteaddr);
333243730Srwatson		goto close;
334243730Srwatson	}
335243730Srwatson	pjdlog_debug(1, "Challenge sent.");
336243730Srwatson
337243730Srwatson	if (proto_recv(conn, resp, sizeof(resp)) == -1) {
338243730Srwatson		pjdlog_errno(LOG_ERR, "Unable to receive response from %s",
339243730Srwatson		    adhost->adh_remoteaddr);
340243730Srwatson		goto close;
341243730Srwatson	}
342243730Srwatson	pjdlog_debug(1, "Response received.");
343243730Srwatson
344243730Srwatson	if (HMAC(EVP_sha256(), adhost->adh_password,
345243730Srwatson	    (int)strlen(adhost->adh_password), rnd, (int)sizeof(rnd), hash,
346243730Srwatson	    NULL) == NULL) {
347243730Srwatson		pjdlog_error("Unable to generate hash.");
348243730Srwatson		goto close;
349243730Srwatson	}
350243730Srwatson	pjdlog_debug(1, "Hash generated.");
351243730Srwatson
352243730Srwatson	if (memcmp(resp, hash, sizeof(hash)) != 0) {
353243730Srwatson		pjdlog_error("Invalid response from %s (wrong password?).",
354243730Srwatson		    adhost->adh_remoteaddr);
355243730Srwatson		goto close;
356243730Srwatson	}
357243730Srwatson	pjdlog_info("Sender authenticated.");
358243730Srwatson
359243730Srwatson	if (proto_recv(conn, rnd, sizeof(rnd)) == -1) {
360243730Srwatson		pjdlog_errno(LOG_ERR, "Unable to receive challenge from %s",
361243730Srwatson		    adhost->adh_remoteaddr);
362243730Srwatson		goto close;
363243730Srwatson	}
364243730Srwatson	pjdlog_debug(1, "Challenge received.");
365243730Srwatson
366243730Srwatson	if (HMAC(EVP_sha256(), adhost->adh_password,
367243730Srwatson	    (int)strlen(adhost->adh_password), rnd, (int)sizeof(rnd), hash,
368243730Srwatson	    NULL) == NULL) {
369243730Srwatson		pjdlog_error("Unable to generate response.");
370243730Srwatson		goto close;
371243730Srwatson	}
372243730Srwatson	pjdlog_debug(1, "Response generated.");
373243730Srwatson
374243730Srwatson	if (proto_send(conn, hash, sizeof(hash)) == -1) {
375243730Srwatson		pjdlog_errno(LOG_ERR, "Unable to send response to %s",
376243730Srwatson		    adhost->adh_remoteaddr);
377243730Srwatson		goto close;
378243730Srwatson	}
379243730Srwatson	pjdlog_debug(1, "Response sent.");
380243730Srwatson
381243730Srwatson	if (adhost->adh_worker_pid != 0) {
382243730Srwatson		pjdlog_debug(1,
383243730Srwatson		    "Receiver process exists (pid=%u), stopping it.",
384243730Srwatson		    (unsigned int)adhost->adh_worker_pid);
385243730Srwatson		/* Stop child process. */
386243730Srwatson		if (kill(adhost->adh_worker_pid, SIGINT) == -1) {
387243730Srwatson			pjdlog_errno(LOG_ERR,
388243730Srwatson			    "Unable to stop worker process (pid=%u)",
389243730Srwatson			    (unsigned int)adhost->adh_worker_pid);
390243730Srwatson			/*
391243730Srwatson			 * Other than logging the problem we
392243730Srwatson			 * ignore it - nothing smart to do.
393243730Srwatson			 */
394243730Srwatson		}
395243730Srwatson		/* Wait for it to exit. */
396243730Srwatson		else if ((pid = waitpid(adhost->adh_worker_pid,
397243730Srwatson		    &status, 0)) != adhost->adh_worker_pid) {
398243730Srwatson			/* We can only log the problem. */
399243730Srwatson			pjdlog_errno(LOG_ERR,
400243730Srwatson			    "Waiting for worker process (pid=%u) failed",
401243730Srwatson			    (unsigned int)adhost->adh_worker_pid);
402243730Srwatson		} else {
403243730Srwatson			child_exit_log("Worker", adhost->adh_worker_pid,
404243730Srwatson			    status);
405243730Srwatson		}
406243730Srwatson		child_cleanup(adhost);
407243730Srwatson	}
408243730Srwatson
409243730Srwatson	adhost->adh_remote = conn;
410243730Srwatson	adist_receiver(adcfg, adhost);
411243730Srwatson
412243730Srwatson	pjdlog_prefix_set("%s", "");
413243730Srwatson	return;
414243730Srwatsonclose:
415243730Srwatson	proto_close(conn);
416243730Srwatson	pjdlog_prefix_set("%s", "");
417243730Srwatson}
418243730Srwatson
419243730Srwatsonstatic void
420243730Srwatsonconnection_migrate(struct adist_host *adhost)
421243730Srwatson{
422243730Srwatson	struct proto_conn *conn;
423243730Srwatson	int16_t val = 0;
424243730Srwatson
425243730Srwatson	pjdlog_prefix_set("[%s] (%s) ", adhost->adh_name,
426243730Srwatson	    role2str(adhost->adh_role));
427243730Srwatson
428243730Srwatson	PJDLOG_ASSERT(adhost->adh_role == ADIST_ROLE_SENDER);
429243730Srwatson
430243730Srwatson	if (proto_recv(adhost->adh_conn, &val, sizeof(val)) < 0) {
431243730Srwatson		pjdlog_errno(LOG_WARNING,
432243730Srwatson		    "Unable to receive connection command");
433243730Srwatson		return;
434243730Srwatson	}
435243730Srwatson	if (proto_set("tls:fingerprint", adhost->adh_fingerprint) == -1) {
436243730Srwatson		val = errno;
437243730Srwatson		pjdlog_errno(LOG_WARNING, "Unable to set fingerprint");
438243730Srwatson		goto out;
439243730Srwatson	}
440243730Srwatson	if (proto_connect(adhost->adh_localaddr[0] != '\0' ?
441243730Srwatson	    adhost->adh_localaddr : NULL,
442243730Srwatson	    adhost->adh_remoteaddr, -1, &conn) < 0) {
443243730Srwatson		val = errno;
444243730Srwatson		pjdlog_errno(LOG_WARNING, "Unable to connect to %s",
445243730Srwatson		    adhost->adh_remoteaddr);
446243730Srwatson		goto out;
447243730Srwatson	}
448243730Srwatson	val = 0;
449243730Srwatsonout:
450243730Srwatson	if (proto_send(adhost->adh_conn, &val, sizeof(val)) < 0) {
451243730Srwatson		pjdlog_errno(LOG_WARNING,
452243730Srwatson		    "Unable to send reply to connection request");
453243730Srwatson	}
454243730Srwatson	if (val == 0 && proto_connection_send(adhost->adh_conn, conn) < 0)
455243730Srwatson		pjdlog_errno(LOG_WARNING, "Unable to send connection");
456243730Srwatson
457243730Srwatson	pjdlog_prefix_set("%s", "");
458243730Srwatson}
459243730Srwatson
460243730Srwatsonstatic void
461243730Srwatsoncheck_signals(void)
462243730Srwatson{
463243730Srwatson	struct timespec sigtimeout;
464243730Srwatson	sigset_t mask;
465243730Srwatson	int signo;
466243730Srwatson
467243730Srwatson	sigtimeout.tv_sec = 0;
468243730Srwatson	sigtimeout.tv_nsec = 0;
469243730Srwatson
470243730Srwatson	PJDLOG_VERIFY(sigemptyset(&mask) == 0);
471243730Srwatson	PJDLOG_VERIFY(sigaddset(&mask, SIGHUP) == 0);
472243730Srwatson	PJDLOG_VERIFY(sigaddset(&mask, SIGINT) == 0);
473243730Srwatson	PJDLOG_VERIFY(sigaddset(&mask, SIGTERM) == 0);
474243730Srwatson	PJDLOG_VERIFY(sigaddset(&mask, SIGCHLD) == 0);
475243730Srwatson
476243730Srwatson	while ((signo = sigtimedwait(&mask, NULL, &sigtimeout)) != -1) {
477243730Srwatson		switch (signo) {
478243730Srwatson		case SIGINT:
479243730Srwatson		case SIGTERM:
480243730Srwatson			sigexit_received = true;
481243730Srwatson			terminate_workers();
482243730Srwatson			exit(EX_OK);
483243730Srwatson			break;
484243730Srwatson		case SIGCHLD:
485243730Srwatson			child_exit();
486243730Srwatson			break;
487243730Srwatson		case SIGHUP:
488243730Srwatson			adist_reload();
489243730Srwatson			break;
490243730Srwatson		default:
491243730Srwatson			PJDLOG_ABORT("Unexpected signal (%d).", signo);
492243730Srwatson		}
493243730Srwatson	}
494243730Srwatson}
495243730Srwatson
496243730Srwatsonstatic void
497243730Srwatsonmain_loop(void)
498243730Srwatson{
499243730Srwatson	struct adist_host *adhost;
500243730Srwatson	struct adist_listen *lst;
501243730Srwatson	struct timeval seltimeout;
502243730Srwatson	int fd, maxfd, ret;
503243730Srwatson	fd_set rfds;
504243730Srwatson
505243730Srwatson	seltimeout.tv_sec = SIGNALS_CHECK_INTERVAL;
506243730Srwatson	seltimeout.tv_usec = 0;
507243730Srwatson
508243730Srwatson	pjdlog_info("Started successfully.");
509243730Srwatson
510243730Srwatson	for (;;) {
511243730Srwatson		check_signals();
512243730Srwatson
513243730Srwatson		/* Setup descriptors for select(2). */
514243730Srwatson		FD_ZERO(&rfds);
515243730Srwatson		maxfd = -1;
516243730Srwatson		TAILQ_FOREACH(lst, &adcfg->adc_listen, adl_next) {
517243730Srwatson			if (lst->adl_conn == NULL)
518243730Srwatson				continue;
519243730Srwatson			fd = proto_descriptor(lst->adl_conn);
520243730Srwatson			PJDLOG_ASSERT(fd >= 0);
521243730Srwatson			FD_SET(fd, &rfds);
522243730Srwatson			maxfd = fd > maxfd ? fd : maxfd;
523243730Srwatson		}
524243730Srwatson		TAILQ_FOREACH(adhost, &adcfg->adc_hosts, adh_next) {
525243730Srwatson			if (adhost->adh_role == ADIST_ROLE_SENDER) {
526243730Srwatson				/* Only sender workers asks for connections. */
527243730Srwatson				PJDLOG_ASSERT(adhost->adh_conn != NULL);
528243730Srwatson				fd = proto_descriptor(adhost->adh_conn);
529243730Srwatson				PJDLOG_ASSERT(fd >= 0);
530243730Srwatson				FD_SET(fd, &rfds);
531243730Srwatson				maxfd = fd > maxfd ? fd : maxfd;
532243730Srwatson			} else {
533243730Srwatson				PJDLOG_ASSERT(adhost->adh_conn == NULL);
534243730Srwatson			}
535243730Srwatson		}
536243730Srwatson
537243730Srwatson		PJDLOG_ASSERT(maxfd + 1 <= (int)FD_SETSIZE);
538243730Srwatson		ret = select(maxfd + 1, &rfds, NULL, NULL, &seltimeout);
539243730Srwatson		if (ret == 0) {
540243730Srwatson			/*
541243730Srwatson			 * select(2) timed out, so there should be no
542243730Srwatson			 * descriptors to check.
543243730Srwatson			 */
544243730Srwatson			continue;
545243730Srwatson		} else if (ret == -1) {
546243730Srwatson			if (errno == EINTR)
547243730Srwatson				continue;
548243730Srwatson			KEEP_ERRNO((void)pidfile_remove(pfh));
549243730Srwatson			pjdlog_exit(EX_OSERR, "select() failed");
550243730Srwatson		}
551243730Srwatson		PJDLOG_ASSERT(ret > 0);
552243730Srwatson
553243730Srwatson		/*
554243730Srwatson		 * Check for signals before we do anything to update our
555243730Srwatson		 * info about terminated workers in the meantime.
556243730Srwatson		 */
557243730Srwatson		check_signals();
558243730Srwatson
559243730Srwatson		TAILQ_FOREACH(lst, &adcfg->adc_listen, adl_next) {
560243730Srwatson			if (lst->adl_conn == NULL)
561243730Srwatson				continue;
562243730Srwatson			if (FD_ISSET(proto_descriptor(lst->adl_conn), &rfds))
563243730Srwatson				listen_accept(lst);
564243730Srwatson		}
565243730Srwatson		TAILQ_FOREACH(adhost, &adcfg->adc_hosts, adh_next) {
566243730Srwatson			if (adhost->adh_role == ADIST_ROLE_SENDER) {
567243730Srwatson				PJDLOG_ASSERT(adhost->adh_conn != NULL);
568243730Srwatson				if (FD_ISSET(proto_descriptor(adhost->adh_conn),
569243730Srwatson				    &rfds)) {
570243730Srwatson					connection_migrate(adhost);
571243730Srwatson				}
572243730Srwatson			} else {
573243730Srwatson				PJDLOG_ASSERT(adhost->adh_conn == NULL);
574243730Srwatson			}
575243730Srwatson		}
576243730Srwatson	}
577243730Srwatson}
578243730Srwatson
579243730Srwatsonstatic void
580243730Srwatsonadist_config_dump(struct adist_config *cfg)
581243730Srwatson{
582243730Srwatson	struct adist_host *adhost;
583243730Srwatson	struct adist_listen *lst;
584243730Srwatson
585243730Srwatson	pjdlog_debug(2, "Configuration:");
586243730Srwatson	pjdlog_debug(2, "  Global:");
587243730Srwatson	pjdlog_debug(2, "    pidfile: %s", cfg->adc_pidfile);
588243730Srwatson	pjdlog_debug(2, "    timeout: %d", cfg->adc_timeout);
589243730Srwatson	if (TAILQ_EMPTY(&cfg->adc_listen)) {
590243730Srwatson		pjdlog_debug(2, "  Sender only, not listening.");
591243730Srwatson	} else {
592243730Srwatson		pjdlog_debug(2, "  Listening on:");
593243730Srwatson		TAILQ_FOREACH(lst, &cfg->adc_listen, adl_next) {
594243730Srwatson			pjdlog_debug(2, "    listen: %s", lst->adl_addr);
595243730Srwatson			pjdlog_debug(2, "    conn: %p", lst->adl_conn);
596243730Srwatson		}
597243730Srwatson	}
598243730Srwatson	pjdlog_debug(2, "  Hosts:");
599243730Srwatson	TAILQ_FOREACH(adhost, &cfg->adc_hosts, adh_next) {
600243730Srwatson		pjdlog_debug(2, "    name: %s", adhost->adh_name);
601243730Srwatson		pjdlog_debug(2, "      role: %s", role2str(adhost->adh_role));
602243730Srwatson		pjdlog_debug(2, "      version: %d", adhost->adh_version);
603243730Srwatson		pjdlog_debug(2, "      localaddr: %s", adhost->adh_localaddr);
604243730Srwatson		pjdlog_debug(2, "      remoteaddr: %s", adhost->adh_remoteaddr);
605243730Srwatson		pjdlog_debug(2, "      remote: %p", adhost->adh_remote);
606243730Srwatson		pjdlog_debug(2, "      directory: %s", adhost->adh_directory);
607243730Srwatson		pjdlog_debug(2, "      compression: %d", adhost->adh_compression);
608243730Srwatson		pjdlog_debug(2, "      checksum: %d", adhost->adh_checksum);
609243730Srwatson		pjdlog_debug(2, "      pid: %ld", (long)adhost->adh_worker_pid);
610243730Srwatson		pjdlog_debug(2, "      conn: %p", adhost->adh_conn);
611243730Srwatson	}
612243730Srwatson}
613243730Srwatson
614243730Srwatsonstatic void
615243730Srwatsondummy_sighandler(int sig __unused)
616243730Srwatson{
617243730Srwatson	/* Nothing to do. */
618243730Srwatson}
619243730Srwatson
620243730Srwatsonint
621243730Srwatsonmain(int argc, char *argv[])
622243730Srwatson{
623243730Srwatson	struct adist_host *adhost;
624243730Srwatson	struct adist_listen *lst;
625243730Srwatson	const char *execpath, *pidfile;
626243730Srwatson	bool foreground, launchd;
627243730Srwatson	pid_t otherpid;
628243730Srwatson	int debuglevel;
629243730Srwatson	sigset_t mask;
630243730Srwatson
631243730Srwatson	execpath = argv[0];
632243730Srwatson	if (execpath[0] != '/') {
633243730Srwatson		errx(EX_USAGE,
634243730Srwatson		    "auditdistd requires execution with an absolute path.");
635243730Srwatson	}
636243730Srwatson
637243730Srwatson	/*
638243730Srwatson	 * We are executed from proto to create sandbox.
639243730Srwatson	 */
640243730Srwatson	if (argc > 1 && strcmp(argv[1], "proto") == 0) {
641243730Srwatson		argc -= 2;
642243730Srwatson		argv += 2;
643243730Srwatson		if (proto_exec(argc, argv) == -1)
644243730Srwatson			err(EX_USAGE, "Unable to execute proto");
645243730Srwatson	}
646243730Srwatson
647243730Srwatson	foreground = false;
648243730Srwatson	debuglevel = 0;
649243730Srwatson	launchd = false;
650243730Srwatson	pidfile = NULL;
651243730Srwatson
652243730Srwatson	for (;;) {
653243730Srwatson		int ch;
654243730Srwatson
655243730Srwatson		ch = getopt(argc, argv, "c:dFhlP:");
656243730Srwatson		if (ch == -1)
657243730Srwatson			break;
658243730Srwatson		switch (ch) {
659243730Srwatson		case 'c':
660243730Srwatson			cfgpath = optarg;
661243730Srwatson			break;
662243730Srwatson		case 'd':
663243730Srwatson			debuglevel++;
664243730Srwatson			break;
665243730Srwatson		case 'F':
666243730Srwatson			foreground = true;
667243730Srwatson			break;
668243730Srwatson		case 'l':
669243730Srwatson			launchd = true;
670243730Srwatson			break;
671243730Srwatson		case 'P':
672243730Srwatson			pidfile = optarg;
673243730Srwatson			break;
674243730Srwatson		case 'h':
675243730Srwatson		default:
676243730Srwatson			usage();
677243730Srwatson		}
678243730Srwatson	}
679243730Srwatson	argc -= optind;
680243730Srwatson	argv += optind;
681243730Srwatson
682243730Srwatson	pjdlog_init(PJDLOG_MODE_STD);
683243730Srwatson	pjdlog_debug_set(debuglevel);
684243730Srwatson
685243730Srwatson	if (proto_set("execpath", execpath) == -1)
686243730Srwatson		pjdlog_exit(EX_TEMPFAIL, "Unable to set executable name");
687243730Srwatson	if (proto_set("user", ADIST_USER) == -1)
688243730Srwatson		pjdlog_exit(EX_TEMPFAIL, "Unable to set proto user");
689243730Srwatson	if (proto_set("tcp:port", ADIST_TCP_PORT) == -1)
690243730Srwatson		pjdlog_exit(EX_TEMPFAIL, "Unable to set default TCP port");
691243730Srwatson
692243730Srwatson	/*
693243730Srwatson	 * When path to the configuration file is relative, obtain full path,
694243730Srwatson	 * so we can always find the file, even after daemonizing and changing
695243730Srwatson	 * working directory to /.
696243730Srwatson	 */
697243730Srwatson	if (cfgpath[0] != '/') {
698243730Srwatson		const char *newcfgpath;
699243730Srwatson
700243730Srwatson		newcfgpath = realpath(cfgpath, NULL);
701243730Srwatson		if (newcfgpath == NULL) {
702243730Srwatson			pjdlog_exit(EX_CONFIG,
703243730Srwatson			    "Unable to obtain full path of %s", cfgpath);
704243730Srwatson		}
705243730Srwatson		cfgpath = newcfgpath;
706243730Srwatson	}
707243730Srwatson
708243730Srwatson	adcfg = yy_config_parse(cfgpath, true);
709243730Srwatson	PJDLOG_ASSERT(adcfg != NULL);
710243730Srwatson	adist_config_dump(adcfg);
711243730Srwatson
712243730Srwatson	if (proto_set("tls:certfile", adcfg->adc_certfile) == -1)
713243730Srwatson		pjdlog_exit(EX_TEMPFAIL, "Unable to set certfile path");
714243730Srwatson	if (proto_set("tls:keyfile", adcfg->adc_keyfile) == -1)
715243730Srwatson		pjdlog_exit(EX_TEMPFAIL, "Unable to set keyfile path");
716243730Srwatson
717243730Srwatson	if (pidfile != NULL) {
718243730Srwatson		if (strlcpy(adcfg->adc_pidfile, pidfile,
719243730Srwatson		    sizeof(adcfg->adc_pidfile)) >=
720243730Srwatson		    sizeof(adcfg->adc_pidfile)) {
721243730Srwatson			pjdlog_exitx(EX_CONFIG, "Pidfile path is too long.");
722243730Srwatson		}
723243730Srwatson	}
724243730Srwatson	if (foreground && pidfile == NULL) {
725243730Srwatson		pfh = NULL;
726243730Srwatson	} else {
727243730Srwatson		pfh = pidfile_open(adcfg->adc_pidfile, 0600, &otherpid);
728243730Srwatson		if (pfh == NULL) {
729243730Srwatson			if (errno == EEXIST) {
730243730Srwatson				pjdlog_exitx(EX_TEMPFAIL,
731243730Srwatson				    "Another auditdistd is already running, pid: %jd.",
732243730Srwatson				    (intmax_t)otherpid);
733243730Srwatson			}
734243730Srwatson			/*
735243730Srwatson			 * If we cannot create pidfile from other reasons,
736243730Srwatson			 * only warn.
737243730Srwatson			 */
738243730Srwatson			pjdlog_errno(LOG_WARNING,
739243730Srwatson			    "Unable to open or create pidfile %s",
740243730Srwatson			    adcfg->adc_pidfile);
741243730Srwatson		}
742243730Srwatson	}
743243730Srwatson
744243730Srwatson	/*
745243730Srwatson	 * Restore default actions for interesting signals in case parent
746243730Srwatson	 * process (like init(8)) decided to ignore some of them (like SIGHUP).
747243730Srwatson	 */
748243730Srwatson	PJDLOG_VERIFY(signal(SIGHUP, SIG_DFL) != SIG_ERR);
749243730Srwatson	PJDLOG_VERIFY(signal(SIGINT, SIG_DFL) != SIG_ERR);
750243730Srwatson	PJDLOG_VERIFY(signal(SIGTERM, SIG_DFL) != SIG_ERR);
751243730Srwatson	/*
752243730Srwatson	 * Because SIGCHLD is ignored by default, setup dummy handler for it,
753243730Srwatson	 * so we can mask it.
754243730Srwatson	 */
755243730Srwatson	PJDLOG_VERIFY(signal(SIGCHLD, dummy_sighandler) != SIG_ERR);
756243730Srwatson
757243730Srwatson	PJDLOG_VERIFY(sigemptyset(&mask) == 0);
758243730Srwatson	PJDLOG_VERIFY(sigaddset(&mask, SIGHUP) == 0);
759243730Srwatson	PJDLOG_VERIFY(sigaddset(&mask, SIGINT) == 0);
760243730Srwatson	PJDLOG_VERIFY(sigaddset(&mask, SIGTERM) == 0);
761243730Srwatson	PJDLOG_VERIFY(sigaddset(&mask, SIGCHLD) == 0);
762243730Srwatson	PJDLOG_VERIFY(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
763243730Srwatson
764243730Srwatson	/* Listen for remote connections. */
765243730Srwatson	TAILQ_FOREACH(lst, &adcfg->adc_listen, adl_next) {
766243730Srwatson		if (proto_server(lst->adl_addr, &lst->adl_conn) == -1) {
767243730Srwatson			KEEP_ERRNO((void)pidfile_remove(pfh));
768243730Srwatson			pjdlog_exit(EX_OSERR, "Unable to listen on address %s",
769243730Srwatson			    lst->adl_addr);
770243730Srwatson		}
771243730Srwatson	}
772243730Srwatson
773243730Srwatson	if (!foreground) {
774243730Srwatson		if (!launchd && daemon(0, 0) == -1) {
775243730Srwatson			KEEP_ERRNO((void)pidfile_remove(pfh));
776243730Srwatson			pjdlog_exit(EX_OSERR, "Unable to daemonize");
777243730Srwatson		}
778243730Srwatson
779243730Srwatson		/* Start logging to syslog. */
780243730Srwatson		pjdlog_mode_set(PJDLOG_MODE_SYSLOG);
781243730Srwatson	}
782243730Srwatson	if (pfh != NULL) {
783243730Srwatson		/* Write PID to a file. */
784243730Srwatson		if (pidfile_write(pfh) < 0) {
785243730Srwatson			pjdlog_errno(LOG_WARNING,
786243730Srwatson			    "Unable to write PID to a file");
787243730Srwatson		}
788243730Srwatson	}
789243730Srwatson
790243730Srwatson	TAILQ_FOREACH(adhost, &adcfg->adc_hosts, adh_next) {
791243730Srwatson		if (adhost->adh_role == ADIST_ROLE_SENDER)
792243730Srwatson			adist_sender(adcfg, adhost);
793243730Srwatson	}
794243730Srwatson
795243730Srwatson	main_loop();
796243730Srwatson
797243730Srwatson	exit(0);
798243730Srwatson}
799