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