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 * 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 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 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 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 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 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 * 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 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 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 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 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 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