1/*-
2 * Copyright (c) 2004-2009 Apple Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1.  Redistributions of source code must retain the above copyright
10 *     notice, this list of conditions and the following disclaimer.
11 * 2.  Redistributions in binary form must reproduce the above copyright
12 *     notice, this list of conditions and the following disclaimer in the
13 *     documentation and/or other materials provided with the distribution.
14 * 3.  Neither the name of Apple Inc. ("Apple") nor the names of
15 *     its contributors may be used to endorse or promote products derived
16 *     from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 *
29 * $P4: //depot/projects/trustedbsd/openbsm/bin/auditd/auditd_darwin.c#5 $
30 */
31
32#include <sys/types.h>
33
34#include <config/config.h>
35
36#include <errno.h>
37#include <stdarg.h>
38#include <stdlib.h>
39#include <unistd.h>
40
41#include <bsm/audit.h>
42#include <bsm/audit_uevents.h>
43#include <bsm/auditd_lib.h>
44#include <bsm/libbsm.h>
45
46#include <asl.h>
47#include <launch.h>
48#include <notify.h>
49#include <mach/port.h>
50#include <mach/mach_error.h>
51#include <mach/mach_traps.h>
52#include <mach/mach.h>
53#include <mach/host_special_ports.h>
54
55#include "auditd.h"
56
57#include "auditd_controlServer.h"
58#include "audit_triggersServer.h"
59
60/*
61 * Apple System Logger Handles.
62 */
63static aslmsg 		au_aslmsg = NULL;
64static aslclient	au_aslclient = NULL;
65
66static mach_port_t	control_port = MACH_PORT_NULL;
67static mach_port_t	signal_port = MACH_PORT_NULL;
68static mach_port_t	port_set = MACH_PORT_NULL;
69
70/*
71 * Current auditing state (cache).
72 */
73static int		auditing_state = AUD_STATE_INIT;
74
75/*
76 * Maximum idle time before auditd terminates under launchd.
77 * If it is zero then auditd does not timeout while idle.
78 */
79static int		max_idletime = 0;
80
81#ifndef	__BSM_INTERNAL_NOTIFY_KEY
82#define	__BSM_INTERNAL_NOTIFY_KEY	"com.apple.audit.change"
83#endif /* __BSM_INTERNAL_NOTIFY_KEY */
84
85#ifndef	__AUDIT_LAUNCHD_LABEL
86#define	__AUDIT_LAUNCHD_LABEL		"com.apple.auditd"
87#endif /* __AUDIT_LAUNCHD_LABEL */
88
89#define	MAX_MSG_SIZE	4096
90
91/*
92 * Open and set up system logging.
93 */
94void
95auditd_openlog(int debug, gid_t gid)
96{
97	uint32_t opt = 0;
98	char *cp = NULL;
99
100	if (debug)
101		opt = ASL_OPT_STDERR;
102
103	au_aslclient = asl_open("auditd", "com.apple.auditd", opt);
104	au_aslmsg = asl_new(ASL_TYPE_MSG);
105
106#ifdef ASL_KEY_READ_UID
107	/*
108	 * Make it only so the audit administrator and members of the audit
109	 * review group (if used) have access to the auditd system log messages.
110	 */
111	asl_set(au_aslmsg, ASL_KEY_READ_UID, "0");
112	asprintf(&cp, "%u", gid);
113	if (cp != NULL) {
114#ifdef ASL_KEY_READ_GID
115		asl_set(au_aslmsg, ASL_KEY_READ_GID, cp);
116#endif
117		free(cp);
118	}
119#endif
120
121	/*
122	 * Set the client-side system log filtering.
123	 */
124	if (debug)
125		asl_set_filter(au_aslclient,
126		    ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG));
127	else
128		asl_set_filter(au_aslclient,
129		    ASL_FILTER_MASK_UPTO(ASL_LEVEL_INFO));
130}
131
132/*
133 * Log messages at different priority levels.
134 */
135void
136auditd_log_err(const char *fmt, ...)
137{
138	va_list ap;
139
140	va_start(ap, fmt);
141	asl_vlog(au_aslclient, au_aslmsg, ASL_LEVEL_ERR, fmt, ap);
142	va_end(ap);
143}
144
145void
146auditd_log_notice(const char *fmt, ...)
147{
148	va_list ap;
149
150	va_start(ap, fmt);
151	asl_vlog(au_aslclient, au_aslmsg, ASL_LEVEL_NOTICE, fmt, ap);
152	va_end(ap);
153}
154
155void
156auditd_log_info(const char *fmt, ...)
157{
158	va_list ap;
159
160	va_start(ap, fmt);
161	asl_vlog(au_aslclient, au_aslmsg, ASL_LEVEL_INFO, fmt, ap);
162	va_end(ap);
163}
164
165void
166auditd_log_debug(const char *fmt, ...)
167{
168	va_list ap;
169
170	va_start(ap, fmt);
171	asl_vlog(au_aslclient, au_aslmsg, ASL_LEVEL_DEBUG, fmt, ap);
172	va_end(ap);
173}
174
175/*
176 * Get the auditing state from the kernel and cache it.
177 */
178static void
179init_audit_state(void)
180{
181	int au_cond;
182
183	if (audit_get_cond(&au_cond) < 0) {
184		if (errno != ENOSYS) {
185			auditd_log_err("Audit status check failed (%s)",
186			    strerror(errno));
187		}
188		auditing_state = AUD_STATE_DISABLED;
189	} else
190		if (au_cond == AUC_NOAUDIT || au_cond == AUC_DISABLED)
191			auditing_state = AUD_STATE_DISABLED;
192		else
193			auditing_state = AUD_STATE_ENABLED;
194}
195
196/*
197 * Update the cached auditing state.  Let other tasks that may be caching it
198 * as well to update their state via notify(3).
199 */
200void
201auditd_set_state(int state)
202{
203	int old_auditing_state = auditing_state;
204
205	if (state == AUD_STATE_INIT)
206		init_audit_state();
207	else
208		auditing_state = state;
209
210	if (auditing_state != old_auditing_state) {
211		notify_post(__BSM_INTERNAL_NOTIFY_KEY);
212
213		if (auditing_state == AUD_STATE_ENABLED)
214			auditd_log_notice("Auditing enabled");
215		if (auditing_state == AUD_STATE_DISABLED)
216			auditd_log_notice("Auditing disabled");
217	}
218}
219
220/*
221 * Get the cached auditing state.
222 */
223int
224auditd_get_state(void)
225{
226
227	if (auditing_state == AUD_STATE_INIT) {
228		init_audit_state();
229		notify_post(__BSM_INTERNAL_NOTIFY_KEY);
230	}
231
232	return (auditing_state);
233}
234
235/*
236 * Lookup the audit mach port in the launchd dictionary.
237 */
238static mach_port_t
239lookup_machport(const char *label)
240{
241	launch_data_t msg, msd, ld, cdict, to;
242	mach_port_t mp = MACH_PORT_NULL;
243
244	msg = launch_data_new_string(LAUNCH_KEY_CHECKIN);
245
246	cdict = launch_msg(msg);
247	if (cdict == NULL) {
248		auditd_log_err("launch_msg(\"" LAUNCH_KEY_CHECKIN
249		    "\") IPC failure: %m");
250                return (MACH_PORT_NULL);
251        }
252
253	if (launch_data_get_type(cdict) == LAUNCH_DATA_ERRNO) {
254		errno = launch_data_get_errno(cdict);
255		auditd_log_err("launch_data_get_type() can't get dict: %m");
256		return (MACH_PORT_NULL);
257	}
258
259	to = launch_data_dict_lookup(cdict, LAUNCH_JOBKEY_TIMEOUT);
260	if (to) {
261		max_idletime = launch_data_get_integer(to);
262		auditd_log_debug("launchd timeout set to %d", max_idletime);
263	} else {
264		auditd_log_debug("launchd timeout not set, setting to 60");
265		max_idletime = 60;
266	}
267
268	msd = launch_data_dict_lookup(cdict, LAUNCH_JOBKEY_MACHSERVICES);
269	if (msd == NULL) {
270		auditd_log_err(
271		    "launch_data_dict_lookup() can't get mach services");
272		return (MACH_PORT_NULL);
273	}
274
275	ld = launch_data_dict_lookup(msd, label);
276	if (ld == NULL) {
277		auditd_log_err("launch_data_dict_lookup can't find %s", label);
278		return (MACH_PORT_NULL);
279	}
280
281	mp = launch_data_get_machport(ld);
282
283	return (mp);
284}
285
286static int
287mach_setup(int launchd_flag)
288{
289	mach_msg_type_name_t poly;
290
291	/*
292	 * Allocate a port set.
293	 */
294	if (mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_PORT_SET,
295	    &port_set) != KERN_SUCCESS)  {
296		auditd_log_err("Allocation of port set failed");
297		return (-1);
298	}
299
300
301	/*
302	 * Allocate a signal reflection port.
303	 */
304	if (mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE,
305	    &signal_port) != KERN_SUCCESS ||
306	    mach_port_move_member(mach_task_self(), signal_port, port_set) !=
307	    KERN_SUCCESS)  {
308		auditd_log_err("Allocation of signal port failed");
309		return (-1);
310	}
311
312	/*
313	 * Allocate a trigger port.
314	 */
315	if (launchd_flag) {
316		/*
317		 * If started under launchd, lookup port in launchd dictionary.
318		 */
319		if ((control_port = lookup_machport(__AUDIT_LAUNCHD_LABEL)) ==
320		    MACH_PORT_NULL || mach_port_move_member(mach_task_self(),
321		    control_port, port_set) != KERN_SUCCESS) {
322			auditd_log_err("Cannot get Mach control port"
323                            " via launchd");
324			return (-1);
325		} else
326			auditd_log_debug("Mach control port registered"
327			    " via launchd");
328	} else {
329		/*
330		 * If not started under launchd, allocate port and register.
331		 */
332		if (mach_port_allocate(mach_task_self(),
333		    MACH_PORT_RIGHT_RECEIVE, &control_port) != KERN_SUCCESS ||
334		    mach_port_move_member(mach_task_self(), control_port,
335		    port_set) != KERN_SUCCESS)
336			auditd_log_err("Allocation of trigger port failed");
337
338		/*
339		 * Create a send right on our trigger port.
340		 */
341		mach_port_extract_right(mach_task_self(), control_port,
342		    MACH_MSG_TYPE_MAKE_SEND, &control_port, &poly);
343
344		/*
345		 * Register the trigger port with the kernel.
346		 */
347		if (host_set_audit_control_port(mach_host_self(),
348		    control_port) != KERN_SUCCESS) {
349                        auditd_log_err("Cannot set Mach control port");
350			return (-1);
351		} else
352			auditd_log_debug("Mach control port registered");
353	}
354
355	return (0);
356}
357
358/*
359 * Open the trigger messaging mechanism.
360 */
361int
362auditd_open_trigger(int launchd_flag)
363{
364
365	return (mach_setup(launchd_flag));
366}
367
368/*
369 * Close the trigger messaging mechanism.
370 */
371int
372auditd_close_trigger(void)
373{
374
375	return (0);
376}
377
378/*
379 * Combined server handler.  Called by the mach message loop when there is
380 * a trigger or signal message.
381 */
382static boolean_t
383auditd_combined_server(mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP)
384{
385	mach_port_t local_port = InHeadP->msgh_local_port;
386
387	/* Reset the idle time alarm, if used. */
388	if (max_idletime)
389		alarm(max_idletime);
390
391	if (local_port == signal_port) {
392		int signo = InHeadP->msgh_id;
393
394		switch(signo) {
395		case SIGTERM:
396		case SIGALRM:
397			auditd_terminate();
398			/* Not reached. */
399
400		case SIGCHLD:
401			auditd_reap_children();
402			return (TRUE);
403
404		case SIGHUP:
405			auditd_config_controls();
406			return (TRUE);
407
408		default:
409			auditd_log_info("Received signal %d", signo);
410			return (TRUE);
411		}
412	} else if (local_port == control_port) {
413		boolean_t result;
414
415		result = audit_triggers_server(InHeadP, OutHeadP);
416		if (!result)
417			result = auditd_control_server(InHeadP, OutHeadP);
418			return (result);
419	}
420	auditd_log_info("Recevied msg on bad port 0x%x.", local_port);
421	return (FALSE);
422}
423
424/*
425 * The main event loop.  Wait for trigger messages or signals and handle them.
426 * It should not return unless there is a problem.
427 */
428void
429auditd_wait_for_events(void)
430{
431	kern_return_t   result;
432
433	/*
434	 * Call the mach messaging server loop.
435 	 */
436	result = mach_msg_server(auditd_combined_server, MAX_MSG_SIZE,
437	    port_set, MACH_MSG_OPTION_NONE);
438}
439
440/*
441 * Implementation of the audit_triggers() MIG simpleroutine.  Simply a
442 * wrapper function.  This handles input from the kernel on the host
443 * special mach port.
444 */
445kern_return_t
446audit_triggers(mach_port_t __unused audit_port, int trigger)
447{
448
449	auditd_handle_trigger(trigger);
450
451	return (KERN_SUCCESS);
452}
453
454/*
455 * Implementation of the auditd_control() MIG simpleroutine.  Simply a
456 * wrapper function.  This handles input from the audit(1) tool.
457 */
458kern_return_t
459auditd_control(mach_port_t __unused auditd_port, int trigger)
460{
461
462	auditd_handle_trigger(trigger);
463
464	return (KERN_SUCCESS);
465}
466
467/*
468 * When we get a signal, we are often not at a clean point.  So, little can
469 * be done in the signal handler itself.  Instead,  we send a message to the
470 * main servicing loop to do proper handling from a non-signal-handler
471 * context.
472 */
473void
474auditd_relay_signal(int signal)
475{
476	mach_msg_empty_send_t msg;
477
478	msg.header.msgh_id = signal;
479	msg.header.msgh_remote_port = signal_port;
480	msg.header.msgh_local_port = MACH_PORT_NULL;
481	msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);
482	mach_msg(&(msg.header), MACH_SEND_MSG|MACH_SEND_TIMEOUT, sizeof(msg),
483	    0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
484}
485