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 readdir_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 <alloca.h> 43 #include <nss_dbdefs.h> 44 #include <stdarg.h> 45 #include <syslog.h> 46 #include <sys/acl.h> 47 #include <sys/types.h> 48 #include <sys/stat.h> 49 #include <sys/ddi.h> 50 #include <sys/sunddi.h> 51 #include <sys/devinfo_impl.h> 52 #include <sys/hwconf.h> 53 #include <sys/modctl.h> 54 #include <libnvpair.h> 55 #include <device_info.h> 56 #include <regex.h> 57 #include <strings.h> 58 #include <libdevinfo.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 #define MAX_LINELEN 256 69 #define LOGINDEVPERM "/etc/logindevperm" 70 #define DIRWILD "/*" /* directory wildcard */ 71 #define DIRWLDLEN 2 /* strlen(DIRWILD) */ 72 73 /* 74 * Revoke all access to a device node and make sure that there are 75 * no interposed streams devices attached. Must be called before a 76 * device is actually opened. 77 * When fdetach is called, the underlying device node is revealed; it 78 * will have the previous owner and that owner can re-attach; so we 79 * retry until we win. 80 * Ignore non-existent devices. 81 */ 82 static int 83 setdevaccess(char *dev, uid_t uid, gid_t gid, mode_t mode, 84 void (*errmsg)(char *)) 85 { 86 int err = 0, local_errno; 87 char errstring[MAX_LINELEN]; 88 89 if (chown(dev, uid, gid) == -1) { 90 if (errno == ENOENT) /* no such file */ 91 return (0); 92 err = -1; 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, *result; 379 380 /* path must be a valid name */ 381 if (stat(path, &stat_buf) == -1) { 382 return (-1); 383 } else { 384 if (!S_ISDIR(stat_buf.st_mode)) { 385 if (strlen(left_to_do) == 0) { 386 /* finally check the driver matches */ 387 if (check_driver_match(path, line) == 0) { 388 /* we are done, set the permissions */ 389 if (setdevaccess(path, 390 uid, gid, mode, errmsg)) { 391 392 return (-1); 393 } 394 } 395 } 396 return (0); 397 } 398 } 399 400 dirp = opendir(path); 401 if (dirp == NULL) { 402 return (0); 403 } else { 404 char *p = strchr(left_to_do, '/'); 405 regex_t regex; 406 int alwaysmatch = 0; 407 char *match; 408 char *name, *newpath, *remainder_path; 409 410 newpath = (char *)malloc(MAXPATHLEN); 411 if (newpath == NULL) { 412 return (-1); 413 } 414 match = (char *)calloc(MAXPATHLEN, 1); 415 if (match == NULL) { 416 free(newpath); 417 return (-1); 418 } 419 420 if (p) { 421 (void) strncpy(match, left_to_do, p - left_to_do); 422 } else { 423 (void) strcpy(match, left_to_do); 424 } 425 426 if (strcmp(match, "*") == 0) { 427 alwaysmatch = 1; 428 } else { 429 if (regcomp(®ex, match, REG_EXTENDED) != 0) { 430 free(newpath); 431 free(match); 432 return (-1); 433 } 434 } 435 436 direntp = alloca(sizeof (struct dirent) + MAXPATHLEN); 437 while (readdir_r(dirp, direntp, &result) == 0) { 438 if (result == NULL) 439 break; 440 441 name = direntp->d_name; 442 if ((strcmp(name, ".") == 0) || 443 (strcmp(name, "..") == 0)) 444 continue; 445 446 if (alwaysmatch || 447 regexec(®ex, name, 0, NULL, 0) == 0) { 448 if (strcmp(path, "/") == 0) { 449 (void) snprintf(newpath, 450 MAXPATHLEN, "%s%s", path, name); 451 } else { 452 (void) snprintf(newpath, 453 MAXPATHLEN, "%s/%s", path, name); 454 } 455 456 /* 457 * recurse but adjust what is still left to do 458 */ 459 remainder_path = (p ? 460 left_to_do + (p - left_to_do) + 1 : 461 &left_to_do[strlen(left_to_do)]); 462 if (dir_dev_acc(newpath, remainder_path, 463 uid, gid, mode, line, errmsg)) { 464 err = -1; 465 break; 466 } 467 } 468 } 469 (void) closedir(dirp); 470 free(newpath); 471 free(match); 472 if (!alwaysmatch) { 473 regfree(®ex); 474 } 475 } 476 477 return (err); 478 } 479 480 /* 481 * di_devperm_login - modify access of devices in /etc/logindevperm 482 * by changing owner/group/permissions to that of ttyn. 483 */ 484 int 485 di_devperm_login(const char *ttyn, uid_t uid, gid_t gid, 486 void (*errmsg)(char *)) 487 { 488 int err; 489 struct group grp, *grpp; 490 gid_t tty_gid; 491 char grbuf[NSS_BUFLEN_GROUP]; 492 493 if (errmsg == NULL) 494 errmsg = logerror; 495 496 if (ttyn == NULL) { 497 (*errmsg)("di_devperm_login: NULL tty device\n"); 498 return (-1); 499 } 500 501 if (getgrnam_r("tty", &grp, grbuf, NSS_BUFLEN_GROUP, &grpp) != 0) { 502 tty_gid = grpp->gr_gid; 503 } else { 504 /* 505 * this should never happen, but if it does set 506 * group to tty's traditional value. 507 */ 508 tty_gid = 7; 509 } 510 511 /* set the login console device permission */ 512 err = setdevaccess((char *)ttyn, uid, tty_gid, 513 S_IRUSR|S_IWUSR|S_IWGRP, errmsg); 514 if (err) { 515 return (err); 516 } 517 518 /* set the device permissions */ 519 return (logindevperm(ttyn, uid, gid, errmsg)); 520 } 521 522 /* 523 * di_devperm_logout - clean up access of devices in /etc/logindevperm 524 * by resetting owner/group/permissions. 525 */ 526 int 527 di_devperm_logout(const char *ttyn) 528 { 529 struct passwd *pwd; 530 uid_t root_uid; 531 gid_t root_gid; 532 533 if (ttyn == NULL) 534 return (-1); 535 536 pwd = getpwnam("root"); 537 if (pwd != NULL) { 538 root_uid = pwd->pw_uid; 539 root_gid = pwd->pw_gid; 540 } else { 541 /* 542 * this should never happen, but if it does set user 543 * and group to root's traditional values. 544 */ 545 root_uid = 0; 546 root_gid = 0; 547 } 548 549 return (logindevperm(ttyn, root_uid, root_gid, NULL)); 550 } 551 552 static void 553 logerror(char *errstring) 554 { 555 syslog(LOG_AUTH | LOG_CRIT, "%s", errstring); 556 } 557 558 559 /* 560 * Tokens are separated by ' ', '\t', ':', '=', '&', '|', ';', '\n', or '\0' 561 */ 562 static int 563 getnexttoken(char *next, char **nextp, char **tokenpp, char *tchar) 564 { 565 char *cp; 566 char *cp1; 567 char *tokenp; 568 569 cp = next; 570 while (*cp == ' ' || *cp == '\t') { 571 cp++; /* skip leading spaces */ 572 } 573 tokenp = cp; /* start of token */ 574 while (*cp != '\0' && *cp != '\n' && *cp != ' ' && *cp != '\t' && 575 *cp != ':' && *cp != '=' && *cp != '&' && 576 *cp != '|' && *cp != ';') { 577 cp++; /* point to next character */ 578 } 579 /* 580 * If terminating character is a space or tab, look ahead to see if 581 * there's another terminator that's not a space or a tab. 582 * (This code handles trailing spaces.) 583 */ 584 if (*cp == ' ' || *cp == '\t') { 585 cp1 = cp; 586 while (*++cp1 == ' ' || *cp1 == '\t') 587 ; 588 if (*cp1 == '=' || *cp1 == ':' || *cp1 == '&' || *cp1 == '|' || 589 *cp1 == ';' || *cp1 == '\n' || *cp1 == '\0') { 590 *cp = NULL; /* terminate token */ 591 cp = cp1; 592 } 593 } 594 if (tchar != NULL) { 595 *tchar = *cp; /* save terminating character */ 596 if (*tchar == '\0') { 597 *tchar = '\n'; 598 } 599 } 600 *cp++ = '\0'; /* terminate token, point to next */ 601 *nextp = cp; /* set pointer to next character */ 602 if (cp - tokenp - 1 == 0) { 603 return (0); 604 } 605 *tokenpp = tokenp; 606 return (1); 607 } 608 609 /* 610 * get a decimal octal or hex number. Handle '~' for one's complement. 611 */ 612 static int 613 getvalue(char *token, int *valuep) 614 { 615 int radix; 616 int retval = 0; 617 int onescompl = 0; 618 int negate = 0; 619 char c; 620 621 if (*token == '~') { 622 onescompl++; /* perform one's complement on result */ 623 token++; 624 } else if (*token == '-') { 625 negate++; 626 token++; 627 } 628 if (*token == '0') { 629 token++; 630 c = *token; 631 632 if (c == '\0') { 633 *valuep = 0; /* value is 0 */ 634 return (0); 635 } 636 637 if (c == 'x' || c == 'X') { 638 radix = 16; 639 token++; 640 } else { 641 radix = 8; 642 } 643 } else 644 radix = 10; 645 646 while ((c = *token++)) { 647 switch (radix) { 648 case 8: 649 if (c >= '0' && c <= '7') { 650 c -= '0'; 651 } else { 652 /* invalid number */ 653 return (0); 654 } 655 retval = (retval << 3) + c; 656 break; 657 case 10: 658 if (c >= '0' && c <= '9') { 659 c -= '0'; 660 } else { 661 /* invalid number */ 662 return (0); 663 } 664 retval = (retval * 10) + c; 665 break; 666 case 16: 667 if (c >= 'a' && c <= 'f') { 668 c = c - 'a' + 10; 669 } else if (c >= 'A' && c <= 'F') { 670 c = c - 'A' + 10; 671 } else if (c >= '0' && c <= '9') { 672 c -= '0'; 673 } else { 674 /* invalid number */ 675 return (0); 676 } 677 retval = (retval << 4) + c; 678 break; 679 } 680 } 681 if (onescompl) { 682 retval = ~retval; 683 } 684 if (negate) { 685 retval = -retval; 686 } 687 *valuep = retval; 688 return (1); 689 } 690 691 /* 692 * Read /etc/minor_perm, return mperm list of entries 693 */ 694 struct mperm * 695 i_devfs_read_minor_perm(char *drvname, void (*errcb)(minorperm_err_t, int)) 696 { 697 FILE *pfd; 698 struct mperm *mp; 699 char line[MAX_MINOR_PERM_LINE]; 700 char *cp, *p, t; 701 struct mperm *minor_perms = NULL; 702 struct mperm *mptail = NULL; 703 struct passwd *pw; 704 struct group *gp; 705 uid_t root_uid; 706 gid_t sys_gid; 707 int ln = 0; 708 709 /* 710 * Get root/sys ids, these being the most common 711 */ 712 if ((pw = getpwnam(DEFAULT_DEV_USER)) != NULL) { 713 root_uid = pw->pw_uid; 714 } else { 715 (*errcb)(MP_CANT_FIND_USER_ERR, 0); 716 root_uid = (uid_t)0; /* assume 0 is root */ 717 } 718 if ((gp = getgrnam(DEFAULT_DEV_GROUP)) != NULL) { 719 sys_gid = gp->gr_gid; 720 } else { 721 (*errcb)(MP_CANT_FIND_GROUP_ERR, 0); 722 sys_gid = (gid_t)3; /* assume 3 is sys */ 723 } 724 725 if ((pfd = fopen(MINOR_PERM_FILE, "r")) == NULL) { 726 (*errcb)(MP_FOPEN_ERR, errno); 727 return (NULL); 728 } 729 while (fgets(line, MAX_MINOR_PERM_LINE - 1, pfd) != NULL) { 730 ln++; 731 mp = (struct mperm *)calloc(1, sizeof (struct mperm)); 732 if (mp == NULL) { 733 (*errcb)(MP_ALLOC_ERR, sizeof (struct mperm)); 734 continue; 735 } 736 cp = line; 737 if (getnexttoken(cp, &cp, &p, &t) == 0) { 738 (*errcb)(MP_IGNORING_LINE_ERR, ln); 739 devfs_free_minor_perm(mp); 740 continue; 741 } 742 mp->mp_drvname = strdup(p); 743 if (mp->mp_drvname == NULL) { 744 (*errcb)(MP_ALLOC_ERR, strlen(p)+1); 745 devfs_free_minor_perm(mp); 746 continue; 747 } else if (t == '\n' || t == '\0') { 748 (*errcb)(MP_IGNORING_LINE_ERR, ln); 749 devfs_free_minor_perm(mp); 750 continue; 751 } 752 if (t == ':') { 753 if (getnexttoken(cp, &cp, &p, &t) == 0) { 754 (*errcb)(MP_IGNORING_LINE_ERR, ln); 755 devfs_free_minor_perm(mp); 756 } 757 mp->mp_minorname = strdup(p); 758 if (mp->mp_minorname == NULL) { 759 (*errcb)(MP_ALLOC_ERR, strlen(p)+1); 760 devfs_free_minor_perm(mp); 761 continue; 762 } 763 } else { 764 mp->mp_minorname = NULL; 765 } 766 767 if (t == '\n' || t == '\0') { 768 devfs_free_minor_perm(mp); 769 (*errcb)(MP_IGNORING_LINE_ERR, ln); 770 continue; 771 } 772 if (getnexttoken(cp, &cp, &p, &t) == 0) { 773 goto link; 774 } 775 if (getvalue(p, (int *)&mp->mp_mode) == 0) { 776 goto link; 777 } 778 if (t == '\n' || t == '\0') { /* no owner or group */ 779 goto link; 780 } 781 if (getnexttoken(cp, &cp, &p, &t) == 0) { 782 goto link; 783 } 784 mp->mp_owner = strdup(p); 785 if (mp->mp_owner == NULL) { 786 (*errcb)(MP_ALLOC_ERR, strlen(p)+1); 787 devfs_free_minor_perm(mp); 788 continue; 789 } else if (t == '\n' || t == '\0') { /* no group */ 790 goto link; 791 } 792 if (getnexttoken(cp, &cp, &p, 0) == 0) { 793 goto link; 794 } 795 mp->mp_group = strdup(p); 796 if (mp->mp_group == NULL) { 797 (*errcb)(MP_ALLOC_ERR, strlen(p)+1); 798 devfs_free_minor_perm(mp); 799 continue; 800 } 801 link: 802 if (drvname != NULL) { 803 /* 804 * We only want the minor perm entry for a 805 * the named driver. The driver name is the 806 * minor in the clone case. 807 */ 808 if (strcmp(mp->mp_drvname, "clone") == 0) { 809 if (mp->mp_minorname == NULL || 810 strcmp(drvname, mp->mp_minorname) != 0) { 811 devfs_free_minor_perm(mp); 812 continue; 813 } 814 } else { 815 if (strcmp(drvname, mp->mp_drvname) != 0) { 816 devfs_free_minor_perm(mp); 817 continue; 818 } 819 } 820 } 821 if (minor_perms == NULL) { 822 minor_perms = mp; 823 } else { 824 mptail->mp_next = mp; 825 } 826 mptail = mp; 827 828 /* 829 * Compute the uid's and gid's here - there are 830 * fewer lines in the /etc/minor_perm file than there 831 * are devices to be stat(2)ed. And almost every 832 * device is 'root sys'. See 1135520. 833 */ 834 if (mp->mp_owner == NULL || 835 strcmp(mp->mp_owner, DEFAULT_DEV_USER) == 0 || 836 (pw = getpwnam(mp->mp_owner)) == NULL) { 837 mp->mp_uid = root_uid; 838 } else { 839 mp->mp_uid = pw->pw_uid; 840 } 841 842 if (mp->mp_group == NULL || 843 strcmp(mp->mp_group, DEFAULT_DEV_GROUP) == 0 || 844 (gp = getgrnam(mp->mp_group)) == NULL) { 845 mp->mp_gid = sys_gid; 846 } else { 847 mp->mp_gid = gp->gr_gid; 848 } 849 } 850 851 if (fclose(pfd) == EOF) { 852 (*errcb)(MP_FCLOSE_ERR, errno); 853 } 854 855 return (minor_perms); 856 } 857 858 struct mperm * 859 devfs_read_minor_perm(void (*errcb)(minorperm_err_t, int)) 860 { 861 return (i_devfs_read_minor_perm(NULL, errcb)); 862 } 863 864 static struct mperm * 865 i_devfs_read_minor_perm_by_driver(char *drvname, 866 void (*errcb)(minorperm_err_t mp_err, int key)) 867 { 868 return (i_devfs_read_minor_perm(drvname, errcb)); 869 } 870 871 /* 872 * Free mperm list of entries 873 */ 874 void 875 devfs_free_minor_perm(struct mperm *mplist) 876 { 877 struct mperm *mp, *next; 878 879 for (mp = mplist; mp != NULL; mp = next) { 880 next = mp->mp_next; 881 882 if (mp->mp_drvname) 883 free(mp->mp_drvname); 884 if (mp->mp_minorname) 885 free(mp->mp_minorname); 886 if (mp->mp_owner) 887 free(mp->mp_owner); 888 if (mp->mp_group) 889 free(mp->mp_group); 890 free(mp); 891 } 892 } 893 894 static int 895 i_devfs_add_perm_entry(nvlist_t *nvl, struct mperm *mp) 896 { 897 int err; 898 899 err = nvlist_add_string(nvl, mp->mp_drvname, mp->mp_minorname); 900 if (err != 0) 901 return (err); 902 903 err = nvlist_add_int32(nvl, "mode", (int32_t)mp->mp_mode); 904 if (err != 0) 905 return (err); 906 907 err = nvlist_add_int32(nvl, "uid", (int32_t)mp->mp_uid); 908 if (err != 0) 909 return (err); 910 911 err = nvlist_add_int32(nvl, "gid", (int32_t)mp->mp_gid); 912 return (err); 913 } 914 915 static nvlist_t * 916 i_devfs_minor_perm_nvlist(struct mperm *mplist, 917 void (*errcb)(minorperm_err_t, int)) 918 { 919 int err; 920 struct mperm *mp; 921 nvlist_t *nvl = NULL; 922 923 if ((err = nvlist_alloc(&nvl, 0, 0)) != 0) { 924 (*errcb)(MP_NVLIST_ERR, err); 925 return (NULL); 926 } 927 928 for (mp = mplist; mp != NULL; mp = mp->mp_next) { 929 if ((err = i_devfs_add_perm_entry(nvl, mp)) != 0) { 930 (*errcb)(MP_NVLIST_ERR, err); 931 nvlist_free(nvl); 932 return (NULL); 933 } 934 } 935 936 return (nvl); 937 } 938 939 /* 940 * Load all minor perm entries into the kernel 941 * Done at boot time via devfsadm 942 */ 943 int 944 devfs_load_minor_perm(struct mperm *mplist, 945 void (*errcb)(minorperm_err_t, int)) 946 { 947 int err; 948 char *buf = NULL; 949 size_t buflen; 950 nvlist_t *nvl; 951 952 nvl = i_devfs_minor_perm_nvlist(mplist, errcb); 953 if (nvl == NULL) 954 return (-1); 955 956 if (nvlist_pack(nvl, &buf, &buflen, NV_ENCODE_NATIVE, 0) != 0) { 957 nvlist_free(nvl); 958 return (-1); 959 } 960 961 err = modctl(MODLOADMINORPERM, buf, buflen); 962 nvlist_free(nvl); 963 free(buf); 964 965 return (err); 966 } 967 968 /* 969 * Add/remove minor perm entry for a driver 970 */ 971 static int 972 i_devfs_update_minor_perm(char *drv, int ctl, 973 void (*errcb)(minorperm_err_t, int)) 974 { 975 int err; 976 char *buf; 977 size_t buflen; 978 nvlist_t *nvl; 979 struct mperm *mplist; 980 981 mplist = i_devfs_read_minor_perm_by_driver(drv, errcb); 982 983 nvl = i_devfs_minor_perm_nvlist(mplist, errcb); 984 if (nvl == NULL) 985 return (-1); 986 987 buf = NULL; 988 if (nvlist_pack(nvl, &buf, &buflen, NV_ENCODE_NATIVE, 0) != 0) { 989 nvlist_free(nvl); 990 return (-1); 991 } 992 993 err = modctl(ctl, buf, buflen); 994 nvlist_free(nvl); 995 devfs_free_minor_perm(mplist); 996 free(buf); 997 998 return (err); 999 } 1000 1001 int 1002 devfs_add_minor_perm(char *drv, 1003 void (*errcb)(minorperm_err_t, int)) 1004 { 1005 return (i_devfs_update_minor_perm(drv, MODADDMINORPERM, errcb)); 1006 } 1007 1008 int 1009 devfs_rm_minor_perm(char *drv, 1010 void (*errcb)(minorperm_err_t, int)) 1011 { 1012 return (i_devfs_update_minor_perm(drv, MODREMMINORPERM, errcb)); 1013 } 1014