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