1 /*-
2 * Copyright (c) 2012 The FreeBSD Foundation
3 * All rights reserved.
4 *
5 * This software was developed by Pawel Jakub Dawidek under sponsorship from
6 * the FreeBSD Foundation.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
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.
16 *
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
27 * SUCH DAMAGE.
28 */
29
30 #include <config/config.h>
31
32 #include <sys/param.h>
33 #include <sys/stat.h>
34
35 #include <dirent.h>
36 #include <errno.h>
37 #include <fcntl.h>
38 #include <stdbool.h>
39 #include <stdint.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
43
44 #include <compat/compat.h>
45 #ifndef HAVE_STRLCPY
46 #include <compat/strlcpy.h>
47 #endif
48 #ifndef HAVE_FACCESSAT
49 #include "faccessat.h"
50 #endif
51 #ifndef HAVE_FSTATAT
52 #include "fstatat.h"
53 #endif
54 #ifndef HAVE_OPENAT
55 #include "openat.h"
56 #endif
57 #ifndef HAVE_UNLINKAT
58 #include "unlinkat.h"
59 #endif
60
61 #include "pjdlog.h"
62 #include "trail.h"
63
64 #define TRAIL_MAGIC 0x79a11
65 struct trail {
66 int tr_magic;
67 /* Path usually to /var/audit/dist/ directory. */
68 char tr_dirname[PATH_MAX];
69 /* Descriptor to td_dirname directory. */
70 DIR *tr_dirfp;
71 /* Path to audit trail file. */
72 char tr_filename[PATH_MAX];
73 /* Descriptor to audit trail file. */
74 int tr_filefd;
75 };
76
77 #define HALF_LEN 14
78
79 bool
trail_is_not_terminated(const char * filename)80 trail_is_not_terminated(const char *filename)
81 {
82
83 return (strcmp(filename + HALF_LEN, ".not_terminated") == 0);
84 }
85
86 bool
trail_is_crash_recovery(const char * filename)87 trail_is_crash_recovery(const char *filename)
88 {
89
90 return (strcmp(filename + HALF_LEN, ".crash_recovery") == 0);
91 }
92
93 struct trail *
trail_new(const char * dirname,bool create)94 trail_new(const char *dirname, bool create)
95 {
96 struct trail *trail;
97
98 trail = calloc(1, sizeof(*trail));
99
100 if (strlcpy(trail->tr_dirname, dirname, sizeof(trail->tr_dirname)) >=
101 sizeof(trail->tr_dirname)) {
102 free(trail);
103 pjdlog_error("Directory name too long (\"%s\").", dirname);
104 errno = ENAMETOOLONG;
105 return (NULL);
106 }
107 trail->tr_dirfp = opendir(dirname);
108 if (trail->tr_dirfp == NULL) {
109 if (create && errno == ENOENT) {
110 if (mkdir(dirname, 0700) == -1) {
111 pjdlog_errno(LOG_ERR,
112 "Unable to create directory \"%s\"",
113 dirname);
114 free(trail);
115 return (NULL);
116 }
117 /* TODO: Set directory ownership. */
118 } else {
119 pjdlog_errno(LOG_ERR,
120 "Unable to open directory \"%s\"",
121 dirname);
122 free(trail);
123 return (NULL);
124 }
125 trail->tr_dirfp = opendir(dirname);
126 if (trail->tr_dirfp == NULL) {
127 pjdlog_errno(LOG_ERR,
128 "Unable to open directory \"%s\"",
129 dirname);
130 free(trail);
131 return (NULL);
132 }
133 }
134 trail->tr_filefd = -1;
135 trail->tr_magic = TRAIL_MAGIC;
136 return (trail);
137 }
138
139 void
trail_free(struct trail * trail)140 trail_free(struct trail *trail)
141 {
142
143 PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
144
145 if (trail->tr_filefd != -1)
146 trail_close(trail);
147 closedir(trail->tr_dirfp);
148 bzero(trail, sizeof(*trail));
149 trail->tr_magic = 0;
150 trail->tr_filefd = -1;
151 free(trail);
152 }
153
154 static uint8_t
trail_type(DIR * dirfp,const char * filename)155 trail_type(DIR *dirfp, const char *filename)
156 {
157 struct stat sb;
158 int dfd;
159
160 PJDLOG_ASSERT(dirfp != NULL);
161
162 dfd = dirfd(dirfp);
163 PJDLOG_ASSERT(dfd >= 0);
164 if (fstatat(dfd, filename, &sb, AT_SYMLINK_NOFOLLOW) == -1) {
165 pjdlog_errno(LOG_ERR, "Unable to stat \"%s\"", filename);
166 return (DT_UNKNOWN);
167 }
168 return (IFTODT(sb.st_mode));
169 }
170
171 /*
172 * Find trail file by first part of the name in case it was renamed.
173 * First part of the trail file name never changes, but trail file
174 * can be renamed when hosts are disconnected from .not_terminated
175 * to .[0-9]{14} or to .crash_recovery.
176 */
177 static bool
trail_find(struct trail * trail)178 trail_find(struct trail *trail)
179 {
180 struct dirent *dp;
181
182 PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
183 PJDLOG_ASSERT(trail_is_not_terminated(trail->tr_filename));
184
185 rewinddir(trail->tr_dirfp);
186 while ((dp = readdir(trail->tr_dirfp)) != NULL) {
187 if (strncmp(dp->d_name, trail->tr_filename, HALF_LEN + 1) == 0)
188 break;
189 }
190 if (dp == NULL)
191 return (false);
192 PJDLOG_VERIFY(strlcpy(trail->tr_filename, dp->d_name,
193 sizeof(trail->tr_filename)) < sizeof(trail->tr_filename));
194 return (true);
195 }
196
197 /*
198 * Open the given trail file and move pointer at the given offset, as this is
199 * where receiver finished the last time.
200 * If the file doesn't exist or the given offset is equal to the file size,
201 * move to the next trail file.
202 */
203 void
trail_start(struct trail * trail,const char * filename,off_t offset)204 trail_start(struct trail *trail, const char *filename, off_t offset)
205 {
206 struct stat sb;
207 int dfd, fd;
208
209 PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
210
211 PJDLOG_VERIFY(strlcpy(trail->tr_filename, filename,
212 sizeof(trail->tr_filename)) < sizeof(trail->tr_filename));
213 trail->tr_filefd = -1;
214
215 if (trail->tr_filename[0] == '\0') {
216 PJDLOG_ASSERT(offset == 0);
217 trail_next(trail);
218 return;
219 }
220
221 dfd = dirfd(trail->tr_dirfp);
222 PJDLOG_ASSERT(dfd >= 0);
223 again:
224 fd = openat(dfd, trail->tr_filename, O_RDONLY);
225 if (fd == -1) {
226 if (errno == ENOENT &&
227 trail_is_not_terminated(trail->tr_filename) &&
228 trail_find(trail)) {
229 /* File was renamed. Retry with new name. */
230 pjdlog_debug(1,
231 "Trail file was renamed since last connection to \"%s/%s\".",
232 trail->tr_dirname, trail->tr_filename);
233 goto again;
234 } else if (errno == ENOENT) {
235 /* File disappeared. */
236 pjdlog_debug(1, "File \"%s/%s\" doesn't exist.",
237 trail->tr_dirname, trail->tr_filename);
238 } else {
239 pjdlog_errno(LOG_ERR,
240 "Unable to open file \"%s/%s\", skipping",
241 trail->tr_dirname, trail->tr_filename);
242 }
243 trail_next(trail);
244 return;
245 }
246 if (fstat(fd, &sb) == -1) {
247 pjdlog_errno(LOG_ERR,
248 "Unable to stat file \"%s/%s\", skipping",
249 trail->tr_dirname, trail->tr_filename);
250 close(fd);
251 trail_next(trail);
252 return;
253 }
254 if (!S_ISREG(sb.st_mode)) {
255 pjdlog_warning("File \"%s/%s\" is not a regular file, skipping.",
256 trail->tr_dirname, trail->tr_filename);
257 close(fd);
258 trail_next(trail);
259 return;
260 }
261 /*
262 * We continue sending requested file if:
263 * 1. It is not fully sent yet, or
264 * 2. It is fully sent, but is not terminated, so new data can be
265 * appended still, or
266 * 3. It is fully sent but file name has changed.
267 * There are two cases here:
268 * 3a. Sender has crashed and the name has changed from
269 * .not_terminated to .crash_recovery.
270 * 3b. Sender was disconnected, no new data was added to the file,
271 * but its name has changed from .not_terminated to terminated
272 * name.
273 *
274 * Note that we are fine if our .not_terminated or .crash_recovery file
275 * is smaller than the one on the receiver side, as it is possible that
276 * more data was send to the receiver than was safely stored on disk.
277 * We accept .not_terminated only because auditdistd can start before
278 * auditd manage to rename it to .crash_recovery.
279 */
280 if (offset < sb.st_size ||
281 (offset >= sb.st_size &&
282 trail_is_not_terminated(trail->tr_filename)) ||
283 (offset >= sb.st_size && trail_is_not_terminated(filename) &&
284 !trail_is_not_terminated(trail->tr_filename))) {
285 /* File was not fully send. Let's finish it. */
286 if (lseek(fd, offset, SEEK_SET) == -1) {
287 pjdlog_errno(LOG_ERR,
288 "Unable to move to offset %jd within file \"%s/%s\", skipping",
289 (intmax_t)offset, trail->tr_dirname,
290 trail->tr_filename);
291 close(fd);
292 trail_next(trail);
293 return;
294 }
295 if (!trail_is_crash_recovery(trail->tr_filename)) {
296 pjdlog_debug(1,
297 "Restarting file \"%s/%s\" at offset %jd.",
298 trail->tr_dirname, trail->tr_filename,
299 (intmax_t)offset);
300 }
301 trail->tr_filefd = fd;
302 return;
303 }
304 close(fd);
305 if (offset > sb.st_size) {
306 pjdlog_warning("File \"%s/%s\" shrinked, removing it.",
307 trail->tr_dirname, trail->tr_filename);
308 } else {
309 pjdlog_debug(1, "File \"%s/%s\" is already sent, removing it.",
310 trail->tr_dirname, trail->tr_filename);
311 }
312 /* Entire file is already sent or it shirnked, we can remove it. */
313 if (unlinkat(dfd, trail->tr_filename, 0) == -1) {
314 pjdlog_errno(LOG_WARNING, "Unable to remove file \"%s/%s\"",
315 trail->tr_dirname, trail->tr_filename);
316 }
317 trail_next(trail);
318 }
319
320 /*
321 * Set next file in the trail->tr_dirname directory and open it for reading.
322 */
323 void
trail_next(struct trail * trail)324 trail_next(struct trail *trail)
325 {
326 char curfile[PATH_MAX];
327 struct dirent *dp;
328 int dfd;
329
330 PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
331 PJDLOG_ASSERT(trail->tr_filefd == -1);
332
333 again:
334 curfile[0] = '\0';
335
336 rewinddir(trail->tr_dirfp);
337 while ((dp = readdir(trail->tr_dirfp)) != NULL) {
338 if (dp->d_name[0] < '0' || dp->d_name[0] > '9')
339 continue;
340 if (dp->d_type == DT_UNKNOWN)
341 dp->d_type = trail_type(trail->tr_dirfp, dp->d_name);
342 /* We are only interested in regular files, skip the rest. */
343 if (dp->d_type != DT_REG) {
344 pjdlog_debug(1,
345 "File \"%s/%s\" is not a regular file, skipping.",
346 trail->tr_dirname, dp->d_name);
347 continue;
348 }
349 /* Skip all files "greater" than curfile. */
350 if (curfile[0] != '\0' && strcmp(dp->d_name, curfile) > 0)
351 continue;
352 /* Skip all files "smaller" than the current trail_filename. */
353 if (trail->tr_filename[0] != '\0' &&
354 strcmp(dp->d_name, trail->tr_filename) <= 0) {
355 continue;
356 }
357 PJDLOG_VERIFY(strlcpy(curfile, dp->d_name, sizeof(curfile)) <
358 sizeof(curfile));
359 }
360 if (curfile[0] == '\0') {
361 /*
362 * There are no new trail files, so we return.
363 * We don't clear trail_filename string, to know where to
364 * start when new file appears.
365 */
366 PJDLOG_ASSERT(trail->tr_filefd == -1);
367 pjdlog_debug(1, "No new trail files.");
368 return;
369 }
370 dfd = dirfd(trail->tr_dirfp);
371 PJDLOG_ASSERT(dfd >= 0);
372 trail->tr_filefd = openat(dfd, curfile, O_RDONLY);
373 if (trail->tr_filefd == -1) {
374 if (errno == ENOENT && trail_is_not_terminated(curfile)) {
375 /*
376 * The .not_terminated file was most likely renamed.
377 * Keep trail->tr_filename as a starting point and
378 * search again.
379 */
380 pjdlog_debug(1,
381 "Unable to open \"%s/%s\", most likely renamed in the meantime, retrying.",
382 trail->tr_dirname, curfile);
383 } else {
384 /*
385 * We were unable to open the file, but not because of
386 * the above. This shouldn't happen, but it did.
387 * We don't know why it happen, so the best we can do
388 * is to just skip this file - this is why we copy the
389 * name, so we can start and the next entry.
390 */
391 PJDLOG_VERIFY(strlcpy(trail->tr_filename, curfile,
392 sizeof(trail->tr_filename)) <
393 sizeof(trail->tr_filename));
394 pjdlog_errno(LOG_ERR,
395 "Unable to open file \"%s/%s\", skipping",
396 trail->tr_dirname, curfile);
397 }
398 goto again;
399 }
400 PJDLOG_VERIFY(strlcpy(trail->tr_filename, curfile,
401 sizeof(trail->tr_filename)) < sizeof(trail->tr_filename));
402 pjdlog_debug(1, "Found next trail file: \"%s/%s\".", trail->tr_dirname,
403 trail->tr_filename);
404 }
405
406 /*
407 * Close current trial file.
408 */
409 void
trail_close(struct trail * trail)410 trail_close(struct trail *trail)
411 {
412
413 PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
414 PJDLOG_ASSERT(trail->tr_filefd >= 0);
415 PJDLOG_ASSERT(trail->tr_filename[0] != '\0');
416
417 PJDLOG_VERIFY(close(trail->tr_filefd) == 0);
418 trail->tr_filefd = -1;
419 }
420
421 /*
422 * Reset trail state. Used when connection is disconnected and we will
423 * need to start over after reconnect. Trail needs to be already closed.
424 */
425 void
trail_reset(struct trail * trail)426 trail_reset(struct trail *trail)
427 {
428
429 PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
430 PJDLOG_ASSERT(trail->tr_filefd == -1);
431
432 trail->tr_filename[0] = '\0';
433 }
434
435 /*
436 * Unlink current trial file.
437 */
438 void
trail_unlink(struct trail * trail,const char * filename)439 trail_unlink(struct trail *trail, const char *filename)
440 {
441 int dfd;
442
443 PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
444 PJDLOG_ASSERT(filename != NULL);
445 PJDLOG_ASSERT(filename[0] != '\0');
446
447 dfd = dirfd(trail->tr_dirfp);
448 PJDLOG_ASSERT(dfd >= 0);
449
450 if (unlinkat(dfd, filename, 0) == -1) {
451 pjdlog_errno(LOG_ERR, "Unable to remove \"%s/%s\"",
452 trail->tr_dirname, filename);
453 } else {
454 pjdlog_debug(1, "Trail file \"%s/%s\" removed.",
455 trail->tr_dirname, filename);
456 }
457 }
458
459 /*
460 * Return true if we should switch to next trail file.
461 * We don't switch if our file name ends with ".not_terminated" and it
462 * exists (ie. wasn't renamed).
463 */
464 bool
trail_switch(struct trail * trail)465 trail_switch(struct trail *trail)
466 {
467 char filename[PATH_MAX];
468 int fd;
469
470 PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
471 PJDLOG_ASSERT(trail->tr_filefd >= 0);
472
473 if (!trail_is_not_terminated(trail->tr_filename))
474 return (true);
475 fd = dirfd(trail->tr_dirfp);
476 PJDLOG_ASSERT(fd >= 0);
477 if (faccessat(fd, trail->tr_filename, F_OK, 0) == 0)
478 return (false);
479 if (errno != ENOENT) {
480 pjdlog_errno(LOG_ERR, "Unable to access file \"%s/%s\"",
481 trail->tr_dirname, trail->tr_filename);
482 }
483 strlcpy(filename, trail->tr_filename, sizeof(filename));
484 if (!trail_find(trail)) {
485 pjdlog_error("Trail file \"%s/%s\" disappeared.",
486 trail->tr_dirname, trail->tr_filename);
487 return (true);
488 }
489 pjdlog_debug(1, "Trail file \"%s/%s\" was renamed to \"%s/%s\".",
490 trail->tr_dirname, filename, trail->tr_dirname,
491 trail->tr_filename);
492 return (true);
493 }
494
495 const char *
trail_filename(const struct trail * trail)496 trail_filename(const struct trail *trail)
497 {
498
499 PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
500
501 return (trail->tr_filename);
502 }
503
504 int
trail_filefd(const struct trail * trail)505 trail_filefd(const struct trail *trail)
506 {
507
508 PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
509
510 return (trail->tr_filefd);
511 }
512
513 int
trail_dirfd(const struct trail * trail)514 trail_dirfd(const struct trail *trail)
515 {
516
517 PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
518
519 return (dirfd(trail->tr_dirfp));
520 }
521
522 /*
523 * Find the last file in the directory opened under dirfp.
524 */
525 void
trail_last(DIR * dirfp,char * filename,size_t filenamesize)526 trail_last(DIR *dirfp, char *filename, size_t filenamesize)
527 {
528 char curfile[PATH_MAX];
529 struct dirent *dp;
530
531 PJDLOG_ASSERT(dirfp != NULL);
532
533 curfile[0] = '\0';
534
535 rewinddir(dirfp);
536 while ((dp = readdir(dirfp)) != NULL) {
537 if (dp->d_name[0] < '0' || dp->d_name[0] > '9')
538 continue;
539 if (dp->d_type == DT_UNKNOWN)
540 dp->d_type = trail_type(dirfp, dp->d_name);
541 /* We are only interested in regular files, skip the rest. */
542 if (dp->d_type != DT_REG)
543 continue;
544 /* Skip all files "greater" than curfile. */
545 if (curfile[0] != '\0' && strcmp(dp->d_name, curfile) < 0)
546 continue;
547 PJDLOG_VERIFY(strlcpy(curfile, dp->d_name, sizeof(curfile)) <
548 sizeof(curfile));
549 }
550 if (curfile[0] == '\0') {
551 /*
552 * There are no trail files, so we return.
553 */
554 pjdlog_debug(1, "No trail files.");
555 bzero(filename, filenamesize);
556 return;
557 }
558 PJDLOG_VERIFY(strlcpy(filename, curfile, filenamesize) < filenamesize);
559 pjdlog_debug(1, "Found the most recent trail file: \"%s\".", filename);
560 }
561
562 /*
563 * Check if the given file name is a valid audit trail file name.
564 * Possible names:
565 * 20120106132657.20120106132805
566 * 20120106132657.not_terminated
567 * 20120106132657.crash_recovery
568 * If two names are given, check if the first name can be renamed
569 * to the second name. When renaming, first part of the name has
570 * to be identical and only the following renames are valid:
571 * 20120106132657.not_terminated -> 20120106132657.20120106132805
572 * 20120106132657.not_terminated -> 20120106132657.crash_recovery
573 */
574 bool
trail_validate_name(const char * srcname,const char * dstname)575 trail_validate_name(const char *srcname, const char *dstname)
576 {
577 int i;
578
579 PJDLOG_ASSERT(srcname != NULL);
580
581 if (strlen(srcname) != 2 * HALF_LEN + 1)
582 return (false);
583 if (srcname[HALF_LEN] != '.')
584 return (false);
585 for (i = 0; i < HALF_LEN; i++) {
586 if (srcname[i] < '0' || srcname[i] > '9')
587 return (false);
588 }
589 for (i = HALF_LEN + 1; i < 2 * HALF_LEN - 1; i++) {
590 if (srcname[i] < '0' || srcname[i] > '9')
591 break;
592 }
593 if (i < 2 * HALF_LEN - 1 &&
594 strcmp(srcname + HALF_LEN + 1, "not_terminated") != 0 &&
595 strcmp(srcname + HALF_LEN + 1, "crash_recovery") != 0) {
596 return (false);
597 }
598
599 if (dstname == NULL)
600 return (true);
601
602 /* We tolarate if both names are identical. */
603 if (strcmp(srcname, dstname) == 0)
604 return (true);
605
606 /* We can only rename not_terminated files. */
607 if (strcmp(srcname + HALF_LEN + 1, "not_terminated") != 0)
608 return (false);
609 if (strlen(dstname) != 2 * HALF_LEN + 1)
610 return (false);
611 if (strncmp(srcname, dstname, HALF_LEN + 1) != 0)
612 return (false);
613 for (i = HALF_LEN + 1; i < 2 * HALF_LEN - 1; i++) {
614 if (dstname[i] < '0' || dstname[i] > '9')
615 break;
616 }
617 if (i < 2 * HALF_LEN - 1 &&
618 strcmp(dstname + HALF_LEN + 1, "crash_recovery") != 0) {
619 return (false);
620 }
621
622 return (true);
623 }
624
625 int
trail_name_compare(const char * name0,const char * name1)626 trail_name_compare(const char *name0, const char *name1)
627 {
628 int ret;
629
630 ret = strcmp(name0, name1);
631 if (ret == 0)
632 return (TRAIL_IDENTICAL);
633 if (strncmp(name0, name1, HALF_LEN + 1) == 0)
634 return (TRAIL_RENAMED);
635 return (ret < 0 ? TRAIL_OLDER : TRAIL_NEWER);
636 }
637