8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / auditd / doorway.c
blob325e3ee06625c98097abf3d2a90fcacb0ecf582b
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
26 * Threads:
28 * auditd is thread 0 and does signal handling
30 * input() is a door server that receives binary audit records and
31 * queues them for handling by an instance of process() for conversion to syslog
32 * message(s). There is one process thread per plugin.
34 * Queues:
36 * Each plugin has a buffer pool and and queue for feeding the
37 * the process threads. The input thread moves buffers from the pool
38 * to the queue and the process thread puts them back.
40 * Another pool, b_pool, contains buffers referenced by each of the
41 * process queues; this is to minimize the number of buffer copies
45 #include <arpa/inet.h>
46 #include <assert.h>
47 #include <bsm/adt.h>
48 #include <dlfcn.h>
49 #include <errno.h>
50 #include <fcntl.h>
51 #include <libintl.h>
52 #include <pthread.h>
53 #include <secdb.h>
54 #include <security/auditd.h>
55 #include <signal.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include <syslog.h>
60 #include <sys/socket.h>
61 #include <sys/types.h>
62 #include <sys/stat.h>
63 #include <unistd.h>
64 #include <audit_plugin.h> /* libbsm */
65 #include "plugin.h"
66 #include <bsm/audit_door_infc.h>
67 #include "queue.h"
69 #define DEBUG 0
71 /* gettext() obfuscation routine for lint */
72 #ifdef __lint
73 #define gettext(x) x
74 #endif
76 #if DEBUG
77 static FILE *dbfp;
78 #define DUMP(w, x, y, z) dump_state(w, x, y, z)
79 #define DPRINT(x) { (void) fprintf x; }
80 #else
81 #define DUMP(w, x, y, z)
82 #define DPRINT(x)
83 #endif
85 #define FATAL_MESSAGE_LEN 256
87 #define MIN_RECORD_SIZE (size_t)25
89 #define INPUT_MIN 2
90 #define THRESHOLD_PCT 75
91 #define DEFAULT_BUF_SZ (size_t)250
92 #define BASE_PRIORITY 10 /* 0 - 20 valid for user, time share */
93 #define HIGH_PRIORITY BASE_PRIORITY - 1
95 static thr_data_t in_thr; /* input thread locks and data */
96 static int doorfd = -1;
98 static int largest_queue = INPUT_MIN;
99 static au_queue_t b_pool;
100 static int b_allocated = 0;
101 static pthread_mutex_t b_alloc_lock;
102 static pthread_mutex_t b_refcnt_lock;
104 static void input(void *, void *, int, door_desc_t *, int);
105 static void process(plugin_t *);
107 static audit_q_t *qpool_withdraw(plugin_t *);
108 static void qpool_init(plugin_t *, int);
109 static void qpool_return(plugin_t *, audit_q_t *);
110 static void qpool_close(plugin_t *);
112 static audit_rec_t *bpool_withdraw(char *, size_t, size_t);
113 static void bpool_init();
114 static void bpool_return(audit_rec_t *);
117 * warn_or_fatal() -- log daemon error and (optionally) exit
119 static void
120 warn_or_fatal(int fatal, char *parting_shot)
122 char *severity;
123 char message[512];
125 if (fatal)
126 severity = gettext("fatal error");
127 else
128 severity = gettext("warning");
130 (void) snprintf(message, 512, "%s: %s", severity, parting_shot);
132 __audit_syslog("auditd", LOG_PID | LOG_ODELAY | LOG_CONS,
133 LOG_DAEMON, LOG_ALERT, message);
135 DPRINT((dbfp, "auditd warn_or_fatal %s: %s\n", severity, parting_shot));
136 if (fatal)
137 auditd_exit(1);
140 /* Internal to doorway.c errors... */
141 #define INTERNAL_LOAD_ERROR -1
142 #define INTERNAL_SYS_ERROR -2
143 #define INTERNAL_CONFIG_ERROR -3
146 * report_error -- handle errors returned by plugin
148 * rc is plugin's return code if it is a non-negative value,
149 * otherwise it is a doorway.c code about a plugin.
151 static void
152 report_error(int rc, char *error_text, char *plugin_path)
154 int warn = 0;
155 char rcbuf[100]; /* short error name string */
156 char message[FATAL_MESSAGE_LEN];
157 int bad_count = 0;
158 char *name;
159 char empty[] = "..";
161 static int no_plug = 0;
162 static int no_load = 0;
163 static int no_thread;
164 static int no_memory = 0;
165 static int invalid = 0;
166 static int retry = 0;
167 static int fail = 0;
169 name = plugin_path;
170 if (error_text == NULL)
171 error_text = empty;
172 if (name == NULL)
173 name = empty;
175 switch (rc) {
176 case INTERNAL_LOAD_ERROR:
177 warn = 1;
178 bad_count = ++no_load;
179 (void) strcpy(rcbuf, "load_error");
180 break;
181 case INTERNAL_SYS_ERROR:
182 warn = 1;
183 bad_count = ++no_thread;
184 (void) strcpy(rcbuf, "sys_error");
185 break;
186 case INTERNAL_CONFIG_ERROR:
187 warn = 1;
188 bad_count = ++no_plug;
189 (void) strcpy(rcbuf, "config_error");
190 name = strdup("--");
191 break;
192 case AUDITD_SUCCESS:
193 break;
194 case AUDITD_NO_MEMORY: /* no_memory */
195 warn = 1;
196 bad_count = ++no_memory;
197 (void) strcpy(rcbuf, "no_memory");
198 break;
199 case AUDITD_INVALID: /* invalid */
200 warn = 1;
201 bad_count = ++invalid;
202 (void) strcpy(rcbuf, "invalid");
203 break;
204 case AUDITD_RETRY:
205 warn = 1;
206 bad_count = ++retry;
207 (void) strcpy(rcbuf, "retry");
208 break;
209 case AUDITD_COMM_FAIL: /* comm_fail */
210 (void) strcpy(rcbuf, "comm_fail");
211 break;
212 case AUDITD_FATAL: /* failure */
213 warn = 1;
214 bad_count = ++fail;
215 (void) strcpy(rcbuf, "failure");
216 break;
217 default:
218 (void) strcpy(rcbuf, "error");
219 break;
221 DPRINT((dbfp, "report_error(%d - %s): %s\n\t%s\n",
222 bad_count, name, rcbuf, error_text));
223 if (warn)
224 __audit_dowarn2("plugin", name, rcbuf, error_text, bad_count);
225 else {
226 (void) snprintf(message, FATAL_MESSAGE_LEN,
227 gettext("audit plugin %s reported error = \"%s\": %s\n"),
228 name, rcbuf, error_text);
229 warn_or_fatal(0, message);
233 static size_t
234 getlen(char *buf)
236 adr_t adr;
237 char tokenid;
238 uint32_t len;
240 adr.adr_now = buf;
241 adr.adr_stream = buf;
243 adrm_char(&adr, &tokenid, 1);
244 if ((tokenid == AUT_OHEADER) || (tokenid == AUT_HEADER32) ||
245 (tokenid == AUT_HEADER32_EX) || (tokenid == AUT_HEADER64) ||
246 (tokenid == AUT_HEADER64_EX)) {
247 adrm_u_int32(&adr, &len, 1);
249 return (len);
251 DPRINT((dbfp, "getlen() is not looking at a header token\n"));
253 return (0);
257 * load_function - call dlsym() to resolve the function address
259 static int
260 load_function(plugin_t *p, char *name, auditd_rc_t (**func)())
262 *func = (auditd_rc_t (*)())dlsym(p->plg_dlptr, name);
263 if (*func == NULL) {
264 char message[FATAL_MESSAGE_LEN];
265 char *errmsg = dlerror();
267 (void) snprintf(message, FATAL_MESSAGE_LEN,
268 gettext("dlsym failed %s: error %s"),
269 name, errmsg != NULL ? errmsg : gettext("Unknown error\n"));
271 warn_or_fatal(0, message);
272 return (-1);
274 return (0);
278 * load the auditd plug in
280 static int
281 load_plugin(plugin_t *p)
283 struct stat64 stat;
284 int fd;
285 int fail = 0;
288 * Stat the file so we can check modes and ownerships
290 if ((fd = open(p->plg_path, O_NONBLOCK | O_RDONLY)) != -1) {
291 if ((fstat64(fd, &stat) == -1) || (!S_ISREG(stat.st_mode)))
292 fail = 1;
293 } else
294 fail = 1;
295 if (fail) {
296 char message[FATAL_MESSAGE_LEN];
298 (void) snprintf(message, FATAL_MESSAGE_LEN,
299 gettext("auditd plugin: stat(%s) failed: %s\n"),
300 p->plg_path, strerror(errno));
302 warn_or_fatal(0, message);
303 return (-1);
306 * Check the ownership of the file
308 if (stat.st_uid != (uid_t)0) {
309 char message[FATAL_MESSAGE_LEN];
311 (void) snprintf(message, FATAL_MESSAGE_LEN,
312 gettext(
313 "auditd plugin: Owner of the module %s is not root\n"),
314 p->plg_path);
316 warn_or_fatal(0, message);
317 return (-1);
320 * Check the modes on the file
322 if (stat.st_mode&S_IWGRP) {
323 char message[FATAL_MESSAGE_LEN];
325 (void) snprintf(message, FATAL_MESSAGE_LEN,
326 gettext("auditd plugin: module %s writable by group\n"),
327 p->plg_path);
329 warn_or_fatal(0, message);
330 return (-1);
332 if (stat.st_mode&S_IWOTH) {
333 char message[FATAL_MESSAGE_LEN];
335 (void) snprintf(message, FATAL_MESSAGE_LEN,
336 gettext("auditd plugin: module %s writable by world\n"),
337 p->plg_path);
339 warn_or_fatal(0, message);
340 return (-1);
343 * Open the plugin
345 p->plg_dlptr = dlopen(p->plg_path, RTLD_LAZY);
347 if (p->plg_dlptr == NULL) {
348 char message[FATAL_MESSAGE_LEN];
349 char *errmsg = dlerror();
351 (void) snprintf(message, FATAL_MESSAGE_LEN,
352 gettext("plugin load %s failed: %s\n"),
353 p->plg_path, errmsg != NULL ? errmsg :
354 gettext("Unknown error\n"));
356 warn_or_fatal(0, message);
357 return (-1);
359 if (load_function(p, "auditd_plugin", &(p->plg_fplugin)))
360 return (-1);
362 if (load_function(p, "auditd_plugin_open", &(p->plg_fplugin_open)))
363 return (-1);
365 if (load_function(p, "auditd_plugin_close", &(p->plg_fplugin_close)))
366 return (-1);
368 return (0);
372 * unload_plugin() unlinks and frees the plugin_t structure after
373 * freeing buffers and structures that hang off it. It also dlcloses
374 * the referenced plugin. The return is the next entry, which may be NULL
376 * hold plugin_mutex for this call
378 static plugin_t *
379 unload_plugin(plugin_t *p)
381 plugin_t *q, **r;
383 assert(pthread_mutex_trylock(&plugin_mutex) != 0);
385 DPRINT((dbfp, "unload_plugin: removing %s\n", p->plg_path));
387 _kva_free(p->plg_kvlist); /* _kva_free accepts NULL */
388 qpool_close(p); /* qpool_close accepts NULL pool, queue */
389 DPRINT((dbfp, "unload_plugin: %s structure removed\n", p->plg_path));
391 (void) dlclose(p->plg_dlptr);
393 DPRINT((dbfp, "unload_plugin: %s dlclosed\n", p->plg_path));
394 free(p->plg_path);
396 (void) pthread_mutex_destroy(&(p->plg_mutex));
397 (void) pthread_cond_destroy(&(p->plg_cv));
399 q = plugin_head;
400 r = &plugin_head;
401 while (q != NULL) {
402 if (q == p) {
403 *r = p->plg_next;
404 free(p);
405 break;
407 r = &(q->plg_next);
408 q = q->plg_next;
410 return (*r);
414 * process return values from plugin_open
416 * presently no attribute is defined.
418 /* ARGSUSED */
419 static void
420 open_return(plugin_t *p, char *attrval)
425 * auditd_thread_init
426 * - create threads
427 * - load plugins
429 * auditd_thread_init is called at auditd startup with an initial list
430 * of plugins and again each time audit catches a SIGHUP or SIGUSR1.
433 auditd_thread_init()
435 int threshold;
436 auditd_rc_t rc;
437 plugin_t *p;
438 char *open_params;
439 char *error_string;
440 int plugin_count = 0;
441 static int threads_ready = 0;
443 if (!threads_ready) {
444 struct sched_param param;
445 #if DEBUG
446 dbfp = __auditd_debug_file_open();
447 #endif
448 doorfd = door_create((void(*)())input, 0,
449 DOOR_REFUSE_DESC | DOOR_NO_CANCEL);
450 if (doorfd < 0)
451 return (1); /* can't create door -> fatal */
453 param.sched_priority = BASE_PRIORITY;
454 (void) pthread_setschedparam(pthread_self(), SCHED_OTHER,
455 &param);
457 /* input door server */
458 (void) pthread_mutex_init(&(in_thr.thd_mutex), NULL);
459 (void) pthread_cond_init(&(in_thr.thd_cv), NULL);
460 in_thr.thd_waiting = 0;
462 bpool_init();
464 p = plugin_head;
465 while (p != NULL) {
466 if (p->plg_removed) {
467 DPRINT((dbfp, "start removing %s\n", p->plg_path));
468 /* tell process(p) to exit and dlclose */
469 (void) pthread_cond_signal(&(p->plg_cv));
470 } else if (!p->plg_initialized) {
471 DPRINT((dbfp, "start initial load of %s\n",
472 p->plg_path));
473 if (load_plugin(p)) {
474 report_error(INTERNAL_LOAD_ERROR,
475 gettext("dynamic load failed"),
476 p->plg_path);
477 p = unload_plugin(p);
478 continue;
480 open_params = NULL;
481 error_string = NULL;
482 if ((rc = p->plg_fplugin_open(
483 p->plg_kvlist,
484 &open_params, &error_string)) != AUDITD_SUCCESS) {
485 report_error(rc, error_string, p->plg_path);
486 free(error_string);
487 p = unload_plugin(p);
488 continue;
490 open_return(p, open_params);
491 p->plg_reopen = 0;
493 threshold = ((p->plg_qmax * THRESHOLD_PCT) + 99) / 100;
494 p->plg_qmin = INPUT_MIN;
496 DPRINT((dbfp,
497 "calling qpool_init for %s with qmax=%d\n",
498 p->plg_path, p->plg_qmax));
500 qpool_init(p, threshold);
501 audit_queue_init(&(p->plg_queue));
502 p->plg_initialized = 1;
504 (void) pthread_mutex_init(&(p->plg_mutex), NULL);
505 (void) pthread_cond_init(&(p->plg_cv), NULL);
506 p->plg_waiting = 0;
508 if (pthread_create(&(p->plg_tid), NULL,
509 (void *(*)(void *))process, p)) {
510 report_error(INTERNAL_SYS_ERROR,
511 gettext("thread creation failed"),
512 p->plg_path);
513 p = unload_plugin(p);
514 continue;
516 } else if (p->plg_reopen) {
517 DPRINT((dbfp, "reopen %s\n", p->plg_path));
518 error_string = NULL;
519 if ((rc = p->plg_fplugin_open(p->plg_kvlist,
520 &open_params, &error_string)) != AUDITD_SUCCESS) {
521 report_error(rc, error_string, p->plg_path);
522 free(error_string);
523 p = unload_plugin(p);
524 continue;
526 open_return(p, open_params);
527 p->plg_reopen = 0;
529 DPRINT((dbfp, "%s qmax=%d\n",
530 p->plg_path, p->plg_qmax));
533 p->plg_q_threshold = ((p->plg_qmax * THRESHOLD_PCT) + 99) / 100;
535 p = p->plg_next;
536 plugin_count++;
538 if (plugin_count == 0) {
539 report_error(INTERNAL_CONFIG_ERROR,
540 gettext("No plugins are configured"), NULL);
541 return (-1);
543 if (!threads_ready) {
544 /* unleash the kernel */
545 rc = auditdoor(doorfd);
547 DPRINT((dbfp, "%d returned from auditdoor.\n",
548 rc));
549 if (rc != 0)
550 return (1); /* fatal */
552 threads_ready = 1;
554 return (0);
558 * Door invocations that are in progress during a
559 * door_revoke() invocation are allowed to complete normally.
560 * -- man page for door_revoke()
562 void
563 auditd_thread_close()
565 if (doorfd == -1)
566 return;
567 (void) door_revoke(doorfd);
568 doorfd = -1;
572 * qpool_init() sets up pool for queue entries (audit_q_t)
575 static void
576 qpool_init(plugin_t *p, int threshold)
578 int i;
579 audit_q_t *node;
581 audit_queue_init(&(p->plg_pool));
583 DPRINT((dbfp, "qpool_init(%d) max, min, threshhold = %d, %d, %d\n",
584 p->plg_tid, p->plg_qmax, p->plg_qmin, threshold));
586 if (p->plg_qmax > largest_queue)
587 largest_queue = p->plg_qmax;
589 p->plg_q_threshold = threshold;
591 for (i = 0; i < p->plg_qmin; i++) {
592 node = malloc(sizeof (audit_q_t));
593 if (node == NULL)
594 warn_or_fatal(1, gettext("no memory\n"));
595 /* doesn't return */
597 audit_enqueue(&p->plg_pool, node);
602 * bpool_init() sets up pool and queue for record entries (audit_rec_t)
605 static void
606 bpool_init()
608 int i;
609 audit_rec_t *node;
611 audit_queue_init(&b_pool);
612 (void) pthread_mutex_init(&b_alloc_lock, NULL);
613 (void) pthread_mutex_init(&b_refcnt_lock, NULL);
615 for (i = 0; i < INPUT_MIN; i++) {
616 node = malloc(AUDIT_REC_HEADER + DEFAULT_BUF_SZ);
617 if (node == NULL)
618 warn_or_fatal(1, gettext("no memory\n"));
619 /* doesn't return */
621 node->abq_buf_len = DEFAULT_BUF_SZ;
623 node->abq_data_len = 0;
624 audit_enqueue(&b_pool, node);
625 (void) pthread_mutex_lock(&b_alloc_lock);
626 b_allocated++;
627 (void) pthread_mutex_unlock(&b_alloc_lock);
632 * qpool_close() discard queue and pool for a discontinued plugin
634 * there is no corresponding bpool_close() since it would only
635 * be called as auditd is going down.
637 static void
638 qpool_close(plugin_t *p)
640 audit_q_t *q_node;
641 audit_rec_t *b_node;
643 if (!p->plg_initialized)
644 return;
646 while (audit_dequeue(&(p->plg_pool), (void *)&q_node) == 0) {
647 free(q_node);
649 audit_queue_destroy(&(p->plg_pool));
651 while (audit_dequeue(&(p->plg_queue), (void *)&q_node) == 0) {
652 b_node = audit_release(&b_refcnt_lock, q_node->aqq_data);
653 if (b_node != NULL)
654 audit_enqueue(&b_pool, b_node);
655 free(q_node);
657 audit_queue_destroy(&(p->plg_queue));
661 * qpool_withdraw
663 static audit_q_t *
664 qpool_withdraw(plugin_t *p)
666 audit_q_t *node;
667 int rc;
669 /* get a buffer from the pool, if any */
670 rc = audit_dequeue(&(p->plg_pool), (void *)&node);
671 if (rc == 0)
672 return (node);
675 * the pool is empty: allocate a new element
677 node = malloc(sizeof (audit_q_t));
679 if (node == NULL)
680 warn_or_fatal(1, gettext("no memory\n"));
681 /* doesn't return */
683 return (node);
687 * bpool_withdraw -- gets a buffer and fills it
690 static audit_rec_t *
691 bpool_withdraw(char *buffer, size_t buff_size, size_t request_size)
693 audit_rec_t *node;
694 int rc;
695 size_t new_length;
697 new_length = (request_size > DEFAULT_BUF_SZ) ?
698 request_size : DEFAULT_BUF_SZ;
700 /* get a buffer from the pool, if any */
701 rc = audit_dequeue(&b_pool, (void *)&node);
703 DPRINT((dbfp, "bpool_withdraw buf length=%d,"
704 " requested size=%d, dequeue rc=%d\n",
705 new_length, request_size, rc));
707 if (rc == 0) {
708 DPRINT((dbfp, "bpool_withdraw node=%p (pool=%d)\n",
709 (void *)node, audit_queue_size(&b_pool)));
711 if (new_length > node->abq_buf_len) {
712 node = realloc(node, AUDIT_REC_HEADER + new_length);
713 if (node == NULL)
714 warn_or_fatal(1, gettext("no memory\n"));
715 /* no return */
717 } else {
719 * the pool is empty: allocate a new element
721 (void) pthread_mutex_lock(&b_alloc_lock);
722 if (b_allocated >= largest_queue) {
723 (void) pthread_mutex_unlock(&b_alloc_lock);
724 DPRINT((dbfp, "bpool_withdraw is over max (pool=%d)\n",
725 audit_queue_size(&b_pool)));
726 return (NULL);
728 (void) pthread_mutex_unlock(&b_alloc_lock);
730 node = malloc(AUDIT_REC_HEADER + new_length);
732 if (node == NULL)
733 warn_or_fatal(1, gettext("no memory\n"));
734 /* no return */
736 (void) pthread_mutex_lock(&b_alloc_lock);
737 b_allocated++;
738 (void) pthread_mutex_unlock(&b_alloc_lock);
739 DPRINT((dbfp, "bpool_withdraw node=%p (alloc=%d, pool=%d)\n",
740 (void *)node, b_allocated, audit_queue_size(&b_pool)));
742 assert(request_size <= new_length);
744 (void) memcpy(node->abq_buffer, buffer, buff_size);
745 node->abq_data_len = buff_size;
746 node->abq_buf_len = new_length;
747 node->abq_ref_count = 0;
749 return (node);
753 * qpool_return() moves queue nodes back to the pool queue.
755 * if the pool is over max, the node is discarded instead.
757 static void
758 qpool_return(plugin_t *p, audit_q_t *node)
760 int qpool_size;
761 int q_size;
763 #if DEBUG
764 uint64_t sequence = node->aqq_sequence;
765 #endif
766 qpool_size = audit_queue_size(&(p->plg_pool));
767 q_size = audit_queue_size(&(p->plg_queue));
769 if (qpool_size + q_size > p->plg_qmax)
770 free(node);
771 else
772 audit_enqueue(&(p->plg_pool), node);
774 DPRINT((dbfp,
775 "qpool_return(%d): seq=%llu, q size=%d,"
776 " pool size=%d (total alloc=%d), threshhold=%d\n",
777 p->plg_tid, sequence, q_size, qpool_size,
778 q_size + qpool_size, p->plg_q_threshold));
782 * bpool_return() moves queue nodes back to the pool queue.
784 static void
785 bpool_return(audit_rec_t *node)
787 #if DEBUG
788 audit_rec_t *copy = node;
789 #endif
790 node = audit_release(&b_refcnt_lock, node); /* decrement ref cnt */
792 if (node != NULL) { /* NULL if ref cnt is not zero */
793 audit_enqueue(&b_pool, node);
794 DPRINT((dbfp,
795 "bpool_return: requeue %p (allocated=%d,"
796 " pool size=%d)\n", (void *)node, b_allocated,
797 audit_queue_size(&b_pool)));
799 #if DEBUG
800 else {
801 DPRINT((dbfp,
802 "bpool_return: decrement count for %p (allocated=%d,"
803 " pool size=%d)\n", (void *)copy, b_allocated,
804 audit_queue_size(&b_pool)));
806 #endif
809 #if DEBUG
810 static void
811 dump_state(char *src, plugin_t *p, uint64_t count, char *msg)
813 struct sched_param param;
814 int policy;
816 * count is message sequence
818 (void) pthread_getschedparam(p->plg_tid, &policy, &param);
819 (void) fprintf(dbfp, "%7s(%d/%llu) %11s:"
820 " input_in_wait=%d"
821 " priority=%d"
822 " queue size=%d pool size=%d"
823 "\n\t"
824 "process wait=%d"
825 " tossed=%d"
826 " queued=%d"
827 " written=%d"
828 "\n",
829 src, p->plg_tid, count, msg,
830 in_thr.thd_waiting, param.sched_priority,
831 audit_queue_size(&(p->plg_queue)),
832 audit_queue_size(&(p->plg_pool)),
833 p->plg_waiting, p->plg_tossed,
834 p->plg_queued, p->plg_output);
836 (void) fflush(dbfp);
838 #endif
841 * policy_is_block: return 1 if the continue policy is off for any active
842 * plugin, else 0
844 static int
845 policy_is_block()
847 plugin_t *p;
849 (void) pthread_mutex_lock(&plugin_mutex);
850 p = plugin_head;
852 while (p != NULL) {
853 if (p->plg_cnt == 0) {
854 (void) pthread_mutex_unlock(&plugin_mutex);
855 DPRINT((dbfp,
856 "policy_is_block: policy is to block\n"));
857 return (1);
859 p = p->plg_next;
861 (void) pthread_mutex_unlock(&plugin_mutex);
862 DPRINT((dbfp, "policy_is_block: policy is to continue\n"));
863 return (0);
867 * policy_update() -- the kernel has received a policy change.
868 * Presently, the only policy auditd cares about is AUDIT_CNT
870 static void
871 policy_update(uint32_t newpolicy)
873 plugin_t *p;
875 DPRINT((dbfp, "policy change: %X\n", newpolicy));
876 (void) pthread_mutex_lock(&plugin_mutex);
877 p = plugin_head;
878 while (p != NULL) {
879 p->plg_cnt = (newpolicy & AUDIT_CNT) ? 1 : 0;
880 (void) pthread_cond_signal(&(p->plg_cv));
882 DPRINT((dbfp, "policy changed for thread %d\n", p->plg_tid));
883 p = p->plg_next;
885 (void) pthread_mutex_unlock(&plugin_mutex);
889 * queue_buffer() inputs a buffer and queues for each active plugin if
890 * it represents a complete audit record. Otherwise it builds a
891 * larger buffer to hold the record and take successive buffers from
892 * c2audit to build a complete record; then queues it for each plugin.
894 * return 0 if data is queued (or damaged and tossed). If resources
895 * are not available, return 0 if all active plugins have the cnt
896 * policy set, else 1. 0 is also returned if the input is a control
897 * message. (aub_buf is aligned on a 64 bit boundary, so casting
898 * it to an integer works just fine.)
900 static int
901 queue_buffer(au_dbuf_t *kl)
903 plugin_t *p;
904 audit_rec_t *b_copy;
905 audit_q_t *q_copy;
906 boolean_t referenced = 0;
907 static char *invalid_msg = "invalid audit record discarded";
908 static char *invalid_control = "invalid audit control discarded";
910 static audit_rec_t *alt_b_copy = NULL;
911 static size_t alt_length;
912 static size_t alt_offset;
915 * the buffer may be a kernel -> auditd message. (only
916 * the policy change message exists so far.)
919 if ((kl->aub_type & AU_DBUF_NOTIFY) != 0) {
920 uint32_t control;
922 control = kl->aub_type & ~AU_DBUF_NOTIFY;
923 switch (control) {
924 case AU_DBUF_POLICY:
925 /* LINTED */
926 policy_update(*(uint32_t *)kl->aub_buf);
927 break;
928 case AU_DBUF_SHUTDOWN:
929 (void) kill(getpid(), SIGTERM);
930 DPRINT((dbfp, "AU_DBUF_SHUTDOWN message\n"));
931 break;
932 default:
933 warn_or_fatal(0, gettext(invalid_control));
934 break;
936 return (0);
939 * The test for valid continuation/completion may fail. Need to
940 * assume the failure was earlier and that this buffer may
941 * be a valid first or complete buffer after discarding the
942 * incomplete record
945 if (alt_b_copy != NULL) {
946 if ((kl->aub_type == AU_DBUF_FIRST) ||
947 (kl->aub_type == AU_DBUF_COMPLETE)) {
948 DPRINT((dbfp, "copy is not null, partial is %d\n",
949 kl->aub_type));
950 bpool_return(alt_b_copy);
951 warn_or_fatal(0, gettext(invalid_msg));
952 alt_b_copy = NULL;
955 if (alt_b_copy != NULL) { /* continue collecting a long record */
956 if (kl->aub_size + alt_offset > alt_length) {
957 bpool_return(alt_b_copy);
958 alt_b_copy = NULL;
959 warn_or_fatal(0, gettext(invalid_msg));
960 return (0);
962 (void) memcpy(alt_b_copy->abq_buffer + alt_offset, kl->aub_buf,
963 kl->aub_size);
964 alt_offset += kl->aub_size;
965 if (kl->aub_type == AU_DBUF_MIDDLE)
966 return (0);
967 b_copy = alt_b_copy;
968 alt_b_copy = NULL;
969 b_copy->abq_data_len = alt_length;
970 } else if (kl->aub_type == AU_DBUF_FIRST) {
971 /* first buffer of a multiple buffer record */
972 alt_length = getlen(kl->aub_buf);
973 if ((alt_length < MIN_RECORD_SIZE) ||
974 (alt_length <= kl->aub_size)) {
975 warn_or_fatal(0, gettext(invalid_msg));
976 return (0);
978 alt_b_copy = bpool_withdraw(kl->aub_buf, kl->aub_size,
979 alt_length);
981 if (alt_b_copy == NULL)
982 return (policy_is_block());
984 alt_offset = kl->aub_size;
985 return (0);
986 } else { /* one buffer, one record -- the basic case */
987 if (kl->aub_type != AU_DBUF_COMPLETE) {
988 DPRINT((dbfp, "copy is null, partial is %d\n",
989 kl->aub_type));
990 warn_or_fatal(0, gettext(invalid_msg));
991 return (0); /* tossed */
993 b_copy = bpool_withdraw(kl->aub_buf, kl->aub_size,
994 kl->aub_size);
996 if (b_copy == NULL)
997 return (policy_is_block());
1000 (void) pthread_mutex_lock(&plugin_mutex);
1001 p = plugin_head;
1002 while (p != NULL) {
1003 if (!p->plg_removed) {
1005 * Link the record buffer to the input queues.
1006 * To avoid a race, it is necessary to wait
1007 * until all reference count increments
1008 * are complete before queueing q_copy.
1010 audit_incr_ref(&b_refcnt_lock, b_copy);
1012 q_copy = qpool_withdraw(p);
1013 q_copy->aqq_sequence = p->plg_sequence++;
1014 q_copy->aqq_data = b_copy;
1016 p->plg_save_q_copy = q_copy; /* enqueue below */
1017 referenced = 1;
1018 } else
1019 p->plg_save_q_copy = NULL;
1020 p = p->plg_next;
1023 * now that the reference count is updated, queue it.
1025 if (referenced) {
1026 p = plugin_head;
1027 while ((p != NULL) && (p->plg_save_q_copy != NULL)) {
1028 audit_enqueue(&(p->plg_queue), p->plg_save_q_copy);
1029 (void) pthread_cond_signal(&(p->plg_cv));
1030 p->plg_queued++;
1031 p = p->plg_next;
1033 } else
1034 bpool_return(b_copy);
1036 (void) pthread_mutex_unlock(&plugin_mutex);
1038 return (0);
1042 * wait_a_while() -- timed wait in the door server to allow output
1043 * time to catch up.
1045 static void
1046 wait_a_while()
1048 struct timespec delay = {0, 500000000}; /* 1/2 second */;
1050 (void) pthread_mutex_lock(&(in_thr.thd_mutex));
1051 in_thr.thd_waiting = 1;
1052 (void) pthread_cond_reltimedwait_np(&(in_thr.thd_cv),
1053 &(in_thr.thd_mutex), &delay);
1054 in_thr.thd_waiting = 0;
1055 (void) pthread_mutex_unlock(&(in_thr.thd_mutex));
1059 * adjust_priority() -- check queue and pools and adjust the priority
1060 * for process() accordingly. If we're way ahead of output, do a
1061 * timed wait as well.
1063 static void
1064 adjust_priority()
1066 int queue_near_full;
1067 plugin_t *p;
1068 int queue_size;
1069 struct sched_param param;
1071 queue_near_full = 0;
1072 (void) pthread_mutex_lock(&plugin_mutex);
1073 p = plugin_head;
1074 while (p != NULL) {
1075 queue_size = audit_queue_size(&(p->plg_queue));
1076 if (queue_size > p->plg_q_threshold) {
1077 if (p->plg_priority != HIGH_PRIORITY) {
1078 p->plg_priority =
1079 param.sched_priority =
1080 HIGH_PRIORITY;
1081 (void) pthread_setschedparam(p->plg_tid,
1082 SCHED_OTHER, &param);
1084 if (queue_size > p->plg_qmax - p->plg_qmin) {
1085 queue_near_full = 1;
1086 break;
1089 p = p->plg_next;
1091 (void) pthread_mutex_unlock(&plugin_mutex);
1093 if (queue_near_full) {
1094 DPRINT((dbfp,
1095 "adjust_priority: input taking a short break\n"));
1096 wait_a_while();
1097 DPRINT((dbfp,
1098 "adjust_priority: input back from my break\n"));
1103 * input() is a door server; it blocks if any plugins have full queues
1104 * with the continue policy off. (auditconfig -setpolicy -cnt)
1106 * input() is called synchronously from c2audit and is NOT
1107 * reentrant due to the (unprotected) static variables in
1108 * queue_buffer(). If multiple clients are created, a context
1109 * structure will be required for queue_buffer.
1111 * timedwait is used when input() gets too far ahead of process();
1112 * the wait terminates either when the set time expires or when
1113 * process() signals that it has nearly caught up.
1115 /* ARGSUSED */
1116 static void
1117 input(void *cookie, void *argp, int arg_size, door_desc_t *dp,
1118 int n_descriptors)
1120 int is_blocked;
1121 plugin_t *p;
1122 #if DEBUG
1123 int loop_count = 0;
1124 static int call_counter = 0;
1125 #endif
1126 if (argp == NULL) {
1127 warn_or_fatal(0,
1128 gettext("invalid data received from c2audit\n"));
1129 goto input_exit;
1131 DPRINT((dbfp, "%d input new buffer: length=%u, "
1132 "partial=%u, arg_size=%d\n",
1133 ++call_counter, ((au_dbuf_t *)argp)->aub_size,
1134 ((au_dbuf_t *)argp)->aub_type, arg_size));
1136 if (((au_dbuf_t *)argp)->aub_size < 1) {
1137 warn_or_fatal(0,
1138 gettext("invalid data length received from c2audit\n"));
1139 goto input_exit;
1142 * is_blocked is true only if one or more plugins have "no
1143 * continue" (-cnt) set and one of those has a full queue.
1144 * All plugins block until success is met.
1146 for (;;) {
1147 DPRINT((dbfp, "%d input is calling queue_buffer\n",
1148 call_counter));
1150 is_blocked = queue_buffer((au_dbuf_t *)argp);
1152 if (!is_blocked) {
1153 adjust_priority();
1154 break;
1155 } else {
1156 DPRINT((dbfp,
1157 "%d input blocked (loop=%d)\n",
1158 call_counter, loop_count));
1160 wait_a_while();
1162 DPRINT((dbfp, "%d input unblocked (loop=%d)\n",
1163 call_counter, loop_count));
1165 #if DEBUG
1166 loop_count++;
1167 #endif
1169 input_exit:
1170 p = plugin_head;
1171 while (p != NULL) {
1172 (void) pthread_cond_signal(&(p->plg_cv));
1173 p = p->plg_next;
1175 ((au_dbuf_t *)argp)->aub_size = 0; /* return code */
1176 (void) door_return(argp, sizeof (uint64_t), NULL, 0);
1180 * process() -- pass a buffer to a plugin
1182 static void
1183 process(plugin_t *p)
1185 int rc;
1186 audit_rec_t *b_node;
1187 audit_q_t *q_node;
1188 auditd_rc_t plugrc;
1189 char *error_string;
1190 struct timespec delay;
1191 int sendsignal;
1192 int queue_len;
1193 struct sched_param param;
1194 static boolean_t once = B_FALSE;
1196 DPRINT((dbfp, "%s is thread %d\n", p->plg_path, p->plg_tid));
1197 p->plg_priority = param.sched_priority = BASE_PRIORITY;
1198 (void) pthread_setschedparam(p->plg_tid, SCHED_OTHER, &param);
1200 delay.tv_nsec = 0;
1202 for (;;) {
1203 while (audit_dequeue(&(p->plg_queue), (void *)&q_node) != 0) {
1204 DUMP("process", p, p->plg_last_seq_out, "blocked");
1205 (void) pthread_cond_signal(&(in_thr.thd_cv));
1207 (void) pthread_mutex_lock(&(p->plg_mutex));
1208 p->plg_waiting++;
1209 (void) pthread_cond_wait(&(p->plg_cv),
1210 &(p->plg_mutex));
1211 p->plg_waiting--;
1212 (void) pthread_mutex_unlock(&(p->plg_mutex));
1214 if (p->plg_removed)
1215 goto plugin_removed;
1217 DUMP("process", p, p->plg_last_seq_out, "unblocked");
1219 #if DEBUG
1220 if (q_node->aqq_sequence != p->plg_last_seq_out + 1)
1221 (void) fprintf(dbfp,
1222 "process(%d): buffer sequence=%llu but prev=%llu\n",
1223 p->plg_tid, q_node->aqq_sequence,
1224 p->plg_last_seq_out);
1225 #endif
1226 error_string = NULL;
1228 b_node = q_node->aqq_data;
1229 retry_mode:
1230 plugrc = p->plg_fplugin(b_node->abq_buffer,
1231 b_node->abq_data_len, q_node->aqq_sequence, &error_string);
1233 if (p->plg_removed)
1234 goto plugin_removed;
1235 #if DEBUG
1236 p->plg_last_seq_out = q_node->aqq_sequence;
1237 #endif
1238 switch (plugrc) {
1239 case AUDITD_RETRY:
1240 if (!once) {
1241 report_error(plugrc, error_string, p->plg_path);
1242 once = B_TRUE;
1244 free(error_string);
1245 error_string = NULL;
1247 DPRINT((dbfp, "process(%d) AUDITD_RETRY returned."
1248 " cnt=%d (if 1, enter retry)\n",
1249 p->plg_tid, p->plg_cnt));
1251 if (p->plg_cnt) /* if cnt is on, lose the buffer */
1252 break;
1254 delay.tv_sec = p->plg_retry_time;
1255 (void) pthread_mutex_lock(&(p->plg_mutex));
1256 p->plg_waiting++;
1257 (void) pthread_cond_reltimedwait_np(&(p->plg_cv),
1258 &(p->plg_mutex), &delay);
1259 p->plg_waiting--;
1260 (void) pthread_mutex_unlock(&(p->plg_mutex));
1262 DPRINT((dbfp, "left retry mode for %d\n", p->plg_tid));
1263 goto retry_mode;
1265 case AUDITD_SUCCESS:
1266 p->plg_output++;
1267 once = B_FALSE;
1268 break;
1269 default:
1270 report_error(plugrc, error_string, p->plg_path);
1271 free(error_string);
1272 error_string = NULL;
1273 break;
1274 } /* end switch */
1275 bpool_return(b_node);
1276 qpool_return(p, q_node);
1278 sendsignal = 0;
1279 queue_len = audit_queue_size(&(p->plg_queue));
1281 (void) pthread_mutex_lock(&(in_thr.thd_mutex));
1282 if (in_thr.thd_waiting && (queue_len > p->plg_qmin) &&
1283 (queue_len < p->plg_q_threshold))
1284 sendsignal = 1;
1286 (void) pthread_mutex_unlock(&(in_thr.thd_mutex));
1288 if (sendsignal) {
1289 (void) pthread_cond_signal(&(in_thr.thd_cv));
1291 * sched_yield(); does not help
1292 * performance and in artificial tests
1293 * (high sustained volume) appears to
1294 * hurt by adding wide variability in
1295 * the results.
1297 } else if ((p->plg_priority < BASE_PRIORITY) &&
1298 (queue_len < p->plg_q_threshold)) {
1299 p->plg_priority = param.sched_priority =
1300 BASE_PRIORITY;
1301 (void) pthread_setschedparam(p->plg_tid, SCHED_OTHER,
1302 &param);
1304 } /* end for (;;) */
1305 plugin_removed:
1306 DUMP("process", p, p->plg_last_seq_out, "exit");
1307 error_string = NULL;
1308 if ((rc = p->plg_fplugin_close(&error_string)) !=
1309 AUDITD_SUCCESS)
1310 report_error(rc, error_string, p->plg_path);
1312 free(error_string);
1314 (void) pthread_mutex_lock(&plugin_mutex);
1315 (void) unload_plugin(p);
1316 (void) pthread_mutex_unlock(&plugin_mutex);