1/*
2 * Copyright (c) 2004-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 <TargetConditionals.h>
25
26#include <sys/types.h>
27#include <sys/socket.h>
28#include <sys/un.h>
29#include <sys/ucred.h>
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
33#include <unistd.h>
34#include <netinet/in.h>
35#include <arpa/inet.h>
36#define SYSLOG_NAMES
37#include <syslog.h>
38#include <sys/fslog.h>
39#include <vproc.h>
40#include <pthread.h>
41#include <vproc_priv.h>
42#include <mach/mach.h>
43#include <assert.h>
44#include <libkern/OSAtomic.h>
45#include <libproc.h>
46#include <uuid/uuid.h>
47#include "daemon.h"
48
49#define LIST_SIZE_DELTA 256
50
51#define streq(A,B) (strcmp(A,B)==0)
52#define forever for(;;)
53
54#define ASL_MSG_TYPE_MASK 0x0000000f
55#define ASL_TYPE_ERROR 2
56
57#define ASL_KEY_FACILITY "Facility"
58
59#define FACILITY_USER "user"
60#define FACILITY_CONSOLE "com.apple.console"
61#define SYSTEM_RESERVED "com.apple.system"
62#define SYSTEM_RESERVED_LEN 16
63
64#define VERIFY_STATUS_OK 0
65#define VERIFY_STATUS_INVALID_MESSAGE 1
66#define VERIFY_STATUS_EXCEEDED_QUOTA 2
67
68extern void disaster_message(aslmsg m);
69extern int asl_action_reset(void);
70extern int asl_action_control_set_param(const char *s);
71
72static char myname[MAXHOSTNAMELEN + 1] = {0};
73static int name_change_token = -1;
74
75static OSSpinLock count_lock = 0;
76
77#if !TARGET_OS_EMBEDDED
78static vproc_transaction_t vproc_trans = {0};
79#endif
80
81#define QUOTA_KERN_EXCEEDED_MESSAGE "*** kernel exceeded %d log message per second limit  -  remaining messages this second discarded ***"
82
83#define DEFAULT_DB_FILE_MAX 25600000
84#define DEFAULT_DB_MEMORY_MAX 8192
85#define DEFAULT_DB_MINI_MAX 256
86#define DEFAULT_MPS_LIMIT 500
87#define DEFAULT_REMOTE_DELAY 5000
88#define DEFAULT_BSD_MAX_DUP_SEC 30
89#define DEFAULT_MARK_SEC 0
90#define DEFAULT_UTMP_TTL_SEC 31622400
91
92static time_t quota_time = 0;
93static int32_t kern_quota;
94static int32_t kern_level;
95
96static const char *kern_notify_key[] =
97{
98	"com.apple.system.log.kernel.emergency",
99	"com.apple.system.log.kernel.alert",
100	"com.apple.system.log.kernel.critical",
101	"com.apple.system.log.kernel.error",
102	"com.apple.system.log.kernel.warning",
103	"com.apple.system.log.kernel.notice",
104	"com.apple.system.log.kernel.info",
105	"com.apple.system.log.kernel.debug"
106};
107
108static int kern_notify_token[8] = {-1, -1, -1, -1, -1, -1, -1, -1 };
109
110static uint32_t
111kern_quota_check(time_t now, aslmsg msg, uint32_t level)
112{
113	char *str, lstr[2];
114
115	if (msg == NULL) return VERIFY_STATUS_INVALID_MESSAGE;
116	if (global.mps_limit == 0) return VERIFY_STATUS_OK;
117
118	if (quota_time != now)
119	{
120		kern_quota = global.mps_limit;
121		kern_level = 7;
122		quota_time = now;
123	}
124
125	if (level < kern_level) kern_level = level;
126	if (kern_quota > 0) kern_quota--;
127
128	if (kern_quota > 0) return VERIFY_STATUS_OK;
129	if (kern_quota < 0)	return VERIFY_STATUS_EXCEEDED_QUOTA;
130
131	kern_quota = -1;
132
133	str = NULL;
134	asprintf(&str, QUOTA_KERN_EXCEEDED_MESSAGE, global.mps_limit);
135	if (str != NULL)
136	{
137		asl_set(msg, ASL_KEY_MSG, str);
138		free(str);
139		lstr[0] = kern_level + '0';
140		lstr[1] = 0;
141		asl_set(msg, ASL_KEY_LEVEL, lstr);
142	}
143
144	return VERIFY_STATUS_OK;
145}
146
147static const char *
148whatsmyhostname()
149{
150	static dispatch_once_t once;
151	int check, status;
152
153	dispatch_once(&once, ^{
154		snprintf(myname, sizeof(myname), "%s", "localhost");
155		notify_register_check(kNotifySCHostNameChange, &name_change_token);
156	});
157
158	check = 1;
159	status = 0;
160
161	if (name_change_token >= 0) status = notify_check(name_change_token, &check);
162
163	if ((status == 0) && (check == 0)) return (const char *)myname;
164
165	if (gethostname(myname, MAXHOSTNAMELEN) < 0)
166	{
167		snprintf(myname, sizeof(myname), "%s", "localhost");
168	}
169	else
170	{
171		char *dot;
172		dot = strchr(myname, '.');
173		if (dot != NULL) *dot = '\0';
174	}
175
176	return (const char *)myname;
177}
178
179void
180asl_client_count_increment()
181{
182	OSSpinLockLock(&count_lock);
183
184#if !TARGET_OS_EMBEDDED
185	if (global.client_count == 0) vproc_trans = vproc_transaction_begin(NULL);
186#endif
187	global.client_count++;
188
189	OSSpinLockUnlock(&count_lock);
190}
191
192void
193asl_client_count_decrement()
194{
195	OSSpinLockLock(&count_lock);
196
197	if (global.client_count > 0) global.client_count--;
198#if !TARGET_OS_EMBEDDED
199	if (global.client_count == 0) vproc_transaction_end(NULL, vproc_trans);
200#endif
201
202	OSSpinLockUnlock(&count_lock);
203}
204
205/*
206 * Checks message content and sets attributes as required
207 *
208 * SOURCE_INTERNAL log messages sent by syslogd itself
209 * SOURCE_ASL_SOCKET legacy asl(3) TCP socket
210 * SOURCE_BSD_SOCKET legacy syslog(3) UDP socket
211 * SOURCE_UDP_SOCKET from the network
212 * SOURCE_KERN from the kernel
213 * SOURCE_ASL_MESSAGE mach messages sent from Libc by asl(3) and syslog(3)
214 * SOURCE_LAUNCHD forwarded from launchd
215 */
216
217static uint32_t
218aslmsg_verify(aslmsg msg, uint32_t source, int32_t *kern_post_level, uid_t *uid_out)
219{
220	const char *val, *fac, *ruval, *rgval;
221	char buf[64];
222	time_t tick, now;
223	uid_t uid;
224	gid_t gid;
225	uint32_t status, level, fnum;
226	pid_t pid;
227	uuid_string_t ustr;
228	struct proc_uniqidentifierinfo pinfo;
229
230	if (msg == NULL) return VERIFY_STATUS_INVALID_MESSAGE;
231
232	/* Time */
233	now = time(NULL);
234
235	if (kern_post_level != NULL) *kern_post_level = -1;
236	if (uid_out != NULL) *uid_out = -2;
237
238	/* PID */
239	pid = 0;
240
241	val = asl_get(msg, ASL_KEY_PID);
242	if (val == NULL) asl_set(msg, ASL_KEY_PID, "0");
243	else pid = (pid_t)atoi(val);
244
245	/* if PID is 1 (launchd), use the refpid if provided */
246	if (pid == 1)
247	{
248		val = asl_get(msg, ASL_KEY_REF_PID);
249		if (val != NULL) pid = (pid_t)atoi(val);
250	}
251
252	/* Level */
253	val = asl_get(msg, ASL_KEY_LEVEL);
254	level = ASL_LEVEL_DEBUG;
255	if ((val != NULL) && (val[1] == '\0') && (val[0] >= '0') && (val[0] <= '7')) level = val[0] - '0';
256	snprintf(buf, sizeof(buf), "%d", level);
257	asl_set(msg, ASL_KEY_LEVEL, buf);
258
259	/* check kernel quota if enabled and no processes are watching */
260	if ((pid == 0) && (global.mps_limit > 0) && (global.watchers_active == 0))
261	{
262		status = kern_quota_check(now, msg, level);
263		if (status != VERIFY_STATUS_OK) return status;
264	}
265
266	if (pid != 0)
267	{
268		/* set Sender_Mach_UUID */
269		uuid_clear(pinfo.p_uuid);
270		if (proc_pidinfo(pid, PROC_PIDUNIQIDENTIFIERINFO, 1, &pinfo, sizeof(pinfo)) == sizeof(pinfo))
271		{
272			uuid_unparse(pinfo.p_uuid, ustr);
273			asl_set(msg, ASL_KEY_SENDER_MACH_UUID, ustr);
274		}
275	}
276
277	tick = 0;
278	val = asl_get(msg, ASL_KEY_TIME);
279	if (val != NULL) tick = asl_parse_time(val);
280
281	/* Set time to now if it is unset or from the future (not allowed!) */
282	if ((tick == 0) || (tick > now)) tick = now;
283
284	/* Canonical form: seconds since the epoch */
285	snprintf(buf, sizeof(buf) - 1, "%lu", tick);
286	asl_set(msg, ASL_KEY_TIME, buf);
287
288	/* Host */
289	val = asl_get(msg, ASL_KEY_HOST);
290	if (val == NULL) asl_set(msg, ASL_KEY_HOST, whatsmyhostname());
291
292	uid = -2;
293	val = asl_get(msg, ASL_KEY_UID);
294	if (val != NULL)
295	{
296		uid = atoi(val);
297		if ((uid == 0) && strcmp(val, "0")) uid = -2;
298		if (uid_out != NULL) *uid_out = uid;
299	}
300
301	gid = -2;
302	val = asl_get(msg, ASL_KEY_GID);
303	if (val != NULL)
304	{
305		gid = atoi(val);
306		if ((gid == 0) && strcmp(val, "0")) gid = -2;
307	}
308
309	/* UID  & GID */
310	switch (source)
311	{
312		case SOURCE_KERN:
313		case SOURCE_INTERNAL:
314		{
315			asl_set(msg, ASL_KEY_UID, "0");
316			asl_set(msg, ASL_KEY_GID, "0");
317			break;
318		}
319		case SOURCE_ASL_SOCKET:
320		case SOURCE_ASL_MESSAGE:
321		case SOURCE_LAUNCHD:
322		{
323			/* we trust the UID & GID in the message */
324			break;
325		}
326		default:
327		{
328			/* we do not trust the UID 0 or GID 0 or 80 in the message */
329			if (uid == 0) asl_set(msg, ASL_KEY_UID, "-2");
330			if ((gid == 0) || (gid == 80)) asl_set(msg, ASL_KEY_GID, "-2");
331		}
332	}
333
334	/* Sender */
335	val = asl_get(msg, ASL_KEY_SENDER);
336	if (val == NULL)
337	{
338		switch (source)
339		{
340			case SOURCE_KERN:
341			{
342				asl_set(msg, ASL_KEY_SENDER, "kernel");
343				break;
344			}
345			case SOURCE_INTERNAL:
346			{
347				asl_set(msg, ASL_KEY_SENDER, "syslogd");
348				break;
349			}
350			default:
351			{
352				asl_set(msg, ASL_KEY_SENDER, "Unknown");
353			}
354		}
355	}
356	else if ((source != SOURCE_KERN) && (uid != 0) && (!strcmp(val, "kernel")))
357	{
358		/* allow UID 0 to send messages with "Sender kernel", but nobody else */
359		asl_set(msg, ASL_KEY_SENDER, "Unknown");
360	}
361
362	/* Facility */
363	fac = asl_get(msg, ASL_KEY_FACILITY);
364	if (fac == NULL)
365	{
366		if (source == SOURCE_KERN) fac = "kern";
367		else fac = "user";
368		asl_set(msg, ASL_KEY_FACILITY, fac);
369	}
370	else if (fac[0] == '#')
371	{
372		fnum = LOG_USER;
373		if ((fac[1] >= '0') && (fac[1] <= '9'))
374		{
375			fnum = atoi(fac + 1) << 3;
376			if ((fnum == 0) && (strcmp(fac + 1, "0"))) fnum = LOG_USER;
377		}
378
379		fac = asl_syslog_faciliy_num_to_name(fnum);
380		asl_set(msg, ASL_KEY_FACILITY, fac);
381	}
382	else if (!strncmp(fac, SYSTEM_RESERVED, SYSTEM_RESERVED_LEN))
383	{
384		/* only UID 0 may use "com.apple.system" */
385		if (uid != 0) asl_set(msg, ASL_KEY_FACILITY, FACILITY_USER);
386	}
387
388	/*
389	 * kernel messages are only readable by root and admin group.
390	 * all other messages are admin-only readable unless they already
391	 * have specific read access controls set.
392	 */
393	if (source == SOURCE_KERN)
394	{
395		asl_set(msg, ASL_KEY_READ_UID, "0");
396		asl_set(msg, ASL_KEY_READ_GID, "80");
397	}
398	else
399	{
400		ruval = asl_get(msg, ASL_KEY_READ_UID);
401		rgval = asl_get(msg, ASL_KEY_READ_GID);
402
403		if ((ruval == NULL) && (rgval == NULL))
404		{
405			asl_set(msg, ASL_KEY_READ_GID, "80");
406		}
407	}
408
409	/* Set DB Expire Time for com.apple.system.utmpx and lastlog */
410	if ((!strcmp(fac, "com.apple.system.utmpx")) || (!strcmp(fac, "com.apple.system.lastlog")))
411	{
412		snprintf(buf, sizeof(buf), "%lu", tick + global.utmp_ttl);
413		asl_set(msg, ASL_KEY_EXPIRE_TIME, buf);
414	}
415
416	/* Set DB Expire Time for Filesystem errors */
417	if (!strcmp(fac, FSLOG_VAL_FACILITY))
418	{
419		snprintf(buf, sizeof(buf), "%lu", tick + FS_TTL_SEC);
420		asl_set(msg, ASL_KEY_EXPIRE_TIME, buf);
421	}
422
423	/*
424	 * special case handling of kernel disaster messages
425	 */
426	if ((source == SOURCE_KERN) && (level <= KERN_DISASTER_LEVEL))
427	{
428		if (kern_post_level != NULL) *kern_post_level = level;
429		disaster_message(msg);
430	}
431
432	return VERIFY_STATUS_OK;
433}
434
435void
436list_append_msg(asl_search_result_t *list, aslmsg msg)
437{
438	if (list == NULL) return;
439	if (msg == NULL) return;
440
441	/*
442	 * NB: curr is the list size
443	 * grow list if necessary
444	 */
445	if (list->count == list->curr)
446	{
447		if (list->curr == 0)
448		{
449			list->msg = (asl_msg_t **)calloc(LIST_SIZE_DELTA, sizeof(asl_msg_t *));
450		}
451		else
452		{
453			list->msg = (asl_msg_t **)reallocf(list->msg, (list->curr + LIST_SIZE_DELTA) * sizeof(asl_msg_t *));
454		}
455
456		if (list->msg == NULL)
457		{
458			list->curr = 0;
459			list->count = 0;
460			return;
461		}
462
463		list->curr += LIST_SIZE_DELTA;
464	}
465
466	list->msg[list->count] = (asl_msg_t *)msg;
467	list->count++;
468}
469
470void
471init_globals(void)
472{
473	asl_out_rule_t *r;
474
475	OSSpinLockLock(&global.lock);
476
477	global.pid = getpid();
478	global.debug = 0;
479	free(global.debug_file);
480	global.debug_file = NULL;
481	global.launchd_enabled = 1;
482
483#if TARGET_OS_EMBEDDED
484	global.dbtype = DB_TYPE_MINI;
485#else
486	global.dbtype = DB_TYPE_FILE;
487#endif
488	global.db_file_max = DEFAULT_DB_FILE_MAX;
489	global.db_memory_max = DEFAULT_DB_MEMORY_MAX;
490	global.db_mini_max = DEFAULT_DB_MINI_MAX;
491	global.mps_limit = DEFAULT_MPS_LIMIT;
492	global.remote_delay_time = DEFAULT_REMOTE_DELAY;
493	global.bsd_max_dup_time = DEFAULT_BSD_MAX_DUP_SEC;
494	global.mark_time = DEFAULT_MARK_SEC;
495	global.utmp_ttl = DEFAULT_UTMP_TTL_SEC;
496
497	global.asl_out_module = asl_out_module_init();
498	OSSpinLockUnlock(&global.lock);
499
500	if (global.asl_out_module != NULL)
501	{
502		for (r = global.asl_out_module->ruleset; r != NULL; r = r->next)
503		{
504			if ((r->action == ACTION_SET_PARAM) && (r->query == NULL) && (!strncmp(r->options, "debug", 5))) control_set_param(r->options, true);
505		}
506	}
507}
508
509/*
510 * Used to set config parameters.
511 * Line format "= name value"
512 */
513int
514control_set_param(const char *s, bool eval)
515{
516	char **l;
517	uint32_t intval, count, v32a, v32b, v32c;
518
519	if (s == NULL) return -1;
520	if (s[0] == '\0') return 0;
521
522	/* skip '=' and whitespace */
523	if (*s == '=') s++;
524	while ((*s == ' ') || (*s == '\t')) s++;
525
526	l = explode(s, " \t");
527	if (l == NULL) return -1;
528
529	for (count = 0; l[count] != NULL; count++);
530
531	/* name is required */
532	if (count == 0)
533	{
534		free_string_list(l);
535		return -1;
536	}
537
538	/* Check variables that allow 0 or 1 / boolean */
539	if (!strcasecmp(l[0], "debug"))
540	{
541		/* = debug [0|1] [file] */
542		if (count == 1)
543		{
544			intval = (eval) ? 1 : 0;
545			config_debug(intval, NULL);
546		}
547		else if (!strcmp(l[1], "0"))
548		{
549			config_debug(0, l[2]);
550		}
551		else if (!strcmp(l[1], "1"))
552		{
553			config_debug(1, l[2]);
554		}
555		else
556		{
557			intval = (eval) ? 1 : 0;
558			config_debug(intval, l[1]);
559		}
560
561		free_string_list(l);
562		return 0;
563	}
564
565	/* value is required */
566	if (count == 1)
567	{
568		free_string_list(l);
569		return -1;
570	}
571
572	if (!strcasecmp(l[0], "mark_time"))
573	{
574		/* = mark_time seconds */
575		OSSpinLockLock(&global.lock);
576		if (eval) global.mark_time = atoll(l[1]);
577		else global.mark_time = 0;
578		OSSpinLockUnlock(&global.lock);
579	}
580	else if (!strcasecmp(l[0], "dup_delay"))
581	{
582		/* = bsd_max_dup_time seconds */
583		OSSpinLockLock(&global.lock);
584		if (eval) global.bsd_max_dup_time = atoll(l[1]);
585		else global.bsd_max_dup_time = DEFAULT_BSD_MAX_DUP_SEC;
586		OSSpinLockUnlock(&global.lock);
587	}
588	else if (!strcasecmp(l[0], "remote_delay"))
589	{
590		/* = remote_delay microseconds */
591		OSSpinLockLock(&global.lock);
592		if (eval) global.remote_delay_time = atol(l[1]);
593		else global.remote_delay_time = DEFAULT_REMOTE_DELAY;
594		OSSpinLockUnlock(&global.lock);
595	}
596	else if (!strcasecmp(l[0], "utmp_ttl"))
597	{
598		/* = utmp_ttl seconds */
599		OSSpinLockLock(&global.lock);
600		if (eval) global.utmp_ttl = (time_t)atoll(l[1]);
601		else global.utmp_ttl = DEFAULT_UTMP_TTL_SEC;
602		OSSpinLockUnlock(&global.lock);
603	}
604	else if (!strcasecmp(l[0], "mps_limit"))
605	{
606		/* = mps_limit number */
607		OSSpinLockLock(&global.lock);
608		if (eval) global.mps_limit = (uint32_t)atol(l[1]);
609		else global.mps_limit = DEFAULT_MPS_LIMIT;
610		OSSpinLockUnlock(&global.lock);
611	}
612	else if (!strcasecmp(l[0], "max_file_size"))
613	{
614		/* = max_file_size bytes */
615		pthread_mutex_lock(global.db_lock);
616
617		if (global.dbtype & DB_TYPE_FILE)
618		{
619			asl_store_close(global.file_db);
620			global.file_db = NULL;
621			if (eval) global.db_file_max = atoi(l[1]);
622			else global.db_file_max = DEFAULT_DB_FILE_MAX;
623		}
624
625		pthread_mutex_unlock(global.db_lock);
626	}
627	else if ((!strcasecmp(l[0], "db")) || (!strcasecmp(l[0], "database")) || (!strcasecmp(l[0], "datastore")))
628	{
629		/* NB this is private / unpublished */
630		/* = db type [max]... */
631		if (eval)
632		{
633			v32a = 0;
634			v32b = 0;
635			v32c = 0;
636
637			if ((l[1][0] >= '0') && (l[1][0] <= '9'))
638			{
639				intval = atoi(l[1]);
640				if ((count >= 3) && (strcmp(l[2], "-"))) v32a = atoi(l[2]);
641				if ((count >= 4) && (strcmp(l[3], "-"))) v32b = atoi(l[3]);
642				if ((count >= 5) && (strcmp(l[4], "-"))) v32c = atoi(l[4]);
643			}
644			else if (!strcasecmp(l[1], "file"))
645			{
646				intval = DB_TYPE_FILE;
647				if ((count >= 3) && (strcmp(l[2], "-"))) v32a = atoi(l[2]);
648			}
649			else if (!strncasecmp(l[1], "mem", 3))
650			{
651				intval = DB_TYPE_MEMORY;
652				if ((count >= 3) && (strcmp(l[2], "-"))) v32b = atoi(l[2]);
653			}
654			else if (!strncasecmp(l[1], "min", 3))
655			{
656				intval = DB_TYPE_MINI;
657				if ((count >= 3) && (strcmp(l[2], "-"))) v32c = atoi(l[2]);
658			}
659			else
660			{
661				free_string_list(l);
662				return -1;
663			}
664
665			if (v32a == 0) v32a = global.db_file_max;
666			if (v32b == 0) v32b = global.db_memory_max;
667			if (v32c == 0) v32c = global.db_mini_max;
668
669			config_data_store(intval, v32a, v32b, v32c);
670		}
671		else
672		{
673#if TARGET_OS_EMBEDDED
674			intval = DB_TYPE_MINI;
675#else
676			intval = DB_TYPE_FILE;
677#endif
678			config_data_store(intval, DEFAULT_DB_FILE_MAX, DEFAULT_DB_MEMORY_MAX, DEFAULT_DB_MINI_MAX);
679		}
680	}
681
682	free_string_list(l);
683	return 0;
684}
685
686static int
687control_message(aslmsg msg)
688{
689	const char *str = asl_get(msg, ASL_KEY_MSG);
690
691	if (str == NULL) return 0;
692
693	if (!strncmp(str, "= reset", 7))
694	{
695		init_globals();
696		return asl_action_reset();
697	}
698	else if (!strncmp(str, "= crash", 7))
699	{
700		abort();
701	}
702	else if (!strncmp(str, "@ ", 2))
703	{
704		return asl_action_control_set_param(str);
705	}
706	else if (!strncmp(str, "= ", 2))
707	{
708		return control_set_param(str, true);
709	}
710
711	return 0;
712}
713
714void
715process_message(aslmsg msg, uint32_t source)
716{
717	if (msg == NULL) return;
718
719	OSAtomicIncrement32(&global.work_queue_count);
720	dispatch_async(global.work_queue, ^{
721		int32_t kplevel;
722		uint32_t status;
723		uid_t uid;
724
725		kplevel = -1;
726		uid = -2;
727
728		status = aslmsg_verify(msg, source, &kplevel, &uid);
729		if (status == VERIFY_STATUS_OK)
730		{
731			if ((source == SOURCE_KERN) && (kplevel >= 0))
732			{
733				if (kplevel > 7) kplevel = 7;
734				if (kern_notify_token[kplevel] < 0)
735				{
736					status = notify_register_plain(kern_notify_key[kplevel], &(kern_notify_token[kplevel]));
737					if (status != 0) asldebug("notify_register_plain(%s) failed status %u\n", status);
738				}
739
740				notify_post(kern_notify_key[kplevel]);
741			}
742
743			if ((uid == 0) && asl_check_option(msg, ASL_OPT_CONTROL)) control_message(msg);
744
745			/* send message to output modules */
746			asl_out_message(msg);
747#if !TARGET_IPHONE_SIMULATOR
748			if (global.bsd_out_enabled) bsd_out_message(msg);
749#endif
750		}
751
752		asl_free(msg);
753		OSAtomicDecrement32(&global.work_queue_count);
754	});
755}
756
757int
758internal_log_message(const char *str)
759{
760	aslmsg msg;
761
762	if (str == NULL) return 1;
763
764	msg = (aslmsg)asl_msg_from_string(str);
765	if (msg == NULL) return 1;
766
767	process_message(msg, SOURCE_INTERNAL);
768
769	return 0;
770}
771
772int
773asldebug(const char *str, ...)
774{
775	va_list v;
776	FILE *dfp = NULL;
777
778	if (global.debug == 0) return 0;
779
780	if (global.debug_file == NULL) dfp = fopen(_PATH_SYSLOGD_LOG, "a");
781	else dfp = fopen(global.debug_file, "a");
782	if (dfp == NULL) return 0;
783
784	va_start(v, str);
785	vfprintf(dfp, str, v);
786	va_end(v);
787
788	fclose(dfp);
789
790	return 0;
791}
792
793void
794asl_mark(void)
795{
796	char *str = NULL;
797
798	asprintf(&str, "[Sender syslogd] [Level 6] [PID %u] [Message -- MARK --] [UID 0] [UID 0] [Facility syslog]", global.pid);
799	internal_log_message(str);
800	free(str);
801}
802
803aslmsg
804asl_syslog_input_convert(const char *in, int len, char *rhost, uint32_t source)
805{
806	int pf, pri, index, n;
807	char *p, *colon, *brace, *space, *tmp, *tval, *hval, *sval, *pval, *mval;
808	char prival[8];
809	const char *fval;
810	aslmsg msg;
811	struct tm time;
812	time_t tick;
813
814	if (in == NULL) return NULL;
815	if (len <= 0) return NULL;
816
817	pri = LOG_DEBUG;
818	tval = NULL;
819	hval = NULL;
820	sval = NULL;
821	pval = NULL;
822	mval = NULL;
823	fval = NULL;
824
825	index = 0;
826	p = (char *)in;
827
828	/* skip leading whitespace */
829	while ((index < len) && ((*p == ' ') || (*p == '\t')))
830	{
831		p++;
832		index++;
833	}
834
835	if (index >= len) return NULL;
836
837	/* parse "<NN>" priority (level and facility) */
838	if (*p == '<')
839	{
840		p++;
841		index++;
842
843		n = sscanf(p, "%d", &pf);
844		if (n == 1)
845		{
846			pri = pf & 0x7;
847			if (pf > 0x7) fval = asl_syslog_faciliy_num_to_name(pf & LOG_FACMASK);
848		}
849
850		while ((index < len) && (*p != '>'))
851		{
852			p++;
853			index++;
854		}
855
856		if (index < len)
857		{
858			p++;
859			index++;
860		}
861	}
862
863	snprintf(prival, sizeof(prival), "%d", pri);
864
865	/* check if a timestamp is included */
866	if (((len - index) > 15) && (p[9] == ':') && (p[12] == ':') && (p[15] == ' '))
867	{
868		tmp = malloc(16);
869		if (tmp == NULL) return NULL;
870
871		memcpy(tmp, p, 15);
872		tmp[15] = '\0';
873
874		tick = asl_parse_time(tmp);
875		if (tick == (time_t)-1)
876		{
877			tval = tmp;
878		}
879		else
880		{
881			free(tmp);
882			gmtime_r(&tick, &time);
883			asprintf(&tval, "%d.%02d.%02d %02d:%02d:%02d UTC", time.tm_year + 1900, time.tm_mon + 1, time.tm_mday, time.tm_hour, time.tm_min, time.tm_sec);
884		}
885
886		p += 16;
887		index += 16;
888	}
889
890	/* stop here for kernel messages */
891	if (source == SOURCE_KERN)
892	{
893		msg = asl_new(ASL_TYPE_MSG);
894		if (msg == NULL) return NULL;
895
896		asl_set(msg, ASL_KEY_MSG, p);
897		asl_set(msg, ASL_KEY_LEVEL, prival);
898		asl_set(msg, ASL_KEY_PID, "0");
899
900		return msg;
901	}
902
903	/* if message is from a network socket, hostname follows */
904	if (source == SOURCE_UDP_SOCKET)
905	{
906		space = strchr(p, ' ');
907		if (space != NULL)
908		{
909			n = space - p;
910			hval = malloc(n + 1);
911			if (hval == NULL) return NULL;
912
913			memcpy(hval, p, n);
914			hval[n] = '\0';
915
916			p = space + 1;
917			index += (n + 1);
918		}
919	}
920
921	colon = strchr(p, ':');
922	brace = strchr(p, '[');
923
924	/* check for "sender:" or sender[pid]:"  */
925	if (colon != NULL)
926	{
927		if ((brace != NULL) && (brace < colon))
928		{
929			n = brace - p;
930			sval = malloc(n + 1);
931			if (sval == NULL) return NULL;
932
933			memcpy(sval, p, n);
934			sval[n] = '\0';
935
936			n = colon - (brace + 1) - 1;
937			pval = malloc(n + 1);
938			if (pval == NULL) return NULL;
939
940			memcpy(pval, (brace + 1), n);
941			pval[n] = '\0';
942		}
943		else
944		{
945			n = colon - p;
946			sval = malloc(n + 1);
947			if (sval == NULL) return NULL;
948
949			memcpy(sval, p, n);
950			sval[n] = '\0';
951		}
952
953		n = colon - p;
954		p = colon + 1;
955		index += (n + 1);
956	}
957
958	if (*p == ' ')
959	{
960		p++;
961		index++;
962	}
963
964	n = len - index;
965	if (n > 0)
966	{
967		mval = malloc(n + 1);
968		if (mval == NULL) return NULL;
969
970		memcpy(mval, p, n);
971		mval[n] = '\0';
972	}
973
974	if (fval == NULL) fval = asl_syslog_faciliy_num_to_name(LOG_USER);
975
976	msg = asl_new(ASL_TYPE_MSG);
977	if (msg == NULL) return NULL;
978
979	if (tval != NULL)
980	{
981		asl_set(msg, ASL_KEY_TIME, tval);
982		free(tval);
983	}
984
985	if (fval != NULL) asl_set(msg, "Facility", fval);
986	else asl_set(msg, "Facility", "user");
987
988	if (sval != NULL)
989	{
990		asl_set(msg, ASL_KEY_SENDER, sval);
991		free(sval);
992	}
993
994	if (pval != NULL)
995	{
996		asl_set(msg, ASL_KEY_PID, pval);
997		free(pval);
998	}
999	else
1000	{
1001		asl_set(msg, ASL_KEY_PID, "-1");
1002	}
1003
1004	if (mval != NULL)
1005	{
1006		asl_set(msg, ASL_KEY_MSG, mval);
1007		free(mval);
1008	}
1009
1010	asl_set(msg, ASL_KEY_LEVEL, prival);
1011	asl_set(msg, ASL_KEY_UID, "-2");
1012	asl_set(msg, ASL_KEY_GID, "-2");
1013
1014	if (hval != NULL)
1015	{
1016		asl_set(msg, ASL_KEY_HOST, hval);
1017		free(hval);
1018	}
1019	else if (rhost != NULL)
1020	{
1021		asl_set(msg, ASL_KEY_HOST, rhost);
1022	}
1023
1024	return msg;
1025}
1026
1027aslmsg
1028asl_input_parse(const char *in, int len, char *rhost, uint32_t source)
1029{
1030	aslmsg msg;
1031	int status, x, legacy, off;
1032
1033	asldebug("asl_input_parse: %s\n", (in == NULL) ? "NULL" : in);
1034
1035	if (in == NULL) return NULL;
1036
1037	legacy = 1;
1038	msg = NULL;
1039
1040	/* calculate length if not provided */
1041	if (len == 0) len = strlen(in);
1042
1043	/*
1044	 * Determine if the input is "old" syslog format or new ASL format.
1045	 * Old format lines should start with "<", but they can just be straight text.
1046	 * ASL input may start with a length (10 bytes) followed by a space and a '['.
1047	 * The length is optional, so ASL messages may also just start with '['.
1048	 */
1049	if ((in[0] != '<') && (len > 11))
1050	{
1051		status = sscanf(in, "%d ", &x);
1052		if ((status == 1) && (in[10] == ' ') && (in[11] == '[')) legacy = 0;
1053	}
1054
1055	if (legacy == 1) return asl_syslog_input_convert(in, len, rhost, source);
1056
1057	off = 11;
1058	if (in[0] == '[') off = 0;
1059
1060	msg = (aslmsg)asl_msg_from_string(in + off);
1061	if (msg == NULL) return NULL;
1062
1063	if (rhost != NULL) asl_set(msg, ASL_KEY_HOST, rhost);
1064
1065	return msg;
1066}
1067
1068#if !TARGET_IPHONE_SIMULATOR
1069void
1070launchd_callback(struct timeval *when, pid_t from_pid, pid_t about_pid, uid_t sender_uid, gid_t sender_gid, int priority, const char *from_name, const char *about_name, const char *session_name, const char *msg)
1071{
1072	aslmsg m;
1073	char str[256];
1074	time_t now;
1075
1076	if (global.launchd_enabled == 0) return;
1077
1078/*
1079	asldebug("launchd_callback Time %lu %lu PID %u RefPID %u UID %d GID %d PRI %d Sender %s Ref %s Session %s Message %s\n",
1080	when->tv_sec, when->tv_usec, from_pid, about_pid, sender_uid, sender_gid, priority, from_name, about_name, session_name, msg);
1081*/
1082
1083	m = asl_new(ASL_TYPE_MSG);
1084	if (m == NULL) return;
1085
1086	/* Level */
1087	if (priority < ASL_LEVEL_EMERG) priority = ASL_LEVEL_EMERG;
1088	if (priority > ASL_LEVEL_DEBUG) priority = ASL_LEVEL_DEBUG;
1089	snprintf(str, sizeof(str), "%d", priority);
1090
1091	asl_set(m, ASL_KEY_LEVEL, str);
1092
1093	/* Time */
1094	if (when != NULL)
1095	{
1096		snprintf(str, sizeof(str), "%lu", when->tv_sec);
1097		asl_set(m, ASL_KEY_TIME, str);
1098
1099		snprintf(str, sizeof(str), "%lu", 1000 * (unsigned long int)when->tv_usec);
1100		asl_set(m, ASL_KEY_TIME_NSEC, str);
1101	}
1102	else
1103	{
1104		now = time(NULL);
1105		snprintf(str, sizeof(str), "%lu", now);
1106		asl_set(m, ASL_KEY_TIME, str);
1107	}
1108
1109	/* Facility */
1110	asl_set(m, ASL_KEY_FACILITY, FACILITY_CONSOLE);
1111
1112	/* UID */
1113	snprintf(str, sizeof(str), "%u", (unsigned int)sender_uid);
1114	asl_set(m, ASL_KEY_UID, str);
1115
1116	/* GID */
1117	snprintf(str, sizeof(str), "%u", (unsigned int)sender_gid);
1118	asl_set(m, ASL_KEY_GID, str);
1119
1120	/* PID */
1121	if (from_pid != 0)
1122	{
1123		snprintf(str, sizeof(str), "%u", (unsigned int)from_pid);
1124		asl_set(m, ASL_KEY_PID, str);
1125	}
1126
1127	/* Reference PID */
1128	if ((about_pid > 0) && (about_pid != from_pid))
1129	{
1130		snprintf(str, sizeof(str), "%u", (unsigned int)about_pid);
1131		asl_set(m, ASL_KEY_REF_PID, str);
1132	}
1133
1134	/* Sender */
1135	if (from_name != NULL)
1136	{
1137		asl_set(m, ASL_KEY_SENDER, from_name);
1138	}
1139
1140	/* ReadUID */
1141	if (sender_uid != 0)
1142	{
1143		snprintf(str, sizeof(str), "%d", (int)sender_uid);
1144		asl_set(m, ASL_KEY_READ_UID, str);
1145	}
1146
1147	/* Reference Process */
1148	if (about_name != NULL)
1149	{
1150		if ((from_name != NULL) && (strcmp(from_name, about_name) != 0))
1151		{
1152			asl_set(m, ASL_KEY_REF_PROC, about_name);
1153		}
1154	}
1155
1156	/* Session */
1157	if (session_name != NULL)
1158	{
1159		asl_set(m, ASL_KEY_SESSION, session_name);
1160	}
1161
1162	/* Message */
1163	if (msg != NULL)
1164	{
1165		asl_set(m, ASL_KEY_MSG, msg);
1166	}
1167
1168	process_message(m, SOURCE_LAUNCHD);
1169}
1170
1171#endif
1172