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