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 }