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