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