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, D_MTPERQ | D_MP, &sadinfo); 90 91 /* 92 * Module linkage information for the kernel. 93 */ 94 95 static struct modldrv modldrv = { 96 &mod_driverops, /* Type of module. This one is a pseudo driver */ 97 "STREAMS Administrative Driver 'sad' %I%", 98 &sad_ops, /* driver ops */ 99 }; 100 101 static struct modlinkage modlinkage = { 102 MODREV_1, &modldrv, NULL 103 }; 104 105 int 106 _init(void) 107 { 108 return (mod_install(&modlinkage)); 109 } 110 111 int 112 _fini(void) 113 { 114 return (mod_remove(&modlinkage)); 115 } 116 117 int 118 _info(struct modinfo *modinfop) 119 { 120 return (mod_info(&modlinkage, modinfop)); 121 } 122 123 static int 124 sad_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 125 { 126 int instance = ddi_get_instance(devi); 127 128 if (cmd != DDI_ATTACH) 129 return (DDI_FAILURE); 130 131 ASSERT(instance == 0); 132 if (instance != 0) 133 return (DDI_FAILURE); 134 135 if (ddi_create_minor_node(devi, "user", S_IFCHR, 136 0, DDI_PSEUDO, NULL) == DDI_FAILURE) { 137 return (DDI_FAILURE); 138 } 139 if (ddi_create_priv_minor_node(devi, "admin", S_IFCHR, 140 1, DDI_PSEUDO, PRIVONLY_DEV, PRIV_SYS_CONFIG, 141 PRIV_SYS_CONFIG, 0666) == DDI_FAILURE) { 142 ddi_remove_minor_node(devi, NULL); 143 return (DDI_FAILURE); 144 } 145 sad_dip = devi; 146 return (DDI_SUCCESS); 147 } 148 149 /* ARGSUSED */ 150 static int 151 sad_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 152 { 153 int error; 154 155 switch (infocmd) { 156 case DDI_INFO_DEVT2DEVINFO: 157 if (sad_dip == NULL) { 158 error = DDI_FAILURE; 159 } else { 160 *result = sad_dip; 161 error = DDI_SUCCESS; 162 } 163 break; 164 case DDI_INFO_DEVT2INSTANCE: 165 *result = (void *)0; 166 error = DDI_SUCCESS; 167 break; 168 default: 169 error = DDI_FAILURE; 170 } 171 return (error); 172 } 173 174 175 /* 176 * sadopen() - 177 * Allocate a sad device. Only one 178 * open at a time allowed per device. 179 */ 180 /* ARGSUSED */ 181 static int 182 sadopen( 183 queue_t *qp, /* pointer to read queue */ 184 dev_t *devp, /* major/minor device of stream */ 185 int flag, /* file open flags */ 186 int sflag, /* stream open flags */ 187 cred_t *credp) /* user credentials */ 188 { 189 int i; 190 netstack_t *ns; 191 str_stack_t *ss; 192 193 if (sflag) /* no longer called from clone driver */ 194 return (EINVAL); 195 196 ns = netstack_find_by_cred(credp); 197 ASSERT(ns != NULL); 198 ss = ns->netstack_str; 199 ASSERT(ss != NULL); 200 201 /* 202 * Both USRMIN and ADMMIN are clone interfaces. 203 */ 204 for (i = 0; i < ss->ss_sadcnt; i++) 205 if (ss->ss_saddev[i].sa_qp == NULL) 206 break; 207 if (i >= ss->ss_sadcnt) { /* no such device */ 208 netstack_rele(ss->ss_netstack); 209 return (ENXIO); 210 } 211 switch (getminor(*devp)) { 212 case USRMIN: /* mere mortal */ 213 ss->ss_saddev[i].sa_flags = 0; 214 break; 215 216 case ADMMIN: /* privileged user */ 217 ss->ss_saddev[i].sa_flags = SADPRIV; 218 break; 219 220 default: 221 netstack_rele(ss->ss_netstack); 222 return (EINVAL); 223 } 224 225 ss->ss_saddev[i].sa_qp = qp; 226 ss->ss_saddev[i].sa_ss = ss; 227 qp->q_ptr = (caddr_t)&ss->ss_saddev[i]; 228 WR(qp)->q_ptr = (caddr_t)&ss->ss_saddev[i]; 229 230 /* 231 * NOTE: should the ADMMIN or USRMIN minors change 232 * then so should the offset of 2 below 233 * Both USRMIN and ADMMIN are clone interfaces and 234 * therefore their minor numbers (0 and 1) are reserved. 235 */ 236 *devp = makedevice(getemajor(*devp), i + 2); 237 qprocson(qp); 238 return (0); 239 } 240 241 /* 242 * sadclose() - 243 * Clean up the data structures. 244 */ 245 /* ARGSUSED */ 246 static int 247 sadclose( 248 queue_t *qp, /* pointer to read queue */ 249 int flag, /* file open flags */ 250 cred_t *credp) /* user credentials */ 251 { 252 struct saddev *sadp; 253 254 qprocsoff(qp); 255 sadp = (struct saddev *)qp->q_ptr; 256 sadp->sa_qp = NULL; 257 sadp->sa_addr = NULL; 258 netstack_rele(sadp->sa_ss->ss_netstack); 259 sadp->sa_ss = NULL; 260 qp->q_ptr = NULL; 261 WR(qp)->q_ptr = NULL; 262 return (0); 263 } 264 265 /* 266 * sadwput() - 267 * Write side put procedure. 268 */ 269 static int 270 sadwput( 271 queue_t *qp, /* pointer to write queue */ 272 mblk_t *mp) /* message pointer */ 273 { 274 struct iocblk *iocp; 275 276 switch (mp->b_datap->db_type) { 277 case M_FLUSH: 278 if (*mp->b_rptr & FLUSHR) { 279 *mp->b_rptr &= ~FLUSHW; 280 qreply(qp, mp); 281 } else 282 freemsg(mp); 283 break; 284 285 case M_IOCTL: 286 iocp = (struct iocblk *)mp->b_rptr; 287 switch (SAD_CMD(iocp->ioc_cmd)) { 288 case SAD_CMD(SAD_SAP): 289 case SAD_CMD(SAD_GAP): 290 apush_ioctl(qp, mp); 291 break; 292 293 case SAD_VML: 294 vml_ioctl(qp, mp); 295 break; 296 297 default: 298 miocnak(qp, mp, 0, EINVAL); 299 break; 300 } 301 break; 302 303 case M_IOCDATA: 304 iocp = (struct iocblk *)mp->b_rptr; 305 switch (SAD_CMD(iocp->ioc_cmd)) { 306 case SAD_CMD(SAD_SAP): 307 case SAD_CMD(SAD_GAP): 308 apush_iocdata(qp, mp); 309 break; 310 311 case SAD_VML: 312 vml_iocdata(qp, mp); 313 break; 314 315 default: 316 cmn_err(CE_WARN, 317 "sadwput: invalid ioc_cmd in case M_IOCDATA: %d", 318 iocp->ioc_cmd); 319 freemsg(mp); 320 break; 321 } 322 break; 323 324 default: 325 freemsg(mp); 326 break; 327 } /* switch (db_type) */ 328 return (0); 329 } 330 331 /* 332 * apush_ioctl() - 333 * Handle the M_IOCTL messages associated with 334 * the autopush feature. 335 */ 336 static void 337 apush_ioctl( 338 queue_t *qp, /* pointer to write queue */ 339 mblk_t *mp) /* message pointer */ 340 { 341 struct iocblk *iocp; 342 struct saddev *sadp; 343 uint_t size; 344 345 iocp = (struct iocblk *)mp->b_rptr; 346 if (iocp->ioc_count != TRANSPARENT) { 347 miocnak(qp, mp, 0, EINVAL); 348 return; 349 } 350 if (SAD_VER(iocp->ioc_cmd) > AP_VERSION) { 351 miocnak(qp, mp, 0, EINVAL); 352 return; 353 } 354 355 sadp = (struct saddev *)qp->q_ptr; 356 switch (SAD_CMD(iocp->ioc_cmd)) { 357 case SAD_CMD(SAD_SAP): 358 if (!(sadp->sa_flags & SADPRIV)) { 359 miocnak(qp, mp, 0, EPERM); 360 break; 361 } 362 /* FALLTHRU */ 363 364 case SAD_CMD(SAD_GAP): 365 sadp->sa_addr = (caddr_t)*(uintptr_t *)mp->b_cont->b_rptr; 366 if (SAD_VER(iocp->ioc_cmd) == 1) 367 size = STRAPUSH_V1_LEN; 368 else 369 size = STRAPUSH_V0_LEN; 370 mcopyin(mp, (void *)GETSTRUCT, size, NULL); 371 qreply(qp, mp); 372 break; 373 374 default: 375 ASSERT(0); 376 miocnak(qp, mp, 0, EINVAL); 377 break; 378 } /* switch (ioc_cmd) */ 379 } 380 381 /* 382 * apush_iocdata() - 383 * Handle the M_IOCDATA messages associated with 384 * the autopush feature. 385 */ 386 static void 387 apush_iocdata( 388 queue_t *qp, /* pointer to write queue */ 389 mblk_t *mp) /* message pointer */ 390 { 391 int i, ret; 392 struct copyresp *csp; 393 struct strapush *sap = NULL; 394 struct autopush *ap, *ap_tmp; 395 struct saddev *sadp; 396 uint_t size; 397 dev_t dev; 398 str_stack_t *ss; 399 400 sadp = (struct saddev *)qp->q_ptr; 401 ss = sadp->sa_ss; 402 403 csp = (struct copyresp *)mp->b_rptr; 404 if (csp->cp_rval) { /* if there was an error */ 405 freemsg(mp); 406 return; 407 } 408 if (mp->b_cont) { 409 /* 410 * sap needed only if mp->b_cont is set. figure out the 411 * size of the expected sap structure and make sure 412 * enough data was supplied. 413 */ 414 if (SAD_VER(csp->cp_cmd) == 1) 415 size = STRAPUSH_V1_LEN; 416 else 417 size = STRAPUSH_V0_LEN; 418 if (MBLKL(mp->b_cont) < size) { 419 miocnak(qp, mp, 0, EINVAL); 420 return; 421 } 422 sap = (struct strapush *)mp->b_cont->b_rptr; 423 dev = makedevice(sap->sap_major, sap->sap_minor); 424 } 425 switch (SAD_CMD(csp->cp_cmd)) { 426 case SAD_CMD(SAD_SAP): 427 428 /* currently we only support one SAD_SAP command */ 429 if (((long)csp->cp_private) != GETSTRUCT) { 430 cmn_err(CE_WARN, 431 "apush_iocdata: cp_private bad in SAD_SAP: %p", 432 (void *)csp->cp_private); 433 miocnak(qp, mp, 0, EINVAL); 434 return; 435 } 436 437 switch (sap->sap_cmd) { 438 default: 439 miocnak(qp, mp, 0, EINVAL); 440 return; 441 case SAP_ONE: 442 case SAP_RANGE: 443 case SAP_ALL: 444 /* allocate and initialize a new config */ 445 ap = sad_ap_alloc(); 446 ap->ap_common = sap->sap_common; 447 if (SAD_VER(csp->cp_cmd) > 0) 448 ap->ap_anchor = sap->sap_anchor; 449 for (i = 0; i < MIN(sap->sap_npush, MAXAPUSH); i++) 450 (void) strncpy(ap->ap_list[i], 451 sap->sap_list[i], FMNAMESZ); 452 453 /* sanity check the request */ 454 if (((ret = sad_ap_verify(ap)) != 0) || 455 ((ret = valid_major(ap->ap_major)) != 0)) { 456 sad_ap_rele(ap, ss); 457 miocnak(qp, mp, 0, ret); 458 return; 459 } 460 461 /* check for overlapping configs */ 462 mutex_enter(&ss->ss_sad_lock); 463 ap_tmp = sad_ap_find(&ap->ap_common, ss); 464 if (ap_tmp != NULL) { 465 /* already configured */ 466 mutex_exit(&ss->ss_sad_lock); 467 sad_ap_rele(ap_tmp, ss); 468 sad_ap_rele(ap, ss); 469 miocnak(qp, mp, 0, EEXIST); 470 return; 471 } 472 473 /* add the new config to our hash */ 474 sad_ap_insert(ap, ss); 475 mutex_exit(&ss->ss_sad_lock); 476 miocack(qp, mp, 0, 0); 477 return; 478 479 case SAP_CLEAR: 480 /* sanity check the request */ 481 if (ret = valid_major(sap->sap_major)) { 482 miocnak(qp, mp, 0, ret); 483 return; 484 } 485 486 /* search for a matching config */ 487 if ((ap = sad_ap_find_by_dev(dev, ss)) == NULL) { 488 /* no config found */ 489 miocnak(qp, mp, 0, ENODEV); 490 return; 491 } 492 493 /* 494 * If we matched a SAP_RANGE config 495 * the minor passed in must match the 496 * beginning of the range exactly. 497 */ 498 if ((ap->ap_type == SAP_RANGE) && 499 (ap->ap_minor != sap->sap_minor)) { 500 sad_ap_rele(ap, ss); 501 miocnak(qp, mp, 0, ERANGE); 502 return; 503 } 504 505 /* 506 * If we matched a SAP_ALL config 507 * the minor passed in must be 0. 508 */ 509 if ((ap->ap_type == SAP_ALL) && 510 (sap->sap_minor != 0)) { 511 sad_ap_rele(ap, ss); 512 miocnak(qp, mp, 0, EINVAL); 513 return; 514 } 515 516 /* 517 * make sure someone else hasn't already 518 * removed this config from the hash. 519 */ 520 mutex_enter(&ss->ss_sad_lock); 521 ap_tmp = sad_ap_find(&ap->ap_common, ss); 522 if (ap_tmp != ap) { 523 mutex_exit(&ss->ss_sad_lock); 524 sad_ap_rele(ap_tmp, ss); 525 sad_ap_rele(ap, ss); 526 miocnak(qp, mp, 0, ENODEV); 527 return; 528 } 529 530 /* remove the config from the hash and return */ 531 sad_ap_remove(ap, ss); 532 mutex_exit(&ss->ss_sad_lock); 533 534 /* 535 * Release thrice, once for sad_ap_find_by_dev(), 536 * once for sad_ap_find(), and once to free. 537 */ 538 sad_ap_rele(ap, ss); 539 sad_ap_rele(ap, ss); 540 sad_ap_rele(ap, ss); 541 miocack(qp, mp, 0, 0); 542 return; 543 } /* switch (sap_cmd) */ 544 /*NOTREACHED*/ 545 546 case SAD_CMD(SAD_GAP): 547 switch ((long)csp->cp_private) { 548 549 case GETSTRUCT: 550 /* sanity check the request */ 551 if (ret = valid_major(sap->sap_major)) { 552 miocnak(qp, mp, 0, ret); 553 return; 554 } 555 556 /* search for a matching config */ 557 if ((ap = sad_ap_find_by_dev(dev, ss)) == NULL) { 558 /* no config found */ 559 miocnak(qp, mp, 0, ENODEV); 560 return; 561 } 562 563 /* copy out the contents of the config */ 564 sap->sap_common = ap->ap_common; 565 if (SAD_VER(csp->cp_cmd) > 0) 566 sap->sap_anchor = ap->ap_anchor; 567 for (i = 0; i < ap->ap_npush; i++) 568 (void) strcpy(sap->sap_list[i], ap->ap_list[i]); 569 for (; i < MAXAPUSH; i++) 570 bzero(sap->sap_list[i], FMNAMESZ + 1); 571 mutex_exit(&ss->ss_sad_lock); 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