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