]> CyberLeo.Net >> Repos - FreeBSD/releng/10.3.git/blob - contrib/openbsm/bin/auditdistd/trail.c
- Copy stable/10@296371 to releng/10.3 in preparation for 10.3-RC1
[FreeBSD/releng/10.3.git] / contrib / openbsm / bin / auditdistd / trail.c
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
80 trail_is_not_terminated(const char *filename)
81 {
82
83         return (strcmp(filename + HALF_LEN, ".not_terminated") == 0);
84 }
85
86 bool
87 trail_is_crash_recovery(const char *filename)
88 {
89
90         return (strcmp(filename + HALF_LEN, ".crash_recovery") == 0);
91 }
92
93 struct trail *
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
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
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
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
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          *
268          * Note that we are fine if our .not_terminated or .crash_recovery file
269          * is smaller than the one on the receiver side, as it is possible that
270          * more data was send to the receiver than was safely stored on disk.
271          * We accept .not_terminated only because auditdistd can start before
272          * auditd manage to rename it to .crash_recovery.
273          */
274         if (offset < sb.st_size ||
275             (offset >= sb.st_size &&
276              trail_is_not_terminated(trail->tr_filename)) ||
277             (offset >= sb.st_size && trail_is_not_terminated(filename) &&
278              trail_is_crash_recovery(trail->tr_filename))) {
279                 /* File was not fully send. Let's finish it. */
280                 if (lseek(fd, offset, SEEK_SET) == -1) {
281                         pjdlog_errno(LOG_ERR,
282                             "Unable to move to offset %jd within file \"%s/%s\", skipping",
283                             (intmax_t)offset, trail->tr_dirname,
284                             trail->tr_filename);
285                         close(fd);
286                         trail_next(trail);
287                         return;
288                 }
289                 if (!trail_is_crash_recovery(trail->tr_filename)) {
290                         pjdlog_debug(1,
291                             "Restarting file \"%s/%s\" at offset %jd.",
292                             trail->tr_dirname, trail->tr_filename,
293                             (intmax_t)offset);
294                 }
295                 trail->tr_filefd = fd;
296                 return;
297         }
298         close(fd);
299         if (offset > sb.st_size) {
300                 pjdlog_warning("File \"%s/%s\" shrinked, removing it.",
301                     trail->tr_dirname, trail->tr_filename);
302         } else {
303                 pjdlog_debug(1, "File \"%s/%s\" is already sent, removing it.",
304                     trail->tr_dirname, trail->tr_filename);
305         }
306         /* Entire file is already sent or it shirnked, we can remove it. */
307         if (unlinkat(dfd, trail->tr_filename, 0) == -1) {
308                 pjdlog_errno(LOG_WARNING, "Unable to remove file \"%s/%s\"",
309                     trail->tr_dirname, trail->tr_filename);
310         }
311         trail_next(trail);
312 }
313
314 /*
315  * Set next file in the trail->tr_dirname directory and open it for reading.
316  */
317 void
318 trail_next(struct trail *trail)
319 {
320         char curfile[PATH_MAX];
321         struct dirent *dp;
322         int dfd;
323
324         PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
325         PJDLOG_ASSERT(trail->tr_filefd == -1);
326
327 again:
328         curfile[0] = '\0';
329
330         rewinddir(trail->tr_dirfp);
331         while ((dp = readdir(trail->tr_dirfp)) != NULL) {
332                 if (dp->d_name[0] < '0' || dp->d_name[0] > '9')
333                         continue;
334                 if (dp->d_type == DT_UNKNOWN)
335                         dp->d_type = trail_type(trail->tr_dirfp, dp->d_name);
336                 /* We are only interested in regular files, skip the rest. */
337                 if (dp->d_type != DT_REG) {
338                         pjdlog_debug(1,
339                             "File \"%s/%s\" is not a regular file, skipping.",
340                             trail->tr_dirname, dp->d_name);
341                         continue;
342                 }
343                 /* Skip all files "greater" than curfile. */
344                 if (curfile[0] != '\0' && strcmp(dp->d_name, curfile) > 0)
345                         continue;
346                 /* Skip all files "smaller" than the current trail_filename. */
347                 if (trail->tr_filename[0] != '\0' &&
348                     strcmp(dp->d_name, trail->tr_filename) <= 0) {
349                         continue;
350                 }
351                 PJDLOG_VERIFY(strlcpy(curfile, dp->d_name, sizeof(curfile)) <
352                     sizeof(curfile));
353         }
354         if (curfile[0] == '\0') {
355                 /*
356                  * There are no new trail files, so we return.
357                  * We don't clear trail_filename string, to know where to
358                  * start when new file appears.
359                  */
360                 PJDLOG_ASSERT(trail->tr_filefd == -1);
361                 pjdlog_debug(1, "No new trail files.");
362                 return;
363         }
364         PJDLOG_VERIFY(strlcpy(trail->tr_filename, curfile,
365             sizeof(trail->tr_filename)) < sizeof(trail->tr_filename));
366         dfd = dirfd(trail->tr_dirfp);
367         PJDLOG_ASSERT(dfd >= 0);
368         trail->tr_filefd = openat(dfd, trail->tr_filename, O_RDONLY);
369         if (trail->tr_filefd == -1) {
370                 pjdlog_errno(LOG_ERR,
371                     "Unable to open file \"%s/%s\", skipping",
372                     trail->tr_dirname, trail->tr_filename);
373                 goto again;
374         }
375         pjdlog_debug(1, "Found next trail file: \"%s/%s\".", trail->tr_dirname,
376             trail->tr_filename);
377 }
378
379 /*
380  * Close current trial file.
381  */
382 void
383 trail_close(struct trail *trail)
384 {
385
386         PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
387         PJDLOG_ASSERT(trail->tr_filefd >= 0);
388         PJDLOG_ASSERT(trail->tr_filename[0] != '\0');
389
390         PJDLOG_VERIFY(close(trail->tr_filefd) == 0);
391         trail->tr_filefd = -1;
392 }
393
394 /*
395  * Reset trail state. Used when connection is disconnected and we will
396  * need to start over after reconnect. Trail needs to be already closed.
397  */
398 void
399 trail_reset(struct trail *trail)
400 {
401
402         PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
403         PJDLOG_ASSERT(trail->tr_filefd == -1);
404
405         trail->tr_filename[0] = '\0';
406 }
407
408 /*
409  * Unlink current trial file.
410  */
411 void
412 trail_unlink(struct trail *trail, const char *filename)
413 {
414         int dfd;
415
416         PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
417         PJDLOG_ASSERT(filename != NULL);
418         PJDLOG_ASSERT(filename[0] != '\0');
419
420         dfd = dirfd(trail->tr_dirfp);
421         PJDLOG_ASSERT(dfd >= 0);
422
423         if (unlinkat(dfd, filename, 0) == -1) {
424                 pjdlog_errno(LOG_ERR, "Unable to remove \"%s/%s\"",
425                     trail->tr_dirname, filename);
426         } else {
427                 pjdlog_debug(1, "Trail file \"%s/%s\" removed.",
428                     trail->tr_dirname, filename);
429         }
430 }
431
432 /*
433  * Return true if we should switch to next trail file.
434  * We don't switch if our file name ends with ".not_terminated" and it
435  * exists (ie. wasn't renamed).
436  */
437 bool
438 trail_switch(struct trail *trail)
439 {
440         char filename[PATH_MAX];
441         int fd;
442
443         PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
444         PJDLOG_ASSERT(trail->tr_filefd >= 0);
445
446         if (!trail_is_not_terminated(trail->tr_filename))
447                 return (true);
448         fd = dirfd(trail->tr_dirfp);
449         PJDLOG_ASSERT(fd >= 0);
450         if (faccessat(fd, trail->tr_filename, F_OK, 0) == 0)
451                 return (false);
452         if (errno != ENOENT) {
453                 pjdlog_errno(LOG_ERR, "Unable to access file \"%s/%s\"",
454                     trail->tr_dirname, trail->tr_filename);
455         }
456         strlcpy(filename, trail->tr_filename, sizeof(filename));
457         if (!trail_find(trail)) {
458                 pjdlog_error("Trail file \"%s/%s\" disappeared.",
459                     trail->tr_dirname, trail->tr_filename);
460                 return (true);
461         }
462         pjdlog_debug(1, "Trail file \"%s/%s\" was renamed to \"%s/%s\".",
463             trail->tr_dirname, filename, trail->tr_dirname,
464             trail->tr_filename);
465         return (true);
466 }
467
468 const char *
469 trail_filename(const struct trail *trail)
470 {
471
472         PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
473
474         return (trail->tr_filename);
475 }
476
477 int
478 trail_filefd(const struct trail *trail)
479 {
480
481         PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
482
483         return (trail->tr_filefd);
484 }
485
486 int
487 trail_dirfd(const struct trail *trail)
488 {
489
490         PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
491
492         return (dirfd(trail->tr_dirfp));
493 }
494
495 /*
496  * Find the last file in the directory opened under dirfp.
497  */
498 void
499 trail_last(DIR *dirfp, char *filename, size_t filenamesize)
500 {
501         char curfile[PATH_MAX];
502         struct dirent *dp;
503
504         PJDLOG_ASSERT(dirfp != NULL);
505
506         curfile[0] = '\0';
507
508         rewinddir(dirfp);
509         while ((dp = readdir(dirfp)) != NULL) {
510                 if (dp->d_name[0] < '0' || dp->d_name[0] > '9')
511                         continue;
512                 if (dp->d_type == DT_UNKNOWN)
513                         dp->d_type = trail_type(dirfp, dp->d_name);
514                 /* We are only interested in regular files, skip the rest. */
515                 if (dp->d_type != DT_REG)
516                         continue;
517                 /* Skip all files "greater" than curfile. */
518                 if (curfile[0] != '\0' && strcmp(dp->d_name, curfile) < 0)
519                         continue;
520                 PJDLOG_VERIFY(strlcpy(curfile, dp->d_name, sizeof(curfile)) <
521                     sizeof(curfile));
522         }
523         if (curfile[0] == '\0') {
524                 /*
525                  * There are no trail files, so we return.
526                  */
527                 pjdlog_debug(1, "No trail files.");
528                 bzero(filename, filenamesize);
529                 return;
530         }
531         PJDLOG_VERIFY(strlcpy(filename, curfile, filenamesize) < filenamesize);
532         pjdlog_debug(1, "Found the most recent trail file: \"%s\".", filename);
533 }
534
535 /*
536  * Check if the given file name is a valid audit trail file name.
537  * Possible names:
538  * 20120106132657.20120106132805
539  * 20120106132657.not_terminated
540  * 20120106132657.crash_recovery
541  * If two names are given, check if the first name can be renamed
542  * to the second name. When renaming, first part of the name has
543  * to be identical and only the following renames are valid:
544  * 20120106132657.not_terminated -> 20120106132657.20120106132805
545  * 20120106132657.not_terminated -> 20120106132657.crash_recovery
546  */
547 bool
548 trail_validate_name(const char *srcname, const char *dstname)
549 {
550         int i;
551
552         PJDLOG_ASSERT(srcname != NULL);
553
554         if (strlen(srcname) != 2 * HALF_LEN + 1)
555                 return (false);
556         if (srcname[HALF_LEN] != '.')
557                 return (false);
558         for (i = 0; i < HALF_LEN; i++) {
559                 if (srcname[i] < '0' || srcname[i] > '9')
560                         return (false);
561         }
562         for (i = HALF_LEN + 1; i < 2 * HALF_LEN - 1; i++) {
563                 if (srcname[i] < '0' || srcname[i] > '9')
564                         break;
565         }
566         if (i < 2 * HALF_LEN - 1 &&
567             strcmp(srcname + HALF_LEN + 1, "not_terminated") != 0 &&
568             strcmp(srcname + HALF_LEN + 1, "crash_recovery") != 0) {
569                 return (false);
570         }
571
572         if (dstname == NULL)
573                 return (true);
574
575         /* We tolarate if both names are identical. */
576         if (strcmp(srcname, dstname) == 0)
577                 return (true);
578
579         /* We can only rename not_terminated files. */
580         if (strcmp(srcname + HALF_LEN + 1, "not_terminated") != 0)
581                 return (false);
582         if (strlen(dstname) != 2 * HALF_LEN + 1)
583                 return (false);
584         if (strncmp(srcname, dstname, HALF_LEN + 1) != 0)
585                 return (false);
586         for (i = HALF_LEN + 1; i < 2 * HALF_LEN - 1; i++) {
587                 if (dstname[i] < '0' || dstname[i] > '9')
588                         break;
589         }
590         if (i < 2 * HALF_LEN - 1 &&
591             strcmp(dstname + HALF_LEN + 1, "crash_recovery") != 0) {
592                 return (false);
593         }
594
595         return (true);
596 }
597
598 int
599 trail_name_compare(const char *name0, const char *name1)
600 {
601         int ret;
602
603         ret = strcmp(name0, name1);
604         if (ret == 0)
605                 return (TRAIL_IDENTICAL);
606         if (strncmp(name0, name1, HALF_LEN + 1) == 0)
607                 return (TRAIL_RENAMED);
608         return (ret < 0 ? TRAIL_OLDER : TRAIL_NEWER);
609 }