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/trail.c#3 $
32 #include <config/config.h>
34 #include <sys/param.h>
46 #include <compat/compat.h>
48 #include <compat/strlcpy.h>
50 #ifndef HAVE_FACCESSAT
51 #include "faccessat.h"
66 #define TRAIL_MAGIC 0x79a11
69 /* Path usually to /var/audit/dist/ directory. */
70 char tr_dirname[PATH_MAX];
71 /* Descriptor to td_dirname directory. */
73 /* Path to audit trail file. */
74 char tr_filename[PATH_MAX];
75 /* Descriptor to audit trail file. */
82 trail_is_not_terminated(const char *filename)
85 return (strcmp(filename + HALF_LEN, ".not_terminated") == 0);
89 trail_is_crash_recovery(const char *filename)
92 return (strcmp(filename + HALF_LEN, ".crash_recovery") == 0);
96 trail_new(const char *dirname, bool create)
100 trail = calloc(1, sizeof(*trail));
102 if (strlcpy(trail->tr_dirname, dirname, sizeof(trail->tr_dirname)) >=
103 sizeof(trail->tr_dirname)) {
105 pjdlog_error("Directory name too long (\"%s\").", dirname);
106 errno = ENAMETOOLONG;
109 trail->tr_dirfp = opendir(dirname);
110 if (trail->tr_dirfp == NULL) {
111 if (create && errno == ENOENT) {
112 if (mkdir(dirname, 0700) == -1) {
113 pjdlog_errno(LOG_ERR,
114 "Unable to create directory \"%s\"",
119 /* TODO: Set directory ownership. */
121 pjdlog_errno(LOG_ERR,
122 "Unable to open directory \"%s\"",
127 trail->tr_dirfp = opendir(dirname);
128 if (trail->tr_dirfp == NULL) {
129 pjdlog_errno(LOG_ERR,
130 "Unable to open directory \"%s\"",
136 trail->tr_filefd = -1;
137 trail->tr_magic = TRAIL_MAGIC;
142 trail_free(struct trail *trail)
145 PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
147 if (trail->tr_filefd != -1)
149 closedir(trail->tr_dirfp);
150 bzero(trail, sizeof(*trail));
152 trail->tr_filefd = -1;
157 trail_type(DIR *dirfp, const char *filename)
162 PJDLOG_ASSERT(dirfp != NULL);
165 PJDLOG_ASSERT(dfd >= 0);
166 if (fstatat(dfd, filename, &sb, AT_SYMLINK_NOFOLLOW) == -1) {
167 pjdlog_errno(LOG_ERR, "Unable to stat \"%s\"", filename);
170 return (IFTODT(sb.st_mode));
174 * Find trail file by first part of the name in case it was renamed.
175 * First part of the trail file name never changes, but trail file
176 * can be renamed when hosts are disconnected from .not_terminated
177 * to .[0-9]{14} or to .crash_recovery.
180 trail_find(struct trail *trail)
184 PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
185 PJDLOG_ASSERT(trail_is_not_terminated(trail->tr_filename));
187 rewinddir(trail->tr_dirfp);
188 while ((dp = readdir(trail->tr_dirfp)) != NULL) {
189 if (strncmp(dp->d_name, trail->tr_filename, HALF_LEN + 1) == 0)
194 PJDLOG_VERIFY(strlcpy(trail->tr_filename, dp->d_name,
195 sizeof(trail->tr_filename)) < sizeof(trail->tr_filename));
200 * Open the given trail file and move pointer at the given offset, as this is
201 * where receiver finished the last time.
202 * If the file doesn't exist or the given offset is equal to the file size,
203 * move to the next trail file.
206 trail_start(struct trail *trail, const char *filename, off_t offset)
211 PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
213 PJDLOG_VERIFY(strlcpy(trail->tr_filename, filename,
214 sizeof(trail->tr_filename)) < sizeof(trail->tr_filename));
215 trail->tr_filefd = -1;
217 if (trail->tr_filename[0] == '\0') {
218 PJDLOG_ASSERT(offset == 0);
223 dfd = dirfd(trail->tr_dirfp);
224 PJDLOG_ASSERT(dfd >= 0);
226 fd = openat(dfd, trail->tr_filename, O_RDONLY);
228 if (errno == ENOENT &&
229 trail_is_not_terminated(trail->tr_filename) &&
231 /* File was renamed. Retry with new name. */
233 "Trail file was renamed since last connection to \"%s/%s\".",
234 trail->tr_dirname, trail->tr_filename);
236 } else if (errno == ENOENT) {
237 /* File disappeared. */
238 pjdlog_debug(1, "File \"%s/%s\" doesn't exist.",
239 trail->tr_dirname, trail->tr_filename);
241 pjdlog_errno(LOG_ERR,
242 "Unable to open file \"%s/%s\", skipping",
243 trail->tr_dirname, trail->tr_filename);
248 if (fstat(fd, &sb) == -1) {
249 pjdlog_errno(LOG_ERR,
250 "Unable to stat file \"%s/%s\", skipping",
251 trail->tr_dirname, trail->tr_filename);
256 if (!S_ISREG(sb.st_mode)) {
257 pjdlog_warning("File \"%s/%s\" is not a regular file, skipping.",
258 trail->tr_dirname, trail->tr_filename);
264 * We continue sending requested file if:
265 * 1. It is not fully sent yet, or
266 * 2. It is fully sent, but is not terminated, so new data can be
268 * 3. It is fully sent but file name has changed.
270 * Note that we are fine if our .not_terminated or .crash_recovery file
271 * is smaller than the one on the receiver side, as it is possible that
272 * more data was send to the receiver than was safely stored on disk.
273 * We accept .not_terminated only because auditdistd can start before
274 * auditd manage to rename it to .crash_recovery.
276 if (offset < sb.st_size ||
277 (offset >= sb.st_size &&
278 trail_is_not_terminated(trail->tr_filename)) ||
279 (offset >= sb.st_size && trail_is_not_terminated(filename) &&
280 trail_is_crash_recovery(trail->tr_filename))) {
281 /* File was not fully send. Let's finish it. */
282 if (lseek(fd, offset, SEEK_SET) == -1) {
283 pjdlog_errno(LOG_ERR,
284 "Unable to move to offset %jd within file \"%s/%s\", skipping",
285 (intmax_t)offset, trail->tr_dirname,
291 if (!trail_is_crash_recovery(trail->tr_filename)) {
293 "Restarting file \"%s/%s\" at offset %jd.",
294 trail->tr_dirname, trail->tr_filename,
297 trail->tr_filefd = fd;
301 if (offset > sb.st_size) {
302 pjdlog_warning("File \"%s/%s\" shrinked, removing it.",
303 trail->tr_dirname, trail->tr_filename);
305 pjdlog_debug(1, "File \"%s/%s\" is already sent, removing it.",
306 trail->tr_dirname, trail->tr_filename);
308 /* Entire file is already sent or it shirnked, we can remove it. */
309 if (unlinkat(dfd, trail->tr_filename, 0) == -1) {
310 pjdlog_errno(LOG_WARNING, "Unable to remove file \"%s/%s\"",
311 trail->tr_dirname, trail->tr_filename);
317 * Set next file in the trail->tr_dirname directory and open it for reading.
320 trail_next(struct trail *trail)
322 char curfile[PATH_MAX];
326 PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
327 PJDLOG_ASSERT(trail->tr_filefd == -1);
332 rewinddir(trail->tr_dirfp);
333 while ((dp = readdir(trail->tr_dirfp)) != NULL) {
334 if (dp->d_name[0] < '0' || dp->d_name[0] > '9')
336 if (dp->d_type == DT_UNKNOWN)
337 dp->d_type = trail_type(trail->tr_dirfp, dp->d_name);
338 /* We are only interested in regular files, skip the rest. */
339 if (dp->d_type != DT_REG) {
341 "File \"%s/%s\" is not a regular file, skipping.",
342 trail->tr_dirname, dp->d_name);
345 /* Skip all files "greater" than curfile. */
346 if (curfile[0] != '\0' && strcmp(dp->d_name, curfile) > 0)
348 /* Skip all files "smaller" than the current trail_filename. */
349 if (trail->tr_filename[0] != '\0' &&
350 strcmp(dp->d_name, trail->tr_filename) <= 0) {
353 PJDLOG_VERIFY(strlcpy(curfile, dp->d_name, sizeof(curfile)) <
356 if (curfile[0] == '\0') {
358 * There are no new trail files, so we return.
359 * We don't clear trail_filename string, to know where to
360 * start when new file appears.
362 PJDLOG_ASSERT(trail->tr_filefd == -1);
363 pjdlog_debug(1, "No new trail files.");
366 PJDLOG_VERIFY(strlcpy(trail->tr_filename, curfile,
367 sizeof(trail->tr_filename)) < sizeof(trail->tr_filename));
368 dfd = dirfd(trail->tr_dirfp);
369 PJDLOG_ASSERT(dfd >= 0);
370 trail->tr_filefd = openat(dfd, trail->tr_filename, O_RDONLY);
371 if (trail->tr_filefd == -1) {
372 pjdlog_errno(LOG_ERR,
373 "Unable to open file \"%s/%s\", skipping",
374 trail->tr_dirname, trail->tr_filename);
377 pjdlog_debug(1, "Found next trail file: \"%s/%s\".", trail->tr_dirname,
382 * Close current trial file.
385 trail_close(struct trail *trail)
388 PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
389 PJDLOG_ASSERT(trail->tr_filefd >= 0);
390 PJDLOG_ASSERT(trail->tr_filename[0] != '\0');
392 PJDLOG_VERIFY(close(trail->tr_filefd) == 0);
393 trail->tr_filefd = -1;
397 * Reset trail state. Used when connection is disconnected and we will
398 * need to start over after reconnect. Trail needs to be already closed.
401 trail_reset(struct trail *trail)
404 PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
405 PJDLOG_ASSERT(trail->tr_filefd == -1);
407 trail->tr_filename[0] = '\0';
411 * Unlink current trial file.
414 trail_unlink(struct trail *trail, const char *filename)
418 PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
419 PJDLOG_ASSERT(filename != NULL);
420 PJDLOG_ASSERT(filename[0] != '\0');
422 dfd = dirfd(trail->tr_dirfp);
423 PJDLOG_ASSERT(dfd >= 0);
425 if (unlinkat(dfd, filename, 0) == -1) {
426 pjdlog_errno(LOG_ERR, "Unable to remove \"%s/%s\"",
427 trail->tr_dirname, filename);
429 pjdlog_debug(1, "Trail file \"%s/%s\" removed.",
430 trail->tr_dirname, filename);
435 * Return true if we should switch to next trail file.
436 * We don't switch if our file name ends with ".not_terminated" and it
437 * exists (ie. wasn't renamed).
440 trail_switch(struct trail *trail)
442 char filename[PATH_MAX];
445 PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
446 PJDLOG_ASSERT(trail->tr_filefd >= 0);
448 if (!trail_is_not_terminated(trail->tr_filename))
450 fd = dirfd(trail->tr_dirfp);
451 PJDLOG_ASSERT(fd >= 0);
452 if (faccessat(fd, trail->tr_filename, F_OK, 0) == 0)
454 if (errno != ENOENT) {
455 pjdlog_errno(LOG_ERR, "Unable to access file \"%s/%s\"",
456 trail->tr_dirname, trail->tr_filename);
458 strlcpy(filename, trail->tr_filename, sizeof(filename));
459 if (!trail_find(trail)) {
460 pjdlog_error("Trail file \"%s/%s\" disappeared.",
461 trail->tr_dirname, trail->tr_filename);
464 pjdlog_debug(1, "Trail file \"%s/%s\" was renamed to \"%s/%s\".",
465 trail->tr_dirname, filename, trail->tr_dirname,
471 trail_filename(const struct trail *trail)
474 PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
476 return (trail->tr_filename);
480 trail_filefd(const struct trail *trail)
483 PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
485 return (trail->tr_filefd);
489 trail_dirfd(const struct trail *trail)
492 PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
494 return (dirfd(trail->tr_dirfp));
498 * Find the last file in the directory opened under dirfp.
501 trail_last(DIR *dirfp, char *filename, size_t filenamesize)
503 char curfile[PATH_MAX];
506 PJDLOG_ASSERT(dirfp != NULL);
511 while ((dp = readdir(dirfp)) != NULL) {
512 if (dp->d_name[0] < '0' || dp->d_name[0] > '9')
514 if (dp->d_type == DT_UNKNOWN)
515 dp->d_type = trail_type(dirfp, dp->d_name);
516 /* We are only interested in regular files, skip the rest. */
517 if (dp->d_type != DT_REG)
519 /* Skip all files "greater" than curfile. */
520 if (curfile[0] != '\0' && strcmp(dp->d_name, curfile) < 0)
522 PJDLOG_VERIFY(strlcpy(curfile, dp->d_name, sizeof(curfile)) <
525 if (curfile[0] == '\0') {
527 * There are no trail files, so we return.
529 pjdlog_debug(1, "No trail files.");
530 bzero(filename, filenamesize);
533 PJDLOG_VERIFY(strlcpy(filename, curfile, filenamesize) < filenamesize);
534 pjdlog_debug(1, "Found the most recent trail file: \"%s\".", filename);
538 * Check if the given file name is a valid audit trail file name.
540 * 20120106132657.20120106132805
541 * 20120106132657.not_terminated
542 * 20120106132657.crash_recovery
543 * If two names are given, check if the first name can be renamed
544 * to the second name. When renaming, first part of the name has
545 * to be identical and only the following renames are valid:
546 * 20120106132657.not_terminated -> 20120106132657.20120106132805
547 * 20120106132657.not_terminated -> 20120106132657.crash_recovery
550 trail_validate_name(const char *srcname, const char *dstname)
554 PJDLOG_ASSERT(srcname != NULL);
556 if (strlen(srcname) != 2 * HALF_LEN + 1)
558 if (srcname[HALF_LEN] != '.')
560 for (i = 0; i < HALF_LEN; i++) {
561 if (srcname[i] < '0' || srcname[i] > '9')
564 for (i = HALF_LEN + 1; i < 2 * HALF_LEN - 1; i++) {
565 if (srcname[i] < '0' || srcname[i] > '9')
568 if (i < 2 * HALF_LEN - 1 &&
569 strcmp(srcname + HALF_LEN + 1, "not_terminated") != 0 &&
570 strcmp(srcname + HALF_LEN + 1, "crash_recovery") != 0) {
577 /* We tolarate if both names are identical. */
578 if (strcmp(srcname, dstname) == 0)
581 /* We can only rename not_terminated files. */
582 if (strcmp(srcname + HALF_LEN + 1, "not_terminated") != 0)
584 if (strlen(dstname) != 2 * HALF_LEN + 1)
586 if (strncmp(srcname, dstname, HALF_LEN + 1) != 0)
588 for (i = HALF_LEN + 1; i < 2 * HALF_LEN - 1; i++) {
589 if (dstname[i] < '0' || dstname[i] > '9')
592 if (i < 2 * HALF_LEN - 1 &&
593 strcmp(dstname + HALF_LEN + 1, "crash_recovery") != 0) {
601 trail_name_compare(const char *name0, const char *name1)
605 ret = strcmp(name0, name1);
607 return (TRAIL_IDENTICAL);
608 if (strncmp(name0, name1, HALF_LEN + 1) == 0)
609 return (TRAIL_RENAMED);
610 return (ret < 0 ? TRAIL_OLDER : TRAIL_NEWER);