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 2009 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#include <sys/stat.h>
28#include <sys/ddi.h>
29#include <sys/sunddi.h>
30#include <sys/time.h>
31#include <sys/varargs.h>
32#include <sys/conf.h>
33#include <sys/modctl.h>
34#include <sys/vnode.h>
35#include <fs/fs_subr.h>
36#include <sys/types.h>
37#include <sys/file.h>
38#include <sys/disp.h>
39#include <sys/vscan.h>
40#include <sys/policy.h>
41#include <sys/sdt.h>
42
43
44/* seconds to wait for daemon to reconnect before disabling */
45#define	VS_DAEMON_WAIT_SEC	60
46
47/* length of minor node name - vscan%d */
48#define	VS_NODENAME_LEN		16
49
50/* global variables - tunable via /etc/system */
51uint32_t vs_reconnect_timeout = VS_DAEMON_WAIT_SEC;
52extern uint32_t vs_nodes_max;	/* max in-progress scan requests */
53
54/*
55 * vscan_drv_state
56 *
57 * Operations on instance 0 represent vscand initiated state
58 * transition events:
59 * open(0) - vscand connect
60 * close(0) - vscan disconnect
61 * enable(0) - vscand enable (ready to hand requests)
62 * disable(0) - vscand disable (shutting down)
63 *
64 *   +------------------------+
65 *   | VS_DRV_UNCONFIG        |
66 *   +------------------------+
67 *      |           ^
68 *      | attach    | detach
69 *      v           |
70 *   +------------------------+
71 *   | VS_DRV_IDLE            |<------|
72 *   +------------------------+       |
73 *      |           ^                 |
74 *      | open(0)   | close(0)        |
75 *      v           |                 |
76 *   +------------------------+       |
77 *   | VS_DRV_CONNECTED       |<-|    |
78 *   +------------------------+  |    |
79 *      |           ^            |    |
80 *      | enable(0) | disable(0) |    |
81 *      v           |            |    |
82 *   +------------------------+  |    |
83 *   | VS_DRV_ENABLED         |  |    |
84 *   +------------------------+  |    |
85 *      |                        |    |
86 *      | close(0)            open(0) |
87 *      v                        |    |
88 *   +------------------------+  |    | timeout
89 *   | VS_DRV_DELAYED_DISABLE | --    |
90 *   +------------------------+	------|
91 *
92 */
93typedef enum {
94	VS_DRV_UNCONFIG,
95	VS_DRV_IDLE,
96	VS_DRV_CONNECTED,
97	VS_DRV_ENABLED,
98	VS_DRV_DELAYED_DISABLE
99} vscan_drv_state_t;
100static vscan_drv_state_t vscan_drv_state = VS_DRV_UNCONFIG;
101
102
103/*
104 * vscan_drv_inst_state
105 *
106 * Instance 0 controls the state of the driver: vscan_drv_state.
107 * vscan_drv_inst_state[0] should NOT be used.
108 *
109 * vscan_drv_inst_state[n] represents the state of driver
110 * instance n, used by vscand to access file data for the
111 * scan request with index n in vscan_svc_reqs.
112 * Minor nodes are created as required then all are destroyed
113 * during driver detach.
114 *
115 *   +------------------------+
116 *   | VS_DRV_INST_UNCONFIG   |
117 *   +------------------------+
118 *      |                 ^
119 *      | create_node(n)  | detach
120 *      v                 |
121 *   +------------------------+
122 *   | VS_DRV_INST_INIT       |<-|
123 *   +------------------------+  |
124 *      |                        |
125 *      | open(n)                |
126 *      v                        |
127 *   +------------------------+  |
128 *   | VS_DRV_INST_OPEN       |--|
129 *   +------------------------+  |
130 *      |                        |
131 *      | read(n)                |
132 *      v                        | close(n)
133 *   +------------------------+  |
134 *   | VS_DRV_INST_READING    |--|
135 *   +------------------------+
136 */
137typedef enum {
138	VS_DRV_INST_UNCONFIG = 0, /* minor node not created */
139	VS_DRV_INST_INIT,
140	VS_DRV_INST_OPEN,
141	VS_DRV_INST_READING
142} vscan_drv_inst_state_t;
143
144static vscan_drv_inst_state_t *vscan_drv_inst_state;
145static int vscan_drv_inst_state_sz;
146
147static dev_info_t *vscan_drv_dip;
148static kmutex_t vscan_drv_mutex;
149static kcondvar_t vscan_drv_cv; /* wait for daemon reconnect */
150
151/*
152 * DDI entry points.
153 */
154static int vscan_drv_attach(dev_info_t *, ddi_attach_cmd_t);
155static int vscan_drv_detach(dev_info_t *, ddi_detach_cmd_t);
156static int vscan_drv_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
157static int vscan_drv_open(dev_t *, int, int, cred_t *);
158static int vscan_drv_close(dev_t, int, int, cred_t *);
159static int vscan_drv_read(dev_t, struct uio *, cred_t *);
160static int vscan_drv_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
161
162static boolean_t vscan_drv_in_use(void);
163static void vscan_drv_delayed_disable(void);
164
165
166/*
167 * module linkage info for the kernel
168 */
169static struct cb_ops cbops = {
170	vscan_drv_open,		/* cb_open */
171	vscan_drv_close,	/* cb_close */
172	nodev,			/* cb_strategy */
173	nodev,			/* cb_print */
174	nodev,			/* cb_dump */
175	vscan_drv_read,		/* cb_read */
176	nodev,			/* cb_write */
177	vscan_drv_ioctl,	/* cb_ioctl */
178	nodev,			/* cb_devmap */
179	nodev,			/* cb_mmap */
180	nodev,			/* cb_segmap */
181	nochpoll,		/* cb_chpoll */
182	ddi_prop_op,		/* cb_prop_op */
183	NULL,			/* cb_streamtab */
184	D_MP,			/* cb_flag */
185	CB_REV,			/* cb_rev */
186	nodev,			/* cb_aread */
187	nodev,			/* cb_awrite */
188};
189
190static struct dev_ops devops = {
191	DEVO_REV,		/* devo_rev */
192	0,			/* devo_refcnt */
193	vscan_drv_getinfo,	/* devo_getinfo */
194	nulldev,		/* devo_identify */
195	nulldev,		/* devo_probe */
196	vscan_drv_attach,	/* devo_attach */
197	vscan_drv_detach,	/* devo_detach */
198	nodev,			/* devo_reset */
199	&cbops,			/* devo_cb_ops */
200	NULL,			/* devo_bus_ops */
201	NULL,			/* devo_power */
202	ddi_quiesce_not_needed,		/* devo_quiesce */
203};
204
205static struct modldrv modldrv = {
206	&mod_driverops,		/* drv_modops */
207	"virus scanning",	/* drv_linkinfo */
208	&devops,
209};
210
211static struct modlinkage modlinkage = {
212
213	MODREV_1,	/* revision of the module, must be: MODREV_1	*/
214	&modldrv,	/* ptr to linkage structures			*/
215	NULL,
216};
217
218
219/*
220 * _init
221 */
222int
223_init(void)
224{
225	int rc;
226
227	vscan_drv_inst_state_sz =
228	    sizeof (vscan_drv_inst_state_t) * (vs_nodes_max + 1);
229
230	if (vscan_door_init() != 0)
231		return (DDI_FAILURE);
232
233	if (vscan_svc_init() != 0) {
234		vscan_door_fini();
235		return (DDI_FAILURE);
236	}
237
238	mutex_init(&vscan_drv_mutex, NULL, MUTEX_DRIVER, NULL);
239	vscan_drv_inst_state = kmem_zalloc(vscan_drv_inst_state_sz, KM_SLEEP);
240
241	cv_init(&vscan_drv_cv, NULL, CV_DEFAULT, NULL);
242
243	if ((rc  = mod_install(&modlinkage)) != 0) {
244		vscan_door_fini();
245		vscan_svc_fini();
246		kmem_free(vscan_drv_inst_state, vscan_drv_inst_state_sz);
247		cv_destroy(&vscan_drv_cv);
248		mutex_destroy(&vscan_drv_mutex);
249	}
250
251	return (rc);
252}
253
254
255/*
256 * _info
257 */
258int
259_info(struct modinfo *modinfop)
260{
261	return (mod_info(&modlinkage, modinfop));
262}
263
264
265/*
266 * _fini
267 */
268int
269_fini(void)
270{
271	int rc;
272
273	if (vscan_drv_in_use())
274		return (EBUSY);
275
276	if ((rc = mod_remove(&modlinkage)) == 0) {
277		vscan_door_fini();
278		vscan_svc_fini();
279		kmem_free(vscan_drv_inst_state, vscan_drv_inst_state_sz);
280		cv_destroy(&vscan_drv_cv);
281		mutex_destroy(&vscan_drv_mutex);
282	}
283
284	return (rc);
285}
286
287
288/*
289 * DDI entry points.
290 */
291
292/*
293 * vscan_drv_getinfo
294 */
295/* ARGSUSED */
296static int
297vscan_drv_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
298{
299	ulong_t inst = getminor((dev_t)arg);
300
301	switch (cmd) {
302	case DDI_INFO_DEVT2DEVINFO:
303		*result = vscan_drv_dip;
304		return (DDI_SUCCESS);
305	case DDI_INFO_DEVT2INSTANCE:
306		*result = (void *)inst;
307		return (DDI_SUCCESS);
308	}
309	return (DDI_FAILURE);
310}
311
312
313/*
314 * vscan_drv_attach
315 */
316static int
317vscan_drv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
318{
319	if (cmd != DDI_ATTACH)
320		return (DDI_FAILURE);
321
322	if (ddi_get_instance(dip) != 0)
323		return (DDI_FAILURE);
324
325	vscan_drv_dip = dip;
326
327	/* create minor node 0 for daemon-driver synchronization */
328	if (vscan_drv_create_node(0) == B_FALSE)
329		return (DDI_FAILURE);
330
331	vscan_drv_state = VS_DRV_IDLE;
332	return (DDI_SUCCESS);
333}
334
335
336/*
337 * vscan_drv_detach
338 */
339static int
340vscan_drv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
341{
342	int i;
343
344	if (cmd != DDI_DETACH)
345		return (DDI_FAILURE);
346
347	if (ddi_get_instance(dip) != 0)
348		return (DDI_FAILURE);
349
350	if (vscan_drv_in_use())
351		return (DDI_FAILURE);
352
353	/* remove all minor nodes */
354	vscan_drv_dip = NULL;
355	ddi_remove_minor_node(dip, NULL);
356	for (i = 0; i <= vs_nodes_max; i++)
357		vscan_drv_inst_state[i] = VS_DRV_INST_UNCONFIG;
358
359	vscan_drv_state = VS_DRV_UNCONFIG;
360	return (DDI_SUCCESS);
361}
362
363
364/*
365 * vscan_drv_in_use
366 *
367 * If the driver state is not IDLE or UNCONFIG then the
368 * driver is in use. Otherwise, check the service interface
369 * (vscan_svc) to see if it is still in use - for example
370 * there there may be requests still in progress.
371 */
372static boolean_t
373vscan_drv_in_use()
374{
375	boolean_t in_use = B_FALSE;
376
377	mutex_enter(&vscan_drv_mutex);
378	if ((vscan_drv_state != VS_DRV_IDLE) &&
379	    (vscan_drv_state != VS_DRV_UNCONFIG)) {
380		in_use = B_TRUE;
381	}
382	mutex_exit(&vscan_drv_mutex);
383
384	if (in_use)
385		return (B_TRUE);
386	else
387		return (vscan_svc_in_use());
388}
389
390
391/*
392 * vscan_drv_open
393 *
394 * If inst == 0, this is vscand initializing.
395 * If the driver is in DELAYED_DISABLE, ie vscand previously
396 * disconnected without a clean shutdown and the driver is
397 * waiting for a period to allow vscand to reconnect, signal
398 * vscan_drv_cv to cancel the delayed disable.
399 *
400 * If inst != 0, open the file associated with inst.
401 */
402/* ARGSUSED */
403static int
404vscan_drv_open(dev_t *devp, int flag, int otyp, cred_t *credp)
405{
406	int rc;
407	int inst = getminor(*devp);
408
409	if ((inst < 0) || (inst > vs_nodes_max))
410		return (EINVAL);
411
412	/* check if caller has privilege for virus scanning */
413	if ((rc = secpolicy_vscan(credp)) != 0) {
414		DTRACE_PROBE1(vscan__priv, int, rc);
415		return (EPERM);
416	}
417
418	mutex_enter(&vscan_drv_mutex);
419	if (inst == 0) {
420		switch (vscan_drv_state) {
421		case VS_DRV_IDLE:
422			vscan_drv_state = VS_DRV_CONNECTED;
423			break;
424		case VS_DRV_DELAYED_DISABLE:
425			cv_signal(&vscan_drv_cv);
426			vscan_drv_state = VS_DRV_CONNECTED;
427			break;
428		default:
429			DTRACE_PROBE1(vscan__drv__state__violation,
430			    int, vscan_drv_state);
431			mutex_exit(&vscan_drv_mutex);
432			return (EINVAL);
433		}
434	} else {
435		if ((vscan_drv_state != VS_DRV_ENABLED) ||
436		    (vscan_drv_inst_state[inst] != VS_DRV_INST_INIT)) {
437			mutex_exit(&vscan_drv_mutex);
438			return (EINVAL);
439		}
440		vscan_drv_inst_state[inst] = VS_DRV_INST_OPEN;
441	}
442	mutex_exit(&vscan_drv_mutex);
443
444	return (0);
445}
446
447
448/*
449 * vscan_drv_close
450 *
451 * If inst == 0, this is vscand detaching.
452 * If the driver is in ENABLED state vscand has terminated without
453 * a clean shutdown (nod DISABLE received). Enter DELAYED_DISABLE
454 * state and initiate a delayed disable to allow vscand time to
455 * reconnect.
456 *
457 * If inst != 0, close the file associated with inst
458 */
459/* ARGSUSED */
460static int
461vscan_drv_close(dev_t dev, int flag, int otyp, cred_t *credp)
462{
463	int i, inst = getminor(dev);
464
465	if ((inst < 0) || (inst > vs_nodes_max))
466		return (EINVAL);
467
468	mutex_enter(&vscan_drv_mutex);
469	if (inst != 0) {
470		vscan_drv_inst_state[inst] = VS_DRV_INST_INIT;
471		mutex_exit(&vscan_drv_mutex);
472		return (0);
473	}
474
475	/* instance 0 - daemon disconnect */
476	if ((vscan_drv_state != VS_DRV_CONNECTED) &&
477	    (vscan_drv_state != VS_DRV_ENABLED)) {
478		DTRACE_PROBE1(vscan__drv__state__violation,
479		    int, vscan_drv_state);
480		mutex_exit(&vscan_drv_mutex);
481		return (EINVAL);
482	}
483
484	for (i = 1; i <= vs_nodes_max; i++) {
485		if (vscan_drv_inst_state[i] != VS_DRV_INST_UNCONFIG)
486			vscan_drv_inst_state[i] = VS_DRV_INST_INIT;
487	}
488
489	if (vscan_drv_state == VS_DRV_CONNECTED) {
490		vscan_drv_state = VS_DRV_IDLE;
491	} else { /* VS_DRV_ENABLED */
492		cmn_err(CE_WARN, "Detected vscand exit without clean shutdown");
493		if (thread_create(NULL, 0, vscan_drv_delayed_disable,
494		    0, 0, &p0, TS_RUN, minclsyspri) == NULL) {
495			vscan_svc_disable();
496			vscan_drv_state = VS_DRV_IDLE;
497		} else {
498			vscan_drv_state = VS_DRV_DELAYED_DISABLE;
499		}
500	}
501	mutex_exit(&vscan_drv_mutex);
502
503	vscan_svc_scan_abort();
504	vscan_door_close();
505	return (0);
506}
507
508
509/*
510 * vscan_drv_delayed_disable
511 *
512 * Invoked from vscan_drv_close if the daemon disconnects
513 * without first sending disable (e.g. daemon crashed).
514 * Delays for vs_reconnect_timeout before disabling, to allow
515 * the daemon to reconnect. During this time, scan requests
516 * will be processed locally (see vscan_svc.c)
517 */
518static void
519vscan_drv_delayed_disable(void)
520{
521	mutex_enter(&vscan_drv_mutex);
522	(void) cv_reltimedwait(&vscan_drv_cv, &vscan_drv_mutex,
523	    SEC_TO_TICK(vs_reconnect_timeout), TR_CLOCK_TICK);
524
525	if (vscan_drv_state == VS_DRV_DELAYED_DISABLE) {
526		vscan_svc_disable();
527		vscan_drv_state = VS_DRV_IDLE;
528	} else {
529		DTRACE_PROBE(vscan__reconnect);
530	}
531	mutex_exit(&vscan_drv_mutex);
532}
533
534
535/*
536 * vscan_drv_read
537 */
538/* ARGSUSED */
539static int
540vscan_drv_read(dev_t dev, struct uio *uiop, cred_t *credp)
541{
542	int rc;
543	int inst = getminor(dev);
544	vnode_t *vp;
545
546	if ((inst <= 0) || (inst > vs_nodes_max))
547		return (EINVAL);
548
549	mutex_enter(&vscan_drv_mutex);
550	if ((vscan_drv_state != VS_DRV_ENABLED) ||
551	    (vscan_drv_inst_state[inst] != VS_DRV_INST_OPEN)) {
552		mutex_exit(&vscan_drv_mutex);
553		return (EINVAL);
554	}
555	vscan_drv_inst_state[inst] = VS_DRV_INST_READING;
556	mutex_exit(&vscan_drv_mutex);
557
558	if ((vp = vscan_svc_get_vnode(inst)) == NULL)
559		return (EINVAL);
560
561	(void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, NULL);
562	rc = VOP_READ(vp, uiop, 0, kcred, NULL);
563	VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL);
564
565	mutex_enter(&vscan_drv_mutex);
566	if (vscan_drv_inst_state[inst] == VS_DRV_INST_READING)
567		vscan_drv_inst_state[inst] = VS_DRV_INST_OPEN;
568	mutex_exit(&vscan_drv_mutex);
569
570	return (rc);
571}
572
573
574/*
575 * vscan_drv_ioctl
576 *
577 * Process ioctls from vscand:
578 * VS_IOCTL_ENABLE - vscand is ready to handle scan requests,
579 *    enable VFS interface.
580 * VS_IOCTL_DISABLE - vscand is shutting down, disable VFS interface
581 * VS_IOCTL_RESULT - scan response data
582 * VS_IOCTL_CONFIG - configuration data from vscand
583 * VS_IOCTL_MAX_REQ - provide the max request idx to vscand,
584 *    to allow vscand to set appropriate resource allocation limits
585 */
586/* ARGSUSED */
587static int
588vscan_drv_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
589	cred_t *credp, int *rvalp)
590{
591	int inst = getminor(dev);
592	vs_config_t conf;
593	vs_scan_rsp_t rsp;
594
595	if (inst != 0)
596		return (EINVAL);
597
598	switch (cmd) {
599	case VS_IOCTL_ENABLE:
600		mutex_enter(&vscan_drv_mutex);
601		if (vscan_drv_state != VS_DRV_CONNECTED) {
602			DTRACE_PROBE1(vscan__drv__state__violation,
603			    int, vscan_drv_state);
604			mutex_exit(&vscan_drv_mutex);
605			return (EINVAL);
606		}
607		if ((vscan_door_open((int)arg) != 0) ||
608		    (vscan_svc_enable() != 0)) {
609			mutex_exit(&vscan_drv_mutex);
610			return (EINVAL);
611		}
612		vscan_drv_state = VS_DRV_ENABLED;
613		mutex_exit(&vscan_drv_mutex);
614		break;
615
616	case VS_IOCTL_DISABLE:
617		mutex_enter(&vscan_drv_mutex);
618		if (vscan_drv_state != VS_DRV_ENABLED) {
619			DTRACE_PROBE1(vscan__drv__state__violation,
620			    int, vscan_drv_state);
621			mutex_exit(&vscan_drv_mutex);
622			return (EINVAL);
623		}
624		vscan_svc_disable();
625		vscan_drv_state = VS_DRV_CONNECTED;
626		mutex_exit(&vscan_drv_mutex);
627		break;
628
629	case VS_IOCTL_RESULT:
630		if (ddi_copyin((void *)arg, &rsp,
631		    sizeof (vs_scan_rsp_t), 0) == -1)
632			return (EFAULT);
633		else
634			vscan_svc_scan_result(&rsp);
635		break;
636
637	case VS_IOCTL_CONFIG:
638		if (ddi_copyin((void *)arg, &conf,
639		    sizeof (vs_config_t), 0) == -1)
640			return (EFAULT);
641		if (vscan_svc_configure(&conf) == -1)
642			return (EINVAL);
643		break;
644
645	case VS_IOCTL_MAX_REQ:
646		if (ddi_copyout(&vs_nodes_max, (void *)arg,
647		    sizeof (uint32_t), 0) == -1)
648			return (EFAULT);
649		break;
650
651	default:
652		return (ENOTTY);
653	}
654
655	return (0);
656}
657
658
659/*
660 * vscan_drv_create_node
661 *
662 * Create minor node with which vscan daemon will communicate
663 * to access a file. Invoked from vscan_svc before scan request
664 * sent up to daemon.
665 * Minor node 0 is reserved for daemon-driver synchronization
666 * and is created during attach.
667 * All minor nodes are removed during detach.
668 */
669boolean_t
670vscan_drv_create_node(int idx)
671{
672	char name[VS_NODENAME_LEN];
673	boolean_t rc = B_TRUE;
674
675	mutex_enter(&vscan_drv_mutex);
676
677	if (vscan_drv_inst_state[idx] == VS_DRV_INST_UNCONFIG) {
678		(void) snprintf(name, VS_NODENAME_LEN, "vscan%d", idx);
679		if (ddi_create_minor_node(vscan_drv_dip, name,
680		    S_IFCHR, idx, DDI_PSEUDO, 0) == DDI_SUCCESS) {
681			vscan_drv_inst_state[idx] = VS_DRV_INST_INIT;
682		} else {
683			rc = B_FALSE;
684		}
685		DTRACE_PROBE2(vscan__minor__node, int, idx, int, rc);
686	}
687
688	mutex_exit(&vscan_drv_mutex);
689
690	return (rc);
691}
692