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 #define _POSIX_PTHREAD_SEMANTICS /* for getgrnam_r */ 29 #ifdef lint 30 #define _REENTRANT /* for strtok_r */ 31 #endif 32 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <ctype.h> 36 #include <string.h> 37 #include <unistd.h> 38 #include <dirent.h> 39 #include <errno.h> 40 #include <grp.h> 41 #include <pwd.h> 42 #include <nss_dbdefs.h> 43 #include <stdarg.h> 44 #include <syslog.h> 45 #include <sys/acl.h> 46 #include <sys/types.h> 47 #include <sys/stat.h> 48 #include <sys/ddi.h> 49 #include <sys/sunddi.h> 50 #include <sys/devinfo_impl.h> 51 #include <sys/hwconf.h> 52 #include <sys/modctl.h> 53 #include <libnvpair.h> 54 #include <device_info.h> 55 #include <regex.h> 56 #include <strings.h> 57 #include <libdevinfo.h> 58 #include <zone.h> 59 60 extern int is_minor_node(const char *, const char **); 61 62 static int logindevperm(const char *, uid_t, gid_t, void (*)()); 63 static int dir_dev_acc(char *, char *, uid_t, gid_t, mode_t, char *line, 64 void (*)()); 65 static int setdevaccess(char *, uid_t, gid_t, mode_t, void (*)()); 66 static void logerror(char *); 67 68 static int is_blank(char *); 69 70 #define MAX_LINELEN 256 71 #define LOGINDEVPERM "/etc/logindevperm" 72 #define DIRWILD "/*" /* directory wildcard */ 73 #define DIRWLDLEN 2 /* strlen(DIRWILD) */ 74 75 /* 76 * Revoke all access to a device node and make sure that there are 77 * no interposed streams devices attached. Must be called before a 78 * device is actually opened. 79 * When fdetach is called, the underlying device node is revealed; it 80 * will have the previous owner and that owner can re-attach; so we 81 * retry until we win. 82 * Ignore non-existent devices. 83 */ 84 static int 85 setdevaccess(char *dev, uid_t uid, gid_t gid, mode_t mode, 86 void (*errmsg)(char *)) 87 { 88 int err = 0, local_errno; 89 char errstring[MAX_LINELEN]; 90 struct stat st; 91 92 if (chown(dev, uid, gid) == -1) { 93 if (errno == ENOENT) /* no such file */ 94 return (0); 95 err = -1; 96 local_errno = errno; 97 } 98 99 /* 100 * don't fdetach block devices, as it will unmount them 101 */ 102 if (!((stat(dev, &st) == 0) && ((st.st_mode & S_IFMT) == S_IFBLK))) { 103 while (fdetach(dev) == 0) { 104 if (chown(dev, uid, gid) == -1) { 105 err = -1; 106 local_errno = errno; 107 } 108 } 109 if (err && errmsg) { 110 (void) snprintf(errstring, MAX_LINELEN, 111 "failed to chown device %s: %s\n", 112 dev, strerror(local_errno)); 113 (*errmsg)(errstring); 114 } 115 } 116 117 /* 118 * strip_acl sets an acl and changes the files owner/group 119 */ 120 err = acl_strip(dev, uid, gid, mode); 121 122 if (err != 0) { 123 /* 124 * If the file system returned ENOSYS, we know that it 125 * doesn't support ACLs, therefore, we must assume that 126 * there were no ACLs to remove in the first place. 127 */ 128 err = 0; 129 if (errno != ENOSYS) { 130 err = -1; 131 132 if (errmsg) { 133 (void) snprintf(errstring, MAX_LINELEN, 134 "failed to set acl on device %s: %s\n", 135 dev, strerror(errno)); 136 (*errmsg)(errstring); 137 } 138 } 139 if (chmod(dev, mode) == -1) { 140 err = -1; 141 if (errmsg) { 142 (void) snprintf(errstring, MAX_LINELEN, 143 "failed to chmod device %s: %s\n", 144 dev, strerror(errno)); 145 (*errmsg)(errstring); 146 } 147 } 148 } 149 150 return (err); 151 } 152 153 /* 154 * logindevperm - change owner/group/permissions of devices 155 * list in /etc/logindevperm. 156 */ 157 static int 158 logindevperm(const char *ttyn, uid_t uid, gid_t gid, void (*errmsg)(char *)) 159 { 160 int err = 0, lineno = 0; 161 const char *field_delims = " \t\n"; 162 char line[MAX_LINELEN], errstring[MAX_LINELEN]; 163 char saveline[MAX_LINELEN]; 164 char *console; 165 char *mode_str; 166 char *dev_list; 167 char *device; 168 char *ptr; 169 int mode; 170 FILE *fp; 171 172 if ((fp = fopen(LOGINDEVPERM, "r")) == NULL) { 173 if (errmsg) { 174 (void) snprintf(errstring, MAX_LINELEN, 175 LOGINDEVPERM ": open failed: %s\n", 176 strerror(errno)); 177 (*errmsg)(errstring); 178 } 179 return (-1); 180 } 181 182 while (fgets(line, MAX_LINELEN, fp) != NULL) { 183 char *last; 184 lineno++; 185 186 if ((ptr = strchr(line, '#')) != NULL) 187 *ptr = '\0'; /* handle comments */ 188 189 (void) strcpy(saveline, line); 190 191 console = strtok_r(line, field_delims, &last); 192 if (console == NULL) 193 continue; /* ignore blank lines */ 194 195 if (strcmp(console, ttyn) != 0) 196 continue; /* not our tty, skip */ 197 198 mode_str = strtok_r(last, field_delims, &last); 199 if (mode_str == NULL) { 200 err = -1; /* invalid entry, skip */ 201 if (errmsg) { 202 (void) snprintf(errstring, MAX_LINELEN, 203 LOGINDEVPERM 204 ": line %d, invalid entry -- %s\n", 205 lineno, line); 206 (*errmsg)(errstring); 207 } 208 continue; 209 } 210 211 /* convert string to octal value */ 212 mode = strtol(mode_str, &ptr, 8); 213 if (mode < 0 || mode > 0777 || *ptr != '\0') { 214 err = -1; /* invalid mode, skip */ 215 if (errmsg) { 216 (void) snprintf(errstring, MAX_LINELEN, 217 LOGINDEVPERM 218 ": line %d, invalid mode -- %s\n", 219 lineno, mode_str); 220 (*errmsg)(errstring); 221 } 222 continue; 223 } 224 225 dev_list = strtok_r(last, field_delims, &last); 226 if (dev_list == NULL) { 227 err = -1; /* empty device list, skip */ 228 if (errmsg) { 229 (void) snprintf(errstring, MAX_LINELEN, 230 LOGINDEVPERM 231 ": line %d, empty device list -- %s\n", 232 lineno, line); 233 (*errmsg)(errstring); 234 } 235 continue; 236 } 237 238 device = strtok_r(dev_list, ":", &last); 239 while (device != NULL) { 240 if ((device[0] != '/') || (strlen(device) <= 1)) { 241 err = -1; 242 } else if (dir_dev_acc("/", &device[1], uid, gid, mode, 243 saveline, errmsg)) { 244 err = -1; 245 } 246 device = strtok_r(last, ":", &last); 247 } 248 } 249 (void) fclose(fp); 250 return (err); 251 } 252 253 /* 254 * returns 0 if resolved, -1 otherwise. 255 * devpath: Absolute path to /dev link 256 * devfs_path: Returns malloced string: /devices path w/out "/devices" 257 */ 258 static int 259 resolve_link(char *devpath, char **devfs_path) 260 { 261 char contents[PATH_MAX + 1]; 262 char stage_link[PATH_MAX + 1]; 263 char *ptr; 264 int linksize; 265 char *slashdev = "/dev/"; 266 267 if (devfs_path) { 268 *devfs_path = NULL; 269 } 270 271 linksize = readlink(devpath, contents, PATH_MAX); 272 273 if (linksize <= 0) { 274 return (-1); 275 } else { 276 contents[linksize] = '\0'; 277 } 278 279 /* 280 * if the link contents is not a minor node assume 281 * that link contents is really a pointer to another 282 * link, and if so recurse and read its link contents. 283 */ 284 if (is_minor_node((const char *)contents, (const char **)&ptr) != 285 1) { 286 if (strncmp(contents, slashdev, strlen(slashdev)) == 0) { 287 /* absolute path, starting with /dev */ 288 (void) strcpy(stage_link, contents); 289 } else { 290 /* relative path, prefix devpath */ 291 if ((ptr = strrchr(devpath, '/')) == NULL) { 292 /* invalid link */ 293 return (-1); 294 } 295 *ptr = '\0'; 296 (void) strcpy(stage_link, devpath); 297 *ptr = '/'; 298 (void) strcat(stage_link, "/"); 299 (void) strcat(stage_link, contents); 300 301 } 302 return (resolve_link(stage_link, devfs_path)); 303 } 304 305 if (devfs_path) { 306 *devfs_path = strdup(ptr); 307 if (*devfs_path == NULL) { 308 return (-1); 309 } 310 } 311 312 return (0); 313 } 314 315 /* 316 * check a logindevperm line for a driver list and match this against 317 * the driver of the minor node 318 * returns 0 if no drivers were specified or a driver match 319 */ 320 static int 321 check_driver_match(char *path, char *line) 322 { 323 char *drv, *driver, *lasts; 324 char *devfs_path = NULL; 325 char saveline[MAX_LINELEN]; 326 char *p; 327 328 if (resolve_link(path, &devfs_path) == 0) { 329 char *p; 330 char pwd_buf[PATH_MAX]; 331 di_node_t node; 332 333 /* truncate on : so we can take a snapshot */ 334 (void) strcpy(pwd_buf, devfs_path); 335 p = strrchr(pwd_buf, ':'); 336 *p = '\0'; 337 338 node = di_init(pwd_buf, DINFOMINOR); 339 free(devfs_path); 340 341 if (node) { 342 drv = di_driver_name(node); 343 di_fini(node); 344 } else { 345 return (0); 346 } 347 } else { 348 return (0); 349 } 350 351 (void) strcpy(saveline, line); 352 353 p = strstr(saveline, "driver"); 354 if (p == NULL) { 355 return (0); 356 } 357 358 driver = strtok_r(p, "=", &lasts); 359 if (driver) { 360 if (strcmp(driver, "driver") == 0) { 361 driver = strtok_r(NULL, ", \t\n", &lasts); 362 while (driver) { 363 if (strcmp(driver, drv) == 0) { 364 return (0); 365 } 366 driver = strtok_r(NULL, ", \t\n", &lasts); 367 } 368 } 369 } 370 371 return (-1); 372 } 373 374 /* 375 * Apply owner/group/perms to all files (except "." and "..") 376 * in a directory. 377 * This function is recursive. We start with "/" and the rest of the pathname 378 * in left_to_do argument, and we walk the entire pathname which may contain 379 * regular expressions or '*' for each directory name or basename. 380 */ 381 static int 382 dir_dev_acc(char *path, char *left_to_do, uid_t uid, gid_t gid, mode_t mode, 383 char *line, void (*errmsg)(char *)) 384 { 385 struct stat stat_buf; 386 int err = 0; 387 char errstring[MAX_LINELEN]; 388 char *p; 389 regex_t regex; 390 int alwaysmatch = 0; 391 char *match; 392 char *name, *newpath, *remainder_path; 393 finddevhdl_t handle; 394 395 /* 396 * Determine if the search needs to be performed via finddev, 397 * which returns only persisted names in the global /dev, or 398 * readdir, for paths other than /dev and non-global zones. 399 * This use of finddev avoids triggering potential implicit 400 * reconfig for names managed by logindevperm but not present 401 * on the system. 402 */ 403 if (!device_exists(path)) { 404 return (-1); 405 } 406 if (stat(path, &stat_buf) == -1) { 407 /* 408 * ENOENT errors are expected errors when there are 409 * dangling /dev device links. Ignore them silently 410 */ 411 if (errno == ENOENT) { 412 return (0); 413 } 414 if (errmsg) { 415 (void) snprintf(errstring, MAX_LINELEN, 416 "failed to stat %s: %s\n", path, 417 strerror(errno)); 418 (*errmsg)(errstring); 419 } 420 return (-1); 421 } else { 422 if (!S_ISDIR(stat_buf.st_mode)) { 423 if (strlen(left_to_do) == 0) { 424 /* finally check the driver matches */ 425 if (check_driver_match(path, line) == 0) { 426 /* we are done, set the permissions */ 427 if (setdevaccess(path, 428 uid, gid, mode, errmsg)) { 429 430 return (-1); 431 } 432 } 433 } 434 return (0); 435 } 436 } 437 438 if (finddev_readdir(path, &handle) != 0) 439 return (0); 440 441 p = strchr(left_to_do, '/'); 442 alwaysmatch = 0; 443 444 newpath = (char *)malloc(MAXPATHLEN); 445 if (newpath == NULL) { 446 finddev_close(handle); 447 return (-1); 448 } 449 match = (char *)calloc(MAXPATHLEN, 1); 450 if (match == NULL) { 451 finddev_close(handle); 452 free(newpath); 453 return (-1); 454 } 455 456 if (p) { 457 (void) strncpy(match, left_to_do, p - left_to_do); 458 } else { 459 (void) strcpy(match, left_to_do); 460 } 461 462 if (strcmp(match, "*") == 0) { 463 alwaysmatch = 1; 464 } else { 465 if (regcomp(®ex, match, REG_EXTENDED) != 0) { 466 free(newpath); 467 free(match); 468 finddev_close(handle); 469 return (-1); 470 } 471 } 472 473 while ((name = (char *)finddev_next(handle)) != NULL) { 474 if (alwaysmatch || 475 regexec(®ex, name, 0, NULL, 0) == 0) { 476 if (strcmp(path, "/") == 0) { 477 (void) snprintf(newpath, 478 MAXPATHLEN, "%s%s", path, name); 479 } else { 480 (void) snprintf(newpath, 481 MAXPATHLEN, "%s/%s", path, name); 482 } 483 484 /* 485 * recurse but adjust what is still left to do 486 */ 487 remainder_path = (p ? 488 left_to_do + (p - left_to_do) + 1 : 489 &left_to_do[strlen(left_to_do)]); 490 if (dir_dev_acc(newpath, remainder_path, 491 uid, gid, mode, line, errmsg)) { 492 err = -1; 493 } 494 } 495 } 496 497 finddev_close(handle); 498 free(newpath); 499 free(match); 500 if (!alwaysmatch) { 501 regfree(®ex); 502 } 503 504 return (err); 505 } 506 507 /* 508 * di_devperm_login - modify access of devices in /etc/logindevperm 509 * by changing owner/group/permissions to that of ttyn. 510 */ 511 int 512 di_devperm_login(const char *ttyn, uid_t uid, gid_t gid, 513 void (*errmsg)(char *)) 514 { 515 int err; 516 struct group grp, *grpp; 517 gid_t tty_gid; 518 char grbuf[NSS_BUFLEN_GROUP]; 519 520 if (errmsg == NULL) 521 errmsg = logerror; 522 523 if (ttyn == NULL) { 524 (*errmsg)("di_devperm_login: NULL tty device\n"); 525 return (-1); 526 } 527 528 if (getgrnam_r("tty", &grp, grbuf, NSS_BUFLEN_GROUP, &grpp) != 0) { 529 tty_gid = grpp->gr_gid; 530 } else { 531 /* 532 * this should never happen, but if it does set 533 * group to tty's traditional value. 534 */ 535 tty_gid = 7; 536 } 537 538 /* set the login console device permission */ 539 err = setdevaccess((char *)ttyn, uid, tty_gid, 540 S_IRUSR|S_IWUSR|S_IWGRP, errmsg); 541 if (err) { 542 return (err); 543 } 544 545 /* set the device permissions */ 546 return (logindevperm(ttyn, uid, gid, errmsg)); 547 } 548 549 /* 550 * di_devperm_logout - clean up access of devices in /etc/logindevperm 551 * by resetting owner/group/permissions. 552 */ 553 int 554 di_devperm_logout(const char *ttyn) 555 { 556 struct passwd *pwd; 557 uid_t root_uid; 558 gid_t root_gid; 559 560 if (ttyn == NULL) 561 return (-1); 562 563 pwd = getpwnam("root"); 564 if (pwd != NULL) { 565 root_uid = pwd->pw_uid; 566 root_gid = pwd->pw_gid; 567 } else { 568 /* 569 * this should never happen, but if it does set user 570 * and group to root's traditional values. 571 */ 572 root_uid = 0; 573 root_gid = 0; 574 } 575 576 return (logindevperm(ttyn, root_uid, root_gid, NULL)); 577 } 578 579 static void 580 logerror(char *errstring) 581 { 582 syslog(LOG_AUTH | LOG_CRIT, "%s", errstring); 583 } 584 585 586 /* 587 * Tokens are separated by ' ', '\t', ':', '=', '&', '|', ';', '\n', or '\0' 588 */ 589 static int 590 getnexttoken(char *next, char **nextp, char **tokenpp, char *tchar) 591 { 592 char *cp; 593 char *cp1; 594 char *tokenp; 595 596 cp = next; 597 while (*cp == ' ' || *cp == '\t') { 598 cp++; /* skip leading spaces */ 599 } 600 tokenp = cp; /* start of token */ 601 while (*cp != '\0' && *cp != '\n' && *cp != ' ' && *cp != '\t' && 602 *cp != ':' && *cp != '=' && *cp != '&' && 603 *cp != '|' && *cp != ';') { 604 cp++; /* point to next character */ 605 } 606 /* 607 * If terminating character is a space or tab, look ahead to see if 608 * there's another terminator that's not a space or a tab. 609 * (This code handles trailing spaces.) 610 */ 611 if (*cp == ' ' || *cp == '\t') { 612 cp1 = cp; 613 while (*++cp1 == ' ' || *cp1 == '\t') 614 ; 615 if (*cp1 == '=' || *cp1 == ':' || *cp1 == '&' || *cp1 == '|' || 616 *cp1 == ';' || *cp1 == '\n' || *cp1 == '\0') { 617 *cp = NULL; /* terminate token */ 618 cp = cp1; 619 } 620 } 621 if (tchar != NULL) { 622 *tchar = *cp; /* save terminating character */ 623 if (*tchar == '\0') { 624 *tchar = '\n'; 625 } 626 } 627 *cp++ = '\0'; /* terminate token, point to next */ 628 *nextp = cp; /* set pointer to next character */ 629 if (cp - tokenp - 1 == 0) { 630 return (0); 631 } 632 *tokenpp = tokenp; 633 return (1); 634 } 635 636 /* 637 * get a decimal octal or hex number. Handle '~' for one's complement. 638 */ 639 static int 640 getvalue(char *token, int *valuep) 641 { 642 int radix; 643 int retval = 0; 644 int onescompl = 0; 645 int negate = 0; 646 char c; 647 648 if (*token == '~') { 649 onescompl++; /* perform one's complement on result */ 650 token++; 651 } else if (*token == '-') { 652 negate++; 653 token++; 654 } 655 if (*token == '0') { 656 token++; 657 c = *token; 658 659 if (c == '\0') { 660 *valuep = 0; /* value is 0 */ 661 return (0); 662 } 663 664 if (c == 'x' || c == 'X') { 665 radix = 16; 666 token++; 667 } else { 668 radix = 8; 669 } 670 } else 671 radix = 10; 672 673 while ((c = *token++)) { 674 switch (radix) { 675 case 8: 676 if (c >= '0' && c <= '7') { 677 c -= '0'; 678 } else { 679 /* invalid number */ 680 return (0); 681 } 682 retval = (retval << 3) + c; 683 break; 684 case 10: 685 if (c >= '0' && c <= '9') { 686 c -= '0'; 687 } else { 688 /* invalid number */ 689 return (0); 690 } 691 retval = (retval * 10) + c; 692 break; 693 case 16: 694 if (c >= 'a' && c <= 'f') { 695 c = c - 'a' + 10; 696 } else if (c >= 'A' && c <= 'F') { 697 c = c - 'A' + 10; 698 } else if (c >= '0' && c <= '9') { 699 c -= '0'; 700 } else { 701 /* invalid number */ 702 return (0); 703 } 704 retval = (retval << 4) + c; 705 break; 706 } 707 } 708 if (onescompl) { 709 retval = ~retval; 710 } 711 if (negate) { 712 retval = -retval; 713 } 714 *valuep = retval; 715 return (1); 716 } 717 718 /* 719 * Read /etc/minor_perm, return mperm list of entries 720 */ 721 struct mperm * 722 i_devfs_read_minor_perm(char *drvname, void (*errcb)(minorperm_err_t, int)) 723 { 724 FILE *pfd; 725 struct mperm *mp; 726 char line[MAX_MINOR_PERM_LINE]; 727 char *cp, *p, t; 728 struct mperm *minor_perms = NULL; 729 struct mperm *mptail = NULL; 730 struct passwd *pw; 731 struct group *gp; 732 uid_t root_uid; 733 gid_t sys_gid; 734 int ln = 0; 735 736 /* 737 * Get root/sys ids, these being the most common 738 */ 739 if ((pw = getpwnam(DEFAULT_DEV_USER)) != NULL) { 740 root_uid = pw->pw_uid; 741 } else { 742 (*errcb)(MP_CANT_FIND_USER_ERR, 0); 743 root_uid = (uid_t)0; /* assume 0 is root */ 744 } 745 if ((gp = getgrnam(DEFAULT_DEV_GROUP)) != NULL) { 746 sys_gid = gp->gr_gid; 747 } else { 748 (*errcb)(MP_CANT_FIND_GROUP_ERR, 0); 749 sys_gid = (gid_t)3; /* assume 3 is sys */ 750 } 751 752 if ((pfd = fopen(MINOR_PERM_FILE, "r")) == NULL) { 753 (*errcb)(MP_FOPEN_ERR, errno); 754 return (NULL); 755 } 756 while (fgets(line, MAX_MINOR_PERM_LINE, pfd) != NULL) { 757 ln++; 758 /* cut off comments starting with '#' */ 759 if ((cp = strchr(line, '#')) != NULL) 760 *cp = '\0'; 761 /* ignore comment or blank lines */ 762 if (is_blank(line)) 763 continue; 764 mp = (struct mperm *)calloc(1, sizeof (struct mperm)); 765 if (mp == NULL) { 766 (*errcb)(MP_ALLOC_ERR, sizeof (struct mperm)); 767 continue; 768 } 769 cp = line; 770 /* sanity-check */ 771 if (getnexttoken(cp, &cp, &p, &t) == 0) { 772 (*errcb)(MP_IGNORING_LINE_ERR, ln); 773 devfs_free_minor_perm(mp); 774 continue; 775 } 776 mp->mp_drvname = strdup(p); 777 if (mp->mp_drvname == NULL) { 778 (*errcb)(MP_ALLOC_ERR, strlen(p)+1); 779 devfs_free_minor_perm(mp); 780 continue; 781 } else if (t == '\n' || t == '\0') { 782 (*errcb)(MP_IGNORING_LINE_ERR, ln); 783 devfs_free_minor_perm(mp); 784 continue; 785 } 786 if (t == ':') { 787 if (getnexttoken(cp, &cp, &p, &t) == 0) { 788 (*errcb)(MP_IGNORING_LINE_ERR, ln); 789 devfs_free_minor_perm(mp); 790 } 791 mp->mp_minorname = strdup(p); 792 if (mp->mp_minorname == NULL) { 793 (*errcb)(MP_ALLOC_ERR, strlen(p)+1); 794 devfs_free_minor_perm(mp); 795 continue; 796 } 797 } else { 798 mp->mp_minorname = NULL; 799 } 800 801 if (t == '\n' || t == '\0') { 802 devfs_free_minor_perm(mp); 803 (*errcb)(MP_IGNORING_LINE_ERR, ln); 804 continue; 805 } 806 if (getnexttoken(cp, &cp, &p, &t) == 0) { 807 goto link; 808 } 809 if (getvalue(p, (int *)&mp->mp_mode) == 0) { 810 goto link; 811 } 812 if (t == '\n' || t == '\0') { /* no owner or group */ 813 goto link; 814 } 815 if (getnexttoken(cp, &cp, &p, &t) == 0) { 816 goto link; 817 } 818 mp->mp_owner = strdup(p); 819 if (mp->mp_owner == NULL) { 820 (*errcb)(MP_ALLOC_ERR, strlen(p)+1); 821 devfs_free_minor_perm(mp); 822 continue; 823 } else if (t == '\n' || t == '\0') { /* no group */ 824 goto link; 825 } 826 if (getnexttoken(cp, &cp, &p, 0) == 0) { 827 goto link; 828 } 829 mp->mp_group = strdup(p); 830 if (mp->mp_group == NULL) { 831 (*errcb)(MP_ALLOC_ERR, strlen(p)+1); 832 devfs_free_minor_perm(mp); 833 continue; 834 } 835 link: 836 if (drvname != NULL) { 837 /* 838 * We only want the minor perm entry for a 839 * the named driver. The driver name is the 840 * minor in the clone case. 841 */ 842 if (strcmp(mp->mp_drvname, "clone") == 0) { 843 if (mp->mp_minorname == NULL || 844 strcmp(drvname, mp->mp_minorname) != 0) { 845 devfs_free_minor_perm(mp); 846 continue; 847 } 848 } else { 849 if (strcmp(drvname, mp->mp_drvname) != 0) { 850 devfs_free_minor_perm(mp); 851 continue; 852 } 853 } 854 } 855 if (minor_perms == NULL) { 856 minor_perms = mp; 857 } else { 858 mptail->mp_next = mp; 859 } 860 mptail = mp; 861 862 /* 863 * Compute the uid's and gid's here - there are 864 * fewer lines in the /etc/minor_perm file than there 865 * are devices to be stat(2)ed. And almost every 866 * device is 'root sys'. See 1135520. 867 */ 868 if (mp->mp_owner == NULL || 869 strcmp(mp->mp_owner, DEFAULT_DEV_USER) == 0 || 870 (pw = getpwnam(mp->mp_owner)) == NULL) { 871 mp->mp_uid = root_uid; 872 } else { 873 mp->mp_uid = pw->pw_uid; 874 } 875 876 if (mp->mp_group == NULL || 877 strcmp(mp->mp_group, DEFAULT_DEV_GROUP) == 0 || 878 (gp = getgrnam(mp->mp_group)) == NULL) { 879 mp->mp_gid = sys_gid; 880 } else { 881 mp->mp_gid = gp->gr_gid; 882 } 883 } 884 885 if (fclose(pfd) == EOF) { 886 (*errcb)(MP_FCLOSE_ERR, errno); 887 } 888 889 return (minor_perms); 890 } 891 892 struct mperm * 893 devfs_read_minor_perm(void (*errcb)(minorperm_err_t, int)) 894 { 895 return (i_devfs_read_minor_perm(NULL, errcb)); 896 } 897 898 static struct mperm * 899 i_devfs_read_minor_perm_by_driver(char *drvname, 900 void (*errcb)(minorperm_err_t mp_err, int key)) 901 { 902 return (i_devfs_read_minor_perm(drvname, errcb)); 903 } 904 905 /* 906 * Free mperm list of entries 907 */ 908 void 909 devfs_free_minor_perm(struct mperm *mplist) 910 { 911 struct mperm *mp, *next; 912 913 for (mp = mplist; mp != NULL; mp = next) { 914 next = mp->mp_next; 915 916 if (mp->mp_drvname) 917 free(mp->mp_drvname); 918 if (mp->mp_minorname) 919 free(mp->mp_minorname); 920 if (mp->mp_owner) 921 free(mp->mp_owner); 922 if (mp->mp_group) 923 free(mp->mp_group); 924 free(mp); 925 } 926 } 927 928 static int 929 i_devfs_add_perm_entry(nvlist_t *nvl, struct mperm *mp) 930 { 931 int err; 932 933 err = nvlist_add_string(nvl, mp->mp_drvname, mp->mp_minorname); 934 if (err != 0) 935 return (err); 936 937 err = nvlist_add_int32(nvl, "mode", (int32_t)mp->mp_mode); 938 if (err != 0) 939 return (err); 940 941 err = nvlist_add_int32(nvl, "uid", (int32_t)mp->mp_uid); 942 if (err != 0) 943 return (err); 944 945 err = nvlist_add_int32(nvl, "gid", (int32_t)mp->mp_gid); 946 return (err); 947 } 948 949 static nvlist_t * 950 i_devfs_minor_perm_nvlist(struct mperm *mplist, 951 void (*errcb)(minorperm_err_t, int)) 952 { 953 int err; 954 struct mperm *mp; 955 nvlist_t *nvl = NULL; 956 957 if ((err = nvlist_alloc(&nvl, 0, 0)) != 0) { 958 (*errcb)(MP_NVLIST_ERR, err); 959 return (NULL); 960 } 961 962 for (mp = mplist; mp != NULL; mp = mp->mp_next) { 963 if ((err = i_devfs_add_perm_entry(nvl, mp)) != 0) { 964 (*errcb)(MP_NVLIST_ERR, err); 965 nvlist_free(nvl); 966 return (NULL); 967 } 968 } 969 970 return (nvl); 971 } 972 973 /* 974 * Load all minor perm entries into the kernel 975 * Done at boot time via devfsadm 976 */ 977 int 978 devfs_load_minor_perm(struct mperm *mplist, 979 void (*errcb)(minorperm_err_t, int)) 980 { 981 int err; 982 char *buf = NULL; 983 size_t buflen; 984 nvlist_t *nvl; 985 986 nvl = i_devfs_minor_perm_nvlist(mplist, errcb); 987 if (nvl == NULL) 988 return (-1); 989 990 if (nvlist_pack(nvl, &buf, &buflen, NV_ENCODE_NATIVE, 0) != 0) { 991 nvlist_free(nvl); 992 return (-1); 993 } 994 995 err = modctl(MODLOADMINORPERM, buf, buflen); 996 nvlist_free(nvl); 997 free(buf); 998 999 return (err); 1000 } 1001 1002 /* 1003 * Add/remove minor perm entry for a driver 1004 */ 1005 static int 1006 i_devfs_update_minor_perm(char *drv, int ctl, 1007 void (*errcb)(minorperm_err_t, int)) 1008 { 1009 int err; 1010 char *buf; 1011 size_t buflen; 1012 nvlist_t *nvl; 1013 struct mperm *mplist; 1014 1015 mplist = i_devfs_read_minor_perm_by_driver(drv, errcb); 1016 1017 nvl = i_devfs_minor_perm_nvlist(mplist, errcb); 1018 if (nvl == NULL) 1019 return (-1); 1020 1021 buf = NULL; 1022 if (nvlist_pack(nvl, &buf, &buflen, NV_ENCODE_NATIVE, 0) != 0) { 1023 nvlist_free(nvl); 1024 return (-1); 1025 } 1026 1027 err = modctl(ctl, buf, buflen); 1028 nvlist_free(nvl); 1029 devfs_free_minor_perm(mplist); 1030 free(buf); 1031 1032 return (err); 1033 } 1034 1035 int 1036 devfs_add_minor_perm(char *drv, 1037 void (*errcb)(minorperm_err_t, int)) 1038 { 1039 return (i_devfs_update_minor_perm(drv, MODADDMINORPERM, errcb)); 1040 } 1041 1042 int 1043 devfs_rm_minor_perm(char *drv, 1044 void (*errcb)(minorperm_err_t, int)) 1045 { 1046 return (i_devfs_update_minor_perm(drv, MODREMMINORPERM, errcb)); 1047 } 1048 1049 /* 1050 * is_blank() returns 1 (true) if a line specified is composed of 1051 * whitespace characters only. otherwise, it returns 0 (false). 1052 * 1053 * Note. the argument (line) must be null-terminated. 1054 */ 1055 static int 1056 is_blank(char *line) 1057 { 1058 for (/* nothing */; *line != '\0'; line++) 1059 if (!isspace(*line)) 1060 return (0); 1061 return (1); 1062 } 1063