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