xref: /illumos-gate/usr/src/cmd/auditd/doorway.c (revision 430fb051)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
23  */
24 
25 /*
26  * Copyright 2022 Tintri by DDN, Inc. All rights reserved.
27  */
28 
29 /*
30  * Threads:
31  *
32  * auditd is thread 0 and does signal handling
33  *
34  * input() is a door server that receives binary audit records and
35  * queues them for handling by an instance of process() for conversion to syslog
36  * message(s).  There is one process thread per plugin.
37  *
38  * Queues:
39  *
40  * Each plugin has a buffer pool and and queue for feeding the
41  * the process threads.  The input thread moves buffers from the pool
42  * to the queue and the process thread puts them back.
43  *
44  * Another pool, b_pool, contains buffers referenced by each of the
45  * process queues; this is to minimize the number of buffer copies
46  *
47  */
48 
49 #include <arpa/inet.h>
50 #include <assert.h>
51 #include <bsm/adt.h>
52 #include <dlfcn.h>
53 #include <errno.h>
54 #include <fcntl.h>
55 #include <libintl.h>
56 #include <pthread.h>
57 #include <secdb.h>
58 #include <security/auditd.h>
59 #include <signal.h>
60 #include <stdio.h>
61 #include <stdlib.h>
62 #include <string.h>
63 #include <syslog.h>
64 #include <sys/socket.h>
65 #include <sys/types.h>
66 #include <sys/stat.h>
67 #include <unistd.h>
68 #include <audit_plugin.h>	/* libbsm */
69 #include "plugin.h"
70 #include <bsm/audit_door_infc.h>
71 #include "queue.h"
72 
73 #define	DEBUG		0
74 
75 /* gettext() obfuscation routine for lint */
76 #ifdef __lint
77 #define	gettext(x)	x
78 #endif
79 
80 #if DEBUG
81 static FILE *dbfp;
82 #define	DUMP(w, x, y, z) dump_state(w, x, y, z)
83 #define	DPRINT(x) { (void) fprintf x; }
84 #else
85 #define	DUMP(w, x, y, z)
86 #define	DPRINT(x)
87 #endif
88 
89 #define	FATAL_MESSAGE_LEN	256
90 
91 #define	MIN_RECORD_SIZE	(size_t)25
92 
93 #define	INPUT_MIN		2
94 #define	THRESHOLD_PCT		75
95 #define	DEFAULT_BUF_SZ		(size_t)250
96 #define	BASE_PRIORITY		10	/* 0 - 20 valid for user, time share */
97 #define	HIGH_PRIORITY		BASE_PRIORITY - 1
98 
99 static thr_data_t	in_thr;		/* input thread locks and data */
100 static int		doorfd = -1;
101 
102 static int		largest_queue = INPUT_MIN;
103 static au_queue_t	b_pool;
104 static int		b_allocated = 0;
105 static pthread_mutex_t	b_alloc_lock;
106 static pthread_mutex_t	b_refcnt_lock;
107 
108 static void		input(void *, void *, int, door_desc_t *, int);
109 static void		*process(void *);
110 
111 static audit_q_t	*qpool_withdraw(plugin_t *);
112 static void		qpool_init(plugin_t *, int);
113 static void		qpool_return(plugin_t *, audit_q_t *);
114 static void		qpool_close(plugin_t *);
115 
116 static audit_rec_t	*bpool_withdraw(char *, size_t, size_t);
117 static void		bpool_init();
118 static void		bpool_return(audit_rec_t *);
119 
120 /*
121  * warn_or_fatal() -- log daemon error and (optionally) exit
122  */
123 static void
warn_or_fatal(int fatal,char * parting_shot)124 warn_or_fatal(int fatal, char *parting_shot)
125 {
126 	char	*severity;
127 	char	message[512];
128 
129 	if (fatal)
130 		severity = gettext("fatal error");
131 	else
132 		severity = gettext("warning");
133 
134 	(void) snprintf(message, 512, "%s:  %s", severity, parting_shot);
135 
136 	__audit_syslog("auditd", LOG_PID | LOG_ODELAY | LOG_CONS,
137 	    LOG_DAEMON, LOG_ALERT, message);
138 
139 	DPRINT((dbfp, "auditd warn_or_fatal %s: %s\n", severity, parting_shot));
140 	if (fatal)
141 		auditd_exit(1);
142 }
143 
144 /* Internal to doorway.c errors... */
145 #define	INTERNAL_LOAD_ERROR	-1
146 #define	INTERNAL_SYS_ERROR	-2
147 #define	INTERNAL_CONFIG_ERROR	-3
148 
149 /*
150  * report_error -- handle errors returned by plugin
151  *
152  * rc is plugin's return code if it is a non-negative value,
153  * otherwise it is a doorway.c code about a plugin.
154  */
155 static void
report_error(int rc,char * error_text,char * plugin_path)156 report_error(int rc, char *error_text, char *plugin_path)
157 {
158 	char		rcbuf[100]; /* short error name string */
159 	char		message[FATAL_MESSAGE_LEN];
160 	int		bad_count = 0;
161 	char		*name;
162 	char		empty[] = "..";
163 	boolean_t	warn = B_FALSE, discard = B_FALSE;
164 
165 	static int	no_plug = 0;
166 	static int	no_load = 0;
167 	static int	no_thread;
168 	static int	no_memory = 0;
169 	static int	invalid = 0;
170 	static int	retry = 0;
171 	static int	fail = 0;
172 
173 	name = plugin_path;
174 	if (error_text == NULL)
175 		error_text = empty;
176 	if (name == NULL)
177 		name = empty;
178 
179 	switch (rc) {
180 	case INTERNAL_LOAD_ERROR:
181 		warn = B_TRUE;
182 		bad_count = ++no_load;
183 		(void) strcpy(rcbuf, "load_error");
184 		break;
185 	case INTERNAL_SYS_ERROR:
186 		warn = B_TRUE;
187 		bad_count = ++no_thread;
188 		(void) strcpy(rcbuf, "sys_error");
189 		break;
190 	case INTERNAL_CONFIG_ERROR:
191 		warn = B_TRUE;
192 		bad_count = ++no_plug;
193 		(void) strcpy(rcbuf, "config_error");
194 		name = strdup("--");
195 		break;
196 	case AUDITD_SUCCESS:
197 		break;
198 	case AUDITD_NO_MEMORY:	/* no_memory */
199 		warn = B_TRUE;
200 		bad_count = ++no_memory;
201 		(void) strcpy(rcbuf, "no_memory");
202 		break;
203 	case AUDITD_INVALID:	/* invalid */
204 		warn = B_TRUE;
205 		bad_count = ++invalid;
206 		(void) strcpy(rcbuf, "invalid");
207 		break;
208 	case AUDITD_RETRY:
209 		warn = B_TRUE;
210 		bad_count = ++retry;
211 		(void) strcpy(rcbuf, "retry");
212 		break;
213 	case AUDITD_COMM_FAIL:	/* comm_fail */
214 		(void) strcpy(rcbuf, "comm_fail");
215 		break;
216 	case AUDITD_FATAL:	/* failure */
217 		warn = B_TRUE;
218 		bad_count = ++fail;
219 		(void) strcpy(rcbuf, "failure");
220 		break;
221 	case AUDITD_DISCARD:	/* discarded - shouldn't get here */
222 		/* Don't report this one; it's a non-error. */
223 		discard = B_TRUE;
224 		(void) strcpy(rcbuf, "discarded");
225 		break;
226 	default:
227 		(void) strcpy(rcbuf, "error");
228 		break;
229 	}
230 	DPRINT((dbfp, "report_error(%d - %s): %s\n\t%s\n",
231 	    bad_count, name, rcbuf, error_text));
232 	if (warn)
233 		__audit_dowarn2("plugin", name, rcbuf, error_text, bad_count);
234 	else if (!discard) {
235 		(void) snprintf(message, FATAL_MESSAGE_LEN,
236 		    gettext("audit plugin %s reported error = \"%s\": %s\n"),
237 		    name, rcbuf, error_text);
238 		warn_or_fatal(0, message);
239 	}
240 }
241 
242 static size_t
getlen(char * buf)243 getlen(char *buf)
244 {
245 	adr_t		adr;
246 	char		tokenid;
247 	uint32_t	len;
248 
249 	adr.adr_now = buf;
250 	adr.adr_stream = buf;
251 
252 	adrm_char(&adr, &tokenid, 1);
253 	if ((tokenid == AUT_OHEADER) || (tokenid == AUT_HEADER32) ||
254 	    (tokenid == AUT_HEADER32_EX) || (tokenid == AUT_HEADER64) ||
255 	    (tokenid == AUT_HEADER64_EX)) {
256 		adrm_u_int32(&adr, &len, 1);
257 
258 		return (len);
259 	}
260 	DPRINT((dbfp, "getlen() is not looking at a header token\n"));
261 
262 	return (0);
263 }
264 
265 /*
266  * load_function - call dlsym() to resolve the function address
267  */
268 static int
load_function(plugin_t * p,char * name,auditd_rc_t (** func)())269 load_function(plugin_t *p, char *name, auditd_rc_t (**func)())
270 {
271 	*func = (auditd_rc_t (*)())dlsym(p->plg_dlptr, name);
272 	if (*func == NULL) {
273 		char message[FATAL_MESSAGE_LEN];
274 		char *errmsg = dlerror();
275 
276 		(void) snprintf(message, FATAL_MESSAGE_LEN,
277 		    gettext("dlsym failed %s: error %s"),
278 		    name, errmsg != NULL ? errmsg : gettext("Unknown error\n"));
279 
280 		warn_or_fatal(0, message);
281 		return (-1);
282 	}
283 	return (0);
284 }
285 
286 /*
287  * load the auditd plug in
288  */
289 static int
load_plugin(plugin_t * p)290 load_plugin(plugin_t *p)
291 {
292 	struct stat64	stat;
293 	int		fd;
294 	int		fail = 0;
295 
296 	/*
297 	 * Stat the file so we can check modes and ownerships
298 	 */
299 	if ((fd = open(p->plg_path, O_NONBLOCK | O_RDONLY)) != -1) {
300 		if ((fstat64(fd, &stat) == -1) || (!S_ISREG(stat.st_mode)))
301 			fail = 1;
302 	} else
303 		fail = 1;
304 	if (fail) {
305 		char message[FATAL_MESSAGE_LEN];
306 
307 		(void) snprintf(message, FATAL_MESSAGE_LEN,
308 		    gettext("auditd plugin: stat(%s) failed: %s\n"),
309 		    p->plg_path, strerror(errno));
310 
311 		warn_or_fatal(0, message);
312 		return (-1);
313 	}
314 	/*
315 	 * Check the ownership of the file
316 	 */
317 	if (stat.st_uid != (uid_t)0) {
318 		char message[FATAL_MESSAGE_LEN];
319 
320 		(void) snprintf(message, FATAL_MESSAGE_LEN,
321 		    gettext(
322 		    "auditd plugin: Owner of the module %s is not root\n"),
323 		    p->plg_path);
324 
325 		warn_or_fatal(0, message);
326 		return (-1);
327 	}
328 	/*
329 	 * Check the modes on the file
330 	 */
331 	if (stat.st_mode&S_IWGRP) {
332 		char message[FATAL_MESSAGE_LEN];
333 
334 		(void) snprintf(message, FATAL_MESSAGE_LEN,
335 		    gettext("auditd plugin: module %s writable by group\n"),
336 		    p->plg_path);
337 
338 		warn_or_fatal(0, message);
339 		return (-1);
340 	}
341 	if (stat.st_mode&S_IWOTH) {
342 		char message[FATAL_MESSAGE_LEN];
343 
344 		(void) snprintf(message, FATAL_MESSAGE_LEN,
345 		    gettext("auditd plugin: module %s writable by world\n"),
346 		    p->plg_path);
347 
348 		warn_or_fatal(0, message);
349 		return (-1);
350 	}
351 	/*
352 	 * Open the plugin
353 	 */
354 	p->plg_dlptr = dlopen(p->plg_path, RTLD_LAZY);
355 
356 	if (p->plg_dlptr == NULL) {
357 		char message[FATAL_MESSAGE_LEN];
358 		char *errmsg = dlerror();
359 
360 		(void) snprintf(message, FATAL_MESSAGE_LEN,
361 		    gettext("plugin load %s failed: %s\n"),
362 		    p->plg_path, errmsg != NULL ? errmsg :
363 		    gettext("Unknown error\n"));
364 
365 		warn_or_fatal(0, message);
366 		return (-1);
367 	}
368 	if (load_function(p, "auditd_plugin", &(p->plg_fplugin)))
369 		return (-1);
370 
371 	if (load_function(p, "auditd_plugin_open", &(p->plg_fplugin_open)))
372 		return (-1);
373 
374 	if (load_function(p, "auditd_plugin_close", &(p->plg_fplugin_close)))
375 		return (-1);
376 
377 	return (0);
378 }
379 
380 /*
381  * unload_plugin() unlinks and frees the plugin_t structure after
382  * freeing buffers and structures that hang off it.  It also dlcloses
383  * the referenced plugin.  The return is the next entry, which may be NULL
384  *
385  * hold plugin_mutex for this call
386  */
387 static plugin_t *
unload_plugin(plugin_t * p)388 unload_plugin(plugin_t *p)
389 {
390 	plugin_t	*q, **r;
391 
392 	assert(pthread_mutex_trylock(&plugin_mutex) != 0);
393 
394 	DPRINT((dbfp, "unload_plugin: removing %s\n", p->plg_path));
395 
396 	_kva_free(p->plg_kvlist);	/* _kva_free accepts NULL */
397 	qpool_close(p);		/* qpool_close accepts NULL pool, queue */
398 	DPRINT((dbfp, "unload_plugin: %s structure removed\n", p->plg_path));
399 
400 	(void) dlclose(p->plg_dlptr);
401 
402 	DPRINT((dbfp, "unload_plugin: %s dlclosed\n", p->plg_path));
403 	free(p->plg_path);
404 
405 	(void) pthread_mutex_destroy(&(p->plg_mutex));
406 	(void) pthread_cond_destroy(&(p->plg_cv));
407 
408 	q = plugin_head;
409 	r = &plugin_head;
410 	while (q != NULL) {
411 		if (q == p) {
412 			*r = p->plg_next;
413 			free(p);
414 			break;
415 		}
416 		r = &(q->plg_next);
417 		q = q->plg_next;
418 	}
419 	return (*r);
420 }
421 
422 /*
423  * process return values from plugin_open
424  *
425  * presently no attribute is defined.
426  */
427 /* ARGSUSED */
428 static void
open_return(plugin_t * p,char * attrval)429 open_return(plugin_t *p, char *attrval)
430 {
431 }
432 
433 /*
434  * auditd_thread_init
435  *	- create threads
436  *	- load plugins
437  *
438  * auditd_thread_init is called at auditd startup with an initial list
439  * of plugins and again each time audit catches a SIGHUP or SIGUSR1.
440  */
441 int
auditd_thread_init()442 auditd_thread_init()
443 {
444 	int		threshold;
445 	auditd_rc_t	rc;
446 	plugin_t	*p;
447 	char		*open_params;
448 	char		*error_string;
449 	int		plugin_count = 0;
450 	static int	threads_ready = 0;
451 
452 	if (!threads_ready) {
453 		struct sched_param	param;
454 #if DEBUG
455 		dbfp = __auditd_debug_file_open();
456 #endif
457 		doorfd = door_create((void(*)())input, 0,
458 		    DOOR_REFUSE_DESC | DOOR_NO_CANCEL);
459 		if (doorfd < 0)
460 			return (1);	/* can't create door -> fatal */
461 
462 		param.sched_priority = BASE_PRIORITY;
463 		(void) pthread_setschedparam(pthread_self(), SCHED_OTHER,
464 		    &param);
465 
466 		/* input door server */
467 		(void) pthread_mutex_init(&(in_thr.thd_mutex), NULL);
468 		(void) pthread_cond_init(&(in_thr.thd_cv), NULL);
469 		in_thr.thd_waiting = 0;
470 
471 		bpool_init();
472 	}
473 	p = plugin_head;
474 	while (p != NULL) {
475 		if (p->plg_removed) {
476 			DPRINT((dbfp, "start removing %s\n", p->plg_path));
477 			/* tell process(p) to exit and dlclose */
478 			(void) pthread_cond_signal(&(p->plg_cv));
479 		} else if (!p->plg_initialized) {
480 			DPRINT((dbfp, "start initial load of %s\n",
481 			    p->plg_path));
482 			if (load_plugin(p)) {
483 				report_error(INTERNAL_LOAD_ERROR,
484 				    gettext("dynamic load failed"),
485 				    p->plg_path);
486 				p = unload_plugin(p);
487 				continue;
488 			}
489 			open_params = NULL;
490 			error_string = NULL;
491 			if ((rc = p->plg_fplugin_open(
492 			    p->plg_kvlist,
493 			    &open_params, &error_string)) != AUDITD_SUCCESS) {
494 				report_error(rc, error_string, p->plg_path);
495 				free(error_string);
496 				p = unload_plugin(p);
497 				continue;
498 			}
499 			open_return(p, open_params);
500 			p->plg_reopen = 0;
501 
502 			threshold = ((p->plg_qmax * THRESHOLD_PCT) + 99) / 100;
503 			p->plg_qmin = INPUT_MIN;
504 
505 			DPRINT((dbfp,
506 			    "calling qpool_init for %s with qmax=%d\n",
507 			    p->plg_path, p->plg_qmax));
508 
509 			qpool_init(p, threshold);
510 			audit_queue_init(&(p->plg_queue));
511 			p->plg_initialized = 1;
512 
513 			(void) pthread_mutex_init(&(p->plg_mutex), NULL);
514 			(void) pthread_cond_init(&(p->plg_cv), NULL);
515 			p->plg_waiting = 0;
516 
517 			if (pthread_create(&(p->plg_tid), NULL, process, p)) {
518 				report_error(INTERNAL_SYS_ERROR,
519 				    gettext("thread creation failed"),
520 				    p->plg_path);
521 				p = unload_plugin(p);
522 				continue;
523 			}
524 		} else if (p->plg_reopen) {
525 			DPRINT((dbfp, "reopen %s\n", p->plg_path));
526 			error_string = NULL;
527 			if ((rc = p->plg_fplugin_open(p->plg_kvlist,
528 			    &open_params, &error_string)) != AUDITD_SUCCESS) {
529 				report_error(rc, error_string, p->plg_path);
530 				free(error_string);
531 				p = unload_plugin(p);
532 				continue;
533 			}
534 			open_return(p, open_params);
535 			p->plg_reopen = 0;
536 
537 			DPRINT((dbfp, "%s qmax=%d\n",
538 			    p->plg_path, p->plg_qmax));
539 
540 		}
541 		p->plg_q_threshold = ((p->plg_qmax * THRESHOLD_PCT) + 99) / 100;
542 
543 		p = p->plg_next;
544 		plugin_count++;
545 	}
546 	if (plugin_count == 0) {
547 		report_error(INTERNAL_CONFIG_ERROR,
548 		    gettext("No plugins are configured"), NULL);
549 		return (-1);
550 	}
551 	if (!threads_ready) {
552 		/* unleash the kernel */
553 		rc = auditdoor(doorfd);
554 
555 		DPRINT((dbfp, "%d returned from auditdoor.\n",
556 		    rc));
557 		if (rc != 0)
558 			return (1);	/* fatal */
559 
560 		threads_ready = 1;
561 	}
562 	return (0);
563 }
564 
565 /*
566  * Door invocations that are in progress during a
567  * door_revoke() invocation are allowed to complete normally.
568  * -- man page for door_revoke()
569  */
570 void
auditd_thread_close()571 auditd_thread_close()
572 {
573 	if (doorfd == -1)
574 		return;
575 	(void) door_revoke(doorfd);
576 	doorfd = -1;
577 }
578 
579 /*
580  * qpool_init() sets up pool for queue entries (audit_q_t)
581  *
582  */
583 static void
qpool_init(plugin_t * p,int threshold)584 qpool_init(plugin_t *p, int threshold)
585 {
586 	int		i;
587 	audit_q_t	*node;
588 
589 	audit_queue_init(&(p->plg_pool));
590 
591 	DPRINT((dbfp, "qpool_init(%d) max, min, threshhold = %d, %d, %d\n",
592 	    p->plg_tid, p->plg_qmax, p->plg_qmin, threshold));
593 
594 	if (p->plg_qmax > largest_queue)
595 		largest_queue = p->plg_qmax;
596 
597 	p->plg_q_threshold = threshold;
598 
599 	for (i = 0; i < p->plg_qmin; i++) {
600 		node = malloc(sizeof (audit_q_t));
601 		if (node == NULL)
602 			warn_or_fatal(1, gettext("no memory\n"));
603 			/* doesn't return */
604 
605 		audit_enqueue(&p->plg_pool, node);
606 	}
607 }
608 
609 /*
610  * bpool_init() sets up pool and queue for record entries (audit_rec_t)
611  *
612  */
613 static void
bpool_init()614 bpool_init()
615 {
616 	int		i;
617 	audit_rec_t	*node;
618 
619 	audit_queue_init(&b_pool);
620 	(void) pthread_mutex_init(&b_alloc_lock, NULL);
621 	(void) pthread_mutex_init(&b_refcnt_lock, NULL);
622 
623 	for (i = 0; i < INPUT_MIN; i++) {
624 		node = malloc(AUDIT_REC_HEADER + DEFAULT_BUF_SZ);
625 		if (node == NULL)
626 			warn_or_fatal(1, gettext("no memory\n"));
627 			/* doesn't return */
628 
629 		node->abq_buf_len = DEFAULT_BUF_SZ;
630 
631 		node->abq_data_len = 0;
632 		audit_enqueue(&b_pool, node);
633 		(void) pthread_mutex_lock(&b_alloc_lock);
634 		b_allocated++;
635 		(void) pthread_mutex_unlock(&b_alloc_lock);
636 	}
637 }
638 
639 /*
640  * qpool_close() discard queue and pool for a discontinued plugin
641  *
642  * there is no corresponding bpool_close() since it would only
643  * be called as auditd is going down.
644  */
645 static void
qpool_close(plugin_t * p)646 qpool_close(plugin_t *p)
647 {
648 	audit_q_t	*q_node;
649 	audit_rec_t	*b_node;
650 
651 	if (!p->plg_initialized)
652 		return;
653 
654 	while (audit_dequeue(&(p->plg_pool), (void *)&q_node) == 0) {
655 		free(q_node);
656 	}
657 	audit_queue_destroy(&(p->plg_pool));
658 
659 	while (audit_dequeue(&(p->plg_queue), (void *)&q_node) == 0) {
660 		b_node = audit_release(&b_refcnt_lock, q_node->aqq_data);
661 		if (b_node != NULL)
662 			audit_enqueue(&b_pool, b_node);
663 		free(q_node);
664 	}
665 	audit_queue_destroy(&(p->plg_queue));
666 }
667 
668 /*
669  * qpool_withdraw
670  */
671 static audit_q_t *
qpool_withdraw(plugin_t * p)672 qpool_withdraw(plugin_t *p)
673 {
674 	audit_q_t	*node;
675 	int		rc;
676 
677 	/* get a buffer from the pool, if any */
678 	rc = audit_dequeue(&(p->plg_pool), (void *)&node);
679 	if (rc == 0)
680 		return (node);
681 
682 	/*
683 	 * the pool is empty: allocate a new element
684 	 */
685 	node = malloc(sizeof (audit_q_t));
686 
687 	if (node == NULL)
688 		warn_or_fatal(1, gettext("no memory\n"));
689 		/* doesn't return */
690 
691 	return (node);
692 }
693 
694 /*
695  * bpool_withdraw -- gets a buffer and fills it
696  *
697  */
698 static audit_rec_t *
bpool_withdraw(char * buffer,size_t buff_size,size_t request_size)699 bpool_withdraw(char *buffer, size_t buff_size, size_t request_size)
700 {
701 	audit_rec_t	*node;
702 	int		rc;
703 	size_t		new_length;
704 
705 	new_length = (request_size > DEFAULT_BUF_SZ) ?
706 	    request_size : DEFAULT_BUF_SZ;
707 
708 	/* get a buffer from the pool, if any */
709 	rc = audit_dequeue(&b_pool, (void *)&node);
710 
711 	DPRINT((dbfp, "bpool_withdraw buf length=%d,"
712 	    " requested size=%d, dequeue rc=%d\n",
713 	    new_length, request_size, rc));
714 
715 	if (rc == 0) {
716 		DPRINT((dbfp, "bpool_withdraw node=%p (pool=%d)\n",
717 		    (void *)node, audit_queue_size(&b_pool)));
718 
719 		if (new_length > node->abq_buf_len) {
720 			node = realloc(node, AUDIT_REC_HEADER + new_length);
721 			if (node == NULL)
722 				warn_or_fatal(1, gettext("no memory\n"));
723 				/* no return */
724 		}
725 	} else {
726 		/*
727 		 * the pool is empty: allocate a new element
728 		 */
729 		(void) pthread_mutex_lock(&b_alloc_lock);
730 		if (b_allocated >= largest_queue) {
731 			(void) pthread_mutex_unlock(&b_alloc_lock);
732 			DPRINT((dbfp, "bpool_withdraw is over max (pool=%d)\n",
733 			    audit_queue_size(&b_pool)));
734 			return (NULL);
735 		}
736 		(void) pthread_mutex_unlock(&b_alloc_lock);
737 
738 		node = malloc(AUDIT_REC_HEADER + new_length);
739 
740 		if (node == NULL)
741 			warn_or_fatal(1, gettext("no memory\n"));
742 		/* no return */
743 
744 		(void) pthread_mutex_lock(&b_alloc_lock);
745 		b_allocated++;
746 		(void) pthread_mutex_unlock(&b_alloc_lock);
747 		DPRINT((dbfp, "bpool_withdraw node=%p (alloc=%d, pool=%d)\n",
748 		    (void *)node, b_allocated, audit_queue_size(&b_pool)));
749 	}
750 	assert(request_size <= new_length);
751 
752 	(void) memcpy(node->abq_buffer, buffer, buff_size);
753 	node->abq_data_len = buff_size;
754 	node->abq_buf_len = new_length;
755 	node->abq_ref_count = 0;
756 
757 	return (node);
758 }
759 
760 /*
761  * qpool_return() moves queue nodes back to the pool queue.
762  *
763  * if the pool is over max, the node is discarded instead.
764  */
765 static void
qpool_return(plugin_t * p,audit_q_t * node)766 qpool_return(plugin_t *p, audit_q_t *node)
767 {
768 	int	qpool_size;
769 	int	q_size;
770 
771 #if DEBUG
772 	uint64_t	sequence = node->aqq_sequence;
773 #endif
774 	qpool_size = audit_queue_size(&(p->plg_pool));
775 	q_size = audit_queue_size(&(p->plg_queue));
776 
777 	if (qpool_size + q_size > p->plg_qmax)
778 		free(node);
779 	else
780 		audit_enqueue(&(p->plg_pool), node);
781 
782 	DPRINT((dbfp,
783 	    "qpool_return(%d):  seq=%llu, q size=%d,"
784 	    " pool size=%d (total alloc=%d), threshhold=%d\n",
785 	    p->plg_tid, sequence, q_size, qpool_size,
786 	    q_size + qpool_size, p->plg_q_threshold));
787 }
788 
789 /*
790  * bpool_return() moves queue nodes back to the pool queue.
791  */
792 static void
bpool_return(audit_rec_t * node)793 bpool_return(audit_rec_t *node)
794 {
795 #if DEBUG
796 	audit_rec_t	*copy = node;
797 #endif
798 	node = audit_release(&b_refcnt_lock, node);	/* decrement ref cnt */
799 
800 	if (node != NULL) {	/* NULL if ref cnt is not zero */
801 		audit_enqueue(&b_pool, node);
802 		DPRINT((dbfp,
803 		    "bpool_return: requeue %p (allocated=%d,"
804 		    " pool size=%d)\n", (void *)node, b_allocated,
805 		    audit_queue_size(&b_pool)));
806 	}
807 #if DEBUG
808 	else {
809 		DPRINT((dbfp,
810 		    "bpool_return: decrement count for %p (allocated=%d,"
811 		    " pool size=%d)\n", (void *)copy, b_allocated,
812 		    audit_queue_size(&b_pool)));
813 	}
814 #endif
815 }
816 
817 #if DEBUG
818 static void
dump_state(char * src,plugin_t * p,uint64_t count,char * msg)819 dump_state(char *src, plugin_t *p, uint64_t count, char *msg)
820 {
821 	struct sched_param	param;
822 	int			policy;
823 /*
824  * count is message sequence
825  */
826 	(void) pthread_getschedparam(p->plg_tid, &policy, &param);
827 	(void) fprintf(dbfp, "%7s(%d/%llu) %11s:"
828 	    " input_in_wait=%d"
829 	    " priority=%d"
830 	    " queue size=%d pool size=%d"
831 	    "\n\t"
832 	    "process wait=%d"
833 	    " tossed=%d"
834 	    " queued=%d"
835 	    " written=%d"
836 	    "\n",
837 	    src, p->plg_tid, count, msg,
838 	    in_thr.thd_waiting, param.sched_priority,
839 	    audit_queue_size(&(p->plg_queue)),
840 	    audit_queue_size(&(p->plg_pool)),
841 	    p->plg_waiting, p->plg_tossed,
842 	    p->plg_queued, p->plg_output);
843 
844 	(void) fflush(dbfp);
845 }
846 #endif
847 
848 /*
849  * policy_is_block: return 1 if the continue policy is off for any active
850  * plugin, else 0
851  */
852 static int
policy_is_block()853 policy_is_block()
854 {
855 	plugin_t *p;
856 
857 	(void) pthread_mutex_lock(&plugin_mutex);
858 	p = plugin_head;
859 
860 	while (p != NULL) {
861 		if (p->plg_cnt == 0) {
862 			(void) pthread_mutex_unlock(&plugin_mutex);
863 			DPRINT((dbfp,
864 			    "policy_is_block:  policy is to block\n"));
865 			return (1);
866 		}
867 		p = p->plg_next;
868 	}
869 	(void) pthread_mutex_unlock(&plugin_mutex);
870 	DPRINT((dbfp, "policy_is_block:  policy is to continue\n"));
871 	return (0);
872 }
873 
874 /*
875  * policy_update() -- the kernel has received a policy change.
876  * Presently, the only policy auditd cares about is AUDIT_CNT
877  */
878 static void
policy_update(uint32_t newpolicy)879 policy_update(uint32_t newpolicy)
880 {
881 	plugin_t *p;
882 
883 	DPRINT((dbfp, "policy change: %X\n", newpolicy));
884 	(void) pthread_mutex_lock(&plugin_mutex);
885 	p = plugin_head;
886 	while (p != NULL) {
887 		p->plg_cnt = (newpolicy & AUDIT_CNT) ? 1 : 0;
888 		(void) pthread_cond_signal(&(p->plg_cv));
889 
890 		DPRINT((dbfp, "policy changed for thread %d\n", p->plg_tid));
891 		p = p->plg_next;
892 	}
893 	(void) pthread_mutex_unlock(&plugin_mutex);
894 }
895 
896 /*
897  * queue_buffer() inputs a buffer and queues for each active plugin if
898  * it represents a complete audit record.  Otherwise it builds a
899  * larger buffer to hold the record and take successive buffers from
900  * c2audit to build a complete record; then queues it for each plugin.
901  *
902  * return 0 if data is queued (or damaged and tossed).  If resources
903  * are not available, return 0 if all active plugins have the cnt
904  * policy set, else 1.  0 is also returned if the input is a control
905  * message.  (aub_buf is aligned on a 64 bit boundary, so casting
906  * it to an integer works just fine.)
907  */
908 static int
queue_buffer(au_dbuf_t * kl)909 queue_buffer(au_dbuf_t *kl)
910 {
911 	plugin_t	*p;
912 	audit_rec_t	*b_copy;
913 	audit_q_t	*q_copy;
914 	boolean_t	referenced = 0;
915 	static char	*invalid_msg = "invalid audit record discarded";
916 	static char	*invalid_control = "invalid audit control discarded";
917 
918 	static audit_rec_t	*alt_b_copy = NULL;
919 	static size_t		alt_length;
920 	static size_t		alt_offset;
921 
922 	/*
923 	 * the buffer may be a kernel -> auditd message.  (only
924 	 * the policy change message exists so far.)
925 	 */
926 
927 	if ((kl->aub_type & AU_DBUF_NOTIFY) != 0) {
928 		uint32_t	control;
929 
930 		control = kl->aub_type & ~AU_DBUF_NOTIFY;
931 		switch (control) {
932 		case AU_DBUF_POLICY:
933 			/* LINTED */
934 			policy_update(*(uint32_t *)kl->aub_buf);
935 			break;
936 		case AU_DBUF_SHUTDOWN:
937 			(void) kill(getpid(), SIGTERM);
938 			DPRINT((dbfp, "AU_DBUF_SHUTDOWN message\n"));
939 			break;
940 		default:
941 			warn_or_fatal(0, gettext(invalid_control));
942 			break;
943 		}
944 		return (0);
945 	}
946 	/*
947 	 * The test for valid continuation/completion may fail. Need to
948 	 * assume the failure was earlier and that this buffer may
949 	 * be a valid first or complete buffer after discarding the
950 	 * incomplete record
951 	 */
952 
953 	if (alt_b_copy != NULL) {
954 		if ((kl->aub_type == AU_DBUF_FIRST) ||
955 		    (kl->aub_type == AU_DBUF_COMPLETE)) {
956 			DPRINT((dbfp, "copy is not null, partial is %d\n",
957 			    kl->aub_type));
958 			bpool_return(alt_b_copy);
959 			warn_or_fatal(0, gettext(invalid_msg));
960 			alt_b_copy = NULL;
961 		}
962 	}
963 	if (alt_b_copy != NULL) { /* continue collecting a long record */
964 		if (kl->aub_size + alt_offset > alt_length) {
965 			bpool_return(alt_b_copy);
966 			alt_b_copy = NULL;
967 			warn_or_fatal(0, gettext(invalid_msg));
968 			return (0);
969 		}
970 		(void) memcpy(alt_b_copy->abq_buffer + alt_offset, kl->aub_buf,
971 		    kl->aub_size);
972 		alt_offset += kl->aub_size;
973 		if (kl->aub_type == AU_DBUF_MIDDLE)
974 			return (0);
975 		b_copy = alt_b_copy;
976 		alt_b_copy = NULL;
977 		b_copy->abq_data_len = alt_length;
978 	} else if (kl->aub_type == AU_DBUF_FIRST) {
979 		/* first buffer of a multiple buffer record */
980 		alt_length = getlen(kl->aub_buf);
981 		if ((alt_length < MIN_RECORD_SIZE) ||
982 		    (alt_length <= kl->aub_size)) {
983 			warn_or_fatal(0, gettext(invalid_msg));
984 			return (0);
985 		}
986 		alt_b_copy = bpool_withdraw(kl->aub_buf, kl->aub_size,
987 		    alt_length);
988 
989 		if (alt_b_copy == NULL)
990 			return (policy_is_block());
991 
992 		alt_offset = kl->aub_size;
993 		return (0);
994 	} else { /* one buffer, one record -- the basic case */
995 		if (kl->aub_type != AU_DBUF_COMPLETE) {
996 			DPRINT((dbfp, "copy is null, partial is %d\n",
997 			    kl->aub_type));
998 			warn_or_fatal(0, gettext(invalid_msg));
999 			return (0);	/* tossed */
1000 		}
1001 		b_copy = bpool_withdraw(kl->aub_buf, kl->aub_size,
1002 		    kl->aub_size);
1003 
1004 		if (b_copy == NULL)
1005 			return (policy_is_block());
1006 	}
1007 
1008 	(void) pthread_mutex_lock(&plugin_mutex);
1009 	p = plugin_head;
1010 	while (p != NULL) {
1011 		if (!p->plg_removed) {
1012 			/*
1013 			 * Link the record buffer to the input queues.
1014 			 * To avoid a race, it is necessary to wait
1015 			 * until all reference count increments
1016 			 * are complete before queueing q_copy.
1017 			 */
1018 			audit_incr_ref(&b_refcnt_lock, b_copy);
1019 
1020 			q_copy = qpool_withdraw(p);
1021 			q_copy->aqq_sequence = p->plg_sequence++;
1022 			q_copy->aqq_data = b_copy;
1023 
1024 			p->plg_save_q_copy = q_copy;	/* enqueue below */
1025 			referenced = 1;
1026 		} else
1027 			p->plg_save_q_copy = NULL;
1028 		p = p->plg_next;
1029 	}
1030 	/*
1031 	 * now that the reference count is updated, queue it.
1032 	 */
1033 	if (referenced) {
1034 		p = plugin_head;
1035 		while ((p != NULL) && (p->plg_save_q_copy != NULL)) {
1036 			audit_enqueue(&(p->plg_queue), p->plg_save_q_copy);
1037 			(void) pthread_cond_signal(&(p->plg_cv));
1038 			p->plg_queued++;
1039 			p = p->plg_next;
1040 		}
1041 	} else
1042 		bpool_return(b_copy);
1043 
1044 	(void) pthread_mutex_unlock(&plugin_mutex);
1045 
1046 	return (0);
1047 }
1048 
1049 /*
1050  * wait_a_while() -- timed wait in the door server to allow output
1051  * time to catch up.
1052  */
1053 static void
wait_a_while()1054 wait_a_while()
1055 {
1056 	struct timespec delay = {0, 500000000};	/* 1/2 second */;
1057 
1058 	(void) pthread_mutex_lock(&(in_thr.thd_mutex));
1059 	in_thr.thd_waiting = 1;
1060 	(void) pthread_cond_reltimedwait_np(&(in_thr.thd_cv),
1061 	    &(in_thr.thd_mutex), &delay);
1062 	in_thr.thd_waiting = 0;
1063 	(void) pthread_mutex_unlock(&(in_thr.thd_mutex));
1064 }
1065 
1066 /*
1067  * adjust_priority() -- check queue and pools and adjust the priority
1068  * for process() accordingly.  If we're way ahead of output, do a
1069  * timed wait as well.
1070  */
1071 static void
adjust_priority()1072 adjust_priority()
1073 {
1074 	int		queue_near_full;
1075 	plugin_t	*p;
1076 	int		queue_size;
1077 	struct sched_param	param;
1078 
1079 	queue_near_full = 0;
1080 	(void) pthread_mutex_lock(&plugin_mutex);
1081 	p = plugin_head;
1082 	while (p != NULL) {
1083 		queue_size = audit_queue_size(&(p->plg_queue));
1084 		if (queue_size > p->plg_q_threshold) {
1085 			if (p->plg_priority != HIGH_PRIORITY) {
1086 				p->plg_priority =
1087 				    param.sched_priority =
1088 				    HIGH_PRIORITY;
1089 				(void) pthread_setschedparam(p->plg_tid,
1090 				    SCHED_OTHER, &param);
1091 			}
1092 			if (queue_size > p->plg_qmax - p->plg_qmin) {
1093 				queue_near_full = 1;
1094 				break;
1095 			}
1096 		}
1097 		p = p->plg_next;
1098 	}
1099 	(void) pthread_mutex_unlock(&plugin_mutex);
1100 
1101 	if (queue_near_full) {
1102 		DPRINT((dbfp,
1103 		    "adjust_priority:  input taking a short break\n"));
1104 		wait_a_while();
1105 		DPRINT((dbfp,
1106 		    "adjust_priority:  input back from my break\n"));
1107 	}
1108 }
1109 
1110 /*
1111  * input() is a door server; it blocks if any plugins have full queues
1112  * with the continue policy off. (auditconfig -setpolicy -cnt)
1113  *
1114  * input() is called synchronously from c2audit and is NOT
1115  * reentrant due to the (unprotected) static variables in
1116  * queue_buffer().  If multiple clients are created, a context
1117  * structure will be required for queue_buffer.
1118  *
1119  * timedwait is used when input() gets too far ahead of process();
1120  * the wait terminates either when the set time expires or when
1121  * process() signals that it has nearly caught up.
1122  */
1123 /* ARGSUSED */
1124 static void
input(void * cookie,void * argp,int arg_size,door_desc_t * dp,int n_descriptors)1125 input(void *cookie, void *argp, int arg_size, door_desc_t *dp,
1126     int n_descriptors)
1127 {
1128 	int		is_blocked;
1129 	plugin_t	*p;
1130 #if DEBUG
1131 	int		loop_count = 0;
1132 	static int	call_counter = 0;
1133 #endif
1134 	if (argp == NULL) {
1135 		warn_or_fatal(0,
1136 		    gettext("invalid data received from c2audit\n"));
1137 		goto input_exit;
1138 	}
1139 	DPRINT((dbfp, "%d input new buffer: length=%u, "
1140 	    "partial=%u, arg_size=%d\n",
1141 	    ++call_counter, ((au_dbuf_t *)argp)->aub_size,
1142 	    ((au_dbuf_t *)argp)->aub_type, arg_size));
1143 
1144 	if (((au_dbuf_t *)argp)->aub_size < 1) {
1145 		warn_or_fatal(0,
1146 		    gettext("invalid data length received from c2audit\n"));
1147 		goto input_exit;
1148 	}
1149 	/*
1150 	 * is_blocked is true only if one or more plugins have "no
1151 	 * continue" (-cnt) set and one of those has a full queue.
1152 	 * All plugins block until success is met.
1153 	 */
1154 	for (;;) {
1155 		DPRINT((dbfp, "%d input is calling queue_buffer\n",
1156 		    call_counter));
1157 
1158 		is_blocked = queue_buffer((au_dbuf_t *)argp);
1159 
1160 		if (!is_blocked) {
1161 			adjust_priority();
1162 			break;
1163 		} else {
1164 			DPRINT((dbfp,
1165 			    "%d input blocked (loop=%d)\n",
1166 			    call_counter, loop_count));
1167 
1168 			wait_a_while();
1169 
1170 			DPRINT((dbfp, "%d input unblocked (loop=%d)\n",
1171 			    call_counter, loop_count));
1172 		}
1173 #if DEBUG
1174 		loop_count++;
1175 #endif
1176 	}
1177 input_exit:
1178 	p = plugin_head;
1179 	while (p != NULL) {
1180 		(void) pthread_cond_signal(&(p->plg_cv));
1181 		p = p->plg_next;
1182 	}
1183 	((au_dbuf_t *)argp)->aub_size = 0;	/* return code */
1184 	(void) door_return(argp, sizeof (uint64_t), NULL, 0);
1185 }
1186 
1187 /*
1188  * process() -- pass a buffer to a plugin
1189  */
1190 static void *
process(void * arg)1191 process(void *arg)
1192 {
1193 	plugin_t *p		= arg;
1194 	int			rc;
1195 	audit_rec_t		*b_node;
1196 	audit_q_t		*q_node;
1197 	auditd_rc_t		plugrc;
1198 	char			*error_string;
1199 	struct timespec		delay;
1200 	int			sendsignal;
1201 	int			queue_len;
1202 	struct sched_param	param;
1203 	static boolean_t	once = B_FALSE;
1204 
1205 	DPRINT((dbfp, "%s is thread %d\n", p->plg_path, p->plg_tid));
1206 	p->plg_priority = param.sched_priority = BASE_PRIORITY;
1207 	(void) pthread_setschedparam(p->plg_tid, SCHED_OTHER, &param);
1208 
1209 	delay.tv_nsec = 0;
1210 
1211 	for (;;) {
1212 		while (audit_dequeue(&(p->plg_queue), (void *)&q_node) != 0) {
1213 			DUMP("process", p, p->plg_last_seq_out, "blocked");
1214 			(void) pthread_cond_signal(&(in_thr.thd_cv));
1215 
1216 			(void) pthread_mutex_lock(&(p->plg_mutex));
1217 			p->plg_waiting++;
1218 			(void) pthread_cond_wait(&(p->plg_cv),
1219 			    &(p->plg_mutex));
1220 			p->plg_waiting--;
1221 			(void) pthread_mutex_unlock(&(p->plg_mutex));
1222 
1223 			if (p->plg_removed)
1224 				goto plugin_removed;
1225 
1226 			DUMP("process", p, p->plg_last_seq_out, "unblocked");
1227 		}
1228 #if DEBUG
1229 		if (q_node->aqq_sequence != p->plg_last_seq_out + 1)
1230 			(void) fprintf(dbfp,
1231 			    "process(%d): buffer sequence=%llu but prev=%llu\n",
1232 			    p->plg_tid, q_node->aqq_sequence,
1233 			    p->plg_last_seq_out);
1234 #endif
1235 		error_string = NULL;
1236 
1237 		b_node = q_node->aqq_data;
1238 retry_mode:
1239 		plugrc = p->plg_fplugin(b_node->abq_buffer,
1240 		    b_node->abq_data_len, q_node->aqq_sequence, &error_string);
1241 
1242 		if (p->plg_removed)
1243 			goto plugin_removed;
1244 #if DEBUG
1245 		p->plg_last_seq_out = q_node->aqq_sequence;
1246 #endif
1247 		switch (plugrc) {
1248 		case AUDITD_RETRY:
1249 			if (!once) {
1250 				report_error(plugrc, error_string, p->plg_path);
1251 				once = B_TRUE;
1252 			}
1253 			free(error_string);
1254 			error_string = NULL;
1255 
1256 			DPRINT((dbfp, "process(%d) AUDITD_RETRY returned."
1257 			    " cnt=%d (if 1, enter retry)\n",
1258 			    p->plg_tid, p->plg_cnt));
1259 
1260 			if (p->plg_cnt)	/* if cnt is on, lose the buffer */
1261 				break;
1262 
1263 			delay.tv_sec = p->plg_retry_time;
1264 			(void) pthread_mutex_lock(&(p->plg_mutex));
1265 			p->plg_waiting++;
1266 			(void) pthread_cond_reltimedwait_np(&(p->plg_cv),
1267 			    &(p->plg_mutex), &delay);
1268 			p->plg_waiting--;
1269 			(void) pthread_mutex_unlock(&(p->plg_mutex));
1270 
1271 			DPRINT((dbfp, "left retry mode for %d\n", p->plg_tid));
1272 			goto retry_mode;
1273 
1274 		case AUDITD_SUCCESS:
1275 			p->plg_output++;
1276 			once = B_FALSE;
1277 			break;
1278 		default:
1279 			report_error(plugrc, error_string, p->plg_path);
1280 			free(error_string);
1281 			error_string = NULL;
1282 			break;
1283 		}	/* end switch */
1284 		bpool_return(b_node);
1285 		qpool_return(p, q_node);
1286 
1287 		sendsignal = 0;
1288 		queue_len = audit_queue_size(&(p->plg_queue));
1289 
1290 		(void) pthread_mutex_lock(&(in_thr.thd_mutex));
1291 		if (in_thr.thd_waiting && (queue_len > p->plg_qmin) &&
1292 		    (queue_len < p->plg_q_threshold))
1293 			sendsignal = 1;
1294 
1295 		(void) pthread_mutex_unlock(&(in_thr.thd_mutex));
1296 
1297 		if (sendsignal) {
1298 			(void) pthread_cond_signal(&(in_thr.thd_cv));
1299 			/*
1300 			 * sched_yield(); does not help
1301 			 * performance and in artificial tests
1302 			 * (high sustained volume) appears to
1303 			 * hurt by adding wide variability in
1304 			 * the results.
1305 			 */
1306 		} else if ((p->plg_priority < BASE_PRIORITY) &&
1307 		    (queue_len < p->plg_q_threshold)) {
1308 			p->plg_priority = param.sched_priority =
1309 			    BASE_PRIORITY;
1310 			(void) pthread_setschedparam(p->plg_tid, SCHED_OTHER,
1311 			    &param);
1312 		}
1313 	}	/* end for (;;) */
1314 plugin_removed:
1315 	DUMP("process", p, p->plg_last_seq_out, "exit");
1316 	error_string = NULL;
1317 	if ((rc = p->plg_fplugin_close(&error_string)) !=
1318 	    AUDITD_SUCCESS)
1319 		report_error(rc, error_string, p->plg_path);
1320 
1321 	free(error_string);
1322 
1323 	(void) pthread_mutex_lock(&plugin_mutex);
1324 	(void) unload_plugin(p);
1325 	(void) pthread_mutex_unlock(&plugin_mutex);
1326 	return (NULL);
1327 }
1328