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/vnode.h> 37 #include <fs/fs_subr.h> 38 #include <sys/types.h> 39 #include <sys/file.h> 40 #include <sys/disp.h> 41 #include <sys/vscan.h> 42 #include <sys/policy.h> 43 #include <sys/sdt.h> 44 45 #define VS_DRV_NODENAME_LEN 16 46 47 48 /* 49 * Instance States: VS_INIT (initial state), VS_OPEN, VS_READING 50 * 51 * Instance 0 controls the state of the driver: vscan_drv_connected. 52 * vscan_drv_state[0] should NOT be used. 53 * Actions: 54 * open: VS_INIT->VS_OPEN, otherwise ERROR 55 * close: any->VS_INIT 56 * read: VS_OPEN->VS_READING, otherwise ERROR 57 */ 58 typedef enum { 59 VS_INIT, 60 VS_OPEN, 61 VS_READING 62 } vscan_drv_state_t; 63 64 static vscan_drv_state_t vscan_drv_state[VS_DRV_MAX_FILES + 1]; 65 static boolean_t vscan_drv_nodes[VS_DRV_MAX_FILES + 1]; 66 static boolean_t vscan_drv_connected = B_FALSE; /* vscand daemon connected */ 67 68 static dev_info_t *vscan_drv_dip; 69 static kmutex_t vscan_drv_mutex; 70 static kcondvar_t vscan_drv_cv; /* wait for daemon reconnect */ 71 72 /* 73 * DDI entry points. 74 */ 75 static int vscan_drv_attach(dev_info_t *, ddi_attach_cmd_t); 76 static int vscan_drv_detach(dev_info_t *, ddi_detach_cmd_t); 77 static int vscan_drv_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 78 static int vscan_drv_open(dev_t *, int, int, cred_t *); 79 static int vscan_drv_close(dev_t, int, int, cred_t *); 80 static int vscan_drv_read(dev_t, struct uio *, cred_t *); 81 static int vscan_drv_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 82 83 static boolean_t vscan_drv_in_use(void); 84 static void vscan_drv_delayed_disable(void); 85 86 87 /* 88 * module linkage info for the kernel 89 */ 90 91 static struct cb_ops cbops = { 92 vscan_drv_open, /* cb_open */ 93 vscan_drv_close, /* cb_close */ 94 nodev, /* cb_strategy */ 95 nodev, /* cb_print */ 96 nodev, /* cb_dump */ 97 vscan_drv_read, /* cb_read */ 98 nodev, /* cb_write */ 99 vscan_drv_ioctl, /* cb_ioctl */ 100 nodev, /* cb_devmap */ 101 nodev, /* cb_mmap */ 102 nodev, /* cb_segmap */ 103 nochpoll, /* cb_chpoll */ 104 ddi_prop_op, /* cb_prop_op */ 105 NULL, /* cb_streamtab */ 106 D_MP, /* cb_flag */ 107 CB_REV, /* cb_rev */ 108 nodev, /* cb_aread */ 109 nodev, /* cb_awrite */ 110 }; 111 112 static struct dev_ops devops = { 113 DEVO_REV, /* devo_rev */ 114 0, /* devo_refcnt */ 115 vscan_drv_getinfo, /* devo_getinfo */ 116 nulldev, /* devo_identify */ 117 nulldev, /* devo_probe */ 118 vscan_drv_attach, /* devo_attach */ 119 vscan_drv_detach, /* devo_detach */ 120 nodev, /* devo_reset */ 121 &cbops, /* devo_cb_ops */ 122 NULL, /* devo_bus_ops */ 123 NULL, /* devo_power */ 124 }; 125 126 static struct modldrv modldrv = { 127 &mod_driverops, /* drv_modops */ 128 "virus scanning", /* drv_linkinfo */ 129 &devops, 130 }; 131 132 static struct modlinkage modlinkage = { 133 134 MODREV_1, /* revision of the module, must be: MODREV_1 */ 135 &modldrv, /* ptr to linkage structures */ 136 NULL, 137 }; 138 139 140 /* 141 * _init 142 */ 143 int 144 _init(void) 145 { 146 int rc; 147 148 mutex_init(&vscan_drv_mutex, NULL, MUTEX_DRIVER, NULL); 149 150 if (vscan_door_init() != 0) { 151 mutex_destroy(&vscan_drv_mutex); 152 return (DDI_FAILURE); 153 } 154 155 if (vscan_svc_init() != 0) { 156 vscan_door_fini(); 157 mutex_destroy(&vscan_drv_mutex); 158 return (DDI_FAILURE); 159 } 160 161 (void) memset(&vscan_drv_state, 0, sizeof (vscan_drv_state)); 162 (void) memset(&vscan_drv_nodes, 0, sizeof (vscan_drv_nodes)); 163 164 if ((rc = mod_install(&modlinkage)) != 0) { 165 vscan_door_fini(); 166 vscan_svc_fini(); 167 mutex_destroy(&vscan_drv_mutex); 168 } 169 170 cv_init(&vscan_drv_cv, NULL, CV_DEFAULT, NULL); 171 return (rc); 172 } 173 174 175 /* 176 * _info 177 */ 178 int 179 _info(struct modinfo *modinfop) 180 { 181 return (mod_info(&modlinkage, modinfop)); 182 } 183 184 185 /* 186 * _fini 187 */ 188 int 189 _fini(void) 190 { 191 int rc; 192 193 if (vscan_drv_in_use()) 194 return (EBUSY); 195 196 if ((rc = mod_remove(&modlinkage)) == 0) { 197 vscan_door_fini(); 198 vscan_svc_fini(); 199 cv_destroy(&vscan_drv_cv); 200 mutex_destroy(&vscan_drv_mutex); 201 } 202 203 return (rc); 204 } 205 206 207 /* 208 * DDI entry points. 209 */ 210 211 /* 212 * vscan_drv_getinfo 213 */ 214 /* ARGSUSED */ 215 static int 216 vscan_drv_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result) 217 { 218 ulong_t inst = getminor((dev_t)arg); 219 220 switch (cmd) { 221 case DDI_INFO_DEVT2DEVINFO: 222 *result = vscan_drv_dip; 223 return (DDI_SUCCESS); 224 case DDI_INFO_DEVT2INSTANCE: 225 *result = (void *)inst; 226 return (DDI_SUCCESS); 227 } 228 return (DDI_FAILURE); 229 } 230 231 232 /* 233 * vscan_drv_attach 234 */ 235 static int 236 vscan_drv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 237 { 238 if (cmd != DDI_ATTACH) 239 return (DDI_FAILURE); 240 241 if (ddi_get_instance(dip) != 0) 242 return (DDI_FAILURE); 243 244 vscan_drv_dip = dip; 245 246 /* create minor node 0 for daemon-driver synchronization */ 247 if (vscan_drv_create_node(0) == B_FALSE) 248 return (DDI_FAILURE); 249 250 return (DDI_SUCCESS); 251 } 252 253 254 /* 255 * vscan_drv_detach 256 */ 257 static int 258 vscan_drv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 259 { 260 if (cmd != DDI_DETACH) 261 return (DDI_FAILURE); 262 263 if (ddi_get_instance(dip) != 0) 264 return (DDI_FAILURE); 265 266 if (vscan_drv_in_use()) 267 return (DDI_FAILURE); 268 269 /* remove all minor nodes */ 270 vscan_drv_dip = NULL; 271 ddi_remove_minor_node(dip, NULL); 272 (void) memset(&vscan_drv_nodes, 0, sizeof (vscan_drv_nodes)); 273 274 return (DDI_SUCCESS); 275 } 276 277 278 /* 279 * vscan_drv_in_use 280 * 281 * If vscand is connected (vscan_drv_connected == B_TRUE) the 282 * vscan driver is obviously in use. Otherwise invoke 283 * vscan_svc_in_use() to determine if the driver is in use, 284 * even though the daemon has disconnected. 285 * For example, there may be requests not yet complete, or 286 * the driver may still be enabled waiting for the daemon to 287 * reconnect. 288 * Used to determine whether the driver can be unloaded. 289 */ 290 static boolean_t 291 vscan_drv_in_use() 292 { 293 boolean_t in_use; 294 295 mutex_enter(&vscan_drv_mutex); 296 in_use = vscan_drv_connected; 297 mutex_exit(&vscan_drv_mutex); 298 299 if (in_use == B_FALSE) 300 in_use = vscan_svc_in_use(); 301 302 return (in_use); 303 } 304 305 306 /* 307 * vscan_drv_open 308 * if inst == 0, this is vscand initializing. 309 * Otherwise, open the file associated with inst. 310 */ 311 /* ARGSUSED */ 312 static int 313 vscan_drv_open(dev_t *devp, int flag, int otyp, cred_t *credp) 314 { 315 int rc; 316 int inst = getminor(*devp); 317 318 if ((inst < 0) || (inst > VS_DRV_MAX_FILES)) 319 return (EINVAL); 320 321 /* check if caller has privilege for virus scanning */ 322 if ((rc = secpolicy_vscan(credp)) != 0) { 323 DTRACE_PROBE1(vscan__priv, int, rc); 324 return (EPERM); 325 } 326 327 mutex_enter(&vscan_drv_mutex); 328 if (inst == 0) { 329 if (vscan_drv_connected) { 330 mutex_exit(&vscan_drv_mutex); 331 return (EINVAL); 332 } 333 vscan_drv_connected = B_TRUE; 334 /* wake any pending delayed disable */ 335 cv_signal(&vscan_drv_cv); 336 } else { 337 if ((!vscan_drv_connected) || 338 (vscan_drv_state[inst] != VS_INIT)) { 339 mutex_exit(&vscan_drv_mutex); 340 return (EINVAL); 341 } 342 vscan_drv_state[inst] = VS_OPEN; 343 } 344 mutex_exit(&vscan_drv_mutex); 345 346 return (0); 347 } 348 349 350 /* 351 * vscan_drv_close 352 * if inst == 0, this is vscand detaching 353 * Otherwise close the file associated with inst 354 */ 355 /* ARGSUSED */ 356 static int 357 vscan_drv_close(dev_t dev, int flag, int otyp, cred_t *credp) 358 { 359 int i, inst = getminor(dev); 360 361 if ((inst < 0) || (inst > VS_DRV_MAX_FILES)) 362 return (EINVAL); 363 364 mutex_enter(&vscan_drv_mutex); 365 if (inst == 0) { 366 for (i = 1; i <= VS_DRV_MAX_FILES; i++) 367 vscan_drv_state[i] = VS_INIT; 368 369 vscan_drv_connected = B_FALSE; 370 if (vscan_svc_is_enabled()) { 371 if (thread_create(NULL, 0, vscan_drv_delayed_disable, 372 0, 0, &p0, TS_RUN, minclsyspri) == NULL) { 373 vscan_svc_enable(); 374 } 375 } 376 vscan_door_close(); 377 } else { 378 vscan_drv_state[inst] = VS_INIT; 379 } 380 mutex_exit(&vscan_drv_mutex); 381 382 return (0); 383 } 384 385 386 /* 387 * vscan_drv_delayed_disable 388 * 389 * Invoked from vscan_drv_close if the daemon disconnects 390 * without first sending disable (e.g. daemon crashed). 391 * Delays for VS_DAEMON_WAIT_SEC before disabling, to allow 392 * the daemon to reconnect. During this time, scan requests 393 * will be processed locally (see vscan_svc.c) 394 */ 395 static void 396 vscan_drv_delayed_disable(void) 397 { 398 clock_t timeout = lbolt + SEC_TO_TICK(VS_DAEMON_WAIT_SEC); 399 400 mutex_enter(&vscan_drv_mutex); 401 (void) cv_timedwait(&vscan_drv_cv, &vscan_drv_mutex, timeout); 402 403 if (vscan_drv_connected) { 404 DTRACE_PROBE(vscan__reconnect); 405 } else { 406 vscan_svc_disable(); 407 } 408 mutex_exit(&vscan_drv_mutex); 409 } 410 411 412 /* 413 * vscan_drv_read 414 */ 415 /* ARGSUSED */ 416 static int 417 vscan_drv_read(dev_t dev, struct uio *uiop, cred_t *credp) 418 { 419 int rc; 420 int inst = getminor(dev); 421 vnode_t *vp; 422 423 if ((inst <= 0) || (inst > VS_DRV_MAX_FILES)) 424 return (EINVAL); 425 426 mutex_enter(&vscan_drv_mutex); 427 if ((!vscan_drv_connected) || (vscan_drv_state[inst] != VS_OPEN)) { 428 mutex_exit(&vscan_drv_mutex); 429 return (EINVAL); 430 } 431 vscan_drv_state[inst] = VS_READING; 432 mutex_exit(&vscan_drv_mutex); 433 434 if ((vp = vscan_svc_get_vnode(inst)) == NULL) 435 return (EINVAL); 436 437 (void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, NULL); 438 rc = VOP_READ(vp, uiop, 0, kcred, NULL); 439 VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL); 440 441 mutex_enter(&vscan_drv_mutex); 442 if (vscan_drv_state[inst] == VS_READING) 443 vscan_drv_state[inst] = VS_OPEN; 444 mutex_exit(&vscan_drv_mutex); 445 446 return (rc); 447 } 448 449 450 /* 451 * vscan_drv_ioctl 452 */ 453 /* ARGSUSED */ 454 static int 455 vscan_drv_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, 456 cred_t *credp, int *rvalp) 457 { 458 int inst = getminor(dev); 459 vs_config_t conf; 460 461 if (inst != 0) 462 return (EINVAL); 463 464 switch (cmd) { 465 case VS_DRV_IOCTL_ENABLE: 466 mutex_enter(&vscan_drv_mutex); 467 if ((!vscan_drv_connected) || 468 (vscan_door_open((int)arg) != 0)) { 469 mutex_exit(&vscan_drv_mutex); 470 return (EINVAL); 471 } 472 vscan_svc_enable(); 473 mutex_exit(&vscan_drv_mutex); 474 break; 475 case VS_DRV_IOCTL_DISABLE: 476 vscan_svc_disable(); 477 break; 478 case VS_DRV_IOCTL_CONFIG: 479 if (ddi_copyin((void *)arg, &conf, 480 sizeof (vs_config_t), 0) == -1) 481 return (EFAULT); 482 if (vscan_svc_configure(&conf) == -1) 483 return (EINVAL); 484 break; 485 default: 486 return (ENOTTY); 487 } 488 489 return (0); 490 } 491 492 493 /* 494 * vscan_drv_create_node 495 * 496 * Create minor node with which vscan daemon will communicate 497 * to access a file. Invoked from vscan_svc before scan request 498 * sent up to daemon. 499 * Minor node 0 is reserved for daemon-driver synchronization 500 * and is created during attach. 501 * All minor nodes are removed during detach. 502 */ 503 boolean_t 504 vscan_drv_create_node(int idx) 505 { 506 char name[VS_DRV_NODENAME_LEN]; 507 boolean_t *pnode, rc; 508 509 mutex_enter(&vscan_drv_mutex); 510 511 pnode = &vscan_drv_nodes[idx]; 512 if (*pnode == B_FALSE) { 513 (void) snprintf(name, VS_DRV_NODENAME_LEN, "vscan%d", idx); 514 if (ddi_create_minor_node(vscan_drv_dip, name, 515 S_IFCHR, idx, DDI_PSEUDO, 0) == DDI_SUCCESS) { 516 *pnode = B_TRUE; 517 } 518 DTRACE_PROBE2(vscan__minor__node, int, idx, int, *pnode); 519 } 520 521 rc = *pnode; 522 mutex_exit(&vscan_drv_mutex); 523 524 return (rc); 525 } 526