xref: /freebsd/contrib/openbsm/bin/auditdistd/receiver.c (revision c6879c6c14eedbd060ba588a3129a6c60ebbe783)
1aa772005SRobert Watson /*-
2aa772005SRobert Watson  * Copyright (c) 2012 The FreeBSD Foundation
3aa772005SRobert Watson  * All rights reserved.
4aa772005SRobert Watson  *
5aa772005SRobert Watson  * This software was developed by Pawel Jakub Dawidek under sponsorship from
6aa772005SRobert Watson  * the FreeBSD Foundation.
7aa772005SRobert Watson  *
8aa772005SRobert Watson  * Redistribution and use in source and binary forms, with or without
9aa772005SRobert Watson  * modification, are permitted provided that the following conditions
10aa772005SRobert Watson  * are met:
11aa772005SRobert Watson  * 1. Redistributions of source code must retain the above copyright
12aa772005SRobert Watson  *    notice, this list of conditions and the following disclaimer.
13aa772005SRobert Watson  * 2. Redistributions in binary form must reproduce the above copyright
14aa772005SRobert Watson  *    notice, this list of conditions and the following disclaimer in the
15aa772005SRobert Watson  *    documentation and/or other materials provided with the distribution.
16aa772005SRobert Watson  *
17aa772005SRobert Watson  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
18aa772005SRobert Watson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19aa772005SRobert Watson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20aa772005SRobert Watson  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
21aa772005SRobert Watson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22aa772005SRobert Watson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23aa772005SRobert Watson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24aa772005SRobert Watson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25aa772005SRobert Watson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26aa772005SRobert Watson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27aa772005SRobert Watson  * SUCH DAMAGE.
28aa772005SRobert Watson  */
29aa772005SRobert Watson 
30aa772005SRobert Watson #include <config/config.h>
31aa772005SRobert Watson 
32aa772005SRobert Watson #include <sys/param.h>
33aa772005SRobert Watson #if defined(HAVE_SYS_ENDIAN_H) && defined(HAVE_BSWAP)
34aa772005SRobert Watson #include <sys/endian.h>
35aa772005SRobert Watson #else /* !HAVE_SYS_ENDIAN_H || !HAVE_BSWAP */
36aa772005SRobert Watson #ifdef HAVE_MACHINE_ENDIAN_H
37aa772005SRobert Watson #include <machine/endian.h>
38aa772005SRobert Watson #else /* !HAVE_MACHINE_ENDIAN_H */
39aa772005SRobert Watson #ifdef HAVE_ENDIAN_H
40aa772005SRobert Watson #include <endian.h>
41aa772005SRobert Watson #else /* !HAVE_ENDIAN_H */
42aa772005SRobert Watson #error "No supported endian.h"
43aa772005SRobert Watson #endif /* !HAVE_ENDIAN_H */
44aa772005SRobert Watson #endif /* !HAVE_MACHINE_ENDIAN_H */
45aa772005SRobert Watson #include <compat/endian.h>
46aa772005SRobert Watson #endif /* !HAVE_SYS_ENDIAN_H || !HAVE_BSWAP */
47aa772005SRobert Watson #include <sys/queue.h>
48aa772005SRobert Watson #include <sys/stat.h>
49aa772005SRobert Watson #include <sys/time.h>
50aa772005SRobert Watson 
51aa772005SRobert Watson #include <err.h>
52aa772005SRobert Watson #include <errno.h>
53aa772005SRobert Watson #include <fcntl.h>
54aa772005SRobert Watson #ifdef HAVE_LIBUTIL_H
55aa772005SRobert Watson #include <libutil.h>
56aa772005SRobert Watson #endif
57aa772005SRobert Watson #include <pthread.h>
58aa772005SRobert Watson #include <pwd.h>
59aa772005SRobert Watson #include <signal.h>
60aa772005SRobert Watson #include <stdint.h>
61aa772005SRobert Watson #include <stdio.h>
62aa772005SRobert Watson #include <string.h>
63aa772005SRobert Watson #include <sysexits.h>
64aa772005SRobert Watson #include <unistd.h>
65aa772005SRobert Watson 
66aa772005SRobert Watson #ifndef HAVE_STRLCPY
67aa772005SRobert Watson #include <compat/strlcpy.h>
68aa772005SRobert Watson #endif
69aa772005SRobert Watson #ifndef HAVE_FSTATAT
70aa772005SRobert Watson #include "fstatat.h"
71aa772005SRobert Watson #endif
72aa772005SRobert Watson #ifndef HAVE_OPENAT
73aa772005SRobert Watson #include "openat.h"
74aa772005SRobert Watson #endif
75aa772005SRobert Watson #ifndef HAVE_RENAMEAT
76aa772005SRobert Watson #include "renameat.h"
77aa772005SRobert Watson #endif
78aa772005SRobert Watson 
79aa772005SRobert Watson #include "auditdistd.h"
80aa772005SRobert Watson #include "pjdlog.h"
81aa772005SRobert Watson #include "proto.h"
82aa772005SRobert Watson #include "sandbox.h"
83aa772005SRobert Watson #include "subr.h"
84aa772005SRobert Watson #include "synch.h"
85aa772005SRobert Watson #include "trail.h"
86aa772005SRobert Watson 
87aa772005SRobert Watson static struct adist_config *adcfg;
88aa772005SRobert Watson static struct adist_host *adhost;
89aa772005SRobert Watson 
90aa772005SRobert Watson static TAILQ_HEAD(, adreq) adist_free_list;
91aa772005SRobert Watson static pthread_mutex_t adist_free_list_lock;
92aa772005SRobert Watson static pthread_cond_t adist_free_list_cond;
93aa772005SRobert Watson static TAILQ_HEAD(, adreq) adist_disk_list;
94aa772005SRobert Watson static pthread_mutex_t adist_disk_list_lock;
95aa772005SRobert Watson static pthread_cond_t adist_disk_list_cond;
96aa772005SRobert Watson static TAILQ_HEAD(, adreq) adist_send_list;
97aa772005SRobert Watson static pthread_mutex_t adist_send_list_lock;
98aa772005SRobert Watson static pthread_cond_t adist_send_list_cond;
99aa772005SRobert Watson 
100aa772005SRobert Watson static void
adreq_clear(struct adreq * adreq)101aa772005SRobert Watson adreq_clear(struct adreq *adreq)
102aa772005SRobert Watson {
103aa772005SRobert Watson 
104aa772005SRobert Watson 	adreq->adr_error = -1;
105aa772005SRobert Watson 	adreq->adr_byteorder = ADIST_BYTEORDER_UNDEFINED;
106aa772005SRobert Watson 	adreq->adr_cmd = ADIST_CMD_UNDEFINED;
107aa772005SRobert Watson 	adreq->adr_seq = 0;
108aa772005SRobert Watson 	adreq->adr_datasize = 0;
109aa772005SRobert Watson }
110aa772005SRobert Watson 
111aa772005SRobert Watson static void
init_environment(void)112aa772005SRobert Watson init_environment(void)
113aa772005SRobert Watson {
114aa772005SRobert Watson 	struct adreq *adreq;
115aa772005SRobert Watson 	unsigned int ii;
116aa772005SRobert Watson 
117aa772005SRobert Watson 	TAILQ_INIT(&adist_free_list);
118aa772005SRobert Watson 	mtx_init(&adist_free_list_lock);
119aa772005SRobert Watson 	cv_init(&adist_free_list_cond);
120aa772005SRobert Watson 	TAILQ_INIT(&adist_disk_list);
121aa772005SRobert Watson 	mtx_init(&adist_disk_list_lock);
122aa772005SRobert Watson 	cv_init(&adist_disk_list_cond);
123aa772005SRobert Watson 	TAILQ_INIT(&adist_send_list);
124aa772005SRobert Watson 	mtx_init(&adist_send_list_lock);
125aa772005SRobert Watson 	cv_init(&adist_send_list_cond);
126aa772005SRobert Watson 
127aa772005SRobert Watson 	for (ii = 0; ii < ADIST_QUEUE_SIZE; ii++) {
128aa772005SRobert Watson 		adreq = malloc(sizeof(*adreq) + ADIST_BUF_SIZE);
129aa772005SRobert Watson 		if (adreq == NULL) {
130aa772005SRobert Watson 			pjdlog_exitx(EX_TEMPFAIL,
131aa772005SRobert Watson 			    "Unable to allocate %zu bytes of memory for adreq object.",
132aa772005SRobert Watson 			    sizeof(*adreq) + ADIST_BUF_SIZE);
133aa772005SRobert Watson 		}
134aa772005SRobert Watson 		adreq_clear(adreq);
135aa772005SRobert Watson 		TAILQ_INSERT_TAIL(&adist_free_list, adreq, adr_next);
136aa772005SRobert Watson 	}
137aa772005SRobert Watson }
138aa772005SRobert Watson 
139aa772005SRobert Watson static void
adreq_decode_and_validate_header(struct adreq * adreq)140aa772005SRobert Watson adreq_decode_and_validate_header(struct adreq *adreq)
141aa772005SRobert Watson {
142aa772005SRobert Watson 
143*58554c8dSPawel Jakub Dawidek 	/* Byte-swap only if the sender is using different byte order. */
144aa772005SRobert Watson 	if (adreq->adr_byteorder != ADIST_BYTEORDER) {
145aa772005SRobert Watson 		adreq->adr_byteorder = ADIST_BYTEORDER;
146aa772005SRobert Watson 		adreq->adr_seq = bswap64(adreq->adr_seq);
147aa772005SRobert Watson 		adreq->adr_datasize = bswap32(adreq->adr_datasize);
148aa772005SRobert Watson 	}
149aa772005SRobert Watson 
150aa772005SRobert Watson 	/* Validate packet header. */
151aa772005SRobert Watson 
152aa772005SRobert Watson 	if (adreq->adr_datasize > ADIST_BUF_SIZE) {
153aa772005SRobert Watson 		pjdlog_exitx(EX_PROTOCOL, "Invalid datasize received (%ju).",
154aa772005SRobert Watson 		    (uintmax_t)adreq->adr_datasize);
155aa772005SRobert Watson 	}
156aa772005SRobert Watson 
157aa772005SRobert Watson 	switch (adreq->adr_cmd) {
158aa772005SRobert Watson 	case ADIST_CMD_OPEN:
159aa772005SRobert Watson 	case ADIST_CMD_APPEND:
160aa772005SRobert Watson 	case ADIST_CMD_CLOSE:
161aa772005SRobert Watson 		if (adreq->adr_datasize == 0) {
162aa772005SRobert Watson 			pjdlog_exitx(EX_PROTOCOL,
163aa772005SRobert Watson 			    "Invalid datasize received (%ju).",
164aa772005SRobert Watson 			    (uintmax_t)adreq->adr_datasize);
165aa772005SRobert Watson 		}
166aa772005SRobert Watson 		break;
167aa772005SRobert Watson 	case ADIST_CMD_KEEPALIVE:
168aa772005SRobert Watson 	case ADIST_CMD_ERROR:
169aa772005SRobert Watson 		if (adreq->adr_datasize > 0) {
170aa772005SRobert Watson 			pjdlog_exitx(EX_PROTOCOL,
171aa772005SRobert Watson 			    "Invalid datasize received (%ju).",
172aa772005SRobert Watson 			    (uintmax_t)adreq->adr_datasize);
173aa772005SRobert Watson 		}
174aa772005SRobert Watson 		break;
175aa772005SRobert Watson 	default:
176aa772005SRobert Watson 		pjdlog_exitx(EX_PROTOCOL, "Invalid command received (%hhu).",
177aa772005SRobert Watson 		    adreq->adr_cmd);
178aa772005SRobert Watson 	}
179aa772005SRobert Watson }
180aa772005SRobert Watson 
181aa772005SRobert Watson static void
adreq_validate_data(const struct adreq * adreq)182aa772005SRobert Watson adreq_validate_data(const struct adreq *adreq)
183aa772005SRobert Watson {
184aa772005SRobert Watson 
185aa772005SRobert Watson 	/* Validate packet data. */
186aa772005SRobert Watson 
187aa772005SRobert Watson 	switch (adreq->adr_cmd) {
188aa772005SRobert Watson 	case ADIST_CMD_OPEN:
189aa772005SRobert Watson 	case ADIST_CMD_CLOSE:
190aa772005SRobert Watson 		/*
191aa772005SRobert Watson 		 * File name must end up with '\0' and there must be no '\0'
192aa772005SRobert Watson 		 * in the middle.
193aa772005SRobert Watson 		 */
194aa772005SRobert Watson 		if (adreq->adr_data[adreq->adr_datasize - 1] != '\0' ||
195aa772005SRobert Watson 		    strchr(adreq->adr_data, '\0') !=
196aa772005SRobert Watson 		    (const char *)adreq->adr_data + adreq->adr_datasize - 1) {
197aa772005SRobert Watson 			pjdlog_exitx(EX_PROTOCOL,
198aa772005SRobert Watson 			    "Invalid file name received.");
199aa772005SRobert Watson 		}
200aa772005SRobert Watson 		break;
201aa772005SRobert Watson 	}
202aa772005SRobert Watson }
203aa772005SRobert Watson 
204aa772005SRobert Watson /*
205aa772005SRobert Watson  * Thread receives requests from the sender.
206aa772005SRobert Watson  */
207aa772005SRobert Watson static void *
recv_thread(void * arg __unused)208aa772005SRobert Watson recv_thread(void *arg __unused)
209aa772005SRobert Watson {
210aa772005SRobert Watson 	struct adreq *adreq;
211aa772005SRobert Watson 
212aa772005SRobert Watson 	for (;;) {
213aa772005SRobert Watson 		pjdlog_debug(3, "recv: Taking free request.");
214aa772005SRobert Watson 		QUEUE_TAKE(adreq, &adist_free_list, 0);
215aa772005SRobert Watson 		pjdlog_debug(3, "recv: (%p) Got request.", adreq);
216aa772005SRobert Watson 
217aa772005SRobert Watson 		if (proto_recv(adhost->adh_remote, &adreq->adr_packet,
218aa772005SRobert Watson 		    sizeof(adreq->adr_packet)) == -1) {
219aa772005SRobert Watson 			pjdlog_exit(EX_TEMPFAIL,
220aa772005SRobert Watson 			    "Unable to receive request header");
221aa772005SRobert Watson 		}
222aa772005SRobert Watson 		adreq_decode_and_validate_header(adreq);
223aa772005SRobert Watson 
224aa772005SRobert Watson 		switch (adreq->adr_cmd) {
225aa772005SRobert Watson 		case ADIST_CMD_KEEPALIVE:
226aa772005SRobert Watson 			adreq->adr_error = 0;
227aa772005SRobert Watson 			adreq_log(LOG_DEBUG, 2, -1, adreq,
228aa772005SRobert Watson 			    "recv: (%p) Got request header: ", adreq);
229aa772005SRobert Watson 			pjdlog_debug(3,
230aa772005SRobert Watson 			    "recv: (%p) Moving request to the send queue.",
231aa772005SRobert Watson 			    adreq);
232aa772005SRobert Watson 			QUEUE_INSERT(adreq, &adist_send_list);
233aa772005SRobert Watson 			continue;
234aa772005SRobert Watson 		case ADIST_CMD_ERROR:
235aa772005SRobert Watson 			pjdlog_error("An error occured on the sender while reading \"%s/%s\".",
236aa772005SRobert Watson 			    adhost->adh_directory, adhost->adh_trail_name);
237aa772005SRobert Watson 			adreq_log(LOG_DEBUG, 2, ADIST_ERROR_READ, adreq,
238aa772005SRobert Watson 			    "recv: (%p) Got request header: ", adreq);
239aa772005SRobert Watson 			pjdlog_debug(3,
240aa772005SRobert Watson 			    "recv: (%p) Moving request to the send queue.",
241aa772005SRobert Watson 			    adreq);
242aa772005SRobert Watson 			QUEUE_INSERT(adreq, &adist_disk_list);
243aa772005SRobert Watson 			continue;
244aa772005SRobert Watson 		case ADIST_CMD_OPEN:
245aa772005SRobert Watson 		case ADIST_CMD_APPEND:
246aa772005SRobert Watson 		case ADIST_CMD_CLOSE:
247aa772005SRobert Watson 			if (proto_recv(adhost->adh_remote, adreq->adr_data,
248aa772005SRobert Watson 			    adreq->adr_datasize) == -1) {
249aa772005SRobert Watson 				pjdlog_exit(EX_TEMPFAIL,
250aa772005SRobert Watson 				    "Unable to receive request data");
251aa772005SRobert Watson 			}
252aa772005SRobert Watson 			adreq_validate_data(adreq);
253aa772005SRobert Watson 			adreq_log(LOG_DEBUG, 2, -1, adreq,
254aa772005SRobert Watson 			    "recv: (%p) Got request header: ", adreq);
255aa772005SRobert Watson 			pjdlog_debug(3,
256aa772005SRobert Watson 			    "recv: (%p) Moving request to the disk queue.",
257aa772005SRobert Watson 			    adreq);
258aa772005SRobert Watson 			QUEUE_INSERT(adreq, &adist_disk_list);
259aa772005SRobert Watson 			break;
260aa772005SRobert Watson 		default:
261aa772005SRobert Watson 			PJDLOG_ABORT("Invalid condition.");
262aa772005SRobert Watson 		}
263aa772005SRobert Watson 	}
264aa772005SRobert Watson 	/* NOTREACHED */
265aa772005SRobert Watson 	return (NULL);
266aa772005SRobert Watson }
267aa772005SRobert Watson 
268aa772005SRobert Watson /*
269aa772005SRobert Watson  * Function that opens trail file requested by the sender.
270aa772005SRobert Watson  * If the file already exist, it has to be the most recent file and it can
271aa772005SRobert Watson  * only be open for append.
272aa772005SRobert Watson  * If the file doesn't already exist, it has to be "older" than all existing
273aa772005SRobert Watson  * files.
274aa772005SRobert Watson  */
275aa772005SRobert Watson static int
receiver_open(const char * filename)276aa772005SRobert Watson receiver_open(const char *filename)
277aa772005SRobert Watson {
278aa772005SRobert Watson 	int fd;
279aa772005SRobert Watson 
280aa772005SRobert Watson 	/*
281aa772005SRobert Watson 	 * Previous file should be closed by now. Sending OPEN request without
282aa772005SRobert Watson 	 * sending CLOSE for the previous file is a sender bug.
283aa772005SRobert Watson 	 */
284aa772005SRobert Watson 	if (adhost->adh_trail_fd != -1) {
285aa772005SRobert Watson 		pjdlog_error("Sender requested opening file \"%s\" without first closing \"%s\".",
286aa772005SRobert Watson 		    filename, adhost->adh_trail_name);
287aa772005SRobert Watson 		return (ADIST_ERROR_WRONG_ORDER);
288aa772005SRobert Watson 	}
289aa772005SRobert Watson 
290aa772005SRobert Watson 	if (!trail_validate_name(filename, NULL)) {
291aa772005SRobert Watson 		pjdlog_error("Sender wants to open file \"%s\", which has invalid name.",
292aa772005SRobert Watson 		    filename);
293aa772005SRobert Watson 		return (ADIST_ERROR_INVALID_NAME);
294aa772005SRobert Watson 	}
295aa772005SRobert Watson 
296aa772005SRobert Watson 	switch (trail_name_compare(filename, adhost->adh_trail_name)) {
297aa772005SRobert Watson 	case TRAIL_RENAMED:
298aa772005SRobert Watson 		if (!trail_is_not_terminated(adhost->adh_trail_name)) {
299aa772005SRobert Watson 			pjdlog_error("Terminated trail \"%s/%s\" was unterminated on the sender as \"%s/%s\"?",
300aa772005SRobert Watson 			    adhost->adh_directory, adhost->adh_trail_name,
301aa772005SRobert Watson 			    adhost->adh_directory, filename);
302aa772005SRobert Watson 			return (ADIST_ERROR_INVALID_NAME);
303aa772005SRobert Watson 		}
304aa772005SRobert Watson 		if (renameat(adhost->adh_trail_dirfd, adhost->adh_trail_name,
305aa772005SRobert Watson 		    adhost->adh_trail_dirfd, filename) == -1) {
306aa772005SRobert Watson 			pjdlog_errno(LOG_ERR,
307aa772005SRobert Watson 			    "Unable to rename file \"%s/%s\" to \"%s/%s\"",
308aa772005SRobert Watson 			    adhost->adh_directory, adhost->adh_trail_name,
309aa772005SRobert Watson 			    adhost->adh_directory, filename);
310aa772005SRobert Watson 			PJDLOG_ASSERT(errno > 0);
311aa772005SRobert Watson 			return (ADIST_ERROR_RENAME);
312aa772005SRobert Watson 		}
313aa772005SRobert Watson 		pjdlog_debug(1, "Renamed file \"%s/%s\" to \"%s/%s\".",
314aa772005SRobert Watson 		    adhost->adh_directory, adhost->adh_trail_name,
315aa772005SRobert Watson 		    adhost->adh_directory, filename);
316aa772005SRobert Watson 		/* FALLTHROUGH */
317aa772005SRobert Watson 	case TRAIL_IDENTICAL:
318aa772005SRobert Watson 		/* Opening existing file. */
319aa772005SRobert Watson 		fd = openat(adhost->adh_trail_dirfd, filename,
320aa772005SRobert Watson 		    O_WRONLY | O_APPEND | O_NOFOLLOW);
321aa772005SRobert Watson 		if (fd == -1) {
322aa772005SRobert Watson 			pjdlog_errno(LOG_ERR,
323aa772005SRobert Watson 			    "Unable to open file \"%s/%s\" for append",
324aa772005SRobert Watson 			    adhost->adh_directory, filename);
325aa772005SRobert Watson 			PJDLOG_ASSERT(errno > 0);
326aa772005SRobert Watson 			return (ADIST_ERROR_OPEN);
327aa772005SRobert Watson 		}
328aa772005SRobert Watson 		pjdlog_debug(1, "Opened file \"%s/%s\".",
329aa772005SRobert Watson 		    adhost->adh_directory, filename);
330aa772005SRobert Watson 		break;
331aa772005SRobert Watson 	case TRAIL_NEWER:
332aa772005SRobert Watson 		/* Opening new file. */
333aa772005SRobert Watson 		fd = openat(adhost->adh_trail_dirfd, filename,
334aa772005SRobert Watson 		    O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW, 0600);
335aa772005SRobert Watson 		if (fd == -1) {
336aa772005SRobert Watson 			pjdlog_errno(LOG_ERR,
337aa772005SRobert Watson 			    "Unable to create file \"%s/%s\"",
338aa772005SRobert Watson 			    adhost->adh_directory, filename);
339aa772005SRobert Watson 			PJDLOG_ASSERT(errno > 0);
340aa772005SRobert Watson 			return (ADIST_ERROR_CREATE);
341aa772005SRobert Watson 		}
342aa772005SRobert Watson 		pjdlog_debug(1, "Created file \"%s/%s\".",
343aa772005SRobert Watson 		    adhost->adh_directory, filename);
344aa772005SRobert Watson 		break;
345aa772005SRobert Watson 	case TRAIL_OLDER:
346aa772005SRobert Watson 		/* Trying to open old file. */
347aa772005SRobert Watson 		pjdlog_error("Sender wants to open an old file \"%s\".", filename);
348aa772005SRobert Watson 		return (ADIST_ERROR_OPEN_OLD);
349aa772005SRobert Watson 	default:
350aa772005SRobert Watson 		PJDLOG_ABORT("Unknown return value from trail_name_compare().");
351aa772005SRobert Watson 	}
352aa772005SRobert Watson 	PJDLOG_VERIFY(strlcpy(adhost->adh_trail_name, filename,
353aa772005SRobert Watson 	    sizeof(adhost->adh_trail_name)) < sizeof(adhost->adh_trail_name));
354aa772005SRobert Watson 	adhost->adh_trail_fd = fd;
355aa772005SRobert Watson 	return (0);
356aa772005SRobert Watson }
357aa772005SRobert Watson 
358aa772005SRobert Watson /*
359aa772005SRobert Watson  * Function appends data to the trail file that is currently open.
360aa772005SRobert Watson  */
361aa772005SRobert Watson static int
receiver_append(const unsigned char * data,size_t size)362aa772005SRobert Watson receiver_append(const unsigned char *data, size_t size)
363aa772005SRobert Watson {
364aa772005SRobert Watson 	ssize_t done;
365aa772005SRobert Watson 	size_t osize;
366aa772005SRobert Watson 
367aa772005SRobert Watson 	/* We should have opened trail file. */
368aa772005SRobert Watson 	if (adhost->adh_trail_fd == -1) {
369aa772005SRobert Watson 		pjdlog_error("Sender requested append without first opening file.");
370aa772005SRobert Watson 		return (ADIST_ERROR_WRONG_ORDER);
371aa772005SRobert Watson 	}
372aa772005SRobert Watson 
373aa772005SRobert Watson 	osize = size;
374aa772005SRobert Watson 	while (size > 0) {
375aa772005SRobert Watson 		done = write(adhost->adh_trail_fd, data, size);
376aa772005SRobert Watson 		if (done == -1) {
377aa772005SRobert Watson 			if (errno == EINTR)
378aa772005SRobert Watson 				continue;
379aa772005SRobert Watson 			pjdlog_errno(LOG_ERR, "Write to \"%s/%s\" failed",
380aa772005SRobert Watson 			    adhost->adh_directory, adhost->adh_trail_name);
381aa772005SRobert Watson 			PJDLOG_ASSERT(errno > 0);
382aa772005SRobert Watson 			return (ADIST_ERROR_WRITE);
383aa772005SRobert Watson 		}
384aa772005SRobert Watson 		pjdlog_debug(3, "Wrote %zd bytes into \"%s/%s\".", done,
385aa772005SRobert Watson 		    adhost->adh_directory, adhost->adh_trail_name);
386aa772005SRobert Watson 		size -= done;
387aa772005SRobert Watson 	}
388aa772005SRobert Watson 	pjdlog_debug(2, "Appended %zu bytes to file \"%s/%s\".",
389aa772005SRobert Watson 	    osize, adhost->adh_directory, adhost->adh_trail_name);
390aa772005SRobert Watson 	return (0);
391aa772005SRobert Watson }
392aa772005SRobert Watson 
393aa772005SRobert Watson static int
receiver_close(const char * filename)394aa772005SRobert Watson receiver_close(const char *filename)
395aa772005SRobert Watson {
396aa772005SRobert Watson 
397aa772005SRobert Watson 	/* We should have opened trail file. */
398aa772005SRobert Watson 	if (adhost->adh_trail_fd == -1) {
399aa772005SRobert Watson 		pjdlog_error("Sender requested closing file without first opening it.");
400aa772005SRobert Watson 		return (ADIST_ERROR_WRONG_ORDER);
401aa772005SRobert Watson 	}
402aa772005SRobert Watson 
403aa772005SRobert Watson 	/* Validate if we can do the rename. */
404aa772005SRobert Watson 	if (!trail_validate_name(adhost->adh_trail_name, filename)) {
405aa772005SRobert Watson 		pjdlog_error("Sender wants to close file \"%s\" using name \"%s\".",
406aa772005SRobert Watson 		    adhost->adh_trail_name, filename);
407aa772005SRobert Watson 		return (ADIST_ERROR_INVALID_NAME);
408aa772005SRobert Watson 	}
409aa772005SRobert Watson 
410aa772005SRobert Watson 	PJDLOG_VERIFY(close(adhost->adh_trail_fd) == 0);
411aa772005SRobert Watson 	adhost->adh_trail_fd = -1;
412aa772005SRobert Watson 
413aa772005SRobert Watson 	pjdlog_debug(1, "Closed file \"%s/%s\".", adhost->adh_directory,
414aa772005SRobert Watson 	    adhost->adh_trail_name);
415aa772005SRobert Watson 
416aa772005SRobert Watson 	if (strcmp(adhost->adh_trail_name, filename) == 0) {
417aa772005SRobert Watson 		/* File name didn't change, we are done here. */
418aa772005SRobert Watson 		return (0);
419aa772005SRobert Watson 	}
420aa772005SRobert Watson 
421aa772005SRobert Watson 	if (renameat(adhost->adh_trail_dirfd, adhost->adh_trail_name,
422aa772005SRobert Watson 	    adhost->adh_trail_dirfd, filename) == -1) {
423aa772005SRobert Watson 		pjdlog_errno(LOG_ERR, "Unable to rename \"%s\" to \"%s\"",
424aa772005SRobert Watson 		    adhost->adh_trail_name, filename);
425aa772005SRobert Watson 		PJDLOG_ASSERT(errno > 0);
426aa772005SRobert Watson 		return (ADIST_ERROR_RENAME);
427aa772005SRobert Watson 	}
428aa772005SRobert Watson 	pjdlog_debug(1, "Renamed file \"%s/%s\" to \"%s/%s\".",
429aa772005SRobert Watson 	    adhost->adh_directory, adhost->adh_trail_name,
430aa772005SRobert Watson 	    adhost->adh_directory, filename);
431aa772005SRobert Watson 	PJDLOG_VERIFY(strlcpy(adhost->adh_trail_name, filename,
432aa772005SRobert Watson 	    sizeof(adhost->adh_trail_name)) < sizeof(adhost->adh_trail_name));
433aa772005SRobert Watson 
434aa772005SRobert Watson 	return (0);
435aa772005SRobert Watson }
436aa772005SRobert Watson 
437aa772005SRobert Watson static int
receiver_error(void)438aa772005SRobert Watson receiver_error(void)
439aa772005SRobert Watson {
440aa772005SRobert Watson 
441aa772005SRobert Watson 	/* We should have opened trail file. */
442aa772005SRobert Watson 	if (adhost->adh_trail_fd == -1) {
443aa772005SRobert Watson 		pjdlog_error("Sender send read error, but file is not open.");
444aa772005SRobert Watson 		return (ADIST_ERROR_WRONG_ORDER);
445aa772005SRobert Watson 	}
446aa772005SRobert Watson 
447aa772005SRobert Watson 	PJDLOG_VERIFY(close(adhost->adh_trail_fd) == 0);
448aa772005SRobert Watson 	adhost->adh_trail_fd = -1;
449aa772005SRobert Watson 
450aa772005SRobert Watson 	pjdlog_debug(1, "Closed file \"%s/%s\".", adhost->adh_directory,
451aa772005SRobert Watson 	    adhost->adh_trail_name);
452aa772005SRobert Watson 
453aa772005SRobert Watson 	return (0);
454aa772005SRobert Watson }
455aa772005SRobert Watson 
456aa772005SRobert Watson static void *
disk_thread(void * arg __unused)457aa772005SRobert Watson disk_thread(void *arg __unused)
458aa772005SRobert Watson {
459aa772005SRobert Watson 	struct adreq *adreq;
460aa772005SRobert Watson 
461aa772005SRobert Watson 	for (;;) {
462aa772005SRobert Watson 		pjdlog_debug(3, "disk: Taking request.");
463aa772005SRobert Watson 		QUEUE_TAKE(adreq, &adist_disk_list, 0);
464aa772005SRobert Watson 		adreq_log(LOG_DEBUG, 3, -1, adreq, "disk: (%p) Got request: ",
465aa772005SRobert Watson 		    adreq);
466aa772005SRobert Watson 		/* Handle the actual request. */
467aa772005SRobert Watson 		switch (adreq->adr_cmd) {
468aa772005SRobert Watson 		case ADIST_CMD_OPEN:
469aa772005SRobert Watson 			adreq->adr_error = receiver_open(adreq->adr_data);
470aa772005SRobert Watson 			break;
471aa772005SRobert Watson 		case ADIST_CMD_APPEND:
472aa772005SRobert Watson 			adreq->adr_error = receiver_append(adreq->adr_data,
473aa772005SRobert Watson 			    adreq->adr_datasize);
474aa772005SRobert Watson 			break;
475aa772005SRobert Watson 		case ADIST_CMD_CLOSE:
476aa772005SRobert Watson 			adreq->adr_error = receiver_close(adreq->adr_data);
477aa772005SRobert Watson 			break;
478aa772005SRobert Watson 		case ADIST_CMD_ERROR:
479aa772005SRobert Watson 			adreq->adr_error = receiver_error();
480aa772005SRobert Watson 			break;
481aa772005SRobert Watson 		default:
482aa772005SRobert Watson 			PJDLOG_ABORT("Unexpected command (cmd=%hhu).",
483aa772005SRobert Watson 			    adreq->adr_cmd);
484aa772005SRobert Watson 		}
485aa772005SRobert Watson 		if (adreq->adr_error != 0) {
486aa772005SRobert Watson 			adreq_log(LOG_ERR, 0, adreq->adr_error, adreq,
487aa772005SRobert Watson 			    "Request failed: ");
488aa772005SRobert Watson 		}
489aa772005SRobert Watson 		pjdlog_debug(3, "disk: (%p) Moving request to the send queue.",
490aa772005SRobert Watson 		    adreq);
491aa772005SRobert Watson 		QUEUE_INSERT(adreq, &adist_send_list);
492aa772005SRobert Watson 	}
493aa772005SRobert Watson 	/* NOTREACHED */
494aa772005SRobert Watson 	return (NULL);
495aa772005SRobert Watson }
496aa772005SRobert Watson 
497aa772005SRobert Watson /*
498aa772005SRobert Watson  * Thread sends requests back to primary node.
499aa772005SRobert Watson  */
500aa772005SRobert Watson static void *
send_thread(void * arg __unused)501aa772005SRobert Watson send_thread(void *arg __unused)
502aa772005SRobert Watson {
503aa772005SRobert Watson 	struct adreq *adreq;
504aa772005SRobert Watson 	struct adrep adrep;
505aa772005SRobert Watson 
506aa772005SRobert Watson 	for (;;) {
507aa772005SRobert Watson 		pjdlog_debug(3, "send: Taking request.");
508aa772005SRobert Watson 		QUEUE_TAKE(adreq, &adist_send_list, 0);
509aa772005SRobert Watson 		adreq_log(LOG_DEBUG, 3, -1, adreq, "send: (%p) Got request: ",
510aa772005SRobert Watson 		    adreq);
511aa772005SRobert Watson 		adrep.adrp_byteorder = ADIST_BYTEORDER;
512aa772005SRobert Watson 		adrep.adrp_seq = adreq->adr_seq;
513aa772005SRobert Watson 		adrep.adrp_error = adreq->adr_error;
514aa772005SRobert Watson 		if (proto_send(adhost->adh_remote, &adrep,
515aa772005SRobert Watson 		    sizeof(adrep)) == -1) {
516aa772005SRobert Watson 			pjdlog_exit(EX_TEMPFAIL, "Unable to send reply");
517aa772005SRobert Watson 		}
518aa772005SRobert Watson 		pjdlog_debug(3, "send: (%p) Moving request to the free queue.",
519aa772005SRobert Watson 		    adreq);
520aa772005SRobert Watson 		adreq_clear(adreq);
521aa772005SRobert Watson 		QUEUE_INSERT(adreq, &adist_free_list);
522aa772005SRobert Watson 	}
523aa772005SRobert Watson 	/* NOTREACHED */
524aa772005SRobert Watson 	return (NULL);
525aa772005SRobert Watson }
526aa772005SRobert Watson 
527aa772005SRobert Watson static void
receiver_directory_create(void)528aa772005SRobert Watson receiver_directory_create(void)
529aa772005SRobert Watson {
530aa772005SRobert Watson 	struct passwd *pw;
531aa772005SRobert Watson 
532aa772005SRobert Watson 	/*
533aa772005SRobert Watson 	 * According to getpwnam(3) we have to clear errno before calling the
534aa772005SRobert Watson 	 * function to be able to distinguish between an error and missing
535aa772005SRobert Watson 	 * entry (with is not treated as error by getpwnam(3)).
536aa772005SRobert Watson 	 */
537aa772005SRobert Watson 	errno = 0;
538aa772005SRobert Watson 	pw = getpwnam(ADIST_USER);
539aa772005SRobert Watson 	if (pw == NULL) {
540aa772005SRobert Watson 		if (errno != 0) {
541aa772005SRobert Watson 			pjdlog_exit(EX_NOUSER,
542aa772005SRobert Watson 			    "Unable to find info about '%s' user", ADIST_USER);
543aa772005SRobert Watson 		} else {
544aa772005SRobert Watson 			pjdlog_exitx(EX_NOUSER, "User '%s' doesn't exist.",
545aa772005SRobert Watson 			    ADIST_USER);
546aa772005SRobert Watson 		}
547aa772005SRobert Watson 	}
548aa772005SRobert Watson 
549aa772005SRobert Watson 	if (mkdir(adhost->adh_directory, 0700) == -1) {
550aa772005SRobert Watson 		pjdlog_exit(EX_OSFILE, "Unable to create directory \"%s\"",
551aa772005SRobert Watson 		    adhost->adh_directory);
552aa772005SRobert Watson 	}
553aa772005SRobert Watson 	if (chown(adhost->adh_directory, pw->pw_uid, pw->pw_gid) == -1) {
554aa772005SRobert Watson 		pjdlog_errno(LOG_ERR,
555aa772005SRobert Watson 		    "Unable to change owner of the directory \"%s\"",
556aa772005SRobert Watson 		    adhost->adh_directory);
557aa772005SRobert Watson 		(void)rmdir(adhost->adh_directory);
558aa772005SRobert Watson 		exit(EX_OSFILE);
559aa772005SRobert Watson 	}
560aa772005SRobert Watson }
561aa772005SRobert Watson 
562aa772005SRobert Watson static void
receiver_directory_open(void)563aa772005SRobert Watson receiver_directory_open(void)
564aa772005SRobert Watson {
565aa772005SRobert Watson 
566aa772005SRobert Watson #ifdef HAVE_FDOPENDIR
567aa772005SRobert Watson 	adhost->adh_trail_dirfd = open(adhost->adh_directory,
568aa772005SRobert Watson 	    O_RDONLY | O_DIRECTORY);
569aa772005SRobert Watson 	if (adhost->adh_trail_dirfd == -1) {
570aa772005SRobert Watson 		if (errno == ENOENT) {
571aa772005SRobert Watson 			receiver_directory_create();
572aa772005SRobert Watson 			adhost->adh_trail_dirfd = open(adhost->adh_directory,
573aa772005SRobert Watson 			    O_RDONLY | O_DIRECTORY);
574aa772005SRobert Watson 		}
575aa772005SRobert Watson 		if (adhost->adh_trail_dirfd == -1) {
576aa772005SRobert Watson 			pjdlog_exit(EX_CONFIG,
577aa772005SRobert Watson 			    "Unable to open directory \"%s\"",
578aa772005SRobert Watson 			    adhost->adh_directory);
579aa772005SRobert Watson 		}
580aa772005SRobert Watson 	}
581aa772005SRobert Watson 	adhost->adh_trail_dirfp = fdopendir(adhost->adh_trail_dirfd);
582aa772005SRobert Watson 	if (adhost->adh_trail_dirfp == NULL) {
583aa772005SRobert Watson 		pjdlog_exit(EX_CONFIG, "Unable to fdopen directory \"%s\"",
584aa772005SRobert Watson 		    adhost->adh_directory);
585aa772005SRobert Watson 	}
586aa772005SRobert Watson #else
587aa772005SRobert Watson 	struct stat sb;
588aa772005SRobert Watson 
589aa772005SRobert Watson 	if (stat(adhost->adh_directory, &sb) == -1) {
590aa772005SRobert Watson 		if (errno == ENOENT) {
591aa772005SRobert Watson 			receiver_directory_create();
592aa772005SRobert Watson 		} else {
593aa772005SRobert Watson 			pjdlog_exit(EX_CONFIG,
594aa772005SRobert Watson 			    "Unable to stat directory \"%s\"",
595aa772005SRobert Watson 			    adhost->adh_directory);
596aa772005SRobert Watson 		}
597aa772005SRobert Watson 	}
598aa772005SRobert Watson 	adhost->adh_trail_dirfp = opendir(adhost->adh_directory);
599aa772005SRobert Watson 	if (adhost->adh_trail_dirfp == NULL) {
600aa772005SRobert Watson 		pjdlog_exit(EX_CONFIG, "Unable to open directory \"%s\"",
601aa772005SRobert Watson 		    adhost->adh_directory);
602aa772005SRobert Watson 	}
603aa772005SRobert Watson 	adhost->adh_trail_dirfd = dirfd(adhost->adh_trail_dirfp);
604aa772005SRobert Watson #endif
605aa772005SRobert Watson }
606aa772005SRobert Watson 
607aa772005SRobert Watson static void
receiver_connect(void)608aa772005SRobert Watson receiver_connect(void)
609aa772005SRobert Watson {
610aa772005SRobert Watson 	uint64_t trail_size;
611aa772005SRobert Watson 	struct stat sb;
612aa772005SRobert Watson 
613aa772005SRobert Watson 	PJDLOG_ASSERT(adhost->adh_trail_dirfp != NULL);
614aa772005SRobert Watson 
615aa772005SRobert Watson 	trail_last(adhost->adh_trail_dirfp, adhost->adh_trail_name,
616aa772005SRobert Watson 	    sizeof(adhost->adh_trail_name));
617aa772005SRobert Watson 
618aa772005SRobert Watson 	if (adhost->adh_trail_name[0] == '\0') {
619aa772005SRobert Watson 		trail_size = 0;
620aa772005SRobert Watson 	} else {
621aa772005SRobert Watson 		if (fstatat(adhost->adh_trail_dirfd, adhost->adh_trail_name,
622aa772005SRobert Watson 		    &sb, AT_SYMLINK_NOFOLLOW) == -1) {
623aa772005SRobert Watson 			pjdlog_exit(EX_CONFIG, "Unable to stat \"%s/%s\"",
624aa772005SRobert Watson 			    adhost->adh_directory, adhost->adh_trail_name);
625aa772005SRobert Watson 		}
626aa772005SRobert Watson 		if (!S_ISREG(sb.st_mode)) {
627aa772005SRobert Watson 			pjdlog_exitx(EX_CONFIG,
628aa772005SRobert Watson 			    "File \"%s/%s\" is not a regular file.",
629aa772005SRobert Watson 			    adhost->adh_directory, adhost->adh_trail_name);
630aa772005SRobert Watson 		}
631aa772005SRobert Watson 		trail_size = sb.st_size;
632aa772005SRobert Watson 	}
633aa772005SRobert Watson 	trail_size = htole64(trail_size);
634aa772005SRobert Watson 	if (proto_send(adhost->adh_remote, &trail_size,
635aa772005SRobert Watson 	    sizeof(trail_size)) == -1) {
636aa772005SRobert Watson 		pjdlog_exit(EX_TEMPFAIL,
637aa772005SRobert Watson 		    "Unable to send size of the most recent trail file");
638aa772005SRobert Watson 	}
639aa772005SRobert Watson 	if (proto_send(adhost->adh_remote, adhost->adh_trail_name,
640aa772005SRobert Watson 	    sizeof(adhost->adh_trail_name)) == -1) {
641aa772005SRobert Watson 		pjdlog_exit(EX_TEMPFAIL,
642aa772005SRobert Watson 		    "Unable to send name of the most recent trail file");
643aa772005SRobert Watson 	}
644aa772005SRobert Watson }
645aa772005SRobert Watson 
646aa772005SRobert Watson void
adist_receiver(struct adist_config * config,struct adist_host * adh)647aa772005SRobert Watson adist_receiver(struct adist_config *config, struct adist_host *adh)
648aa772005SRobert Watson {
649aa772005SRobert Watson 	sigset_t mask;
650aa772005SRobert Watson 	pthread_t td;
651aa772005SRobert Watson 	pid_t pid;
652aa772005SRobert Watson 	int error, mode, debuglevel;
653aa772005SRobert Watson 
654aa772005SRobert Watson 	pid = fork();
655aa772005SRobert Watson 	if (pid == -1) {
656aa772005SRobert Watson 		pjdlog_errno(LOG_ERR, "Unable to fork");
657aa772005SRobert Watson 		proto_close(adh->adh_remote);
658aa772005SRobert Watson 		adh->adh_remote = NULL;
659aa772005SRobert Watson 		return;
660aa772005SRobert Watson 	}
661aa772005SRobert Watson 
662aa772005SRobert Watson 	if (pid > 0) {
663aa772005SRobert Watson 		/* This is parent. */
664aa772005SRobert Watson 		proto_close(adh->adh_remote);
665aa772005SRobert Watson 		adh->adh_remote = NULL;
666aa772005SRobert Watson 		adh->adh_worker_pid = pid;
667aa772005SRobert Watson 		return;
668aa772005SRobert Watson 	}
669aa772005SRobert Watson 
670aa772005SRobert Watson 	adcfg = config;
671aa772005SRobert Watson 	adhost = adh;
672aa772005SRobert Watson 	mode = pjdlog_mode_get();
673aa772005SRobert Watson 	debuglevel = pjdlog_debug_get();
674aa772005SRobert Watson 
675aa772005SRobert Watson 	descriptors_cleanup(adhost);
676aa772005SRobert Watson 
677aa772005SRobert Watson //	descriptors_assert(adhost, mode);
678aa772005SRobert Watson 
679aa772005SRobert Watson 	pjdlog_init(mode);
680aa772005SRobert Watson 	pjdlog_debug_set(debuglevel);
681aa772005SRobert Watson 	pjdlog_prefix_set("[%s] (%s) ", adhost->adh_name,
682aa772005SRobert Watson 	    role2str(adhost->adh_role));
683aa772005SRobert Watson #ifdef HAVE_SETPROCTITLE
684aa772005SRobert Watson 	setproctitle("%s (%s)", adhost->adh_name, role2str(adhost->adh_role));
685aa772005SRobert Watson #endif
686aa772005SRobert Watson 
687aa772005SRobert Watson 	PJDLOG_VERIFY(sigemptyset(&mask) == 0);
688aa772005SRobert Watson 	PJDLOG_VERIFY(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
689aa772005SRobert Watson 
690aa772005SRobert Watson 	/* Error in setting timeout is not critical, but why should it fail? */
691aa772005SRobert Watson 	if (proto_timeout(adhost->adh_remote, adcfg->adc_timeout) == -1)
692aa772005SRobert Watson 		pjdlog_errno(LOG_WARNING, "Unable to set connection timeout");
693aa772005SRobert Watson 
694aa772005SRobert Watson 	init_environment();
695aa772005SRobert Watson 
696aa772005SRobert Watson 	adhost->adh_trail_fd = -1;
697aa772005SRobert Watson 	receiver_directory_open();
698aa772005SRobert Watson 
699aa772005SRobert Watson 	if (sandbox(ADIST_USER, true, "auditdistd: %s (%s)",
700aa772005SRobert Watson 	    role2str(adhost->adh_role), adhost->adh_name) != 0) {
701aa772005SRobert Watson 		exit(EX_CONFIG);
702aa772005SRobert Watson 	}
703aa772005SRobert Watson 	pjdlog_info("Privileges successfully dropped.");
704aa772005SRobert Watson 
705aa772005SRobert Watson 	receiver_connect();
706aa772005SRobert Watson 
707aa772005SRobert Watson 	error = pthread_create(&td, NULL, recv_thread, adhost);
708aa772005SRobert Watson 	PJDLOG_ASSERT(error == 0);
709aa772005SRobert Watson 	error = pthread_create(&td, NULL, disk_thread, adhost);
710aa772005SRobert Watson 	PJDLOG_ASSERT(error == 0);
711aa772005SRobert Watson 	(void)send_thread(adhost);
712aa772005SRobert Watson }
713