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