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 } 610