2 * Copyright (c) 2012 The FreeBSD Foundation
5 * This software was developed by Pawel Jakub Dawidek under sponsorship from
6 * the FreeBSD Foundation.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 #include <config/config.h>
32 #include <sys/param.h>
33 #if defined(HAVE_SYS_ENDIAN_H) && defined(HAVE_BSWAP)
34 #include <sys/endian.h>
35 #else /* !HAVE_SYS_ENDIAN_H || !HAVE_BSWAP */
36 #ifdef HAVE_MACHINE_ENDIAN_H
37 #include <machine/endian.h>
38 #else /* !HAVE_MACHINE_ENDIAN_H */
41 #else /* !HAVE_ENDIAN_H */
42 #error "No supported endian.h"
43 #endif /* !HAVE_ENDIAN_H */
44 #endif /* !HAVE_MACHINE_ENDIAN_H */
45 #include <compat/endian.h>
46 #endif /* !HAVE_SYS_ENDIAN_H || !HAVE_BSWAP */
47 #include <sys/queue.h>
67 #include <compat/strlcpy.h>
79 #include "auditdistd.h"
87 static struct adist_config *adcfg;
88 static struct adist_host *adhost;
90 static TAILQ_HEAD(, adreq) adist_free_list;
91 static pthread_mutex_t adist_free_list_lock;
92 static pthread_cond_t adist_free_list_cond;
93 static TAILQ_HEAD(, adreq) adist_disk_list;
94 static pthread_mutex_t adist_disk_list_lock;
95 static pthread_cond_t adist_disk_list_cond;
96 static TAILQ_HEAD(, adreq) adist_send_list;
97 static pthread_mutex_t adist_send_list_lock;
98 static pthread_cond_t adist_send_list_cond;
101 adreq_clear(struct adreq *adreq)
104 adreq->adr_error = -1;
105 adreq->adr_byteorder = ADIST_BYTEORDER_UNDEFINED;
106 adreq->adr_cmd = ADIST_CMD_UNDEFINED;
108 adreq->adr_datasize = 0;
112 init_environment(void)
117 TAILQ_INIT(&adist_free_list);
118 mtx_init(&adist_free_list_lock);
119 cv_init(&adist_free_list_cond);
120 TAILQ_INIT(&adist_disk_list);
121 mtx_init(&adist_disk_list_lock);
122 cv_init(&adist_disk_list_cond);
123 TAILQ_INIT(&adist_send_list);
124 mtx_init(&adist_send_list_lock);
125 cv_init(&adist_send_list_cond);
127 for (ii = 0; ii < ADIST_QUEUE_SIZE; ii++) {
128 adreq = malloc(sizeof(*adreq) + ADIST_BUF_SIZE);
130 pjdlog_exitx(EX_TEMPFAIL,
131 "Unable to allocate %zu bytes of memory for adreq object.",
132 sizeof(*adreq) + ADIST_BUF_SIZE);
135 TAILQ_INSERT_TAIL(&adist_free_list, adreq, adr_next);
140 adreq_decode_and_validate_header(struct adreq *adreq)
143 /* Byte-swap only if the sender is using different byte order. */
144 if (adreq->adr_byteorder != ADIST_BYTEORDER) {
145 adreq->adr_byteorder = ADIST_BYTEORDER;
146 adreq->adr_seq = bswap64(adreq->adr_seq);
147 adreq->adr_datasize = bswap32(adreq->adr_datasize);
150 /* Validate packet header. */
152 if (adreq->adr_datasize > ADIST_BUF_SIZE) {
153 pjdlog_exitx(EX_PROTOCOL, "Invalid datasize received (%ju).",
154 (uintmax_t)adreq->adr_datasize);
157 switch (adreq->adr_cmd) {
159 case ADIST_CMD_APPEND:
160 case ADIST_CMD_CLOSE:
161 if (adreq->adr_datasize == 0) {
162 pjdlog_exitx(EX_PROTOCOL,
163 "Invalid datasize received (%ju).",
164 (uintmax_t)adreq->adr_datasize);
167 case ADIST_CMD_KEEPALIVE:
168 case ADIST_CMD_ERROR:
169 if (adreq->adr_datasize > 0) {
170 pjdlog_exitx(EX_PROTOCOL,
171 "Invalid datasize received (%ju).",
172 (uintmax_t)adreq->adr_datasize);
176 pjdlog_exitx(EX_PROTOCOL, "Invalid command received (%hhu).",
182 adreq_validate_data(const struct adreq *adreq)
185 /* Validate packet data. */
187 switch (adreq->adr_cmd) {
189 case ADIST_CMD_CLOSE:
191 * File name must end up with '\0' and there must be no '\0'
194 if (adreq->adr_data[adreq->adr_datasize - 1] != '\0' ||
195 strchr(adreq->adr_data, '\0') !=
196 (const char *)adreq->adr_data + adreq->adr_datasize - 1) {
197 pjdlog_exitx(EX_PROTOCOL,
198 "Invalid file name received.");
205 * Thread receives requests from the sender.
208 recv_thread(void *arg __unused)
213 pjdlog_debug(3, "recv: Taking free request.");
214 QUEUE_TAKE(adreq, &adist_free_list, 0);
215 pjdlog_debug(3, "recv: (%p) Got request.", adreq);
217 if (proto_recv(adhost->adh_remote, &adreq->adr_packet,
218 sizeof(adreq->adr_packet)) == -1) {
219 pjdlog_exit(EX_TEMPFAIL,
220 "Unable to receive request header");
222 adreq_decode_and_validate_header(adreq);
224 switch (adreq->adr_cmd) {
225 case ADIST_CMD_KEEPALIVE:
226 adreq->adr_error = 0;
227 adreq_log(LOG_DEBUG, 2, -1, adreq,
228 "recv: (%p) Got request header: ", adreq);
230 "recv: (%p) Moving request to the send queue.",
232 QUEUE_INSERT(adreq, &adist_send_list);
234 case ADIST_CMD_ERROR:
235 pjdlog_error("An error occured on the sender while reading \"%s/%s\".",
236 adhost->adh_directory, adhost->adh_trail_name);
237 adreq_log(LOG_DEBUG, 2, ADIST_ERROR_READ, adreq,
238 "recv: (%p) Got request header: ", adreq);
240 "recv: (%p) Moving request to the send queue.",
242 QUEUE_INSERT(adreq, &adist_disk_list);
245 case ADIST_CMD_APPEND:
246 case ADIST_CMD_CLOSE:
247 if (proto_recv(adhost->adh_remote, adreq->adr_data,
248 adreq->adr_datasize) == -1) {
249 pjdlog_exit(EX_TEMPFAIL,
250 "Unable to receive request data");
252 adreq_validate_data(adreq);
253 adreq_log(LOG_DEBUG, 2, -1, adreq,
254 "recv: (%p) Got request header: ", adreq);
256 "recv: (%p) Moving request to the disk queue.",
258 QUEUE_INSERT(adreq, &adist_disk_list);
261 PJDLOG_ABORT("Invalid condition.");
269 * Function that opens trail file requested by the sender.
270 * If the file already exist, it has to be the most recent file and it can
271 * only be open for append.
272 * If the file doesn't already exist, it has to be "older" than all existing
276 receiver_open(const char *filename)
281 * Previous file should be closed by now. Sending OPEN request without
282 * sending CLOSE for the previous file is a sender bug.
284 if (adhost->adh_trail_fd != -1) {
285 pjdlog_error("Sender requested opening file \"%s\" without first closing \"%s\".",
286 filename, adhost->adh_trail_name);
287 return (ADIST_ERROR_WRONG_ORDER);
290 if (!trail_validate_name(filename, NULL)) {
291 pjdlog_error("Sender wants to open file \"%s\", which has invalid name.",
293 return (ADIST_ERROR_INVALID_NAME);
296 switch (trail_name_compare(filename, adhost->adh_trail_name)) {
298 if (!trail_is_not_terminated(adhost->adh_trail_name)) {
299 pjdlog_error("Terminated trail \"%s/%s\" was unterminated on the sender as \"%s/%s\"?",
300 adhost->adh_directory, adhost->adh_trail_name,
301 adhost->adh_directory, filename);
302 return (ADIST_ERROR_INVALID_NAME);
304 if (renameat(adhost->adh_trail_dirfd, adhost->adh_trail_name,
305 adhost->adh_trail_dirfd, filename) == -1) {
306 pjdlog_errno(LOG_ERR,
307 "Unable to rename file \"%s/%s\" to \"%s/%s\"",
308 adhost->adh_directory, adhost->adh_trail_name,
309 adhost->adh_directory, filename);
310 PJDLOG_ASSERT(errno > 0);
311 return (ADIST_ERROR_RENAME);
313 pjdlog_debug(1, "Renamed file \"%s/%s\" to \"%s/%s\".",
314 adhost->adh_directory, adhost->adh_trail_name,
315 adhost->adh_directory, filename);
317 case TRAIL_IDENTICAL:
318 /* Opening existing file. */
319 fd = openat(adhost->adh_trail_dirfd, filename,
320 O_WRONLY | O_APPEND | O_NOFOLLOW);
322 pjdlog_errno(LOG_ERR,
323 "Unable to open file \"%s/%s\" for append",
324 adhost->adh_directory, filename);
325 PJDLOG_ASSERT(errno > 0);
326 return (ADIST_ERROR_OPEN);
328 pjdlog_debug(1, "Opened file \"%s/%s\".",
329 adhost->adh_directory, filename);
332 /* Opening new file. */
333 fd = openat(adhost->adh_trail_dirfd, filename,
334 O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW, 0600);
336 pjdlog_errno(LOG_ERR,
337 "Unable to create file \"%s/%s\"",
338 adhost->adh_directory, filename);
339 PJDLOG_ASSERT(errno > 0);
340 return (ADIST_ERROR_CREATE);
342 pjdlog_debug(1, "Created file \"%s/%s\".",
343 adhost->adh_directory, filename);
346 /* Trying to open old file. */
347 pjdlog_error("Sender wants to open an old file \"%s\".", filename);
348 return (ADIST_ERROR_OPEN_OLD);
350 PJDLOG_ABORT("Unknown return value from trail_name_compare().");
352 PJDLOG_VERIFY(strlcpy(adhost->adh_trail_name, filename,
353 sizeof(adhost->adh_trail_name)) < sizeof(adhost->adh_trail_name));
354 adhost->adh_trail_fd = fd;
359 * Function appends data to the trail file that is currently open.
362 receiver_append(const unsigned char *data, size_t size)
367 /* We should have opened trail file. */
368 if (adhost->adh_trail_fd == -1) {
369 pjdlog_error("Sender requested append without first opening file.");
370 return (ADIST_ERROR_WRONG_ORDER);
375 done = write(adhost->adh_trail_fd, data, size);
379 pjdlog_errno(LOG_ERR, "Write to \"%s/%s\" failed",
380 adhost->adh_directory, adhost->adh_trail_name);
381 PJDLOG_ASSERT(errno > 0);
382 return (ADIST_ERROR_WRITE);
384 pjdlog_debug(3, "Wrote %zd bytes into \"%s/%s\".", done,
385 adhost->adh_directory, adhost->adh_trail_name);
388 pjdlog_debug(2, "Appended %zu bytes to file \"%s/%s\".",
389 osize, adhost->adh_directory, adhost->adh_trail_name);
394 receiver_close(const char *filename)
397 /* We should have opened trail file. */
398 if (adhost->adh_trail_fd == -1) {
399 pjdlog_error("Sender requested closing file without first opening it.");
400 return (ADIST_ERROR_WRONG_ORDER);
403 /* Validate if we can do the rename. */
404 if (!trail_validate_name(adhost->adh_trail_name, filename)) {
405 pjdlog_error("Sender wants to close file \"%s\" using name \"%s\".",
406 adhost->adh_trail_name, filename);
407 return (ADIST_ERROR_INVALID_NAME);
410 PJDLOG_VERIFY(close(adhost->adh_trail_fd) == 0);
411 adhost->adh_trail_fd = -1;
413 pjdlog_debug(1, "Closed file \"%s/%s\".", adhost->adh_directory,
414 adhost->adh_trail_name);
416 if (strcmp(adhost->adh_trail_name, filename) == 0) {
417 /* File name didn't change, we are done here. */
421 if (renameat(adhost->adh_trail_dirfd, adhost->adh_trail_name,
422 adhost->adh_trail_dirfd, filename) == -1) {
423 pjdlog_errno(LOG_ERR, "Unable to rename \"%s\" to \"%s\"",
424 adhost->adh_trail_name, filename);
425 PJDLOG_ASSERT(errno > 0);
426 return (ADIST_ERROR_RENAME);
428 pjdlog_debug(1, "Renamed file \"%s/%s\" to \"%s/%s\".",
429 adhost->adh_directory, adhost->adh_trail_name,
430 adhost->adh_directory, filename);
431 PJDLOG_VERIFY(strlcpy(adhost->adh_trail_name, filename,
432 sizeof(adhost->adh_trail_name)) < sizeof(adhost->adh_trail_name));
441 /* We should have opened trail file. */
442 if (adhost->adh_trail_fd == -1) {
443 pjdlog_error("Sender send read error, but file is not open.");
444 return (ADIST_ERROR_WRONG_ORDER);
447 PJDLOG_VERIFY(close(adhost->adh_trail_fd) == 0);
448 adhost->adh_trail_fd = -1;
450 pjdlog_debug(1, "Closed file \"%s/%s\".", adhost->adh_directory,
451 adhost->adh_trail_name);
457 disk_thread(void *arg __unused)
462 pjdlog_debug(3, "disk: Taking request.");
463 QUEUE_TAKE(adreq, &adist_disk_list, 0);
464 adreq_log(LOG_DEBUG, 3, -1, adreq, "disk: (%p) Got request: ",
466 /* Handle the actual request. */
467 switch (adreq->adr_cmd) {
469 adreq->adr_error = receiver_open(adreq->adr_data);
471 case ADIST_CMD_APPEND:
472 adreq->adr_error = receiver_append(adreq->adr_data,
473 adreq->adr_datasize);
475 case ADIST_CMD_CLOSE:
476 adreq->adr_error = receiver_close(adreq->adr_data);
478 case ADIST_CMD_ERROR:
479 adreq->adr_error = receiver_error();
482 PJDLOG_ABORT("Unexpected command (cmd=%hhu).",
485 if (adreq->adr_error != 0) {
486 adreq_log(LOG_ERR, 0, adreq->adr_error, adreq,
489 pjdlog_debug(3, "disk: (%p) Moving request to the send queue.",
491 QUEUE_INSERT(adreq, &adist_send_list);
498 * Thread sends requests back to primary node.
501 send_thread(void *arg __unused)
507 pjdlog_debug(3, "send: Taking request.");
508 QUEUE_TAKE(adreq, &adist_send_list, 0);
509 adreq_log(LOG_DEBUG, 3, -1, adreq, "send: (%p) Got request: ",
511 adrep.adrp_byteorder = ADIST_BYTEORDER;
512 adrep.adrp_seq = adreq->adr_seq;
513 adrep.adrp_error = adreq->adr_error;
514 if (proto_send(adhost->adh_remote, &adrep,
515 sizeof(adrep)) == -1) {
516 pjdlog_exit(EX_TEMPFAIL, "Unable to send reply");
518 pjdlog_debug(3, "send: (%p) Moving request to the free queue.",
521 QUEUE_INSERT(adreq, &adist_free_list);
528 receiver_directory_create(void)
533 * According to getpwnam(3) we have to clear errno before calling the
534 * function to be able to distinguish between an error and missing
535 * entry (with is not treated as error by getpwnam(3)).
538 pw = getpwnam(ADIST_USER);
541 pjdlog_exit(EX_NOUSER,
542 "Unable to find info about '%s' user", ADIST_USER);
544 pjdlog_exitx(EX_NOUSER, "User '%s' doesn't exist.",
549 if (mkdir(adhost->adh_directory, 0700) == -1) {
550 pjdlog_exit(EX_OSFILE, "Unable to create directory \"%s\"",
551 adhost->adh_directory);
553 if (chown(adhost->adh_directory, pw->pw_uid, pw->pw_gid) == -1) {
554 pjdlog_errno(LOG_ERR,
555 "Unable to change owner of the directory \"%s\"",
556 adhost->adh_directory);
557 (void)rmdir(adhost->adh_directory);
563 receiver_directory_open(void)
566 #ifdef HAVE_FDOPENDIR
567 adhost->adh_trail_dirfd = open(adhost->adh_directory,
568 O_RDONLY | O_DIRECTORY);
569 if (adhost->adh_trail_dirfd == -1) {
570 if (errno == ENOENT) {
571 receiver_directory_create();
572 adhost->adh_trail_dirfd = open(adhost->adh_directory,
573 O_RDONLY | O_DIRECTORY);
575 if (adhost->adh_trail_dirfd == -1) {
576 pjdlog_exit(EX_CONFIG,
577 "Unable to open directory \"%s\"",
578 adhost->adh_directory);
581 adhost->adh_trail_dirfp = fdopendir(adhost->adh_trail_dirfd);
582 if (adhost->adh_trail_dirfp == NULL) {
583 pjdlog_exit(EX_CONFIG, "Unable to fdopen directory \"%s\"",
584 adhost->adh_directory);
589 if (stat(adhost->adh_directory, &sb) == -1) {
590 if (errno == ENOENT) {
591 receiver_directory_create();
593 pjdlog_exit(EX_CONFIG,
594 "Unable to stat directory \"%s\"",
595 adhost->adh_directory);
598 adhost->adh_trail_dirfp = opendir(adhost->adh_directory);
599 if (adhost->adh_trail_dirfp == NULL) {
600 pjdlog_exit(EX_CONFIG, "Unable to open directory \"%s\"",
601 adhost->adh_directory);
603 adhost->adh_trail_dirfd = dirfd(adhost->adh_trail_dirfp);
608 receiver_connect(void)
613 PJDLOG_ASSERT(adhost->adh_trail_dirfp != NULL);
615 trail_last(adhost->adh_trail_dirfp, adhost->adh_trail_name,
616 sizeof(adhost->adh_trail_name));
618 if (adhost->adh_trail_name[0] == '\0') {
621 if (fstatat(adhost->adh_trail_dirfd, adhost->adh_trail_name,
622 &sb, AT_SYMLINK_NOFOLLOW) == -1) {
623 pjdlog_exit(EX_CONFIG, "Unable to stat \"%s/%s\"",
624 adhost->adh_directory, adhost->adh_trail_name);
626 if (!S_ISREG(sb.st_mode)) {
627 pjdlog_exitx(EX_CONFIG,
628 "File \"%s/%s\" is not a regular file.",
629 adhost->adh_directory, adhost->adh_trail_name);
631 trail_size = sb.st_size;
633 trail_size = htole64(trail_size);
634 if (proto_send(adhost->adh_remote, &trail_size,
635 sizeof(trail_size)) == -1) {
636 pjdlog_exit(EX_TEMPFAIL,
637 "Unable to send size of the most recent trail file");
639 if (proto_send(adhost->adh_remote, adhost->adh_trail_name,
640 sizeof(adhost->adh_trail_name)) == -1) {
641 pjdlog_exit(EX_TEMPFAIL,
642 "Unable to send name of the most recent trail file");
647 adist_receiver(struct adist_config *config, struct adist_host *adh)
652 int error, mode, debuglevel;
656 pjdlog_errno(LOG_ERR, "Unable to fork");
657 proto_close(adh->adh_remote);
658 adh->adh_remote = NULL;
663 /* This is parent. */
664 proto_close(adh->adh_remote);
665 adh->adh_remote = NULL;
666 adh->adh_worker_pid = pid;
672 mode = pjdlog_mode_get();
673 debuglevel = pjdlog_debug_get();
675 descriptors_cleanup(adhost);
677 // descriptors_assert(adhost, mode);
680 pjdlog_debug_set(debuglevel);
681 pjdlog_prefix_set("[%s] (%s) ", adhost->adh_name,
682 role2str(adhost->adh_role));
683 #ifdef HAVE_SETPROCTITLE
684 setproctitle("%s (%s)", adhost->adh_name, role2str(adhost->adh_role));
687 PJDLOG_VERIFY(sigemptyset(&mask) == 0);
688 PJDLOG_VERIFY(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
690 /* Error in setting timeout is not critical, but why should it fail? */
691 if (proto_timeout(adhost->adh_remote, adcfg->adc_timeout) == -1)
692 pjdlog_errno(LOG_WARNING, "Unable to set connection timeout");
696 adhost->adh_trail_fd = -1;
697 receiver_directory_open();
699 if (sandbox(ADIST_USER, true, "auditdistd: %s (%s)",
700 role2str(adhost->adh_role), adhost->adh_name) != 0) {
703 pjdlog_info("Privileges successfully dropped.");
707 error = pthread_create(&td, NULL, recv_thread, adhost);
708 PJDLOG_ASSERT(error == 0);
709 error = pthread_create(&td, NULL, disk_thread, adhost);
710 PJDLOG_ASSERT(error == 0);
711 (void)send_thread(adhost);