xref: /NextBSD/lib/libasl/asl_common.c (revision 33da5adc555b3bc29986eeadca03829e4ad06b1e)
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