xref: /NextBSD/lib/libasl/asl_core.c (revision 33da5adc555b3bc29986eeadca03829e4ad06b1e)
1 /*
2  * Copyright (c) 2007-2013 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 <ctype.h>
25 #include <stdio.h>
26 #include <stdbool.h>
27 #include <uuid/uuid.h>
28 #include <asl.h>
29 #include <asl_string.h>
30 #include <asl_core.h>
31 #include <asl_private.h>
32 #include <string.h>
33 //#include <membership.h>
34 #include <pthread.h>
35 #ifdef __FreeBSD__
36 #include <atomic_compat.h>
37 #else
38 #include <libkern/OSAtomic.h>
39 #endif
40 #include <servers/bootstrap.h>
41 #include <bootstrap_priv.h>
42 #include <mach/kern_return.h>
43 #include <mach/mach_init.h>
44 #include <mach/mach_vm.h>
45 #include <mach/vm_map.h>
46 #include <vm/vm_param.h>
47 #include <dispatch/dispatch.h>
48 
49 const char *ASL_LEVEL_TO_STRING[] =
50 {
51 	ASL_STRING_EMERG,
52 	ASL_STRING_ALERT,
53 	ASL_STRING_CRIT,
54 	ASL_STRING_ERR,
55 	ASL_STRING_WARNING,
56 	ASL_STRING_NOTICE,
57 	ASL_STRING_INFO,
58 	ASL_STRING_DEBUG
59 };
60 
61 static char *asl_filesystem_path_database = NULL;
62 static char *asl_filesystem_path_archive = NULL;
63 
64 /*
65  * Message ID generation
66  */
67 static uint64_t _asl_core_msg_next_id = 1;
68 static pthread_mutex_t core_lock = PTHREAD_MUTEX_INITIALIZER;
69 
70 #define mix(a, b, c) \
71 { \
72 	a -= b; a -= c; a ^= (c>>13); \
73 	b -= c; b -= a; b ^= (a<< 8); \
74 	c -= a; c -= b; c ^= (b>>13); \
75 	a -= b; a -= c; a ^= (c>>12); \
76 	b -= c; b -= a; b ^= (a<<16); \
77 	c -= a; c -= b; c ^= (b>> 5); \
78 	a -= b; a -= c; a ^= (c>> 3); \
79 	b -= c; b -= a; b ^= (a<<10); \
80 	c -= a; c -= b; c ^= (b>>15); \
81 }
82 
83 /*
84  * Get ASL server mach port.
85  * reset != 0 flushes cached port.
86  * reset < 0 returns MACH_PORT_NULL
87  */
88 mach_port_t
asl_core_get_service_port(int reset)89 asl_core_get_service_port(int reset)
90 {
91 	static mach_port_t server_port = MACH_PORT_NULL;
92 	mach_port_t tmp;
93 	kern_return_t kstatus;
94 
95 	if ((reset != 0) && (server_port != MACH_PORT_NULL))
96 	{
97 		mach_port_t tmp = server_port;
98 		server_port = MACH_PORT_NULL;
99 		mach_port_deallocate(mach_task_self(), tmp);
100 	}
101 
102 	if (reset < 0) return MACH_PORT_NULL;
103 
104 	if (server_port != MACH_PORT_NULL) return server_port;
105 
106 	tmp = MACH_PORT_NULL;
107 	char *str = getenv("ASL_DISABLE");
108 	if ((str != NULL) && (!strcmp(str, "1"))) return MACH_PORT_NULL;
109 
110 	kstatus = bootstrap_look_up2(bootstrap_port, ASL_SERVICE_NAME, &tmp, 0, BOOTSTRAP_PRIVILEGED_SERVER);
111 	if ((kstatus != KERN_SUCCESS) || (tmp == MACH_PORT_NULL)) return MACH_PORT_NULL;
112 
113 	if (!OSAtomicCompareAndSwap32Barrier(MACH_PORT_NULL, tmp, (int32_t *)&server_port))
114 	{
115 		mach_port_deallocate(mach_task_self(), tmp);
116 	}
117 
118 	return server_port;
119 }
120 
121 /*
122  * Hash is used to improve string search.
123  */
124 uint32_t
asl_core_string_hash(const char * s,uint32_t inlen)125 asl_core_string_hash(const char *s, uint32_t inlen)
126 {
127 	uint32_t a, b, c, l, len;
128 
129 	if (s == NULL) return 0;
130 
131 	l = inlen;
132 	if (l == 0)
133 	{
134 		if (s[0] == '\0') return 0;
135 		l = strlen(s);
136 	}
137 
138 	len = l;
139 	a = b = 0x9e3779b9;
140 	c = 0;
141 
142 	while (len >= 12)
143 	{
144 		a += (s[0] + ((uint32_t)s[1]<<8) + ((uint32_t)s[ 2]<<16) + ((uint32_t)s[ 3]<<24));
145 		b += (s[4] + ((uint32_t)s[5]<<8) + ((uint32_t)s[ 6]<<16) + ((uint32_t)s[ 7]<<24));
146 		c += (s[8] + ((uint32_t)s[9]<<8) + ((uint32_t)s[10]<<16) + ((uint32_t)s[11]<<24));
147 
148 		mix(a, b, c);
149 
150 		s += 12;
151 		len -= 12;
152 	}
153 
154 	c += l;
155 	switch(len)
156 	{
157 		case 11: c += ((uint32_t)s[10]<<24);
158 		case 10: c += ((uint32_t)s[9]<<16);
159 		case 9 : c += ((uint32_t)s[8]<<8);
160 
161 		case 8 : b += ((uint32_t)s[7]<<24);
162 		case 7 : b += ((uint32_t)s[6]<<16);
163 		case 6 : b += ((uint32_t)s[5]<<8);
164 		case 5 : b += s[4];
165 
166 		case 4 : a += ((uint32_t)s[3]<<24);
167 		case 3 : a += ((uint32_t)s[2]<<16);
168 		case 2 : a += ((uint32_t)s[1]<<8);
169 		case 1 : a += s[0];
170 	}
171 
172 	mix(a, b, c);
173 
174 	if (c == 0) c = 1;
175 	return c;
176 }
177 
178 const char *
asl_core_error(uint32_t code)179 asl_core_error(uint32_t code)
180 {
181 	switch (code)
182 	{
183 		case ASL_STATUS_OK: return "Operation Succeeded";
184 		case ASL_STATUS_INVALID_ARG: return "Invalid Argument";
185 		case ASL_STATUS_INVALID_STORE: return "Invalid Data Store";
186 		case ASL_STATUS_INVALID_STRING: return "Invalid String";
187 		case ASL_STATUS_INVALID_ID: return "Invalid ID Number";
188 		case ASL_STATUS_INVALID_MESSAGE: return "Invalid Message";
189 		case ASL_STATUS_NOT_FOUND: return "Not Found";
190 		case ASL_STATUS_READ_FAILED: return "Read Operation Failed";
191 		case ASL_STATUS_WRITE_FAILED: return "Write Operation Failed";
192 		case ASL_STATUS_NO_MEMORY: return "System Memory Allocation Failed";
193 		case ASL_STATUS_ACCESS_DENIED: return "Access Denied";
194 		case ASL_STATUS_READ_ONLY: return "Read Only Access";
195 		case ASL_STATUS_WRITE_ONLY: return "Write Only Access";
196 		case ASL_STATUS_MATCH_FAILED: return "Match Failed";
197 		case ASL_STATUS_NO_RECORDS: return "No More Records";
198 	}
199 
200 	return "Operation Failed";
201 }
202 
203 const char *
asl_core_level_to_string(uint32_t level)204 asl_core_level_to_string(uint32_t level)
205 {
206 	if (level > ASL_LEVEL_DEBUG) return "invalid";
207 	return ASL_LEVEL_TO_STRING[level];
208 }
209 
210 static ASL_STATUS
asl_core_check_user_access(int32_t msgu,int32_t readu)211 asl_core_check_user_access(int32_t msgu, int32_t readu)
212 {
213 	/* -1 means anyone may read */
214 	if (msgu == -1) return ASL_STATUS_OK;
215 
216 	/* Check for exact match */
217 	if (msgu == readu) return ASL_STATUS_OK;
218 
219 	return ASL_STATUS_ACCESS_DENIED;
220 }
221 
222 static ASL_STATUS
asl_core_check_group_access(int32_t msgg,int32_t readu,int32_t readg)223 asl_core_check_group_access(int32_t msgg, int32_t readu, int32_t readg)
224 {
225 	int check;
226 	uuid_t uu, gu;
227 
228 	/* -1 means anyone may read */
229 	if (msgg == -1) return ASL_STATUS_OK;
230 
231 	/* Check for exact match */
232 	if (msgg == readg) return ASL_STATUS_OK;
233 
234 #if 0
235 	/* Check if user (u) is in read group (msgg) */
236 	mbr_uid_to_uuid(readu, uu);
237 	mbr_gid_to_uuid(msgg, gu);
238 
239 	check = 0;
240 	mbr_check_membership(uu, gu, &check);
241 	if (check != 0) return ASL_STATUS_OK;
242 
243 	return ASL_STATUS_ACCESS_DENIED;
244 #endif
245 	return ASL_STATUS_OK;
246 }
247 
248 ASL_STATUS
asl_core_check_access(int32_t msgu,int32_t msgg,int32_t readu,int32_t readg,uint16_t flags)249 asl_core_check_access(int32_t msgu, int32_t msgg, int32_t readu, int32_t readg, uint16_t flags)
250 {
251 	uint16_t uset, gset;
252 
253 	/* root (uid 0) may always read */
254 	if (readu == 0) return ASL_STATUS_OK;
255 
256 	uset = flags & ASL_MSG_FLAG_READ_UID_SET;
257 	gset = flags & ASL_MSG_FLAG_READ_GID_SET;
258 
259 	/* if no access controls are set, anyone may read */
260 	if ((uset | gset) == 0) return ASL_STATUS_OK;
261 
262 	/* if only uid is set, then access is only by uid match */
263 	if ((uset != 0) && (gset == 0)) return asl_core_check_user_access(msgu, readu);
264 
265 	/* if only gid is set, then access is only by gid match */
266 	if ((uset == 0) && (gset != 0)) return asl_core_check_group_access(msgg, readu, readg);
267 
268 	/* both uid and gid are set - check user, then group */
269 	if ((asl_core_check_user_access(msgu, readu)) == ASL_STATUS_OK) return ASL_STATUS_OK;
270 	return asl_core_check_group_access(msgg, readu, readg);
271 }
272 
273 uint64_t
asl_core_htonq(uint64_t n)274 asl_core_htonq(uint64_t n)
275 {
276 #ifdef __BIG_ENDIAN__
277 	return n;
278 #else
279 	u_int32_t t;
280 	union
281 	{
282 		u_int64_t q;
283 		u_int32_t l[2];
284 	} x;
285 
286 	x.q = n;
287 	t = x.l[0];
288 	x.l[0] = htonl(x.l[1]);
289 	x.l[1] = htonl(t);
290 
291 	return x.q;
292 #endif
293 }
294 
295 uint64_t
asl_core_ntohq(uint64_t n)296 asl_core_ntohq(uint64_t n)
297 {
298 #ifdef __BIG_ENDIAN__
299 	return n;
300 #else
301 	u_int32_t t;
302 	union
303 	{
304 		u_int64_t q;
305 		u_int32_t l[2];
306 	} x;
307 
308 	x.q = n;
309 	t = x.l[0];
310 	x.l[0] = ntohl(x.l[1]);
311 	x.l[1] = ntohl(t);
312 
313 	return x.q;
314 #endif
315 }
316 
317 uint64_t
asl_core_new_msg_id(uint64_t start)318 asl_core_new_msg_id(uint64_t start)
319 {
320 	uint64_t out;
321 
322 	pthread_mutex_lock(&core_lock);
323 
324 	if (start != 0) _asl_core_msg_next_id = start;
325 
326 	out = _asl_core_msg_next_id;
327 	_asl_core_msg_next_id++;
328 
329 	pthread_mutex_unlock(&core_lock);
330 
331 	return out;
332 }
333 
334 const char *
asl_filesystem_path(uint32_t place)335 asl_filesystem_path(uint32_t place)
336 {
337 	static dispatch_once_t once;
338 
339 	dispatch_once(&once, ^{
340 		char *asl_var_log = NULL;
341 		const char *const_asl_var_log = "/var/log";
342 
343 #if TARGET_IPHONE_SIMULATOR
344 		asl_var_log = getenv("SIMULATOR_LOG_ROOT");
345 #endif
346 
347 		if (asl_var_log != NULL) const_asl_var_log = (const char *)asl_var_log;
348 
349 		asprintf(&asl_filesystem_path_database, "%s/asl", const_asl_var_log);
350 		asprintf(&asl_filesystem_path_archive, "%s/asl.archive", const_asl_var_log);
351 	});
352 
353 	switch (place)
354 	{
355 		case ASL_PLACE_DATABASE:
356 		{
357 			if (asl_filesystem_path_database == NULL) return ASL_PLACE_DATABASE_DEFAULT;
358 			return asl_filesystem_path_database;
359 		}
360 		case ASL_PLACE_ARCHIVE:
361 		{
362 			if (asl_filesystem_path_archive == NULL) return ASL_PLACE_ARCHIVE_DEFAULT;
363 			return asl_filesystem_path_archive;
364 		}
365 		default:
366 		{
367 			return NULL;
368 		}
369 	}
370 
371 	return NULL;
372 }
373 
374 #pragma mark -
375 #pragma mark data buffer encoding
376 
377 /*
378  * asl_core_encode_buffer
379  * encode arbitrary data as a C string without embedded zero (nul) characters
380  *
381  * The routine computes a histogram of the input buffer and finds
382  * the two least frequently used non-nul chars (L[0] and L[1]).
383  *
384  * L[0] is used to stand in for nul.
385  * L[1] is used as the escape character.
386  * Occurrences of nul in the data are encoded as L[0]
387  * Occurrences of L[0] in the data are encoded as the sequence L[1] 1.
388  * Occurrences of L[1] in the data are encoded as the sequence L[1] 2.
389  *
390  * The output string is preceded by L[0] L[1], and is nul terminated.
391  * The output length is 2 + n + N(L[0]) + N(L[1]) + 1
392  * where N(x) is the number of occurrences of x in the input string.
393  * The worst case occurs when all characters are equally frequent,
394  * In that case the output size will less that 1% larger than the input.
395  */
396 char *
asl_core_encode_buffer(const char * in,uint32_t len)397 asl_core_encode_buffer(const char *in, uint32_t len)
398 {
399 	char *str;
400 	uint32_t i, j, k, outlen, breakit, min, hist[256];
401 	uint32_t lfu[2], save[2];
402 	uint8_t v;
403 
404 	if (in == NULL) return NULL;
405 	if (len == 0) return NULL;
406 
407 	memset(hist, 0, sizeof(hist));
408 	save[0] = 0;
409 	save[1] = 0;
410 
411 	for (i = 0; i < len; i++)
412 	{
413 		v = in[i];
414 		hist[v]++;
415 	}
416 
417 	for (j = 0; j < 2; j++)
418 	{
419 		lfu[j] = 1;
420 		min = hist[1];
421 
422 		for (i = 2; i < 256; i++)
423 		{
424 			if (hist[i] < min)
425 			{
426 				lfu[j] = i;
427 				min = hist[i];
428 
429 				/*
430 				 * Stop if there are no occurances or character i in the input.
431 				 * The minimum will never be less than zero.
432 				 */
433 				if (min == 0) break;
434 
435 				/*
436 				 * When looking for the second least frequently used character,
437 				 * stop scanning if we hit the same minimum as we saw in the first
438 				 * pass.  There will be no smaller values.
439 				 */
440 				if ((j == 1) && (min == save[0])) break;
441 			}
442 		}
443 
444 		save[j] = hist[lfu[j]];
445 		hist[lfu[j]] = (uint32_t)-1;
446 	}
447 
448 	outlen = 2 + len + save[0] + save[1] + 1;
449 
450 	str = malloc(outlen);
451 	if (str == NULL) return NULL;
452 
453 	str[outlen - 1] = '\0';
454 
455 	str[0] = lfu[0];
456 	str[1] = lfu[1];
457 
458 	j = 2;
459 
460 	for (i = 0; i < len; i++)
461 	{
462 		v = in[i];
463 		if (v == 0)
464 		{
465 			str[j++] = lfu[0];
466 			continue;
467 		}
468 
469 		breakit = 0;
470 		for (k = 0; (k < 2) && (breakit == 0); k++)
471 		{
472 			if (v == lfu[k])
473 			{
474 				str[j++] = lfu[1];
475 				str[j++] = k + 1;
476 				breakit = 1;
477 			}
478 		}
479 
480 		if (breakit == 1) continue;
481 
482 		str[j++] = v;
483 	}
484 
485 	return str;
486 }
487 
488 /*
489  * asl_core_decode_buffer
490  * decode a string produced by asl_encode_buffer to recreate the original data
491  */
492 int32_t
asl_core_decode_buffer(const char * in,char ** buf,uint32_t * len)493 asl_core_decode_buffer(const char *in, char **buf, uint32_t *len)
494 {
495 	uint8_t v;
496 	uint32_t i, j, n, outlen;
497 	uint8_t lfu[2];
498 	char *out;
499 
500 	if (buf == NULL) return -1;
501 	if (len == NULL) return -1;
502 
503 	lfu[0] = in[0];
504 	lfu[1] = in[1];
505 
506 	outlen = 0;
507 
508 	/* strip trailing nul */
509 	n = strlen(in);
510 
511 	/* determine output length and check for invalid input */
512 	for (i = 2; i < n; i++)
513 	{
514 		v = in[i];
515 		if (v == lfu[1])
516 		{
517 			i++;
518 			if (i == n) return -1;
519 
520 			v = in[i];
521 			if ((v < 1) || (v > 2)) return -1;
522 
523 			outlen++;
524 		}
525 		else outlen++;
526 	}
527 
528 	if (outlen == 0) return -1;
529 
530 	out = malloc(outlen);
531 	if (out == NULL) return -1;
532 
533 	j = 0;
534 	for (i = 2; i < n; i++)
535 	{
536 		v = in[i];
537 		if (v == lfu[0])
538 		{
539 			out[j++] = 0;
540 		}
541 		else if (v == lfu[1])
542 		{
543 			i++;
544 			v = in[i];
545 			out[j++] = lfu[v - 1];
546 		}
547 		else out[j++] = v;
548 	}
549 
550 	*len = outlen;
551 	*buf = out;
552 	return 0;
553 }
554 
555 #pragma mark -
556 #pragma mark time parsing
557 
558 /*
559  * utility for converting a time string into a time_t
560  * we only deal with the following formats:
561  * dotted form YYYY.MM.DD hh:mm:ss UTC
562  * ctime() form Mth dd hh:mm:ss (e.g. Aug 25 09:54:37)
563  * absolute form - # seconds since the epoch (e.g. 1095789191)
564  * relative time - seconds before or after now (e.g. -300, +43200)
565  * relative time - days/hours/minutes/seconds before or after now (e.g. -1d, +6h, +30m, -10s)
566  * ISO8601 - YYYY[-]MM[-]DDTHH[:]MM[:]SS optionally followed by Z or +/-HH[[:]MM]
567  */
568 
569 /*
570  * light(er)-weight replcaement (in place of regex) for asl_core_parse_time
571  */
572 
573 #define MFLAG_INCLUDE 0x00000001
574 #define MFLAG_EXCLUDE 0x00000002
575 
576 #define DIGITS "0123456789"
577 #define WHITESPACE "	 "
578 
579 #define SECONDS_PER_MINUTE 60
580 #define SECONDS_PER_HOUR 3600
581 #define SECONDS_PER_DAY 86400
582 #define SECONDS_PER_WEEK 604800
583 
584 /*
585  * Match between mincount and maxcount characters in or not in mset.
586  * maxcount == 0 means no limit.
587  *
588  */
589 bool
asl_core_str_match(const char * target,const char * mset,uint32_t mincount,uint32_t maxcount,uint32_t flags,uint32_t * length)590 asl_core_str_match(const char *target, const char *mset, uint32_t mincount, uint32_t maxcount, uint32_t flags, uint32_t *length)
591 {
592 	const char *x;
593 	uint32_t n;
594 
595 	if (length == NULL) length = &n;
596 
597 	if (target == NULL) return (mincount == 0);
598 
599 	for (x = target, *length = 0; *x != '\0'; x++, *length = *length + 1)
600 	{
601 		char *s;
602 
603 		if ((*length == maxcount) && (maxcount > 0)) return true;
604 		if (mset == NULL) continue;
605 
606 		s = strchr(mset, *x);
607 		if ((s == NULL) && (flags & MFLAG_EXCLUDE)) continue;
608 		if ((s != NULL) && (flags & MFLAG_INCLUDE)) continue;
609 
610 		break;
611 	}
612 
613 	return (*length >= mincount);
614 }
615 
616 bool
asl_core_str_match_char(const char * target,const char c,uint32_t mincount,uint32_t flags,uint32_t * length)617 asl_core_str_match_char(const char *target, const char c, uint32_t mincount, uint32_t flags, uint32_t *length)
618 {
619 	uint32_t n;
620 
621 	if (length == NULL) length = &n;
622 	*length = 0;
623 
624 	if (target == NULL) return (mincount == 0);
625 
626 	if ((*target == c) && (flags & MFLAG_INCLUDE)) *length = 1;
627 	if ((*target != c) && (flags & MFLAG_EXCLUDE)) *length = 1;
628 
629 	return (*length >= mincount);
630 }
631 
632 uint32_t
asl_core_str_to_uint32(const char * target,uint32_t length)633 asl_core_str_to_uint32(const char *target, uint32_t length)
634 {
635 	uint32_t i, d, out = 0;
636 
637 	for (i = 0; i < length; i++)
638 	{
639 		d = target[i] - '0';
640 		out = (out * 10) + d;
641 	}
642 
643 	return out;
644 }
645 
646 static bool
asl_core_str_match_absolute_or_relative_time(const char * target,time_t * tval,uint32_t * tlen)647 asl_core_str_match_absolute_or_relative_time(const char *target, time_t *tval, uint32_t *tlen)
648 {
649 	uint32_t len;
650 	int32_t val, sign = 1;
651 	bool test;
652 	const char *p;
653 	time_t start = 0;
654 
655 	if (target == NULL) return false;
656 
657 	/* [+-] */
658 	p = target;
659 	test = asl_core_str_match(p, "+-", 0, 1, MFLAG_INCLUDE, &len);
660 	if (!test) return false;
661 
662 	if (len == 1)
663 	{
664 		/* relative time */
665 		start = time(NULL);
666 		if (*p == '-') sign = -1;
667 	}
668 
669 	/* [0-9]+ */
670 	p += len;
671 	test = asl_core_str_match(p, DIGITS, 1, 0, MFLAG_INCLUDE, &len);
672 	if (!test) return false;
673 	val = asl_core_str_to_uint32(p, len);
674 
675 	/* [shmdw] */
676 	p += len;
677 	test = asl_core_str_match(p, "SsMmHhDdWw", 0, 1, MFLAG_INCLUDE, &len);
678 	if (!test) return false;
679 
680 	if ((*p == 'M') || (*p == 'm')) val *= SECONDS_PER_MINUTE;
681 	else if ((*p == 'H') || (*p == 'h')) val *= SECONDS_PER_HOUR;
682 	else if ((*p == 'D') || (*p == 'd')) val *= SECONDS_PER_DAY;
683 	else if ((*p == 'W') || (*p == 'w')) val *= SECONDS_PER_WEEK;
684 
685 	/* matched string must be followed by space, tab, newline (not counted in length) */
686 	p += len;
687 	if (*p != '\0')
688 	{
689 		test = asl_core_str_match(p, " \t\n", 1, 1, MFLAG_INCLUDE, &len);
690 		if (!test) return false;
691 	}
692 
693 	if (tlen != NULL) *tlen = p - target;
694 	if (tval != NULL) *tval = start + (sign * val);
695 
696 	return true;
697 }
698 
699 static int
_month_num(const char * s)700 _month_num(const char *s)
701 {
702 	if (!strncasecmp(s, "jan", 3)) return  0;
703 	if (!strncasecmp(s, "feb", 3)) return  1;
704 	if (!strncasecmp(s, "mar", 3)) return  2;
705 	if (!strncasecmp(s, "apr", 3)) return  3;
706 	if (!strncasecmp(s, "may", 3)) return  4;
707 	if (!strncasecmp(s, "jun", 3)) return  5;
708 	if (!strncasecmp(s, "jul", 3)) return  6;
709 	if (!strncasecmp(s, "aug", 3)) return  7;
710 	if (!strncasecmp(s, "sep", 3)) return  8;
711 	if (!strncasecmp(s, "oct", 3)) return  9;
712 	if (!strncasecmp(s, "nov", 3)) return 10;
713 	if (!strncasecmp(s, "dec", 3)) return 11;
714 	return -1;
715 
716 }
717 
718 /*
719  * Match ctime() format - Mth [D]D [h]h:mm:ss
720  */
721 bool
asl_core_str_match_c_time(const char * target,time_t * tval,uint32_t * tlen)722 asl_core_str_match_c_time(const char *target, time_t *tval, uint32_t *tlen)
723 {
724 	uint32_t len, y;
725 	bool test;
726 	const char *p;
727 	struct tm t;
728 	time_t now;
729 
730 	if (target == NULL) return false;
731 	memset(&t, 0, sizeof(t));
732 
733 	/* determine current date */
734 	now = time(NULL);
735 	localtime_r(&now, &t);
736 	y = t.tm_year;
737 	memset(&t, 0, sizeof(t));
738 	t.tm_year = y;
739 
740 	/* Mth */
741 	p = target;
742 	t.tm_mon = _month_num(p);
743 	len = 3;
744 	if (t.tm_mon == -1) return false;
745 
746 	/* whitespace */
747 	p += len;
748 	test = asl_core_str_match(p, WHITESPACE, 1, 0, MFLAG_INCLUDE, &len);
749 	if (!test) return false;
750 
751 	/* [D]D */
752 	p += len;
753 	test = asl_core_str_match(p, DIGITS, 1, 2, MFLAG_INCLUDE, &len);
754 	if (!test) return false;
755 	t.tm_mday = asl_core_str_to_uint32(p, len);
756 	if (t.tm_mday > 31) return false;
757 
758 	/* whitespace */
759 	p += len;
760 	test = asl_core_str_match(p, WHITESPACE, 1, 0, MFLAG_INCLUDE, &len);
761 	if (!test) return false;
762 
763 	/* [h]h */
764 	p += len;
765 	test = asl_core_str_match(p, DIGITS, 1, 2, MFLAG_INCLUDE, &len);
766 	if (!test) return false;
767 	t.tm_hour = asl_core_str_to_uint32(p, len);
768 	if (t.tm_hour > 23) return false;
769 
770 	/* : */
771 	p += len;
772 	if (*p != ':') return false;
773 	len = 1;
774 
775 	/* mm */
776 	p += len;
777 	test = asl_core_str_match(p, DIGITS, 2, 2, MFLAG_INCLUDE, &len);
778 	if (!test) return false;
779 	t.tm_min = asl_core_str_to_uint32(p, len);
780 	if (t.tm_min > 59) return false;
781 
782 	/* : */
783 	p += len;
784 	if (*p != ':') return false;
785 	len = 1;
786 
787 	/* ss */
788 	p += len;
789 	test = asl_core_str_match(p, DIGITS, 2, 2, MFLAG_INCLUDE, &len);
790 	if (!test) return false;
791 	t.tm_sec = asl_core_str_to_uint32(p, len);
792 	if (t.tm_sec > 59) return false;
793 
794 	/* matched string must be followed by space, tab, newline (not counted in length) */
795 	p += len;
796 	if (*p != '\0')
797 	{
798 		test = asl_core_str_match(p, " \t\n", 1, 1, MFLAG_INCLUDE, &len);
799 		if (!test) return false;
800 	}
801 
802 	t.tm_isdst = -1;
803 
804 	if (tlen != NULL) *tlen = p - target;
805 	if (tval != NULL) *tval = mktime(&t);
806 
807 	return true;
808 }
809 
810 /*
811  * Match YYYY.[M]M.[D]D [h]h:mm:ss UTC
812  */
813 static bool
asl_core_str_match_dotted_time(const char * target,time_t * tval,uint32_t * tlen)814 asl_core_str_match_dotted_time(const char *target, time_t *tval, uint32_t *tlen)
815 {
816 	uint32_t len;
817 	bool test;
818 	const char *p;
819 	struct tm t;
820 
821 	if (target == NULL) return false;
822 	memset(&t, 0, sizeof(t));
823 
824 	/* YYYY */
825 	p = target;
826 	test = asl_core_str_match(p, DIGITS, 4, 4, MFLAG_INCLUDE, &len);
827 	if (!test) return false;
828 	t.tm_year = asl_core_str_to_uint32(p, len) - 1900;
829 
830 	/* . */
831 	p += len;
832 	if (*p != '.') return false;
833 	len = 1;
834 
835 	/* [M]M */
836 	p += len;
837 	test = asl_core_str_match(p, DIGITS, 1, 2, MFLAG_INCLUDE, &len);
838 	if (!test) return false;
839 	t.tm_mon = asl_core_str_to_uint32(p, len);
840 	if (t.tm_mon < 1) return false;
841 	if (t.tm_mon > 12) return false;
842 	t.tm_mon -= 1;
843 
844 	/* . */
845 	p += len;
846 	if (*p != '.') return false;
847 	len = 1;
848 
849 	/* [D]D */
850 	p += len;
851 	test = asl_core_str_match(p, DIGITS, 1, 2, MFLAG_INCLUDE, &len);
852 	if (!test) return false;
853 	t.tm_mday = asl_core_str_to_uint32(p, len);
854 	if (t.tm_mday > 31) return false;
855 
856 	/* whitespace */
857 	p += len;
858 	test = asl_core_str_match(p, WHITESPACE, 1, 0, MFLAG_INCLUDE, &len);
859 	if (!test) return false;
860 
861 	/* [h]h */
862 	p += len;
863 	test = asl_core_str_match(p, DIGITS, 1, 2, MFLAG_INCLUDE, &len);
864 	if (!test) return false;
865 	t.tm_hour = asl_core_str_to_uint32(p, len);
866 	if (t.tm_hour > 23) return false;
867 
868 	/* : */
869 	p += len;
870 	if (*p != ':') return false;
871 	len = 1;
872 
873 	/* mm */
874 	p += len;
875 	test = asl_core_str_match(p, DIGITS, 2, 2, MFLAG_INCLUDE, &len);
876 	if (!test) return false;
877 	t.tm_min = asl_core_str_to_uint32(p, len);
878 	if (t.tm_min > 59) return false;
879 
880 	/* : */
881 	p += len;
882 	if (*p != ':') return false;
883 	len = 1;
884 
885 	/* ss */
886 	p += len;
887 	test = asl_core_str_match(p, DIGITS, 2, 2, MFLAG_INCLUDE, &len);
888 	if (!test) return false;
889 	t.tm_sec = asl_core_str_to_uint32(p, len);
890 	if (t.tm_sec > 59) return false;
891 
892 	/* whitespace */
893 	p += len;
894 	test = asl_core_str_match(p, WHITESPACE, 1, 0, MFLAG_INCLUDE, &len);
895 	if (!test) return false;
896 
897 	/* UTC */
898 	p += len;
899 	if (strncmp(p, "UTC", 3)) return false;
900 	len = 3;
901 
902 	/* matched string must be followed by space, tab, newline (not counted in length) */
903 	p += len;
904 	if (*p != '\0')
905 	{
906 		test = asl_core_str_match(p, " \t\n", 1, 1, MFLAG_INCLUDE, &len);
907 		if (!test) return false;
908 	}
909 
910 	if (tlen != NULL) *tlen = p - target;
911 	if (tval != NULL) *tval = timegm(&t);
912 
913 	return true;
914 }
915 
916 /*
917  * Match YYYY[-]MM[-]DD[Tt]hh[:]hh[:]ss[Zz] or YYYY[-]MM[-]DD[Tt]hh[:]hh[:]ss[+-][H]H[[:]MM]
918  */
919 static bool
asl_core_str_match_iso_8601_time(const char * target,time_t * tval,uint32_t * tlen)920 asl_core_str_match_iso_8601_time(const char *target, time_t *tval, uint32_t *tlen)
921 {
922 	uint32_t len;
923 	bool test;
924 	const char *p;
925 	struct tm t;
926 	int32_t tzh, tzs, sign = -1;
927 
928 	if (target == NULL) return false;
929 	memset(&t, 0, sizeof(t));
930 
931 	/* YYYY */
932 	p = target;
933 	test = asl_core_str_match(p, DIGITS, 4, 4, MFLAG_INCLUDE, &len);
934 	if (!test) return false;
935 	t.tm_year = asl_core_str_to_uint32(p, len) - 1900;
936 
937 	/* [-] */
938 	p += len;
939 	test = asl_core_str_match_char(p, '-', 0, MFLAG_INCLUDE, &len);
940 	if (!test) return false;
941 
942 	/* MM */
943 	p += len;
944 	test = asl_core_str_match(p, DIGITS, 2, 2, MFLAG_INCLUDE, &len);
945 	if (!test) return false;
946 	t.tm_mon = asl_core_str_to_uint32(p, len);
947 	if (t.tm_mon < 1) return false;
948 	if (t.tm_mon > 12) return false;
949 	t.tm_mon -= 1;
950 
951 	/* [-] */
952 	p += len;
953 	test = asl_core_str_match_char(p, '-', 0, MFLAG_INCLUDE, &len);
954 	if (!test) return false;
955 
956 	/* DD */
957 	p += len;
958 	test = asl_core_str_match(p, DIGITS, 2, 2, MFLAG_INCLUDE, &len);
959 	if (!test) return false;
960 	t.tm_mday = asl_core_str_to_uint32(p, len);
961 	if (t.tm_mday > 31) return false;
962 
963 	/* T or t */
964 	p += len;
965 	test = asl_core_str_match(p, "Tt", 1, 1, MFLAG_INCLUDE, &len);
966 	if (!test) return false;
967 
968 	/* hh */
969 	p += len;
970 	test = asl_core_str_match(p, DIGITS, 2, 2, MFLAG_INCLUDE, &len);
971 	if (!test) return false;
972 	t.tm_hour = asl_core_str_to_uint32(p, len);
973 	if (t.tm_hour > 23) return false;
974 
975 	/* [:] */
976 	p += len;
977 	test = asl_core_str_match_char(p, ':', 0, MFLAG_INCLUDE, &len);
978 	if (!test) return false;
979 
980 	/* mm */
981 	p += len;
982 	test = asl_core_str_match(p, DIGITS, 2, 2, MFLAG_INCLUDE, &len);
983 	if (!test) return false;
984 	t.tm_min = asl_core_str_to_uint32(p, len);
985 	if (t.tm_min > 59) return false;
986 
987 	/* [:] */
988 	p += len;
989 	test = asl_core_str_match_char(p, ':', 0, MFLAG_INCLUDE, &len);
990 	if (!test) return false;
991 
992 	/* ss */
993 	p += len;
994 	test = asl_core_str_match(p, DIGITS, 2, 2, MFLAG_INCLUDE, &len);
995 	if (!test) return false;
996 	t.tm_sec = asl_core_str_to_uint32(p, len);
997 	if (t.tm_sec > 59) return false;
998 
999 	p += len;
1000 
1001 	/* default to local time if we hit the end of the string */
1002 	if ((*p == '\0') || (*p == ' ') || (*p == '\t') || (*p == '\n'))
1003 	{
1004 		t.tm_isdst = -1;
1005 
1006 		if (tlen != NULL) *tlen = p - target;
1007 		if (tval != NULL) *tval = mktime(&t);
1008 
1009 		return true;
1010 	}
1011 
1012 	/* Z, z, +, or - */
1013 	test = asl_core_str_match(p, "Zz+-", 1, 1, MFLAG_INCLUDE, &len);
1014 	if (!test) return false;
1015 
1016 	if ((*p == 'Z') || (*p == 'z'))
1017 	{
1018 		/* matched string must be followed by space, tab, newline (not counted in length) */
1019 		p += len;
1020 		if (*p != '\0')
1021 		{
1022 			test = asl_core_str_match(p, " \t\n", 1, 1, MFLAG_INCLUDE, &len);
1023 			if (!test) return false;
1024 		}
1025 
1026 		if (tlen != NULL) *tlen = p - target;
1027 		if (tval != NULL) *tval = timegm(&t);
1028 
1029 		return true;
1030 	}
1031 
1032 	if (*p == '-') sign = 1;
1033 
1034 	/* [h]h */
1035 	p += len;
1036 	test = asl_core_str_match(p, DIGITS, 1, 2, MFLAG_INCLUDE, &len);
1037 	if (!test) return false;
1038 	tzh = asl_core_str_to_uint32(p, len);
1039 	if (tzh > 23) return false;
1040 
1041 	/* [:] */
1042 	p += len;
1043 	test = asl_core_str_match_char(p, ':', 0, MFLAG_INCLUDE, &len);
1044 	if (!test) return false;
1045 
1046 	/* mm */
1047 	p += len;
1048 	test = asl_core_str_match(p, DIGITS, 0, 2, MFLAG_INCLUDE, &len);
1049 	if (!test) return false;
1050 	tzs = asl_core_str_to_uint32(p, len);
1051 	if (tzs > 59) return false;
1052 
1053 	t.tm_sec += (sign * (tzh * SECONDS_PER_HOUR) + (tzs * SECONDS_PER_MINUTE));
1054 
1055 	/* matched string must be followed by space, tab, newline (not counted in length) */
1056 	p += len;
1057 	if (*p != '\0')
1058 	{
1059 		test = asl_core_str_match(p, " \t\n", 1, 1, MFLAG_INCLUDE, &len);
1060 		if (!test) return false;
1061 	}
1062 
1063 	if (tlen != NULL) *tlen = p - target;
1064 	if (tval != NULL) *tval = timegm(&t);
1065 
1066 	return true;
1067 }
1068 
1069 time_t
asl_core_parse_time(const char * in,uint32_t * tlen)1070 asl_core_parse_time(const char *in, uint32_t *tlen)
1071 {
1072 	time_t tval = 0;
1073 	uint32_t inlen;
1074 
1075 	if (tlen != NULL) *tlen = 0;
1076 
1077 	if (in == NULL) return -1;
1078 
1079 	/*
1080 	 * Heuristics to determine the string format.
1081 	 * Warning: this code must be checked and may need to be adjusted if new formats are added.
1082 	 */
1083 	inlen = strlen(in);
1084 	if (inlen == 0) return -1;
1085 
1086 	/* leading plus or minus means it must be a relative time */
1087 	if ((in[0] == '+') || (in[0] == '-'))
1088 	{
1089 		if (asl_core_str_match_absolute_or_relative_time(in, &tval, tlen)) return tval;
1090 		return -1;
1091 	}
1092 
1093 	/* leading alphabetic char means it must be ctime() format */
1094 	if (((in[0] >= 'a') && (in[0] <= 'z')) || ((in[0] >= 'A') && (in[0] <= 'Z')))
1095 	{
1096 		if (asl_core_str_match_c_time(in, &tval, tlen)) return tval;
1097 		return -1;
1098 	}
1099 
1100 	/* only absolute, dotted, or iso8601 formats at this point */
1101 
1102 	/* one to for chars means it must be absolute */
1103 	if (inlen < 5)
1104 	{
1105 		if (asl_core_str_match_absolute_or_relative_time(in, &tval, tlen)) return tval;
1106 		return -1;
1107 	}
1108 
1109 	/* check for dot */
1110 	if (in[4] == '.')
1111 	{
1112 		if (asl_core_str_match_dotted_time(in, &tval, tlen)) return tval;
1113 		return -1;
1114 	}
1115 
1116 	/* only absolute or iso8601 at this point */
1117 
1118 	/* check for absolute first, since that's quicker */
1119 	if (asl_core_str_match_absolute_or_relative_time(in, &tval, tlen)) return tval;
1120 
1121 	if (asl_core_str_match_iso_8601_time(in, &tval, tlen)) return tval;
1122 
1123 	return -1;
1124 }
1125 
1126 /*
1127  * asl_parse_time is old SPI used all over the place.
1128  */
1129 time_t
asl_parse_time(const char * in)1130 asl_parse_time(const char *in)
1131 {
1132 	return asl_core_parse_time(in, NULL);
1133 }
1134