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 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 29 /* All Rights Reserved */ 30 31 32 /* 33 * STREAMS Administrative Driver 34 * 35 * Currently only handles autopush and module name verification. 36 */ 37 38 #include <sys/types.h> 39 #include <sys/param.h> 40 #include <sys/errno.h> 41 #include <sys/stream.h> 42 #include <sys/stropts.h> 43 #include <sys/strsubr.h> 44 #include <sys/strsun.h> 45 #include <sys/conf.h> 46 #include <sys/sad.h> 47 #include <sys/cred.h> 48 #include <sys/debug.h> 49 #include <sys/ddi.h> 50 #include <sys/sunddi.h> 51 #include <sys/stat.h> 52 #include <sys/cmn_err.h> 53 #include <sys/systm.h> 54 #include <sys/modctl.h> 55 #include <sys/priv_names.h> 56 #include <sys/sysmacros.h> 57 58 static int sadopen(queue_t *, dev_t *, int, int, cred_t *); 59 static int sadclose(queue_t *, int, cred_t *); 60 static int sadwput(queue_t *qp, mblk_t *mp); 61 62 static int sad_info(dev_info_t *, ddi_info_cmd_t, void *, void **); 63 static int sad_attach(dev_info_t *, ddi_attach_cmd_t); 64 65 static void apush_ioctl(), apush_iocdata(); 66 static void vml_ioctl(), vml_iocdata(); 67 static int valid_major(major_t); 68 69 static dev_info_t *sad_dip; /* private copy of devinfo pointer */ 70 71 static struct module_info sad_minfo = { 72 0x7361, "sad", 0, INFPSZ, 0, 0 73 }; 74 75 static struct qinit sad_rinit = { 76 NULL, NULL, sadopen, sadclose, NULL, &sad_minfo, NULL 77 }; 78 79 static struct qinit sad_winit = { 80 sadwput, NULL, NULL, NULL, NULL, &sad_minfo, NULL 81 }; 82 83 struct streamtab sadinfo = { 84 &sad_rinit, &sad_winit, NULL, NULL 85 }; 86 87 DDI_DEFINE_STREAM_OPS(sad_ops, nulldev, nulldev, sad_attach, 88 nodev, nodev, sad_info, D_MTPERQ | D_MP, &sadinfo); 89 90 /* 91 * Module linkage information for the kernel. 92 */ 93 94 static struct modldrv modldrv = { 95 &mod_driverops, /* Type of module. This one is a pseudo driver */ 96 "STREAMS Administrative Driver 'sad' %I%", 97 &sad_ops, /* driver ops */ 98 }; 99 100 static struct modlinkage modlinkage = { 101 MODREV_1, &modldrv, NULL 102 }; 103 104 int 105 _init(void) 106 { 107 return (mod_install(&modlinkage)); 108 } 109 110 int 111 _fini(void) 112 { 113 return (mod_remove(&modlinkage)); 114 } 115 116 int 117 _info(struct modinfo *modinfop) 118 { 119 return (mod_info(&modlinkage, modinfop)); 120 } 121 122 static int 123 sad_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 124 { 125 int instance = ddi_get_instance(devi); 126 127 if (cmd != DDI_ATTACH) 128 return (DDI_FAILURE); 129 130 ASSERT(instance == 0); 131 if (instance != 0) 132 return (DDI_FAILURE); 133 134 if (ddi_create_minor_node(devi, "user", S_IFCHR, 135 0, DDI_PSEUDO, NULL) == DDI_FAILURE) { 136 return (DDI_FAILURE); 137 } 138 if (ddi_create_priv_minor_node(devi, "admin", S_IFCHR, 139 1, DDI_PSEUDO, PRIVONLY_DEV, PRIV_SYS_CONFIG, 140 PRIV_SYS_CONFIG, 0666) == DDI_FAILURE) { 141 ddi_remove_minor_node(devi, NULL); 142 return (DDI_FAILURE); 143 } 144 sad_dip = devi; 145 return (DDI_SUCCESS); 146 } 147 148 /* ARGSUSED */ 149 static int 150 sad_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 151 { 152 int error; 153 154 switch (infocmd) { 155 case DDI_INFO_DEVT2DEVINFO: 156 if (sad_dip == NULL) { 157 error = DDI_FAILURE; 158 } else { 159 *result = sad_dip; 160 error = DDI_SUCCESS; 161 } 162 break; 163 case DDI_INFO_DEVT2INSTANCE: 164 *result = (void *)0; 165 error = DDI_SUCCESS; 166 break; 167 default: 168 error = DDI_FAILURE; 169 } 170 return (error); 171 } 172 173 174 /* 175 * sadopen() - 176 * Allocate a sad device. Only one 177 * open at a time allowed per device. 178 */ 179 /* ARGSUSED */ 180 static int 181 sadopen( 182 queue_t *qp, /* pointer to read queue */ 183 dev_t *devp, /* major/minor device of stream */ 184 int flag, /* file open flags */ 185 int sflag, /* stream open flags */ 186 cred_t *credp) /* user credentials */ 187 { 188 int i; 189 190 if (sflag) /* no longer called from clone driver */ 191 return (EINVAL); 192 193 /* 194 * Both USRMIN and ADMMIN are clone interfaces. 195 */ 196 for (i = 0; i < sadcnt; i++) 197 if (saddev[i].sa_qp == NULL) 198 break; 199 if (i >= sadcnt) /* no such device */ 200 return (ENXIO); 201 202 switch (getminor(*devp)) { 203 case USRMIN: /* mere mortal */ 204 saddev[i].sa_flags = 0; 205 break; 206 207 case ADMMIN: /* privileged user */ 208 saddev[i].sa_flags = SADPRIV; 209 break; 210 211 default: 212 return (EINVAL); 213 } 214 215 saddev[i].sa_qp = qp; 216 qp->q_ptr = (caddr_t)&saddev[i]; 217 WR(qp)->q_ptr = (caddr_t)&saddev[i]; 218 219 /* 220 * NOTE: should the ADMMIN or USRMIN minors change 221 * then so should the offset of 2 below 222 * Both USRMIN and ADMMIN are clone interfaces and 223 * therefore their minor numbers (0 and 1) are reserved. 224 */ 225 *devp = makedevice(getemajor(*devp), i + 2); 226 qprocson(qp); 227 return (0); 228 } 229 230 /* 231 * sadclose() - 232 * Clean up the data structures. 233 */ 234 /* ARGSUSED */ 235 static int 236 sadclose( 237 queue_t *qp, /* pointer to read queue */ 238 int flag, /* file open flags */ 239 cred_t *credp) /* user credentials */ 240 { 241 struct saddev *sadp; 242 243 qprocsoff(qp); 244 sadp = (struct saddev *)qp->q_ptr; 245 sadp->sa_qp = NULL; 246 sadp->sa_addr = NULL; 247 qp->q_ptr = NULL; 248 WR(qp)->q_ptr = NULL; 249 return (0); 250 } 251 252 /* 253 * sadwput() - 254 * Write side put procedure. 255 */ 256 static int 257 sadwput( 258 queue_t *qp, /* pointer to write queue */ 259 mblk_t *mp) /* message pointer */ 260 { 261 struct iocblk *iocp; 262 263 switch (mp->b_datap->db_type) { 264 case M_FLUSH: 265 if (*mp->b_rptr & FLUSHR) { 266 *mp->b_rptr &= ~FLUSHW; 267 qreply(qp, mp); 268 } else 269 freemsg(mp); 270 break; 271 272 case M_IOCTL: 273 iocp = (struct iocblk *)mp->b_rptr; 274 switch (SAD_CMD(iocp->ioc_cmd)) { 275 case SAD_CMD(SAD_SAP): 276 case SAD_CMD(SAD_GAP): 277 apush_ioctl(qp, mp); 278 break; 279 280 case SAD_VML: 281 vml_ioctl(qp, mp); 282 break; 283 284 default: 285 miocnak(qp, mp, 0, EINVAL); 286 break; 287 } 288 break; 289 290 case M_IOCDATA: 291 iocp = (struct iocblk *)mp->b_rptr; 292 switch (SAD_CMD(iocp->ioc_cmd)) { 293 case SAD_CMD(SAD_SAP): 294 case SAD_CMD(SAD_GAP): 295 apush_iocdata(qp, mp); 296 break; 297 298 case SAD_VML: 299 vml_iocdata(qp, mp); 300 break; 301 302 default: 303 cmn_err(CE_WARN, 304 "sadwput: invalid ioc_cmd in case M_IOCDATA: %d", 305 iocp->ioc_cmd); 306 freemsg(mp); 307 break; 308 } 309 break; 310 311 default: 312 freemsg(mp); 313 break; 314 } /* switch (db_type) */ 315 return (0); 316 } 317 318 /* 319 * apush_ioctl() - 320 * Handle the M_IOCTL messages associated with 321 * the autopush feature. 322 */ 323 static void 324 apush_ioctl( 325 queue_t *qp, /* pointer to write queue */ 326 mblk_t *mp) /* message pointer */ 327 { 328 struct iocblk *iocp; 329 struct saddev *sadp; 330 uint_t size; 331 332 iocp = (struct iocblk *)mp->b_rptr; 333 if (iocp->ioc_count != TRANSPARENT) { 334 miocnak(qp, mp, 0, EINVAL); 335 return; 336 } 337 if (SAD_VER(iocp->ioc_cmd) > AP_VERSION) { 338 miocnak(qp, mp, 0, EINVAL); 339 return; 340 } 341 342 sadp = (struct saddev *)qp->q_ptr; 343 switch (SAD_CMD(iocp->ioc_cmd)) { 344 case SAD_CMD(SAD_SAP): 345 if (!(sadp->sa_flags & SADPRIV)) { 346 miocnak(qp, mp, 0, EPERM); 347 break; 348 } 349 /* FALLTHRU */ 350 351 case SAD_CMD(SAD_GAP): 352 sadp->sa_addr = (caddr_t)*(uintptr_t *)mp->b_cont->b_rptr; 353 if (SAD_VER(iocp->ioc_cmd) == 1) 354 size = STRAPUSH_V1_LEN; 355 else 356 size = STRAPUSH_V0_LEN; 357 mcopyin(mp, (void *)GETSTRUCT, size, NULL); 358 qreply(qp, mp); 359 break; 360 361 default: 362 ASSERT(0); 363 miocnak(qp, mp, 0, EINVAL); 364 break; 365 } /* switch (ioc_cmd) */ 366 } 367 368 /* 369 * apush_iocdata() - 370 * Handle the M_IOCDATA messages associated with 371 * the autopush feature. 372 */ 373 static void 374 apush_iocdata( 375 queue_t *qp, /* pointer to write queue */ 376 mblk_t *mp) /* message pointer */ 377 { 378 int i, ret; 379 struct copyresp *csp; 380 struct strapush *sap = NULL; 381 struct autopush *ap, *ap_tmp; 382 struct saddev *sadp; 383 uint_t size; 384 dev_t dev; 385 386 csp = (struct copyresp *)mp->b_rptr; 387 if (csp->cp_rval) { /* if there was an error */ 388 freemsg(mp); 389 return; 390 } 391 if (mp->b_cont) { 392 /* 393 * sap needed only if mp->b_cont is set. figure out the 394 * size of the expected sap structure and make sure 395 * enough data was supplied. 396 */ 397 if (SAD_VER(csp->cp_cmd) == 1) 398 size = STRAPUSH_V1_LEN; 399 else 400 size = STRAPUSH_V0_LEN; 401 if (MBLKL(mp->b_cont) < size) { 402 miocnak(qp, mp, 0, EINVAL); 403 return; 404 } 405 sap = (struct strapush *)mp->b_cont->b_rptr; 406 dev = makedevice(sap->sap_major, sap->sap_minor); 407 } 408 switch (SAD_CMD(csp->cp_cmd)) { 409 case SAD_CMD(SAD_SAP): 410 411 /* currently we only support one SAD_SAP command */ 412 if (((long)csp->cp_private) != GETSTRUCT) { 413 cmn_err(CE_WARN, 414 "apush_iocdata: cp_private bad in SAD_SAP: %p", 415 (void *)csp->cp_private); 416 miocnak(qp, mp, 0, EINVAL); 417 return; 418 } 419 420 switch (sap->sap_cmd) { 421 default: 422 miocnak(qp, mp, 0, EINVAL); 423 return; 424 case SAP_ONE: 425 case SAP_RANGE: 426 case SAP_ALL: 427 /* allocate and initialize a new config */ 428 ap = sad_ap_alloc(); 429 ap->ap_common = sap->sap_common; 430 if (SAD_VER(csp->cp_cmd) > 0) 431 ap->ap_anchor = sap->sap_anchor; 432 for (i = 0; i < MIN(sap->sap_npush, MAXAPUSH); i++) 433 (void) strncpy(ap->ap_list[i], 434 sap->sap_list[i], FMNAMESZ); 435 436 /* sanity check the request */ 437 if (((ret = sad_ap_verify(ap)) != 0) || 438 ((ret = valid_major(ap->ap_major)) != 0)) { 439 sad_ap_rele(ap); 440 miocnak(qp, mp, 0, ret); 441 return; 442 } 443 444 /* check for overlapping configs */ 445 mutex_enter(&sad_lock); 446 if ((ap_tmp = sad_ap_find(&ap->ap_common)) != NULL) { 447 /* already configured */ 448 mutex_exit(&sad_lock); 449 sad_ap_rele(ap_tmp); 450 sad_ap_rele(ap); 451 miocnak(qp, mp, 0, EEXIST); 452 return; 453 } 454 455 /* add the new config to our hash */ 456 sad_ap_insert(ap); 457 mutex_exit(&sad_lock); 458 miocack(qp, mp, 0, 0); 459 return; 460 461 case SAP_CLEAR: 462 /* sanity check the request */ 463 if (ret = valid_major(sap->sap_major)) { 464 miocnak(qp, mp, 0, ret); 465 return; 466 } 467 468 /* search for a matching config */ 469 if ((ap = sad_ap_find_by_dev(dev)) == NULL) { 470 /* no config found */ 471 miocnak(qp, mp, 0, ENODEV); 472 return; 473 } 474 475 /* 476 * If we matched a SAP_RANGE config 477 * the minor passed in must match the 478 * beginning of the range exactly. 479 */ 480 if ((ap->ap_type == SAP_RANGE) && 481 (ap->ap_minor != sap->sap_minor)) { 482 sad_ap_rele(ap); 483 miocnak(qp, mp, 0, ERANGE); 484 return; 485 } 486 487 /* 488 * If we matched a SAP_ALL config 489 * the minor passed in must be 0. 490 */ 491 if ((ap->ap_type == SAP_ALL) && 492 (sap->sap_minor != 0)) { 493 sad_ap_rele(ap); 494 miocnak(qp, mp, 0, EINVAL); 495 return; 496 } 497 498 /* 499 * make sure someone else hasn't already 500 * removed this config from the hash. 501 */ 502 mutex_enter(&sad_lock); 503 ap_tmp = sad_ap_find(&ap->ap_common); 504 if (ap_tmp != ap) { 505 mutex_exit(&sad_lock); 506 sad_ap_rele(ap_tmp); 507 sad_ap_rele(ap); 508 miocnak(qp, mp, 0, ENODEV); 509 return; 510 } else 511 512 /* remove the config from the hash and return */ 513 sad_ap_remove(ap); 514 mutex_exit(&sad_lock); 515 516 /* 517 * Release thrice, once for sad_ap_find_by_dev(), 518 * once for sad_ap_find(), and once to free. 519 */ 520 sad_ap_rele(ap); 521 sad_ap_rele(ap); 522 sad_ap_rele(ap); 523 miocack(qp, mp, 0, 0); 524 return; 525 } /* switch (sap_cmd) */ 526 /*NOTREACHED*/ 527 528 case SAD_CMD(SAD_GAP): 529 switch ((long)csp->cp_private) { 530 531 case GETSTRUCT: 532 /* sanity check the request */ 533 if (ret = valid_major(sap->sap_major)) { 534 miocnak(qp, mp, 0, ret); 535 return; 536 } 537 538 /* search for a matching config */ 539 if ((ap = sad_ap_find_by_dev(dev)) == NULL) { 540 /* no config found */ 541 miocnak(qp, mp, 0, ENODEV); 542 return; 543 } 544 545 /* copy out the contents of the config */ 546 sap->sap_common = ap->ap_common; 547 if (SAD_VER(csp->cp_cmd) > 0) 548 sap->sap_anchor = ap->ap_anchor; 549 for (i = 0; i < ap->ap_npush; i++) 550 (void) strcpy(sap->sap_list[i], ap->ap_list[i]); 551 for (; i < MAXAPUSH; i++) 552 bzero(sap->sap_list[i], FMNAMESZ + 1); 553 554 /* release our hold on the config */ 555 sad_ap_rele(ap); 556 557 /* copyout the results */ 558 if (SAD_VER(csp->cp_cmd) == 1) 559 size = STRAPUSH_V1_LEN; 560 else 561 size = STRAPUSH_V0_LEN; 562 563 sadp = (struct saddev *)qp->q_ptr; 564 mcopyout(mp, (void *)GETRESULT, size, sadp->sa_addr, 565 NULL); 566 qreply(qp, mp); 567 return; 568 case GETRESULT: 569 miocack(qp, mp, 0, 0); 570 return; 571 572 default: 573 cmn_err(CE_WARN, 574 "apush_iocdata: cp_private bad case SAD_GAP: %p", 575 (void *)csp->cp_private); 576 freemsg(mp); 577 return; 578 } /* switch (cp_private) */ 579 /*NOTREACHED*/ 580 default: /* can't happen */ 581 ASSERT(0); 582 freemsg(mp); 583 return; 584 } /* switch (cp_cmd) */ 585 } 586 587 /* 588 * vml_ioctl() - 589 * Handle the M_IOCTL message associated with a request 590 * to validate a module list. 591 */ 592 static void 593 vml_ioctl( 594 queue_t *qp, /* pointer to write queue */ 595 mblk_t *mp) /* message pointer */ 596 { 597 struct iocblk *iocp; 598 599 iocp = (struct iocblk *)mp->b_rptr; 600 if (iocp->ioc_count != TRANSPARENT) { 601 miocnak(qp, mp, 0, EINVAL); 602 return; 603 } 604 ASSERT(iocp->ioc_cmd == SAD_VML); 605 mcopyin(mp, (void *)GETSTRUCT, 606 SIZEOF_STRUCT(str_list, iocp->ioc_flag), NULL); 607 qreply(qp, mp); 608 } 609 610 /* 611 * vml_iocdata() - 612 * Handle the M_IOCDATA messages associated with 613 * a request to validate a module list. 614 */ 615 static void 616 vml_iocdata( 617 queue_t *qp, /* pointer to write queue */ 618 mblk_t *mp) /* message pointer */ 619 { 620 long i; 621 int nmods; 622 struct copyresp *csp; 623 struct str_mlist *lp; 624 STRUCT_HANDLE(str_list, slp); 625 struct saddev *sadp; 626 627 csp = (struct copyresp *)mp->b_rptr; 628 if (csp->cp_rval) { /* if there was an error */ 629 freemsg(mp); 630 return; 631 } 632 633 ASSERT(csp->cp_cmd == SAD_VML); 634 sadp = (struct saddev *)qp->q_ptr; 635 switch ((long)csp->cp_private) { 636 case GETSTRUCT: 637 STRUCT_SET_HANDLE(slp, csp->cp_flag, 638 (struct str_list *)mp->b_cont->b_rptr); 639 nmods = STRUCT_FGET(slp, sl_nmods); 640 if (nmods <= 0) { 641 miocnak(qp, mp, 0, EINVAL); 642 break; 643 } 644 sadp->sa_addr = (caddr_t)(uintptr_t)nmods; 645 646 mcopyin(mp, (void *)GETLIST, nmods * sizeof (struct str_mlist), 647 STRUCT_FGETP(slp, sl_modlist)); 648 qreply(qp, mp); 649 break; 650 651 case GETLIST: 652 lp = (struct str_mlist *)mp->b_cont->b_rptr; 653 for (i = 0; i < (long)sadp->sa_addr; i++, lp++) { 654 lp->l_name[FMNAMESZ] = '\0'; 655 if (fmodsw_find(lp->l_name, FMODSW_LOAD) == NULL) { 656 miocack(qp, mp, 0, 1); 657 return; 658 } 659 } 660 miocack(qp, mp, 0, 0); 661 break; 662 663 default: 664 cmn_err(CE_WARN, "vml_iocdata: invalid cp_private value: %p", 665 (void *)csp->cp_private); 666 freemsg(mp); 667 break; 668 } /* switch (cp_private) */ 669 } 670 671 /* 672 * Validate a major number and also verify if 673 * it is a STREAMS device. 674 * Return values: 0 if a valid STREAMS dev 675 * error code otherwise 676 */ 677 static int 678 valid_major(major_t major) 679 { 680 int ret = 0; 681 682 if (etoimajor(major) == -1) 683 return (EINVAL); 684 685 /* 686 * attempt to load the driver 'major' and verify that 687 * it is a STREAMS driver. 688 */ 689 if (ddi_hold_driver(major) == NULL) 690 return (EINVAL); 691 692 if (!STREAMSTAB(major)) 693 ret = ENOSTR; 694 695 ddi_rele_driver(major); 696 697 return (ret); 698 } 699