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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 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 /* 30 * Device policy implementation. 31 * 32 * Maintains the device policy table and defines the lookup functions. 33 * 34 * The table contains one entry for each major device number; each 35 * major bucket has a list of minor number specific entries. First 36 * match gets it. Not even simple minor names are expanded as that 37 * would cause the device to be loaded. Non-wildcard entries are expanded 38 * on first match. Wildcard entries are matched each open but the actual 39 * policy is cached with the common snode, so the matching code will 40 * probably be called infrequently. The trivial wildcard ``*'' does 41 * not cause expensive string expansions and matches. 42 * 43 * When the policy is updated, the the generation count is increased; 44 * whenever a cached policy is used, the generation count is compared; 45 * if there's no match, the device policy is refreshed. 46 * 47 * The special policy "nullpolicy" is used to mean "no checking beyond DAC 48 * needed". It too will change when the policy is rev'ed to make sure 49 * that devices with nullpolicy are also refreshed. 50 * 51 * The special policy "dfltpolicy" is used for those devices with no 52 * matching policy. On boot, it is "all privileges required". 53 * This restriction on boot functions as a fail-safe; if no device policy 54 * is loaded a "no restriction policy" would lead to security problems that 55 * are not immediately noticable. 56 */ 57 58 #include <sys/priv_impl.h> 59 #include <sys/policy.h> 60 #include <sys/atomic.h> 61 #include <sys/autoconf.h> 62 #include <sys/sysmacros.h> 63 #include <sys/systm.h> 64 #include <sys/vnode.h> 65 #include <sys/devpolicy.h> 66 #include <sys/priv.h> 67 #include <sys/kmem.h> 68 #include <sys/ksynch.h> 69 #include <sys/errno.h> 70 #include <sys/sunddi.h> 71 #include <c2/audit.h> 72 #include <sys/fs/dv_node.h> 73 74 /* 75 * Internal data structures definitions. 76 */ 77 78 typedef struct devplcyent devplcyent_t; 79 80 /* 81 * The device policy entry; if there is an expression string, the 82 * minor numbers are not relevant. This is indicated by dpe_len > 0. 83 */ 84 struct devplcyent { 85 devplcyent_t *dpe_next; /* next entry in this list */ 86 devplcy_t *dpe_plcy; /* policy for this entry */ 87 char *dpe_expr; /* expression matching minor mode */ 88 int dpe_len; /* size of allocated mem for expr */ 89 uint32_t dpe_flags; /* flags */ 90 minor_t dpe_lomin; /* expanded: low minor number */ 91 minor_t dpe_himin; /* expanded: high minor number */ 92 vtype_t dpe_spec; /* expanded: VBLK or VCHR */ 93 }; 94 95 #define DPE_WILDC 0x01 /* Expression has wildcard */ 96 #define DPE_ALLMINOR 0x02 /* Matches all minor numbers */ 97 #define DPE_EXPANDED 0x04 /* Minor numbers expanded */ 98 99 typedef struct tableent { 100 devplcyent_t *t_ent; /* list of policies by minor */ 101 major_t t_major; /* device major number */ 102 } tableent_t; 103 104 /* 105 * The data store. 106 */ 107 108 static int ntabent; /* # of major numbers */ 109 static int totitems; /* Number of entries in all buckets + dflt */ 110 static tableent_t *devpolicy; /* The device policy itself */ 111 112 static krwlock_t policyrw; /* protects the table */ 113 static kmutex_t policymutex; /* allows only one concurrent devpolicy_load */ 114 115 devplcy_t *nullpolicy; /* public because it's used for shortcuts */ 116 static devplcy_t *dfltpolicy; 117 static devplcy_t *netpolicy; 118 119 /* 120 * Device policy generation count; only device policies matching the 121 * generation count are still valid. 122 */ 123 volatile uint32_t devplcy_gen; 124 125 /* 126 * Tunable: maximum number of device policy entries to load in 127 * a system call. (Protects KM_SLEEP call) 128 */ 129 int maxdevpolicy = MAXDEVPOLICY; 130 131 /* 132 * Initialize the device policy code 133 */ 134 void 135 devpolicy_init(void) 136 { 137 rw_init(&policyrw, NULL, RW_DRIVER, NULL); 138 mutex_init(&policymutex, NULL, MUTEX_DRIVER, NULL); 139 140 /* The mutex is held here in order to satisfy the ASSERT in dpget() */ 141 mutex_enter(&policymutex); 142 143 nullpolicy = dpget(); 144 dfltpolicy = dpget(); 145 netpolicy = dpget(); 146 147 /* 148 * Initially, we refuse access to all devices except 149 * to processes with all privileges. 150 */ 151 priv_fillset(&dfltpolicy->dp_rdp); 152 priv_fillset(&dfltpolicy->dp_wrp); 153 154 totitems = 1; 155 156 devplcy_gen++; 157 mutex_exit(&policymutex); 158 159 /* initialize default network privilege */ 160 priv_emptyset(&netpolicy->dp_rdp); 161 priv_emptyset(&netpolicy->dp_wrp); 162 priv_addset(&netpolicy->dp_rdp, PRIV_NET_RAWACCESS); 163 priv_addset(&netpolicy->dp_wrp, PRIV_NET_RAWACCESS); 164 } 165 166 /* 167 * Devpolicy reference counting/allocation routines. 168 * cf. crget()/crhold()/crfree(). 169 */ 170 devplcy_t * 171 dpget(void) 172 { 173 devplcy_t *dp = kmem_zalloc(sizeof (*dp), KM_SLEEP); 174 175 ASSERT(MUTEX_HELD(&policymutex)); 176 177 dp->dp_ref = 1; 178 /* New ones belong to the next generation */ 179 dp->dp_gen = devplcy_gen + 1; 180 return (dp); 181 } 182 183 void 184 dphold(devplcy_t *dp) 185 { 186 ASSERT(dp->dp_ref != 0xdeadbeef && dp->dp_ref != 0); 187 atomic_add_32(&dp->dp_ref, 1); 188 } 189 190 void 191 dpfree(devplcy_t *dp) 192 { 193 ASSERT(dp->dp_ref != 0xdeadbeef && dp->dp_ref != 0); 194 if (atomic_add_32_nv(&dp->dp_ref, -1) == 0) 195 kmem_free(dp, sizeof (*dp)); 196 } 197 198 /* 199 * Find the policy that matches this device. 200 */ 201 static devplcy_t * 202 match_policy(devplcyent_t *de, dev_t dev, vtype_t spec) 203 { 204 char *mname = NULL; 205 minor_t min = getminor(dev); 206 207 for (; de != NULL; de = de->dpe_next) { 208 if (de->dpe_flags & DPE_ALLMINOR) 209 break; 210 211 if (de->dpe_flags & DPE_EXPANDED) { 212 if (min >= de->dpe_lomin && min <= de->dpe_himin && 213 spec == de->dpe_spec) { 214 break; 215 } else { 216 continue; 217 } 218 } 219 220 /* 221 * We now need the minor name to match string or 222 * simle regexp. Could we use csp->s_dip and not 223 * allocate a string here? 224 */ 225 if (mname == NULL && 226 ddi_lyr_get_minor_name(dev, spec, &mname) != DDI_SUCCESS) 227 /* mname can be set after the function fails */ 228 return (dfltpolicy); 229 230 /* Simple wildcard, with only one ``*'' */ 231 if (de->dpe_flags & DPE_WILDC) { 232 int plen = de->dpe_len - 1; 233 int slen = strlen(mname); 234 char *pp = de->dpe_expr; 235 char *sp = mname; 236 237 /* string must be at least as long as pattern w/o '*' */ 238 if (slen < plen - 1) 239 continue; 240 241 /* skip prefix */ 242 while (*pp == *sp && *pp != '\0') { 243 pp++; 244 sp++; 245 } 246 /* matched single '*' */ 247 if (*pp == '\0') 248 if (*sp == '\0') 249 break; 250 else 251 continue; 252 if (*pp != '*') 253 continue; 254 255 pp++; 256 /* 257 * skip characters matched by '*': difference of 258 * length of s and length of pattern sans '*' 259 */ 260 sp += slen - (plen - 1); 261 if (strcmp(pp, sp) == 0) /* match! */ 262 break; 263 264 } else if (strcmp(de->dpe_expr, mname) == 0) { 265 /* Store minor number, if no contention */ 266 if (rw_tryupgrade(&policyrw)) { 267 de->dpe_lomin = de->dpe_himin = min; 268 de->dpe_spec = spec; 269 de->dpe_flags |= DPE_EXPANDED; 270 } 271 break; 272 } 273 274 } 275 276 if (mname != NULL) 277 kmem_free(mname, strlen(mname) + 1); 278 279 return (de != NULL ? de->dpe_plcy : dfltpolicy); 280 } 281 282 static int 283 devpolicyent_bymajor(major_t maj) 284 { 285 int lo, hi; 286 287 ASSERT(RW_LOCK_HELD(&policyrw)); 288 289 lo = 0; 290 hi = ntabent - 1; 291 292 /* Binary search for major number */ 293 while (lo <= hi) { 294 int mid = (lo + hi) / 2; 295 296 if (devpolicy[mid].t_major == maj) 297 return (mid); 298 else if (maj < devpolicy[mid].t_major) 299 hi = mid - 1; 300 else 301 lo = mid + 1; 302 } 303 return (-1); 304 } 305 306 /* 307 * Returns held device policy for the specific device node. 308 * Note devfs_devpolicy returns with a hold on the policy. 309 */ 310 devplcy_t * 311 devpolicy_find(vnode_t *vp) 312 { 313 dev_t dev = vp->v_rdev; 314 vtype_t spec = vp->v_type; 315 major_t maj = getmajor(dev); 316 int i; 317 devplcy_t *res; 318 319 if (maj == clone_major) 320 maj = getminor(dev); 321 322 rw_enter(&policyrw, RW_READER); 323 324 i = devpolicyent_bymajor(maj); 325 326 if (i != -1) { 327 res = match_policy(devpolicy[i].t_ent, dev, spec); 328 dphold(res); 329 } else if (devfs_devpolicy(vp, &res) != 0) { 330 res = NETWORK_DRV(maj) ? netpolicy : dfltpolicy; 331 dphold(res); 332 } 333 334 rw_exit(&policyrw); 335 336 return (res); 337 } 338 339 static devplcyent_t * 340 parse_policy(devplcysys_t *ds, devplcy_t *nullp, devplcy_t *defp) 341 { 342 devplcyent_t *de = kmem_zalloc(sizeof (*de), KM_SLEEP); 343 devplcy_t *np; 344 345 if (priv_isemptyset(&ds->dps_rdp) && priv_isemptyset(&ds->dps_wrp)) 346 dphold(np = nullp); 347 else if (defp != nullp && 348 priv_isequalset(&ds->dps_rdp, &defp->dp_rdp) && 349 priv_isequalset(&ds->dps_wrp, &defp->dp_wrp)) 350 dphold(np = defp); 351 else { 352 np = dpget(); 353 np->dp_rdp = ds->dps_rdp; 354 np->dp_wrp = ds->dps_wrp; 355 } 356 357 if (ds->dps_minornm[0] != '\0') { 358 de->dpe_len = strlen(ds->dps_minornm) + 1; 359 360 if (strchr(ds->dps_minornm, '*') != NULL) { 361 if (de->dpe_len == 2) { /* "*\0" */ 362 de->dpe_flags = DPE_ALLMINOR; 363 de->dpe_len = 0; 364 } else 365 de->dpe_flags = DPE_WILDC; 366 } 367 if (de->dpe_len != 0) { 368 de->dpe_expr = kmem_alloc(de->dpe_len, KM_SLEEP); 369 (void) strcpy(de->dpe_expr, ds->dps_minornm); 370 } 371 } else { 372 de->dpe_lomin = ds->dps_lomin; 373 de->dpe_himin = ds->dps_himin; 374 de->dpe_flags = DPE_EXPANDED; 375 de->dpe_spec = ds->dps_isblock ? VBLK : VCHR; 376 } 377 de->dpe_plcy = np; 378 379 ASSERT((de->dpe_flags & (DPE_ALLMINOR|DPE_EXPANDED)) || 380 de->dpe_expr != NULL); 381 382 return (de); 383 } 384 385 static void 386 freechain(devplcyent_t *de) 387 { 388 devplcyent_t *dn; 389 390 do { 391 dn = de->dpe_next; 392 dpfree(de->dpe_plcy); 393 if (de->dpe_len != 0) 394 kmem_free(de->dpe_expr, de->dpe_len); 395 kmem_free(de, sizeof (*de)); 396 de = dn; 397 } while (de != NULL); 398 } 399 400 /* 401 * Load the device policy. 402 * The device policy currently makes nu distinction between the 403 * block and characters devices; that is generally not a problem 404 * as the names of those devices cannot clash. 405 */ 406 int 407 devpolicy_load(int nitems, size_t sz, devplcysys_t *uitmp) 408 { 409 int i, j; 410 int nmaj = 0; 411 major_t lastmajor; 412 devplcysys_t *items; 413 size_t mem; 414 major_t curmaj; 415 devplcyent_t **last, *de; 416 417 tableent_t *newpolicy, *oldpolicy; 418 devplcy_t *newnull, *newdflt, *oldnull, *olddflt; 419 int oldcnt; 420 int lastlen; 421 int lastwild; 422 423 #ifdef lint 424 /* Lint can't figure out that the "i == 1" test protects all */ 425 lastlen = 0; 426 lastwild = 0; 427 lastmajor = 0; 428 #endif 429 /* 430 * The application must agree with the kernel on the size of each 431 * item; it must not exceed the maximum number and must be 432 * at least 1 item in size. 433 */ 434 if (sz != sizeof (devplcysys_t) || nitems > maxdevpolicy || nitems < 1) 435 return (EINVAL); 436 437 mem = nitems * sz; 438 439 items = kmem_alloc(mem, KM_SLEEP); 440 441 if (copyin(uitmp, items, mem)) { 442 kmem_free(items, mem); 443 return (EFAULT); 444 } 445 446 /* Check for default policy, it must exist and be sorted first */ 447 if (items[0].dps_maj != DEVPOLICY_DFLT_MAJ) { 448 kmem_free(items, mem); 449 return (EINVAL); 450 } 451 452 /* 453 * Application must deliver entries sorted. 454 * Sorted meaning here: 455 * In major number order 456 * For each major number, we first need to have the explicit 457 * entries, then the wild card entries, longest first. 458 */ 459 for (i = 1; i < nitems; i++) { 460 int len, wild; 461 char *tmp; 462 463 curmaj = items[i].dps_maj; 464 len = strlen(items[i].dps_minornm); 465 wild = len > 0 && 466 (tmp = strchr(items[i].dps_minornm, '*')) != NULL; 467 468 /* Another default major, string too long or too many ``*'' */ 469 if (curmaj == DEVPOLICY_DFLT_MAJ || 470 len >= sizeof (items[i].dps_minornm) || 471 wild && strchr(tmp + 1, '*') != NULL) { 472 kmem_free(items, mem); 473 return (EINVAL); 474 } 475 if (i == 1 || lastmajor < curmaj) { 476 lastmajor = curmaj; 477 nmaj++; 478 } else if (lastmajor > curmaj || lastwild > wild || 479 lastwild && lastlen < len) { 480 kmem_free(items, mem); 481 return (EINVAL); 482 } 483 lastlen = len; 484 lastwild = wild; 485 } 486 487 #ifdef C2_AUDIT 488 if (audit_active) 489 audit_devpolicy(nitems, items); 490 #endif 491 492 /* 493 * Parse the policy. We create an array for all major numbers 494 * and in each major number bucket we'll have a linked list of 495 * entries. Each item may contain either a lo,hi minor pair 496 * or a string/wild card matching a minor node. 497 */ 498 if (nmaj > 0) 499 newpolicy = kmem_zalloc(nmaj * sizeof (tableent_t), KM_SLEEP); 500 501 /* 502 * We want to lock out concurrent updates but we don't want to 503 * lock out device opens while we still need to allocate memory. 504 * As soon as we allocate new devplcy_t's we commit to the next 505 * generation number, so we must lock out other updates from here. 506 */ 507 mutex_enter(&policymutex); 508 509 /* New default and NULL policy */ 510 newnull = dpget(); 511 512 if (priv_isemptyset(&items[0].dps_rdp) && 513 priv_isemptyset(&items[0].dps_wrp)) { 514 newdflt = newnull; 515 dphold(newdflt); 516 } else { 517 newdflt = dpget(); 518 newdflt->dp_rdp = items[0].dps_rdp; 519 newdflt->dp_wrp = items[0].dps_wrp; 520 } 521 522 j = -1; 523 524 /* Userland made sure sorting was ok */ 525 for (i = 1; i < nitems; i++) { 526 de = parse_policy(&items[i], newnull, newdflt); 527 528 if (j == -1 || curmaj != items[i].dps_maj) { 529 j++; 530 newpolicy[j].t_major = curmaj = items[i].dps_maj; 531 last = &newpolicy[j].t_ent; 532 } 533 *last = de; 534 last = &de->dpe_next; 535 } 536 537 /* Done parsing, throw away input */ 538 kmem_free(items, mem); 539 540 /* Lock out all devpolicy_find()s */ 541 rw_enter(&policyrw, RW_WRITER); 542 543 /* Install the new global data */ 544 oldnull = nullpolicy; 545 nullpolicy = newnull; 546 547 olddflt = dfltpolicy; 548 dfltpolicy = newdflt; 549 550 oldcnt = ntabent; 551 ntabent = nmaj; 552 553 totitems = nitems; 554 555 oldpolicy = devpolicy; 556 devpolicy = newpolicy; 557 558 /* Force all calls by devpolicy_find() */ 559 devplcy_gen++; 560 561 /* Reenable policy finds */ 562 rw_exit(&policyrw); 563 mutex_exit(&policymutex); 564 565 /* Free old stuff */ 566 if (oldcnt != 0) { 567 for (i = 0; i < oldcnt; i++) 568 freechain(oldpolicy[i].t_ent); 569 kmem_free(oldpolicy, oldcnt * sizeof (*oldpolicy)); 570 } 571 572 dpfree(oldnull); 573 dpfree(olddflt); 574 575 return (0); 576 } 577 578 /* 579 * Get device policy: argument one is a pointer to an integer holding 580 * the number of items allocated for the 3rd argument; the size argument 581 * is a revision check between kernel and userland. 582 */ 583 int 584 devpolicy_get(int *nitemp, size_t sz, devplcysys_t *uitmp) 585 { 586 int i; 587 devplcyent_t *de; 588 devplcysys_t *itmp; 589 int ind; 590 int nitems; 591 int err = 0; 592 size_t alloced; 593 594 if (sz != sizeof (devplcysys_t)) 595 return (EINVAL); 596 597 if (copyin(nitemp, &nitems, sizeof (nitems))) 598 return (EFAULT); 599 600 rw_enter(&policyrw, RW_READER); 601 602 if (copyout(&totitems, nitemp, sizeof (totitems))) 603 err = EFAULT; 604 else if (nitems < totitems) 605 err = ENOMEM; 606 607 if (err != 0) { 608 rw_exit(&policyrw); 609 return (err); 610 } 611 612 alloced = totitems * sizeof (devplcysys_t); 613 itmp = kmem_zalloc(alloced, KM_SLEEP); 614 615 itmp[0].dps_rdp = dfltpolicy->dp_rdp; 616 itmp[0].dps_wrp = dfltpolicy->dp_wrp; 617 itmp[0].dps_maj = DEVPOLICY_DFLT_MAJ; 618 619 ind = 1; 620 621 for (i = 0; i < ntabent; i++) { 622 for (de = devpolicy[i].t_ent; de != NULL; de = de->dpe_next) { 623 itmp[ind].dps_maj = devpolicy[i].t_major; 624 itmp[ind].dps_rdp = de->dpe_plcy->dp_rdp; 625 itmp[ind].dps_wrp = de->dpe_plcy->dp_wrp; 626 if (de->dpe_len) 627 (void) strcpy(itmp[ind].dps_minornm, 628 de->dpe_expr); 629 else if (de->dpe_flags & DPE_ALLMINOR) 630 (void) strcpy(itmp[ind].dps_minornm, "*"); 631 else { 632 itmp[ind].dps_lomin = de->dpe_lomin; 633 itmp[ind].dps_himin = de->dpe_himin; 634 itmp[ind].dps_isblock = de->dpe_spec == VBLK; 635 } 636 ind++; 637 } 638 } 639 640 rw_exit(&policyrw); 641 642 if (copyout(itmp, uitmp, alloced)) 643 err = EFAULT; 644 645 kmem_free(itmp, alloced); 646 return (err); 647 } 648 649 /* 650 * Get device policy by device name. 651 * This is the implementation of MODGETDEVPOLICYBYNAME 652 */ 653 int 654 devpolicy_getbyname(size_t sz, devplcysys_t *uitmp, char *devname) 655 { 656 devplcysys_t itm; 657 devplcy_t *plcy; 658 vtype_t spec; 659 vnode_t *vp; 660 661 if (sz != sizeof (devplcysys_t)) 662 return (EINVAL); 663 664 if (lookupname(devname, UIO_USERSPACE, FOLLOW, 665 NULLVPP, &vp) != 0) 666 return (EINVAL); 667 668 spec = vp->v_type; 669 if (spec != VBLK && spec != VCHR) { 670 VN_RELE(vp); 671 return (EINVAL); 672 } 673 674 plcy = devpolicy_find(vp); 675 VN_RELE(vp); 676 677 bzero(&itm, sizeof (itm)); 678 679 /* These are the only values of interest */ 680 itm.dps_rdp = plcy->dp_rdp; 681 itm.dps_wrp = plcy->dp_wrp; 682 683 dpfree(plcy); 684 685 if (copyout(&itm, uitmp, sz)) 686 return (EFAULT); 687 else 688 return (0); 689 } 690 691 static void 692 priv_str_to_set(const char *priv_name, priv_set_t *priv_set) 693 { 694 if (priv_name == NULL || strcmp(priv_name, "none") == 0) { 695 priv_emptyset(priv_set); 696 } else if (strcmp(priv_name, "all") == 0) { 697 priv_fillset(priv_set); 698 } else { 699 int priv; 700 priv = priv_getbyname(priv_name, PRIV_ALLOC); 701 if (priv < 0) { 702 cmn_err(CE_WARN, "fail to allocate privilege: %s", 703 priv_name); 704 return; 705 } 706 priv_emptyset(priv_set); 707 priv_addset(priv_set, priv); 708 } 709 } 710 711 /* 712 * Return device privileges by privilege name 713 * Called by ddi_create_priv_minor_node() 714 */ 715 devplcy_t * 716 devpolicy_priv_by_name(const char *read_priv, const char *write_priv) 717 { 718 devplcy_t *dp; 719 mutex_enter(&policymutex); 720 dp = dpget(); 721 mutex_exit(&policymutex); 722 priv_str_to_set(read_priv, &dp->dp_rdp); 723 priv_str_to_set(write_priv, &dp->dp_wrp); 724 725 return (dp); 726 } 727