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