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