xref: /freebsd/contrib/openbsm/bin/auditd/auditd_darwin.c (revision a7623790fb345e6dc986dfd31df0ace115e6f2e4)
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  */
61 static aslmsg 		au_aslmsg = NULL;
62 static aslclient	au_aslclient = NULL;
63 
64 static mach_port_t	control_port = MACH_PORT_NULL;
65 static mach_port_t	signal_port = MACH_PORT_NULL;
66 static mach_port_t	port_set = MACH_PORT_NULL;
67 
68 /*
69  * Current auditing state (cache).
70  */
71 static 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  */
77 static 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  */
92 void
93 auditd_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  */
133 void
134 auditd_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 
143 void
144 auditd_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 
153 void
154 auditd_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 
163 void
164 auditd_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  */
176 static void
177 init_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  */
198 void
199 auditd_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  */
221 int
222 auditd_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  */
236 static mach_port_t
237 lookup_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 
284 static int
285 mach_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  */
359 int
360 auditd_open_trigger(int launchd_flag)
361 {
362 
363 	return (mach_setup(launchd_flag));
364 }
365 
366 /*
367  * Close the trigger messaging mechanism.
368  */
369 int
370 auditd_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  */
380 static boolean_t
381 auditd_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  */
426 void
427 auditd_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  */
443 kern_return_t
444 audit_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  */
456 kern_return_t
457 auditd_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  */
471 void
472 auditd_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