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/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_connected = B_FALSE; /* vscand daemon connected */ 66 67 static dev_info_t *vscan_drv_dip; 68 static kmutex_t vscan_drv_mutex; 69 70 /* 71 * DDI entry points. 72 */ 73 static int vscan_drv_attach(dev_info_t *, ddi_attach_cmd_t); 74 static int vscan_drv_detach(dev_info_t *, ddi_detach_cmd_t); 75 static int vscan_drv_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 76 static int vscan_drv_open(dev_t *, int, int, cred_t *); 77 static int vscan_drv_close(dev_t, int, int, cred_t *); 78 static int vscan_drv_read(dev_t, struct uio *, cred_t *); 79 static int vscan_drv_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 80 81 static boolean_t vscan_drv_in_use(); 82 83 84 /* 85 * module linkage info for the kernel 86 */ 87 88 static struct cb_ops cbops = { 89 vscan_drv_open, /* cb_open */ 90 vscan_drv_close, /* cb_close */ 91 nodev, /* cb_strategy */ 92 nodev, /* cb_print */ 93 nodev, /* cb_dump */ 94 vscan_drv_read, /* cb_read */ 95 nodev, /* cb_write */ 96 vscan_drv_ioctl, /* cb_ioctl */ 97 nodev, /* cb_devmap */ 98 nodev, /* cb_mmap */ 99 nodev, /* cb_segmap */ 100 nochpoll, /* cb_chpoll */ 101 ddi_prop_op, /* cb_prop_op */ 102 NULL, /* cb_streamtab */ 103 D_MP, /* cb_flag */ 104 CB_REV, /* cb_rev */ 105 nodev, /* cb_aread */ 106 nodev, /* cb_awrite */ 107 }; 108 109 static struct dev_ops devops = { 110 DEVO_REV, /* devo_rev */ 111 0, /* devo_refcnt */ 112 vscan_drv_getinfo, /* devo_getinfo */ 113 nulldev, /* devo_identify */ 114 nulldev, /* devo_probe */ 115 vscan_drv_attach, /* devo_attach */ 116 vscan_drv_detach, /* devo_detach */ 117 nodev, /* devo_reset */ 118 &cbops, /* devo_cb_ops */ 119 NULL, /* devo_bus_ops */ 120 NULL, /* devo_power */ 121 }; 122 123 static struct modldrv modldrv = { 124 &mod_driverops, /* drv_modops */ 125 "virus scanning", /* drv_linkinfo */ 126 &devops, 127 }; 128 129 static struct modlinkage modlinkage = { 130 131 MODREV_1, /* revision of the module, must be: MODREV_1 */ 132 &modldrv, /* ptr to linkage structures */ 133 NULL, 134 }; 135 136 137 /* 138 * _init 139 */ 140 int 141 _init(void) 142 { 143 int rc; 144 145 mutex_init(&vscan_drv_mutex, NULL, MUTEX_DRIVER, NULL); 146 147 if (vscan_door_init() != 0) { 148 mutex_destroy(&vscan_drv_mutex); 149 return (DDI_FAILURE); 150 } 151 152 if (vscan_svc_init() != 0) { 153 vscan_door_fini(); 154 mutex_destroy(&vscan_drv_mutex); 155 return (DDI_FAILURE); 156 } 157 158 (void) memset(&vscan_drv_state, 0, sizeof (vscan_drv_state)); 159 160 if ((rc = mod_install(&modlinkage)) != 0) { 161 vscan_door_fini(); 162 vscan_svc_fini(); 163 mutex_destroy(&vscan_drv_mutex); 164 } 165 166 return (rc); 167 } 168 169 170 /* 171 * _info 172 */ 173 int 174 _info(struct modinfo *modinfop) 175 { 176 return (mod_info(&modlinkage, modinfop)); 177 } 178 179 180 /* 181 * _fini 182 */ 183 int 184 _fini(void) 185 { 186 int rc; 187 188 if (vscan_drv_in_use()) 189 return (EBUSY); 190 191 if ((rc = mod_remove(&modlinkage)) == 0) { 192 vscan_door_fini(); 193 vscan_svc_fini(); 194 mutex_destroy(&vscan_drv_mutex); 195 } 196 197 return (rc); 198 } 199 200 201 /* 202 * DDI entry points. 203 */ 204 205 /* 206 * vscan_drv_getinfo 207 */ 208 /* ARGSUSED */ 209 static int 210 vscan_drv_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result) 211 { 212 ulong_t inst = getminor((dev_t)arg); 213 214 switch (cmd) { 215 case DDI_INFO_DEVT2DEVINFO: 216 *result = vscan_drv_dip; 217 return (DDI_SUCCESS); 218 case DDI_INFO_DEVT2INSTANCE: 219 *result = (void *)inst; 220 return (DDI_SUCCESS); 221 } 222 return (DDI_FAILURE); 223 } 224 225 226 /* 227 * vscan_drv_attach 228 */ 229 static int 230 vscan_drv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 231 { 232 int i; 233 char name[VS_DRV_NODENAME_LEN]; 234 235 if (cmd != DDI_ATTACH) 236 return (DDI_FAILURE); 237 238 if (ddi_get_instance(dip) != 0) 239 return (DDI_FAILURE); 240 241 vscan_drv_dip = dip; 242 243 /* create the minor nodes */ 244 for (i = 0; i <= VS_DRV_MAX_FILES; i++) { 245 (void) snprintf(name, VS_DRV_NODENAME_LEN, "vscan%d", i); 246 if (ddi_create_minor_node(dip, name, S_IFCHR, i, 247 DDI_PSEUDO, 0) != DDI_SUCCESS) { 248 ddi_remove_minor_node(dip, NULL); 249 return (DDI_FAILURE); 250 } 251 } 252 253 return (DDI_SUCCESS); 254 } 255 256 257 /* 258 * vscan_drv_detach 259 */ 260 static int 261 vscan_drv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 262 { 263 if (cmd != DDI_DETACH) 264 return (DDI_FAILURE); 265 266 if (ddi_get_instance(dip) != 0) 267 return (DDI_FAILURE); 268 269 if (vscan_drv_in_use()) 270 return (DDI_FAILURE); 271 272 vscan_drv_dip = NULL; 273 ddi_remove_minor_node(dip, NULL); 274 275 return (DDI_SUCCESS); 276 } 277 278 279 /* 280 * vscan_drv_in_use 281 */ 282 static boolean_t 283 vscan_drv_in_use() 284 { 285 if (vscan_drv_connected) 286 return (B_TRUE); 287 else 288 return (vscan_svc_in_use()); 289 } 290 291 292 /* 293 * vscan_drv_open 294 * if inst == 0, this is vscand initializing. 295 * Otherwise, open the file associated with inst. 296 */ 297 /* ARGSUSED */ 298 static int 299 vscan_drv_open(dev_t *devp, int flag, int otyp, cred_t *credp) 300 { 301 int rc; 302 int inst = getminor(*devp); 303 304 if ((inst < 0) || (inst > VS_DRV_MAX_FILES)) 305 return (EINVAL); 306 307 /* check if caller has privilege for virus scanning */ 308 if ((rc = secpolicy_vscan(credp)) != 0) { 309 DTRACE_PROBE1(vscan__priv, int, rc); 310 return (EPERM); 311 } 312 313 mutex_enter(&vscan_drv_mutex); 314 if (inst == 0) { 315 if (vscan_drv_connected) { 316 mutex_exit(&vscan_drv_mutex); 317 return (EINVAL); 318 } 319 vscan_drv_connected = B_TRUE; 320 } else { 321 if ((!vscan_drv_connected) || 322 (vscan_drv_state[inst] != VS_INIT)) { 323 mutex_exit(&vscan_drv_mutex); 324 return (EINVAL); 325 } 326 vscan_drv_state[inst] = VS_OPEN; 327 } 328 mutex_exit(&vscan_drv_mutex); 329 330 return (0); 331 } 332 333 334 /* 335 * vscan_drv_close 336 * if inst == 0, this is vscand detaching 337 * Otherwise close the file associated with inst 338 */ 339 /* ARGSUSED */ 340 static int 341 vscan_drv_close(dev_t dev, int flag, int otyp, cred_t *credp) 342 { 343 int i, inst = getminor(dev); 344 345 if ((inst < 0) || (inst > VS_DRV_MAX_FILES)) 346 return (EINVAL); 347 348 mutex_enter(&vscan_drv_mutex); 349 if (inst == 0) { 350 for (i = 1; i <= VS_DRV_MAX_FILES; i++) 351 vscan_drv_state[i] = VS_INIT; 352 353 vscan_drv_connected = B_FALSE; 354 vscan_svc_enable(B_FALSE); 355 vscan_door_close(); 356 } else { 357 vscan_drv_state[inst] = VS_INIT; 358 } 359 mutex_exit(&vscan_drv_mutex); 360 361 return (0); 362 } 363 364 365 /* 366 * vscan_drv_read 367 */ 368 /* ARGSUSED */ 369 static int 370 vscan_drv_read(dev_t dev, struct uio *uiop, cred_t *credp) 371 { 372 int rc; 373 int inst = getminor(dev); 374 vnode_t *vp; 375 376 if ((inst <= 0) || (inst > VS_DRV_MAX_FILES)) 377 return (EINVAL); 378 379 mutex_enter(&vscan_drv_mutex); 380 if ((!vscan_drv_connected) || (vscan_drv_state[inst] != VS_OPEN)) { 381 mutex_exit(&vscan_drv_mutex); 382 return (EINVAL); 383 } 384 vscan_drv_state[inst] = VS_READING; 385 mutex_exit(&vscan_drv_mutex); 386 387 if ((vp = vscan_svc_get_vnode(inst)) == NULL) 388 return (EINVAL); 389 390 (void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, NULL); 391 rc = VOP_READ(vp, uiop, 0, kcred, NULL); 392 VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL); 393 394 mutex_enter(&vscan_drv_mutex); 395 if (vscan_drv_state[inst] == VS_READING) 396 vscan_drv_state[inst] = VS_OPEN; 397 mutex_exit(&vscan_drv_mutex); 398 399 return (rc); 400 } 401 402 403 /* 404 * vscan_drv_ioctl 405 */ 406 /* ARGSUSED */ 407 static int 408 vscan_drv_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, 409 cred_t *credp, int *rvalp) 410 { 411 int inst = getminor(dev); 412 vs_config_t conf; 413 414 if (inst != 0) 415 return (EINVAL); 416 417 switch (cmd) { 418 case VS_DRV_IOCTL_ENABLE: 419 mutex_enter(&vscan_drv_mutex); 420 if ((!vscan_drv_connected) || 421 (vscan_door_open((int)arg) != 0)) { 422 mutex_exit(&vscan_drv_mutex); 423 return (EINVAL); 424 } 425 vscan_svc_enable(B_TRUE); 426 mutex_exit(&vscan_drv_mutex); 427 break; 428 case VS_DRV_IOCTL_DISABLE: 429 vscan_svc_enable(B_FALSE); 430 break; 431 case VS_DRV_IOCTL_CONFIG: 432 if (ddi_copyin((void *)arg, &conf, 433 sizeof (vs_config_t), 0) == -1) 434 return (EFAULT); 435 if (vscan_svc_configure(&conf) == -1) 436 return (EINVAL); 437 break; 438 default: 439 return (ENOTTY); 440 } 441 442 return (0); 443 } 444