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