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