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