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