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