vscan_svc.c revision 53c110294d8b1410cabc201a52f94b03ae2ef448
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#define VS_TASKQ_NUM_THREADS VS_DRV_MAX_FILES
#define VS_EXT_RECURSE_DEPTH 8
/* represents request received from filesystem - currently only use vp */
typedef struct vscan_fs_req {
/*
* vscan_svc_files - table of files being scanned
*
* The index into this table is passed in the door call to
* vscand. vscand uses the idx to determine which minor node
* to open to read the file data. Within the kernel driver
* the minor device number can thus be used to identify the
* table index to get the appropriate vnode.
*
*/
typedef struct vscan_file {
} vscan_file_t;
static int vscan_svc_wait_count = 0; /* # waiting for slot in vscan_svc_files */
static int vscan_svc_req_count = 0; /* # scan requests */
/*
* vscan_svc_mutex protects the data pertaining to scan requests:
* file table - vscan_svc_files
* counts - vscan_svc_wait_count, vscan_svc_req_count
*/
static kmutex_t vscan_svc_mutex;
/*
* vscan_svc_cfg_mutex protects the configuration data:
* vscan_svc_config, vscan_svc_types
*/
static kmutex_t vscan_svc_cfg_mutex;
/* configuration data - for virus scan exemption */
static vs_config_t vscan_svc_config;
static char *vscan_svc_types[VS_TYPES_MAX];
/* local functions */
void vscan_svc_taskq_callback(void *);
static int vscan_svc_exempt_filetype(char *);
static int vscan_svc_match_ext(char *, char *, int);
static int vscan_svc_do_scan(vscan_fs_req_t *);
static int vscan_svc_wait_for_scan(vnode_t *);
static int vscan_svc_insert_file(vscan_fs_req_t *);
static void vscan_svc_release_file(int);
static int vscan_svc_find_slot(void);
static void vscan_svc_process_scan_result(int);
static void vscan_svc_notify_scan_complete(int);
static int vscan_svc_getattr(int);
static int vscan_svc_setattr(int, int);
static vs_scan_req_t *vscan_svc_populate_req(int);
static void vscan_svc_parse_rsp(int, vs_scan_req_t *);
/*
* vscan_svc_init
*/
int
{
return (0);
}
/*
* vscan_svc_fini
*/
void
{
}
/*
* vscan_svc_enable
*/
void
vscan_svc_enable(void)
{
if (vscan_svc_taskq == NULL) {
"will be processed synchronously");
}
}
}
/*
* vscan_svc_disable
*/
void
vscan_svc_disable(void)
{
if (vscan_svc_taskq) {
}
}
/*
* vscan_svc_is_enabled
*/
{
return (vscan_svc_enabled);
}
/*
* vscan_svc_in_use
*
* The vscan driver is considered to be in use if it is
* enabled or if there are in-progress scan requests.
* Used to determine whether the driver can be unloaded.
*/
{
return (rc);
}
/*
* vscan_svc_get_vnode
*
* Get the file vnode indexed by idx.
* Returns NULL if idx not valid.
*/
vnode_t *
vscan_svc_get_vnode(int idx)
{
return (NULL);
else
}
/*
* vscan_svc_scan_file
*
* This function is the entry point for the file system to
* request that a file be virus scanned.
*
* Asynchronous requests:
* If an async scan request cannot be queued it is discarded.
* By definition the caller of an async request is not dependent
* on the outcome of the result. Although the file will thus
* not be scanned at this time, it will be scanned
* (synchronously) on subsequent access.
* This scenario should not occur during normal operation.
*
* Before queuing an async request do VN_HOLD(vp). VN_RELE(vp)
* will be done when the scan completes or if the request
* couldn't be queued.
*
* The vscan_fs_req_t, allocated to hold the request information
* passed from the fs, will be free'd when the scan completes.
*/
int
{
int rc = 0;
return (0);
}
/* check if size or type exempts file from scanning */
return (0);
return (EACCES);
}
if (async) {
if (vscan_svc_taskq &&
return (0);
} else {
}
} else {
}
return (rc);
}
/*
* vscan_svc_taskq_callback
*
* Callback function for async scan requests
*/
void
vscan_svc_taskq_callback(void *data)
{
(void) vscan_svc_do_scan(req);
}
/*
* vscan_svc_do_scan
*
* Should never be called directly. Invoke via vscan_svc_scan_file()
* If scan is in progress wait for it to complete, otherwise
* initiate door call to scan the file.
*/
static int
{
/*
* if a scan is in progress on the files vscan_svc_wait_for_scan will
* wait for it to complete and return the idx of the scan request.
* Otherwise it will return -1 and we will initiate a scan here.
*/
} else {
/* insert the scan request into vscan_svc_files */
if (vscan_svc_enabled) {
if (vscan_svc_getattr(idx) == 0) {
/* valid scan_req ptr guaranteed */
if (rc == 0)
/* process scan result */
DTRACE_PROBE2(vscan__result, int,
svc_file->vsf_result, int,
} else {
/* if getattr fails: log error, deny access */
}
} else {
/* if vscan not enabled (shutting down), allow ACCESS */
}
}
/* When a scan completes the result is saved in vscan_svc_files */
/* wake threads waiting for result, or for a slot in vscan_svc_files */
/* remove the entry from vscan_svc_files if nobody else is waiting */
return (rc);
}
/*
* vscan_svc_process_scan_result
*
* Sets vsf_access and updates file attributes based on vsf_result,
* as follows:
*
* VS_STATUS_INFECTED
* deny access, set quarantine attribute, clear scanstamp
* VS_STATUS_CLEAN
* allow access, set scanstamp,
* if file not modified since scan initiated, clear modified attribute
* VS_STATUS_NO_SCAN
* deny access if file quarantined, otherwise allow access
* VS_STATUS_UNDEFINED, VS_STATUS_ERROR
* deny access if file quarantined, modified or no scanstamp
* otherwise, allow access
*/
static void
{
switch (svc_file->vsf_result) {
case VS_STATUS_INFECTED:
(void) vscan_svc_setattr(idx,
return;
case VS_STATUS_CLEAN:
/* if mtime has changed, don't clear the modified attribute */
svc_file);
return;
}
svc_file->vsf_modified = 0;
(void) vscan_svc_setattr(idx,
return;
case VS_STATUS_NO_SCAN:
if (svc_file->vsf_quarantined)
else
return;
case VS_STATUS_ERROR:
case VS_STATUS_UNDEFINED:
default:
if ((svc_file->vsf_quarantined) ||
(svc_file->vsf_modified) ||
else
return;
}
}
/*
* vscan_svc_wait_for_scan
*
* Search for vp in vscan_svc_files. If vp already exists in
* vscan_svc_files scan is already in progress on file so wait
* for the inprogress scan to complete.
*
* Returns: idx of file waited for
* -1 if file not already scanning
*/
static int
{
int idx;
break;
}
/* file not found in table thus not currently being scanned */
if (idx > VS_DRV_MAX_FILES)
return (-1);
/* file found - wait for scan to complete */
return (idx);
}
/*
* vscan_svc_find_slot
*
* Find empty slot in vscan_svc_files table.
*
* Returns idx of slot, or -1 if not found
*/
static int
vscan_svc_find_slot(void)
{
int idx;
return (idx);
}
return (-1);
}
/*
* vscan_svc_insert_file
*
* Find the next available flot in vscan_svc_files and
* initialize it for the scan request. If no slot is
* available, vscan_svc_find_slot will wait for one.
*
* Returns: idx of scan request in vscan_svc_files table
*/
static int
{
int idx;
}
return (idx);
}
/*
* vscan_svc_release_file
*
* Release the file (free the slot in vscan_svc_files)
* if no thread is waiting on it.
*/
static void
{
if (svc_file->vsf_wait_count != 0)
return;
DTRACE_PROBE2(vscan__release, char *,
}
/*
* vscan_svc_populate_req
*
* Allocate a scan request to be sent to vscand, populating it
* from the data in vscan_svc_files[idx].
*
* Returns: scan request object
*/
static vs_scan_req_t *
{
return (scan_req);
}
/*
* vscan_svc_parse_rsp
*
* Parse scan response data and save in vscan_svc_files[idx]
*/
static void
{
}
/*
* vscan_svc_notify_scan_complete
*
* signal vscan_svc_files.vsf_cv and vscan_svc_cv to wake
* threads waiting for the scan result for the specified
* file (vscan_svc_files[idx].vsf_cv) or for a slot in
* vscan_svc_files table (vscan_svc_cv)
*/
static void
{
/* if someone waiting for result, cv_signal */
if (svc_file->vsf_wait_count > 0)
/* signal vscan_svc_cv if any threads waiting for a slot */
if (vscan_svc_wait_count > 0)
}
/*
* vscan_svc_getattr
*
* Get the vscan related system attributes, AT_SIZE & AT_MTIME.
*/
static int
vscan_svc_getattr(int idx)
{
return (-1);
/* get the attributes */
return (-1);
"file system does not support virus scanning");
return (-1);
}
return (-1);
return (-1);
}
return (0);
}
/*
* vscan_svc_setattr
*
* Set the vscan related system attributes.
*/
static int
{
int len;
return (-1);
/* update the attributes */
return (-1);
if (which & XAT_AV_MODIFIED) {
}
if (which & XAT_AV_QUARANTINED) {
}
if (which & XAT_AV_SCANSTAMP) {
}
/* if access is denied, set mtime to invalidate client cache */
}
return (-1);
return (0);
}
/*
* vscan_svc_configure
*
* store configuration in vscan_svc_config
* set up vscan_svc_types array of pointers into
* vscan_svc_config.vsc_types for efficient searching
*/
int
{
int count = 0;
vscan_svc_config = *conf;
if (count >= VS_TYPES_MAX) {
return (-1);
}
vscan_svc_types[count] = p;
++count;
}
return (0);
}
/*
* vscan_svc_exempt_file
*
* check if a file's size or type exempts it from virus scanning
*
* If the file is exempt from virus scanning, allow will be set
* to define whether files access should be allowed (B_TRUE) or
* denied (B_FALSE)
*
* Returns: 1 exempt
* 0 scan required
*/
static int
{
return (0);
}
DTRACE_PROBE2(vscan__exempt__filesize, char *,
return (1);
}
return (1);
}
return (0);
}
/*
* vscan_svc_exempt_filetype
*
* Each entry in vscan_svc_types includes a rule indicator (+,-)
* followed by the match string for file types to which the rule
* applies. Look for first match of file type in vscan_svc_types
* and return 1 (exempt) if the indicator is '-', and 0 (not exempt)
* if the indicator is '+'.
* If vscan_svc_match_ext fails, or no match is found, return 0
* (not exempt)
*
* Returns 1: exempt, 0: not exempt
*/
static int
{
else
filename++;
ext = "";
else
ext++;
for (i = 0; i < VS_TYPES_MAX; i ++) {
if (vscan_svc_types[i] == 0)
break;
if (rc == -1)
break;
if (rc > 0) {
char *, vscan_svc_types[i]);
break;
}
}
return (exempt);
}
/*
* vscan_svc_match_ext
*
* Performs a case-insensitive match for two strings. The first string
* argument can contain the wildcard characters '?' and '*'
*
* Returns: 0 no match
* 1 match
* -1 recursion error
*/
static int
{
if (depth > VS_EXT_RECURSE_DEPTH)
return (-1);
for (;;) {
switch (*patn) {
case 0:
return (*str == 0);
case '?':
if (*str != 0) {
str++;
patn++;
continue;
}
return (0);
case '*':
patn++;
if (*patn == 0)
return (1);
while (*str) {
return (1);
str++;
}
return (0);
default:
return (0);
}
str++;
patn++;
continue;
}
}
/* NOT REACHED */
}