1/*-
2 * Copyright (c) 2005 Wayne J. Salamon
3 * All rights reserved.
4 *
5 * This software was developed by Wayne Salamon for the TrustedBSD Project.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <sys/cdefs.h>
30__FBSDID("$FreeBSD$");
31
32#include <sys/param.h>
33#include <sys/conf.h>
34#include <sys/kernel.h>
35#include <sys/malloc.h>
36#include <sys/proc.h>
37#include <sys/queue.h>
38#include <sys/systm.h>
39#include <sys/uio.h>
40
41#include <security/audit/audit.h>
42#include <security/audit/audit_private.h>
43
44/*
45 * Structures and operations to support the basic character special device
46 * used to communicate with userland.  /dev/audit reliably delivers one-byte
47 * messages to a listening application (or discards them if there is no
48 * listening application).
49 *
50 * Currently, select/poll are not supported on the trigger device.
51 */
52struct trigger_info {
53	unsigned int			trigger;
54	TAILQ_ENTRY(trigger_info)	list;
55};
56
57static MALLOC_DEFINE(M_AUDITTRIGGER, "audit_trigger", "Audit trigger events");
58static struct cdev *audit_dev;
59static int audit_isopen = 0;
60static TAILQ_HEAD(, trigger_info) trigger_list;
61static struct mtx audit_trigger_mtx;
62
63static int
64audit_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
65{
66	int error;
67
68	/* Only one process may open the device at a time. */
69	mtx_lock(&audit_trigger_mtx);
70	if (!audit_isopen) {
71		error = 0;
72		audit_isopen = 1;
73	} else
74		error = EBUSY;
75	mtx_unlock(&audit_trigger_mtx);
76
77	return (error);
78}
79
80static int
81audit_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
82{
83	struct trigger_info *ti;
84
85	/* Flush the queue of pending trigger events. */
86	mtx_lock(&audit_trigger_mtx);
87	audit_isopen = 0;
88	while (!TAILQ_EMPTY(&trigger_list)) {
89		ti = TAILQ_FIRST(&trigger_list);
90		TAILQ_REMOVE(&trigger_list, ti, list);
91		free(ti, M_AUDITTRIGGER);
92	}
93	mtx_unlock(&audit_trigger_mtx);
94
95	return (0);
96}
97
98static int
99audit_read(struct cdev *dev, struct uio *uio, int ioflag)
100{
101	int error = 0;
102	struct trigger_info *ti = NULL;
103
104	mtx_lock(&audit_trigger_mtx);
105	while (TAILQ_EMPTY(&trigger_list)) {
106		error = msleep(&trigger_list, &audit_trigger_mtx,
107		    PSOCK | PCATCH, "auditd", 0);
108		if (error)
109			break;
110	}
111	if (!error) {
112		ti = TAILQ_FIRST(&trigger_list);
113		TAILQ_REMOVE(&trigger_list, ti, list);
114	}
115	mtx_unlock(&audit_trigger_mtx);
116	if (!error) {
117		error = uiomove(&ti->trigger, sizeof(ti->trigger), uio);
118		free(ti, M_AUDITTRIGGER);
119	}
120	return (error);
121}
122
123static int
124audit_write(struct cdev *dev, struct uio *uio, int ioflag)
125{
126
127	/* Communication is kernel->userspace only. */
128	return (EOPNOTSUPP);
129}
130
131int
132audit_send_trigger(unsigned int trigger)
133{
134	struct trigger_info *ti;
135
136	ti = malloc(sizeof *ti, M_AUDITTRIGGER, M_WAITOK);
137	mtx_lock(&audit_trigger_mtx);
138	if (!audit_isopen) {
139		/* If nobody's listening, we ain't talking. */
140		mtx_unlock(&audit_trigger_mtx);
141		free(ti, M_AUDITTRIGGER);
142		return (ENODEV);
143	}
144	ti->trigger = trigger;
145	TAILQ_INSERT_TAIL(&trigger_list, ti, list);
146	wakeup(&trigger_list);
147	mtx_unlock(&audit_trigger_mtx);
148	return (0);
149}
150
151static struct cdevsw audit_cdevsw = {
152	.d_version =	D_VERSION,
153	.d_open =	audit_open,
154	.d_close =	audit_close,
155	.d_read =	audit_read,
156	.d_write =	audit_write,
157	.d_name =	"audit"
158};
159
160void
161audit_trigger_init(void)
162{
163
164	TAILQ_INIT(&trigger_list);
165	mtx_init(&audit_trigger_mtx, "audit_trigger_mtx", NULL, MTX_DEF);
166}
167
168static void
169audit_trigger_cdev_init(void *unused)
170{
171
172	/* Create the special device file. */
173	audit_dev = make_dev(&audit_cdevsw, 0, UID_ROOT, GID_KMEM, 0600,
174	    AUDITDEV_FILENAME);
175}
176
177SYSINIT(audit_trigger_cdev_init, SI_SUB_DRIVERS, SI_ORDER_MIDDLE,
178    audit_trigger_cdev_init, NULL);
179