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