1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include <sys/stat.h> 28 #include <sys/ddi.h> 29 #include <sys/sunddi.h> 30 #include <sys/time.h> 31 #include <sys/varargs.h> 32 #include <sys/conf.h> 33 #include <sys/modctl.h> 34 #include <sys/vnode.h> 35 #include <fs/fs_subr.h> 36 #include <sys/types.h> 37 #include <sys/file.h> 38 #include <sys/disp.h> 39 #include <sys/vscan.h> 40 #include <sys/policy.h> 41 #include <sys/sdt.h> 42 43 44 /* seconds to wait for daemon to reconnect before disabling */ 45 #define VS_DAEMON_WAIT_SEC 60 46 47 /* length of minor node name - vscan%d */ 48 #define VS_NODENAME_LEN 16 49 50 /* global variables - tunable via /etc/system */ 51 uint32_t vs_reconnect_timeout = VS_DAEMON_WAIT_SEC; 52 extern uint32_t vs_nodes_max; /* max in-progress scan requests */ 53 54 /* 55 * vscan_drv_state 56 * 57 * Operations on instance 0 represent vscand initiated state 58 * transition events: 59 * open(0) - vscand connect 60 * close(0) - vscan disconnect 61 * enable(0) - vscand enable (ready to hand requests) 62 * disable(0) - vscand disable (shutting down) 63 * 64 * +------------------------+ 65 * | VS_DRV_UNCONFIG | 66 * +------------------------+ 67 * | ^ 68 * | attach | detach 69 * v | 70 * +------------------------+ 71 * | VS_DRV_IDLE |<------| 72 * +------------------------+ | 73 * | ^ | 74 * | open(0) | close(0) | 75 * v | | 76 * +------------------------+ | 77 * | VS_DRV_CONNECTED |<-| | 78 * +------------------------+ | | 79 * | ^ | | 80 * | enable(0) | disable(0) | | 81 * v | | | 82 * +------------------------+ | | 83 * | VS_DRV_ENABLED | | | 84 * +------------------------+ | | 85 * | | | 86 * | close(0) open(0) | 87 * v | | 88 * +------------------------+ | | timeout 89 * | VS_DRV_DELAYED_DISABLE | -- | 90 * +------------------------+ ------| 91 * 92 */ 93 typedef enum { 94 VS_DRV_UNCONFIG, 95 VS_DRV_IDLE, 96 VS_DRV_CONNECTED, 97 VS_DRV_ENABLED, 98 VS_DRV_DELAYED_DISABLE 99 } vscan_drv_state_t; 100 static vscan_drv_state_t vscan_drv_state = VS_DRV_UNCONFIG; 101 102 103 /* 104 * vscan_drv_inst_state 105 * 106 * Instance 0 controls the state of the driver: vscan_drv_state. 107 * vscan_drv_inst_state[0] should NOT be used. 108 * 109 * vscan_drv_inst_state[n] represents the state of driver 110 * instance n, used by vscand to access file data for the 111 * scan request with index n in vscan_svc_reqs. 112 * Minor nodes are created as required then all are destroyed 113 * during driver detach. 114 * 115 * +------------------------+ 116 * | VS_DRV_INST_UNCONFIG | 117 * +------------------------+ 118 * | ^ 119 * | create_node(n) | detach 120 * v | 121 * +------------------------+ 122 * | VS_DRV_INST_INIT |<-| 123 * +------------------------+ | 124 * | | 125 * | open(n) | 126 * v | 127 * +------------------------+ | 128 * | VS_DRV_INST_OPEN |--| 129 * +------------------------+ | 130 * | | 131 * | read(n) | 132 * v | close(n) 133 * +------------------------+ | 134 * | VS_DRV_INST_READING |--| 135 * +------------------------+ 136 */ 137 typedef enum { 138 VS_DRV_INST_UNCONFIG = 0, /* minor node not created */ 139 VS_DRV_INST_INIT, 140 VS_DRV_INST_OPEN, 141 VS_DRV_INST_READING 142 } vscan_drv_inst_state_t; 143 144 static vscan_drv_inst_state_t *vscan_drv_inst_state; 145 static int vscan_drv_inst_state_sz; 146 147 static dev_info_t *vscan_drv_dip; 148 static kmutex_t vscan_drv_mutex; 149 static kcondvar_t vscan_drv_cv; /* wait for daemon reconnect */ 150 151 /* 152 * DDI entry points. 153 */ 154 static int vscan_drv_attach(dev_info_t *, ddi_attach_cmd_t); 155 static int vscan_drv_detach(dev_info_t *, ddi_detach_cmd_t); 156 static int vscan_drv_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 157 static int vscan_drv_open(dev_t *, int, int, cred_t *); 158 static int vscan_drv_close(dev_t, int, int, cred_t *); 159 static int vscan_drv_read(dev_t, struct uio *, cred_t *); 160 static int vscan_drv_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 161 162 static boolean_t vscan_drv_in_use(void); 163 static void vscan_drv_delayed_disable(void); 164 165 166 /* 167 * module linkage info for the kernel 168 */ 169 static struct cb_ops cbops = { 170 vscan_drv_open, /* cb_open */ 171 vscan_drv_close, /* cb_close */ 172 nodev, /* cb_strategy */ 173 nodev, /* cb_print */ 174 nodev, /* cb_dump */ 175 vscan_drv_read, /* cb_read */ 176 nodev, /* cb_write */ 177 vscan_drv_ioctl, /* cb_ioctl */ 178 nodev, /* cb_devmap */ 179 nodev, /* cb_mmap */ 180 nodev, /* cb_segmap */ 181 nochpoll, /* cb_chpoll */ 182 ddi_prop_op, /* cb_prop_op */ 183 NULL, /* cb_streamtab */ 184 D_MP, /* cb_flag */ 185 CB_REV, /* cb_rev */ 186 nodev, /* cb_aread */ 187 nodev, /* cb_awrite */ 188 }; 189 190 static struct dev_ops devops = { 191 DEVO_REV, /* devo_rev */ 192 0, /* devo_refcnt */ 193 vscan_drv_getinfo, /* devo_getinfo */ 194 nulldev, /* devo_identify */ 195 nulldev, /* devo_probe */ 196 vscan_drv_attach, /* devo_attach */ 197 vscan_drv_detach, /* devo_detach */ 198 nodev, /* devo_reset */ 199 &cbops, /* devo_cb_ops */ 200 NULL, /* devo_bus_ops */ 201 NULL, /* devo_power */ 202 ddi_quiesce_not_needed, /* devo_quiesce */ 203 }; 204 205 static struct modldrv modldrv = { 206 &mod_driverops, /* drv_modops */ 207 "virus scanning", /* drv_linkinfo */ 208 &devops, 209 }; 210 211 static struct modlinkage modlinkage = { 212 213 MODREV_1, /* revision of the module, must be: MODREV_1 */ 214 { &modldrv, NULL } /* ptr to linkage structures */ 215 }; 216 217 218 /* 219 * _init 220 */ 221 int 222 _init(void) 223 { 224 int rc; 225 226 vscan_drv_inst_state_sz = 227 sizeof (vscan_drv_inst_state_t) * (vs_nodes_max + 1); 228 229 if (vscan_door_init() != 0) 230 return (DDI_FAILURE); 231 232 if (vscan_svc_init() != 0) { 233 vscan_door_fini(); 234 return (DDI_FAILURE); 235 } 236 237 mutex_init(&vscan_drv_mutex, NULL, MUTEX_DRIVER, NULL); 238 vscan_drv_inst_state = kmem_zalloc(vscan_drv_inst_state_sz, KM_SLEEP); 239 240 cv_init(&vscan_drv_cv, NULL, CV_DEFAULT, NULL); 241 242 if ((rc = mod_install(&modlinkage)) != 0) { 243 vscan_door_fini(); 244 vscan_svc_fini(); 245 kmem_free(vscan_drv_inst_state, vscan_drv_inst_state_sz); 246 cv_destroy(&vscan_drv_cv); 247 mutex_destroy(&vscan_drv_mutex); 248 } 249 250 return (rc); 251 } 252 253 254 /* 255 * _info 256 */ 257 int 258 _info(struct modinfo *modinfop) 259 { 260 return (mod_info(&modlinkage, modinfop)); 261 } 262 263 264 /* 265 * _fini 266 */ 267 int 268 _fini(void) 269 { 270 int rc; 271 272 if (vscan_drv_in_use()) 273 return (EBUSY); 274 275 if ((rc = mod_remove(&modlinkage)) == 0) { 276 vscan_door_fini(); 277 vscan_svc_fini(); 278 kmem_free(vscan_drv_inst_state, vscan_drv_inst_state_sz); 279 cv_destroy(&vscan_drv_cv); 280 mutex_destroy(&vscan_drv_mutex); 281 } 282 283 return (rc); 284 } 285 286 287 /* 288 * DDI entry points. 289 */ 290 291 /* 292 * vscan_drv_getinfo 293 */ 294 /* ARGSUSED */ 295 static int 296 vscan_drv_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result) 297 { 298 ulong_t inst = getminor((dev_t)arg); 299 300 switch (cmd) { 301 case DDI_INFO_DEVT2DEVINFO: 302 *result = vscan_drv_dip; 303 return (DDI_SUCCESS); 304 case DDI_INFO_DEVT2INSTANCE: 305 *result = (void *)inst; 306 return (DDI_SUCCESS); 307 } 308 return (DDI_FAILURE); 309 } 310 311 312 /* 313 * vscan_drv_attach 314 */ 315 static int 316 vscan_drv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 317 { 318 if (cmd != DDI_ATTACH) 319 return (DDI_FAILURE); 320 321 if (ddi_get_instance(dip) != 0) 322 return (DDI_FAILURE); 323 324 vscan_drv_dip = dip; 325 326 /* create minor node 0 for daemon-driver synchronization */ 327 if (vscan_drv_create_node(0) == B_FALSE) 328 return (DDI_FAILURE); 329 330 vscan_drv_state = VS_DRV_IDLE; 331 return (DDI_SUCCESS); 332 } 333 334 335 /* 336 * vscan_drv_detach 337 */ 338 static int 339 vscan_drv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 340 { 341 int i; 342 343 if (cmd != DDI_DETACH) 344 return (DDI_FAILURE); 345 346 if (ddi_get_instance(dip) != 0) 347 return (DDI_FAILURE); 348 349 if (vscan_drv_in_use()) 350 return (DDI_FAILURE); 351 352 /* remove all minor nodes */ 353 vscan_drv_dip = NULL; 354 ddi_remove_minor_node(dip, NULL); 355 for (i = 0; i <= vs_nodes_max; i++) 356 vscan_drv_inst_state[i] = VS_DRV_INST_UNCONFIG; 357 358 vscan_drv_state = VS_DRV_UNCONFIG; 359 return (DDI_SUCCESS); 360 } 361 362 363 /* 364 * vscan_drv_in_use 365 * 366 * If the driver state is not IDLE or UNCONFIG then the 367 * driver is in use. Otherwise, check the service interface 368 * (vscan_svc) to see if it is still in use - for example 369 * there there may be requests still in progress. 370 */ 371 static boolean_t 372 vscan_drv_in_use() 373 { 374 boolean_t in_use = B_FALSE; 375 376 mutex_enter(&vscan_drv_mutex); 377 if ((vscan_drv_state != VS_DRV_IDLE) && 378 (vscan_drv_state != VS_DRV_UNCONFIG)) { 379 in_use = B_TRUE; 380 } 381 mutex_exit(&vscan_drv_mutex); 382 383 if (in_use) 384 return (B_TRUE); 385 else 386 return (vscan_svc_in_use()); 387 } 388 389 390 /* 391 * vscan_drv_open 392 * 393 * If inst == 0, this is vscand initializing. 394 * If the driver is in DELAYED_DISABLE, ie vscand previously 395 * disconnected without a clean shutdown and the driver is 396 * waiting for a period to allow vscand to reconnect, signal 397 * vscan_drv_cv to cancel the delayed disable. 398 * 399 * If inst != 0, open the file associated with inst. 400 */ 401 /* ARGSUSED */ 402 static int 403 vscan_drv_open(dev_t *devp, int flag, int otyp, cred_t *credp) 404 { 405 int rc; 406 int inst = getminor(*devp); 407 408 if ((inst < 0) || (inst > vs_nodes_max)) 409 return (EINVAL); 410 411 /* check if caller has privilege for virus scanning */ 412 if ((rc = secpolicy_vscan(credp)) != 0) { 413 DTRACE_PROBE1(vscan__priv, int, rc); 414 return (EPERM); 415 } 416 417 mutex_enter(&vscan_drv_mutex); 418 if (inst == 0) { 419 switch (vscan_drv_state) { 420 case VS_DRV_IDLE: 421 vscan_drv_state = VS_DRV_CONNECTED; 422 break; 423 case VS_DRV_DELAYED_DISABLE: 424 cv_signal(&vscan_drv_cv); 425 vscan_drv_state = VS_DRV_CONNECTED; 426 break; 427 default: 428 DTRACE_PROBE1(vscan__drv__state__violation, 429 int, vscan_drv_state); 430 mutex_exit(&vscan_drv_mutex); 431 return (EINVAL); 432 } 433 } else { 434 if ((vscan_drv_state != VS_DRV_ENABLED) || 435 (vscan_drv_inst_state[inst] != VS_DRV_INST_INIT)) { 436 mutex_exit(&vscan_drv_mutex); 437 return (EINVAL); 438 } 439 vscan_drv_inst_state[inst] = VS_DRV_INST_OPEN; 440 } 441 mutex_exit(&vscan_drv_mutex); 442 443 return (0); 444 } 445 446 447 /* 448 * vscan_drv_close 449 * 450 * If inst == 0, this is vscand detaching. 451 * If the driver is in ENABLED state vscand has terminated without 452 * a clean shutdown (nod DISABLE received). Enter DELAYED_DISABLE 453 * state and initiate a delayed disable to allow vscand time to 454 * reconnect. 455 * 456 * If inst != 0, close the file associated with inst 457 */ 458 /* ARGSUSED */ 459 static int 460 vscan_drv_close(dev_t dev, int flag, int otyp, cred_t *credp) 461 { 462 int i, inst = getminor(dev); 463 464 if ((inst < 0) || (inst > vs_nodes_max)) 465 return (EINVAL); 466 467 mutex_enter(&vscan_drv_mutex); 468 if (inst != 0) { 469 vscan_drv_inst_state[inst] = VS_DRV_INST_INIT; 470 mutex_exit(&vscan_drv_mutex); 471 return (0); 472 } 473 474 /* instance 0 - daemon disconnect */ 475 if ((vscan_drv_state != VS_DRV_CONNECTED) && 476 (vscan_drv_state != VS_DRV_ENABLED)) { 477 DTRACE_PROBE1(vscan__drv__state__violation, 478 int, vscan_drv_state); 479 mutex_exit(&vscan_drv_mutex); 480 return (EINVAL); 481 } 482 483 for (i = 1; i <= vs_nodes_max; i++) { 484 if (vscan_drv_inst_state[i] != VS_DRV_INST_UNCONFIG) 485 vscan_drv_inst_state[i] = VS_DRV_INST_INIT; 486 } 487 488 if (vscan_drv_state == VS_DRV_CONNECTED) { 489 vscan_drv_state = VS_DRV_IDLE; 490 } else { /* VS_DRV_ENABLED */ 491 cmn_err(CE_WARN, "Detected vscand exit without clean shutdown"); 492 if (thread_create(NULL, 0, vscan_drv_delayed_disable, 493 0, 0, &p0, TS_RUN, minclsyspri) == NULL) { 494 vscan_svc_disable(); 495 vscan_drv_state = VS_DRV_IDLE; 496 } else { 497 vscan_drv_state = VS_DRV_DELAYED_DISABLE; 498 } 499 } 500 mutex_exit(&vscan_drv_mutex); 501 502 vscan_svc_scan_abort(); 503 vscan_door_close(); 504 return (0); 505 } 506 507 508 /* 509 * vscan_drv_delayed_disable 510 * 511 * Invoked from vscan_drv_close if the daemon disconnects 512 * without first sending disable (e.g. daemon crashed). 513 * Delays for vs_reconnect_timeout before disabling, to allow 514 * the daemon to reconnect. During this time, scan requests 515 * will be processed locally (see vscan_svc.c) 516 */ 517 static void 518 vscan_drv_delayed_disable(void) 519 { 520 mutex_enter(&vscan_drv_mutex); 521 (void) cv_reltimedwait(&vscan_drv_cv, &vscan_drv_mutex, 522 SEC_TO_TICK(vs_reconnect_timeout), TR_CLOCK_TICK); 523 524 if (vscan_drv_state == VS_DRV_DELAYED_DISABLE) { 525 vscan_svc_disable(); 526 vscan_drv_state = VS_DRV_IDLE; 527 } else { 528 DTRACE_PROBE(vscan__reconnect); 529 } 530 mutex_exit(&vscan_drv_mutex); 531 } 532 533 534 /* 535 * vscan_drv_read 536 */ 537 /* ARGSUSED */ 538 static int 539 vscan_drv_read(dev_t dev, struct uio *uiop, cred_t *credp) 540 { 541 int rc; 542 int inst = getminor(dev); 543 vnode_t *vp; 544 545 if ((inst <= 0) || (inst > vs_nodes_max)) 546 return (EINVAL); 547 548 mutex_enter(&vscan_drv_mutex); 549 if ((vscan_drv_state != VS_DRV_ENABLED) || 550 (vscan_drv_inst_state[inst] != VS_DRV_INST_OPEN)) { 551 mutex_exit(&vscan_drv_mutex); 552 return (EINVAL); 553 } 554 vscan_drv_inst_state[inst] = VS_DRV_INST_READING; 555 mutex_exit(&vscan_drv_mutex); 556 557 if ((vp = vscan_svc_get_vnode(inst)) == NULL) 558 return (EINVAL); 559 560 (void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, NULL); 561 rc = VOP_READ(vp, uiop, 0, kcred, NULL); 562 VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL); 563 564 mutex_enter(&vscan_drv_mutex); 565 if (vscan_drv_inst_state[inst] == VS_DRV_INST_READING) 566 vscan_drv_inst_state[inst] = VS_DRV_INST_OPEN; 567 mutex_exit(&vscan_drv_mutex); 568 569 return (rc); 570 } 571 572 573 /* 574 * vscan_drv_ioctl 575 * 576 * Process ioctls from vscand: 577 * VS_IOCTL_ENABLE - vscand is ready to handle scan requests, 578 * enable VFS interface. 579 * VS_IOCTL_DISABLE - vscand is shutting down, disable VFS interface 580 * VS_IOCTL_RESULT - scan response data 581 * VS_IOCTL_CONFIG - configuration data from vscand 582 * VS_IOCTL_MAX_REQ - provide the max request idx to vscand, 583 * to allow vscand to set appropriate resource allocation limits 584 */ 585 /* ARGSUSED */ 586 static int 587 vscan_drv_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, 588 cred_t *credp, int *rvalp) 589 { 590 int inst = getminor(dev); 591 vs_config_t conf; 592 vs_scan_rsp_t rsp; 593 594 if (inst != 0) 595 return (EINVAL); 596 597 switch (cmd) { 598 case VS_IOCTL_ENABLE: 599 mutex_enter(&vscan_drv_mutex); 600 if (vscan_drv_state != VS_DRV_CONNECTED) { 601 DTRACE_PROBE1(vscan__drv__state__violation, 602 int, vscan_drv_state); 603 mutex_exit(&vscan_drv_mutex); 604 return (EINVAL); 605 } 606 if ((vscan_door_open((int)arg) != 0) || 607 (vscan_svc_enable() != 0)) { 608 mutex_exit(&vscan_drv_mutex); 609 return (EINVAL); 610 } 611 vscan_drv_state = VS_DRV_ENABLED; 612 mutex_exit(&vscan_drv_mutex); 613 break; 614 615 case VS_IOCTL_DISABLE: 616 mutex_enter(&vscan_drv_mutex); 617 if (vscan_drv_state != VS_DRV_ENABLED) { 618 DTRACE_PROBE1(vscan__drv__state__violation, 619 int, vscan_drv_state); 620 mutex_exit(&vscan_drv_mutex); 621 return (EINVAL); 622 } 623 vscan_svc_disable(); 624 vscan_drv_state = VS_DRV_CONNECTED; 625 mutex_exit(&vscan_drv_mutex); 626 break; 627 628 case VS_IOCTL_RESULT: 629 if (ddi_copyin((void *)arg, &rsp, 630 sizeof (vs_scan_rsp_t), 0) == -1) 631 return (EFAULT); 632 else 633 vscan_svc_scan_result(&rsp); 634 break; 635 636 case VS_IOCTL_CONFIG: 637 if (ddi_copyin((void *)arg, &conf, 638 sizeof (vs_config_t), 0) == -1) 639 return (EFAULT); 640 if (vscan_svc_configure(&conf) == -1) 641 return (EINVAL); 642 break; 643 644 case VS_IOCTL_MAX_REQ: 645 if (ddi_copyout(&vs_nodes_max, (void *)arg, 646 sizeof (uint32_t), 0) == -1) 647 return (EFAULT); 648 break; 649 650 default: 651 return (ENOTTY); 652 } 653 654 return (0); 655 } 656 657 658 /* 659 * vscan_drv_create_node 660 * 661 * Create minor node with which vscan daemon will communicate 662 * to access a file. Invoked from vscan_svc before scan request 663 * sent up to daemon. 664 * Minor node 0 is reserved for daemon-driver synchronization 665 * and is created during attach. 666 * All minor nodes are removed during detach. 667 */ 668 boolean_t 669 vscan_drv_create_node(int idx) 670 { 671 char name[VS_NODENAME_LEN]; 672 boolean_t rc = B_TRUE; 673 674 mutex_enter(&vscan_drv_mutex); 675 676 if (vscan_drv_inst_state[idx] == VS_DRV_INST_UNCONFIG) { 677 (void) snprintf(name, VS_NODENAME_LEN, "vscan%d", idx); 678 if (ddi_create_minor_node(vscan_drv_dip, name, 679 S_IFCHR, idx, DDI_PSEUDO, 0) == DDI_SUCCESS) { 680 vscan_drv_inst_state[idx] = VS_DRV_INST_INIT; 681 } else { 682 rc = B_FALSE; 683 } 684 DTRACE_PROBE2(vscan__minor__node, int, idx, int, rc); 685 } 686 687 mutex_exit(&vscan_drv_mutex); 688 689 return (rc); 690 }