1/* 2 * Copyright (c) 2012 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24#include <assert.h> 25#include <stdlib.h> 26#include <stdint.h> 27#include <stdbool.h> 28#include <string.h> 29#include <dirent.h> 30#include <errno.h> 31#include <time.h> 32#include <fcntl.h> 33#include <unistd.h> 34#include <sys/param.h> 35#include <sys/stat.h> 36#include <sys/acl.h> 37#include <membership.h> 38#include <xpc/xpc.h> 39#include <TargetConditionals.h> 40#include <configuration_profile.h> 41#include <asl.h> 42#include <asl_core.h> 43#include <asl_msg.h> 44#include "asl_common.h" 45 46#define _PATH_ASL_CONF "/etc/asl.conf" 47#define _PATH_ASL_CONF_DIR "/etc/asl" 48 49#define PATH_VAR_LOG "/var/log/" 50#define PATH_VAR_LOG_LEN 9 51 52#define PATH_LIBRARY_LOGS "/Library/Logs/" 53#define PATH_LIBRARY_LOGS_LEN 14 54 55#if !TARGET_IPHONE_SIMULATOR 56#define _PATH_ASL_CONF_LOCAL_DIR "/usr/local/etc/asl" 57#endif 58 59static const char *asl_out_action_name[] = 60{ 61 "none ", 62 "set ", 63 "output ", 64 "ignore ", 65 "skip ", 66 "claim ", 67 "notify ", 68 "broadcast ", 69 "access ", 70 "set ", 71 "unset ", 72 "store ", 73 "asl_file ", 74 "asl_dir ", 75 "file ", 76 "forward ", 77 "control ", 78 "set (file) ", 79 "set (plist) ", 80 "set (profile)" 81}; 82 83#define forever for(;;) 84#define KEYMATCH(S,K) ((strncasecmp(S, K, strlen(K)) == 0)) 85 86#define STAMP_STYLE_INVALID -1 87#define STAMP_STYLE_NULL 0 88#define STAMP_STYLE_SEC 1 89#define STAMP_STYLE_SEQ 2 90#define STAMP_STYLE_UTC_OR_LCL 3 91 92asl_msg_t * 93xpc_object_to_asl_msg(xpc_object_t xobj) 94{ 95 __block asl_msg_t *out; 96 97 if (xobj == NULL) return NULL; 98 if (xpc_get_type(xobj) != XPC_TYPE_DICTIONARY) return NULL; 99 100 out = asl_msg_new(ASL_TYPE_MSG); 101 xpc_dictionary_apply(xobj, ^bool(const char *key, xpc_object_t xval) { 102 char tmp[64]; 103 104 if (xpc_get_type(xval) == XPC_TYPE_NULL) 105 { 106 asl_msg_set_key_val_op(out, key, NULL, 0); 107 } 108 else if (xpc_get_type(xval) == XPC_TYPE_BOOL) 109 { 110 if (xpc_bool_get_value(xval)) asl_msg_set_key_val_op(out, key, "1", 0); 111 else asl_msg_set_key_val_op(out, key, "0", 0); 112 } 113 else if (xpc_get_type(xval) == XPC_TYPE_INT64) 114 { 115 snprintf(tmp, sizeof(tmp), "%lld", xpc_int64_get_value(xval)); 116 asl_msg_set_key_val_op(out, key, tmp, 0); 117 } 118 else if (xpc_get_type(xval) == XPC_TYPE_UINT64) 119 { 120 snprintf(tmp, sizeof(tmp), "%llu", xpc_uint64_get_value(xval)); 121 asl_msg_set_key_val_op(out, key, tmp, 0); 122 } 123 else if (xpc_get_type(xval) == XPC_TYPE_DOUBLE) 124 { 125 snprintf(tmp, sizeof(tmp), "%f", xpc_double_get_value(xval)); 126 asl_msg_set_key_val_op(out, key, tmp, 0); 127 } 128 else if (xpc_get_type(xval) == XPC_TYPE_DATE) 129 { 130 snprintf(tmp, sizeof(tmp), "%lld", xpc_date_get_value(xval)); 131 asl_msg_set_key_val_op(out, key, tmp, 0); 132 } 133 else if (xpc_get_type(xval) == XPC_TYPE_DATA) 134 { 135 size_t len = xpc_data_get_length(xval); 136 char *encoded = asl_core_encode_buffer(xpc_data_get_bytes_ptr(xval), len); 137 asl_msg_set_key_val_op(out, key, encoded, 0); 138 free(encoded); 139 } 140 else if (xpc_get_type(xval) == XPC_TYPE_STRING) 141 { 142 asl_msg_set_key_val_op(out, key, xpc_string_get_string_ptr(xval), 0); 143 } 144 else if (xpc_get_type(xval) == XPC_TYPE_UUID) 145 { 146 uuid_string_t us; 147 uuid_unparse(xpc_uuid_get_bytes(xval), us); 148 asl_msg_set_key_val_op(out, key, us, 0); 149 } 150 else if (xpc_get_type(xval) == XPC_TYPE_FD) 151 { 152 /* XPC_TYPE_FD is not supported */ 153 asl_msg_set_key_val_op(out, key, "{XPC_TYPE_FD}", 0); 154 } 155 else if (xpc_get_type(xval) == XPC_TYPE_SHMEM) 156 { 157 /* XPC_TYPE_SHMEM is not supported */ 158 asl_msg_set_key_val_op(out, key, "{XPC_TYPE_SHMEM}", 0); 159 } 160 else if (xpc_get_type(xval) == XPC_TYPE_ARRAY) 161 { 162 /* XPC_TYPE_ARRAY is not supported */ 163 asl_msg_set_key_val_op(out, key, "{XPC_TYPE_ARRAY}", 0); 164 } 165 else if (xpc_get_type(xval) == XPC_TYPE_DICTIONARY) 166 { 167 /* XPC_TYPE_DICTIONARY is not supported */ 168 asl_msg_set_key_val_op(out, key, "{XPC_TYPE_DICTIONARY}", 0); 169 } 170 else if (xpc_get_type(xval) == XPC_TYPE_ERROR) 171 { 172 /* XPC_TYPE_ERROR is not supported */ 173 asl_msg_set_key_val_op(out, key, "{XPC_TYPE_ERROR}", 0); 174 } 175 else 176 { 177 /* UNKNOWN TYPE */ 178 asl_msg_set_key_val_op(out, key, "{XPC_TYPE_???}", 0); 179 } 180 181 return true; 182 }); 183 184 return out; 185} 186 187asl_msg_t * 188configuration_profile_to_asl_msg(const char *ident) 189{ 190 xpc_object_t xobj = configuration_profile_copy_property_list(ident); 191 asl_msg_t *out = xpc_object_to_asl_msg(xobj); 192 if (xobj != NULL) xpc_release(xobj); 193 return out; 194} 195 196/* strdup + skip leading and trailing whitespace */ 197static char * 198_strdup_clean(const char *s) 199{ 200 char *out; 201 const char *first, *last; 202 size_t len; 203 204 if (s == NULL) return NULL; 205 206 first = s; 207 while ((*first == ' ') || (*first == '\t')) first++; 208 len = strlen(first); 209 if (len == 0) return NULL; 210 211 last = first + len - 1; 212 while ((len > 0) && ((*last == ' ') || (*last == '\t'))) 213 { 214 last--; 215 len--; 216 } 217 218 if (len == 0) return NULL; 219 220 out = malloc(len + 1); 221 if (out == NULL) return NULL; 222 223 memcpy(out, first, len); 224 out[len] = '\0'; 225 return out; 226} 227 228static char ** 229_insert_string(char *s, char **l, uint32_t x) 230{ 231 int i, len; 232 233 if (s == NULL) return l; 234 if (l == NULL) 235 { 236 l = (char **)malloc(2 * sizeof(char *)); 237 if (l == NULL) return NULL; 238 239 l[0] = strdup(s); 240 if (l[0] == NULL) 241 { 242 free(l); 243 return NULL; 244 } 245 246 l[1] = NULL; 247 return l; 248 } 249 250 for (i = 0; l[i] != NULL; i++); 251 252 /* len includes the NULL at the end of the list */ 253 len = i + 1; 254 255 l = (char **)reallocf(l, (len + 1) * sizeof(char *)); 256 if (l == NULL) return NULL; 257 258 if ((x >= (len - 1)) || (x == IndexNull)) 259 { 260 l[len - 1] = strdup(s); 261 if (l[len - 1] == NULL) 262 { 263 free(l); 264 return NULL; 265 } 266 267 l[len] = NULL; 268 return l; 269 } 270 271 for (i = len; i > x; i--) l[i] = l[i - 1]; 272 l[x] = strdup(s); 273 if (l[x] == NULL) return NULL; 274 275 return l; 276} 277 278char ** 279explode(const char *s, const char *delim) 280{ 281 char **l = NULL; 282 const char *p; 283 char *t, quote; 284 int i, n; 285 286 if (s == NULL) return NULL; 287 288 quote = '\0'; 289 290 p = s; 291 while (p[0] != '\0') 292 { 293 /* scan forward */ 294 for (i = 0; p[i] != '\0'; i++) 295 { 296 if (quote == '\0') 297 { 298 /* not inside a quoted string: check for delimiters and quotes */ 299 if (strchr(delim, p[i]) != NULL) break; 300 else if (p[i] == '\'') quote = p[i]; 301 else if (p[i] == '"') quote = p[i]; 302 } 303 else 304 { 305 /* inside a quoted string - look for matching quote */ 306 if (p[i] == quote) quote = '\0'; 307 } 308 } 309 310 n = i; 311 t = malloc(n + 1); 312 if (t == NULL) return NULL; 313 314 for (i = 0; i < n; i++) t[i] = p[i]; 315 t[n] = '\0'; 316 l = _insert_string(t, l, IndexNull); 317 free(t); 318 t = NULL; 319 if (p[i] == '\0') return l; 320 if (p[i + 1] == '\0') l = _insert_string("", l, IndexNull); 321 p = p + i + 1; 322 } 323 324 return l; 325} 326 327void 328free_string_list(char **l) 329{ 330 int i; 331 332 if (l == NULL) return; 333 for (i = 0; l[i] != NULL; i++) free(l[i]); 334 free(l); 335} 336 337char * 338get_line_from_file(FILE *f) 339{ 340 char *s, *out; 341 size_t len; 342 343 out = fgetln(f, &len); 344 if (out == NULL) return NULL; 345 if (len == 0) return NULL; 346 347 s = malloc(len + 1); 348 if (s == NULL) return NULL; 349 350 memcpy(s, out, len); 351 352 if (s[len - 1] != '\n') len++; 353 s[len - 1] = '\0'; 354 return s; 355} 356 357char * 358next_word_from_string(char **s) 359{ 360 char *a, *p, *e, *out, s0; 361 int quote1, quote2, len; 362 363 if (s == NULL) return NULL; 364 if (*s == NULL) return NULL; 365 366 s0 = **s; 367 368 quote1 = 0; 369 quote2 = 0; 370 371 p = *s; 372 373 /* allow whole word to be contained in quotes */ 374 if (*p == '\'') 375 { 376 quote1 = 1; 377 p++; 378 } 379 380 if (*p == '"') 381 { 382 quote2 = 1; 383 p++; 384 } 385 386 a = p; 387 e = p; 388 389 while (*p != '\0') 390 { 391 if (*p == '\\') 392 { 393 p++; 394 e = p; 395 396 if (*p == '\0') 397 { 398 p--; 399 break; 400 } 401 402 p++; 403 e = p; 404 continue; 405 } 406 407 if (*p == '\'') 408 { 409 if (quote1 == 0) quote1 = 1; 410 else quote1 = 0; 411 } 412 413 if (*p == '"') 414 { 415 if (quote2 == 0) quote2 = 1; 416 else quote2 = 0; 417 } 418 419 if (((*p == ' ') || (*p == '\t')) && (quote1 == 0) && (quote2 == 0)) 420 { 421 e = p + 1; 422 break; 423 } 424 425 p++; 426 e = p; 427 } 428 429 *s = e; 430 431 len = p - a; 432 433 /* check for quoted string */ 434 if (((s0 == '\'') || (s0 == '"')) && (s0 == a[len-1])) len--; 435 436 if (len == 0) return NULL; 437 438 out = malloc(len + 1); 439 if (out == NULL) return NULL; 440 441 memcpy(out, a, len); 442 out[len] = '\0'; 443 return out; 444} 445 446static asl_out_dst_data_t * 447_asl_out_dest_for_path(asl_out_module_t *m, const char *path) 448{ 449 if (m == NULL) return NULL; 450 if (path == NULL) return NULL; 451 452 while (m != NULL) 453 { 454 asl_out_rule_t *r = m->ruleset; 455 while (r != NULL) 456 { 457 if ((r->action == ACTION_OUT_DEST) && (r->dst != NULL) && (r->dst->path != NULL) && (!strcmp(r->dst->path, path))) return r->dst; 458 r = r->next; 459 } 460 461 m = m->next; 462 } 463 464 return NULL; 465} 466 467/* 468 * Create a directory path. 469 * 470 * mlist provides owner, group, and access mode, which are required for "non-standard" 471 * directories. Directories for standard paths (/var/log or /Library/Logs) default 472 * to root/admin/0755 if there is no mlist rule for them. 473 */ 474static int 475_asl_common_make_dir_path(asl_out_module_t *mlist, uint32_t flags, const char *path) 476{ 477 int i; 478 char **path_parts; 479 asl_string_t *processed_path; 480 mode_t mode; 481 482 if (path == NULL) return 0; 483 484 path_parts = explode(path, "/"); 485 if (path_parts == NULL) return 0; 486 487 processed_path = asl_string_new(ASL_ENCODE_NONE); 488 489 i = 0; 490 if (path[0] == '/') i = 1; 491 492 for (; path_parts[i] != NULL; i++) 493 { 494 struct stat sb; 495 int status; 496 mode_t mask; 497 asl_out_dst_data_t *dst; 498 char *tmp; 499 500 asl_string_append_char_no_encoding(processed_path, '/'); 501 asl_string_append_no_encoding(processed_path, path_parts[i]); 502 tmp = asl_string_bytes(processed_path); 503 504 memset(&sb, 0, sizeof(struct stat)); 505 status = lstat(tmp, &sb); 506 if ((status == 0) && S_ISLNK(sb.st_mode)) 507 { 508 char real[MAXPATHLEN]; 509 if (realpath(tmp, real) == NULL) 510 { 511 asl_string_release(processed_path); 512 free_string_list(path_parts); 513 return -1; 514 } 515 516 memset(&sb, 0, sizeof(struct stat)); 517 status = stat(real, &sb); 518 } 519 520 if (status == 0) 521 { 522 if (!S_ISDIR(sb.st_mode)) 523 { 524 /* path component is not a directory! */ 525 asl_string_release(processed_path); 526 free_string_list(path_parts); 527 return -1; 528 } 529 530 /* exists and is a directory or a link to a directory */ 531 continue; 532 } 533 else if (errno != ENOENT) 534 { 535 /* unexpected status from stat() */ 536 asl_string_release(processed_path); 537 free_string_list(path_parts); 538 return -1; 539 } 540 541 dst = _asl_out_dest_for_path(mlist, tmp); 542 if ((dst == NULL) && (flags & MODULE_FLAG_NONSTD_DIR)) 543 { 544 /* no rule to create a non-standard path component! */ 545 asl_string_release(processed_path); 546 free_string_list(path_parts); 547 return -1; 548 } 549 550 mode = 0755; 551 if (dst != NULL) 552 { 553 mode = dst->mode; 554 if (mode == 010000) mode = 0755; 555 } 556 557 mask = umask(0); 558 status = mkdir(tmp, mode); 559 umask(mask); 560 561#if !TARGET_IPHONE_SIMULATOR 562 uid_t u = 0; 563 gid_t g = 80; 564 565 if (dst != NULL) 566 { 567 if (dst->nuid > 0) u = dst->uid[0]; 568 if (dst->ngid > 0) g = dst->gid[0]; 569 } 570 571 chown(tmp, u, g); 572#endif 573 } 574 575 asl_string_release(processed_path); 576 free_string_list(path_parts); 577 578 return 0; 579} 580 581int 582asl_out_mkpath(asl_out_module_t *mlist, asl_out_rule_t *r) 583{ 584 char tmp[MAXPATHLEN], *p; 585 struct stat sb; 586 int status; 587 588 if (r == NULL) return -1; 589 if (r->dst == NULL) return -1; 590 if (r->dst->path == NULL) return -1; 591 592 snprintf(tmp, sizeof(tmp), "%s", r->dst->path); 593 594 if (r->action != ACTION_ASL_DIR) 595 { 596 p = strrchr(tmp, '/'); 597 if (p == NULL) return -1; 598 *p = '\0'; 599 } 600 601 memset(&sb, 0, sizeof(struct stat)); 602 status = stat(tmp, &sb); 603 if (status == 0) 604 { 605 if (S_ISDIR(sb.st_mode)) return 0; 606 return -1; 607 } 608 609 if (errno == ENOENT) 610 { 611 uint32_t dirflag = r->dst->flags & MODULE_FLAG_NONSTD_DIR; 612 status = _asl_common_make_dir_path(mlist, dirflag, tmp); 613 return status; 614 } 615 616 return -1; 617} 618 619void 620asl_make_timestamp(time_t stamp, uint32_t flags, char *buf, size_t len) 621{ 622 struct tm t; 623 uint32_t h, m, s; 624 625 if (buf == NULL) return; 626 627 if (flags & MODULE_FLAG_STYLE_UTC) 628 { 629 memset(&t, 0, sizeof(t)); 630 gmtime_r(&stamp, &t); 631 snprintf(buf, len, "%d-%02d-%02dT%02d:%02d:%02dZ", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec); 632 } 633 else if (flags & MODULE_FLAG_STYLE_UTC_B) 634 { 635 memset(&t, 0, sizeof(t)); 636 gmtime_r(&stamp, &t); 637 snprintf(buf, len, "%d%02d%02dT%02d%02d%02dZ", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec); 638 } 639 else if (flags & MODULE_FLAG_STYLE_LCL) 640 { 641 bool neg = false; 642 memset(&t, 0, sizeof(t)); 643 localtime_r(&stamp, &t); 644 645 if ((neg = (t.tm_gmtoff < 0))) t.tm_gmtoff *= -1; 646 647 s = t.tm_gmtoff; 648 h = s / 3600; 649 s %= 3600; 650 m = s / 60; 651 s %= 60; 652 653 if (s > 0) snprintf(buf, len, "%d-%02d-%02dT%02d:%02d:%02d%c%u:%02u:%02u", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, neg ? '-' : '+', h, m, s); 654 else if (m > 0) snprintf(buf, len, "%d-%02d-%02dT%02d:%02d:%02d%c%u:%02u", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, neg ? '-' : '+', h, m); 655 else snprintf(buf, len, "%d-%02d-%02dT%02d:%02d:%02d%c%u", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, neg ? '-' : '+', h); 656 } 657 else if (flags & MODULE_FLAG_STYLE_LCL_B) 658 { 659 bool neg = false; 660 memset(&t, 0, sizeof(t)); 661 localtime_r(&stamp, &t); 662 663 if ((neg = (t.tm_gmtoff < 0))) t.tm_gmtoff *= -1; 664 665 s = t.tm_gmtoff; 666 h = s / 3600; 667 s %= 3600; 668 m = s / 60; 669 s %= 60; 670 671 if (s > 0) snprintf(buf, len, "%d%02d%02dT%02d%02d%02d%c%02u%02u%02u", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, neg ? '-' : '+', h, m, s); 672 else if (m > 0) snprintf(buf, len, "%d%02d%02dT%02d%02d%02d%c%02u%02u", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, neg ? '-' : '+', h, m); 673 else snprintf(buf, len, "%d%02d%02dT%02d%02d%02d%c%02u", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, neg ? '-' : '+', h); 674 } 675 else 676 { 677 snprintf(buf, len, "%c%lu", STYLE_SEC_PREFIX_CHAR, stamp); 678 } 679} 680 681void 682asl_make_dst_filename(asl_out_dst_data_t *dst, char *buf, size_t len) 683{ 684 if (dst == NULL) return; 685 if (buf == NULL) return; 686 687 if (dst->flags & (MODULE_FLAG_BASESTAMP | MODULE_FLAG_TYPE_ASL_DIR)) 688 { 689 char tstamp[32]; 690 const char *name = dst->path; 691 692 if (dst->flags & MODULE_FLAG_TYPE_ASL_DIR) name = dst->fname; 693 694 if (dst->stamp == 0) dst->stamp = time(NULL); 695 asl_make_timestamp(dst->stamp, dst->flags, tstamp, sizeof(tstamp)); 696 snprintf(buf, len, "%s.%s", name, tstamp); 697 } 698 else 699 { 700 snprintf(buf, len, "%s", dst->path); 701 } 702} 703 704int 705asl_check_option(asl_msg_t *msg, const char *opt) 706{ 707 const char *p; 708 uint32_t len; 709 710 if (msg == NULL) return 0; 711 if (opt == NULL) return 0; 712 713 len = strlen(opt); 714 if (len == 0) return 0; 715 716 p = asl_msg_get_val_for_key(msg, ASL_KEY_OPTION); 717 if (p == NULL) return 0; 718 719 while (*p != '\0') 720 { 721 while ((*p == ' ') || (*p == '\t') || (*p == ',')) p++; 722 if (*p == '\0') return 0; 723 724 if (strncasecmp(p, opt, len) == 0) 725 { 726 p += len; 727 if ((*p == ' ') || (*p == '\t') || (*p == ',') || (*p == '\0')) return 1; 728 } 729 730 while ((*p != ' ') && (*p != '\t') && (*p != ',') && (*p != '\0')) p++; 731 } 732 733 return 0; 734} 735 736void 737asl_out_dst_data_release(asl_out_dst_data_t *dst) 738{ 739 if (dst == NULL) return; 740 741 if (dst->refcount > 0) dst->refcount--; 742 if (dst->refcount > 0) return; 743 744 free(dst->path); 745 free(dst->fname); 746 free(dst->rotate_dir); 747 free(dst->fmt); 748#if !TARGET_IPHONE_SIMULATOR 749 free(dst->uid); 750 free(dst->gid); 751#endif 752 free(dst); 753} 754 755asl_out_dst_data_t * 756asl_out_dst_data_retain(asl_out_dst_data_t *dst) 757{ 758 if (dst == NULL) return NULL; 759 dst->refcount++; 760 return dst; 761} 762 763/* set owner, group, mode, and acls for a file */ 764int 765asl_out_dst_set_access(int fd, asl_out_dst_data_t *dst) 766{ 767#if !TARGET_IPHONE_SIMULATOR 768 uid_t fuid = 0; 769 gid_t fgid = 80; 770#if !TARGET_OS_EMBEDDED 771 int status; 772 acl_t acl; 773 uuid_t uuid; 774 acl_entry_t entry; 775 acl_permset_t perms; 776 uint32_t i; 777#endif 778#endif 779 780 if (dst == NULL) return -1; 781 if (fd < 0) return -1; 782 783#if TARGET_IPHONE_SIMULATOR 784 return fd; 785#else 786 787 if (dst->nuid > 0) fuid = dst->uid[0]; 788 if (dst->ngid > 0) fgid = dst->gid[0]; 789 790 fchown(fd, fuid, fgid); 791 792#if TARGET_OS_EMBEDDED 793 return fd; 794#else 795 acl = acl_init(1); 796 797 for (i = 0; i < dst->ngid; i++) 798 { 799 if (dst->gid[i] == -2) continue; 800 801 /* 802 * Don't bother setting group access if this is 803 * file's group and the file is group-readable. 804 */ 805 if ((dst->gid[i] == fgid) && (dst->mode & 00040)) continue; 806 807 status = mbr_gid_to_uuid(dst->gid[i], uuid); 808 if (status != 0) 809 { 810 dst->gid[i] = -2; 811 continue; 812 } 813 814 status = acl_create_entry_np(&acl, &entry, ACL_FIRST_ENTRY); 815 if (status != 0) goto asl_file_create_return; 816 817 status = acl_set_tag_type(entry, ACL_EXTENDED_ALLOW); 818 if (status != 0) goto asl_file_create_return; 819 820 status = acl_set_qualifier(entry, &uuid); 821 if (status != 0) goto asl_file_create_return; 822 823 status = acl_get_permset(entry, &perms); 824 if (status != 0) goto asl_file_create_return; 825 826 status = acl_add_perm(perms, ACL_READ_DATA); 827 if (status != 0) goto asl_file_create_return; 828 } 829 830 for (i = 0; i < dst->nuid; i++) 831 { 832 if (dst->uid[i] == -2) continue; 833 834 /* 835 * Don't bother setting user access if this is 836 * file's owner and the file is owner-readable. 837 */ 838 if ((dst->uid[i] == fuid) && (dst->mode & 00400)) continue; 839 840 status = mbr_uid_to_uuid(dst->uid[i], uuid); 841 if (status != 0) 842 { 843 dst->uid[i] = -2; 844 continue; 845 } 846 847 status = acl_create_entry_np(&acl, &entry, ACL_FIRST_ENTRY); 848 if (status != 0) goto asl_file_create_return; 849 850 status = acl_set_tag_type(entry, ACL_EXTENDED_ALLOW); 851 if (status != 0) goto asl_file_create_return; 852 853 status = acl_set_qualifier(entry, &uuid); 854 if (status != 0) goto asl_file_create_return; 855 856 status = acl_get_permset(entry, &perms); 857 if (status != 0) goto asl_file_create_return; 858 859 status = acl_add_perm(perms, ACL_READ_DATA); 860 if (status != 0) goto asl_file_create_return; 861 } 862 863 status = acl_set_fd(fd, acl); 864 if (status != 0) 865 { 866 close(fd); 867 fd = -1; 868 } 869 870asl_file_create_return: 871 872 acl_free(acl); 873 return fd; 874#endif /* !TARGET_OS_EMBEDDED */ 875#endif /* !TARGET_IPHONE_SIMULATOR */ 876} 877 878/* create a file with acls */ 879int 880asl_out_dst_file_create_open(asl_out_dst_data_t *dst, char **pathp) 881{ 882 int fd, status; 883 struct stat sb; 884 char outpath[MAXPATHLEN]; 885 886 if (dst == NULL) return -1; 887 if (dst->path == NULL) return -1; 888 889 asl_make_dst_filename(dst, outpath, sizeof(outpath)); 890 if (dst->fname != NULL) free(dst->fname); 891 892 dst->fname = strdup(outpath); 893 if (dst->fname == NULL) return -1; 894 895 if (pathp != NULL) *pathp = strdup(outpath); 896 897 memset(&sb, 0, sizeof(struct stat)); 898 status = stat(outpath, &sb); 899 if (status == 0) 900 { 901 /* must be a regular file */ 902 if (!S_ISREG(sb.st_mode)) return -1; 903 904 /* file exists */ 905 fd = open(outpath, O_RDWR | O_APPEND | O_EXCL, 0); 906 907 if (dst->stamp == 0) dst->stamp = sb.st_birthtimespec.tv_sec; 908 if (dst->stamp == 0) dst->stamp = sb.st_mtimespec.tv_sec; 909 dst->size = sb.st_size; 910 911 return fd; 912 } 913 else if (errno != ENOENT) 914 { 915 /* stat error other than non-existant file */ 916 return -1; 917 } 918 919 fd = open(outpath, O_RDWR | O_CREAT | O_EXCL, (dst->mode & 00666)); 920 if (fd < 0) return -1; 921 922 dst->stamp = time(NULL); 923 924 fd = asl_out_dst_set_access(fd, dst); 925 if (fd < 0) unlink(outpath); 926 927 return fd; 928} 929 930void 931asl_out_module_free(asl_out_module_t *m) 932{ 933 asl_out_rule_t *r, *n; 934 asl_out_module_t *x; 935 936 while (m != NULL) 937 { 938 x = m->next; 939 940 /* free name */ 941 free(m->name); 942 943 /* free ruleset */ 944 r = m->ruleset; 945 while (r != NULL) 946 { 947 n = r->next; 948 if (r->dst != NULL) asl_out_dst_data_release(r->dst); 949 950 if (r->query != NULL) asl_msg_release(r->query); 951 free(r->options); 952 free(r); 953 r = n; 954 } 955 956 free(m); 957 m = x; 958 } 959} 960 961asl_out_module_t * 962asl_out_module_new(const char *name) 963{ 964 asl_out_module_t *out = (asl_out_module_t *)calloc(1, sizeof(asl_out_module_t)); 965 966 if (out == NULL) return NULL; 967 if (name == NULL) return NULL; 968 969 out->name = strdup(name); 970 if (out->name == NULL) 971 { 972 free(out); 973 return NULL; 974 } 975 976 out->flags = MODULE_FLAG_ENABLED; 977 978 return out; 979} 980 981/* Skip over query */ 982static char * 983_asl_out_module_find_action(char *s) 984{ 985 char *p; 986 987 p = s; 988 if (p == NULL) return NULL; 989 990 /* Skip command character (?, Q, *, or =) */ 991 p++; 992 993 forever 994 { 995 /* Find next [ */ 996 while ((*p == ' ') || (*p == '\t')) p++; 997 998 if (*p == '\0') return NULL; 999 if (*p != '[') return p; 1000 1001 /* skip to closing ] */ 1002 while (*p != ']') 1003 { 1004 p++; 1005 if (*p == '\\') 1006 { 1007 p++; 1008 if (*p == ']') p++; 1009 } 1010 } 1011 1012 if (*p == ']') p++; 1013 } 1014 1015 /* skip whitespace */ 1016 while ((*p == ' ') || (*p == '\t')) p++; 1017 1018 return NULL; 1019} 1020 1021/* 1022 * Parse parameter setting line 1023 * 1024 * = param options 1025 * evaluated once when module is initialized 1026 * 1027 * = [query] param options 1028 * evaluated for each message, param set if message matches query 1029 * 1030 * = param [File path] 1031 * evaluated once when module is initialized 1032 * evaluated when change notification received for path 1033 * 1034 * = param [Plist path] ... 1035 * evaluated once when module is initialized 1036 * evaluated when change notification received for path 1037 * 1038 * = param [Profile name] ... 1039 * evaluated once when module is initialized 1040 * evaluated when change notification received for profile 1041 */ 1042static asl_out_rule_t * 1043_asl_out_module_parse_set_param(asl_out_module_t *m, char *s) 1044{ 1045 char *act, *p, *q; 1046 asl_out_rule_t *out, *rule; 1047 1048 if (m == NULL) return NULL; 1049 1050 out = (asl_out_rule_t *)calloc(1, sizeof(asl_out_rule_t)); 1051 if (out == NULL) return NULL; 1052 1053 q = s + 1; 1054 while ((*q == ' ') || (*q == '\'')) q++; 1055 out->action = ACTION_SET_PARAM; 1056 1057 if (*q == '[') 1058 { 1059 /* = [query] param options */ 1060 act = _asl_out_module_find_action(s); 1061 if (act == NULL) 1062 { 1063 free(out); 1064 return NULL; 1065 } 1066 1067 out->options = _strdup_clean(act); 1068 1069 p = act - 1; 1070 if (*p == ']') p = act; 1071 *p = '\0'; 1072 1073 *s = 'Q'; 1074 out->query = asl_msg_from_string(s); 1075 if (out->query == NULL) 1076 { 1077 free(out->options); 1078 free(out); 1079 return NULL; 1080 } 1081 } 1082 else 1083 { 1084 /* = param ... */ 1085 p = strchr(s, '['); 1086 if (p == NULL) 1087 { 1088 /* = param options */ 1089 out->options = _strdup_clean(q); 1090 } 1091 else 1092 { 1093 /* = param [query] */ 1094 if ((!strncmp(p, "[File ", 6)) || (!strncmp(p, "[File\t", 6))) out->action = ACTION_SET_FILE; 1095 else if ((!strncmp(p, "[Plist ", 7)) || (!strncmp(p, "[Plist\t", 7))) out->action = ACTION_SET_PLIST; 1096 else if ((!strncmp(p, "[Profile ", 9)) || (!strncmp(p, "[Profile\t", 9))) out->action = ACTION_SET_PROF; 1097 1098 p--; 1099 *p = '\0'; 1100 out->options = _strdup_clean(q); 1101 1102 *p = ' '; 1103 p--; 1104 *p = 'Q'; 1105 out->query = asl_msg_from_string(p); 1106 if (out->query == NULL) 1107 { 1108 free(out->options); 1109 free(out); 1110 return NULL; 1111 } 1112 } 1113 } 1114 1115 if (m->ruleset == NULL) m->ruleset = out; 1116 else 1117 { 1118 for (rule = m->ruleset; rule->next != NULL; rule = rule->next); 1119 rule->next = out; 1120 } 1121 1122 return out; 1123} 1124 1125#if !TARGET_IPHONE_SIMULATOR 1126static void 1127_dst_add_uid(asl_out_dst_data_t *dst, char *s) 1128{ 1129 int i; 1130 uid_t uid; 1131 1132 if (dst == NULL) return; 1133 if (s == NULL) return; 1134 1135 uid = atoi(s); 1136 1137 for (i = 0 ; i < dst->nuid; i++) 1138 { 1139 if (dst->uid[i] == uid) return; 1140 } 1141 1142 dst->uid = reallocf(dst->uid, (dst->nuid + 1) * sizeof(uid_t)); 1143 if (dst->uid == NULL) 1144 { 1145 dst->nuid = 0; 1146 return; 1147 } 1148 1149 dst->uid[dst->nuid++] = uid; 1150} 1151 1152static void 1153_dst_add_gid(asl_out_dst_data_t *dst, char *s) 1154{ 1155 int i; 1156 gid_t gid; 1157 1158 if (dst == NULL) return; 1159 if (s == NULL) return; 1160 1161 gid = atoi(s); 1162 1163 for (i = 0 ; i < dst->ngid; i++) 1164 { 1165 if (dst->gid[i] == gid) return; 1166 } 1167 1168 dst->gid = reallocf(dst->gid, (dst->ngid + 1) * sizeof(gid_t)); 1169 if (dst->gid == NULL) 1170 { 1171 dst->ngid = 0; 1172 return; 1173 } 1174 1175 dst->gid[dst->ngid++] = gid; 1176} 1177#endif /* !TARGET_IPHONE_SIMULATOR */ 1178 1179static char * 1180_dst_format_string(char *s) 1181{ 1182 char *fmt; 1183 size_t i, len, n; 1184 1185 if (s == NULL) return NULL; 1186 1187 len = strlen(s); 1188 1189 /* format string can be enclosed by quotes */ 1190 if ((len >= 2) && ((s[0] == '\'') || (s[0] == '"')) && (s[len-1] == s[0])) 1191 { 1192 s++; 1193 len -= 2; 1194 } 1195 1196 n = 0; 1197 for (i = 0; i < len; i++) if (s[i] == '\\') n++; 1198 1199 fmt = malloc(1 + len - n); 1200 if (fmt == NULL) return NULL; 1201 1202 for (i = 0, n = 0; i < len; i++) if (s[i] != '\\') fmt[n++] = s[i]; 1203 fmt[n] = '\0'; 1204 return fmt; 1205} 1206 1207size_t 1208asl_str_to_size(char *s) 1209{ 1210 size_t len, n, max; 1211 char x; 1212 1213 if (s == NULL) return 0; 1214 1215 len = strlen(s); 1216 if (len == 0) return 0; 1217 1218 n = 1; 1219 x = s[len - 1]; 1220 if (x > 90) x -= 32; 1221 if (x == 'K') n = 1ll << 10; 1222 else if (x == 'M') n = 1ll << 20; 1223 else if (x == 'G') n = 1ll << 30; 1224 1225 max = atoll(s) * n; 1226 return max; 1227} 1228 1229static bool 1230_dst_path_match(const char *newpath, const char *existingpath) 1231{ 1232 if (newpath == NULL) return (existingpath == NULL); 1233 if (existingpath == NULL) return false; 1234 if (newpath[0] == '/') return (strcmp(newpath, existingpath) == 0); 1235 1236 const char *trailing = strrchr(existingpath, '/'); 1237 if (trailing == NULL) return (strcmp(newpath, existingpath) == 0); 1238 trailing++; 1239 return (strcmp(newpath, trailing) == 0); 1240} 1241 1242static asl_out_dst_data_t * 1243_asl_out_module_parse_dst(asl_out_module_t *m, char *s, mode_t def_mode) 1244{ 1245 asl_out_rule_t *out, *rule; 1246 asl_out_dst_data_t *dst; 1247 char *p, *opts, *path; 1248 char **path_parts; 1249 int has_dotdot, recursion_limit; 1250 uint32_t i, flags = 0; 1251 1252 if (m == NULL) return NULL; 1253 if (s == NULL) return NULL; 1254 1255 /* skip whitespace */ 1256 while ((*s == ' ') || (*s == '\t')) s++; 1257 1258 opts = s; 1259 path = next_word_from_string(&opts); 1260 if (path == NULL) return NULL; 1261 1262 /* 1263 * Check path for ".." component (not permitted). 1264 * Also substitute environment variables. 1265 */ 1266 has_dotdot = 0; 1267 path_parts = explode(path, "/"); 1268 asl_string_t *processed_path = asl_string_new(ASL_ENCODE_NONE); 1269 recursion_limit = 5; 1270 1271 while ((recursion_limit > 0) && (path_parts != NULL) && (processed_path != NULL)) 1272 { 1273 uint32_t i; 1274 int did_sub = 0; 1275 1276 for (i = 0; path_parts[i] != NULL; i++) 1277 { 1278 if (!strncmp(path_parts[i], "$ENV(", 5)) 1279 { 1280 char *p = strchr(path_parts[i], ')'); 1281 if (p != NULL) *p = '\0'; 1282 char *env_val = getenv(path_parts[i] + 5); 1283 if (env_val != NULL) 1284 { 1285 did_sub = 1; 1286 1287 if (env_val[0] != '/') asl_string_append_char_no_encoding(processed_path, '/'); 1288 asl_string_append_no_encoding(processed_path, env_val); 1289 } 1290 } 1291 else 1292 { 1293 if (i == 0) 1294 { 1295 if (path_parts[0][0] != '\0') asl_string_append_no_encoding(processed_path, path_parts[i]); 1296 } 1297 else 1298 { 1299 asl_string_append_char_no_encoding(processed_path, '/'); 1300 asl_string_append_no_encoding(processed_path, path_parts[i]); 1301 } 1302 } 1303 1304 if ((has_dotdot == 0) && (!strcmp(path_parts[i], ".."))) has_dotdot = 1; 1305 } 1306 1307 free_string_list(path_parts); 1308 path_parts = NULL; 1309 1310 if ((did_sub == 1) && (has_dotdot == 0)) 1311 { 1312 /* substitution might have added a ".." so check the new path */ 1313 free(path); 1314 path = asl_string_release_return_bytes(processed_path); 1315 processed_path = asl_string_new(ASL_ENCODE_NONE); 1316 path_parts = explode(path, "/"); 1317 recursion_limit--; 1318 } 1319 } 1320 1321 free(path); 1322 free_string_list(path_parts); 1323 path_parts = NULL; 1324 1325 if ((has_dotdot != 0) || (recursion_limit == 0)) 1326 { 1327 asl_string_release(processed_path); 1328 return NULL; 1329 } 1330 1331 path = asl_string_release_return_bytes(processed_path); 1332 1333 /* check if there's already a dst for this path */ 1334 for (rule = m->ruleset; rule != NULL; rule = rule->next) 1335 { 1336 if (rule->action != ACTION_OUT_DEST) continue; 1337 1338 dst = rule->dst; 1339 if (dst == NULL) continue; 1340 1341 if (_dst_path_match(path, dst->path)) 1342 { 1343 free(path); 1344 return dst; 1345 } 1346 } 1347 1348 flags |= MODULE_FLAG_NONSTD_DIR; 1349 1350 if (path[0] != '/') 1351 { 1352 char *t = path; 1353 const char *log_root = "/var/log"; 1354 1355#if TARGET_IPHONE_SIMULATOR 1356 log_root = getenv("SIMULATOR_LOG_ROOT"); 1357 if (log_root == NULL) log_root = "/tmp/log"; 1358#endif 1359 1360 if (!strcmp(m->name, ASL_MODULE_NAME)) 1361 { 1362 asprintf(&path, "%s/%s", log_root, t); 1363 } 1364 else 1365 { 1366 asprintf(&path, "%s/module/%s/%s", log_root, m->name, t); 1367 } 1368 1369 free(t); 1370 flags &= ~MODULE_FLAG_NONSTD_DIR; 1371 } 1372 else 1373 { 1374 /* 1375 * Standard log directories get marked so that syslogd 1376 * will create them without explicit rules. 1377 */ 1378 if (!strncmp(path, PATH_VAR_LOG, PATH_VAR_LOG_LEN)) flags &= ~MODULE_FLAG_NONSTD_DIR; 1379 else if (!strncmp(path, PATH_LIBRARY_LOGS, PATH_LIBRARY_LOGS_LEN)) flags &= ~MODULE_FLAG_NONSTD_DIR; 1380 } 1381 1382 out = (asl_out_rule_t *)calloc(1, sizeof(asl_out_rule_t)); 1383 dst = (asl_out_dst_data_t *)calloc(1, sizeof(asl_out_dst_data_t)); 1384 if ((out == NULL) || (dst == NULL)) 1385 { 1386 free(path); 1387 free(out); 1388 free(dst); 1389 return NULL; 1390 } 1391 1392 dst->refcount = 1; 1393 dst->path = path; 1394 dst->mode = def_mode; 1395 dst->ttl[LEVEL_ALL] = DEFAULT_TTL; 1396 dst->flags = flags | MODULE_FLAG_COALESCE; 1397 1398 while (NULL != (p = next_word_from_string(&opts))) 1399 { 1400 if (KEYMATCH(p, "mode=")) dst->mode = strtol(p+5, NULL, 0); 1401#if !TARGET_IPHONE_SIMULATOR 1402 else if (KEYMATCH(p, "uid=")) _dst_add_uid(dst, p+4); 1403 else if (KEYMATCH(p, "gid=")) _dst_add_gid(dst, p+4); 1404#endif 1405 else if (KEYMATCH(p, "fmt=")) dst->fmt = _dst_format_string(p+4); 1406 else if (KEYMATCH(p, "format=")) dst->fmt = _dst_format_string(p+7); 1407 else if (KEYMATCH(p, "dest=")) dst->rotate_dir = _strdup_clean(p+5); 1408 else if (KEYMATCH(p, "dst=")) dst->rotate_dir = _strdup_clean(p+4); 1409 else if (KEYMATCH(p, "coalesce=")) 1410 { 1411 if (KEYMATCH(p+9, "0")) dst->flags &= ~MODULE_FLAG_COALESCE; 1412 else if (KEYMATCH(p+9, "off")) dst->flags &= ~MODULE_FLAG_COALESCE; 1413 else if (KEYMATCH(p+9, "false")) dst->flags &= ~MODULE_FLAG_COALESCE; 1414 } 1415 else if (KEYMATCH(p, "compress")) dst->flags |= MODULE_FLAG_COMPRESS; 1416 else if (KEYMATCH(p, "extern")) dst->flags |= MODULE_FLAG_EXTERNAL; 1417 else if (KEYMATCH(p, "truncate")) dst->flags |= MODULE_FLAG_TRUNCATE; 1418 else if (KEYMATCH(p, "dir")) dst->flags |= MODULE_FLAG_TYPE_ASL_DIR; 1419 else if (KEYMATCH(p, "soft")) dst->flags |= MODULE_FLAG_SOFT_WRITE; 1420 else if (KEYMATCH(p, "file_max=")) dst->file_max = asl_str_to_size(p+9); 1421 else if (KEYMATCH(p, "all_max=")) dst->all_max = asl_str_to_size(p+8); 1422 else if (KEYMATCH(p, "style=") || KEYMATCH(p, "rotate=")) 1423 { 1424 const char *x = p + 6; 1425 1426 if (KEYMATCH(p, "rotate=")) x++; 1427 1428 dst->flags |= MODULE_FLAG_ROTATE; 1429 1430 if (KEYMATCH(x, "sec") || KEYMATCH(x, "seconds")) 1431 { 1432 dst->flags |= MODULE_FLAG_STYLE_SEC; 1433 } 1434 else if (KEYMATCH(x, "utc") || KEYMATCH(x, "date") || KEYMATCH(x, "zulu")) 1435 { 1436 const char *dash = strchr(x, '-'); 1437 if ((dash != NULL) && (*(dash + 1) == 'b')) dst->flags |= MODULE_FLAG_STYLE_UTC_B; 1438 else dst->flags |= MODULE_FLAG_STYLE_UTC; 1439 } 1440 else if (KEYMATCH(x, "local") || KEYMATCH(x, "lcl")) 1441 { 1442 const char *dash = strchr(x, '-'); 1443 if ((dash != NULL) && (*(dash + 1) == 'b')) dst->flags |= MODULE_FLAG_STYLE_LCL_B; 1444 else dst->flags |= MODULE_FLAG_STYLE_LCL; 1445 } 1446 else if (KEYMATCH(x, "#") || KEYMATCH(x, "seq") || KEYMATCH(x, "sequence")) 1447 { 1448 dst->flags |= MODULE_FLAG_STYLE_SEQ; 1449 } 1450 else 1451 { 1452 dst->flags |= MODULE_FLAG_STYLE_SEC; 1453 } 1454 } 1455 else if (KEYMATCH(p, "rotate")) dst->flags |= MODULE_FLAG_ROTATE; 1456 else if (KEYMATCH(p, "crashlog")) 1457 { 1458 /* crashlog implies rotation */ 1459 dst->flags |= MODULE_FLAG_ROTATE; 1460 dst->flags |= MODULE_FLAG_CRASHLOG; 1461 dst->flags |= MODULE_FLAG_BASESTAMP; 1462 dst->flags &= ~MODULE_FLAG_COALESCE; 1463 } 1464 else if (KEYMATCH(p, "basestamp")) 1465 { 1466 dst->flags |= MODULE_FLAG_BASESTAMP; 1467 } 1468 else if (KEYMATCH(p, "ttl")) 1469 { 1470 char *q = p + 3; 1471 if (*q == '=') 1472 { 1473 dst->ttl[LEVEL_ALL] = strtol(p+4, NULL, 0); 1474 } 1475 else if ((*q >= '0') && (*q <= '7') && (*(q+1) == '=')) 1476 { 1477 uint32_t x = *q - '0'; 1478 dst->ttl[x] = strtol(p+5, NULL, 0); 1479 } 1480 } 1481 1482 free(p); 1483 p = NULL; 1484 } 1485 1486#if TARGET_OS_EMBEDDED 1487 /* check for crashreporter files */ 1488 if ((KEYMATCH(dst->path, _PATH_CRASHREPORTER)) || (KEYMATCH(dst->path, _PATH_CRASHREPORTER_MOBILE))) 1489 { 1490 dst->flags |= MODULE_FLAG_ROTATE; 1491 dst->flags |= MODULE_FLAG_CRASHLOG; 1492 dst->flags |= MODULE_FLAG_BASESTAMP; 1493 dst->flags &= ~MODULE_FLAG_COALESCE; 1494 } 1495#endif 1496 1497 /* ttl[LEVEL_ALL] must be max of all level-specific ttls */ 1498 for (i = 0; i <= 7; i++) if (dst->ttl[i] > dst->ttl[LEVEL_ALL]) dst->ttl[LEVEL_ALL] = dst->ttl[i]; 1499 1500 /* default text file format is "std" */ 1501 if (dst->fmt == NULL) dst->fmt = strdup("std"); 1502 1503 /* duplicate compression is only possible for std and bsd formats */ 1504 if (strcmp(dst->fmt, "std") && strcmp(dst->fmt, "bsd")) dst->flags &= ~MODULE_FLAG_COALESCE; 1505 1506 /* note if format is one of std, bsd, or msg */ 1507 if (!strcmp(dst->fmt, "std") || !strcmp(dst->fmt, "bsd") || !strcmp(dst->fmt, "msg")) dst->flags |= MODULE_FLAG_STD_BSD_MSG; 1508 1509 /* MODULE_FLAG_STYLE_SEQ can not be used with MODULE_FLAG_BASESTAMP */ 1510 if ((dst->flags & MODULE_FLAG_BASESTAMP) && (dst->flags & MODULE_FLAG_STYLE_SEQ)) 1511 { 1512 dst->flags &= ~MODULE_FLAG_STYLE_SEQ; 1513 dst->flags |= MODULE_FLAG_STYLE_SEC; 1514 } 1515 1516 /* set time format for raw output */ 1517 if (!strcmp(dst->fmt, "raw")) dst->tfmt = "sec"; 1518 1519 /* check for ASL_PLACE_DATABASE_DEFAULT */ 1520 if (!strcmp(dst->path, ASL_PLACE_DATABASE_DEFAULT)) 1521 { 1522 dst->flags = MODULE_FLAG_TYPE_ASL_DIR; 1523 } 1524 1525 out->action = ACTION_OUT_DEST; 1526 out->dst = dst; 1527 1528 /* dst rules go first */ 1529 out->next = m->ruleset; 1530 m->ruleset = out; 1531 1532 return dst; 1533} 1534 1535static asl_out_rule_t * 1536_asl_out_module_parse_query_action(asl_out_module_t *m, char *s) 1537{ 1538 char *act, *p; 1539 asl_out_rule_t *out, *rule; 1540 1541 if (m == NULL) return NULL; 1542 1543 out = (asl_out_rule_t *)calloc(1, sizeof(asl_out_rule_t)); 1544 if (out == NULL) return NULL; 1545 1546 act = _asl_out_module_find_action(s); 1547 if (act == NULL) return NULL; 1548 1549 /* find whitespace delimiter */ 1550 p = strchr(act, ' '); 1551 if (p == NULL) p = strchr(act, '\t'); 1552 if (p != NULL) *p = '\0'; 1553 1554 if (!strcasecmp(act, "ignore")) out->action = ACTION_IGNORE; 1555 else if (!strcasecmp(act, "skip")) out->action = ACTION_SKIP; 1556 else if (!strcasecmp(act, "claim")) out->action = ACTION_CLAIM; 1557 else if (!strcasecmp(act, "notify")) out->action = ACTION_NOTIFY; 1558 else if (!strcasecmp(act, "file")) out->action = ACTION_FILE; 1559 else if (!strcasecmp(act, "asl_file")) out->action = ACTION_ASL_FILE; 1560 else if (!strcasecmp(act, "directory")) out->action = ACTION_ASL_DIR; 1561 else if (!strcasecmp(act, "dir")) out->action = ACTION_ASL_DIR; 1562 else if (!strcasecmp(act, "asl_directory")) out->action = ACTION_ASL_DIR; 1563 else if (!strcasecmp(act, "asl_dir")) out->action = ACTION_ASL_DIR; 1564 else if (!strcasecmp(act, "store_dir")) out->action = ACTION_ASL_DIR; 1565 else if (!strcasecmp(act, "store_directory")) out->action = ACTION_ASL_DIR; 1566 else if (!strcasecmp(act, "control")) out->action = ACTION_CONTROL; 1567 else if (!strcasecmp(act, "save")) out->action = ACTION_ASL_STORE; 1568 else if (!strcasecmp(act, "store")) out->action = ACTION_ASL_STORE; 1569 else if (!strcasecmp(act, "access")) out->action = ACTION_ACCESS; 1570 else if (!strcasecmp(act, "set")) out->action = ACTION_SET_KEY; 1571 else if (!strcasecmp(act, "unset")) out->action = ACTION_UNSET_KEY; 1572 else if (!strcmp(m->name, ASL_MODULE_NAME)) 1573 { 1574 /* actions only allowed in com.apple.asl */ 1575 if (!strcasecmp(act, "broadcast")) out->action = ACTION_BROADCAST; 1576 else if (!strcasecmp(act, "forward")) out->action = ACTION_FORWARD; 1577 } 1578 1579 if (out->action == ACTION_NONE) 1580 { 1581 free(out); 1582 return NULL; 1583 } 1584 1585 /* options follow delimited (now zero) */ 1586 if (p != NULL) 1587 { 1588 /* skip whitespace */ 1589 while ((*p == ' ') || (*p == '\t')) p++; 1590 1591 out->options = _strdup_clean(p+1); 1592 1593 if (out->options == NULL) 1594 { 1595 free(out); 1596 return NULL; 1597 } 1598 } 1599 1600 p = act - 1; 1601 1602 *p = '\0'; 1603 1604 if (*s== '*') 1605 { 1606 out->query = asl_msg_new(ASL_TYPE_QUERY); 1607 } 1608 else 1609 { 1610 *s = 'Q'; 1611 out->query = asl_msg_from_string(s); 1612 } 1613 1614 if (out->query == NULL) 1615 { 1616 free(out->options); 1617 free(out); 1618 return NULL; 1619 } 1620 1621 /* store /some/path means save to an asl file */ 1622 if (out->action == ACTION_ASL_STORE) 1623 { 1624 if (out->options == NULL) out->dst = asl_out_dst_data_retain(_asl_out_module_parse_dst(m, ASL_PLACE_DATABASE_DEFAULT, 0755)); 1625 else if (!strncmp(out->options, ASL_PLACE_DATABASE_DEFAULT, strlen(ASL_PLACE_DATABASE_DEFAULT))) out->dst = asl_out_dst_data_retain(_asl_out_module_parse_dst(m, out->options, 0755)); 1626 else if (out->options != NULL) out->action = ACTION_ASL_FILE; 1627 } 1628 1629 if ((out->action == ACTION_FILE) || (out->action == ACTION_ASL_FILE) || (out->action == ACTION_ASL_DIR)) 1630 { 1631 mode_t def_mode = 0644; 1632 if (out->action == ACTION_ASL_DIR) def_mode = 0755; 1633 1634 out->dst = asl_out_dst_data_retain(_asl_out_module_parse_dst(m, out->options, def_mode)); 1635 if (out->dst == NULL) 1636 { 1637 out->action = ACTION_NONE; 1638 return out; 1639 } 1640 1641 /* 1642 * dst might have been set up by a previous ACTION_OUT_DEST ('>') rule with no mode. 1643 * If so, mode would be 010000. Set it now, since we know whether it is a file or dir. 1644 */ 1645 if (out->dst->mode == 010000) out->dst->mode = def_mode; 1646 1647 if ((out->action == ACTION_FILE) && (out->dst != NULL) && (out->dst->fmt != NULL) && (!strcasecmp(out->dst->fmt, "asl"))) 1648 { 1649 out->action = ACTION_ASL_FILE; 1650 } 1651 1652 if ((out->action == ACTION_ASL_FILE) && (out->dst != NULL)) 1653 { 1654 out->dst->flags |= MODULE_FLAG_TYPE_ASL; 1655 } 1656 1657 if (out->action == ACTION_ASL_DIR) 1658 { 1659 /* coalesce is meaningless for ASL directories */ 1660 out->dst->flags &= ~MODULE_FLAG_COALESCE; 1661 1662 /* no compression at this point */ 1663 out->dst->flags &= ~MODULE_FLAG_COMPRESS; 1664 1665 out->dst->flags |= MODULE_FLAG_TYPE_ASL_DIR; 1666 1667 /* set style bits for basestamp asl_dirs */ 1668 if (((out->dst->flags & MODULE_FLAG_STYLE_BITS) == 0) && (out->dst->flags & MODULE_FLAG_BASESTAMP)) out->dst->flags |= MODULE_FLAG_STYLE_LCL_B; 1669 } 1670 1671 /* only ACTION_FILE and ACTION_ASL_FILE may rotate */ 1672 if ((out->action != ACTION_FILE) && (out->action != ACTION_ASL_FILE)) 1673 { 1674 out->dst->flags &= ~MODULE_FLAG_ROTATE; 1675 } 1676 1677#if !TARGET_IPHONE_SIMULATOR 1678 if (out->dst->nuid == 0) _dst_add_uid(out->dst, "0"); 1679 if (out->dst->ngid == 0) _dst_add_gid(out->dst, "80"); 1680#endif 1681 } 1682 1683 if (m->ruleset == NULL) m->ruleset = out; 1684 else 1685 { 1686 for (rule = m->ruleset; rule->next != NULL; rule = rule->next); 1687 rule->next = out; 1688 } 1689 1690 return out; 1691} 1692 1693asl_out_rule_t * 1694asl_out_module_parse_line(asl_out_module_t *m, char *s) 1695{ 1696 while ((*s == ' ') || (*s == '\t')) s++; 1697 1698 if ((*s == 'Q') || (*s == '?') || (*s == '*')) 1699 { 1700 return _asl_out_module_parse_query_action(m, s); 1701 } 1702 else if (*s == '=') 1703 { 1704 return _asl_out_module_parse_set_param(m, s); 1705 } 1706 else if (*s == '>') 1707 { 1708 _asl_out_module_parse_dst(m, s + 1, 010000); 1709 } 1710 1711 return NULL; 1712} 1713 1714asl_out_module_t * 1715asl_out_module_init_from_file(const char *name, FILE *f) 1716{ 1717 asl_out_module_t *out; 1718 char *line; 1719 1720 if (f == NULL) return NULL; 1721 1722 out = asl_out_module_new(name); 1723 if (out == NULL) return NULL; 1724 1725 /* read and parse config file */ 1726 while (NULL != (line = get_line_from_file(f))) 1727 { 1728 asl_out_module_parse_line(out, line); 1729 free(line); 1730 } 1731 1732 return out; 1733} 1734 1735static asl_out_module_t * 1736_asl_out_module_find(asl_out_module_t *list, const char *name) 1737{ 1738 asl_out_module_t *x; 1739 1740 if (list == NULL) return NULL; 1741 if (name == NULL) return NULL; 1742 1743 for (x = list; x != NULL; x = x->next) 1744 { 1745 if ((x->name != NULL) && (!strcmp(x->name, name))) return x; 1746 } 1747 1748 return NULL; 1749} 1750 1751static void 1752_asl_out_module_read_and_merge_dir(asl_out_module_t **list, const char *path, uint32_t flags) 1753{ 1754 DIR *d; 1755 struct dirent *ent; 1756 FILE *f; 1757 asl_out_module_t *last, *x; 1758 1759 if (list == NULL) return; 1760 if (path == NULL) return; 1761 1762 last = *list; 1763 if (last != NULL) 1764 { 1765 while (last->next != NULL) last = last->next; 1766 } 1767 1768 d = opendir(path); 1769 if (d != NULL) 1770 { 1771 while (NULL != (ent = readdir(d))) 1772 { 1773 if ((ent->d_name != NULL) && (ent->d_name[0] != '.')) 1774 { 1775 /* merge: skip this file if we already have a module with this name */ 1776 if (_asl_out_module_find(*list, ent->d_name) != NULL) continue; 1777 1778 char tmp[MAXPATHLEN]; 1779 snprintf(tmp, sizeof(tmp), "%s/%s", path, ent->d_name); 1780 f = fopen(tmp, "r"); 1781 if (f != NULL) 1782 { 1783 x = asl_out_module_init_from_file(ent->d_name, f); 1784 fclose(f); 1785 1786 if (x != NULL) 1787 { 1788 x->flags |= flags; 1789 1790 if (!strcmp(ent->d_name, ASL_MODULE_NAME)) 1791 { 1792 /* com.apple.asl goes at the head of the list */ 1793 x->next = *list; 1794 *list = x; 1795 if (last == NULL) last = *list; 1796 } 1797 else if (*list == NULL) 1798 { 1799 *list = x; 1800 last = *list; 1801 } 1802 else 1803 { 1804 last->next = x; 1805 last = x; 1806 } 1807 } 1808 } 1809 } 1810 } 1811 1812 closedir(d); 1813 } 1814} 1815 1816asl_out_module_t * 1817asl_out_module_init(void) 1818{ 1819 asl_out_module_t *out = NULL; 1820 1821#if TARGET_IPHONE_SIMULATOR 1822 char *sim_root_path, *sim_resources_path; 1823 char *asl_conf, *asl_conf_dir, *asl_conf_local_dir; 1824 1825 sim_root_path = getenv("IPHONE_SIMULATOR_ROOT"); 1826 assert(sim_root_path); 1827 1828 sim_resources_path = getenv("IPHONE_SHARED_RESOURCES_DIRECTORY"); 1829 assert(sim_resources_path); 1830 1831 asprintf(&asl_conf, "%s%s", sim_root_path, _PATH_ASL_CONF); 1832 asprintf(&asl_conf_dir, "%s%s", sim_root_path, _PATH_ASL_CONF_DIR); 1833 asprintf(&asl_conf_local_dir, "%s%s", sim_resources_path, _PATH_ASL_CONF_DIR); 1834 1835 _asl_out_module_read_and_merge_dir(&out, asl_conf_local_dir, MODULE_FLAG_LOCAL); 1836 free(asl_conf_local_dir); 1837 1838 _asl_out_module_read_and_merge_dir(&out, asl_conf_dir, 0); 1839 free(asl_conf_dir); 1840#else 1841 _asl_out_module_read_and_merge_dir(&out, _PATH_ASL_CONF_LOCAL_DIR, MODULE_FLAG_LOCAL); 1842 _asl_out_module_read_and_merge_dir(&out, _PATH_ASL_CONF_DIR, 0); 1843#endif 1844 1845 if (_asl_out_module_find(out, ASL_MODULE_NAME) == NULL) 1846 { 1847 /* system just has old-style /etc/asl.conf */ 1848#if TARGET_IPHONE_SIMULATOR 1849 FILE *f = fopen(asl_conf, "r"); 1850 free(asl_conf); 1851#else 1852 FILE *f = fopen(_PATH_ASL_CONF, "r"); 1853#endif 1854 if (f != NULL) 1855 { 1856 asl_out_module_t *x = asl_out_module_init_from_file(ASL_MODULE_NAME, f); 1857 fclose(f); 1858 if (x != NULL) 1859 { 1860 x->next = out; 1861 out = x; 1862 } 1863 } 1864 } 1865 1866 return out; 1867} 1868 1869/* 1870 * Print rule 1871 */ 1872char * 1873asl_out_module_rule_to_string(asl_out_rule_t *r) 1874{ 1875 uint32_t len; 1876 char *str, *out; 1877 1878 if (r == NULL) 1879 { 1880 asprintf(&out, "NULL rule"); 1881 return out; 1882 } 1883 1884 str = asl_msg_to_string(r->query, &len); 1885 1886 asprintf(&out, " %s%s%s%s%s", 1887 asl_out_action_name[r->action], 1888 (r->query == NULL) ? "" : " ", 1889 (r->query == NULL) ? "" : str, 1890 (r->options == NULL) ? "" : " ", 1891 (r->options == NULL) ? "" : r->options); 1892 1893 free(str); 1894 return out; 1895} 1896 1897/* 1898 * Print module 1899 */ 1900void 1901asl_out_module_print(FILE *f, asl_out_module_t *m) 1902{ 1903 asl_out_rule_t *r, *n; 1904 asl_out_dst_data_t *o; 1905 uint32_t i, ttlnset; 1906 1907 n = NULL; 1908 for (r = m->ruleset; r != NULL; r = n) 1909 { 1910 uint32_t len; 1911 char *str = asl_msg_to_string(r->query, &len); 1912 1913 fprintf(f, " %s", asl_out_action_name[r->action]); 1914 if (r->query != NULL) fprintf(f, " %s", str); 1915 if (r->options != NULL) fprintf(f, " %s", r->options); 1916 if (r->action == ACTION_OUT_DEST) 1917 { 1918 o = r->dst; 1919 if (o == NULL) 1920 { 1921 fprintf(f, " data: NULL"); 1922 } 1923 else 1924 { 1925 fprintf(f, "%s\n", o->path); 1926 fprintf(f, " rules: %u\n", o->refcount - 1); 1927 fprintf(f, " dest: %s\n", (o->rotate_dir == NULL) ? "(none)" : o->rotate_dir); 1928 fprintf(f, " format: %s\n", (o->fmt == NULL) ? "std" : o->fmt); 1929 fprintf(f, " time_format: %s\n", (o->tfmt == NULL) ? "lcl" : o->tfmt); 1930 fprintf(f, " flags: 0x%08x", o->flags); 1931 if (o->flags != 0) 1932 { 1933 char c = '('; 1934 fprintf(f, " "); 1935 if (o->flags & MODULE_FLAG_ENABLED) 1936 { 1937 fprintf(f, "%cenabled", c); 1938 c = ' '; 1939 } 1940 if (o->flags & MODULE_FLAG_SOFT_WRITE) 1941 { 1942 fprintf(f, "%csoft", c); 1943 c = ' '; 1944 } 1945 if (o->flags & MODULE_FLAG_ROTATE) 1946 { 1947 fprintf(f, "%crotate", c); 1948 c = ' '; 1949 } 1950 if (o->flags & MODULE_FLAG_COALESCE) 1951 { 1952 fprintf(f, "%ccoalesce", c); 1953 c = ' '; 1954 } 1955 if (o->flags & MODULE_FLAG_COMPRESS) 1956 { 1957 fprintf(f, "%ccompress", c); 1958 c = ' '; 1959 } 1960 if (o->flags & MODULE_FLAG_STYLE_SEC) 1961 { 1962 fprintf(f, "%cseconds", c); 1963 c = ' '; 1964 } 1965 if (o->flags & MODULE_FLAG_STYLE_SEQ) 1966 { 1967 fprintf(f, "%csequence", c); 1968 c = ' '; 1969 } 1970 if (o->flags & MODULE_FLAG_STYLE_UTC) 1971 { 1972 fprintf(f, "%cutc", c); 1973 c = ' '; 1974 } 1975 if (o->flags & MODULE_FLAG_STYLE_UTC_B) 1976 { 1977 fprintf(f, "%cutc-basic", c); 1978 c = ' '; 1979 } 1980 if (o->flags & MODULE_FLAG_STYLE_LCL) 1981 { 1982 fprintf(f, "%clocal", c); 1983 c = ' '; 1984 } 1985 if (o->flags & MODULE_FLAG_STYLE_LCL_B) 1986 { 1987 fprintf(f, "%clocal-basic", c); 1988 c = ' '; 1989 } 1990 if (o->flags & MODULE_FLAG_BASESTAMP) 1991 { 1992 fprintf(f, "%cbasestamp", c); 1993 c = ' '; 1994 } 1995 if (o->flags & MODULE_FLAG_NONSTD_DIR) 1996 { 1997 fprintf(f, "%cnon-std_dir", c); 1998 c = ' '; 1999 } 2000 if (o->flags & MODULE_FLAG_EXTERNAL) 2001 { 2002 fprintf(f, "%cexternal", c); 2003 c = ' '; 2004 } 2005 if (o->flags & MODULE_FLAG_CRASHLOG) 2006 { 2007 fprintf(f, "%ccrashlog", c); 2008 c = ' '; 2009 } 2010 if (o->flags & MODULE_FLAG_TYPE_ASL) 2011 { 2012 fprintf(f, "%casl_file", c); 2013 c = ' '; 2014 } 2015 if (o->flags & MODULE_FLAG_TYPE_ASL_DIR) 2016 { 2017 fprintf(f, "%casl_directory", c); 2018 c = ' '; 2019 } 2020 fprintf(f, ")"); 2021 } 2022 fprintf(f, "\n"); 2023 2024 fprintf(f, " ttl: %u", o->ttl[LEVEL_ALL]); 2025 ttlnset = 0; 2026 for (i = 0; (i <= 7) & (ttlnset == 0); i++) if (o->ttl[i] != 0) ttlnset = 1; 2027 if (ttlnset != 0) for (i = 0; i <= 7; i++) printf(" [%d %d]", i, (o->ttl[i] == 0) ? o->ttl[LEVEL_ALL] : o->ttl[i]); 2028 fprintf(f, "\n"); 2029 2030 fprintf(f, " mode: 0%o\n", o->mode); 2031 fprintf(f, " file_max: %lu\n", o->file_max); 2032 fprintf(f, " all_max: %lu\n", o->all_max); 2033#if !TARGET_IPHONE_SIMULATOR 2034 fprintf(f, " uid:"); 2035 for (i = 0; i < o->nuid; i++) fprintf(f, " %d", o->uid[i]); 2036 fprintf(f, "\n"); 2037 fprintf(f, " gid:"); 2038 for (i = 0; i < o->ngid; i++) fprintf(f, " %d", o->gid[i]); 2039#endif 2040 } 2041 } 2042 2043 fprintf(f, "\n"); 2044 n = r->next; 2045 2046 free(str); 2047 } 2048} 2049 2050void 2051asl_out_file_list_free(asl_out_file_list_t *l) 2052{ 2053 asl_out_file_list_t *n; 2054 2055 if (l == NULL) return; 2056 2057 while (l != NULL) 2058 { 2059 free(l->name); 2060 n = l->next; 2061 free(l); 2062 l = n; 2063 } 2064} 2065 2066/* 2067 * Checks input name for the form base[.stamp][.gz] 2068 * name == base is allowed if src is true. 2069 * base.gz is not allowed. 2070 * Output parameter stamp must be freed by caller. 2071 */ 2072bool 2073_check_file_name(const char *name, const char *base, bool src, char **stamp) 2074{ 2075 size_t baselen, nparts; 2076 const char *p, *q, *part[2]; 2077 bool isgz = false; 2078 2079 if (name == NULL) return false; 2080 if (base == NULL) return false; 2081 2082 baselen = strlen(base); 2083 if (baselen == 0) return false; 2084 2085 if (stamp != NULL) *stamp = NULL; 2086 2087 if (strncmp(name, base, baselen)) return false; 2088 2089 p = name + baselen; 2090 2091 /* name == base not allowed (it's the "active" file) */ 2092 if (*p == '\0') return false; 2093 2094 /* name must be base.something */ 2095 if (*p != '.') return false; 2096 2097 /* maximum of 2 parts (stamp and gz) */ 2098 nparts = 0; 2099 for (q = p; *q != '\0'; q++) 2100 { 2101 if (*q == '.') 2102 { 2103 if (nparts == 2) return false; 2104 part[nparts++] = q + 1; 2105 } 2106 } 2107 2108 if (nparts == 0) return false; 2109 2110 isgz = strcmp(part[nparts - 1], "gz") == 0; 2111 2112 /* no compressed files in src */ 2113 if (src && isgz) return false; 2114 2115 /* expecting base.stamp or base.stamp.gz */ 2116 2117 if (nparts == 1) 2118 { 2119 /* compressed files must have a stamp (base.gz is not allowed) */ 2120 if (isgz) return false; 2121 2122 /* got base.stamp */ 2123 if (stamp != NULL) *stamp = strdup(part[0]); 2124 return true; 2125 } 2126 2127 /* expecting base.stamp.gz */ 2128 if (!isgz) return false; 2129 2130 /* got base.stamp.gz */ 2131 if (stamp != NULL) 2132 { 2133 *stamp = strdup(part[0]); 2134 char *x = strchr(*stamp, '.'); 2135 if (x != NULL) *x = '\0'; 2136 } 2137 2138 return true; 2139} 2140 2141/* 2142 * Find files in a directory (dir) that all have a common prefix (base). 2143 * Bits in flags further control the search. 2144 * 2145 * MODULE_FLAG_STYLE_SEQ means a numeric sequence number is expected, although not required. 2146 * E.g. foo.log foo.log.0 2147 * 2148 * MODULE_FLAG_STYLE_SEC also means a numeric sequence number is required following an 'T' character. 2149 * The numeric value is the file's timestamp in seconds. E.g foo.log.T1335200452 2150 * 2151 * MODULE_FLAG_STYLE_UTC requires a date/time component as the file's timestamp. 2152 * E.g. foo.2012-04-06T15:30:00Z 2153 * 2154 * MODULE_FLAG_STYLE_UTC_B requires a date/time component as the file's timestamp. 2155 * E.g. foo.20120406T153000Z 2156 * 2157 * MODULE_FLAG_STYLE_LCL requires a date/time component as the file's timestamp. 2158 * E.g. foo.2012-04-06T15:30:00-7 2159 * 2160 * MODULE_FLAG_STYLE_LCL_B requires a date/time component as the file's timestamp. 2161 * E.g. foo.20120406T153000-07 2162 */ 2163int 2164_parse_stamp_style(char *stamp, uint32_t flags, uint32_t *sp, time_t *tp) 2165{ 2166 int i, n; 2167 bool digits; 2168 struct tm t; 2169 char zone; 2170 uint32_t h, m, s; 2171 long utc_offset = 0; 2172 time_t ftime = 0; 2173 2174 /* check for NULL (no stamp) */ 2175 if (stamp == NULL) return STAMP_STYLE_NULL; 2176 2177 /* check for MODULE_FLAG_STYLE_SEC (foo.T12345678) */ 2178 if (stamp[0] == 'T') 2179 { 2180 n = atoi(stamp + 1); 2181 if ((n == 0) && strcmp(stamp + 1, "0")) return STAMP_STYLE_INVALID; 2182 if (tp != NULL) *tp = (time_t)n; 2183 2184 return STAMP_STYLE_SEC; 2185 } 2186 2187 /* check for MODULE_FLAG_STYLE_SEQ (foo.0 or foo.2.gz) */ 2188 digits = true; 2189 for (i = 0; digits && (stamp[i] != '\0'); i++) digits = (stamp[i] >= '0') && (stamp[i] <= '9'); 2190 2191 if (!digits && (!strcmp(stamp + i, ".gz"))) digits = true; 2192 2193 if (digits) 2194 { 2195 n = atoi(stamp); 2196 if (sp != NULL) *sp = (uint32_t)n; 2197 return STAMP_STYLE_SEQ; 2198 } 2199 2200 /* check for MODULE_FLAG_STYLE_UTC, UTC_B, LCL, or LCL_B */ 2201 memset(&t, 0, sizeof(t)); 2202 h = m = s = 0; 2203 2204 n = 0; 2205 if ((flags & MODULE_FLAG_STYLE_UTC) || (flags & MODULE_FLAG_STYLE_LCL)) 2206 { 2207 n = sscanf(stamp, "%d-%d-%dT%d:%d:%d%c%u:%u:%u", &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min, &t.tm_sec, &zone, &h, &m, &s); 2208 } 2209 else if ((flags & MODULE_FLAG_STYLE_UTC_B) || (flags & MODULE_FLAG_STYLE_LCL_B)) 2210 { 2211 n = sscanf(stamp, "%4d%2d%2dT%2d%2d%2d%c%2u%2u%2u", &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min, &t.tm_sec, &zone, &h, &m, &s); 2212 } 2213 else 2214 { 2215 return STAMP_STYLE_INVALID; 2216 } 2217 2218 if (n < 6) return STAMP_STYLE_INVALID; 2219 2220 if (n == 6) 2221 { 2222 zone = 'J'; 2223 } 2224 else if ((zone == '-') || (zone == '+')) 2225 { 2226 if (n >= 8) utc_offset += (3600 * h); 2227 if (n >= 9) utc_offset += (60 * m); 2228 if (n == 10) utc_offset += s; 2229 if (zone == '-') utc_offset *= -1; 2230 } 2231 else if ((zone >= 'A') && (zone <= 'Z')) 2232 { 2233 if (zone < 'J') utc_offset = 3600 * ((zone - 'A') + 1); 2234 else if ((zone >= 'K') && (zone <= 'M')) utc_offset = 3600 * (zone - 'A'); 2235 else if (zone <= 'Y') utc_offset = -3600 * ((zone - 'N') + 1); 2236 } 2237 else if ((zone >= 'a') && (zone <= 'z')) 2238 { 2239 if (zone < 'j') utc_offset = 3600 * ((zone - 'a') + 1); 2240 else if ((zone >= 'k') && (zone <= 'm')) utc_offset = 3600 * (zone - 'a'); 2241 else if (zone <= 'y') utc_offset = -3600 * ((zone - 'n') + 1); 2242 } 2243 else 2244 { 2245 return STAMP_STYLE_INVALID; 2246 } 2247 2248 t.tm_year -= 1900; 2249 t.tm_mon -= 1; 2250 t.tm_sec += utc_offset; 2251 t.tm_isdst = -1; 2252 2253 if ((zone == 'J') || (zone == 'j')) ftime = mktime(&t); 2254 else ftime = timegm(&t); 2255 2256 if (tp != NULL) *tp = ftime; 2257 2258 return STAMP_STYLE_UTC_OR_LCL; 2259} 2260 2261asl_out_file_list_t * 2262asl_list_log_files(const char *dir, const char *base, bool src, uint32_t flags) 2263{ 2264 DIR *d; 2265 struct dirent *ent; 2266 char path[MAXPATHLEN]; 2267 uint32_t seq; 2268 time_t ftime; 2269 struct stat sb; 2270 int pstyle, fstyle; 2271 asl_out_file_list_t *out, *x, *y; 2272 2273 if (dir == NULL) return NULL; 2274 if (base == NULL) return NULL; 2275 2276 out = NULL; 2277 2278 d = opendir(dir); 2279 if (d == NULL) return NULL; 2280 2281 while (NULL != (ent = readdir(d))) 2282 { 2283 char *stamp = NULL; 2284 bool check; 2285 2286 if (ent->d_name == NULL) continue; 2287 2288 check = _check_file_name(ent->d_name, base, src, &stamp); 2289 if (!check) continue; 2290 2291 seq = IndexNull; 2292 ftime = 0; 2293 2294 pstyle = _parse_stamp_style(stamp, flags, &seq, &ftime); 2295 free(stamp); 2296 2297 if (pstyle == STAMP_STYLE_INVALID) continue; 2298 2299 fstyle = STAMP_STYLE_NULL; 2300 if (flags & MODULE_FLAG_STYLE_SEC) fstyle = STAMP_STYLE_SEC; 2301 else if (flags & MODULE_FLAG_STYLE_SEQ) fstyle = STAMP_STYLE_SEQ; 2302 else if ((flags & MODULE_FLAG_STYLE_UTC) || (flags & MODULE_FLAG_STYLE_LCL)) fstyle = STAMP_STYLE_UTC_OR_LCL; 2303 else if ((flags & MODULE_FLAG_STYLE_UTC_B) || (flags & MODULE_FLAG_STYLE_LCL_B)) fstyle = STAMP_STYLE_UTC_OR_LCL; 2304 2305 /* 2306 * accept the file if: 2307 * style is STAMP_STYLE_NULL (no timestamp) 2308 * src is true and style is STAMP_STYLE_SEC 2309 * actual style matches the style implied by the input flags 2310 */ 2311 2312 check = false; 2313 if (pstyle == STAMP_STYLE_NULL) check = true; 2314 if ((pstyle == STAMP_STYLE_SEC) && src) check = true; 2315 if (pstyle == fstyle) check = true; 2316 2317 if (!check) continue; 2318 2319 x = (asl_out_file_list_t *)calloc(1, sizeof(asl_out_file_list_t)); 2320 if (x == NULL) 2321 { 2322 asl_out_file_list_free(out); 2323 return NULL; 2324 } 2325 2326 x->name = strdup(ent->d_name); 2327 x->ftime = ftime; 2328 x->seq = seq; 2329 2330 memset(&sb, 0, sizeof(sb)); 2331 snprintf(path, sizeof(path), "%s/%s", dir, ent->d_name); 2332 if (stat(path, &sb) == 0) 2333 { 2334 x->size = sb.st_size; 2335 if (pstyle == STAMP_STYLE_SEQ) 2336 { 2337 x->ftime = sb.st_birthtimespec.tv_sec; 2338 if (x->ftime == 0) x->ftime = sb.st_mtimespec.tv_sec; 2339 } 2340 } 2341 2342 if (pstyle == STAMP_STYLE_SEQ) 2343 { 2344 if (out == NULL) 2345 { 2346 out = x; 2347 } 2348 else if ((x->seq == IndexNull) || ((x->seq < out->seq) && (out->seq != IndexNull))) 2349 { 2350 x->next = out; 2351 out->prev = x; 2352 out = x; 2353 } 2354 else 2355 { 2356 for (y = out; y != NULL; y = y->next) 2357 { 2358 if (y->next == NULL) 2359 { 2360 y->next = x; 2361 x->prev = y; 2362 break; 2363 } 2364 else if ((x->seq < y->next->seq) && (y->next->seq != IndexNull)) 2365 { 2366 x->next = y->next; 2367 y->next = x; 2368 x->prev = y; 2369 x->next->prev = x; 2370 break; 2371 } 2372 } 2373 } 2374 } 2375 else 2376 { 2377 if (out == NULL) 2378 { 2379 out = x; 2380 } 2381 else if (x->ftime < out->ftime) 2382 { 2383 x->next = out; 2384 out->prev = x; 2385 out = x; 2386 } 2387 else 2388 { 2389 for (y = out; y != NULL; y = y->next) 2390 { 2391 if (y->next == NULL) 2392 { 2393 y->next = x; 2394 x->prev = y; 2395 break; 2396 } 2397 else if (x->ftime < y->next->ftime) 2398 { 2399 x->next = y->next; 2400 y->next = x; 2401 x->prev = y; 2402 x->next->prev = x; 2403 break; 2404 } 2405 } 2406 } 2407 } 2408 } 2409 2410 closedir(d); 2411 return out; 2412} 2413 2414/* 2415 * List the source files for an output asl_out_dst_data_t 2416 */ 2417asl_out_file_list_t * 2418asl_list_src_files(asl_out_dst_data_t *dst) 2419{ 2420 char *base; 2421 uint32_t flags = MODULE_FLAG_STYLE_SEC; 2422 asl_out_file_list_t *out; 2423 2424 if (dst == NULL) return NULL; 2425 if (dst->path == NULL) return NULL; 2426 2427 /* 2428 * MODULE_FLAG_EXTERNAL means some process other than syslogd writes the file. 2429 * We check for its existence, and that it is non-zero in size. 2430 */ 2431 if (dst->flags & MODULE_FLAG_EXTERNAL) 2432 { 2433 struct stat sb; 2434 2435 memset(&sb, 0, sizeof(struct stat)); 2436 2437 if (stat(dst->path, &sb) == 0) 2438 { 2439 if (S_ISREG(sb.st_mode) && (sb.st_size != 0)) 2440 { 2441 out = (asl_out_file_list_t *)calloc(1, sizeof(asl_out_file_list_t)); 2442 if (out != NULL) 2443 { 2444 char *p = strrchr(dst->path, '/'); 2445 if (p == NULL) p = dst->path; 2446 else p++; 2447 out->name = strdup(p); 2448 out->ftime = sb.st_birthtimespec.tv_sec; 2449 if (out->ftime == 0) out->ftime = sb.st_mtimespec.tv_sec; 2450 return out; 2451 } 2452 } 2453 } 2454 2455 return NULL; 2456 } 2457 2458 /* 2459 * Checkpoint / source format may be one of: 2460 * MODULE_FLAG_STYLE_SEC (foo.T12345678.log), 2461 * MODULE_FLAG_STYLE_UTC (foo.20120-06-24T12:34:56Z.log) 2462 * MODULE_FLAG_STYLE_UTC_B (foo.201200624T123456Z.log) 2463 * MODULE_FLAG_STYLE_LCL (foo.20120-06-24T12:34:56-7.log) 2464 * MODULE_FLAG_STYLE_LCL_B (foo.201200624T123456-07.log) 2465 * 2466 * MODULE_FLAG_STYLE_SEC format is used for sequenced (MODULE_FLAG_STYLE_SEQ) files. 2467 * aslmanager converts the file names. 2468 */ 2469 2470 if (dst->flags & MODULE_FLAG_STYLE_UTC) flags = MODULE_FLAG_STYLE_UTC; 2471 else if (dst->flags & MODULE_FLAG_STYLE_UTC_B) flags = MODULE_FLAG_STYLE_UTC_B; 2472 else if (dst->flags & MODULE_FLAG_STYLE_LCL) flags = MODULE_FLAG_STYLE_LCL; 2473 else if (dst->flags & MODULE_FLAG_STYLE_LCL_B) flags = MODULE_FLAG_STYLE_LCL_B; 2474 2475 if ((dst->rotate_dir == NULL) && ((dst->flags & MODULE_FLAG_STYLE_SEQ) == 0) && ((dst->flags & MODULE_FLAG_COMPRESS) == 0)) 2476 { 2477 /* files do not move to a dest dir, get renamed, or get compressed - nothing to do */ 2478 return NULL; 2479 } 2480 2481 base = strrchr(dst->path, '/'); 2482 if (base == NULL) return NULL; 2483 2484 *base = '\0'; 2485 base++; 2486 2487 out = asl_list_log_files(dst->path, base, true, flags); 2488 2489 if (base != NULL) *--base = '/'; 2490 2491 return out; 2492} 2493 2494/* 2495 * List the destination files for an output asl_out_dst_data_t 2496 */ 2497asl_out_file_list_t * 2498asl_list_dst_files(asl_out_dst_data_t *dst) 2499{ 2500 char *base, *dst_dir; 2501 asl_out_file_list_t *out; 2502 2503 if (dst == NULL) return NULL; 2504 if (dst->path == NULL) return NULL; 2505 2506 base = strrchr(dst->path, '/'); 2507 if (base == NULL) return NULL; 2508 2509 *base = '\0'; 2510 base++; 2511 2512 dst_dir = dst->rotate_dir; 2513 if (dst_dir == NULL) dst_dir = dst->path; 2514 2515 out = asl_list_log_files(dst_dir, base, false, dst->flags); 2516 2517 if (base != NULL) *--base = '/'; 2518 2519 return out; 2520} 2521 2522static int 2523asl_secure_open_dir(const char *path) 2524{ 2525 int fd, i; 2526 char **path_parts; 2527 2528 if (path == NULL) return -1; 2529 if (path[0] != '/') return -1; 2530 2531 path_parts = explode(path + 1, "/"); 2532 if (path_parts == NULL) return 0; 2533 2534 fd = open("/", O_RDONLY | O_NOFOLLOW, 0); 2535 if (fd < 0) 2536 { 2537 free_string_list(path_parts); 2538 return -1; 2539 } 2540 2541 for (i = 0; path_parts[i] != NULL; i++) 2542 { 2543 int fd_next, status; 2544 struct stat sb; 2545 2546 fd_next = openat(fd, path_parts[i], O_RDONLY | O_NOFOLLOW, 0); 2547 close(fd); 2548 fd = fd_next; 2549 if (fd < 0) 2550 { 2551 free_string_list(path_parts); 2552 return -1; 2553 } 2554 2555 memset(&sb, 0, sizeof(sb)); 2556 2557 status = fstat(fd, &sb); 2558 if (status < 0) 2559 { 2560 free_string_list(path_parts); 2561 return -1; 2562 } 2563 2564 if (!S_ISDIR(sb.st_mode)) 2565 { 2566 free_string_list(path_parts); 2567 return -1; 2568 } 2569 } 2570 2571 free_string_list(path_parts); 2572 return fd; 2573} 2574 2575int 2576asl_secure_chown_chmod_dir(const char *path, uid_t uid, gid_t gid, mode_t mode) 2577{ 2578 int fd, status; 2579 2580 fd = asl_secure_open_dir(path); 2581 if (fd < 0) return fd; 2582 2583 status = fchown(fd, uid, gid); 2584 if (status < 0) 2585 { 2586 close(fd); 2587 return -1; 2588 } 2589 2590 if (mode >= 0) status = fchmod(fd, mode); 2591 close(fd); 2592 2593 if (status < 0) return -1; 2594 return 0; 2595} 2596