receiver.c revision 256281
1234285Sdim/*-
2234285Sdim * Copyright (c) 2012 The FreeBSD Foundation
3234285Sdim * All rights reserved.
4234285Sdim *
5234285Sdim * This software was developed by Pawel Jakub Dawidek under sponsorship from
6234285Sdim * the FreeBSD Foundation.
7234285Sdim *
8234285Sdim * Redistribution and use in source and binary forms, with or without
9234285Sdim * modification, are permitted provided that the following conditions
10234285Sdim * are met:
11234285Sdim * 1. Redistributions of source code must retain the above copyright
12234285Sdim *    notice, this list of conditions and the following disclaimer.
13234285Sdim * 2. Redistributions in binary form must reproduce the above copyright
14234285Sdim *    notice, this list of conditions and the following disclaimer in the
15234285Sdim *    documentation and/or other materials provided with the distribution.
16234285Sdim *
17234285Sdim * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
18234285Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19234285Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20234285Sdim * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
21234285Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22234285Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23234285Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24249423Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25249423Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26234285Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27234285Sdim * SUCH DAMAGE.
28234285Sdim *
29249423Sdim * $P4: //depot/projects/trustedbsd/openbsm/bin/auditdistd/receiver.c#3 $
30234285Sdim */
31234285Sdim
32234285Sdim#include <config/config.h>
33234285Sdim
34234285Sdim#include <sys/param.h>
35234285Sdim#if defined(HAVE_SYS_ENDIAN_H) && defined(HAVE_BSWAP)
36234285Sdim#include <sys/endian.h>
37234285Sdim#else /* !HAVE_SYS_ENDIAN_H || !HAVE_BSWAP */
38234285Sdim#ifdef HAVE_MACHINE_ENDIAN_H
39234285Sdim#include <machine/endian.h>
40234285Sdim#else /* !HAVE_MACHINE_ENDIAN_H */
41234285Sdim#ifdef HAVE_ENDIAN_H
42234285Sdim#include <endian.h>
43234285Sdim#else /* !HAVE_ENDIAN_H */
44234285Sdim#error "No supported endian.h"
45263508Sdim#endif /* !HAVE_ENDIAN_H */
46234285Sdim#endif /* !HAVE_MACHINE_ENDIAN_H */
47263508Sdim#include <compat/endian.h>
48263508Sdim#endif /* !HAVE_SYS_ENDIAN_H || !HAVE_BSWAP */
49263508Sdim#include <sys/queue.h>
50234285Sdim#include <sys/stat.h>
51234285Sdim#include <sys/time.h>
52234285Sdim
53239462Sdim#include <err.h>
54234285Sdim#include <errno.h>
55234285Sdim#include <fcntl.h>
56234285Sdim#ifdef HAVE_LIBUTIL_H
57234285Sdim#include <libutil.h>
58234285Sdim#endif
59234285Sdim#include <pthread.h>
60234285Sdim#include <pwd.h>
61234285Sdim#include <signal.h>
62234285Sdim#include <stdint.h>
63234285Sdim#include <stdio.h>
64234285Sdim#include <string.h>
65234285Sdim#include <sysexits.h>
66234285Sdim#include <unistd.h>
67234285Sdim
68234285Sdim#ifndef HAVE_STRLCPY
69234285Sdim#include <compat/strlcpy.h>
70234285Sdim#endif
71234285Sdim#ifndef HAVE_FSTATAT
72234285Sdim#include "fstatat.h"
73234285Sdim#endif
74234285Sdim#ifndef HAVE_OPENAT
75234285Sdim#include "openat.h"
76234285Sdim#endif
77234285Sdim#ifndef HAVE_RENAMEAT
78234285Sdim#include "renameat.h"
79234285Sdim#endif
80234285Sdim
81234285Sdim#include "auditdistd.h"
82234285Sdim#include "pjdlog.h"
83234285Sdim#include "proto.h"
84234285Sdim#include "sandbox.h"
85234285Sdim#include "subr.h"
86234285Sdim#include "synch.h"
87234285Sdim#include "trail.h"
88234285Sdim
89234285Sdimstatic struct adist_config *adcfg;
90234285Sdimstatic struct adist_host *adhost;
91234285Sdim
92234285Sdimstatic TAILQ_HEAD(, adreq) adist_free_list;
93234285Sdimstatic pthread_mutex_t adist_free_list_lock;
94234285Sdimstatic pthread_cond_t adist_free_list_cond;
95234285Sdimstatic TAILQ_HEAD(, adreq) adist_disk_list;
96234285Sdimstatic pthread_mutex_t adist_disk_list_lock;
97249423Sdimstatic pthread_cond_t adist_disk_list_cond;
98234285Sdimstatic TAILQ_HEAD(, adreq) adist_send_list;
99249423Sdimstatic pthread_mutex_t adist_send_list_lock;
100234285Sdimstatic pthread_cond_t adist_send_list_cond;
101234285Sdim
102234285Sdimstatic void
103234285Sdimadreq_clear(struct adreq *adreq)
104234285Sdim{
105234285Sdim
106234285Sdim	adreq->adr_error = -1;
107234285Sdim	adreq->adr_byteorder = ADIST_BYTEORDER_UNDEFINED;
108234285Sdim	adreq->adr_cmd = ADIST_CMD_UNDEFINED;
109234285Sdim	adreq->adr_seq = 0;
110234285Sdim	adreq->adr_datasize = 0;
111234285Sdim}
112234285Sdim
113234285Sdimstatic void
114234285Sdiminit_environment(void)
115234285Sdim{
116234285Sdim	struct adreq *adreq;
117234285Sdim	unsigned int ii;
118234285Sdim
119234285Sdim	TAILQ_INIT(&adist_free_list);
120234285Sdim	mtx_init(&adist_free_list_lock);
121234285Sdim	cv_init(&adist_free_list_cond);
122234285Sdim	TAILQ_INIT(&adist_disk_list);
123234285Sdim	mtx_init(&adist_disk_list_lock);
124234285Sdim	cv_init(&adist_disk_list_cond);
125234285Sdim	TAILQ_INIT(&adist_send_list);
126234285Sdim	mtx_init(&adist_send_list_lock);
127234285Sdim	cv_init(&adist_send_list_cond);
128234285Sdim
129234285Sdim	for (ii = 0; ii < ADIST_QUEUE_SIZE; ii++) {
130234285Sdim		adreq = malloc(sizeof(*adreq) + ADIST_BUF_SIZE);
131234285Sdim		if (adreq == NULL) {
132234285Sdim			pjdlog_exitx(EX_TEMPFAIL,
133234285Sdim			    "Unable to allocate %zu bytes of memory for adreq object.",
134234285Sdim			    sizeof(*adreq) + ADIST_BUF_SIZE);
135249423Sdim		}
136234285Sdim		adreq_clear(adreq);
137249423Sdim		TAILQ_INSERT_TAIL(&adist_free_list, adreq, adr_next);
138234285Sdim	}
139234285Sdim}
140234285Sdim
141234285Sdimstatic void
142234285Sdimadreq_decode_and_validate_header(struct adreq *adreq)
143234285Sdim{
144234285Sdim
145234285Sdim	/* Byte-swap only is the sender is using different byte order. */
146234285Sdim	if (adreq->adr_byteorder != ADIST_BYTEORDER) {
147234285Sdim		adreq->adr_byteorder = ADIST_BYTEORDER;
148234285Sdim		adreq->adr_seq = bswap64(adreq->adr_seq);
149234285Sdim		adreq->adr_datasize = bswap32(adreq->adr_datasize);
150234285Sdim	}
151234285Sdim
152234285Sdim	/* Validate packet header. */
153234285Sdim
154234285Sdim	if (adreq->adr_datasize > ADIST_BUF_SIZE) {
155234285Sdim		pjdlog_exitx(EX_PROTOCOL, "Invalid datasize received (%ju).",
156234285Sdim		    (uintmax_t)adreq->adr_datasize);
157234285Sdim	}
158234285Sdim
159234285Sdim	switch (adreq->adr_cmd) {
160234285Sdim	case ADIST_CMD_OPEN:
161234285Sdim	case ADIST_CMD_APPEND:
162234285Sdim	case ADIST_CMD_CLOSE:
163234285Sdim		if (adreq->adr_datasize == 0) {
164234285Sdim			pjdlog_exitx(EX_PROTOCOL,
165234285Sdim			    "Invalid datasize received (%ju).",
166234285Sdim			    (uintmax_t)adreq->adr_datasize);
167234285Sdim		}
168234285Sdim		break;
169234285Sdim	case ADIST_CMD_KEEPALIVE:
170234285Sdim	case ADIST_CMD_ERROR:
171234285Sdim		if (adreq->adr_datasize > 0) {
172234285Sdim			pjdlog_exitx(EX_PROTOCOL,
173234285Sdim			    "Invalid datasize received (%ju).",
174234285Sdim			    (uintmax_t)adreq->adr_datasize);
175234285Sdim		}
176234285Sdim		break;
177234285Sdim	default:
178234285Sdim		pjdlog_exitx(EX_PROTOCOL, "Invalid command received (%hhu).",
179234285Sdim		    adreq->adr_cmd);
180234285Sdim	}
181234285Sdim}
182234285Sdim
183234285Sdimstatic void
184234285Sdimadreq_validate_data(const struct adreq *adreq)
185234285Sdim{
186234285Sdim
187234285Sdim	/* Validate packet data. */
188234285Sdim
189234285Sdim	switch (adreq->adr_cmd) {
190234285Sdim	case ADIST_CMD_OPEN:
191234285Sdim	case ADIST_CMD_CLOSE:
192234285Sdim		/*
193234285Sdim		 * File name must end up with '\0' and there must be no '\0'
194234285Sdim		 * in the middle.
195234285Sdim		 */
196234285Sdim		if (adreq->adr_data[adreq->adr_datasize - 1] != '\0' ||
197234285Sdim		    strchr(adreq->adr_data, '\0') !=
198234285Sdim		    (const char *)adreq->adr_data + adreq->adr_datasize - 1) {
199234285Sdim			pjdlog_exitx(EX_PROTOCOL,
200234285Sdim			    "Invalid file name received.");
201234285Sdim		}
202234285Sdim		break;
203234285Sdim	}
204234285Sdim}
205234285Sdim
206234285Sdim/*
207234285Sdim * Thread receives requests from the sender.
208234285Sdim */
209234285Sdimstatic void *
210234285Sdimrecv_thread(void *arg __unused)
211234285Sdim{
212234285Sdim	struct adreq *adreq;
213234285Sdim
214234285Sdim	for (;;) {
215234285Sdim		pjdlog_debug(3, "recv: Taking free request.");
216234285Sdim		QUEUE_TAKE(adreq, &adist_free_list, 0);
217234285Sdim		pjdlog_debug(3, "recv: (%p) Got request.", adreq);
218234285Sdim
219234285Sdim		if (proto_recv(adhost->adh_remote, &adreq->adr_packet,
220234285Sdim		    sizeof(adreq->adr_packet)) == -1) {
221234285Sdim			pjdlog_exit(EX_TEMPFAIL,
222234285Sdim			    "Unable to receive request header");
223234285Sdim		}
224234285Sdim		adreq_decode_and_validate_header(adreq);
225234285Sdim
226234285Sdim		switch (adreq->adr_cmd) {
227234285Sdim		case ADIST_CMD_KEEPALIVE:
228234285Sdim			adreq->adr_error = 0;
229234285Sdim			adreq_log(LOG_DEBUG, 2, -1, adreq,
230234285Sdim			    "recv: (%p) Got request header: ", adreq);
231234285Sdim			pjdlog_debug(3,
232234285Sdim			    "recv: (%p) Moving request to the send queue.",
233234285Sdim			    adreq);
234234285Sdim			QUEUE_INSERT(adreq, &adist_send_list);
235234285Sdim			continue;
236234285Sdim		case ADIST_CMD_ERROR:
237234285Sdim			pjdlog_error("An error occured on the sender while reading \"%s/%s\".",
238234285Sdim			    adhost->adh_directory, adhost->adh_trail_name);
239234285Sdim			adreq_log(LOG_DEBUG, 2, ADIST_ERROR_READ, adreq,
240234285Sdim			    "recv: (%p) Got request header: ", adreq);
241234285Sdim			pjdlog_debug(3,
242234285Sdim			    "recv: (%p) Moving request to the send queue.",
243234285Sdim			    adreq);
244234285Sdim			QUEUE_INSERT(adreq, &adist_disk_list);
245234285Sdim			continue;
246234285Sdim		case ADIST_CMD_OPEN:
247234285Sdim		case ADIST_CMD_APPEND:
248234285Sdim		case ADIST_CMD_CLOSE:
249234285Sdim			if (proto_recv(adhost->adh_remote, adreq->adr_data,
250234285Sdim			    adreq->adr_datasize) == -1) {
251234285Sdim				pjdlog_exit(EX_TEMPFAIL,
252234285Sdim				    "Unable to receive request data");
253234285Sdim			}
254234285Sdim			adreq_validate_data(adreq);
255234285Sdim			adreq_log(LOG_DEBUG, 2, -1, adreq,
256234285Sdim			    "recv: (%p) Got request header: ", adreq);
257234285Sdim			pjdlog_debug(3,
258234285Sdim			    "recv: (%p) Moving request to the disk queue.",
259234285Sdim			    adreq);
260234285Sdim			QUEUE_INSERT(adreq, &adist_disk_list);
261234285Sdim			break;
262234285Sdim		default:
263234285Sdim			PJDLOG_ABORT("Invalid condition.");
264234285Sdim		}
265234285Sdim	}
266234285Sdim	/* NOTREACHED */
267234285Sdim	return (NULL);
268234285Sdim}
269234285Sdim
270234285Sdim/*
271234285Sdim * Function that opens trail file requested by the sender.
272234285Sdim * If the file already exist, it has to be the most recent file and it can
273234285Sdim * only be open for append.
274234285Sdim * If the file doesn't already exist, it has to be "older" than all existing
275234285Sdim * files.
276234285Sdim */
277234285Sdimstatic int
278234285Sdimreceiver_open(const char *filename)
279234285Sdim{
280234285Sdim	int fd;
281234285Sdim
282234285Sdim	/*
283234285Sdim	 * Previous file should be closed by now. Sending OPEN request without
284234285Sdim	 * sending CLOSE for the previous file is a sender bug.
285234285Sdim	 */
286234285Sdim	if (adhost->adh_trail_fd != -1) {
287234285Sdim		pjdlog_error("Sender requested opening file \"%s\" without first closing \"%s\".",
288234285Sdim		    filename, adhost->adh_trail_name);
289234285Sdim		return (ADIST_ERROR_WRONG_ORDER);
290234285Sdim	}
291234285Sdim
292234285Sdim	if (!trail_validate_name(filename, NULL)) {
293234285Sdim		pjdlog_error("Sender wants to open file \"%s\", which has invalid name.",
294234285Sdim		    filename);
295234285Sdim		return (ADIST_ERROR_INVALID_NAME);
296234285Sdim	}
297234285Sdim
298234285Sdim	switch (trail_name_compare(filename, adhost->adh_trail_name)) {
299234285Sdim	case TRAIL_RENAMED:
300234285Sdim		if (!trail_is_not_terminated(adhost->adh_trail_name)) {
301234285Sdim			pjdlog_error("Terminated trail \"%s/%s\" was unterminated on the sender as \"%s/%s\"?",
302234285Sdim			    adhost->adh_directory, adhost->adh_trail_name,
303234285Sdim			    adhost->adh_directory, filename);
304234285Sdim			return (ADIST_ERROR_INVALID_NAME);
305234285Sdim		}
306234285Sdim		if (renameat(adhost->adh_trail_dirfd, adhost->adh_trail_name,
307234285Sdim		    adhost->adh_trail_dirfd, filename) == -1) {
308234285Sdim			pjdlog_errno(LOG_ERR,
309234285Sdim			    "Unable to rename file \"%s/%s\" to \"%s/%s\"",
310234285Sdim			    adhost->adh_directory, adhost->adh_trail_name,
311234285Sdim			    adhost->adh_directory, filename);
312234285Sdim			PJDLOG_ASSERT(errno > 0);
313234285Sdim			return (ADIST_ERROR_RENAME);
314234285Sdim		}
315234285Sdim		pjdlog_debug(1, "Renamed file \"%s/%s\" to \"%s/%s\".",
316234285Sdim		    adhost->adh_directory, adhost->adh_trail_name,
317234285Sdim		    adhost->adh_directory, filename);
318234285Sdim		/* FALLTHROUGH */
319234285Sdim	case TRAIL_IDENTICAL:
320234285Sdim		/* Opening existing file. */
321239462Sdim		fd = openat(adhost->adh_trail_dirfd, filename,
322234285Sdim		    O_WRONLY | O_APPEND | O_NOFOLLOW);
323234285Sdim		if (fd == -1) {
324234285Sdim			pjdlog_errno(LOG_ERR,
325234285Sdim			    "Unable to open file \"%s/%s\" for append",
326234285Sdim			    adhost->adh_directory, filename);
327234285Sdim			PJDLOG_ASSERT(errno > 0);
328234285Sdim			return (ADIST_ERROR_OPEN);
329234285Sdim		}
330234285Sdim		pjdlog_debug(1, "Opened file \"%s/%s\".",
331234285Sdim		    adhost->adh_directory, filename);
332234285Sdim		break;
333234285Sdim	case TRAIL_NEWER:
334234285Sdim		/* Opening new file. */
335249423Sdim		fd = openat(adhost->adh_trail_dirfd, filename,
336234285Sdim		    O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW, 0600);
337234285Sdim		if (fd == -1) {
338234285Sdim			pjdlog_errno(LOG_ERR,
339234285Sdim			    "Unable to create file \"%s/%s\"",
340234285Sdim			    adhost->adh_directory, filename);
341234285Sdim			PJDLOG_ASSERT(errno > 0);
342234285Sdim			return (ADIST_ERROR_CREATE);
343234285Sdim		}
344249423Sdim		pjdlog_debug(1, "Created file \"%s/%s\".",
345234285Sdim		    adhost->adh_directory, filename);
346234285Sdim		break;
347234285Sdim	case TRAIL_OLDER:
348234285Sdim		/* Trying to open old file. */
349234285Sdim		pjdlog_error("Sender wants to open an old file \"%s\".", filename);
350234285Sdim		return (ADIST_ERROR_OPEN_OLD);
351234285Sdim	default:
352234285Sdim		PJDLOG_ABORT("Unknown return value from trail_name_compare().");
353234285Sdim	}
354234285Sdim	PJDLOG_VERIFY(strlcpy(adhost->adh_trail_name, filename,
355234285Sdim	    sizeof(adhost->adh_trail_name)) < sizeof(adhost->adh_trail_name));
356239462Sdim	adhost->adh_trail_fd = fd;
357234285Sdim	return (0);
358234285Sdim}
359234285Sdim
360234285Sdim/*
361234285Sdim * Function appends data to the trail file that is currently open.
362234285Sdim */
363234285Sdimstatic int
364234285Sdimreceiver_append(const unsigned char *data, size_t size)
365234285Sdim{
366234285Sdim	ssize_t done;
367234285Sdim	size_t osize;
368234285Sdim
369234285Sdim	/* We should have opened trail file. */
370234285Sdim	if (adhost->adh_trail_fd == -1) {
371234285Sdim		pjdlog_error("Sender requested append without first opening file.");
372234285Sdim		return (ADIST_ERROR_WRONG_ORDER);
373234285Sdim	}
374234285Sdim
375234285Sdim	osize = size;
376234285Sdim	while (size > 0) {
377234285Sdim		done = write(adhost->adh_trail_fd, data, size);
378234285Sdim		if (done == -1) {
379234285Sdim			if (errno == EINTR)
380234285Sdim				continue;
381234285Sdim			pjdlog_errno(LOG_ERR, "Write to \"%s/%s\" failed",
382234285Sdim			    adhost->adh_directory, adhost->adh_trail_name);
383234285Sdim			PJDLOG_ASSERT(errno > 0);
384234285Sdim			return (ADIST_ERROR_WRITE);
385234285Sdim		}
386234285Sdim		pjdlog_debug(3, "Wrote %zd bytes into \"%s/%s\".", done,
387234285Sdim		    adhost->adh_directory, adhost->adh_trail_name);
388234285Sdim		size -= done;
389234285Sdim	}
390234285Sdim	pjdlog_debug(2, "Appended %zu bytes to file \"%s/%s\".",
391234285Sdim	    osize, adhost->adh_directory, adhost->adh_trail_name);
392263508Sdim	return (0);
393263508Sdim}
394263508Sdim
395234285Sdimstatic int
396234285Sdimreceiver_close(const char *filename)
397234285Sdim{
398234285Sdim
399234285Sdim	/* We should have opened trail file. */
400234285Sdim	if (adhost->adh_trail_fd == -1) {
401234285Sdim		pjdlog_error("Sender requested closing file without first opening it.");
402234285Sdim		return (ADIST_ERROR_WRONG_ORDER);
403234285Sdim	}
404234285Sdim
405234285Sdim	/* Validate if we can do the rename. */
406234285Sdim	if (!trail_validate_name(adhost->adh_trail_name, filename)) {
407234285Sdim		pjdlog_error("Sender wants to close file \"%s\" using name \"%s\".",
408234285Sdim		    adhost->adh_trail_name, filename);
409234285Sdim		return (ADIST_ERROR_INVALID_NAME);
410234285Sdim	}
411234285Sdim
412234285Sdim	PJDLOG_VERIFY(close(adhost->adh_trail_fd) == 0);
413234285Sdim	adhost->adh_trail_fd = -1;
414234285Sdim
415234285Sdim	pjdlog_debug(1, "Closed file \"%s/%s\".", adhost->adh_directory,
416234285Sdim	    adhost->adh_trail_name);
417234285Sdim
418234285Sdim	if (strcmp(adhost->adh_trail_name, filename) == 0) {
419234285Sdim		/* File name didn't change, we are done here. */
420234285Sdim		return (0);
421234285Sdim	}
422234285Sdim
423234285Sdim	if (renameat(adhost->adh_trail_dirfd, adhost->adh_trail_name,
424234285Sdim	    adhost->adh_trail_dirfd, filename) == -1) {
425234285Sdim		pjdlog_errno(LOG_ERR, "Unable to rename \"%s\" to \"%s\"",
426234285Sdim		    adhost->adh_trail_name, filename);
427234285Sdim		PJDLOG_ASSERT(errno > 0);
428234285Sdim		return (ADIST_ERROR_RENAME);
429234285Sdim	}
430234285Sdim	pjdlog_debug(1, "Renamed file \"%s/%s\" to \"%s/%s\".",
431234285Sdim	    adhost->adh_directory, adhost->adh_trail_name,
432234285Sdim	    adhost->adh_directory, filename);
433234285Sdim	PJDLOG_VERIFY(strlcpy(adhost->adh_trail_name, filename,
434234285Sdim	    sizeof(adhost->adh_trail_name)) < sizeof(adhost->adh_trail_name));
435234285Sdim
436234285Sdim	return (0);
437234285Sdim}
438234285Sdim
439234285Sdimstatic int
440234285Sdimreceiver_error(void)
441234285Sdim{
442234285Sdim
443234285Sdim	/* We should have opened trail file. */
444234285Sdim	if (adhost->adh_trail_fd == -1) {
445234285Sdim		pjdlog_error("Sender send read error, but file is not open.");
446234285Sdim		return (ADIST_ERROR_WRONG_ORDER);
447234285Sdim	}
448234285Sdim
449234285Sdim	PJDLOG_VERIFY(close(adhost->adh_trail_fd) == 0);
450234285Sdim	adhost->adh_trail_fd = -1;
451263508Sdim
452234285Sdim	pjdlog_debug(1, "Closed file \"%s/%s\".", adhost->adh_directory,
453234285Sdim	    adhost->adh_trail_name);
454234285Sdim
455234285Sdim	return (0);
456234285Sdim}
457234285Sdim
458234285Sdimstatic void *
459263508Sdimdisk_thread(void *arg __unused)
460234285Sdim{
461234285Sdim	struct adreq *adreq;
462234285Sdim
463263508Sdim	for (;;) {
464234285Sdim		pjdlog_debug(3, "disk: Taking request.");
465234285Sdim		QUEUE_TAKE(adreq, &adist_disk_list, 0);
466234285Sdim		adreq_log(LOG_DEBUG, 3, -1, adreq, "disk: (%p) Got request: ",
467234285Sdim		    adreq);
468234285Sdim		/* Handle the actual request. */
469234285Sdim		switch (adreq->adr_cmd) {
470234285Sdim		case ADIST_CMD_OPEN:
471234285Sdim			adreq->adr_error = receiver_open(adreq->adr_data);
472234285Sdim			break;
473234285Sdim		case ADIST_CMD_APPEND:
474234285Sdim			adreq->adr_error = receiver_append(adreq->adr_data,
475234285Sdim			    adreq->adr_datasize);
476234285Sdim			break;
477234285Sdim		case ADIST_CMD_CLOSE:
478234285Sdim			adreq->adr_error = receiver_close(adreq->adr_data);
479234285Sdim			break;
480234285Sdim		case ADIST_CMD_ERROR:
481234285Sdim			adreq->adr_error = receiver_error();
482234285Sdim			break;
483234285Sdim		default:
484234285Sdim			PJDLOG_ABORT("Unexpected command (cmd=%hhu).",
485234285Sdim			    adreq->adr_cmd);
486234285Sdim		}
487249423Sdim		if (adreq->adr_error != 0) {
488234285Sdim			adreq_log(LOG_ERR, 0, adreq->adr_error, adreq,
489234285Sdim			    "Request failed: ");
490234285Sdim		}
491234285Sdim		pjdlog_debug(3, "disk: (%p) Moving request to the send queue.",
492234285Sdim		    adreq);
493234285Sdim		QUEUE_INSERT(adreq, &adist_send_list);
494234285Sdim	}
495234285Sdim	/* NOTREACHED */
496234285Sdim	return (NULL);
497234285Sdim}
498249423Sdim
499234285Sdim/*
500234285Sdim * Thread sends requests back to primary node.
501234285Sdim */
502234285Sdimstatic void *
503234285Sdimsend_thread(void *arg __unused)
504234285Sdim{
505234285Sdim	struct adreq *adreq;
506234285Sdim	struct adrep adrep;
507234285Sdim
508234285Sdim	for (;;) {
509234285Sdim		pjdlog_debug(3, "send: Taking request.");
510234285Sdim		QUEUE_TAKE(adreq, &adist_send_list, 0);
511234285Sdim		adreq_log(LOG_DEBUG, 3, -1, adreq, "send: (%p) Got request: ",
512234285Sdim		    adreq);
513234285Sdim		adrep.adrp_byteorder = ADIST_BYTEORDER;
514234285Sdim		adrep.adrp_seq = adreq->adr_seq;
515234285Sdim		adrep.adrp_error = adreq->adr_error;
516234285Sdim		if (proto_send(adhost->adh_remote, &adrep,
517234285Sdim		    sizeof(adrep)) == -1) {
518234285Sdim			pjdlog_exit(EX_TEMPFAIL, "Unable to send reply");
519234285Sdim		}
520234285Sdim		pjdlog_debug(3, "send: (%p) Moving request to the free queue.",
521234285Sdim		    adreq);
522234285Sdim		adreq_clear(adreq);
523234285Sdim		QUEUE_INSERT(adreq, &adist_free_list);
524234285Sdim	}
525234285Sdim	/* NOTREACHED */
526234285Sdim	return (NULL);
527234285Sdim}
528234285Sdim
529234285Sdimstatic void
530234285Sdimreceiver_directory_create(void)
531234285Sdim{
532234285Sdim	struct passwd *pw;
533234285Sdim
534234285Sdim	/*
535234285Sdim	 * According to getpwnam(3) we have to clear errno before calling the
536234285Sdim	 * function to be able to distinguish between an error and missing
537234285Sdim	 * entry (with is not treated as error by getpwnam(3)).
538234285Sdim	 */
539234285Sdim	errno = 0;
540234285Sdim	pw = getpwnam(ADIST_USER);
541234285Sdim	if (pw == NULL) {
542234285Sdim		if (errno != 0) {
543234285Sdim			pjdlog_exit(EX_NOUSER,
544234285Sdim			    "Unable to find info about '%s' user", ADIST_USER);
545234285Sdim		} else {
546234285Sdim			pjdlog_exitx(EX_NOUSER, "User '%s' doesn't exist.",
547234285Sdim			    ADIST_USER);
548234285Sdim		}
549234285Sdim	}
550234285Sdim
551234285Sdim	if (mkdir(adhost->adh_directory, 0700) == -1) {
552234285Sdim		pjdlog_exit(EX_OSFILE, "Unable to create directory \"%s\"",
553234285Sdim		    adhost->adh_directory);
554234285Sdim	}
555234285Sdim	if (chown(adhost->adh_directory, pw->pw_uid, pw->pw_gid) == -1) {
556234285Sdim		pjdlog_errno(LOG_ERR,
557234285Sdim		    "Unable to change owner of the directory \"%s\"",
558234285Sdim		    adhost->adh_directory);
559234285Sdim		(void)rmdir(adhost->adh_directory);
560234285Sdim		exit(EX_OSFILE);
561234285Sdim	}
562234285Sdim}
563234285Sdim
564234285Sdimstatic void
565234285Sdimreceiver_directory_open(void)
566234285Sdim{
567234285Sdim
568234285Sdim#ifdef HAVE_FDOPENDIR
569234285Sdim	adhost->adh_trail_dirfd = open(adhost->adh_directory,
570234285Sdim	    O_RDONLY | O_DIRECTORY);
571234285Sdim	if (adhost->adh_trail_dirfd == -1) {
572234285Sdim		if (errno == ENOENT) {
573234285Sdim			receiver_directory_create();
574234285Sdim			adhost->adh_trail_dirfd = open(adhost->adh_directory,
575234285Sdim			    O_RDONLY | O_DIRECTORY);
576234285Sdim		}
577234285Sdim		if (adhost->adh_trail_dirfd == -1) {
578234285Sdim			pjdlog_exit(EX_CONFIG,
579234285Sdim			    "Unable to open directory \"%s\"",
580234285Sdim			    adhost->adh_directory);
581234285Sdim		}
582234285Sdim	}
583234285Sdim	adhost->adh_trail_dirfp = fdopendir(adhost->adh_trail_dirfd);
584234285Sdim	if (adhost->adh_trail_dirfp == NULL) {
585234285Sdim		pjdlog_exit(EX_CONFIG, "Unable to fdopen directory \"%s\"",
586234285Sdim		    adhost->adh_directory);
587234285Sdim	}
588234285Sdim#else
589234285Sdim	struct stat sb;
590234285Sdim
591234285Sdim	if (stat(adhost->adh_directory, &sb) == -1) {
592234285Sdim		if (errno == ENOENT) {
593234285Sdim			receiver_directory_create();
594234285Sdim		} else {
595234285Sdim			pjdlog_exit(EX_CONFIG,
596234285Sdim			    "Unable to stat directory \"%s\"",
597234285Sdim			    adhost->adh_directory);
598234285Sdim		}
599234285Sdim	}
600234285Sdim	adhost->adh_trail_dirfp = opendir(adhost->adh_directory);
601234285Sdim	if (adhost->adh_trail_dirfp == NULL) {
602234285Sdim		pjdlog_exit(EX_CONFIG, "Unable to open directory \"%s\"",
603234285Sdim		    adhost->adh_directory);
604234285Sdim	}
605234285Sdim	adhost->adh_trail_dirfd = dirfd(adhost->adh_trail_dirfp);
606249423Sdim#endif
607234285Sdim}
608234285Sdim
609234285Sdimstatic void
610234285Sdimreceiver_connect(void)
611234285Sdim{
612234285Sdim	uint64_t trail_size;
613234285Sdim	struct stat sb;
614234285Sdim
615234285Sdim	PJDLOG_ASSERT(adhost->adh_trail_dirfp != NULL);
616234285Sdim
617234285Sdim	trail_last(adhost->adh_trail_dirfp, adhost->adh_trail_name,
618234285Sdim	    sizeof(adhost->adh_trail_name));
619234285Sdim
620234285Sdim	if (adhost->adh_trail_name[0] == '\0') {
621234285Sdim		trail_size = 0;
622234285Sdim	} else {
623234285Sdim		if (fstatat(adhost->adh_trail_dirfd, adhost->adh_trail_name,
624234285Sdim		    &sb, AT_SYMLINK_NOFOLLOW) == -1) {
625234285Sdim			pjdlog_exit(EX_CONFIG, "Unable to stat \"%s/%s\"",
626234285Sdim			    adhost->adh_directory, adhost->adh_trail_name);
627234285Sdim		}
628234285Sdim		if (!S_ISREG(sb.st_mode)) {
629234285Sdim			pjdlog_exitx(EX_CONFIG,
630234285Sdim			    "File \"%s/%s\" is not a regular file.",
631234285Sdim			    adhost->adh_directory, adhost->adh_trail_name);
632234285Sdim		}
633234285Sdim		trail_size = sb.st_size;
634234285Sdim	}
635234285Sdim	trail_size = htole64(trail_size);
636234285Sdim	if (proto_send(adhost->adh_remote, &trail_size,
637234285Sdim	    sizeof(trail_size)) == -1) {
638234285Sdim		pjdlog_exit(EX_TEMPFAIL,
639234285Sdim		    "Unable to send size of the most recent trail file");
640234285Sdim	}
641234285Sdim	if (proto_send(adhost->adh_remote, adhost->adh_trail_name,
642234285Sdim	    sizeof(adhost->adh_trail_name)) == -1) {
643234285Sdim		pjdlog_exit(EX_TEMPFAIL,
644234285Sdim		    "Unable to send name of the most recent trail file");
645234285Sdim	}
646234285Sdim}
647234285Sdim
648234285Sdimvoid
649234285Sdimadist_receiver(struct adist_config *config, struct adist_host *adh)
650234285Sdim{
651234285Sdim	sigset_t mask;
652234285Sdim	pthread_t td;
653234285Sdim	pid_t pid;
654234285Sdim	int error, mode, debuglevel;
655
656	pid = fork();
657	if (pid == -1) {
658		pjdlog_errno(LOG_ERR, "Unable to fork");
659		proto_close(adh->adh_remote);
660		adh->adh_remote = NULL;
661		return;
662	}
663
664	if (pid > 0) {
665		/* This is parent. */
666		proto_close(adh->adh_remote);
667		adh->adh_remote = NULL;
668		adh->adh_worker_pid = pid;
669		return;
670	}
671
672	adcfg = config;
673	adhost = adh;
674	mode = pjdlog_mode_get();
675	debuglevel = pjdlog_debug_get();
676
677	descriptors_cleanup(adhost);
678
679//	descriptors_assert(adhost, mode);
680
681	pjdlog_init(mode);
682	pjdlog_debug_set(debuglevel);
683	pjdlog_prefix_set("[%s] (%s) ", adhost->adh_name,
684	    role2str(adhost->adh_role));
685#ifdef HAVE_SETPROCTITLE
686	setproctitle("%s (%s)", adhost->adh_name, role2str(adhost->adh_role));
687#endif
688
689	PJDLOG_VERIFY(sigemptyset(&mask) == 0);
690	PJDLOG_VERIFY(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
691
692	/* Error in setting timeout is not critical, but why should it fail? */
693	if (proto_timeout(adhost->adh_remote, adcfg->adc_timeout) == -1)
694		pjdlog_errno(LOG_WARNING, "Unable to set connection timeout");
695
696	init_environment();
697
698	adhost->adh_trail_fd = -1;
699	receiver_directory_open();
700
701	if (sandbox(ADIST_USER, true, "auditdistd: %s (%s)",
702	    role2str(adhost->adh_role), adhost->adh_name) != 0) {
703		exit(EX_CONFIG);
704	}
705	pjdlog_info("Privileges successfully dropped.");
706
707	receiver_connect();
708
709	error = pthread_create(&td, NULL, recv_thread, adhost);
710	PJDLOG_ASSERT(error == 0);
711	error = pthread_create(&td, NULL, disk_thread, adhost);
712	PJDLOG_ASSERT(error == 0);
713	(void)send_thread(adhost);
714}
715