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