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