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, /* ptr to linkage structures */ 215 NULL, 216 }; 217 218 219 /* 220 * _init 221 */ 222 int 223 _init(void) 224 { 225 int rc; 226 227 vscan_drv_inst_state_sz = 228 sizeof (vscan_drv_inst_state_t) * (vs_nodes_max + 1); 229 230 if (vscan_door_init() != 0) 231 return (DDI_FAILURE); 232 233 if (vscan_svc_init() != 0) { 234 vscan_door_fini(); 235 return (DDI_FAILURE); 236 } 237 238 mutex_init(&vscan_drv_mutex, NULL, MUTEX_DRIVER, NULL); 239 vscan_drv_inst_state = kmem_zalloc(vscan_drv_inst_state_sz, KM_SLEEP); 240 241 cv_init(&vscan_drv_cv, NULL, CV_DEFAULT, NULL); 242 243 if ((rc = mod_install(&modlinkage)) != 0) { 244 vscan_door_fini(); 245 vscan_svc_fini(); 246 kmem_free(vscan_drv_inst_state, vscan_drv_inst_state_sz); 247 cv_destroy(&vscan_drv_cv); 248 mutex_destroy(&vscan_drv_mutex); 249 } 250 251 return (rc); 252 } 253 254 255 /* 256 * _info 257 */ 258 int 259 _info(struct modinfo *modinfop) 260 { 261 return (mod_info(&modlinkage, modinfop)); 262 } 263 264 265 /* 266 * _fini 267 */ 268 int 269 _fini(void) 270 { 271 int rc; 272 273 if (vscan_drv_in_use()) 274 return (EBUSY); 275 276 if ((rc = mod_remove(&modlinkage)) == 0) { 277 vscan_door_fini(); 278 vscan_svc_fini(); 279 kmem_free(vscan_drv_inst_state, vscan_drv_inst_state_sz); 280 cv_destroy(&vscan_drv_cv); 281 mutex_destroy(&vscan_drv_mutex); 282 } 283 284 return (rc); 285 } 286 287 288 /* 289 * DDI entry points. 290 */ 291 292 /* 293 * vscan_drv_getinfo 294 */ 295 /* ARGSUSED */ 296 static int 297 vscan_drv_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result) 298 { 299 ulong_t inst = getminor((dev_t)arg); 300 301 switch (cmd) { 302 case DDI_INFO_DEVT2DEVINFO: 303 *result = vscan_drv_dip; 304 return (DDI_SUCCESS); 305 case DDI_INFO_DEVT2INSTANCE: 306 *result = (void *)inst; 307 return (DDI_SUCCESS); 308 } 309 return (DDI_FAILURE); 310 } 311 312 313 /* 314 * vscan_drv_attach 315 */ 316 static int 317 vscan_drv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 318 { 319 if (cmd != DDI_ATTACH) 320 return (DDI_FAILURE); 321 322 if (ddi_get_instance(dip) != 0) 323 return (DDI_FAILURE); 324 325 vscan_drv_dip = dip; 326 327 /* create minor node 0 for daemon-driver synchronization */ 328 if (vscan_drv_create_node(0) == B_FALSE) 329 return (DDI_FAILURE); 330 331 vscan_drv_state = VS_DRV_IDLE; 332 return (DDI_SUCCESS); 333 } 334 335 336 /* 337 * vscan_drv_detach 338 */ 339 static int 340 vscan_drv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 341 { 342 int i; 343 344 if (cmd != DDI_DETACH) 345 return (DDI_FAILURE); 346 347 if (ddi_get_instance(dip) != 0) 348 return (DDI_FAILURE); 349 350 if (vscan_drv_in_use()) 351 return (DDI_FAILURE); 352 353 /* remove all minor nodes */ 354 vscan_drv_dip = NULL; 355 ddi_remove_minor_node(dip, NULL); 356 for (i = 0; i <= vs_nodes_max; i++) 357 vscan_drv_inst_state[i] = VS_DRV_INST_UNCONFIG; 358 359 vscan_drv_state = VS_DRV_UNCONFIG; 360 return (DDI_SUCCESS); 361 } 362 363 364 /* 365 * vscan_drv_in_use 366 * 367 * If the driver state is not IDLE or UNCONFIG then the 368 * driver is in use. Otherwise, check the service interface 369 * (vscan_svc) to see if it is still in use - for example 370 * there there may be requests still in progress. 371 */ 372 static boolean_t 373 vscan_drv_in_use() 374 { 375 boolean_t in_use = B_FALSE; 376 377 mutex_enter(&vscan_drv_mutex); 378 if ((vscan_drv_state != VS_DRV_IDLE) && 379 (vscan_drv_state != VS_DRV_UNCONFIG)) { 380 in_use = B_TRUE; 381 } 382 mutex_exit(&vscan_drv_mutex); 383 384 if (in_use) 385 return (B_TRUE); 386 else 387 return (vscan_svc_in_use()); 388 } 389 390 391 /* 392 * vscan_drv_open 393 * 394 * If inst == 0, this is vscand initializing. 395 * If the driver is in DELAYED_DISABLE, ie vscand previously 396 * disconnected without a clean shutdown and the driver is 397 * waiting for a period to allow vscand to reconnect, signal 398 * vscan_drv_cv to cancel the delayed disable. 399 * 400 * If inst != 0, open the file associated with inst. 401 */ 402 /* ARGSUSED */ 403 static int 404 vscan_drv_open(dev_t *devp, int flag, int otyp, cred_t *credp) 405 { 406 int rc; 407 int inst = getminor(*devp); 408 409 if ((inst < 0) || (inst > vs_nodes_max)) 410 return (EINVAL); 411 412 /* check if caller has privilege for virus scanning */ 413 if ((rc = secpolicy_vscan(credp)) != 0) { 414 DTRACE_PROBE1(vscan__priv, int, rc); 415 return (EPERM); 416 } 417 418 mutex_enter(&vscan_drv_mutex); 419 if (inst == 0) { 420 switch (vscan_drv_state) { 421 case VS_DRV_IDLE: 422 vscan_drv_state = VS_DRV_CONNECTED; 423 break; 424 case VS_DRV_DELAYED_DISABLE: 425 cv_signal(&vscan_drv_cv); 426 vscan_drv_state = VS_DRV_CONNECTED; 427 break; 428 default: 429 DTRACE_PROBE1(vscan__drv__state__violation, 430 int, vscan_drv_state); 431 mutex_exit(&vscan_drv_mutex); 432 return (EINVAL); 433 } 434 } else { 435 if ((vscan_drv_state != VS_DRV_ENABLED) || 436 (vscan_drv_inst_state[inst] != VS_DRV_INST_INIT)) { 437 mutex_exit(&vscan_drv_mutex); 438 return (EINVAL); 439 } 440 vscan_drv_inst_state[inst] = VS_DRV_INST_OPEN; 441 } 442 mutex_exit(&vscan_drv_mutex); 443 444 return (0); 445 } 446 447 448 /* 449 * vscan_drv_close 450 * 451 * If inst == 0, this is vscand detaching. 452 * If the driver is in ENABLED state vscand has terminated without 453 * a clean shutdown (nod DISABLE received). Enter DELAYED_DISABLE 454 * state and initiate a delayed disable to allow vscand time to 455 * reconnect. 456 * 457 * If inst != 0, close the file associated with inst 458 */ 459 /* ARGSUSED */ 460 static int 461 vscan_drv_close(dev_t dev, int flag, int otyp, cred_t *credp) 462 { 463 int i, inst = getminor(dev); 464 465 if ((inst < 0) || (inst > vs_nodes_max)) 466 return (EINVAL); 467 468 mutex_enter(&vscan_drv_mutex); 469 if (inst != 0) { 470 vscan_drv_inst_state[inst] = VS_DRV_INST_INIT; 471 mutex_exit(&vscan_drv_mutex); 472 return (0); 473 } 474 475 /* instance 0 - daemon disconnect */ 476 if ((vscan_drv_state != VS_DRV_CONNECTED) && 477 (vscan_drv_state != VS_DRV_ENABLED)) { 478 DTRACE_PROBE1(vscan__drv__state__violation, 479 int, vscan_drv_state); 480 mutex_exit(&vscan_drv_mutex); 481 return (EINVAL); 482 } 483 484 for (i = 1; i <= vs_nodes_max; i++) { 485 if (vscan_drv_inst_state[i] != VS_DRV_INST_UNCONFIG) 486 vscan_drv_inst_state[i] = VS_DRV_INST_INIT; 487 } 488 489 if (vscan_drv_state == VS_DRV_CONNECTED) { 490 vscan_drv_state = VS_DRV_IDLE; 491 } else { /* VS_DRV_ENABLED */ 492 cmn_err(CE_WARN, "Detected vscand exit without clean shutdown"); 493 if (thread_create(NULL, 0, vscan_drv_delayed_disable, 494 0, 0, &p0, TS_RUN, minclsyspri) == NULL) { 495 vscan_svc_disable(); 496 vscan_drv_state = VS_DRV_IDLE; 497 } else { 498 vscan_drv_state = VS_DRV_DELAYED_DISABLE; 499 } 500 } 501 mutex_exit(&vscan_drv_mutex); 502 503 vscan_svc_scan_abort(); 504 vscan_door_close(); 505 return (0); 506 } 507 508 509 /* 510 * vscan_drv_delayed_disable 511 * 512 * Invoked from vscan_drv_close if the daemon disconnects 513 * without first sending disable (e.g. daemon crashed). 514 * Delays for vs_reconnect_timeout before disabling, to allow 515 * the daemon to reconnect. During this time, scan requests 516 * will be processed locally (see vscan_svc.c) 517 */ 518 static void 519 vscan_drv_delayed_disable(void) 520 { 521 mutex_enter(&vscan_drv_mutex); 522 (void) cv_reltimedwait(&vscan_drv_cv, &vscan_drv_mutex, 523 SEC_TO_TICK(vs_reconnect_timeout), TR_CLOCK_TICK); 524 525 if (vscan_drv_state == VS_DRV_DELAYED_DISABLE) { 526 vscan_svc_disable(); 527 vscan_drv_state = VS_DRV_IDLE; 528 } else { 529 DTRACE_PROBE(vscan__reconnect); 530 } 531 mutex_exit(&vscan_drv_mutex); 532 } 533 534 535 /* 536 * vscan_drv_read 537 */ 538 /* ARGSUSED */ 539 static int 540 vscan_drv_read(dev_t dev, struct uio *uiop, cred_t *credp) 541 { 542 int rc; 543 int inst = getminor(dev); 544 vnode_t *vp; 545 546 if ((inst <= 0) || (inst > vs_nodes_max)) 547 return (EINVAL); 548 549 mutex_enter(&vscan_drv_mutex); 550 if ((vscan_drv_state != VS_DRV_ENABLED) || 551 (vscan_drv_inst_state[inst] != VS_DRV_INST_OPEN)) { 552 mutex_exit(&vscan_drv_mutex); 553 return (EINVAL); 554 } 555 vscan_drv_inst_state[inst] = VS_DRV_INST_READING; 556 mutex_exit(&vscan_drv_mutex); 557 558 if ((vp = vscan_svc_get_vnode(inst)) == NULL) 559 return (EINVAL); 560 561 (void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, NULL); 562 rc = VOP_READ(vp, uiop, 0, kcred, NULL); 563 VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL); 564 565 mutex_enter(&vscan_drv_mutex); 566 if (vscan_drv_inst_state[inst] == VS_DRV_INST_READING) 567 vscan_drv_inst_state[inst] = VS_DRV_INST_OPEN; 568 mutex_exit(&vscan_drv_mutex); 569 570 return (rc); 571 } 572 573 574 /* 575 * vscan_drv_ioctl 576 * 577 * Process ioctls from vscand: 578 * VS_IOCTL_ENABLE - vscand is ready to handle scan requests, 579 * enable VFS interface. 580 * VS_IOCTL_DISABLE - vscand is shutting down, disable VFS interface 581 * VS_IOCTL_RESULT - scan response data 582 * VS_IOCTL_CONFIG - configuration data from vscand 583 * VS_IOCTL_MAX_REQ - provide the max request idx to vscand, 584 * to allow vscand to set appropriate resource allocation limits 585 */ 586 /* ARGSUSED */ 587 static int 588 vscan_drv_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, 589 cred_t *credp, int *rvalp) 590 { 591 int inst = getminor(dev); 592 vs_config_t conf; 593 vs_scan_rsp_t rsp; 594 595 if (inst != 0) 596 return (EINVAL); 597 598 switch (cmd) { 599 case VS_IOCTL_ENABLE: 600 mutex_enter(&vscan_drv_mutex); 601 if (vscan_drv_state != VS_DRV_CONNECTED) { 602 DTRACE_PROBE1(vscan__drv__state__violation, 603 int, vscan_drv_state); 604 mutex_exit(&vscan_drv_mutex); 605 return (EINVAL); 606 } 607 if ((vscan_door_open((int)arg) != 0) || 608 (vscan_svc_enable() != 0)) { 609 mutex_exit(&vscan_drv_mutex); 610 return (EINVAL); 611 } 612 vscan_drv_state = VS_DRV_ENABLED; 613 mutex_exit(&vscan_drv_mutex); 614 break; 615 616 case VS_IOCTL_DISABLE: 617 mutex_enter(&vscan_drv_mutex); 618 if (vscan_drv_state != VS_DRV_ENABLED) { 619 DTRACE_PROBE1(vscan__drv__state__violation, 620 int, vscan_drv_state); 621 mutex_exit(&vscan_drv_mutex); 622 return (EINVAL); 623 } 624 vscan_svc_disable(); 625 vscan_drv_state = VS_DRV_CONNECTED; 626 mutex_exit(&vscan_drv_mutex); 627 break; 628 629 case VS_IOCTL_RESULT: 630 if (ddi_copyin((void *)arg, &rsp, 631 sizeof (vs_scan_rsp_t), 0) == -1) 632 return (EFAULT); 633 else 634 vscan_svc_scan_result(&rsp); 635 break; 636 637 case VS_IOCTL_CONFIG: 638 if (ddi_copyin((void *)arg, &conf, 639 sizeof (vs_config_t), 0) == -1) 640 return (EFAULT); 641 if (vscan_svc_configure(&conf) == -1) 642 return (EINVAL); 643 break; 644 645 case VS_IOCTL_MAX_REQ: 646 if (ddi_copyout(&vs_nodes_max, (void *)arg, 647 sizeof (uint32_t), 0) == -1) 648 return (EFAULT); 649 break; 650 651 default: 652 return (ENOTTY); 653 } 654 655 return (0); 656 } 657 658 659 /* 660 * vscan_drv_create_node 661 * 662 * Create minor node with which vscan daemon will communicate 663 * to access a file. Invoked from vscan_svc before scan request 664 * sent up to daemon. 665 * Minor node 0 is reserved for daemon-driver synchronization 666 * and is created during attach. 667 * All minor nodes are removed during detach. 668 */ 669 boolean_t 670 vscan_drv_create_node(int idx) 671 { 672 char name[VS_NODENAME_LEN]; 673 boolean_t rc = B_TRUE; 674 675 mutex_enter(&vscan_drv_mutex); 676 677 if (vscan_drv_inst_state[idx] == VS_DRV_INST_UNCONFIG) { 678 (void) snprintf(name, VS_NODENAME_LEN, "vscan%d", idx); 679 if (ddi_create_minor_node(vscan_drv_dip, name, 680 S_IFCHR, idx, DDI_PSEUDO, 0) == DDI_SUCCESS) { 681 vscan_drv_inst_state[idx] = VS_DRV_INST_INIT; 682 } else { 683 rc = B_FALSE; 684 } 685 DTRACE_PROBE2(vscan__minor__node, int, idx, int, rc); 686 } 687 688 mutex_exit(&vscan_drv_mutex); 689 690 return (rc); 691 } 692