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