1186545Srwatson/*-
2191273Srwatson * Copyright (c) 2004-2009 Apple Inc.
3186545Srwatson * All rights reserved.
4186545Srwatson *
5186545Srwatson * Redistribution and use in source and binary forms, with or without
6186545Srwatson * modification, are permitted provided that the following conditions
7186545Srwatson * are met:
8186545Srwatson *
9186545Srwatson * 1.  Redistributions of source code must retain the above copyright
10186545Srwatson *     notice, this list of conditions and the following disclaimer.
11186545Srwatson * 2.  Redistributions in binary form must reproduce the above copyright
12186545Srwatson *     notice, this list of conditions and the following disclaimer in the
13186545Srwatson *     documentation and/or other materials provided with the distribution.
14186545Srwatson * 3.  Neither the name of Apple Inc. ("Apple") nor the names of
15186545Srwatson *     its contributors may be used to endorse or promote products derived
16186545Srwatson *     from this software without specific prior written permission.
17186545Srwatson *
18186545Srwatson * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19186545Srwatson * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20186545Srwatson * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21186545Srwatson * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22186545Srwatson * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23186545Srwatson * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24186545Srwatson * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25186545Srwatson * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26186545Srwatson * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27186545Srwatson * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28186545Srwatson *
29191273Srwatson * $P4: //depot/projects/trustedbsd/openbsm/bin/auditd/auditd_darwin.c#5 $
30186545Srwatson */
31186545Srwatson
32186545Srwatson#include <sys/types.h>
33186545Srwatson
34186545Srwatson#include <config/config.h>
35186545Srwatson
36186545Srwatson#include <errno.h>
37186545Srwatson#include <stdarg.h>
38186545Srwatson#include <stdlib.h>
39186545Srwatson#include <unistd.h>
40186545Srwatson
41186545Srwatson#include <bsm/audit.h>
42186545Srwatson#include <bsm/audit_uevents.h>
43186545Srwatson#include <bsm/auditd_lib.h>
44186545Srwatson#include <bsm/libbsm.h>
45186545Srwatson
46186545Srwatson#include <asl.h>
47186545Srwatson#include <launch.h>
48186545Srwatson#include <notify.h>
49186545Srwatson#include <mach/port.h>
50186545Srwatson#include <mach/mach_error.h>
51186545Srwatson#include <mach/mach_traps.h>
52186545Srwatson#include <mach/mach.h>
53186545Srwatson#include <mach/host_special_ports.h>
54186545Srwatson
55186545Srwatson#include "auditd.h"
56186545Srwatson
57186545Srwatson#include "auditd_controlServer.h"
58186545Srwatson#include "audit_triggersServer.h"
59186545Srwatson
60186545Srwatson/*
61186545Srwatson * Apple System Logger Handles.
62186545Srwatson */
63186545Srwatsonstatic aslmsg 		au_aslmsg = NULL;
64186545Srwatsonstatic aslclient	au_aslclient = NULL;
65186545Srwatson
66186545Srwatsonstatic mach_port_t	control_port = MACH_PORT_NULL;
67186545Srwatsonstatic mach_port_t	signal_port = MACH_PORT_NULL;
68186545Srwatsonstatic mach_port_t	port_set = MACH_PORT_NULL;
69186545Srwatson
70186545Srwatson/*
71186545Srwatson * Current auditing state (cache).
72186545Srwatson */
73186545Srwatsonstatic int		auditing_state = AUD_STATE_INIT;
74186545Srwatson
75186545Srwatson/*
76186545Srwatson * Maximum idle time before auditd terminates under launchd.
77186545Srwatson * If it is zero then auditd does not timeout while idle.
78186545Srwatson */
79186545Srwatsonstatic int		max_idletime = 0;
80186545Srwatson
81186545Srwatson#ifndef	__BSM_INTERNAL_NOTIFY_KEY
82186545Srwatson#define	__BSM_INTERNAL_NOTIFY_KEY	"com.apple.audit.change"
83186545Srwatson#endif /* __BSM_INTERNAL_NOTIFY_KEY */
84186545Srwatson
85186545Srwatson#ifndef	__AUDIT_LAUNCHD_LABEL
86187214Srwatson#define	__AUDIT_LAUNCHD_LABEL		"com.apple.auditd"
87186545Srwatson#endif /* __AUDIT_LAUNCHD_LABEL */
88186545Srwatson
89186545Srwatson#define	MAX_MSG_SIZE	4096
90186545Srwatson
91186545Srwatson/*
92186545Srwatson * Open and set up system logging.
93186545Srwatson */
94186545Srwatsonvoid
95186545Srwatsonauditd_openlog(int debug, gid_t gid)
96186545Srwatson{
97186545Srwatson	uint32_t opt = 0;
98186545Srwatson	char *cp = NULL;
99186545Srwatson
100186545Srwatson	if (debug)
101186545Srwatson		opt = ASL_OPT_STDERR;
102186545Srwatson
103187214Srwatson	au_aslclient = asl_open("auditd", "com.apple.auditd", opt);
104186545Srwatson	au_aslmsg = asl_new(ASL_TYPE_MSG);
105186545Srwatson
106186545Srwatson#ifdef ASL_KEY_READ_UID
107186545Srwatson	/*
108186545Srwatson	 * Make it only so the audit administrator and members of the audit
109186545Srwatson	 * review group (if used) have access to the auditd system log messages.
110186545Srwatson	 */
111186545Srwatson	asl_set(au_aslmsg, ASL_KEY_READ_UID, "0");
112186545Srwatson	asprintf(&cp, "%u", gid);
113186545Srwatson	if (cp != NULL) {
114186545Srwatson#ifdef ASL_KEY_READ_GID
115186545Srwatson		asl_set(au_aslmsg, ASL_KEY_READ_GID, cp);
116186545Srwatson#endif
117186545Srwatson		free(cp);
118186545Srwatson	}
119186545Srwatson#endif
120186545Srwatson
121186545Srwatson	/*
122186545Srwatson	 * Set the client-side system log filtering.
123186545Srwatson	 */
124186545Srwatson	if (debug)
125186545Srwatson		asl_set_filter(au_aslclient,
126186545Srwatson		    ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG));
127186545Srwatson	else
128186545Srwatson		asl_set_filter(au_aslclient,
129186545Srwatson		    ASL_FILTER_MASK_UPTO(ASL_LEVEL_INFO));
130186545Srwatson}
131186545Srwatson
132186545Srwatson/*
133186545Srwatson * Log messages at different priority levels.
134186545Srwatson */
135186545Srwatsonvoid
136186545Srwatsonauditd_log_err(const char *fmt, ...)
137186545Srwatson{
138186545Srwatson	va_list ap;
139186545Srwatson
140186545Srwatson	va_start(ap, fmt);
141186545Srwatson	asl_vlog(au_aslclient, au_aslmsg, ASL_LEVEL_ERR, fmt, ap);
142186545Srwatson	va_end(ap);
143186545Srwatson}
144186545Srwatson
145186545Srwatsonvoid
146186545Srwatsonauditd_log_notice(const char *fmt, ...)
147186545Srwatson{
148186545Srwatson	va_list ap;
149186545Srwatson
150186545Srwatson	va_start(ap, fmt);
151186545Srwatson	asl_vlog(au_aslclient, au_aslmsg, ASL_LEVEL_NOTICE, fmt, ap);
152186545Srwatson	va_end(ap);
153186545Srwatson}
154186545Srwatson
155186545Srwatsonvoid
156186545Srwatsonauditd_log_info(const char *fmt, ...)
157186545Srwatson{
158186545Srwatson	va_list ap;
159186545Srwatson
160186545Srwatson	va_start(ap, fmt);
161186545Srwatson	asl_vlog(au_aslclient, au_aslmsg, ASL_LEVEL_INFO, fmt, ap);
162186545Srwatson	va_end(ap);
163186545Srwatson}
164186545Srwatson
165186545Srwatsonvoid
166186545Srwatsonauditd_log_debug(const char *fmt, ...)
167186545Srwatson{
168186545Srwatson	va_list ap;
169186545Srwatson
170186545Srwatson	va_start(ap, fmt);
171186545Srwatson	asl_vlog(au_aslclient, au_aslmsg, ASL_LEVEL_DEBUG, fmt, ap);
172186545Srwatson	va_end(ap);
173186545Srwatson}
174186545Srwatson
175186545Srwatson/*
176186545Srwatson * Get the auditing state from the kernel and cache it.
177186545Srwatson */
178186545Srwatsonstatic void
179186545Srwatsoninit_audit_state(void)
180186545Srwatson{
181191273Srwatson	int au_cond;
182186545Srwatson
183191273Srwatson	if (audit_get_cond(&au_cond) < 0) {
184186545Srwatson		if (errno != ENOSYS) {
185186545Srwatson			auditd_log_err("Audit status check failed (%s)",
186186545Srwatson			    strerror(errno));
187186545Srwatson		}
188186545Srwatson		auditing_state = AUD_STATE_DISABLED;
189186545Srwatson	} else
190186545Srwatson		if (au_cond == AUC_NOAUDIT || au_cond == AUC_DISABLED)
191186545Srwatson			auditing_state = AUD_STATE_DISABLED;
192186545Srwatson		else
193186545Srwatson			auditing_state = AUD_STATE_ENABLED;
194186545Srwatson}
195186545Srwatson
196186545Srwatson/*
197186545Srwatson * Update the cached auditing state.  Let other tasks that may be caching it
198186545Srwatson * as well to update their state via notify(3).
199186545Srwatson */
200186545Srwatsonvoid
201186545Srwatsonauditd_set_state(int state)
202186545Srwatson{
203186545Srwatson	int old_auditing_state = auditing_state;
204186545Srwatson
205186545Srwatson	if (state == AUD_STATE_INIT)
206186545Srwatson		init_audit_state();
207186545Srwatson	else
208186545Srwatson		auditing_state = state;
209186545Srwatson
210186545Srwatson	if (auditing_state != old_auditing_state) {
211186545Srwatson		notify_post(__BSM_INTERNAL_NOTIFY_KEY);
212186545Srwatson
213186545Srwatson		if (auditing_state == AUD_STATE_ENABLED)
214186545Srwatson			auditd_log_notice("Auditing enabled");
215186545Srwatson		if (auditing_state == AUD_STATE_DISABLED)
216186545Srwatson			auditd_log_notice("Auditing disabled");
217186545Srwatson	}
218186545Srwatson}
219186545Srwatson
220186545Srwatson/*
221186545Srwatson * Get the cached auditing state.
222186545Srwatson */
223186545Srwatsonint
224186545Srwatsonauditd_get_state(void)
225186545Srwatson{
226186545Srwatson
227186545Srwatson	if (auditing_state == AUD_STATE_INIT) {
228186545Srwatson		init_audit_state();
229186545Srwatson		notify_post(__BSM_INTERNAL_NOTIFY_KEY);
230186545Srwatson	}
231186545Srwatson
232186545Srwatson	return (auditing_state);
233186545Srwatson}
234186545Srwatson
235186545Srwatson/*
236186545Srwatson * Lookup the audit mach port in the launchd dictionary.
237186545Srwatson */
238186545Srwatsonstatic mach_port_t
239186545Srwatsonlookup_machport(const char *label)
240186545Srwatson{
241186545Srwatson	launch_data_t msg, msd, ld, cdict, to;
242186545Srwatson	mach_port_t mp = MACH_PORT_NULL;
243186545Srwatson
244186545Srwatson	msg = launch_data_new_string(LAUNCH_KEY_CHECKIN);
245186545Srwatson
246186545Srwatson	cdict = launch_msg(msg);
247186545Srwatson	if (cdict == NULL) {
248186545Srwatson		auditd_log_err("launch_msg(\"" LAUNCH_KEY_CHECKIN
249186545Srwatson		    "\") IPC failure: %m");
250186545Srwatson                return (MACH_PORT_NULL);
251186545Srwatson        }
252186545Srwatson
253186545Srwatson	if (launch_data_get_type(cdict) == LAUNCH_DATA_ERRNO) {
254186545Srwatson		errno = launch_data_get_errno(cdict);
255186545Srwatson		auditd_log_err("launch_data_get_type() can't get dict: %m");
256186545Srwatson		return (MACH_PORT_NULL);
257186545Srwatson	}
258186545Srwatson
259186545Srwatson	to = launch_data_dict_lookup(cdict, LAUNCH_JOBKEY_TIMEOUT);
260186545Srwatson	if (to) {
261186545Srwatson		max_idletime = launch_data_get_integer(to);
262186545Srwatson		auditd_log_debug("launchd timeout set to %d", max_idletime);
263186545Srwatson	} else {
264186545Srwatson		auditd_log_debug("launchd timeout not set, setting to 60");
265186545Srwatson		max_idletime = 60;
266186545Srwatson	}
267186545Srwatson
268186545Srwatson	msd = launch_data_dict_lookup(cdict, LAUNCH_JOBKEY_MACHSERVICES);
269186545Srwatson	if (msd == NULL) {
270186545Srwatson		auditd_log_err(
271186545Srwatson		    "launch_data_dict_lookup() can't get mach services");
272186545Srwatson		return (MACH_PORT_NULL);
273186545Srwatson	}
274186545Srwatson
275186545Srwatson	ld = launch_data_dict_lookup(msd, label);
276186545Srwatson	if (ld == NULL) {
277186545Srwatson		auditd_log_err("launch_data_dict_lookup can't find %s", label);
278186545Srwatson		return (MACH_PORT_NULL);
279186545Srwatson	}
280186545Srwatson
281186545Srwatson	mp = launch_data_get_machport(ld);
282186545Srwatson
283186545Srwatson	return (mp);
284186545Srwatson}
285186545Srwatson
286186545Srwatsonstatic int
287186545Srwatsonmach_setup(int launchd_flag)
288186545Srwatson{
289186545Srwatson	mach_msg_type_name_t poly;
290186545Srwatson
291186545Srwatson	/*
292186545Srwatson	 * Allocate a port set.
293186545Srwatson	 */
294186545Srwatson	if (mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_PORT_SET,
295186545Srwatson	    &port_set) != KERN_SUCCESS)  {
296186545Srwatson		auditd_log_err("Allocation of port set failed");
297186545Srwatson		return (-1);
298186545Srwatson	}
299186545Srwatson
300186545Srwatson
301186545Srwatson	/*
302186545Srwatson	 * Allocate a signal reflection port.
303186545Srwatson	 */
304186545Srwatson	if (mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE,
305186545Srwatson	    &signal_port) != KERN_SUCCESS ||
306186545Srwatson	    mach_port_move_member(mach_task_self(), signal_port, port_set) !=
307186545Srwatson	    KERN_SUCCESS)  {
308186545Srwatson		auditd_log_err("Allocation of signal port failed");
309186545Srwatson		return (-1);
310186545Srwatson	}
311186545Srwatson
312186545Srwatson	/*
313186545Srwatson	 * Allocate a trigger port.
314186545Srwatson	 */
315186545Srwatson	if (launchd_flag) {
316186545Srwatson		/*
317186545Srwatson		 * If started under launchd, lookup port in launchd dictionary.
318186545Srwatson		 */
319186545Srwatson		if ((control_port = lookup_machport(__AUDIT_LAUNCHD_LABEL)) ==
320186545Srwatson		    MACH_PORT_NULL || mach_port_move_member(mach_task_self(),
321186545Srwatson		    control_port, port_set) != KERN_SUCCESS) {
322186545Srwatson			auditd_log_err("Cannot get Mach control port"
323186545Srwatson                            " via launchd");
324186545Srwatson			return (-1);
325186545Srwatson		} else
326186545Srwatson			auditd_log_debug("Mach control port registered"
327186545Srwatson			    " via launchd");
328186545Srwatson	} else {
329186545Srwatson		/*
330186545Srwatson		 * If not started under launchd, allocate port and register.
331186545Srwatson		 */
332186545Srwatson		if (mach_port_allocate(mach_task_self(),
333186545Srwatson		    MACH_PORT_RIGHT_RECEIVE, &control_port) != KERN_SUCCESS ||
334186545Srwatson		    mach_port_move_member(mach_task_self(), control_port,
335186545Srwatson		    port_set) != KERN_SUCCESS)
336186545Srwatson			auditd_log_err("Allocation of trigger port failed");
337186545Srwatson
338186545Srwatson		/*
339186545Srwatson		 * Create a send right on our trigger port.
340186545Srwatson		 */
341186545Srwatson		mach_port_extract_right(mach_task_self(), control_port,
342186545Srwatson		    MACH_MSG_TYPE_MAKE_SEND, &control_port, &poly);
343186545Srwatson
344186545Srwatson		/*
345186545Srwatson		 * Register the trigger port with the kernel.
346186545Srwatson		 */
347186545Srwatson		if (host_set_audit_control_port(mach_host_self(),
348186545Srwatson		    control_port) != KERN_SUCCESS) {
349186545Srwatson                        auditd_log_err("Cannot set Mach control port");
350186545Srwatson			return (-1);
351186545Srwatson		} else
352186545Srwatson			auditd_log_debug("Mach control port registered");
353186545Srwatson	}
354186545Srwatson
355186545Srwatson	return (0);
356186545Srwatson}
357186545Srwatson
358186545Srwatson/*
359186545Srwatson * Open the trigger messaging mechanism.
360186545Srwatson */
361186545Srwatsonint
362186545Srwatsonauditd_open_trigger(int launchd_flag)
363186545Srwatson{
364186545Srwatson
365186545Srwatson	return (mach_setup(launchd_flag));
366186545Srwatson}
367186545Srwatson
368186545Srwatson/*
369186545Srwatson * Close the trigger messaging mechanism.
370186545Srwatson */
371186545Srwatsonint
372186545Srwatsonauditd_close_trigger(void)
373186545Srwatson{
374186545Srwatson
375186545Srwatson	return (0);
376186545Srwatson}
377186545Srwatson
378186545Srwatson/*
379186545Srwatson * Combined server handler.  Called by the mach message loop when there is
380186545Srwatson * a trigger or signal message.
381186545Srwatson */
382186545Srwatsonstatic boolean_t
383186545Srwatsonauditd_combined_server(mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP)
384186545Srwatson{
385186545Srwatson	mach_port_t local_port = InHeadP->msgh_local_port;
386186545Srwatson
387186545Srwatson	/* Reset the idle time alarm, if used. */
388186545Srwatson	if (max_idletime)
389186545Srwatson		alarm(max_idletime);
390186545Srwatson
391186545Srwatson	if (local_port == signal_port) {
392186545Srwatson		int signo = InHeadP->msgh_id;
393186545Srwatson
394186545Srwatson		switch(signo) {
395186545Srwatson		case SIGTERM:
396186545Srwatson		case SIGALRM:
397186545Srwatson			auditd_terminate();
398186545Srwatson			/* Not reached. */
399186545Srwatson
400186545Srwatson		case SIGCHLD:
401186545Srwatson			auditd_reap_children();
402186545Srwatson			return (TRUE);
403186545Srwatson
404186545Srwatson		case SIGHUP:
405186545Srwatson			auditd_config_controls();
406186545Srwatson			return (TRUE);
407186545Srwatson
408186545Srwatson		default:
409186545Srwatson			auditd_log_info("Received signal %d", signo);
410186545Srwatson			return (TRUE);
411186545Srwatson		}
412186545Srwatson	} else if (local_port == control_port) {
413186545Srwatson		boolean_t result;
414186545Srwatson
415186545Srwatson		result = audit_triggers_server(InHeadP, OutHeadP);
416186545Srwatson		if (!result)
417186545Srwatson			result = auditd_control_server(InHeadP, OutHeadP);
418186545Srwatson			return (result);
419186545Srwatson	}
420186545Srwatson	auditd_log_info("Recevied msg on bad port 0x%x.", local_port);
421186545Srwatson	return (FALSE);
422186545Srwatson}
423186545Srwatson
424186545Srwatson/*
425186545Srwatson * The main event loop.  Wait for trigger messages or signals and handle them.
426186545Srwatson * It should not return unless there is a problem.
427186545Srwatson */
428186545Srwatsonvoid
429186545Srwatsonauditd_wait_for_events(void)
430186545Srwatson{
431186545Srwatson	kern_return_t   result;
432186545Srwatson
433186545Srwatson	/*
434186545Srwatson	 * Call the mach messaging server loop.
435186545Srwatson 	 */
436186545Srwatson	result = mach_msg_server(auditd_combined_server, MAX_MSG_SIZE,
437186545Srwatson	    port_set, MACH_MSG_OPTION_NONE);
438186545Srwatson}
439186545Srwatson
440186545Srwatson/*
441186545Srwatson * Implementation of the audit_triggers() MIG simpleroutine.  Simply a
442186545Srwatson * wrapper function.  This handles input from the kernel on the host
443186545Srwatson * special mach port.
444186545Srwatson */
445186545Srwatsonkern_return_t
446186545Srwatsonaudit_triggers(mach_port_t __unused audit_port, int trigger)
447186545Srwatson{
448186545Srwatson
449186545Srwatson	auditd_handle_trigger(trigger);
450186545Srwatson
451186545Srwatson	return (KERN_SUCCESS);
452186545Srwatson}
453186545Srwatson
454186545Srwatson/*
455186545Srwatson * Implementation of the auditd_control() MIG simpleroutine.  Simply a
456186545Srwatson * wrapper function.  This handles input from the audit(1) tool.
457186545Srwatson */
458186545Srwatsonkern_return_t
459186545Srwatsonauditd_control(mach_port_t __unused auditd_port, int trigger)
460186545Srwatson{
461186545Srwatson
462186545Srwatson	auditd_handle_trigger(trigger);
463186545Srwatson
464186545Srwatson	return (KERN_SUCCESS);
465186545Srwatson}
466186545Srwatson
467186545Srwatson/*
468186545Srwatson * When we get a signal, we are often not at a clean point.  So, little can
469186545Srwatson * be done in the signal handler itself.  Instead,  we send a message to the
470186545Srwatson * main servicing loop to do proper handling from a non-signal-handler
471186545Srwatson * context.
472186545Srwatson */
473186545Srwatsonvoid
474186545Srwatsonauditd_relay_signal(int signal)
475186545Srwatson{
476186545Srwatson	mach_msg_empty_send_t msg;
477186545Srwatson
478186545Srwatson	msg.header.msgh_id = signal;
479186545Srwatson	msg.header.msgh_remote_port = signal_port;
480186545Srwatson	msg.header.msgh_local_port = MACH_PORT_NULL;
481186545Srwatson	msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);
482186545Srwatson	mach_msg(&(msg.header), MACH_SEND_MSG|MACH_SEND_TIMEOUT, sizeof(msg),
483186545Srwatson	    0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
484186545Srwatson}
485