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 2008 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_nodes[VS_DRV_MAX_FILES + 1];
66 static boolean_t vscan_drv_connected = B_FALSE; /* vscand daemon connected */
67 
68 static dev_info_t *vscan_drv_dip;
69 static kmutex_t vscan_drv_mutex;
70 static kcondvar_t vscan_drv_cv; /* wait for daemon reconnect */
71 
72 /*
73  * DDI entry points.
74  */
75 static int vscan_drv_attach(dev_info_t *, ddi_attach_cmd_t);
76 static int vscan_drv_detach(dev_info_t *, ddi_detach_cmd_t);
77 static int vscan_drv_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
78 static int vscan_drv_open(dev_t *, int, int, cred_t *);
79 static int vscan_drv_close(dev_t, int, int, cred_t *);
80 static int vscan_drv_read(dev_t, struct uio *, cred_t *);
81 static int vscan_drv_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
82 
83 static boolean_t vscan_drv_in_use(void);
84 static void vscan_drv_delayed_disable(void);
85 
86 
87 /*
88  * module linkage info for the kernel
89  */
90 
91 static struct cb_ops cbops = {
92 	vscan_drv_open,		/* cb_open */
93 	vscan_drv_close,	/* cb_close */
94 	nodev,			/* cb_strategy */
95 	nodev,			/* cb_print */
96 	nodev,			/* cb_dump */
97 	vscan_drv_read,		/* cb_read */
98 	nodev,			/* cb_write */
99 	vscan_drv_ioctl,	/* cb_ioctl */
100 	nodev,			/* cb_devmap */
101 	nodev,			/* cb_mmap */
102 	nodev,			/* cb_segmap */
103 	nochpoll,		/* cb_chpoll */
104 	ddi_prop_op,		/* cb_prop_op */
105 	NULL,			/* cb_streamtab */
106 	D_MP,			/* cb_flag */
107 	CB_REV,			/* cb_rev */
108 	nodev,			/* cb_aread */
109 	nodev,			/* cb_awrite */
110 };
111 
112 static struct dev_ops devops = {
113 	DEVO_REV,		/* devo_rev */
114 	0,			/* devo_refcnt */
115 	vscan_drv_getinfo,	/* devo_getinfo */
116 	nulldev,		/* devo_identify */
117 	nulldev,		/* devo_probe */
118 	vscan_drv_attach,	/* devo_attach */
119 	vscan_drv_detach,	/* devo_detach */
120 	nodev,			/* devo_reset */
121 	&cbops,			/* devo_cb_ops */
122 	NULL,			/* devo_bus_ops */
123 	NULL,			/* devo_power */
124 };
125 
126 static struct modldrv modldrv = {
127 	&mod_driverops,		/* drv_modops */
128 	"virus scanning",	/* drv_linkinfo */
129 	&devops,
130 };
131 
132 static struct modlinkage modlinkage = {
133 
134 	MODREV_1,	/* revision of the module, must be: MODREV_1	*/
135 	&modldrv,	/* ptr to linkage structures			*/
136 	NULL,
137 };
138 
139 
140 /*
141  * _init
142  */
143 int
144 _init(void)
145 {
146 	int rc;
147 
148 	mutex_init(&vscan_drv_mutex, NULL, MUTEX_DRIVER, NULL);
149 
150 	if (vscan_door_init() != 0) {
151 		mutex_destroy(&vscan_drv_mutex);
152 		return (DDI_FAILURE);
153 	}
154 
155 	if (vscan_svc_init() != 0) {
156 		vscan_door_fini();
157 		mutex_destroy(&vscan_drv_mutex);
158 		return (DDI_FAILURE);
159 	}
160 
161 	(void) memset(&vscan_drv_state, 0, sizeof (vscan_drv_state));
162 	(void) memset(&vscan_drv_nodes, 0, sizeof (vscan_drv_nodes));
163 
164 	if ((rc  = mod_install(&modlinkage)) != 0) {
165 		vscan_door_fini();
166 		vscan_svc_fini();
167 		mutex_destroy(&vscan_drv_mutex);
168 	}
169 
170 	cv_init(&vscan_drv_cv, NULL, CV_DEFAULT, NULL);
171 	return (rc);
172 }
173 
174 
175 /*
176  * _info
177  */
178 int
179 _info(struct modinfo *modinfop)
180 {
181 	return (mod_info(&modlinkage, modinfop));
182 }
183 
184 
185 /*
186  * _fini
187  */
188 int
189 _fini(void)
190 {
191 	int rc;
192 
193 	if (vscan_drv_in_use())
194 		return (EBUSY);
195 
196 	if ((rc = mod_remove(&modlinkage)) == 0) {
197 		vscan_door_fini();
198 		vscan_svc_fini();
199 		cv_destroy(&vscan_drv_cv);
200 		mutex_destroy(&vscan_drv_mutex);
201 	}
202 
203 	return (rc);
204 }
205 
206 
207 /*
208  * DDI entry points.
209  */
210 
211 /*
212  * vscan_drv_getinfo
213  */
214 /* ARGSUSED */
215 static int
216 vscan_drv_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
217 {
218 	ulong_t inst = getminor((dev_t)arg);
219 
220 	switch (cmd) {
221 	case DDI_INFO_DEVT2DEVINFO:
222 		*result = vscan_drv_dip;
223 		return (DDI_SUCCESS);
224 	case DDI_INFO_DEVT2INSTANCE:
225 		*result = (void *)inst;
226 		return (DDI_SUCCESS);
227 	}
228 	return (DDI_FAILURE);
229 }
230 
231 
232 /*
233  * vscan_drv_attach
234  */
235 static int
236 vscan_drv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
237 {
238 	if (cmd != DDI_ATTACH)
239 		return (DDI_FAILURE);
240 
241 	if (ddi_get_instance(dip) != 0)
242 		return (DDI_FAILURE);
243 
244 	vscan_drv_dip = dip;
245 
246 	/* create minor node 0 for daemon-driver synchronization */
247 	if (vscan_drv_create_node(0) == B_FALSE)
248 		return (DDI_FAILURE);
249 
250 	return (DDI_SUCCESS);
251 }
252 
253 
254 /*
255  * vscan_drv_detach
256  */
257 static int
258 vscan_drv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
259 {
260 	if (cmd != DDI_DETACH)
261 		return (DDI_FAILURE);
262 
263 	if (ddi_get_instance(dip) != 0)
264 		return (DDI_FAILURE);
265 
266 	if (vscan_drv_in_use())
267 		return (DDI_FAILURE);
268 
269 	/* remove all minor nodes */
270 	vscan_drv_dip = NULL;
271 	ddi_remove_minor_node(dip, NULL);
272 	(void) memset(&vscan_drv_nodes, 0, sizeof (vscan_drv_nodes));
273 
274 	return (DDI_SUCCESS);
275 }
276 
277 
278 /*
279  * vscan_drv_in_use
280  *
281  * If vscand is connected (vscan_drv_connected == B_TRUE) the
282  * vscan driver is obviously in use. Otherwise invoke
283  * vscan_svc_in_use() to determine if the driver is in use,
284  * even though the daemon has disconnected.
285  * For example, there may be requests not yet complete, or
286  * the driver may still be enabled waiting for the daemon to
287  * reconnect.
288  * Used to determine whether the driver can be unloaded.
289  */
290 static boolean_t
291 vscan_drv_in_use()
292 {
293 	boolean_t in_use;
294 
295 	mutex_enter(&vscan_drv_mutex);
296 	in_use = vscan_drv_connected;
297 	mutex_exit(&vscan_drv_mutex);
298 
299 	if (in_use == B_FALSE)
300 		in_use = vscan_svc_in_use();
301 
302 	return (in_use);
303 }
304 
305 
306 /*
307  * vscan_drv_open
308  * if inst == 0, this is vscand initializing.
309  * Otherwise, open the file associated with inst.
310  */
311 /* ARGSUSED */
312 static int
313 vscan_drv_open(dev_t *devp, int flag, int otyp, cred_t *credp)
314 {
315 	int rc;
316 	int inst = getminor(*devp);
317 
318 	if ((inst < 0) || (inst > VS_DRV_MAX_FILES))
319 		return (EINVAL);
320 
321 	/* check if caller has privilege for virus scanning */
322 	if ((rc = secpolicy_vscan(credp)) != 0) {
323 		DTRACE_PROBE1(vscan__priv, int, rc);
324 		return (EPERM);
325 	}
326 
327 	mutex_enter(&vscan_drv_mutex);
328 	if (inst == 0) {
329 		if (vscan_drv_connected) {
330 			mutex_exit(&vscan_drv_mutex);
331 			return (EINVAL);
332 		}
333 		vscan_drv_connected = B_TRUE;
334 		/* wake any pending delayed disable */
335 		cv_signal(&vscan_drv_cv);
336 	} else {
337 		if ((!vscan_drv_connected) ||
338 		    (vscan_drv_state[inst] != VS_INIT)) {
339 				mutex_exit(&vscan_drv_mutex);
340 				return (EINVAL);
341 		}
342 		vscan_drv_state[inst] = VS_OPEN;
343 	}
344 	mutex_exit(&vscan_drv_mutex);
345 
346 	return (0);
347 }
348 
349 
350 /*
351  * vscan_drv_close
352  * if inst == 0, this is vscand detaching
353  * Otherwise close the file associated with inst
354  */
355 /* ARGSUSED */
356 static int
357 vscan_drv_close(dev_t dev, int flag, int otyp, cred_t *credp)
358 {
359 	int i, inst = getminor(dev);
360 
361 	if ((inst < 0) || (inst > VS_DRV_MAX_FILES))
362 		return (EINVAL);
363 
364 	mutex_enter(&vscan_drv_mutex);
365 	if (inst == 0) {
366 		for (i = 1; i <= VS_DRV_MAX_FILES; i++)
367 			vscan_drv_state[i] = VS_INIT;
368 
369 		vscan_drv_connected = B_FALSE;
370 		if (vscan_svc_is_enabled()) {
371 			if (thread_create(NULL, 0, vscan_drv_delayed_disable,
372 			    0, 0, &p0, TS_RUN, minclsyspri) == NULL) {
373 				vscan_svc_enable();
374 			}
375 		}
376 		vscan_door_close();
377 	} else {
378 		vscan_drv_state[inst] = VS_INIT;
379 	}
380 	mutex_exit(&vscan_drv_mutex);
381 
382 	return (0);
383 }
384 
385 
386 /*
387  * vscan_drv_delayed_disable
388  *
389  * Invoked from vscan_drv_close if the daemon disconnects
390  * without first sending disable (e.g. daemon crashed).
391  * Delays for VS_DAEMON_WAIT_SEC before disabling, to allow
392  * the daemon to reconnect. During this time, scan requests
393  * will be processed locally (see vscan_svc.c)
394  */
395 static void
396 vscan_drv_delayed_disable(void)
397 {
398 	clock_t timeout = lbolt + SEC_TO_TICK(VS_DAEMON_WAIT_SEC);
399 
400 	mutex_enter(&vscan_drv_mutex);
401 	(void) cv_timedwait(&vscan_drv_cv, &vscan_drv_mutex, timeout);
402 
403 	if (vscan_drv_connected) {
404 		DTRACE_PROBE(vscan__reconnect);
405 	} else {
406 		vscan_svc_disable();
407 	}
408 	mutex_exit(&vscan_drv_mutex);
409 }
410 
411 
412 /*
413  * vscan_drv_read
414  */
415 /* ARGSUSED */
416 static int
417 vscan_drv_read(dev_t dev, struct uio *uiop, cred_t *credp)
418 {
419 	int rc;
420 	int inst = getminor(dev);
421 	vnode_t *vp;
422 
423 	if ((inst <= 0) || (inst > VS_DRV_MAX_FILES))
424 		return (EINVAL);
425 
426 	mutex_enter(&vscan_drv_mutex);
427 	if ((!vscan_drv_connected) || (vscan_drv_state[inst] != VS_OPEN)) {
428 		mutex_exit(&vscan_drv_mutex);
429 		return (EINVAL);
430 	}
431 	vscan_drv_state[inst] = VS_READING;
432 	mutex_exit(&vscan_drv_mutex);
433 
434 	if ((vp = vscan_svc_get_vnode(inst)) == NULL)
435 		return (EINVAL);
436 
437 	(void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, NULL);
438 	rc = VOP_READ(vp, uiop, 0, kcred, NULL);
439 	VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL);
440 
441 	mutex_enter(&vscan_drv_mutex);
442 	if (vscan_drv_state[inst] == VS_READING)
443 		vscan_drv_state[inst] = VS_OPEN;
444 	mutex_exit(&vscan_drv_mutex);
445 
446 	return (rc);
447 }
448 
449 
450 /*
451  * vscan_drv_ioctl
452  */
453 /* ARGSUSED */
454 static int
455 vscan_drv_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
456 	cred_t *credp, int *rvalp)
457 {
458 	int inst = getminor(dev);
459 	vs_config_t conf;
460 
461 	if (inst != 0)
462 		return (EINVAL);
463 
464 	switch (cmd) {
465 	case VS_DRV_IOCTL_ENABLE:
466 		mutex_enter(&vscan_drv_mutex);
467 		if ((!vscan_drv_connected) ||
468 		    (vscan_door_open((int)arg) != 0)) {
469 			mutex_exit(&vscan_drv_mutex);
470 			return (EINVAL);
471 		}
472 		vscan_svc_enable();
473 		mutex_exit(&vscan_drv_mutex);
474 		break;
475 	case VS_DRV_IOCTL_DISABLE:
476 		vscan_svc_disable();
477 		break;
478 	case VS_DRV_IOCTL_CONFIG:
479 		if (ddi_copyin((void *)arg, &conf,
480 		    sizeof (vs_config_t), 0) == -1)
481 			return (EFAULT);
482 		if (vscan_svc_configure(&conf) == -1)
483 			return (EINVAL);
484 		break;
485 	default:
486 		return (ENOTTY);
487 	}
488 
489 	return (0);
490 }
491 
492 
493 /*
494  * vscan_drv_create_node
495  *
496  * Create minor node with which vscan daemon will communicate
497  * to access a file. Invoked from vscan_svc before scan request
498  * sent up to daemon.
499  * Minor node 0 is reserved for daemon-driver synchronization
500  * and is created during attach.
501  * All minor nodes are removed during detach.
502  */
503 boolean_t
504 vscan_drv_create_node(int idx)
505 {
506 	char name[VS_DRV_NODENAME_LEN];
507 	boolean_t *pnode, rc;
508 
509 	mutex_enter(&vscan_drv_mutex);
510 
511 	pnode = &vscan_drv_nodes[idx];
512 	if (*pnode == B_FALSE) {
513 		(void) snprintf(name, VS_DRV_NODENAME_LEN, "vscan%d", idx);
514 		if (ddi_create_minor_node(vscan_drv_dip, name,
515 		    S_IFCHR, idx, DDI_PSEUDO, 0) == DDI_SUCCESS) {
516 			*pnode = B_TRUE;
517 		}
518 		DTRACE_PROBE2(vscan__minor__node, int, idx, int, *pnode);
519 	}
520 
521 	rc = *pnode;
522 	mutex_exit(&vscan_drv_mutex);
523 
524 	return (rc);
525 }
526