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/cmn_err.h>
35 #include <sys/vnode.h>
36 #include <fs/fs_subr.h>
37 #include <sys/types.h>
38 #include <sys/file.h>
39 #include <sys/disp.h>
40 #include <sys/sdt.h>
41 #include <sys/cred.h>
42 #include <sys/list.h>
43 #include <sys/vscan.h>
44
45 #define VS_REQ_MAGIC 0x52515354 /* 'RQST' */
46
47 #define VS_REQS_DEFAULT 20000 /* pending scan requests - reql */
48 #define VS_NODES_DEFAULT 128 /* concurrent file scans */
49 #define VS_WORKERS_DEFAULT 32 /* worker threads */
50 #define VS_SCANWAIT_DEFAULT 15*60 /* seconds to wait for scan result */
51 #define VS_REQL_HANDLER_TIMEOUT 30
52 #define VS_EXT_RECURSE_DEPTH 8
53
54 /* access derived from scan result (VS_STATUS_XXX) and file attributes */
55 #define VS_ACCESS_UNDEFINED 0
56 #define VS_ACCESS_ALLOW 1 /* return 0 */
57 #define VS_ACCESS_DENY 2 /* return EACCES */
58
59 #define tolower(C) (((C) >= 'A' && (C) <= 'Z') ? (C) - 'A' + 'a' : (C))
60 #if defined(__GNUC__)
61 #define offsetof(s, m) __builtin_offsetof(s, m)
62 #else
63 #define offsetof(s, m) ((size_t)(&(((s *)0)->m)))
64 #endif
65
66 /* global variables - tunable via /etc/system */
67 uint32_t vs_reqs_max = VS_REQS_DEFAULT; /* max scan requests */
68 uint32_t vs_nodes_max = VS_NODES_DEFAULT; /* max in-progress scan requests */
69 uint32_t vs_workers = VS_WORKERS_DEFAULT; /* max workers send reqs to vscand */
70 uint32_t vs_scan_wait = VS_SCANWAIT_DEFAULT; /* secs to wait for scan result */
71
72
73 /*
74 * vscan_svc_state
75 *
76 * +-----------------+
77 * | VS_SVC_UNCONFIG |
78 * +-----------------+
79 * | ^
80 * | svc_init | svc_fini
81 * v |
82 * +-----------------+
83 * | VS_SVC_IDLE |<----|
84 * +-----------------+ |
85 * | |
86 * | svc_enable |
87 * |<----------------| |
88 * v | |
89 * +-----------------+ | |
90 * | VS_SVC_ENABLED |--| |
91 * +-----------------+ |
92 * | |
93 * | svc_disable | handler thread exit,
94 * v | all requests complete
95 * +-----------------+ |
96 * | VS_SVC_DISABLED |-----|
97 * +-----------------+
98 *
99 * svc_enable may occur when we are already in the ENABLED
100 * state if vscand has exited without clean shutdown and
101 * then reconnected within the delayed disable time period
102 * (vs_reconnect_timeout) - see vscan_drv
103 */
104
105 typedef enum {
106 VS_SVC_UNCONFIG,
107 VS_SVC_IDLE,
108 VS_SVC_ENABLED, /* service enabled and registered */
109 VS_SVC_DISABLED /* service disabled and nunregistered */
110 } vscan_svc_state_t;
111 static vscan_svc_state_t vscan_svc_state = VS_SVC_UNCONFIG;
112
113
114 /*
115 * vscan_svc_req_state
116 *
117 * When a scan request is received from the file system it is
118 * identified in or inserted into the vscan_svc_reql (INIT).
119 * If the request is asynchronous 0 is then returned to the caller.
120 * If the request is synchronous the req's refcnt is incremented
121 * and the caller waits for the request to complete.
122 * The refcnt is also incremented when the request is inserted
123 * in vscan_svc_nodes, and decremented on scan_complete.
124 *
125 * vscan_svc_handler processes requests from the request list,
126 * inserting them into vscan_svc_nodes and the task queue (QUEUED).
127 * When the task queue call back (vscan_svc_do_scan) is invoked
128 * the request transitions to IN_PROGRESS state. If the request
129 * is sucessfully sent to vscand (door_call) and the door response
130 * is SCANNING then the scan result will be received asynchronously.
131 * Although unusual, it is possible that the async response is
132 * received before the door call returns (hence the ASYNC_COMPLETE
133 * state).
134 * When the result has been determined / received,
135 * vscan_svc_scan_complete is invoked to transition the request to
136 * COMPLETE state, decrement refcnt and signal all waiting callers.
137 * When the last waiting caller has processed the result (refcnt == 0)
138 * the request is removed from vscan_svc_reql and vscan_svc_nodes
139 * and deleted.
140 *
141 * | ^
142 * | reql_insert | refcnt == 0
143 * v | (delete)
144 * +------------------------+ +---------------------+
145 * | VS_SVC_REQ_INIT | -----DISABLE----> | VS_SVC_REQ_COMPLETE |
146 * +------------------------+ +---------------------+
147 * | ^
148 * | insert_req, tq_dispatch |
149 * v |
150 * +------------------------+ |
151 * | VS_SVC_REQ_QUEUED | scan_complete
152 * +------------------------+ |
153 * | |
154 * | tq_callback (do_scan) |
155 * | |
156 * v scan not req'd, error, |
157 * +------------------------+ or door_result != SCANNING |
158 * | VS_SVC_REQ_IN_PROGRESS |----------------->-------------|
159 * +------------------------+ |
160 * | | |
161 * | | door_result == SCANNING |
162 * | v |
163 * | +---------------------------+ async result |
164 * | | VS_SVC_REQ_SCANNING |-------->---------|
165 * | +---------------------------+ |
166 * | |
167 * | async result |
168 * v |
169 * +---------------------------+ door_result = SCANNING |
170 * | VS_SVC_REQ_ASYNC_COMPLETE |-------->------------------|
171 * +---------------------------+
172 */
173 typedef enum {
174 VS_SVC_REQ_INIT,
175 VS_SVC_REQ_QUEUED,
176 VS_SVC_REQ_IN_PROGRESS,
177 VS_SVC_REQ_SCANNING,
178 VS_SVC_REQ_ASYNC_COMPLETE,
179 VS_SVC_REQ_COMPLETE
180 } vscan_svc_req_state_t;
181
182
183 /*
184 * vscan_svc_reql - the list of pending and in-progress scan requests
185 */
186 typedef struct vscan_req {
187 uint32_t vsr_magic; /* VS_REQ_MAGIC */
188 list_node_t vsr_lnode;
189 vnode_t *vsr_vp;
190 uint32_t vsr_idx; /* vscan_svc_nodes index */
191 uint32_t vsr_seqnum; /* unigue request id */
192 uint32_t vsr_refcnt;
193 kcondvar_t vsr_cv;
194 vscan_svc_req_state_t vsr_state;
195 } vscan_req_t;
196
197 static list_t vscan_svc_reql;
198
199
200 /*
201 * vscan_svc_nodes - table of files being scanned
202 *
203 * The index into this table is passed in the door call to
204 * vscand. vscand uses the idx to determine which minor node
205 * to open to read the file data. Within the kernel driver
206 * the minor device number can thus be used to identify the
207 * table index to get the appropriate vnode.
208 *
209 * Instance 0 is reserved for the daemon/driver control
210 * interface: enable/configure/disable
211 */
212 typedef struct vscan_svc_node {
213 vscan_req_t *vsn_req;
214 uint8_t vsn_quarantined;
215 uint8_t vsn_modified;
216 uint64_t vsn_size;
217 timestruc_t vsn_mtime;
218 vs_scanstamp_t vsn_scanstamp;
219 uint32_t vsn_result;
220 uint32_t vsn_access;
221 } vscan_svc_node_t;
222
223 static vscan_svc_node_t *vscan_svc_nodes;
224 static int vscan_svc_nodes_sz;
225
226
227 /* vscan_svc_taskq - queue of requests waiting to be sent to vscand */
228 static taskq_t *vscan_svc_taskq = NULL;
229
230 /* counts of entries in vscan_svc_reql, vscan_svc_nodes & vscan_svc_taskq */
231 typedef struct {
232 uint32_t vsc_reql;
233 uint32_t vsc_node;
234 uint32_t vsc_tq;
235 } vscan_svc_counts_t;
236 static vscan_svc_counts_t vscan_svc_counts;
237
238 /*
239 * vscan_svc_mutex protects the data pertaining to scan requests:
240 * request list - vscan_svc_reql
241 * node table - vscan_svc_nodes
242 */
243 static kmutex_t vscan_svc_mutex;
244
245 /* unique request id for vscand request/response correlation */
246 static uint32_t vscan_svc_seqnum = 0;
247
248 /*
249 * vscan_svc_cfg_mutex protects the configuration data:
250 * vscan_svc_config, vscan_svc_types
251 */
252 static kmutex_t vscan_svc_cfg_mutex;
253
254 /* configuration data - for virus scan exemption */
255 static vs_config_t vscan_svc_config;
256 static char *vscan_svc_types[VS_TYPES_MAX];
257
258 /* thread to insert reql entries into vscan_svc_nodes & vscan_svc_taskq */
259 static kthread_t *vscan_svc_reql_thread;
260 static kcondvar_t vscan_svc_reql_cv;
261 static vscan_req_t *vscan_svc_reql_next; /* next pending scan request */
262
263 /* local functions */
264 int vscan_svc_scan_file(vnode_t *, cred_t *, int);
265 static void vscan_svc_taskq_callback(void *);
266 static int vscan_svc_exempt_file(vnode_t *, boolean_t *);
267 static int vscan_svc_exempt_filetype(char *);
268 static int vscan_svc_match_ext(char *, char *, int);
269 static void vscan_svc_do_scan(vscan_req_t *);
270 static vs_scan_req_t *vscan_svc_populate_req(int);
271 static void vscan_svc_process_scan_result(int);
272 static void vscan_svc_scan_complete(vscan_req_t *);
273 static void vscan_svc_delete_req(vscan_req_t *);
274 static int vscan_svc_insert_req(vscan_req_t *);
275 static void vscan_svc_remove_req(int);
276 static vscan_req_t *vscan_svc_reql_find(vnode_t *);
277 static vscan_req_t *vscan_svc_reql_insert(vnode_t *);
278 static void vscan_svc_reql_remove(vscan_req_t *);
279
280 static int vscan_svc_getattr(int);
281 static int vscan_svc_setattr(int, int);
282
283 /* thread to insert reql entries into vscan_svc_nodes & vscan_svc_taskq */
284 static void vscan_svc_reql_handler(void);
285
286
287 /*
288 * vscan_svc_init
289 */
290 int
291 vscan_svc_init()
292 {
293 if (vscan_svc_state != VS_SVC_UNCONFIG) {
294 DTRACE_PROBE1(vscan__svc__state__violation,
295 int, vscan_svc_state);
296 return (-1);
297 }
298
299 mutex_init(&vscan_svc_mutex, NULL, MUTEX_DEFAULT, NULL);
300 mutex_init(&vscan_svc_cfg_mutex, NULL, MUTEX_DEFAULT, NULL);
301 cv_init(&vscan_svc_reql_cv, NULL, CV_DEFAULT, NULL);
302
303 vscan_svc_nodes_sz = sizeof (vscan_svc_node_t) * (vs_nodes_max + 1);
304 vscan_svc_nodes = kmem_zalloc(vscan_svc_nodes_sz, KM_SLEEP);
305
306 vscan_svc_counts.vsc_reql = 0;
307 vscan_svc_counts.vsc_node = 0;
308 vscan_svc_counts.vsc_tq = 0;
309
310 vscan_svc_state = VS_SVC_IDLE;
311
312 return (0);
313 }
314
315
316 /*
317 * vscan_svc_fini
318 */
319 void
320 vscan_svc_fini()
321 {
322 if (vscan_svc_state != VS_SVC_IDLE) {
323 DTRACE_PROBE1(vscan__svc__state__violation,
324 int, vscan_svc_state);
325 return;
326 }
327
328 kmem_free(vscan_svc_nodes, vscan_svc_nodes_sz);
329
330 cv_destroy(&vscan_svc_reql_cv);
331 mutex_destroy(&vscan_svc_mutex);
332 mutex_destroy(&vscan_svc_cfg_mutex);
333 vscan_svc_state = VS_SVC_UNCONFIG;
334 }
335
336
337 /*
338 * vscan_svc_enable
339 */
340 int
341 vscan_svc_enable(void)
342 {
343 mutex_enter(&vscan_svc_mutex);
344
345 switch (vscan_svc_state) {
346 case VS_SVC_ENABLED:
347 /*
348 * it's possible (and okay) for vscan_svc_enable to be
349 * called when already enabled if vscand reconnects
350 * during a delayed disable
351 */
352 break;
353 case VS_SVC_IDLE:
354 list_create(&vscan_svc_reql, sizeof (vscan_req_t),
355 offsetof(vscan_req_t, vsr_lnode));
356 vscan_svc_reql_next = list_head(&vscan_svc_reql);
357
358 vscan_svc_taskq = taskq_create("vscan_taskq", vs_workers,
359 MINCLSYSPRI, 1, INT_MAX, TASKQ_DYNAMIC);
360 ASSERT(vscan_svc_taskq != NULL);
361
362 vscan_svc_reql_thread = thread_create(NULL, 0,
363 vscan_svc_reql_handler, 0, 0, &p0, TS_RUN, MINCLSYSPRI);
364 ASSERT(vscan_svc_reql_thread != NULL);
365
366 /* ready to start processing requests */
367 vscan_svc_state = VS_SVC_ENABLED;
368 fs_vscan_register(vscan_svc_scan_file);
369 break;
370 default:
371 DTRACE_PROBE1(vscan__svc__state__violation,
372 int, vscan_svc_state);
373 return (-1);
374 }
375
376 mutex_exit(&vscan_svc_mutex);
377 return (0);
378 }
379
380
381 /*
382 * vscan_svc_disable
383 *
384 * Resources allocated during vscan_svc_enable are free'd by
385 * the handler thread immediately prior to exiting
386 */
387 void
388 vscan_svc_disable(void)
389 {
390 mutex_enter(&vscan_svc_mutex);
391
392 switch (vscan_svc_state) {
393 case VS_SVC_ENABLED:
394 fs_vscan_register(NULL);
395 vscan_svc_state = VS_SVC_DISABLED;
396 cv_signal(&vscan_svc_reql_cv); /* wake handler thread */
397 break;
398 default:
399 DTRACE_PROBE1(vscan__svc__state__violation, int,
400 vscan_svc_state);
401 }
402
403 mutex_exit(&vscan_svc_mutex);
404 }
405
406
407 /*
408 * vscan_svc_in_use
409 */
410 boolean_t
411 vscan_svc_in_use()
412 {
413 boolean_t in_use;
414
415 mutex_enter(&vscan_svc_mutex);
416
417 switch (vscan_svc_state) {
418 case VS_SVC_IDLE:
419 case VS_SVC_UNCONFIG:
420 in_use = B_FALSE;
421 break;
422 default:
423 in_use = B_TRUE;
424 break;
425 }
426
427 mutex_exit(&vscan_svc_mutex);
428 return (in_use);
429 }
430
431
432 /*
433 * vscan_svc_get_vnode
434 *
435 * Get the file vnode indexed by idx.
436 */
437 vnode_t *
438 vscan_svc_get_vnode(int idx)
439 {
440 vnode_t *vp = NULL;
441
442 ASSERT(idx > 0);
443 ASSERT(idx <= vs_nodes_max);
444
445 mutex_enter(&vscan_svc_mutex);
446 if (vscan_svc_nodes[idx].vsn_req)
447 vp = vscan_svc_nodes[idx].vsn_req->vsr_vp;
448 mutex_exit(&vscan_svc_mutex);
449
450 return (vp);
451 }
452
453
454 /*
455 * vscan_svc_scan_file
456 *
457 * This function is the entry point for the file system to
458 * request that a file be virus scanned.
459 */
460 int
461 vscan_svc_scan_file(vnode_t *vp, cred_t *cr, int async)
462 {
463 int access;
464 vscan_req_t *req;
465 boolean_t allow;
466 clock_t timeout, time_left;
467
468 if ((vp == NULL) || (vp->v_path == NULL) || cr == NULL)
469 return (0);
470
471 DTRACE_PROBE2(vscan__scan__file, char *, vp->v_path, int, async);
472
473 /* check if size or type exempts file from scanning */
474 if (vscan_svc_exempt_file(vp, &allow)) {
475 if ((allow == B_TRUE) || (async != 0))
476 return (0);
477
478 return (EACCES);
479 }
480
481 mutex_enter(&vscan_svc_mutex);
482
483 if (vscan_svc_state != VS_SVC_ENABLED) {
484 DTRACE_PROBE1(vscan__svc__state__violation,
485 int, vscan_svc_state);
486 mutex_exit(&vscan_svc_mutex);
487 return (0);
488 }
489
490 /* insert (or find) request in list */
491 if ((req = vscan_svc_reql_insert(vp)) == NULL) {
492 mutex_exit(&vscan_svc_mutex);
493 cmn_err(CE_WARN, "Virus scan request list full");
494 return ((async != 0) ? 0 : EACCES);
495 }
496
497 /* asynchronous request: return 0 */
498 if (async) {
499 mutex_exit(&vscan_svc_mutex);
500 return (0);
501 }
502
503 /* synchronous scan request: wait for result */
504 ++(req->vsr_refcnt);
505 time_left = SEC_TO_TICK(vs_scan_wait);
506 while ((time_left > 0) && (req->vsr_state != VS_SVC_REQ_COMPLETE)) {
507 timeout = time_left;
508 time_left = cv_reltimedwait_sig(&(req->vsr_cv),
509 &vscan_svc_mutex, timeout, TR_CLOCK_TICK);
510 }
511
512 if (time_left == -1) {
513 cmn_err(CE_WARN, "Virus scan request timeout %s (%d) \n",
514 vp->v_path, req->vsr_seqnum);
515 DTRACE_PROBE1(vscan__scan__timeout, vscan_req_t *, req);
516 }
517
518 ASSERT(req->vsr_magic == VS_REQ_MAGIC);
519 if (vscan_svc_state == VS_SVC_DISABLED)
520 access = VS_ACCESS_ALLOW;
521 else if (req->vsr_idx == 0)
522 access = VS_ACCESS_DENY;
523 else
524 access = vscan_svc_nodes[req->vsr_idx].vsn_access;
525
526 if ((--req->vsr_refcnt) == 0)
527 vscan_svc_delete_req(req);
528
529 mutex_exit(&vscan_svc_mutex);
530 return ((access == VS_ACCESS_ALLOW) ? 0 : EACCES);
531 }
532
533
534 /*
535 * vscan_svc_reql_handler
536 *
537 * inserts scan requests (from vscan_svc_reql) into
538 * vscan_svc_nodes and vscan_svc_taskq
539 */
540 static void
541 vscan_svc_reql_handler(void)
542 {
543 vscan_req_t *req, *next;
544
545 for (;;) {
546 mutex_enter(&vscan_svc_mutex);
547
548 if ((vscan_svc_state == VS_SVC_DISABLED) &&
549 (vscan_svc_counts.vsc_reql == 0)) {
550 /* free resources allocated durining enable */
551 taskq_destroy(vscan_svc_taskq);
552 vscan_svc_taskq = NULL;
553 list_destroy(&vscan_svc_reql);
554 vscan_svc_state = VS_SVC_IDLE;
555 mutex_exit(&vscan_svc_mutex);
556 return;
557 }
558
559 /*
560 * If disabled, scan_complete any pending requests.
561 * Otherwise insert pending requests into vscan_svc_nodes
562 * and vscan_svc_taskq. If no slots are available in
563 * vscan_svc_nodes break loop and wait for one
564 */
565 req = vscan_svc_reql_next;
566
567 while (req != NULL) {
568 ASSERT(req->vsr_magic == VS_REQ_MAGIC);
569 next = list_next(&vscan_svc_reql, req);
570
571 if (vscan_svc_state == VS_SVC_DISABLED) {
572 vscan_svc_scan_complete(req);
573 } else {
574 /* insert request into vscan_svc_nodes */
575 if (vscan_svc_insert_req(req) == -1)
576 break;
577
578 /* add the scan request into the taskq */
579 (void) taskq_dispatch(vscan_svc_taskq,
580 vscan_svc_taskq_callback,
581 (void *)req, TQ_SLEEP);
582 ++(vscan_svc_counts.vsc_tq);
583
584 req->vsr_state = VS_SVC_REQ_QUEUED;
585 }
586 req = next;
587 }
588
589 vscan_svc_reql_next = req;
590
591 DTRACE_PROBE2(vscan__req__counts, char *, "handler wait",
592 vscan_svc_counts_t *, &vscan_svc_counts);
593
594 (void) cv_reltimedwait(&vscan_svc_reql_cv, &vscan_svc_mutex,
595 SEC_TO_TICK(VS_REQL_HANDLER_TIMEOUT), TR_CLOCK_TICK);
596
597 DTRACE_PROBE2(vscan__req__counts, char *, "handler wake",
598 vscan_svc_counts_t *, &vscan_svc_counts);
599
600 mutex_exit(&vscan_svc_mutex);
601 }
602 }
603
604
605 static void
606 vscan_svc_taskq_callback(void *data)
607 {
608 vscan_req_t *req;
609
610 mutex_enter(&vscan_svc_mutex);
611
612 req = (vscan_req_t *)data;
613 ASSERT(req->vsr_magic == VS_REQ_MAGIC);
614 vscan_svc_do_scan(req);
615 if (req->vsr_state != VS_SVC_REQ_SCANNING)
616 vscan_svc_scan_complete(req);
617
618 --(vscan_svc_counts.vsc_tq);
619 mutex_exit(&vscan_svc_mutex);
620 }
621
622
623 /*
624 * vscan_svc_do_scan
625 *
626 * Note: To avoid potential deadlock it is important that
627 * vscan_svc_mutex is not held during the call to
628 * vscan_drv_create_note. vscan_drv_create_note enters
629 * the vscan_drv_mutex and it is possible that a thread
630 * holding that mutex could be waiting for vscan_svc_mutex.
631 */
632 static void
633 vscan_svc_do_scan(vscan_req_t *req)
634 {
635 int idx, result;
636 vscan_svc_node_t *node;
637 vs_scan_req_t *door_req;
638
639 ASSERT(MUTEX_HELD(&vscan_svc_mutex));
640
641 idx = req->vsr_idx;
642 node = &vscan_svc_nodes[idx];
643
644 req->vsr_state = VS_SVC_REQ_IN_PROGRESS;
645
646 /* if vscan not enabled (shutting down), allow ACCESS */
647 if (vscan_svc_state != VS_SVC_ENABLED) {
648 node->vsn_access = VS_ACCESS_ALLOW;
649 return;
650 }
651
652 if (vscan_svc_getattr(idx) != 0) {
653 cmn_err(CE_WARN, "Can't access xattr for %s\n",
654 req->vsr_vp->v_path);
655 node->vsn_access = VS_ACCESS_DENY;
656 return;
657 }
658
659 /* valid scan_req ptr guaranteed */
660 door_req = vscan_svc_populate_req(idx);
661
662 /* free up mutex around create node and door call */
663 mutex_exit(&vscan_svc_mutex);
664 if (vscan_drv_create_node(idx) != B_TRUE)
665 result = VS_STATUS_ERROR;
666 else
667 result = vscan_door_scan_file(door_req);
668 kmem_free(door_req, sizeof (vs_scan_req_t));
669 mutex_enter(&vscan_svc_mutex);
670
671 if (result != VS_STATUS_SCANNING) {
672 vscan_svc_nodes[idx].vsn_result = result;
673 vscan_svc_process_scan_result(idx);
674 } else { /* async response */
675 if (req->vsr_state == VS_SVC_REQ_IN_PROGRESS)
676 req->vsr_state = VS_SVC_REQ_SCANNING;
677 }
678 }
679
680
681 /*
682 * vscan_svc_populate_req
683 *
684 * Allocate a scan request to be sent to vscand, populating it
685 * from the data in vscan_svc_nodes[idx].
686 *
687 * Returns: scan request object
688 */
689 static vs_scan_req_t *
690 vscan_svc_populate_req(int idx)
691 {
692 vs_scan_req_t *scan_req;
693 vscan_req_t *req;
694 vscan_svc_node_t *node;
695
696 ASSERT(MUTEX_HELD(&vscan_svc_mutex));
697
698 node = &vscan_svc_nodes[idx];
699 req = node->vsn_req;
700 scan_req = kmem_zalloc(sizeof (vs_scan_req_t), KM_SLEEP);
701
702 scan_req->vsr_idx = idx;
703 scan_req->vsr_seqnum = req->vsr_seqnum;
704 (void) strncpy(scan_req->vsr_path, req->vsr_vp->v_path, MAXPATHLEN);
705 scan_req->vsr_size = node->vsn_size;
706 scan_req->vsr_modified = node->vsn_modified;
707 scan_req->vsr_quarantined = node->vsn_quarantined;
708 scan_req->vsr_flags = 0;
709 (void) strncpy(scan_req->vsr_scanstamp,
710 node->vsn_scanstamp, sizeof (vs_scanstamp_t));
711
712 return (scan_req);
713 }
714
715
716 /*
717 * vscan_svc_scan_complete
718 */
719 static void
720 vscan_svc_scan_complete(vscan_req_t *req)
721 {
722 ASSERT(MUTEX_HELD(&vscan_svc_mutex));
723 ASSERT(req != NULL);
724
725 req->vsr_state = VS_SVC_REQ_COMPLETE;
726
727 if ((--req->vsr_refcnt) == 0)
728 vscan_svc_delete_req(req);
729 else
730 cv_broadcast(&(req->vsr_cv));
731 }
732
733
734 /*
735 * vscan_svc_delete_req
736 */
737 static void
738 vscan_svc_delete_req(vscan_req_t *req)
739 {
740 int idx;
741
742 ASSERT(MUTEX_HELD(&vscan_svc_mutex));
743 ASSERT(req != NULL);
744 ASSERT(req->vsr_refcnt == 0);
745 ASSERT(req->vsr_state == VS_SVC_REQ_COMPLETE);
746
747 if ((idx = req->vsr_idx) != 0)
748 vscan_svc_remove_req(idx);
749
750 vscan_svc_reql_remove(req);
751
752 cv_signal(&vscan_svc_reql_cv);
753 }
754
755
756 /*
757 * vscan_svc_scan_result
758 *
759 * Invoked from vscan_drv.c on receipt of an ioctl containing
760 * an async scan result (VS_DRV_IOCTL_RESULT)
761 * If the vsr_seqnum in the response does not match that in the
762 * vscan_svc_nodes entry the result is discarded.
763 */
764 void
765 vscan_svc_scan_result(vs_scan_rsp_t *scan_rsp)
766 {
767 vscan_req_t *req;
768 vscan_svc_node_t *node;
769
770 mutex_enter(&vscan_svc_mutex);
771
772 node = &vscan_svc_nodes[scan_rsp->vsr_idx];
773
774 if ((req = node->vsn_req) == NULL) {
775 mutex_exit(&vscan_svc_mutex);
776 return;
777 }
778
779 ASSERT(req->vsr_magic == VS_REQ_MAGIC);
780
781 if (scan_rsp->vsr_seqnum != req->vsr_seqnum) {
782 mutex_exit(&vscan_svc_mutex);
783 return;
784 }
785
786 node->vsn_result = scan_rsp->vsr_result;
787 (void) strncpy(node->vsn_scanstamp,
788 scan_rsp->vsr_scanstamp, sizeof (vs_scanstamp_t));
789
790 vscan_svc_process_scan_result(scan_rsp->vsr_idx);
791
792 if (node->vsn_req->vsr_state == VS_SVC_REQ_SCANNING)
793 vscan_svc_scan_complete(node->vsn_req);
794 else
795 node->vsn_req->vsr_state = VS_SVC_REQ_ASYNC_COMPLETE;
796
797 mutex_exit(&vscan_svc_mutex);
798 }
799
800
801 /*
802 * vscan_svc_scan_abort
803 *
804 * Abort in-progress scan requests.
805 */
806 void
807 vscan_svc_scan_abort()
808 {
809 int idx;
810 vscan_req_t *req;
811
812 mutex_enter(&vscan_svc_mutex);
813
814 for (idx = 1; idx <= vs_nodes_max; idx++) {
815 if ((req = vscan_svc_nodes[idx].vsn_req) == NULL)
816 continue;
817
818 ASSERT(req->vsr_magic == VS_REQ_MAGIC);
819
820 if (req->vsr_state == VS_SVC_REQ_SCANNING) {
821 DTRACE_PROBE1(vscan__abort, vscan_req_t *, req);
822 vscan_svc_process_scan_result(idx);
823 vscan_svc_scan_complete(req);
824 }
825 }
826
827 mutex_exit(&vscan_svc_mutex);
828 }
829
830
831 /*
832 * vscan_svc_process_scan_result
833 *
834 * Sets vsn_access and updates file attributes based on vsn_result,
835 * as follows:
836 *
837 * VS_STATUS_INFECTED
838 * deny access, set quarantine attribute, clear scanstamp
839 * VS_STATUS_CLEAN
840 * allow access, set scanstamp,
841 * if file not modified since scan initiated, clear modified attribute
842 * VS_STATUS_NO_SCAN
843 * deny access if file quarantined, otherwise allow access
844 * VS_STATUS_UNDEFINED, VS_STATUS_ERROR
845 * deny access if file quarantined, modified or no scanstamp
846 * otherwise, allow access
847 */
848 static void
849 vscan_svc_process_scan_result(int idx)
850 {
851 struct vattr attr;
852 vnode_t *vp;
853 timestruc_t *mtime;
854 vscan_svc_node_t *node;
855
856 ASSERT(MUTEX_HELD(&vscan_svc_mutex));
857
858 node = &vscan_svc_nodes[idx];
859
860 switch (node->vsn_result) {
861 case VS_STATUS_INFECTED:
862 node->vsn_access = VS_ACCESS_DENY;
863 node->vsn_quarantined = 1;
864 node->vsn_scanstamp[0] = '\0';
865 (void) vscan_svc_setattr(idx,
866 XAT_AV_QUARANTINED | XAT_AV_SCANSTAMP);
867 break;
868
869 case VS_STATUS_CLEAN:
870 node->vsn_access = VS_ACCESS_ALLOW;
871
872 /* if mtime has changed, don't clear the modified attribute */
873 vp = node->vsn_req->vsr_vp;
874 mtime = &(node->vsn_mtime);
875 attr.va_mask = AT_MTIME;
876 if ((VOP_GETATTR(vp, &attr, 0, kcred, NULL) != 0) ||
877 (mtime->tv_sec != attr.va_mtime.tv_sec) ||
878 (mtime->tv_nsec != attr.va_mtime.tv_nsec)) {
879 DTRACE_PROBE1(vscan__mtime__changed, vscan_svc_node_t *,
880 node);
881 (void) vscan_svc_setattr(idx, XAT_AV_SCANSTAMP);
882 break;
883 }
884
885 node->vsn_modified = 0;
886 (void) vscan_svc_setattr(idx,
887 XAT_AV_SCANSTAMP | XAT_AV_MODIFIED);
888 break;
889
890 case VS_STATUS_NO_SCAN:
891 if (node->vsn_quarantined)
892 node->vsn_access = VS_ACCESS_DENY;
893 else
894 node->vsn_access = VS_ACCESS_ALLOW;
895 break;
896
897 case VS_STATUS_ERROR:
898 case VS_STATUS_UNDEFINED:
899 default:
900 if ((node->vsn_quarantined) ||
901 (node->vsn_modified) ||
902 (node->vsn_scanstamp[0] == '\0'))
903 node->vsn_access = VS_ACCESS_DENY;
904 else
905 node->vsn_access = VS_ACCESS_ALLOW;
906 break;
907 }
908
909 DTRACE_PROBE4(vscan__result,
910 int, idx, int, node->vsn_req->vsr_seqnum,
911 int, node->vsn_result, int, node->vsn_access);
912 }
913
914
915 /*
916 * vscan_svc_getattr
917 *
918 * Get the vscan related system attributes, AT_SIZE & AT_MTIME.
919 */
920 static int
921 vscan_svc_getattr(int idx)
922 {
923 xvattr_t xvattr;
924 xoptattr_t *xoap = NULL;
925 vnode_t *vp;
926 vscan_svc_node_t *node;
927
928 ASSERT(MUTEX_HELD(&vscan_svc_mutex));
929
930 node = &vscan_svc_nodes[idx];
931 if ((vp = node->vsn_req->vsr_vp) == NULL)
932 return (-1);
933
934 /* get the attributes */
935 xva_init(&xvattr); /* sets AT_XVATTR */
936
937 xvattr.xva_vattr.va_mask |= AT_SIZE;
938 xvattr.xva_vattr.va_mask |= AT_MTIME;
939 XVA_SET_REQ(&xvattr, XAT_AV_MODIFIED);
940 XVA_SET_REQ(&xvattr, XAT_AV_QUARANTINED);
941 XVA_SET_REQ(&xvattr, XAT_AV_SCANSTAMP);
942
943 if (VOP_GETATTR(vp, (vattr_t *)&xvattr, 0, kcred, NULL) != 0)
944 return (-1);
945
946 if ((xoap = xva_getxoptattr(&xvattr)) == NULL) {
947 cmn_err(CE_NOTE, "Virus scan request failed; "
948 "file system does not support virus scanning");
949 return (-1);
950 }
951
952 node->vsn_size = xvattr.xva_vattr.va_size;
953 node->vsn_mtime.tv_sec = xvattr.xva_vattr.va_mtime.tv_sec;
954 node->vsn_mtime.tv_nsec = xvattr.xva_vattr.va_mtime.tv_nsec;
955
956 if (XVA_ISSET_RTN(&xvattr, XAT_AV_MODIFIED) == 0)
957 return (-1);
958 node->vsn_modified = xoap->xoa_av_modified;
959
960 if (XVA_ISSET_RTN(&xvattr, XAT_AV_QUARANTINED) == 0)
961 return (-1);
962 node->vsn_quarantined = xoap->xoa_av_quarantined;
963
964 if (XVA_ISSET_RTN(&xvattr, XAT_AV_SCANSTAMP) != 0) {
965 (void) memcpy(node->vsn_scanstamp,
966 xoap->xoa_av_scanstamp, AV_SCANSTAMP_SZ);
967 }
968
969 DTRACE_PROBE1(vscan__getattr, vscan_svc_node_t *, node);
970 return (0);
971 }
972
973
974 /*
975 * vscan_svc_setattr
976 *
977 * Set the vscan related system attributes.
978 */
979 static int
980 vscan_svc_setattr(int idx, int which)
981 {
982 xvattr_t xvattr;
983 xoptattr_t *xoap = NULL;
984 vnode_t *vp;
985 int len;
986 vscan_svc_node_t *node;
987
988 ASSERT(MUTEX_HELD(&vscan_svc_mutex));
989
990 node = &vscan_svc_nodes[idx];
991 if ((vp = node->vsn_req->vsr_vp) == NULL)
992 return (-1);
993
994 /* update the attributes */
995 xva_init(&xvattr); /* sets AT_XVATTR */
996 if ((xoap = xva_getxoptattr(&xvattr)) == NULL)
997 return (-1);
998
999 if (which & XAT_AV_MODIFIED) {
1000 XVA_SET_REQ(&xvattr, XAT_AV_MODIFIED);
1001 xoap->xoa_av_modified = node->vsn_modified;
1002 }
1003
1004 if (which & XAT_AV_QUARANTINED) {
1005 XVA_SET_REQ(&xvattr, XAT_AV_QUARANTINED);
1006 xoap->xoa_av_quarantined = node->vsn_quarantined;
1007 }
1008
1009 if (which & XAT_AV_SCANSTAMP) {
1010 XVA_SET_REQ(&xvattr, XAT_AV_SCANSTAMP);
1011 len = strlen(node->vsn_scanstamp);
1012 (void) memcpy(xoap->xoa_av_scanstamp,
1013 node->vsn_scanstamp, len);
1014 }
1015
1016 /* if access is denied, set mtime to invalidate client cache */
1017 if (node->vsn_access != VS_ACCESS_ALLOW) {
1018 xvattr.xva_vattr.va_mask |= AT_MTIME;
1019 gethrestime(&xvattr.xva_vattr.va_mtime);
1020 }
1021
1022 if (VOP_SETATTR(vp, (vattr_t *)&xvattr, 0, kcred, NULL) != 0)
1023 return (-1);
1024
1025 DTRACE_PROBE2(vscan__setattr,
1026 vscan_svc_node_t *, node, int, which);
1027
1028 return (0);
1029 }
1030
1031
1032 /*
1033 * vscan_svc_configure
1034 *
1035 * store configuration in vscan_svc_config
1036 * set up vscan_svc_types array of pointers into
1037 * vscan_svc_config.vsc_types for efficient searching
1038 */
1039 int
1040 vscan_svc_configure(vs_config_t *conf)
1041 {
1042 int count = 0;
1043 char *p, *beg, *end;
1044
1045 mutex_enter(&vscan_svc_cfg_mutex);
1046
1047 vscan_svc_config = *conf;
1048
1049 (void) memset(vscan_svc_types, 0, sizeof (vscan_svc_types));
1050
1051 beg = vscan_svc_config.vsc_types;
1052 end = beg + vscan_svc_config.vsc_types_len;
1053
1054 for (p = beg; p < end; p += strlen(p) + 1) {
1055 if (count >= VS_TYPES_MAX) {
1056 mutex_exit(&vscan_svc_mutex);
1057 return (-1);
1058 }
1059
1060 vscan_svc_types[count] = p;
1061 ++count;
1062 }
1063
1064 mutex_exit(&vscan_svc_cfg_mutex);
1065 return (0);
1066 }
1067
1068
1069 /*
1070 * vscan_svc_exempt_file
1071 *
1072 * check if a file's size or type exempts it from virus scanning
1073 *
1074 * If the file is exempt from virus scanning, allow will be set
1075 * to define whether files access should be allowed (B_TRUE) or
1076 * denied (B_FALSE)
1077 *
1078 * Returns: 1 exempt
1079 * 0 scan required
1080 */
1081 static int
1082 vscan_svc_exempt_file(vnode_t *vp, boolean_t *allow)
1083 {
1084 struct vattr attr;
1085
1086 ASSERT(vp != NULL);
1087 ASSERT(vp->v_path != NULL);
1088
1089 attr.va_mask = AT_SIZE;
1090
1091 if (VOP_GETATTR(vp, &attr, 0, kcred, NULL) != 0) {
1092 *allow = B_FALSE;
1093 return (0);
1094 }
1095
1096 mutex_enter(&vscan_svc_cfg_mutex);
1097
1098 if (attr.va_size > vscan_svc_config.vsc_max_size) {
1099 DTRACE_PROBE2(vscan__exempt__filesize, char *,
1100 vp->v_path, int, *allow);
1101
1102 *allow = (vscan_svc_config.vsc_allow) ? B_TRUE : B_FALSE;
1103 mutex_exit(&vscan_svc_cfg_mutex);
1104 return (1);
1105 }
1106
1107 if (vscan_svc_exempt_filetype(vp->v_path)) {
1108 DTRACE_PROBE1(vscan__exempt__filetype, char *, vp->v_path);
1109 *allow = B_TRUE;
1110 mutex_exit(&vscan_svc_cfg_mutex);
1111 return (1);
1112 }
1113
1114 mutex_exit(&vscan_svc_cfg_mutex);
1115 return (0);
1116 }
1117
1118
1119 /*
1120 * vscan_svc_exempt_filetype
1121 *
1122 * Each entry in vscan_svc_types includes a rule indicator (+,-)
1123 * followed by the match string for file types to which the rule
1124 * applies. Look for first match of file type in vscan_svc_types
1125 * and return 1 (exempt) if the indicator is '-', and 0 (not exempt)
1126 * if the indicator is '+'.
1127 * If vscan_svc_match_ext fails, or no match is found, return 0
1128 * (not exempt)
1129 *
1130 * Returns 1: exempt, 0: not exempt
1131 */
1132 static int
1133 vscan_svc_exempt_filetype(char *filepath)
1134 {
1135 int i, rc, exempt = 0;
1136 char *filename, *ext;
1137
1138 ASSERT(MUTEX_HELD(&vscan_svc_cfg_mutex));
1139
1140 if ((filename = strrchr(filepath, '/')) == 0)
1141 filename = filepath;
1142 else
1143 filename++;
1144
1145 if ((ext = strrchr(filename, '.')) == NULL)
1146 ext = "";
1147 else
1148 ext++;
1149
1150 for (i = 0; i < VS_TYPES_MAX; i ++) {
1151 if (vscan_svc_types[i] == 0)
1152 break;
1153
1154 rc = vscan_svc_match_ext(vscan_svc_types[i] + 1, ext, 1);
1155 if (rc == -1)
1156 break;
1157 if (rc > 0) {
1158 DTRACE_PROBE2(vscan__type__match, char *, ext,
1159 char *, vscan_svc_types[i]);
1160 exempt = (vscan_svc_types[i][0] == '-');
1161 break;
1162 }
1163 }
1164
1165 return (exempt);
1166 }
1167
1168
1169 /*
1170 * vscan_svc_match_ext
1171 *
1172 * Performs a case-insensitive match for two strings. The first string
1173 * argument can contain the wildcard characters '?' and '*'
1174 *
1175 * Returns: 0 no match
1176 * 1 match
1177 * -1 recursion error
1178 */
1179 static int
1180 vscan_svc_match_ext(char *patn, char *str, int depth)
1181 {
1182 int c1, c2;
1183 if (depth > VS_EXT_RECURSE_DEPTH)
1184 return (-1);
1185
1186 for (;;) {
1187 switch (*patn) {
1188 case 0:
1189 return (*str == 0);
1190
1191 case '?':
1192 if (*str != 0) {
1193 str++;
1194 patn++;
1195 continue;
1196 }
1197 return (0);
1198
1199 case '*':
1200 patn++;
1201 if (*patn == 0)
1202 return (1);
1203
1204 while (*str) {
1205 if (vscan_svc_match_ext(patn, str, depth + 1))
1206 return (1);
1207 str++;
1208 }
1209 return (0);
1210
1211 default:
1212 if (*str != *patn) {
1213 c1 = *str;
1214 c2 = *patn;
1215
1216 c1 = tolower(c1);
1217 c2 = tolower(c2);
1218 if (c1 != c2)
1219 return (0);
1220 }
1221 str++;
1222 patn++;
1223 continue;
1224 }
1225 }
1226 /* NOT REACHED */
1227 }
1228
1229
1230 /*
1231 * vscan_svc_insert_req
1232 *
1233 * Insert request in next available available slot in vscan_svc_nodes
1234 *
1235 * Returns: idx of slot, or -1 if no slot available
1236 */
1237 static int
1238 vscan_svc_insert_req(vscan_req_t *req)
1239 {
1240 int idx;
1241 vscan_svc_node_t *node;
1242
1243 ASSERT(MUTEX_HELD(&vscan_svc_mutex));
1244
1245 if (vscan_svc_counts.vsc_node == vs_nodes_max)
1246 return (-1);
1247
1248 for (idx = 1; idx <= vs_nodes_max; idx++) {
1249 if (vscan_svc_nodes[idx].vsn_req == NULL) {
1250 req->vsr_idx = idx;
1251
1252 node = &vscan_svc_nodes[idx];
1253 (void) memset(node, 0, sizeof (vscan_svc_node_t));
1254 node->vsn_req = req;
1255 node->vsn_modified = 1;
1256 node->vsn_result = VS_STATUS_UNDEFINED;
1257 node->vsn_access = VS_ACCESS_UNDEFINED;
1258
1259 ++(vscan_svc_counts.vsc_node);
1260 return (idx);
1261 }
1262 }
1263
1264 return (-1);
1265 }
1266
1267
1268 /*
1269 * vscan_svc_remove_req
1270 */
1271 static void
1272 vscan_svc_remove_req(int idx)
1273 {
1274 ASSERT(MUTEX_HELD(&vscan_svc_mutex));
1275
1276 if (idx != 0) {
1277 (void) memset(&vscan_svc_nodes[idx], 0,
1278 sizeof (vscan_svc_node_t));
1279 --(vscan_svc_counts.vsc_node);
1280 }
1281 }
1282
1283
1284 /*
1285 * vscan_svc_reql_find
1286 */
1287 static vscan_req_t *
1288 vscan_svc_reql_find(vnode_t *vp)
1289 {
1290 vscan_req_t *req;
1291 ASSERT(MUTEX_HELD(&vscan_svc_mutex));
1292
1293 req = list_head(&vscan_svc_reql);
1294
1295 while (req != NULL) {
1296 ASSERT(req->vsr_magic == VS_REQ_MAGIC);
1297 if ((req->vsr_vp == vp) &&
1298 (req->vsr_state != VS_SVC_REQ_COMPLETE))
1299 break;
1300
1301 req = list_next(&vscan_svc_reql, req);
1302 }
1303
1304 return (req);
1305 }
1306
1307
1308 /*
1309 * vscan_svc_reql_insert
1310 */
1311 static vscan_req_t *
1312 vscan_svc_reql_insert(vnode_t *vp)
1313 {
1314 vscan_req_t *req;
1315
1316 ASSERT(MUTEX_HELD(&vscan_svc_mutex));
1317
1318 /* if request already in list then return it */
1319 if ((req = vscan_svc_reql_find(vp)) != NULL)
1320 return (req);
1321
1322 /* if list is full return NULL */
1323 if (vscan_svc_counts.vsc_reql == vs_reqs_max)
1324 return (NULL);
1325
1326 /* create a new request and insert into list */
1327 VN_HOLD(vp);
1328
1329 req = kmem_zalloc(sizeof (vscan_req_t), KM_SLEEP);
1330
1331 req->vsr_magic = VS_REQ_MAGIC;
1332 if (vscan_svc_seqnum == UINT32_MAX)
1333 vscan_svc_seqnum = 0;
1334 req->vsr_seqnum = ++vscan_svc_seqnum;
1335 req->vsr_vp = vp;
1336 req->vsr_refcnt = 1; /* decremented in vscan_svc_scan_complete */
1337 req->vsr_state = VS_SVC_REQ_INIT;
1338 cv_init(&(req->vsr_cv), NULL, CV_DEFAULT, NULL);
1339
1340 list_insert_tail(&vscan_svc_reql, req);
1341 if (vscan_svc_reql_next == NULL)
1342 vscan_svc_reql_next = req;
1343
1344 ++(vscan_svc_counts.vsc_reql);
1345
1346 /* wake reql handler thread */
1347 cv_signal(&vscan_svc_reql_cv);
1348
1349 return (req);
1350 }
1351
1352
1353 /*
1354 * vscan_svc_reql_remove
1355 */
1356 static void
1357 vscan_svc_reql_remove(vscan_req_t *req)
1358 {
1359 ASSERT(MUTEX_HELD(&vscan_svc_mutex));
1360 ASSERT(req->vsr_magic == VS_REQ_MAGIC);
1361
1362 if (vscan_svc_reql_next == req)
1363 vscan_svc_reql_next = list_next(&vscan_svc_reql, req);
1364
1365 list_remove(&vscan_svc_reql, req);
1366 cv_destroy(&(req->vsr_cv));
1367 VN_RELE(req->vsr_vp);
1368
1369 kmem_free(req, sizeof (vscan_req_t));
1370 --(vscan_svc_counts.vsc_reql);
1371 }