xref: /illumos-gate/usr/src/uts/common/io/vscan/vscan_drv.c (revision a194faf8907a6722dcf10ad16c6ca72c9b7bd0ba)
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 /*
23  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/stat.h>
30 #include <sys/ddi.h>
31 #include <sys/sunddi.h>
32 #include <sys/time.h>
33 #include <sys/varargs.h>
34 #include <sys/conf.h>
35 #include <sys/modctl.h>
36 #include <sys/vnode.h>
37 #include <fs/fs_subr.h>
38 #include <sys/types.h>
39 #include <sys/file.h>
40 #include <sys/disp.h>
41 #include <sys/vscan.h>
42 #include <sys/policy.h>
43 #include <sys/sdt.h>
44 
45 #define	VS_DRV_NODENAME_LEN	16
46 
47 
48 /*
49  * Instance States: VS_INIT (initial state), VS_OPEN, VS_READING
50  *
51  * Instance 0 controls the state of the driver: vscan_drv_connected.
52  *   vscan_drv_state[0] should NOT be used.
53  * Actions:
54  * open:	VS_INIT->VS_OPEN, otherwise ERROR
55  * close:	any->VS_INIT
56  * read:	VS_OPEN->VS_READING, otherwise ERROR
57  */
58 typedef enum {
59 	VS_INIT,
60 	VS_OPEN,
61 	VS_READING
62 } vscan_drv_state_t;
63 
64 static vscan_drv_state_t vscan_drv_state[VS_DRV_MAX_FILES + 1];
65 static boolean_t vscan_drv_connected = B_FALSE; /* vscand daemon connected */
66 
67 static dev_info_t *vscan_drv_dip;
68 static kmutex_t vscan_drv_mutex;
69 
70 /*
71  * DDI entry points.
72  */
73 static int vscan_drv_attach(dev_info_t *, ddi_attach_cmd_t);
74 static int vscan_drv_detach(dev_info_t *, ddi_detach_cmd_t);
75 static int vscan_drv_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
76 static int vscan_drv_open(dev_t *, int, int, cred_t *);
77 static int vscan_drv_close(dev_t, int, int, cred_t *);
78 static int vscan_drv_read(dev_t, struct uio *, cred_t *);
79 static int vscan_drv_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
80 
81 static boolean_t vscan_drv_in_use();
82 
83 
84 /*
85  * module linkage info for the kernel
86  */
87 
88 static struct cb_ops cbops = {
89 	vscan_drv_open,		/* cb_open */
90 	vscan_drv_close,	/* cb_close */
91 	nodev,			/* cb_strategy */
92 	nodev,			/* cb_print */
93 	nodev,			/* cb_dump */
94 	vscan_drv_read,		/* cb_read */
95 	nodev,			/* cb_write */
96 	vscan_drv_ioctl,	/* cb_ioctl */
97 	nodev,			/* cb_devmap */
98 	nodev,			/* cb_mmap */
99 	nodev,			/* cb_segmap */
100 	nochpoll,		/* cb_chpoll */
101 	ddi_prop_op,		/* cb_prop_op */
102 	NULL,			/* cb_streamtab */
103 	D_MP,			/* cb_flag */
104 	CB_REV,			/* cb_rev */
105 	nodev,			/* cb_aread */
106 	nodev,			/* cb_awrite */
107 };
108 
109 static struct dev_ops devops = {
110 	DEVO_REV,		/* devo_rev */
111 	0,			/* devo_refcnt */
112 	vscan_drv_getinfo,	/* devo_getinfo */
113 	nulldev,		/* devo_identify */
114 	nulldev,		/* devo_probe */
115 	vscan_drv_attach,	/* devo_attach */
116 	vscan_drv_detach,	/* devo_detach */
117 	nodev,			/* devo_reset */
118 	&cbops,			/* devo_cb_ops */
119 	NULL,			/* devo_bus_ops */
120 	NULL,			/* devo_power */
121 };
122 
123 static struct modldrv modldrv = {
124 	&mod_driverops,		/* drv_modops */
125 	"virus scanning",	/* drv_linkinfo */
126 	&devops,
127 };
128 
129 static struct modlinkage modlinkage = {
130 
131 	MODREV_1,	/* revision of the module, must be: MODREV_1	*/
132 	&modldrv,	/* ptr to linkage structures			*/
133 	NULL,
134 };
135 
136 
137 /*
138  * _init
139  */
140 int
141 _init(void)
142 {
143 	int rc;
144 
145 	mutex_init(&vscan_drv_mutex, NULL, MUTEX_DRIVER, NULL);
146 
147 	if (vscan_door_init() != 0) {
148 		mutex_destroy(&vscan_drv_mutex);
149 		return (DDI_FAILURE);
150 	}
151 
152 	if (vscan_svc_init() != 0) {
153 		vscan_door_fini();
154 		mutex_destroy(&vscan_drv_mutex);
155 		return (DDI_FAILURE);
156 	}
157 
158 	(void) memset(&vscan_drv_state, 0, sizeof (vscan_drv_state));
159 
160 	if ((rc  = mod_install(&modlinkage)) != 0) {
161 		vscan_door_fini();
162 		vscan_svc_fini();
163 		mutex_destroy(&vscan_drv_mutex);
164 	}
165 
166 	return (rc);
167 }
168 
169 
170 /*
171  * _info
172  */
173 int
174 _info(struct modinfo *modinfop)
175 {
176 	return (mod_info(&modlinkage, modinfop));
177 }
178 
179 
180 /*
181  * _fini
182  */
183 int
184 _fini(void)
185 {
186 	int rc;
187 
188 	if (vscan_drv_in_use())
189 		return (EBUSY);
190 
191 	if ((rc = mod_remove(&modlinkage)) == 0) {
192 		vscan_door_fini();
193 		vscan_svc_fini();
194 		mutex_destroy(&vscan_drv_mutex);
195 	}
196 
197 	return (rc);
198 }
199 
200 
201 /*
202  * DDI entry points.
203  */
204 
205 /*
206  * vscan_drv_getinfo
207  */
208 /* ARGSUSED */
209 static int
210 vscan_drv_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
211 {
212 	ulong_t inst = getminor((dev_t)arg);
213 
214 	switch (cmd) {
215 	case DDI_INFO_DEVT2DEVINFO:
216 		*result = vscan_drv_dip;
217 		return (DDI_SUCCESS);
218 	case DDI_INFO_DEVT2INSTANCE:
219 		*result = (void *)inst;
220 		return (DDI_SUCCESS);
221 	}
222 	return (DDI_FAILURE);
223 }
224 
225 
226 /*
227  * vscan_drv_attach
228  */
229 static int
230 vscan_drv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
231 {
232 	int i;
233 	char name[VS_DRV_NODENAME_LEN];
234 
235 	if (cmd != DDI_ATTACH)
236 		return (DDI_FAILURE);
237 
238 	if (ddi_get_instance(dip) != 0)
239 		return (DDI_FAILURE);
240 
241 	vscan_drv_dip = dip;
242 
243 	/* create the minor nodes */
244 	for (i = 0; i <= VS_DRV_MAX_FILES; i++) {
245 		(void) snprintf(name, VS_DRV_NODENAME_LEN, "vscan%d", i);
246 		if (ddi_create_minor_node(dip, name, S_IFCHR, i,
247 		    DDI_PSEUDO, 0) != DDI_SUCCESS) {
248 			ddi_remove_minor_node(dip, NULL);
249 			return (DDI_FAILURE);
250 		}
251 	}
252 
253 	return (DDI_SUCCESS);
254 }
255 
256 
257 /*
258  * vscan_drv_detach
259  */
260 static int
261 vscan_drv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
262 {
263 	if (cmd != DDI_DETACH)
264 		return (DDI_FAILURE);
265 
266 	if (ddi_get_instance(dip) != 0)
267 		return (DDI_FAILURE);
268 
269 	if (vscan_drv_in_use())
270 		return (DDI_FAILURE);
271 
272 	vscan_drv_dip = NULL;
273 	ddi_remove_minor_node(dip, NULL);
274 
275 	return (DDI_SUCCESS);
276 }
277 
278 
279 /*
280  * vscan_drv_in_use
281  */
282 static boolean_t
283 vscan_drv_in_use()
284 {
285 	if (vscan_drv_connected)
286 		return (B_TRUE);
287 	else
288 		return (vscan_svc_in_use());
289 }
290 
291 
292 /*
293  * vscan_drv_open
294  * if inst == 0, this is vscand initializing.
295  * Otherwise, open the file associated with inst.
296  */
297 /* ARGSUSED */
298 static int
299 vscan_drv_open(dev_t *devp, int flag, int otyp, cred_t *credp)
300 {
301 	int rc;
302 	int inst = getminor(*devp);
303 
304 	if ((inst < 0) || (inst > VS_DRV_MAX_FILES))
305 		return (EINVAL);
306 
307 	/* check if caller has privilege for virus scanning */
308 	if ((rc = secpolicy_vscan(credp)) != 0) {
309 		DTRACE_PROBE1(vscan__priv, int, rc);
310 		return (EPERM);
311 	}
312 
313 	mutex_enter(&vscan_drv_mutex);
314 	if (inst == 0) {
315 		if (vscan_drv_connected) {
316 			mutex_exit(&vscan_drv_mutex);
317 			return (EINVAL);
318 		}
319 		vscan_drv_connected = B_TRUE;
320 	} else {
321 		if ((!vscan_drv_connected) ||
322 		    (vscan_drv_state[inst] != VS_INIT)) {
323 				mutex_exit(&vscan_drv_mutex);
324 				return (EINVAL);
325 		}
326 		vscan_drv_state[inst] = VS_OPEN;
327 	}
328 	mutex_exit(&vscan_drv_mutex);
329 
330 	return (0);
331 }
332 
333 
334 /*
335  * vscan_drv_close
336  * if inst == 0, this is vscand detaching
337  * Otherwise close the file associated with inst
338  */
339 /* ARGSUSED */
340 static int
341 vscan_drv_close(dev_t dev, int flag, int otyp, cred_t *credp)
342 {
343 	int i, inst = getminor(dev);
344 
345 	if ((inst < 0) || (inst > VS_DRV_MAX_FILES))
346 		return (EINVAL);
347 
348 	mutex_enter(&vscan_drv_mutex);
349 	if (inst == 0) {
350 		for (i = 1; i <= VS_DRV_MAX_FILES; i++)
351 			vscan_drv_state[i] = VS_INIT;
352 
353 		vscan_drv_connected = B_FALSE;
354 		vscan_svc_enable(B_FALSE);
355 		vscan_door_close();
356 	} else {
357 		vscan_drv_state[inst] = VS_INIT;
358 	}
359 	mutex_exit(&vscan_drv_mutex);
360 
361 	return (0);
362 }
363 
364 
365 /*
366  * vscan_drv_read
367  */
368 /* ARGSUSED */
369 static int
370 vscan_drv_read(dev_t dev, struct uio *uiop, cred_t *credp)
371 {
372 	int rc;
373 	int inst = getminor(dev);
374 	vnode_t *vp;
375 
376 	if ((inst <= 0) || (inst > VS_DRV_MAX_FILES))
377 		return (EINVAL);
378 
379 	mutex_enter(&vscan_drv_mutex);
380 	if ((!vscan_drv_connected) || (vscan_drv_state[inst] != VS_OPEN)) {
381 		mutex_exit(&vscan_drv_mutex);
382 		return (EINVAL);
383 	}
384 	vscan_drv_state[inst] = VS_READING;
385 	mutex_exit(&vscan_drv_mutex);
386 
387 	if ((vp = vscan_svc_get_vnode(inst)) == NULL)
388 		return (EINVAL);
389 
390 	(void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, NULL);
391 	rc = VOP_READ(vp, uiop, 0, kcred, NULL);
392 	VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL);
393 
394 	mutex_enter(&vscan_drv_mutex);
395 	if (vscan_drv_state[inst] == VS_READING)
396 		vscan_drv_state[inst] = VS_OPEN;
397 	mutex_exit(&vscan_drv_mutex);
398 
399 	return (rc);
400 }
401 
402 
403 /*
404  * vscan_drv_ioctl
405  */
406 /* ARGSUSED */
407 static int
408 vscan_drv_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
409 	cred_t *credp, int *rvalp)
410 {
411 	int inst = getminor(dev);
412 	vs_config_t conf;
413 
414 	if (inst != 0)
415 		return (EINVAL);
416 
417 	switch (cmd) {
418 	case VS_DRV_IOCTL_ENABLE:
419 		mutex_enter(&vscan_drv_mutex);
420 		if ((!vscan_drv_connected) ||
421 		    (vscan_door_open((int)arg) != 0)) {
422 			mutex_exit(&vscan_drv_mutex);
423 			return (EINVAL);
424 		}
425 		vscan_svc_enable(B_TRUE);
426 		mutex_exit(&vscan_drv_mutex);
427 		break;
428 	case VS_DRV_IOCTL_DISABLE:
429 		vscan_svc_enable(B_FALSE);
430 		break;
431 	case VS_DRV_IOCTL_CONFIG:
432 		if (ddi_copyin((void *)arg, &conf,
433 		    sizeof (vs_config_t), 0) == -1)
434 			return (EFAULT);
435 		if (vscan_svc_configure(&conf) == -1)
436 			return (EINVAL);
437 		break;
438 	default:
439 		return (ENOTTY);
440 	}
441 
442 	return (0);
443 }
444