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