xref: /illumos-gate/usr/src/uts/common/io/vscan/vscan_svc.c (revision bea83d026ee1bd1b2a2419e1d0232f107a5d7d9b)
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/cmn_err.h>
37 #include <sys/vnode.h>
38 #include <fs/fs_subr.h>
39 #include <sys/types.h>
40 #include <sys/file.h>
41 #include <sys/disp.h>
42 #include <sys/sdt.h>
43 #include <sys/cred.h>
44 #include <sys/vscan.h>
45 
46 #define	VS_TASKQ_NUM_THREADS	VS_DRV_MAX_FILES
47 #define	VS_EXT_RECURSE_DEPTH	8
48 #define	tolower(C)	(((C) >= 'A' && (C) <= 'Z') ? (C) - 'A' + 'a' : (C))
49 
50 /* represents request received from filesystem - currently only use vp */
51 typedef struct vscan_fs_req {
52 	vnode_t *vsr_vp;
53 } vscan_fs_req_t;
54 
55 /*
56  * vscan_svc_files - table of files being scanned
57  *
58  * The index into this table is passed in the door call to
59  * vscand. vscand uses the idx to determine which minor node
60  * to open to read the file data. Within the kernel driver
61  * the minor device number can thus be used to identify the
62  * table index to get the appropriate vnode.
63  *
64  * Instance 0 is reserved for the daemon/driver control
65  * interface: enable/configure/disable
66  */
67 typedef struct vscan_file {
68 	vscan_fs_req_t vsf_req;
69 	uint32_t vsf_wait_count;
70 	kcondvar_t vsf_cv; /* wait for in progress scan */
71 	uint8_t vsf_quarantined;
72 	uint8_t vsf_modified;
73 	uint64_t vsf_size;
74 	timestruc_t vsf_mtime;
75 	vs_scanstamp_t vsf_scanstamp;
76 	uint32_t vsf_result;
77 	uint32_t vsf_access;
78 } vscan_file_t;
79 
80 static vscan_file_t vscan_svc_files[VS_DRV_MAX_FILES + 1];
81 static kcondvar_t vscan_svc_cv; /* wait for slot in vscan_svc_files */
82 static int vscan_svc_wait_count = 0; /* # waiting for slot in vscan_svc_files */
83 static int vscan_svc_req_count = 0; /* # scan requests */
84 
85 static taskq_t *vscan_svc_taskq = NULL;
86 static boolean_t vscan_svc_enabled = B_FALSE;
87 
88 /*
89  * vscan_svc_mutex protects the data pertaining to scan requests:
90  * file table - vscan_svc_files
91  * counts - vscan_svc_wait_count, vscan_svc_req_count
92  */
93 static kmutex_t vscan_svc_mutex;
94 
95 /*
96  * vscan_svc_cfg_mutex protects the configuration data:
97  * vscan_svc_config, vscan_svc_types
98  */
99 static kmutex_t vscan_svc_cfg_mutex;
100 
101 /* configuration data - for virus scan exemption */
102 static vs_config_t vscan_svc_config;
103 static char *vscan_svc_types[VS_TYPES_MAX];
104 
105 /* local functions */
106 int vscan_svc_scan_file(vnode_t *, cred_t *, int);
107 void vscan_svc_taskq_callback(void *);
108 static int vscan_svc_exempt_file(vnode_t *, boolean_t *);
109 static int vscan_svc_exempt_filetype(char *);
110 static int vscan_svc_match_ext(char *, char *, int);
111 static int vscan_svc_do_scan(vscan_fs_req_t *);
112 static int vscan_svc_wait_for_scan(vnode_t *);
113 static int vscan_svc_insert_file(vscan_fs_req_t *);
114 static void vscan_svc_release_file(int);
115 static int vscan_svc_find_slot(void);
116 static void vscan_svc_process_scan_result(int);
117 static void vscan_svc_notify_scan_complete(int);
118 static int vscan_svc_getattr(int);
119 static int vscan_svc_setattr(int, int);
120 
121 static vs_scan_req_t *vscan_svc_populate_req(int);
122 static void vscan_svc_parse_rsp(int, vs_scan_req_t *);
123 
124 
125 /*
126  * vscan_svc_init
127  */
128 int
129 vscan_svc_init()
130 {
131 	mutex_init(&vscan_svc_mutex, NULL, MUTEX_DRIVER, NULL);
132 	mutex_init(&vscan_svc_cfg_mutex, NULL, MUTEX_DRIVER, NULL);
133 	(void) memset(&vscan_svc_files, 0, sizeof (vscan_svc_files));
134 	cv_init(&vscan_svc_cv, NULL, CV_DEFAULT, NULL);
135 
136 	return (0);
137 }
138 
139 /*
140  * vscan_svc_fini
141  */
142 void
143 vscan_svc_fini()
144 {
145 	ASSERT(vscan_svc_enabled == B_FALSE);
146 	ASSERT(vscan_svc_in_use() == B_FALSE);
147 
148 	cv_destroy(&vscan_svc_cv);
149 	mutex_destroy(&vscan_svc_mutex);
150 	mutex_destroy(&vscan_svc_cfg_mutex);
151 }
152 
153 /*
154  * vscan_svc_enable
155  */
156 void
157 vscan_svc_enable(void)
158 {
159 	mutex_enter(&vscan_svc_mutex);
160 	vscan_svc_enabled = B_TRUE;
161 
162 	if (vscan_svc_taskq == NULL) {
163 		if ((vscan_svc_taskq = taskq_create("vscan",
164 		    VS_TASKQ_NUM_THREADS, MINCLSYSPRI, 1,
165 		    INT_MAX, TASKQ_DYNAMIC)) == NULL) {
166 			cmn_err(CE_WARN, "All scan requests "
167 			    "will be processed synchronously");
168 		}
169 	}
170 
171 	fs_vscan_register(vscan_svc_scan_file);
172 	mutex_exit(&vscan_svc_mutex);
173 }
174 
175 
176 /*
177  * vscan_svc_disable
178  */
179 void
180 vscan_svc_disable(void)
181 {
182 	mutex_enter(&vscan_svc_mutex);
183 	vscan_svc_enabled = B_FALSE;
184 	fs_vscan_register(NULL);
185 
186 	if (vscan_svc_taskq) {
187 		taskq_destroy(vscan_svc_taskq);
188 		vscan_svc_taskq = NULL;
189 	}
190 	mutex_exit(&vscan_svc_mutex);
191 }
192 
193 
194 
195 /*
196  * vscan_svc_is_enabled
197  */
198 boolean_t
199 vscan_svc_is_enabled()
200 {
201 	return (vscan_svc_enabled);
202 }
203 
204 
205 /*
206  * vscan_svc_in_use
207  *
208  * The vscan driver is considered to be in use if it is
209  * enabled or if there are in-progress scan requests.
210  * Used to determine whether the driver can be unloaded.
211  */
212 boolean_t
213 vscan_svc_in_use()
214 {
215 	boolean_t rc;
216 
217 	mutex_enter(&vscan_svc_mutex);
218 	rc = (vscan_svc_enabled == B_TRUE) || (vscan_svc_req_count > 0);
219 	mutex_exit(&vscan_svc_mutex);
220 
221 	return (rc);
222 }
223 
224 /*
225  * vscan_svc_get_vnode
226  *
227  * Get the file vnode indexed by idx.
228  * Returns NULL if idx not valid.
229  */
230 vnode_t *
231 vscan_svc_get_vnode(int idx)
232 {
233 	ASSERT(idx > 0);
234 	ASSERT(idx <= VS_DRV_MAX_FILES);
235 
236 	if ((idx <= 0) || (idx > VS_DRV_MAX_FILES))
237 		return (NULL);
238 	else
239 		return (vscan_svc_files[idx].vsf_req.vsr_vp);
240 }
241 
242 
243 /*
244  * vscan_svc_scan_file
245  *
246  * This function is the entry point for the file system to
247  * request that a file be virus scanned.
248  *
249  * Asynchronous requests:
250  * If an async scan request cannot be queued it is discarded.
251  *   By definition the caller of an async request is not dependent
252  *   on the outcome of the result. Although the file will thus
253  *   not be scanned at this time, it will be scanned
254  *   (synchronously) on subsequent access.
255  *   This scenario should not occur during normal operation.
256  *
257  * Before queuing an async request do VN_HOLD(vp). VN_RELE(vp)
258  *   will be done when the scan completes or if the request
259  *   couldn't be queued.
260  *
261  * The vscan_fs_req_t, allocated to hold the request information
262  * passed from the fs, will be free'd when the scan completes.
263  */
264 int
265 vscan_svc_scan_file(vnode_t *vp, cred_t *cr, int async)
266 {
267 	int rc = 0;
268 	vscan_fs_req_t *req;
269 	boolean_t allow;
270 
271 	mutex_enter(&vscan_svc_mutex);
272 
273 	if ((vp == NULL) || (vp->v_path == NULL) || cr == NULL) {
274 		mutex_exit(&vscan_svc_mutex);
275 		return (0);
276 	}
277 
278 	DTRACE_PROBE2(vscan__scan__file, char *, vp->v_path, int, async);
279 
280 	/* check if size or type exempts file from scanning */
281 	if (vscan_svc_exempt_file(vp, &allow)) {
282 		mutex_exit(&vscan_svc_mutex);
283 		if ((allow == B_TRUE) || (async != 0))
284 			return (0);
285 
286 		return (EACCES);
287 	}
288 
289 	vscan_svc_req_count++;
290 	mutex_exit(&vscan_svc_mutex);
291 
292 	req = kmem_zalloc(sizeof (vscan_fs_req_t), KM_SLEEP);
293 	req->vsr_vp = vp;
294 
295 	if (async) {
296 		VN_HOLD(vp);
297 		if (vscan_svc_taskq &&
298 		    taskq_dispatch(vscan_svc_taskq, vscan_svc_taskq_callback,
299 		    (void *)req, TQ_SLEEP)) {
300 			return (0);
301 		} else {
302 			VN_RELE(vp);
303 			kmem_free(req, sizeof (vscan_fs_req_t));
304 		}
305 	} else {
306 		rc = vscan_svc_do_scan(req);
307 		kmem_free(req, sizeof (vscan_fs_req_t));
308 	}
309 
310 	mutex_enter(&vscan_svc_mutex);
311 	vscan_svc_req_count--;
312 	mutex_exit(&vscan_svc_mutex);
313 
314 	return (rc);
315 }
316 
317 
318 /*
319  * vscan_svc_taskq_callback
320  *
321  * Callback function for async scan requests
322  */
323 void
324 vscan_svc_taskq_callback(void *data)
325 {
326 	vscan_fs_req_t *req = (vscan_fs_req_t *)data;
327 
328 	(void) vscan_svc_do_scan(req);
329 	VN_RELE(req->vsr_vp); /* VN_HOLD done before request queued */
330 	kmem_free(req, sizeof (vscan_fs_req_t));
331 
332 	mutex_enter(&vscan_svc_mutex);
333 	vscan_svc_req_count--;
334 	mutex_exit(&vscan_svc_mutex);
335 }
336 
337 
338 /*
339  * vscan_svc_do_scan
340  *
341  * Should never be called directly. Invoke via vscan_svc_scan_file()
342  * If scan is in progress wait for it to complete, otherwise
343  * initiate door call to scan the file.
344  */
345 static int
346 vscan_svc_do_scan(vscan_fs_req_t *req)
347 {
348 	int rc = -1, idx;
349 	vs_scan_req_t *scan_req;
350 	vscan_file_t *svc_file;
351 
352 	mutex_enter(&vscan_svc_mutex);
353 
354 	/*
355 	 * if a scan is in progress on the files vscan_svc_wait_for_scan will
356 	 * wait for it to complete and return the idx of the scan request.
357 	 * Otherwise it will return -1 and we will initiate a scan here.
358 	 */
359 	if ((idx = vscan_svc_wait_for_scan(req->vsr_vp)) != -1) {
360 		svc_file = &vscan_svc_files[idx];
361 	} else {
362 		/* insert the scan request into vscan_svc_files */
363 		idx = vscan_svc_insert_file(req);
364 		svc_file = &vscan_svc_files[idx];
365 
366 		if (vscan_svc_enabled) {
367 			if (vscan_svc_getattr(idx) == 0) {
368 				/* valid scan_req ptr guaranteed */
369 				scan_req = vscan_svc_populate_req(idx);
370 				mutex_exit(&vscan_svc_mutex);
371 				if (vscan_drv_create_node(idx) == B_TRUE)
372 					rc = vscan_door_scan_file(scan_req);
373 				mutex_enter(&vscan_svc_mutex);
374 				if (rc == 0)
375 					vscan_svc_parse_rsp(idx, scan_req);
376 				kmem_free(scan_req, sizeof (vs_scan_req_t));
377 
378 				/* process scan result */
379 				vscan_svc_process_scan_result(idx);
380 				DTRACE_PROBE2(vscan__result, int,
381 				    svc_file->vsf_result, int,
382 				    svc_file->vsf_access);
383 			} else {
384 				/* if getattr fails: log error, deny access */
385 				cmn_err(CE_WARN, "Can't access xattr for %s\n",
386 				    svc_file->vsf_req.vsr_vp->v_path);
387 				svc_file->vsf_access = VS_ACCESS_DENY;
388 			}
389 		} else {
390 			/* if vscan not enabled (shutting down), allow ACCESS */
391 			svc_file->vsf_access = VS_ACCESS_ALLOW;
392 		}
393 	}
394 
395 	/* When a scan completes the result is saved in vscan_svc_files */
396 	rc = (svc_file->vsf_access == VS_ACCESS_ALLOW) ? 0 : EACCES;
397 
398 	/* wake threads waiting for result, or for a slot in vscan_svc_files */
399 	vscan_svc_notify_scan_complete(idx);
400 
401 	/* remove the entry from vscan_svc_files if nobody else is waiting */
402 	vscan_svc_release_file(idx);
403 
404 	mutex_exit(&vscan_svc_mutex);
405 
406 	return (rc);
407 }
408 
409 
410 /*
411  * vscan_svc_process_scan_result
412  *
413  * Sets vsf_access and updates file attributes based on vsf_result,
414  * as follows:
415  *
416  * VS_STATUS_INFECTED
417  *  deny access, set quarantine attribute, clear scanstamp
418  * VS_STATUS_CLEAN
419  *  allow access, set scanstamp,
420  *  if file not modified since scan initiated, clear modified attribute
421  * VS_STATUS_NO_SCAN
422  *  deny access if file quarantined, otherwise allow access
423  * VS_STATUS_UNDEFINED, VS_STATUS_ERROR
424  *  deny access if file quarantined, modified or no scanstamp
425  *  otherwise, allow access
426  */
427 static void
428 vscan_svc_process_scan_result(int idx)
429 {
430 	struct vattr attr;
431 	vnode_t *vp;
432 	timestruc_t *mtime;
433 	vscan_file_t *svc_file;
434 
435 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
436 
437 	svc_file = &vscan_svc_files[idx];
438 
439 	switch (svc_file->vsf_result) {
440 	case VS_STATUS_INFECTED:
441 		svc_file->vsf_access = VS_ACCESS_DENY;
442 		svc_file->vsf_quarantined = 1;
443 		svc_file->vsf_scanstamp[0] = '\0';
444 		(void) vscan_svc_setattr(idx,
445 		    XAT_AV_QUARANTINED | XAT_AV_SCANSTAMP);
446 		return;
447 
448 	case VS_STATUS_CLEAN:
449 		svc_file->vsf_access = VS_ACCESS_ALLOW;
450 
451 		/* if mtime has changed, don't clear the modified attribute */
452 		vp = svc_file->vsf_req.vsr_vp;
453 		mtime = &(svc_file->vsf_mtime);
454 		attr.va_mask = AT_MTIME;
455 		if ((VOP_GETATTR(vp, &attr, 0, kcred, NULL) != 0) ||
456 		    (mtime->tv_sec != attr.va_mtime.tv_sec) ||
457 		    (mtime->tv_nsec != attr.va_mtime.tv_nsec)) {
458 			DTRACE_PROBE1(vscan__mtime__changed, vscan_file_t *,
459 			    svc_file);
460 			(void) vscan_svc_setattr(idx, XAT_AV_SCANSTAMP);
461 			return;
462 		}
463 
464 		svc_file->vsf_modified = 0;
465 		(void) vscan_svc_setattr(idx,
466 		    XAT_AV_SCANSTAMP | XAT_AV_MODIFIED);
467 		return;
468 
469 	case VS_STATUS_NO_SCAN:
470 		if (svc_file->vsf_quarantined)
471 			svc_file->vsf_access = VS_ACCESS_DENY;
472 		else
473 			svc_file->vsf_access = VS_ACCESS_ALLOW;
474 		return;
475 
476 	case VS_STATUS_ERROR:
477 	case VS_STATUS_UNDEFINED:
478 	default:
479 		if ((svc_file->vsf_quarantined) ||
480 		    (svc_file->vsf_modified) ||
481 		    (svc_file->vsf_scanstamp[0] == '\0'))
482 			svc_file->vsf_access = VS_ACCESS_DENY;
483 		else
484 			svc_file->vsf_access = VS_ACCESS_ALLOW;
485 		return;
486 	}
487 }
488 
489 
490 /*
491  * vscan_svc_wait_for_scan
492  *
493  * Search for vp in vscan_svc_files. If vp already exists in
494  * vscan_svc_files scan is already in progress on file so wait
495  * for the inprogress scan to complete.
496  *
497  * Returns: idx of file waited for
498  *          -1 if file not already scanning
499  */
500 static int
501 vscan_svc_wait_for_scan(vnode_t *vp)
502 {
503 	int idx;
504 	vscan_file_t *svc_file;
505 
506 	ASSERT(vp);
507 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
508 
509 	for (idx = 1; idx <= VS_DRV_MAX_FILES; idx++) {
510 		if (vscan_svc_files[idx].vsf_req.vsr_vp == vp)
511 			break;
512 	}
513 
514 	/* file not found in table thus not currently being scanned */
515 	if (idx > VS_DRV_MAX_FILES)
516 		return (-1);
517 
518 	/* file found - wait for scan to complete */
519 	svc_file = &vscan_svc_files[idx];
520 	svc_file->vsf_wait_count++;
521 
522 	DTRACE_PROBE2(vscan__wait__scan, vscan_file_t *, svc_file, int, idx);
523 
524 	while (svc_file->vsf_access == VS_ACCESS_UNDEFINED)
525 		cv_wait(&(svc_file->vsf_cv), &vscan_svc_mutex);
526 
527 	svc_file->vsf_wait_count--;
528 
529 	return (idx);
530 }
531 
532 
533 /*
534  * vscan_svc_find_slot
535  *
536  * Find empty slot in vscan_svc_files table.
537  *
538  * Returns idx of slot, or -1 if not found
539  */
540 static int
541 vscan_svc_find_slot(void)
542 {
543 	int idx;
544 
545 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
546 	for (idx = 1; idx <= VS_DRV_MAX_FILES; idx++) {
547 		if (vscan_svc_files[idx].vsf_req.vsr_vp == NULL)
548 			return (idx);
549 	}
550 
551 	return (-1);
552 }
553 
554 
555 /*
556  * vscan_svc_insert_file
557  *
558  * Find the next available flot in vscan_svc_files and
559  * initialize it for the scan request. If no slot is
560  * available, vscan_svc_find_slot will wait for one.
561  *
562  * Returns: idx of scan request in vscan_svc_files table
563  */
564 static int
565 vscan_svc_insert_file(vscan_fs_req_t *req)
566 {
567 	int idx;
568 	vscan_file_t *svc_file;
569 
570 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
571 
572 	while ((idx = vscan_svc_find_slot()) == -1) {
573 		DTRACE_PROBE1(vscan__wait__slot, char *, req->vsr_vp->v_path);
574 		vscan_svc_wait_count++;
575 		cv_wait(&(vscan_svc_cv), &vscan_svc_mutex);
576 		vscan_svc_wait_count--;
577 	}
578 
579 	svc_file = &vscan_svc_files[idx];
580 
581 	(void) memset(svc_file, 0, sizeof (vscan_file_t));
582 	svc_file->vsf_req = *req;
583 	svc_file->vsf_modified = 1;
584 	svc_file->vsf_result = VS_STATUS_UNDEFINED;
585 	svc_file->vsf_access = VS_ACCESS_UNDEFINED;
586 	cv_init(&(svc_file->vsf_cv), NULL, CV_DEFAULT, NULL);
587 
588 	DTRACE_PROBE2(vscan__insert, char *, req->vsr_vp->v_path, int, idx);
589 	return (idx);
590 }
591 
592 
593 /*
594  * vscan_svc_release_file
595  *
596  * Release the file (free the slot in vscan_svc_files)
597  * if no thread is waiting on it.
598  */
599 static void
600 vscan_svc_release_file(int idx)
601 {
602 	vscan_file_t *svc_file;
603 
604 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
605 	svc_file = &vscan_svc_files[idx];
606 
607 	if (svc_file->vsf_wait_count != 0)
608 		return;
609 
610 	DTRACE_PROBE2(vscan__release, char *,
611 	    svc_file->vsf_req.vsr_vp->v_path, int, idx);
612 
613 	cv_destroy(&(svc_file->vsf_cv));
614 	(void) memset(svc_file, 0, sizeof (vscan_file_t));
615 }
616 
617 
618 /*
619  * vscan_svc_populate_req
620  *
621  * Allocate a scan request to be sent to vscand, populating it
622  * from the data in vscan_svc_files[idx].
623  *
624  * Returns: scan request object
625  */
626 static vs_scan_req_t *
627 vscan_svc_populate_req(int idx)
628 {
629 	vs_scan_req_t *scan_req;
630 	vscan_fs_req_t *req;
631 	vscan_file_t *svc_file;
632 
633 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
634 
635 	svc_file = &vscan_svc_files[idx];
636 	req = &(svc_file->vsf_req);
637 	scan_req = kmem_zalloc(sizeof (vs_scan_req_t), KM_SLEEP);
638 
639 	scan_req->vsr_id = idx;
640 	(void) strncpy(scan_req->vsr_path, req->vsr_vp->v_path, MAXPATHLEN);
641 	scan_req->vsr_size = svc_file->vsf_size;
642 	scan_req->vsr_modified = svc_file->vsf_modified;
643 	scan_req->vsr_quarantined = svc_file->vsf_quarantined;
644 	scan_req->vsr_flags = 0;
645 	(void) strncpy(scan_req->vsr_scanstamp,
646 	    svc_file->vsf_scanstamp, sizeof (vs_scanstamp_t));
647 
648 	return (scan_req);
649 }
650 
651 
652 /*
653  * vscan_svc_parse_rsp
654  *
655  * Parse scan response data and save in vscan_svc_files[idx]
656  */
657 static void
658 vscan_svc_parse_rsp(int idx, vs_scan_req_t *scan_req)
659 {
660 	vscan_file_t *svc_file;
661 
662 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
663 
664 	svc_file = &vscan_svc_files[idx];
665 	svc_file->vsf_result = scan_req->vsr_result;
666 	(void) strncpy(svc_file->vsf_scanstamp,
667 	    scan_req->vsr_scanstamp, sizeof (vs_scanstamp_t));
668 }
669 
670 
671 /*
672  * vscan_svc_notify_scan_complete
673  *
674  * signal vscan_svc_files.vsf_cv and vscan_svc_cv to wake
675  * threads waiting for the scan result for the specified
676  * file (vscan_svc_files[idx].vsf_cv) or for a slot in
677  * vscan_svc_files table (vscan_svc_cv)
678  */
679 static void
680 vscan_svc_notify_scan_complete(int idx)
681 {
682 	vscan_file_t *svc_file;
683 
684 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
685 
686 	svc_file = &vscan_svc_files[idx];
687 
688 	/* if someone waiting for result, cv_signal */
689 	if (svc_file->vsf_wait_count > 0)
690 		cv_signal(&(svc_file->vsf_cv));
691 
692 	/* signal vscan_svc_cv if any threads waiting for a slot */
693 	if (vscan_svc_wait_count > 0)
694 		cv_signal(&vscan_svc_cv);
695 }
696 
697 
698 /*
699  * vscan_svc_getattr
700  *
701  * Get the vscan related system attributes, AT_SIZE & AT_MTIME.
702  */
703 static int
704 vscan_svc_getattr(int idx)
705 {
706 	xvattr_t xvattr;
707 	xoptattr_t *xoap = NULL;
708 	vnode_t *vp;
709 	vscan_file_t *svc_file;
710 
711 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
712 
713 	svc_file = &vscan_svc_files[idx];
714 	if ((vp = svc_file->vsf_req.vsr_vp) == NULL)
715 		return (-1);
716 
717 	/* get the attributes */
718 	xva_init(&xvattr); /* sets AT_XVATTR */
719 
720 	xvattr.xva_vattr.va_mask |= AT_SIZE;
721 	xvattr.xva_vattr.va_mask |= AT_MTIME;
722 	XVA_SET_REQ(&xvattr, XAT_AV_MODIFIED);
723 	XVA_SET_REQ(&xvattr, XAT_AV_QUARANTINED);
724 	XVA_SET_REQ(&xvattr, XAT_AV_SCANSTAMP);
725 
726 	if (VOP_GETATTR(vp, (vattr_t *)&xvattr, 0, kcred, NULL) != 0)
727 		return (-1);
728 
729 	if ((xoap = xva_getxoptattr(&xvattr)) == NULL) {
730 		cmn_err(CE_NOTE, "Virus scan request failed; "
731 		    "file system does not support virus scanning");
732 		return (-1);
733 	}
734 
735 	svc_file->vsf_size = xvattr.xva_vattr.va_size;
736 	svc_file->vsf_mtime.tv_sec = xvattr.xva_vattr.va_mtime.tv_sec;
737 	svc_file->vsf_mtime.tv_nsec = xvattr.xva_vattr.va_mtime.tv_nsec;
738 
739 	if (XVA_ISSET_RTN(&xvattr, XAT_AV_MODIFIED) == 0)
740 		return (-1);
741 	svc_file->vsf_modified = xoap->xoa_av_modified;
742 
743 	if (XVA_ISSET_RTN(&xvattr, XAT_AV_QUARANTINED) == 0)
744 		return (-1);
745 	svc_file->vsf_quarantined = xoap->xoa_av_quarantined;
746 
747 	if (XVA_ISSET_RTN(&xvattr, XAT_AV_SCANSTAMP) != 0) {
748 		(void) memcpy(svc_file->vsf_scanstamp,
749 		    xoap->xoa_av_scanstamp, AV_SCANSTAMP_SZ);
750 	}
751 
752 	DTRACE_PROBE1(vscan__getattr, vscan_file_t *, svc_file);
753 	return (0);
754 }
755 
756 
757 /*
758  * vscan_svc_setattr
759  *
760  * Set the vscan related system attributes.
761  */
762 static int
763 vscan_svc_setattr(int idx, int which)
764 {
765 	xvattr_t xvattr;
766 	xoptattr_t *xoap = NULL;
767 	vnode_t *vp;
768 	int len;
769 	vscan_file_t *svc_file;
770 
771 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
772 
773 	svc_file = &vscan_svc_files[idx];
774 	if ((vp = svc_file->vsf_req.vsr_vp) == NULL)
775 		return (-1);
776 
777 	/* update the attributes */
778 	xva_init(&xvattr); /* sets AT_XVATTR */
779 	if ((xoap = xva_getxoptattr(&xvattr)) == NULL)
780 		return (-1);
781 
782 	if (which & XAT_AV_MODIFIED) {
783 		XVA_SET_REQ(&xvattr, XAT_AV_MODIFIED);
784 		xoap->xoa_av_modified = svc_file->vsf_modified;
785 	}
786 
787 	if (which & XAT_AV_QUARANTINED) {
788 		XVA_SET_REQ(&xvattr, XAT_AV_QUARANTINED);
789 		xoap->xoa_av_quarantined = svc_file->vsf_quarantined;
790 	}
791 
792 	if (which & XAT_AV_SCANSTAMP) {
793 		XVA_SET_REQ(&xvattr, XAT_AV_SCANSTAMP);
794 		len = strlen(svc_file->vsf_scanstamp);
795 		(void) memcpy(xoap->xoa_av_scanstamp,
796 		    svc_file->vsf_scanstamp, len);
797 	}
798 
799 	/* if access is denied, set mtime to invalidate client cache */
800 	if (svc_file->vsf_access != VS_ACCESS_ALLOW) {
801 		xvattr.xva_vattr.va_mask |= AT_MTIME;
802 		gethrestime(&xvattr.xva_vattr.va_mtime);
803 	}
804 
805 	if (VOP_SETATTR(vp, (vattr_t *)&xvattr, 0, kcred, NULL) != 0)
806 		return (-1);
807 
808 	DTRACE_PROBE2(vscan__setattr,
809 	    vscan_file_t *, svc_file, int, which);
810 
811 	return (0);
812 }
813 
814 
815 /*
816  * vscan_svc_configure
817  *
818  * store configuration in vscan_svc_config
819  * set up vscan_svc_types array of pointers into
820  * vscan_svc_config.vsc_types for efficient searching
821  */
822 int
823 vscan_svc_configure(vs_config_t *conf)
824 {
825 	int count = 0;
826 	char *p, *beg, *end;
827 
828 	mutex_enter(&vscan_svc_cfg_mutex);
829 
830 	vscan_svc_config = *conf;
831 
832 	(void) memset(vscan_svc_types, 0, sizeof (vscan_svc_types));
833 
834 	beg = vscan_svc_config.vsc_types;
835 	end = beg + vscan_svc_config.vsc_types_len;
836 
837 	for (p = beg; p < end; p += strlen(p) + 1) {
838 		if (count >= VS_TYPES_MAX) {
839 			mutex_exit(&vscan_svc_mutex);
840 			return (-1);
841 		}
842 
843 		vscan_svc_types[count] = p;
844 		++count;
845 	}
846 
847 	mutex_exit(&vscan_svc_cfg_mutex);
848 	return (0);
849 }
850 
851 
852 /*
853  * vscan_svc_exempt_file
854  *
855  * check if a file's size or type exempts it from virus scanning
856  *
857  * If the file is exempt from virus scanning, allow will be set
858  * to define whether files access should be allowed (B_TRUE) or
859  * denied (B_FALSE)
860  *
861  * Returns: 1 exempt
862  *          0 scan required
863  */
864 static int
865 vscan_svc_exempt_file(vnode_t *vp, boolean_t *allow)
866 {
867 	struct vattr attr;
868 
869 	ASSERT(vp != NULL);
870 	ASSERT(vp->v_path != NULL);
871 
872 	attr.va_mask = AT_SIZE;
873 
874 	if (VOP_GETATTR(vp, &attr, 0, kcred, NULL) != 0) {
875 		*allow = B_FALSE;
876 		return (0);
877 	}
878 
879 	mutex_enter(&vscan_svc_cfg_mutex);
880 
881 	if (attr.va_size > vscan_svc_config.vsc_max_size) {
882 		DTRACE_PROBE2(vscan__exempt__filesize, char *,
883 		    vp->v_path, int, *allow);
884 
885 		*allow = (vscan_svc_config.vsc_allow) ? B_TRUE : B_FALSE;
886 		mutex_exit(&vscan_svc_cfg_mutex);
887 		return (1);
888 	}
889 
890 	if (vscan_svc_exempt_filetype(vp->v_path)) {
891 		DTRACE_PROBE1(vscan__exempt__filetype, char *, vp->v_path);
892 		*allow = B_TRUE;
893 		mutex_exit(&vscan_svc_cfg_mutex);
894 		return (1);
895 	}
896 
897 	mutex_exit(&vscan_svc_cfg_mutex);
898 	return (0);
899 }
900 
901 
902 /*
903  * vscan_svc_exempt_filetype
904  *
905  * Each entry in vscan_svc_types includes a rule indicator (+,-)
906  * followed by the match string for file types to which the rule
907  * applies. Look for first match of file type in vscan_svc_types
908  * and return 1 (exempt) if the indicator is '-', and 0 (not exempt)
909  * if the indicator is '+'.
910  * If vscan_svc_match_ext fails, or no match is found, return 0
911  * (not exempt)
912  *
913  * Returns 1: exempt, 0: not exempt
914  */
915 static int
916 vscan_svc_exempt_filetype(char *filepath)
917 {
918 	int i, rc, exempt = 0;
919 	char *filename, *ext;
920 
921 	ASSERT(MUTEX_HELD(&vscan_svc_cfg_mutex));
922 
923 	if ((filename = strrchr(filepath, '/')) == 0)
924 		filename = filepath;
925 	else
926 		filename++;
927 
928 	if ((ext = strrchr(filename, '.')) == NULL)
929 		ext = "";
930 	else
931 		ext++;
932 
933 
934 	for (i = 0; i < VS_TYPES_MAX; i ++) {
935 		if (vscan_svc_types[i] == 0)
936 			break;
937 
938 		rc = vscan_svc_match_ext(vscan_svc_types[i] + 1, ext, 1);
939 		if (rc == -1)
940 			break;
941 		if (rc > 0) {
942 			DTRACE_PROBE2(vscan__type__match, char *, ext,
943 			    char *, vscan_svc_types[i]);
944 			exempt = (vscan_svc_types[i][0] == '-');
945 			break;
946 		}
947 	}
948 
949 	return (exempt);
950 }
951 
952 
953 /*
954  *  vscan_svc_match_ext
955  *
956  * Performs a case-insensitive match for two strings.  The first string
957  * argument can contain the wildcard characters '?' and '*'
958  *
959  * Returns: 0 no match
960  *          1 match
961  *         -1 recursion error
962  */
963 static int
964 vscan_svc_match_ext(char *patn, char *str, int depth)
965 {
966 	int c1, c2;
967 	if (depth > VS_EXT_RECURSE_DEPTH)
968 		return (-1);
969 
970 	for (;;) {
971 		switch (*patn) {
972 		case 0:
973 			return (*str == 0);
974 
975 		case '?':
976 			if (*str != 0) {
977 				str++;
978 				patn++;
979 				continue;
980 			}
981 			return (0);
982 
983 		case '*':
984 			patn++;
985 			if (*patn == 0)
986 				return (1);
987 
988 			while (*str) {
989 				if (vscan_svc_match_ext(patn, str, depth + 1))
990 					return (1);
991 				str++;
992 			}
993 			return (0);
994 
995 		default:
996 			if (*str != *patn) {
997 				c1 = *str;
998 				c2 = *patn;
999 
1000 				c1 = tolower(c1);
1001 				c2 = tolower(c2);
1002 				if (c1 != c2)
1003 					return (0);
1004 			}
1005 			str++;
1006 			patn++;
1007 			continue;
1008 		}
1009 	}
1010 	/* NOT REACHED */
1011 }
1012