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 */
51 uint32_t vs_reconnect_timeout = VS_DAEMON_WAIT_SEC;
52 extern 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 */
93 typedef 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;
100 static 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 */
137 typedef 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
144 static vscan_drv_inst_state_t *vscan_drv_inst_state;
145 static int vscan_drv_inst_state_sz;
146
147 static dev_info_t *vscan_drv_dip;
148 static kmutex_t vscan_drv_mutex;
149 static kcondvar_t vscan_drv_cv; /* wait for daemon reconnect */
150
151 /*
152 * DDI entry points.
153 */
154 static int vscan_drv_attach(dev_info_t *, ddi_attach_cmd_t);
155 static int vscan_drv_detach(dev_info_t *, ddi_detach_cmd_t);
156 static int vscan_drv_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
157 static int vscan_drv_open(dev_t *, int, int, cred_t *);
158 static int vscan_drv_close(dev_t, int, int, cred_t *);
159 static int vscan_drv_read(dev_t, struct uio *, cred_t *);
160 static int vscan_drv_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
161
162 static boolean_t vscan_drv_in_use(void);
163 static void vscan_drv_delayed_disable(void);
164
165
166 /*
167 * module linkage info for the kernel
168 */
169 static 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
190 static 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
205 static struct modldrv modldrv = {
206 &mod_driverops, /* drv_modops */
207 "virus scanning", /* drv_linkinfo */
208 &devops,
209 };
210
211 static struct modlinkage modlinkage = {
212
213 MODREV_1, /* revision of the module, must be: MODREV_1 */
214 { &modldrv, NULL } /* ptr to linkage structures */
215 };
216
217
218 /*
219 * _init
220 */
221 int
222 _init(void)
223 {
224 int rc;
225
226 vscan_drv_inst_state_sz =
227 sizeof (vscan_drv_inst_state_t) * (vs_nodes_max + 1);
228
229 if (vscan_door_init() != 0)
230 return (DDI_FAILURE);
231
232 if (vscan_svc_init() != 0) {
233 vscan_door_fini();
234 return (DDI_FAILURE);
235 }
236
237 mutex_init(&vscan_drv_mutex, NULL, MUTEX_DRIVER, NULL);
238 vscan_drv_inst_state = kmem_zalloc(vscan_drv_inst_state_sz, KM_SLEEP);
239
240 cv_init(&vscan_drv_cv, NULL, CV_DEFAULT, NULL);
241
242 if ((rc = mod_install(&modlinkage)) != 0) {
243 vscan_door_fini();
244 vscan_svc_fini();
245 kmem_free(vscan_drv_inst_state, vscan_drv_inst_state_sz);
246 cv_destroy(&vscan_drv_cv);
247 mutex_destroy(&vscan_drv_mutex);
248 }
249
250 return (rc);
251 }
252
253
254 /*
255 * _info
256 */
257 int
258 _info(struct modinfo *modinfop)
259 {
260 return (mod_info(&modlinkage, modinfop));
261 }
262
263
264 /*
265 * _fini
266 */
267 int
268 _fini(void)
269 {
270 int rc;
271
272 if (vscan_drv_in_use())
273 return (EBUSY);
274
275 if ((rc = mod_remove(&modlinkage)) == 0) {
276 vscan_door_fini();
277 vscan_svc_fini();
278 kmem_free(vscan_drv_inst_state, vscan_drv_inst_state_sz);
279 cv_destroy(&vscan_drv_cv);
280 mutex_destroy(&vscan_drv_mutex);
281 }
282
283 return (rc);
284 }
285
286
287 /*
288 * DDI entry points.
289 */
290
291 /*
292 * vscan_drv_getinfo
293 */
294 /* ARGSUSED */
295 static int
296 vscan_drv_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
297 {
298 ulong_t inst = getminor((dev_t)arg);
299
300 switch (cmd) {
301 case DDI_INFO_DEVT2DEVINFO:
302 *result = vscan_drv_dip;
303 return (DDI_SUCCESS);
304 case DDI_INFO_DEVT2INSTANCE:
305 *result = (void *)inst;
306 return (DDI_SUCCESS);
307 }
308 return (DDI_FAILURE);
309 }
310
311
312 /*
313 * vscan_drv_attach
314 */
315 static int
316 vscan_drv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
317 {
318 if (cmd != DDI_ATTACH)
319 return (DDI_FAILURE);
320
321 if (ddi_get_instance(dip) != 0)
322 return (DDI_FAILURE);
323
324 vscan_drv_dip = dip;
325
326 /* create minor node 0 for daemon-driver synchronization */
327 if (vscan_drv_create_node(0) == B_FALSE)
328 return (DDI_FAILURE);
329
330 vscan_drv_state = VS_DRV_IDLE;
331 return (DDI_SUCCESS);
332 }
333
334
335 /*
336 * vscan_drv_detach
337 */
338 static int
339 vscan_drv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
340 {
341 int i;
342
343 if (cmd != DDI_DETACH)
344 return (DDI_FAILURE);
345
346 if (ddi_get_instance(dip) != 0)
347 return (DDI_FAILURE);
348
349 if (vscan_drv_in_use())
350 return (DDI_FAILURE);
351
352 /* remove all minor nodes */
353 vscan_drv_dip = NULL;
354 ddi_remove_minor_node(dip, NULL);
355 for (i = 0; i <= vs_nodes_max; i++)
356 vscan_drv_inst_state[i] = VS_DRV_INST_UNCONFIG;
357
358 vscan_drv_state = VS_DRV_UNCONFIG;
359 return (DDI_SUCCESS);
360 }
361
362
363 /*
364 * vscan_drv_in_use
365 *
366 * If the driver state is not IDLE or UNCONFIG then the
367 * driver is in use. Otherwise, check the service interface
368 * (vscan_svc) to see if it is still in use - for example
369 * there there may be requests still in progress.
370 */
371 static boolean_t
372 vscan_drv_in_use()
373 {
374 boolean_t in_use = B_FALSE;
375
376 mutex_enter(&vscan_drv_mutex);
377 if ((vscan_drv_state != VS_DRV_IDLE) &&
378 (vscan_drv_state != VS_DRV_UNCONFIG)) {
379 in_use = B_TRUE;
380 }
381 mutex_exit(&vscan_drv_mutex);
382
383 if (in_use)
384 return (B_TRUE);
385 else
386 return (vscan_svc_in_use());
387 }
388
389
390 /*
391 * vscan_drv_open
392 *
393 * If inst == 0, this is vscand initializing.
394 * If the driver is in DELAYED_DISABLE, ie vscand previously
395 * disconnected without a clean shutdown and the driver is
396 * waiting for a period to allow vscand to reconnect, signal
397 * vscan_drv_cv to cancel the delayed disable.
398 *
399 * If inst != 0, open the file associated with inst.
400 */
401 /* ARGSUSED */
402 static int
403 vscan_drv_open(dev_t *devp, int flag, int otyp, cred_t *credp)
404 {
405 int rc;
406 int inst = getminor(*devp);
407
408 if ((inst < 0) || (inst > vs_nodes_max))
409 return (EINVAL);
410
411 /* check if caller has privilege for virus scanning */
412 if ((rc = secpolicy_vscan(credp)) != 0) {
413 DTRACE_PROBE1(vscan__priv, int, rc);
414 return (EPERM);
415 }
416
417 mutex_enter(&vscan_drv_mutex);
418 if (inst == 0) {
419 switch (vscan_drv_state) {
420 case VS_DRV_IDLE:
421 vscan_drv_state = VS_DRV_CONNECTED;
422 break;
423 case VS_DRV_DELAYED_DISABLE:
424 cv_signal(&vscan_drv_cv);
425 vscan_drv_state = VS_DRV_CONNECTED;
426 break;
427 default:
428 DTRACE_PROBE1(vscan__drv__state__violation,
429 int, vscan_drv_state);
430 mutex_exit(&vscan_drv_mutex);
431 return (EINVAL);
432 }
433 } else {
434 if ((vscan_drv_state != VS_DRV_ENABLED) ||
435 (vscan_drv_inst_state[inst] != VS_DRV_INST_INIT)) {
436 mutex_exit(&vscan_drv_mutex);
437 return (EINVAL);
438 }
439 vscan_drv_inst_state[inst] = VS_DRV_INST_OPEN;
440 }
441 mutex_exit(&vscan_drv_mutex);
442
443 return (0);
444 }
445
446
447 /*
448 * vscan_drv_close
449 *
450 * If inst == 0, this is vscand detaching.
451 * If the driver is in ENABLED state vscand has terminated without
452 * a clean shutdown (nod DISABLE received). Enter DELAYED_DISABLE
453 * state and initiate a delayed disable to allow vscand time to
454 * reconnect.
455 *
456 * If inst != 0, close the file associated with inst
457 */
458 /* ARGSUSED */
459 static int
460 vscan_drv_close(dev_t dev, int flag, int otyp, cred_t *credp)
461 {
462 int i, inst = getminor(dev);
463
464 if ((inst < 0) || (inst > vs_nodes_max))
465 return (EINVAL);
466
467 mutex_enter(&vscan_drv_mutex);
468 if (inst != 0) {
469 vscan_drv_inst_state[inst] = VS_DRV_INST_INIT;
470 mutex_exit(&vscan_drv_mutex);
471 return (0);
472 }
473
474 /* instance 0 - daemon disconnect */
475 if ((vscan_drv_state != VS_DRV_CONNECTED) &&
476 (vscan_drv_state != VS_DRV_ENABLED)) {
477 DTRACE_PROBE1(vscan__drv__state__violation,
478 int, vscan_drv_state);
479 mutex_exit(&vscan_drv_mutex);
480 return (EINVAL);
481 }
482
483 for (i = 1; i <= vs_nodes_max; i++) {
484 if (vscan_drv_inst_state[i] != VS_DRV_INST_UNCONFIG)
485 vscan_drv_inst_state[i] = VS_DRV_INST_INIT;
486 }
487
488 if (vscan_drv_state == VS_DRV_CONNECTED) {
489 vscan_drv_state = VS_DRV_IDLE;
490 } else { /* VS_DRV_ENABLED */
491 cmn_err(CE_WARN, "Detected vscand exit without clean shutdown");
492 if (thread_create(NULL, 0, vscan_drv_delayed_disable,
493 0, 0, &p0, TS_RUN, minclsyspri) == NULL) {
494 vscan_svc_disable();
495 vscan_drv_state = VS_DRV_IDLE;
496 } else {
497 vscan_drv_state = VS_DRV_DELAYED_DISABLE;
498 }
499 }
500 mutex_exit(&vscan_drv_mutex);
501
502 vscan_svc_scan_abort();
503 vscan_door_close();
504 return (0);
505 }
506
507
508 /*
509 * vscan_drv_delayed_disable
510 *
511 * Invoked from vscan_drv_close if the daemon disconnects
512 * without first sending disable (e.g. daemon crashed).
513 * Delays for vs_reconnect_timeout before disabling, to allow
514 * the daemon to reconnect. During this time, scan requests
515 * will be processed locally (see vscan_svc.c)
516 */
517 static void
518 vscan_drv_delayed_disable(void)
519 {
520 mutex_enter(&vscan_drv_mutex);
521 (void) cv_reltimedwait(&vscan_drv_cv, &vscan_drv_mutex,
522 SEC_TO_TICK(vs_reconnect_timeout), TR_CLOCK_TICK);
523
524 if (vscan_drv_state == VS_DRV_DELAYED_DISABLE) {
525 vscan_svc_disable();
526 vscan_drv_state = VS_DRV_IDLE;
527 } else {
528 DTRACE_PROBE(vscan__reconnect);
529 }
530 mutex_exit(&vscan_drv_mutex);
531 }
532
533
534 /*
535 * vscan_drv_read
536 */
537 /* ARGSUSED */
538 static int
539 vscan_drv_read(dev_t dev, struct uio *uiop, cred_t *credp)
540 {
541 int rc;
542 int inst = getminor(dev);
543 vnode_t *vp;
544
545 if ((inst <= 0) || (inst > vs_nodes_max))
546 return (EINVAL);
547
548 mutex_enter(&vscan_drv_mutex);
549 if ((vscan_drv_state != VS_DRV_ENABLED) ||
550 (vscan_drv_inst_state[inst] != VS_DRV_INST_OPEN)) {
551 mutex_exit(&vscan_drv_mutex);
552 return (EINVAL);
553 }
554 vscan_drv_inst_state[inst] = VS_DRV_INST_READING;
555 mutex_exit(&vscan_drv_mutex);
556
557 if ((vp = vscan_svc_get_vnode(inst)) == NULL)
558 return (EINVAL);
559
560 (void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, NULL);
561 rc = VOP_READ(vp, uiop, 0, kcred, NULL);
562 VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL);
563
564 mutex_enter(&vscan_drv_mutex);
565 if (vscan_drv_inst_state[inst] == VS_DRV_INST_READING)
566 vscan_drv_inst_state[inst] = VS_DRV_INST_OPEN;
567 mutex_exit(&vscan_drv_mutex);
568
569 return (rc);
570 }
571
572
573 /*
574 * vscan_drv_ioctl
575 *
576 * Process ioctls from vscand:
577 * VS_IOCTL_ENABLE - vscand is ready to handle scan requests,
578 * enable VFS interface.
579 * VS_IOCTL_DISABLE - vscand is shutting down, disable VFS interface
580 * VS_IOCTL_RESULT - scan response data
581 * VS_IOCTL_CONFIG - configuration data from vscand
582 * VS_IOCTL_MAX_REQ - provide the max request idx to vscand,
583 * to allow vscand to set appropriate resource allocation limits
584 */
585 /* ARGSUSED */
586 static int
587 vscan_drv_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
588 cred_t *credp, int *rvalp)
589 {
590 int inst = getminor(dev);
591 vs_config_t conf;
592 vs_scan_rsp_t rsp;
593
594 if (inst != 0)
595 return (EINVAL);
596
597 switch (cmd) {
598 case VS_IOCTL_ENABLE:
599 mutex_enter(&vscan_drv_mutex);
600 if (vscan_drv_state != VS_DRV_CONNECTED) {
601 DTRACE_PROBE1(vscan__drv__state__violation,
602 int, vscan_drv_state);
603 mutex_exit(&vscan_drv_mutex);
604 return (EINVAL);
605 }
606 if ((vscan_door_open((int)arg) != 0) ||
607 (vscan_svc_enable() != 0)) {
608 mutex_exit(&vscan_drv_mutex);
609 return (EINVAL);
610 }
611 vscan_drv_state = VS_DRV_ENABLED;
612 mutex_exit(&vscan_drv_mutex);
613 break;
614
615 case VS_IOCTL_DISABLE:
616 mutex_enter(&vscan_drv_mutex);
617 if (vscan_drv_state != VS_DRV_ENABLED) {
618 DTRACE_PROBE1(vscan__drv__state__violation,
619 int, vscan_drv_state);
620 mutex_exit(&vscan_drv_mutex);
621 return (EINVAL);
622 }
623 vscan_svc_disable();
624 vscan_drv_state = VS_DRV_CONNECTED;
625 mutex_exit(&vscan_drv_mutex);
626 break;
627
628 case VS_IOCTL_RESULT:
629 if (ddi_copyin((void *)arg, &rsp,
630 sizeof (vs_scan_rsp_t), 0) == -1)
631 return (EFAULT);
632 else
633 vscan_svc_scan_result(&rsp);
634 break;
635
636 case VS_IOCTL_CONFIG:
637 if (ddi_copyin((void *)arg, &conf,
638 sizeof (vs_config_t), 0) == -1)
639 return (EFAULT);
640 if (vscan_svc_configure(&conf) == -1)
641 return (EINVAL);
642 break;
643
644 case VS_IOCTL_MAX_REQ:
645 if (ddi_copyout(&vs_nodes_max, (void *)arg,
646 sizeof (uint32_t), 0) == -1)
647 return (EFAULT);
648 break;
649
650 default:
651 return (ENOTTY);
652 }
653
654 return (0);
655 }
656
657
658 /*
659 * vscan_drv_create_node
660 *
661 * Create minor node with which vscan daemon will communicate
662 * to access a file. Invoked from vscan_svc before scan request
663 * sent up to daemon.
664 * Minor node 0 is reserved for daemon-driver synchronization
665 * and is created during attach.
666 * All minor nodes are removed during detach.
667 */
668 boolean_t
669 vscan_drv_create_node(int idx)
670 {
671 char name[VS_NODENAME_LEN];
672 boolean_t rc = B_TRUE;
673
674 mutex_enter(&vscan_drv_mutex);
675
676 if (vscan_drv_inst_state[idx] == VS_DRV_INST_UNCONFIG) {
677 (void) snprintf(name, VS_NODENAME_LEN, "vscan%d", idx);
678 if (ddi_create_minor_node(vscan_drv_dip, name,
679 S_IFCHR, idx, DDI_PSEUDO, 0) == DDI_SUCCESS) {
680 vscan_drv_inst_state[idx] = VS_DRV_INST_INIT;
681 } else {
682 rc = B_FALSE;
683 }
684 DTRACE_PROBE2(vscan__minor__node, int, idx, int, rc);
685 }
686
687 mutex_exit(&vscan_drv_mutex);
688
689 return (rc);
690 }