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/vscan.h> 45 46 #define VS_TASKQ_NUM_THREADS VS_DRV_MAX_FILES 47 #define VS_EXT_RECURSE_DEPTH 8 48 #define tolower(C) (((C) >= 'A' && (C) <= 'Z') ? (C) - 'A' + 'a' : (C)) 49 50 /* represents request received from filesystem - currently only use vp */ 51 typedef struct vscan_fs_req { 52 vnode_t *vsr_vp; 53 } vscan_fs_req_t; 54 55 /* 56 * vscan_svc_files - table of files being scanned 57 * 58 * The index into this table is passed in the door call to 59 * vscand. vscand uses the idx to determine which minor node 60 * to open to read the file data. Within the kernel driver 61 * the minor device number can thus be used to identify the 62 * table index to get the appropriate vnode. 63 * 64 * Instance 0 is reserved for the daemon/driver control 65 * interface: enable/configure/disable 66 */ 67 typedef struct vscan_file { 68 vscan_fs_req_t vsf_req; 69 uint32_t vsf_wait_count; 70 kcondvar_t vsf_cv; /* wait for in progress scan */ 71 uint8_t vsf_quarantined; 72 uint8_t vsf_modified; 73 uint64_t vsf_size; 74 timestruc_t vsf_mtime; 75 vs_scanstamp_t vsf_scanstamp; 76 uint32_t vsf_result; 77 uint32_t vsf_access; 78 } vscan_file_t; 79 80 static vscan_file_t vscan_svc_files[VS_DRV_MAX_FILES + 1]; 81 static kcondvar_t vscan_svc_cv; /* wait for slot in vscan_svc_files */ 82 static int vscan_svc_wait_count = 0; /* # waiting for slot in vscan_svc_files */ 83 static int vscan_svc_req_count = 0; /* # scan requests */ 84 85 static taskq_t *vscan_svc_taskq = NULL; 86 static boolean_t vscan_svc_enabled = B_FALSE; 87 88 /* 89 * vscan_svc_mutex protects the data pertaining to scan requests: 90 * file table - vscan_svc_files 91 * counts - vscan_svc_wait_count, vscan_svc_req_count 92 */ 93 static kmutex_t vscan_svc_mutex; 94 95 /* 96 * vscan_svc_cfg_mutex protects the configuration data: 97 * vscan_svc_config, vscan_svc_types 98 */ 99 static kmutex_t vscan_svc_cfg_mutex; 100 101 /* configuration data - for virus scan exemption */ 102 static vs_config_t vscan_svc_config; 103 static char *vscan_svc_types[VS_TYPES_MAX]; 104 105 /* local functions */ 106 int vscan_svc_scan_file(vnode_t *, cred_t *, int); 107 void vscan_svc_taskq_callback(void *); 108 static int vscan_svc_exempt_file(vnode_t *, boolean_t *); 109 static int vscan_svc_exempt_filetype(char *); 110 static int vscan_svc_match_ext(char *, char *, int); 111 static int vscan_svc_do_scan(vscan_fs_req_t *); 112 static int vscan_svc_wait_for_scan(vnode_t *); 113 static int vscan_svc_insert_file(vscan_fs_req_t *); 114 static void vscan_svc_release_file(int); 115 static int vscan_svc_find_slot(void); 116 static void vscan_svc_process_scan_result(int); 117 static void vscan_svc_notify_scan_complete(int); 118 static int vscan_svc_getattr(int); 119 static int vscan_svc_setattr(int, int); 120 121 static vs_scan_req_t *vscan_svc_populate_req(int); 122 static void vscan_svc_parse_rsp(int, vs_scan_req_t *); 123 124 125 /* 126 * vscan_svc_init 127 */ 128 int 129 vscan_svc_init() 130 { 131 mutex_init(&vscan_svc_mutex, NULL, MUTEX_DRIVER, NULL); 132 mutex_init(&vscan_svc_cfg_mutex, NULL, MUTEX_DRIVER, NULL); 133 (void) memset(&vscan_svc_files, 0, sizeof (vscan_svc_files)); 134 cv_init(&vscan_svc_cv, NULL, CV_DEFAULT, NULL); 135 136 return (0); 137 } 138 139 /* 140 * vscan_svc_fini 141 */ 142 void 143 vscan_svc_fini() 144 { 145 ASSERT(vscan_svc_enabled == B_FALSE); 146 ASSERT(vscan_svc_in_use() == B_FALSE); 147 148 cv_destroy(&vscan_svc_cv); 149 mutex_destroy(&vscan_svc_mutex); 150 mutex_destroy(&vscan_svc_cfg_mutex); 151 } 152 153 /* 154 * vscan_svc_enable 155 */ 156 void 157 vscan_svc_enable(void) 158 { 159 mutex_enter(&vscan_svc_mutex); 160 vscan_svc_enabled = B_TRUE; 161 162 if (vscan_svc_taskq == NULL) { 163 if ((vscan_svc_taskq = taskq_create("vscan", 164 VS_TASKQ_NUM_THREADS, MINCLSYSPRI, 1, 165 INT_MAX, TASKQ_DYNAMIC)) == NULL) { 166 cmn_err(CE_WARN, "All scan requests " 167 "will be processed synchronously"); 168 } 169 } 170 171 fs_vscan_register(vscan_svc_scan_file); 172 mutex_exit(&vscan_svc_mutex); 173 } 174 175 176 /* 177 * vscan_svc_disable 178 */ 179 void 180 vscan_svc_disable(void) 181 { 182 mutex_enter(&vscan_svc_mutex); 183 vscan_svc_enabled = B_FALSE; 184 fs_vscan_register(NULL); 185 186 if (vscan_svc_taskq) { 187 taskq_destroy(vscan_svc_taskq); 188 vscan_svc_taskq = NULL; 189 } 190 mutex_exit(&vscan_svc_mutex); 191 } 192 193 194 195 /* 196 * vscan_svc_is_enabled 197 */ 198 boolean_t 199 vscan_svc_is_enabled() 200 { 201 return (vscan_svc_enabled); 202 } 203 204 205 /* 206 * vscan_svc_in_use 207 * 208 * The vscan driver is considered to be in use if it is 209 * enabled or if there are in-progress scan requests. 210 * Used to determine whether the driver can be unloaded. 211 */ 212 boolean_t 213 vscan_svc_in_use() 214 { 215 boolean_t rc; 216 217 mutex_enter(&vscan_svc_mutex); 218 rc = (vscan_svc_enabled == B_TRUE) || (vscan_svc_req_count > 0); 219 mutex_exit(&vscan_svc_mutex); 220 221 return (rc); 222 } 223 224 /* 225 * vscan_svc_get_vnode 226 * 227 * Get the file vnode indexed by idx. 228 * Returns NULL if idx not valid. 229 */ 230 vnode_t * 231 vscan_svc_get_vnode(int idx) 232 { 233 ASSERT(idx > 0); 234 ASSERT(idx <= VS_DRV_MAX_FILES); 235 236 if ((idx <= 0) || (idx > VS_DRV_MAX_FILES)) 237 return (NULL); 238 else 239 return (vscan_svc_files[idx].vsf_req.vsr_vp); 240 } 241 242 243 /* 244 * vscan_svc_scan_file 245 * 246 * This function is the entry point for the file system to 247 * request that a file be virus scanned. 248 * 249 * Asynchronous requests: 250 * If an async scan request cannot be queued it is discarded. 251 * By definition the caller of an async request is not dependent 252 * on the outcome of the result. Although the file will thus 253 * not be scanned at this time, it will be scanned 254 * (synchronously) on subsequent access. 255 * This scenario should not occur during normal operation. 256 * 257 * Before queuing an async request do VN_HOLD(vp). VN_RELE(vp) 258 * will be done when the scan completes or if the request 259 * couldn't be queued. 260 * 261 * The vscan_fs_req_t, allocated to hold the request information 262 * passed from the fs, will be free'd when the scan completes. 263 */ 264 int 265 vscan_svc_scan_file(vnode_t *vp, cred_t *cr, int async) 266 { 267 int rc = 0; 268 vscan_fs_req_t *req; 269 boolean_t allow; 270 271 mutex_enter(&vscan_svc_mutex); 272 273 if ((vp == NULL) || (vp->v_path == NULL) || cr == NULL) { 274 mutex_exit(&vscan_svc_mutex); 275 return (0); 276 } 277 278 DTRACE_PROBE2(vscan__scan__file, char *, vp->v_path, int, async); 279 280 /* check if size or type exempts file from scanning */ 281 if (vscan_svc_exempt_file(vp, &allow)) { 282 mutex_exit(&vscan_svc_mutex); 283 if ((allow == B_TRUE) || (async != 0)) 284 return (0); 285 286 return (EACCES); 287 } 288 289 vscan_svc_req_count++; 290 mutex_exit(&vscan_svc_mutex); 291 292 req = kmem_zalloc(sizeof (vscan_fs_req_t), KM_SLEEP); 293 req->vsr_vp = vp; 294 295 if (async) { 296 VN_HOLD(vp); 297 if (vscan_svc_taskq && 298 taskq_dispatch(vscan_svc_taskq, vscan_svc_taskq_callback, 299 (void *)req, TQ_SLEEP)) { 300 return (0); 301 } else { 302 VN_RELE(vp); 303 kmem_free(req, sizeof (vscan_fs_req_t)); 304 } 305 } else { 306 rc = vscan_svc_do_scan(req); 307 kmem_free(req, sizeof (vscan_fs_req_t)); 308 } 309 310 mutex_enter(&vscan_svc_mutex); 311 vscan_svc_req_count--; 312 mutex_exit(&vscan_svc_mutex); 313 314 return (rc); 315 } 316 317 318 /* 319 * vscan_svc_taskq_callback 320 * 321 * Callback function for async scan requests 322 */ 323 void 324 vscan_svc_taskq_callback(void *data) 325 { 326 vscan_fs_req_t *req = (vscan_fs_req_t *)data; 327 328 (void) vscan_svc_do_scan(req); 329 VN_RELE(req->vsr_vp); /* VN_HOLD done before request queued */ 330 kmem_free(req, sizeof (vscan_fs_req_t)); 331 332 mutex_enter(&vscan_svc_mutex); 333 vscan_svc_req_count--; 334 mutex_exit(&vscan_svc_mutex); 335 } 336 337 338 /* 339 * vscan_svc_do_scan 340 * 341 * Should never be called directly. Invoke via vscan_svc_scan_file() 342 * If scan is in progress wait for it to complete, otherwise 343 * initiate door call to scan the file. 344 */ 345 static int 346 vscan_svc_do_scan(vscan_fs_req_t *req) 347 { 348 int rc = -1, idx; 349 vs_scan_req_t *scan_req; 350 vscan_file_t *svc_file; 351 352 mutex_enter(&vscan_svc_mutex); 353 354 /* 355 * if a scan is in progress on the files vscan_svc_wait_for_scan will 356 * wait for it to complete and return the idx of the scan request. 357 * Otherwise it will return -1 and we will initiate a scan here. 358 */ 359 if ((idx = vscan_svc_wait_for_scan(req->vsr_vp)) != -1) { 360 svc_file = &vscan_svc_files[idx]; 361 } else { 362 /* insert the scan request into vscan_svc_files */ 363 idx = vscan_svc_insert_file(req); 364 svc_file = &vscan_svc_files[idx]; 365 366 if (vscan_svc_enabled) { 367 if (vscan_svc_getattr(idx) == 0) { 368 /* valid scan_req ptr guaranteed */ 369 scan_req = vscan_svc_populate_req(idx); 370 mutex_exit(&vscan_svc_mutex); 371 if (vscan_drv_create_node(idx) == B_TRUE) 372 rc = vscan_door_scan_file(scan_req); 373 mutex_enter(&vscan_svc_mutex); 374 if (rc == 0) 375 vscan_svc_parse_rsp(idx, scan_req); 376 kmem_free(scan_req, sizeof (vs_scan_req_t)); 377 378 /* process scan result */ 379 vscan_svc_process_scan_result(idx); 380 DTRACE_PROBE2(vscan__result, int, 381 svc_file->vsf_result, int, 382 svc_file->vsf_access); 383 } else { 384 /* if getattr fails: log error, deny access */ 385 cmn_err(CE_WARN, "Can't access xattr for %s\n", 386 svc_file->vsf_req.vsr_vp->v_path); 387 svc_file->vsf_access = VS_ACCESS_DENY; 388 } 389 } else { 390 /* if vscan not enabled (shutting down), allow ACCESS */ 391 svc_file->vsf_access = VS_ACCESS_ALLOW; 392 } 393 } 394 395 /* When a scan completes the result is saved in vscan_svc_files */ 396 rc = (svc_file->vsf_access == VS_ACCESS_ALLOW) ? 0 : EACCES; 397 398 /* wake threads waiting for result, or for a slot in vscan_svc_files */ 399 vscan_svc_notify_scan_complete(idx); 400 401 /* remove the entry from vscan_svc_files if nobody else is waiting */ 402 vscan_svc_release_file(idx); 403 404 mutex_exit(&vscan_svc_mutex); 405 406 return (rc); 407 } 408 409 410 /* 411 * vscan_svc_process_scan_result 412 * 413 * Sets vsf_access and updates file attributes based on vsf_result, 414 * as follows: 415 * 416 * VS_STATUS_INFECTED 417 * deny access, set quarantine attribute, clear scanstamp 418 * VS_STATUS_CLEAN 419 * allow access, set scanstamp, 420 * if file not modified since scan initiated, clear modified attribute 421 * VS_STATUS_NO_SCAN 422 * deny access if file quarantined, otherwise allow access 423 * VS_STATUS_UNDEFINED, VS_STATUS_ERROR 424 * deny access if file quarantined, modified or no scanstamp 425 * otherwise, allow access 426 */ 427 static void 428 vscan_svc_process_scan_result(int idx) 429 { 430 struct vattr attr; 431 vnode_t *vp; 432 timestruc_t *mtime; 433 vscan_file_t *svc_file; 434 435 ASSERT(MUTEX_HELD(&vscan_svc_mutex)); 436 437 svc_file = &vscan_svc_files[idx]; 438 439 switch (svc_file->vsf_result) { 440 case VS_STATUS_INFECTED: 441 svc_file->vsf_access = VS_ACCESS_DENY; 442 svc_file->vsf_quarantined = 1; 443 svc_file->vsf_scanstamp[0] = '\0'; 444 (void) vscan_svc_setattr(idx, 445 XAT_AV_QUARANTINED | XAT_AV_SCANSTAMP); 446 return; 447 448 case VS_STATUS_CLEAN: 449 svc_file->vsf_access = VS_ACCESS_ALLOW; 450 451 /* if mtime has changed, don't clear the modified attribute */ 452 vp = svc_file->vsf_req.vsr_vp; 453 mtime = &(svc_file->vsf_mtime); 454 attr.va_mask = AT_MTIME; 455 if ((VOP_GETATTR(vp, &attr, 0, kcred, NULL) != 0) || 456 (mtime->tv_sec != attr.va_mtime.tv_sec) || 457 (mtime->tv_nsec != attr.va_mtime.tv_nsec)) { 458 DTRACE_PROBE1(vscan__mtime__changed, vscan_file_t *, 459 svc_file); 460 (void) vscan_svc_setattr(idx, XAT_AV_SCANSTAMP); 461 return; 462 } 463 464 svc_file->vsf_modified = 0; 465 (void) vscan_svc_setattr(idx, 466 XAT_AV_SCANSTAMP | XAT_AV_MODIFIED); 467 return; 468 469 case VS_STATUS_NO_SCAN: 470 if (svc_file->vsf_quarantined) 471 svc_file->vsf_access = VS_ACCESS_DENY; 472 else 473 svc_file->vsf_access = VS_ACCESS_ALLOW; 474 return; 475 476 case VS_STATUS_ERROR: 477 case VS_STATUS_UNDEFINED: 478 default: 479 if ((svc_file->vsf_quarantined) || 480 (svc_file->vsf_modified) || 481 (svc_file->vsf_scanstamp[0] == '\0')) 482 svc_file->vsf_access = VS_ACCESS_DENY; 483 else 484 svc_file->vsf_access = VS_ACCESS_ALLOW; 485 return; 486 } 487 } 488 489 490 /* 491 * vscan_svc_wait_for_scan 492 * 493 * Search for vp in vscan_svc_files. If vp already exists in 494 * vscan_svc_files scan is already in progress on file so wait 495 * for the inprogress scan to complete. 496 * 497 * Returns: idx of file waited for 498 * -1 if file not already scanning 499 */ 500 static int 501 vscan_svc_wait_for_scan(vnode_t *vp) 502 { 503 int idx; 504 vscan_file_t *svc_file; 505 506 ASSERT(vp); 507 ASSERT(MUTEX_HELD(&vscan_svc_mutex)); 508 509 for (idx = 1; idx <= VS_DRV_MAX_FILES; idx++) { 510 if (vscan_svc_files[idx].vsf_req.vsr_vp == vp) 511 break; 512 } 513 514 /* file not found in table thus not currently being scanned */ 515 if (idx > VS_DRV_MAX_FILES) 516 return (-1); 517 518 /* file found - wait for scan to complete */ 519 svc_file = &vscan_svc_files[idx]; 520 svc_file->vsf_wait_count++; 521 522 DTRACE_PROBE2(vscan__wait__scan, vscan_file_t *, svc_file, int, idx); 523 524 while (svc_file->vsf_access == VS_ACCESS_UNDEFINED) 525 cv_wait(&(svc_file->vsf_cv), &vscan_svc_mutex); 526 527 svc_file->vsf_wait_count--; 528 529 return (idx); 530 } 531 532 533 /* 534 * vscan_svc_find_slot 535 * 536 * Find empty slot in vscan_svc_files table. 537 * 538 * Returns idx of slot, or -1 if not found 539 */ 540 static int 541 vscan_svc_find_slot(void) 542 { 543 int idx; 544 545 ASSERT(MUTEX_HELD(&vscan_svc_mutex)); 546 for (idx = 1; idx <= VS_DRV_MAX_FILES; idx++) { 547 if (vscan_svc_files[idx].vsf_req.vsr_vp == NULL) 548 return (idx); 549 } 550 551 return (-1); 552 } 553 554 555 /* 556 * vscan_svc_insert_file 557 * 558 * Find the next available flot in vscan_svc_files and 559 * initialize it for the scan request. If no slot is 560 * available, vscan_svc_find_slot will wait for one. 561 * 562 * Returns: idx of scan request in vscan_svc_files table 563 */ 564 static int 565 vscan_svc_insert_file(vscan_fs_req_t *req) 566 { 567 int idx; 568 vscan_file_t *svc_file; 569 570 ASSERT(MUTEX_HELD(&vscan_svc_mutex)); 571 572 while ((idx = vscan_svc_find_slot()) == -1) { 573 DTRACE_PROBE1(vscan__wait__slot, char *, req->vsr_vp->v_path); 574 vscan_svc_wait_count++; 575 cv_wait(&(vscan_svc_cv), &vscan_svc_mutex); 576 vscan_svc_wait_count--; 577 } 578 579 svc_file = &vscan_svc_files[idx]; 580 581 (void) memset(svc_file, 0, sizeof (vscan_file_t)); 582 svc_file->vsf_req = *req; 583 svc_file->vsf_modified = 1; 584 svc_file->vsf_result = VS_STATUS_UNDEFINED; 585 svc_file->vsf_access = VS_ACCESS_UNDEFINED; 586 cv_init(&(svc_file->vsf_cv), NULL, CV_DEFAULT, NULL); 587 588 DTRACE_PROBE2(vscan__insert, char *, req->vsr_vp->v_path, int, idx); 589 return (idx); 590 } 591 592 593 /* 594 * vscan_svc_release_file 595 * 596 * Release the file (free the slot in vscan_svc_files) 597 * if no thread is waiting on it. 598 */ 599 static void 600 vscan_svc_release_file(int idx) 601 { 602 vscan_file_t *svc_file; 603 604 ASSERT(MUTEX_HELD(&vscan_svc_mutex)); 605 svc_file = &vscan_svc_files[idx]; 606 607 if (svc_file->vsf_wait_count != 0) 608 return; 609 610 DTRACE_PROBE2(vscan__release, char *, 611 svc_file->vsf_req.vsr_vp->v_path, int, idx); 612 613 cv_destroy(&(svc_file->vsf_cv)); 614 (void) memset(svc_file, 0, sizeof (vscan_file_t)); 615 } 616 617 618 /* 619 * vscan_svc_populate_req 620 * 621 * Allocate a scan request to be sent to vscand, populating it 622 * from the data in vscan_svc_files[idx]. 623 * 624 * Returns: scan request object 625 */ 626 static vs_scan_req_t * 627 vscan_svc_populate_req(int idx) 628 { 629 vs_scan_req_t *scan_req; 630 vscan_fs_req_t *req; 631 vscan_file_t *svc_file; 632 633 ASSERT(MUTEX_HELD(&vscan_svc_mutex)); 634 635 svc_file = &vscan_svc_files[idx]; 636 req = &(svc_file->vsf_req); 637 scan_req = kmem_zalloc(sizeof (vs_scan_req_t), KM_SLEEP); 638 639 scan_req->vsr_id = idx; 640 (void) strncpy(scan_req->vsr_path, req->vsr_vp->v_path, MAXPATHLEN); 641 scan_req->vsr_size = svc_file->vsf_size; 642 scan_req->vsr_modified = svc_file->vsf_modified; 643 scan_req->vsr_quarantined = svc_file->vsf_quarantined; 644 scan_req->vsr_flags = 0; 645 (void) strncpy(scan_req->vsr_scanstamp, 646 svc_file->vsf_scanstamp, sizeof (vs_scanstamp_t)); 647 648 return (scan_req); 649 } 650 651 652 /* 653 * vscan_svc_parse_rsp 654 * 655 * Parse scan response data and save in vscan_svc_files[idx] 656 */ 657 static void 658 vscan_svc_parse_rsp(int idx, vs_scan_req_t *scan_req) 659 { 660 vscan_file_t *svc_file; 661 662 ASSERT(MUTEX_HELD(&vscan_svc_mutex)); 663 664 svc_file = &vscan_svc_files[idx]; 665 svc_file->vsf_result = scan_req->vsr_result; 666 (void) strncpy(svc_file->vsf_scanstamp, 667 scan_req->vsr_scanstamp, sizeof (vs_scanstamp_t)); 668 } 669 670 671 /* 672 * vscan_svc_notify_scan_complete 673 * 674 * signal vscan_svc_files.vsf_cv and vscan_svc_cv to wake 675 * threads waiting for the scan result for the specified 676 * file (vscan_svc_files[idx].vsf_cv) or for a slot in 677 * vscan_svc_files table (vscan_svc_cv) 678 */ 679 static void 680 vscan_svc_notify_scan_complete(int idx) 681 { 682 vscan_file_t *svc_file; 683 684 ASSERT(MUTEX_HELD(&vscan_svc_mutex)); 685 686 svc_file = &vscan_svc_files[idx]; 687 688 /* if someone waiting for result, cv_signal */ 689 if (svc_file->vsf_wait_count > 0) 690 cv_signal(&(svc_file->vsf_cv)); 691 692 /* signal vscan_svc_cv if any threads waiting for a slot */ 693 if (vscan_svc_wait_count > 0) 694 cv_signal(&vscan_svc_cv); 695 } 696 697 698 /* 699 * vscan_svc_getattr 700 * 701 * Get the vscan related system attributes, AT_SIZE & AT_MTIME. 702 */ 703 static int 704 vscan_svc_getattr(int idx) 705 { 706 xvattr_t xvattr; 707 xoptattr_t *xoap = NULL; 708 vnode_t *vp; 709 vscan_file_t *svc_file; 710 711 ASSERT(MUTEX_HELD(&vscan_svc_mutex)); 712 713 svc_file = &vscan_svc_files[idx]; 714 if ((vp = svc_file->vsf_req.vsr_vp) == NULL) 715 return (-1); 716 717 /* get the attributes */ 718 xva_init(&xvattr); /* sets AT_XVATTR */ 719 720 xvattr.xva_vattr.va_mask |= AT_SIZE; 721 xvattr.xva_vattr.va_mask |= AT_MTIME; 722 XVA_SET_REQ(&xvattr, XAT_AV_MODIFIED); 723 XVA_SET_REQ(&xvattr, XAT_AV_QUARANTINED); 724 XVA_SET_REQ(&xvattr, XAT_AV_SCANSTAMP); 725 726 if (VOP_GETATTR(vp, (vattr_t *)&xvattr, 0, kcred, NULL) != 0) 727 return (-1); 728 729 if ((xoap = xva_getxoptattr(&xvattr)) == NULL) { 730 cmn_err(CE_NOTE, "Virus scan request failed; " 731 "file system does not support virus scanning"); 732 return (-1); 733 } 734 735 svc_file->vsf_size = xvattr.xva_vattr.va_size; 736 svc_file->vsf_mtime.tv_sec = xvattr.xva_vattr.va_mtime.tv_sec; 737 svc_file->vsf_mtime.tv_nsec = xvattr.xva_vattr.va_mtime.tv_nsec; 738 739 if (XVA_ISSET_RTN(&xvattr, XAT_AV_MODIFIED) == 0) 740 return (-1); 741 svc_file->vsf_modified = xoap->xoa_av_modified; 742 743 if (XVA_ISSET_RTN(&xvattr, XAT_AV_QUARANTINED) == 0) 744 return (-1); 745 svc_file->vsf_quarantined = xoap->xoa_av_quarantined; 746 747 if (XVA_ISSET_RTN(&xvattr, XAT_AV_SCANSTAMP) != 0) { 748 (void) memcpy(svc_file->vsf_scanstamp, 749 xoap->xoa_av_scanstamp, AV_SCANSTAMP_SZ); 750 } 751 752 DTRACE_PROBE1(vscan__getattr, vscan_file_t *, svc_file); 753 return (0); 754 } 755 756 757 /* 758 * vscan_svc_setattr 759 * 760 * Set the vscan related system attributes. 761 */ 762 static int 763 vscan_svc_setattr(int idx, int which) 764 { 765 xvattr_t xvattr; 766 xoptattr_t *xoap = NULL; 767 vnode_t *vp; 768 int len; 769 vscan_file_t *svc_file; 770 771 ASSERT(MUTEX_HELD(&vscan_svc_mutex)); 772 773 svc_file = &vscan_svc_files[idx]; 774 if ((vp = svc_file->vsf_req.vsr_vp) == NULL) 775 return (-1); 776 777 /* update the attributes */ 778 xva_init(&xvattr); /* sets AT_XVATTR */ 779 if ((xoap = xva_getxoptattr(&xvattr)) == NULL) 780 return (-1); 781 782 if (which & XAT_AV_MODIFIED) { 783 XVA_SET_REQ(&xvattr, XAT_AV_MODIFIED); 784 xoap->xoa_av_modified = svc_file->vsf_modified; 785 } 786 787 if (which & XAT_AV_QUARANTINED) { 788 XVA_SET_REQ(&xvattr, XAT_AV_QUARANTINED); 789 xoap->xoa_av_quarantined = svc_file->vsf_quarantined; 790 } 791 792 if (which & XAT_AV_SCANSTAMP) { 793 XVA_SET_REQ(&xvattr, XAT_AV_SCANSTAMP); 794 len = strlen(svc_file->vsf_scanstamp); 795 (void) memcpy(xoap->xoa_av_scanstamp, 796 svc_file->vsf_scanstamp, len); 797 } 798 799 /* if access is denied, set mtime to invalidate client cache */ 800 if (svc_file->vsf_access != VS_ACCESS_ALLOW) { 801 xvattr.xva_vattr.va_mask |= AT_MTIME; 802 gethrestime(&xvattr.xva_vattr.va_mtime); 803 } 804 805 if (VOP_SETATTR(vp, (vattr_t *)&xvattr, 0, kcred, NULL) != 0) 806 return (-1); 807 808 DTRACE_PROBE2(vscan__setattr, 809 vscan_file_t *, svc_file, int, which); 810 811 return (0); 812 } 813 814 815 /* 816 * vscan_svc_configure 817 * 818 * store configuration in vscan_svc_config 819 * set up vscan_svc_types array of pointers into 820 * vscan_svc_config.vsc_types for efficient searching 821 */ 822 int 823 vscan_svc_configure(vs_config_t *conf) 824 { 825 int count = 0; 826 char *p, *beg, *end; 827 828 mutex_enter(&vscan_svc_cfg_mutex); 829 830 vscan_svc_config = *conf; 831 832 (void) memset(vscan_svc_types, 0, sizeof (vscan_svc_types)); 833 834 beg = vscan_svc_config.vsc_types; 835 end = beg + vscan_svc_config.vsc_types_len; 836 837 for (p = beg; p < end; p += strlen(p) + 1) { 838 if (count >= VS_TYPES_MAX) { 839 mutex_exit(&vscan_svc_mutex); 840 return (-1); 841 } 842 843 vscan_svc_types[count] = p; 844 ++count; 845 } 846 847 mutex_exit(&vscan_svc_cfg_mutex); 848 return (0); 849 } 850 851 852 /* 853 * vscan_svc_exempt_file 854 * 855 * check if a file's size or type exempts it from virus scanning 856 * 857 * If the file is exempt from virus scanning, allow will be set 858 * to define whether files access should be allowed (B_TRUE) or 859 * denied (B_FALSE) 860 * 861 * Returns: 1 exempt 862 * 0 scan required 863 */ 864 static int 865 vscan_svc_exempt_file(vnode_t *vp, boolean_t *allow) 866 { 867 struct vattr attr; 868 869 ASSERT(vp != NULL); 870 ASSERT(vp->v_path != NULL); 871 872 attr.va_mask = AT_SIZE; 873 874 if (VOP_GETATTR(vp, &attr, 0, kcred, NULL) != 0) { 875 *allow = B_FALSE; 876 return (0); 877 } 878 879 mutex_enter(&vscan_svc_cfg_mutex); 880 881 if (attr.va_size > vscan_svc_config.vsc_max_size) { 882 DTRACE_PROBE2(vscan__exempt__filesize, char *, 883 vp->v_path, int, *allow); 884 885 *allow = (vscan_svc_config.vsc_allow) ? B_TRUE : B_FALSE; 886 mutex_exit(&vscan_svc_cfg_mutex); 887 return (1); 888 } 889 890 if (vscan_svc_exempt_filetype(vp->v_path)) { 891 DTRACE_PROBE1(vscan__exempt__filetype, char *, vp->v_path); 892 *allow = B_TRUE; 893 mutex_exit(&vscan_svc_cfg_mutex); 894 return (1); 895 } 896 897 mutex_exit(&vscan_svc_cfg_mutex); 898 return (0); 899 } 900 901 902 /* 903 * vscan_svc_exempt_filetype 904 * 905 * Each entry in vscan_svc_types includes a rule indicator (+,-) 906 * followed by the match string for file types to which the rule 907 * applies. Look for first match of file type in vscan_svc_types 908 * and return 1 (exempt) if the indicator is '-', and 0 (not exempt) 909 * if the indicator is '+'. 910 * If vscan_svc_match_ext fails, or no match is found, return 0 911 * (not exempt) 912 * 913 * Returns 1: exempt, 0: not exempt 914 */ 915 static int 916 vscan_svc_exempt_filetype(char *filepath) 917 { 918 int i, rc, exempt = 0; 919 char *filename, *ext; 920 921 ASSERT(MUTEX_HELD(&vscan_svc_cfg_mutex)); 922 923 if ((filename = strrchr(filepath, '/')) == 0) 924 filename = filepath; 925 else 926 filename++; 927 928 if ((ext = strrchr(filename, '.')) == NULL) 929 ext = ""; 930 else 931 ext++; 932 933 934 for (i = 0; i < VS_TYPES_MAX; i ++) { 935 if (vscan_svc_types[i] == 0) 936 break; 937 938 rc = vscan_svc_match_ext(vscan_svc_types[i] + 1, ext, 1); 939 if (rc == -1) 940 break; 941 if (rc > 0) { 942 DTRACE_PROBE2(vscan__type__match, char *, ext, 943 char *, vscan_svc_types[i]); 944 exempt = (vscan_svc_types[i][0] == '-'); 945 break; 946 } 947 } 948 949 return (exempt); 950 } 951 952 953 /* 954 * vscan_svc_match_ext 955 * 956 * Performs a case-insensitive match for two strings. The first string 957 * argument can contain the wildcard characters '?' and '*' 958 * 959 * Returns: 0 no match 960 * 1 match 961 * -1 recursion error 962 */ 963 static int 964 vscan_svc_match_ext(char *patn, char *str, int depth) 965 { 966 int c1, c2; 967 if (depth > VS_EXT_RECURSE_DEPTH) 968 return (-1); 969 970 for (;;) { 971 switch (*patn) { 972 case 0: 973 return (*str == 0); 974 975 case '?': 976 if (*str != 0) { 977 str++; 978 patn++; 979 continue; 980 } 981 return (0); 982 983 case '*': 984 patn++; 985 if (*patn == 0) 986 return (1); 987 988 while (*str) { 989 if (vscan_svc_match_ext(patn, str, depth + 1)) 990 return (1); 991 str++; 992 } 993 return (0); 994 995 default: 996 if (*str != *patn) { 997 c1 = *str; 998 c2 = *patn; 999 1000 c1 = tolower(c1); 1001 c2 = tolower(c2); 1002 if (c1 != c2) 1003 return (0); 1004 } 1005 str++; 1006 patn++; 1007 continue; 1008 } 1009 } 1010 /* NOT REACHED */ 1011 } 1012