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