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
29 * $P4: //depot/projects/trustedbsd/openbsm/bin/auditdistd/receiver.c#3 $
32 #include <config/config.h>
34 #include <sys/param.h>
35 #if defined(HAVE_SYS_ENDIAN_H) && defined(HAVE_BSWAP)
36 #include <sys/endian.h>
37 #else /* !HAVE_SYS_ENDIAN_H || !HAVE_BSWAP */
38 #ifdef HAVE_MACHINE_ENDIAN_H
39 #include <machine/endian.h>
40 #else /* !HAVE_MACHINE_ENDIAN_H */
43 #else /* !HAVE_ENDIAN_H */
44 #error "No supported endian.h"
45 #endif /* !HAVE_ENDIAN_H */
46 #endif /* !HAVE_MACHINE_ENDIAN_H */
47 #include <compat/endian.h>
48 #endif /* !HAVE_SYS_ENDIAN_H || !HAVE_BSWAP */
49 #include <sys/queue.h>
69 #include <compat/strlcpy.h>
81 #include "auditdistd.h"
89 static struct adist_config *adcfg;
90 static struct adist_host *adhost;
92 static TAILQ_HEAD(, adreq) adist_free_list;
93 static pthread_mutex_t adist_free_list_lock;
94 static pthread_cond_t adist_free_list_cond;
95 static TAILQ_HEAD(, adreq) adist_disk_list;
96 static pthread_mutex_t adist_disk_list_lock;
97 static pthread_cond_t adist_disk_list_cond;
98 static TAILQ_HEAD(, adreq) adist_send_list;
99 static pthread_mutex_t adist_send_list_lock;
100 static pthread_cond_t adist_send_list_cond;
103 adreq_clear(struct adreq *adreq)
106 adreq->adr_error = -1;
107 adreq->adr_byteorder = ADIST_BYTEORDER_UNDEFINED;
108 adreq->adr_cmd = ADIST_CMD_UNDEFINED;
110 adreq->adr_datasize = 0;
114 init_environment(void)
119 TAILQ_INIT(&adist_free_list);
120 mtx_init(&adist_free_list_lock);
121 cv_init(&adist_free_list_cond);
122 TAILQ_INIT(&adist_disk_list);
123 mtx_init(&adist_disk_list_lock);
124 cv_init(&adist_disk_list_cond);
125 TAILQ_INIT(&adist_send_list);
126 mtx_init(&adist_send_list_lock);
127 cv_init(&adist_send_list_cond);
129 for (ii = 0; ii < ADIST_QUEUE_SIZE; ii++) {
130 adreq = malloc(sizeof(*adreq) + ADIST_BUF_SIZE);
132 pjdlog_exitx(EX_TEMPFAIL,
133 "Unable to allocate %zu bytes of memory for adreq object.",
134 sizeof(*adreq) + ADIST_BUF_SIZE);
137 TAILQ_INSERT_TAIL(&adist_free_list, adreq, adr_next);
142 adreq_decode_and_validate_header(struct adreq *adreq)
145 /* Byte-swap only is the sender is using different byte order. */
146 if (adreq->adr_byteorder != ADIST_BYTEORDER) {
147 adreq->adr_byteorder = ADIST_BYTEORDER;
148 adreq->adr_seq = bswap64(adreq->adr_seq);
149 adreq->adr_datasize = bswap32(adreq->adr_datasize);
152 /* Validate packet header. */
154 if (adreq->adr_datasize > ADIST_BUF_SIZE) {
155 pjdlog_exitx(EX_PROTOCOL, "Invalid datasize received (%ju).",
156 (uintmax_t)adreq->adr_datasize);
159 switch (adreq->adr_cmd) {
161 case ADIST_CMD_APPEND:
162 case ADIST_CMD_CLOSE:
163 if (adreq->adr_datasize == 0) {
164 pjdlog_exitx(EX_PROTOCOL,
165 "Invalid datasize received (%ju).",
166 (uintmax_t)adreq->adr_datasize);
169 case ADIST_CMD_KEEPALIVE:
170 case ADIST_CMD_ERROR:
171 if (adreq->adr_datasize > 0) {
172 pjdlog_exitx(EX_PROTOCOL,
173 "Invalid datasize received (%ju).",
174 (uintmax_t)adreq->adr_datasize);
178 pjdlog_exitx(EX_PROTOCOL, "Invalid command received (%hhu).",
184 adreq_validate_data(const struct adreq *adreq)
187 /* Validate packet data. */
189 switch (adreq->adr_cmd) {
191 case ADIST_CMD_CLOSE:
193 * File name must end up with '\0' and there must be no '\0'
196 if (adreq->adr_data[adreq->adr_datasize - 1] != '\0' ||
197 strchr(adreq->adr_data, '\0') !=
198 (const char *)adreq->adr_data + adreq->adr_datasize - 1) {
199 pjdlog_exitx(EX_PROTOCOL,
200 "Invalid file name received.");
207 * Thread receives requests from the sender.
210 recv_thread(void *arg __unused)
215 pjdlog_debug(3, "recv: Taking free request.");
216 QUEUE_TAKE(adreq, &adist_free_list, 0);
217 pjdlog_debug(3, "recv: (%p) Got request.", adreq);
219 if (proto_recv(adhost->adh_remote, &adreq->adr_packet,
220 sizeof(adreq->adr_packet)) == -1) {
221 pjdlog_exit(EX_TEMPFAIL,
222 "Unable to receive request header");
224 adreq_decode_and_validate_header(adreq);
226 switch (adreq->adr_cmd) {
227 case ADIST_CMD_KEEPALIVE:
228 adreq->adr_error = 0;
229 adreq_log(LOG_DEBUG, 2, -1, adreq,
230 "recv: (%p) Got request header: ", adreq);
232 "recv: (%p) Moving request to the send queue.",
234 QUEUE_INSERT(adreq, &adist_send_list);
236 case ADIST_CMD_ERROR:
237 pjdlog_error("An error occured on the sender while reading \"%s/%s\".",
238 adhost->adh_directory, adhost->adh_trail_name);
239 adreq_log(LOG_DEBUG, 2, ADIST_ERROR_READ, adreq,
240 "recv: (%p) Got request header: ", adreq);
242 "recv: (%p) Moving request to the send queue.",
244 QUEUE_INSERT(adreq, &adist_disk_list);
247 case ADIST_CMD_APPEND:
248 case ADIST_CMD_CLOSE:
249 if (proto_recv(adhost->adh_remote, adreq->adr_data,
250 adreq->adr_datasize) == -1) {
251 pjdlog_exit(EX_TEMPFAIL,
252 "Unable to receive request data");
254 adreq_validate_data(adreq);
255 adreq_log(LOG_DEBUG, 2, -1, adreq,
256 "recv: (%p) Got request header: ", adreq);
258 "recv: (%p) Moving request to the disk queue.",
260 QUEUE_INSERT(adreq, &adist_disk_list);
263 PJDLOG_ABORT("Invalid condition.");
271 * Function that opens trail file requested by the sender.
272 * If the file already exist, it has to be the most recent file and it can
273 * only be open for append.
274 * If the file doesn't already exist, it has to be "older" than all existing
278 receiver_open(const char *filename)
283 * Previous file should be closed by now. Sending OPEN request without
284 * sending CLOSE for the previous file is a sender bug.
286 if (adhost->adh_trail_fd != -1) {
287 pjdlog_error("Sender requested opening file \"%s\" without first closing \"%s\".",
288 filename, adhost->adh_trail_name);
289 return (ADIST_ERROR_WRONG_ORDER);
292 if (!trail_validate_name(filename, NULL)) {
293 pjdlog_error("Sender wants to open file \"%s\", which has invalid name.",
295 return (ADIST_ERROR_INVALID_NAME);
298 switch (trail_name_compare(filename, adhost->adh_trail_name)) {
300 if (!trail_is_not_terminated(adhost->adh_trail_name)) {
301 pjdlog_error("Terminated trail \"%s/%s\" was unterminated on the sender as \"%s/%s\"?",
302 adhost->adh_directory, adhost->adh_trail_name,
303 adhost->adh_directory, filename);
304 return (ADIST_ERROR_INVALID_NAME);
306 if (renameat(adhost->adh_trail_dirfd, adhost->adh_trail_name,
307 adhost->adh_trail_dirfd, filename) == -1) {
308 pjdlog_errno(LOG_ERR,
309 "Unable to rename file \"%s/%s\" to \"%s/%s\"",
310 adhost->adh_directory, adhost->adh_trail_name,
311 adhost->adh_directory, filename);
312 PJDLOG_ASSERT(errno > 0);
313 return (ADIST_ERROR_RENAME);
315 pjdlog_debug(1, "Renamed file \"%s/%s\" to \"%s/%s\".",
316 adhost->adh_directory, adhost->adh_trail_name,
317 adhost->adh_directory, filename);
319 case TRAIL_IDENTICAL:
320 /* Opening existing file. */
321 fd = openat(adhost->adh_trail_dirfd, filename,
322 O_WRONLY | O_APPEND | O_NOFOLLOW);
324 pjdlog_errno(LOG_ERR,
325 "Unable to open file \"%s/%s\" for append",
326 adhost->adh_directory, filename);
327 PJDLOG_ASSERT(errno > 0);
328 return (ADIST_ERROR_OPEN);
330 pjdlog_debug(1, "Opened file \"%s/%s\".",
331 adhost->adh_directory, filename);
334 /* Opening new file. */
335 fd = openat(adhost->adh_trail_dirfd, filename,
336 O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW, 0600);
338 pjdlog_errno(LOG_ERR,
339 "Unable to create file \"%s/%s\"",
340 adhost->adh_directory, filename);
341 PJDLOG_ASSERT(errno > 0);
342 return (ADIST_ERROR_CREATE);
344 pjdlog_debug(1, "Created file \"%s/%s\".",
345 adhost->adh_directory, filename);
348 /* Trying to open old file. */
349 pjdlog_error("Sender wants to open an old file \"%s\".", filename);
350 return (ADIST_ERROR_OPEN_OLD);
352 PJDLOG_ABORT("Unknown return value from trail_name_compare().");
354 PJDLOG_VERIFY(strlcpy(adhost->adh_trail_name, filename,
355 sizeof(adhost->adh_trail_name)) < sizeof(adhost->adh_trail_name));
356 adhost->adh_trail_fd = fd;
361 * Function appends data to the trail file that is currently open.
364 receiver_append(const unsigned char *data, size_t size)
369 /* We should have opened trail file. */
370 if (adhost->adh_trail_fd == -1) {
371 pjdlog_error("Sender requested append without first opening file.");
372 return (ADIST_ERROR_WRONG_ORDER);
377 done = write(adhost->adh_trail_fd, data, size);
381 pjdlog_errno(LOG_ERR, "Write to \"%s/%s\" failed",
382 adhost->adh_directory, adhost->adh_trail_name);
383 PJDLOG_ASSERT(errno > 0);
384 return (ADIST_ERROR_WRITE);
386 pjdlog_debug(3, "Wrote %zd bytes into \"%s/%s\".", done,
387 adhost->adh_directory, adhost->adh_trail_name);
390 pjdlog_debug(2, "Appended %zu bytes to file \"%s/%s\".",
391 osize, adhost->adh_directory, adhost->adh_trail_name);
396 receiver_close(const char *filename)
399 /* We should have opened trail file. */
400 if (adhost->adh_trail_fd == -1) {
401 pjdlog_error("Sender requested closing file without first opening it.");
402 return (ADIST_ERROR_WRONG_ORDER);
405 /* Validate if we can do the rename. */
406 if (!trail_validate_name(adhost->adh_trail_name, filename)) {
407 pjdlog_error("Sender wants to close file \"%s\" using name \"%s\".",
408 adhost->adh_trail_name, filename);
409 return (ADIST_ERROR_INVALID_NAME);
412 PJDLOG_VERIFY(close(adhost->adh_trail_fd) == 0);
413 adhost->adh_trail_fd = -1;
415 pjdlog_debug(1, "Closed file \"%s/%s\".", adhost->adh_directory,
416 adhost->adh_trail_name);
418 if (strcmp(adhost->adh_trail_name, filename) == 0) {
419 /* File name didn't change, we are done here. */
423 if (renameat(adhost->adh_trail_dirfd, adhost->adh_trail_name,
424 adhost->adh_trail_dirfd, filename) == -1) {
425 pjdlog_errno(LOG_ERR, "Unable to rename \"%s\" to \"%s\"",
426 adhost->adh_trail_name, filename);
427 PJDLOG_ASSERT(errno > 0);
428 return (ADIST_ERROR_RENAME);
430 pjdlog_debug(1, "Renamed file \"%s/%s\" to \"%s/%s\".",
431 adhost->adh_directory, adhost->adh_trail_name,
432 adhost->adh_directory, filename);
433 PJDLOG_VERIFY(strlcpy(adhost->adh_trail_name, filename,
434 sizeof(adhost->adh_trail_name)) < sizeof(adhost->adh_trail_name));
443 /* We should have opened trail file. */
444 if (adhost->adh_trail_fd == -1) {
445 pjdlog_error("Sender send read error, but file is not open.");
446 return (ADIST_ERROR_WRONG_ORDER);
449 PJDLOG_VERIFY(close(adhost->adh_trail_fd) == 0);
450 adhost->adh_trail_fd = -1;
452 pjdlog_debug(1, "Closed file \"%s/%s\".", adhost->adh_directory,
453 adhost->adh_trail_name);
459 disk_thread(void *arg __unused)
464 pjdlog_debug(3, "disk: Taking request.");
465 QUEUE_TAKE(adreq, &adist_disk_list, 0);
466 adreq_log(LOG_DEBUG, 3, -1, adreq, "disk: (%p) Got request: ",
468 /* Handle the actual request. */
469 switch (adreq->adr_cmd) {
471 adreq->adr_error = receiver_open(adreq->adr_data);
473 case ADIST_CMD_APPEND:
474 adreq->adr_error = receiver_append(adreq->adr_data,
475 adreq->adr_datasize);
477 case ADIST_CMD_CLOSE:
478 adreq->adr_error = receiver_close(adreq->adr_data);
480 case ADIST_CMD_ERROR:
481 adreq->adr_error = receiver_error();
484 PJDLOG_ABORT("Unexpected command (cmd=%hhu).",
487 if (adreq->adr_error != 0) {
488 adreq_log(LOG_ERR, 0, adreq->adr_error, adreq,
491 pjdlog_debug(3, "disk: (%p) Moving request to the send queue.",
493 QUEUE_INSERT(adreq, &adist_send_list);
500 * Thread sends requests back to primary node.
503 send_thread(void *arg __unused)
509 pjdlog_debug(3, "send: Taking request.");
510 QUEUE_TAKE(adreq, &adist_send_list, 0);
511 adreq_log(LOG_DEBUG, 3, -1, adreq, "send: (%p) Got request: ",
513 adrep.adrp_byteorder = ADIST_BYTEORDER;
514 adrep.adrp_seq = adreq->adr_seq;
515 adrep.adrp_error = adreq->adr_error;
516 if (proto_send(adhost->adh_remote, &adrep,
517 sizeof(adrep)) == -1) {
518 pjdlog_exit(EX_TEMPFAIL, "Unable to send reply");
520 pjdlog_debug(3, "send: (%p) Moving request to the free queue.",
523 QUEUE_INSERT(adreq, &adist_free_list);
530 receiver_directory_create(void)
535 * According to getpwnam(3) we have to clear errno before calling the
536 * function to be able to distinguish between an error and missing
537 * entry (with is not treated as error by getpwnam(3)).
540 pw = getpwnam(ADIST_USER);
543 pjdlog_exit(EX_NOUSER,
544 "Unable to find info about '%s' user", ADIST_USER);
546 pjdlog_exitx(EX_NOUSER, "User '%s' doesn't exist.",
551 if (mkdir(adhost->adh_directory, 0700) == -1) {
552 pjdlog_exit(EX_OSFILE, "Unable to create directory \"%s\"",
553 adhost->adh_directory);
555 if (chown(adhost->adh_directory, pw->pw_uid, pw->pw_gid) == -1) {
556 pjdlog_errno(LOG_ERR,
557 "Unable to change owner of the directory \"%s\"",
558 adhost->adh_directory);
559 (void)rmdir(adhost->adh_directory);
565 receiver_directory_open(void)
568 #ifdef HAVE_FDOPENDIR
569 adhost->adh_trail_dirfd = open(adhost->adh_directory,
570 O_RDONLY | O_DIRECTORY);
571 if (adhost->adh_trail_dirfd == -1) {
572 if (errno == ENOENT) {
573 receiver_directory_create();
574 adhost->adh_trail_dirfd = open(adhost->adh_directory,
575 O_RDONLY | O_DIRECTORY);
577 if (adhost->adh_trail_dirfd == -1) {
578 pjdlog_exit(EX_CONFIG,
579 "Unable to open directory \"%s\"",
580 adhost->adh_directory);
583 adhost->adh_trail_dirfp = fdopendir(adhost->adh_trail_dirfd);
584 if (adhost->adh_trail_dirfp == NULL) {
585 pjdlog_exit(EX_CONFIG, "Unable to fdopen directory \"%s\"",
586 adhost->adh_directory);
591 if (stat(adhost->adh_directory, &sb) == -1) {
592 if (errno == ENOENT) {
593 receiver_directory_create();
595 pjdlog_exit(EX_CONFIG,
596 "Unable to stat directory \"%s\"",
597 adhost->adh_directory);
600 adhost->adh_trail_dirfp = opendir(adhost->adh_directory);
601 if (adhost->adh_trail_dirfp == NULL) {
602 pjdlog_exit(EX_CONFIG, "Unable to open directory \"%s\"",
603 adhost->adh_directory);
605 adhost->adh_trail_dirfd = dirfd(adhost->adh_trail_dirfp);
610 receiver_connect(void)
615 PJDLOG_ASSERT(adhost->adh_trail_dirfp != NULL);
617 trail_last(adhost->adh_trail_dirfp, adhost->adh_trail_name,
618 sizeof(adhost->adh_trail_name));
620 if (adhost->adh_trail_name[0] == '\0') {
623 if (fstatat(adhost->adh_trail_dirfd, adhost->adh_trail_name,
624 &sb, AT_SYMLINK_NOFOLLOW) == -1) {
625 pjdlog_exit(EX_CONFIG, "Unable to stat \"%s/%s\"",
626 adhost->adh_directory, adhost->adh_trail_name);
628 if (!S_ISREG(sb.st_mode)) {
629 pjdlog_exitx(EX_CONFIG,
630 "File \"%s/%s\" is not a regular file.",
631 adhost->adh_directory, adhost->adh_trail_name);
633 trail_size = sb.st_size;
635 trail_size = htole64(trail_size);
636 if (proto_send(adhost->adh_remote, &trail_size,
637 sizeof(trail_size)) == -1) {
638 pjdlog_exit(EX_TEMPFAIL,
639 "Unable to send size of the most recent trail file");
641 if (proto_send(adhost->adh_remote, adhost->adh_trail_name,
642 sizeof(adhost->adh_trail_name)) == -1) {
643 pjdlog_exit(EX_TEMPFAIL,
644 "Unable to send name of the most recent trail file");
649 adist_receiver(struct adist_config *config, struct adist_host *adh)
654 int error, mode, debuglevel;
658 pjdlog_errno(LOG_ERR, "Unable to fork");
659 proto_close(adh->adh_remote);
660 adh->adh_remote = NULL;
665 /* This is parent. */
666 proto_close(adh->adh_remote);
667 adh->adh_remote = NULL;
668 adh->adh_worker_pid = pid;
674 mode = pjdlog_mode_get();
675 debuglevel = pjdlog_debug_get();
677 descriptors_cleanup(adhost);
679 // descriptors_assert(adhost, mode);
682 pjdlog_debug_set(debuglevel);
683 pjdlog_prefix_set("[%s] (%s) ", adhost->adh_name,
684 role2str(adhost->adh_role));
685 #ifdef HAVE_SETPROCTITLE
686 setproctitle("%s (%s)", adhost->adh_name, role2str(adhost->adh_role));
689 PJDLOG_VERIFY(sigemptyset(&mask) == 0);
690 PJDLOG_VERIFY(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
692 /* Error in setting timeout is not critical, but why should it fail? */
693 if (proto_timeout(adhost->adh_remote, adcfg->adc_timeout) == -1)
694 pjdlog_errno(LOG_WARNING, "Unable to set connection timeout");
698 adhost->adh_trail_fd = -1;
699 receiver_directory_open();
701 if (sandbox(ADIST_USER, true, "auditdistd: %s (%s)",
702 role2str(adhost->adh_role), adhost->adh_name) != 0) {
705 pjdlog_info("Privileges successfully dropped.");
709 error = pthread_create(&td, NULL, recv_thread, adhost);
710 PJDLOG_ASSERT(error == 0);
711 error = pthread_create(&td, NULL, disk_thread, adhost);
712 PJDLOG_ASSERT(error == 0);
713 (void)send_thread(adhost);