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) 2011 Gary Mills 23 * 24 * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. 25 */ 26 27 #define _POSIX_PTHREAD_SEMANTICS /* for getgrnam_r */ 28 #ifdef lint 29 #define _REENTRANT /* for strtok_r */ 30 #endif 31 32 #include <stdio.h> 33 #include <stdlib.h> 34 #include <ctype.h> 35 #include <string.h> 36 #include <unistd.h> 37 #include <dirent.h> 38 #include <errno.h> 39 #include <grp.h> 40 #include <pwd.h> 41 #include <nss_dbdefs.h> 42 #include <stdarg.h> 43 #include <syslog.h> 44 #include <sys/acl.h> 45 #include <sys/types.h> 46 #include <sys/stat.h> 47 #include <sys/ddi.h> 48 #include <sys/sunddi.h> 49 #include <sys/devinfo_impl.h> 50 #include <sys/hwconf.h> 51 #include <sys/modctl.h> 52 #include <libnvpair.h> 53 #include <device_info.h> 54 #include <regex.h> 55 #include <strings.h> 56 #include <libdevinfo.h> 57 #include <zone.h> 58 #include <fcntl.h> 59 #include <utmpx.h> 60 61 extern int is_minor_node(const char *, const char **); 62 63 static int is_login_user(uid_t); 64 static int logindevperm(const char *, uid_t, gid_t, void (*)()); 65 static int dir_dev_acc(char *, char *, uid_t, gid_t, mode_t, char *line, 66 void (*)()); 67 static int setdevaccess(char *, uid_t, gid_t, mode_t, void (*)()); 68 static void logerror(char *); 69 70 static int is_blank(char *); 71 72 #define MAX_LINELEN 256 73 #define LOGINDEVPERM "/etc/logindevperm" 74 #define DIRWILD "/*" /* directory wildcard */ 75 #define DIRWLDLEN 2 /* strlen(DIRWILD) */ 76 77 /* 78 * Revoke all access to a device node and make sure that there are 79 * no interposed streams devices attached. Must be called before a 80 * device is actually opened. 81 * When fdetach is called, the underlying device node is revealed; it 82 * will have the previous owner and that owner can re-attach; so we 83 * retry until we win. 84 * Ignore non-existent devices. 85 */ 86 static int 87 setdevaccess(char *dev, uid_t uid, gid_t gid, mode_t mode, 88 void (*errmsg)(char *)) 89 { 90 int err = 0, local_errno; 91 char errstring[MAX_LINELEN]; 92 struct stat st; 93 94 if (chown(dev, uid, gid) == -1) { 95 if (errno == ENOENT) /* no such file */ 96 return (0); 97 err = -1; 98 local_errno = errno; 99 } 100 101 /* 102 * don't fdetach block devices, as it will unmount them 103 */ 104 if (!((stat(dev, &st) == 0) && ((st.st_mode & S_IFMT) == S_IFBLK))) { 105 while (fdetach(dev) == 0) { 106 if (chown(dev, uid, gid) == -1) { 107 err = -1; 108 local_errno = errno; 109 } 110 } 111 if (err && errmsg) { 112 (void) snprintf(errstring, MAX_LINELEN, 113 "failed to chown device %s: %s\n", 114 dev, strerror(local_errno)); 115 (*errmsg)(errstring); 116 } 117 } 118 119 /* 120 * strip_acl sets an acl and changes the files owner/group 121 */ 122 err = acl_strip(dev, uid, gid, mode); 123 124 if (err != 0) { 125 /* 126 * If the file system returned ENOSYS, we know that it 127 * doesn't support ACLs, therefore, we must assume that 128 * there were no ACLs to remove in the first place. 129 */ 130 err = 0; 131 if (errno != ENOSYS) { 132 err = -1; 133 134 if (errmsg) { 135 (void) snprintf(errstring, MAX_LINELEN, 136 "failed to set acl on device %s: %s\n", 137 dev, strerror(errno)); 138 (*errmsg)(errstring); 139 } 140 } 141 if (chmod(dev, mode) == -1) { 142 err = -1; 143 if (errmsg) { 144 (void) snprintf(errstring, MAX_LINELEN, 145 "failed to chmod device %s: %s\n", 146 dev, strerror(errno)); 147 (*errmsg)(errstring); 148 } 149 } 150 } 151 152 return (err); 153 } 154 155 /* 156 * logindevperm - change owner/group/permissions of devices 157 * list in /etc/logindevperm. 158 */ 159 static int 160 logindevperm(const char *ttyn, uid_t uid, gid_t gid, void (*errmsg)(char *)) 161 { 162 int err = 0, lineno = 0; 163 const char *field_delims = " \t\n"; 164 char line[MAX_LINELEN], errstring[MAX_LINELEN]; 165 char saveline[MAX_LINELEN]; 166 char *console; 167 char *mode_str; 168 char *dev_list; 169 char *device; 170 char *ptr; 171 int mode; 172 FILE *fp; 173 char ttyn_path[PATH_MAX + 1]; 174 int n; 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 if ((n = resolvepath(ttyn, ttyn_path, PATH_MAX)) == -1) 187 return (-1); 188 ttyn_path[n] = '\0'; 189 190 while (fgets(line, MAX_LINELEN, fp) != NULL) { 191 char *last; 192 char tmp[PATH_MAX + 1]; 193 194 lineno++; 195 196 if ((ptr = strchr(line, '#')) != NULL) 197 *ptr = '\0'; /* handle comments */ 198 199 (void) strcpy(saveline, line); 200 201 console = strtok_r(line, field_delims, &last); 202 if (console == NULL) 203 continue; /* ignore blank lines */ 204 205 if ((n = resolvepath(console, tmp, PATH_MAX)) == -1) 206 continue; 207 tmp[n] = '\0'; 208 209 if (strcmp(ttyn_path, tmp) != 0) 210 continue; 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) != 0) || 400 (ppwd == NULL)) { 401 return (0); 402 } 403 404 setutxent(); 405 while ((utx = getutxent()) != NULL) { 406 if (utx->ut_type == USER_PROCESS && 407 strncmp(utx->ut_user, ppwd->pw_name, 408 strlen(ppwd->pw_name)) == 0 && (strncmp(utx->ut_line, 409 "console", strlen("console")) == 0 || strncmp(utx->ut_line, 410 "vt", strlen("vt")) == 0)) { 411 412 changed = 1; 413 break; 414 } 415 } 416 endutxent(); 417 418 return (changed); 419 } 420 421 /* 422 * Apply owner/group/perms to all files (except "." and "..") 423 * in a directory. 424 * This function is recursive. We start with "/" and the rest of the pathname 425 * in left_to_do argument, and we walk the entire pathname which may contain 426 * regular expressions or '*' for each directory name or basename. 427 */ 428 static int 429 dir_dev_acc(char *path, char *left_to_do, uid_t uid, gid_t gid, mode_t mode, 430 char *line, void (*errmsg)(char *)) 431 { 432 struct stat stat_buf; 433 int err = 0; 434 char errstring[MAX_LINELEN]; 435 char *p; 436 regex_t regex; 437 int alwaysmatch = 0; 438 char *match; 439 char *name, *newpath, *remainder_path; 440 finddevhdl_t handle; 441 442 /* 443 * Determine if the search needs to be performed via finddev, 444 * which returns only persisted names in the global /dev, or 445 * readdir, for paths other than /dev and non-global zones. 446 * This use of finddev avoids triggering potential implicit 447 * reconfig for names managed by logindevperm but not present 448 * on the system. 449 */ 450 if (!device_exists(path)) { 451 return (-1); 452 } 453 if (stat(path, &stat_buf) == -1) { 454 /* 455 * ENOENT errors are expected errors when there are 456 * dangling /dev device links. Ignore them silently 457 */ 458 if (errno == ENOENT) { 459 return (0); 460 } 461 if (errmsg) { 462 (void) snprintf(errstring, MAX_LINELEN, 463 "failed to stat %s: %s\n", path, 464 strerror(errno)); 465 (*errmsg)(errstring); 466 } 467 return (-1); 468 } else { 469 if (!S_ISDIR(stat_buf.st_mode)) { 470 if (strlen(left_to_do) == 0) { 471 /* finally check the driver matches */ 472 if (check_driver_match(path, line) == 0) { 473 /* 474 * if the owner of device has been 475 * login, the ownership and mode 476 * should be set already. in 477 * this case, do not set the 478 * permissions. 479 */ 480 if (is_login_user(stat_buf.st_uid)) { 481 482 return (0); 483 } 484 /* we are done, set the permissions */ 485 if (setdevaccess(path, 486 uid, gid, mode, errmsg)) { 487 488 return (-1); 489 } 490 } 491 } 492 return (0); 493 } 494 } 495 496 if (finddev_readdir(path, &handle) != 0) 497 return (0); 498 499 p = strchr(left_to_do, '/'); 500 alwaysmatch = 0; 501 502 newpath = (char *)malloc(MAXPATHLEN); 503 if (newpath == NULL) { 504 finddev_close(handle); 505 return (-1); 506 } 507 match = (char *)calloc(MAXPATHLEN + 2, 1); 508 if (match == NULL) { 509 finddev_close(handle); 510 free(newpath); 511 return (-1); 512 } 513 514 /* transform pattern into ^pattern$ for exact match */ 515 if (snprintf(match, MAXPATHLEN + 2, "^%.*s$", 516 p ? (p - left_to_do) : strlen(left_to_do), left_to_do) >= 517 MAXPATHLEN + 2) { 518 finddev_close(handle); 519 free(newpath); 520 free(match); 521 return (-1); 522 } 523 524 if (strcmp(match, "^*$") == 0) { 525 alwaysmatch = 1; 526 } else { 527 if (regcomp(®ex, match, REG_EXTENDED) != 0) { 528 free(newpath); 529 free(match); 530 finddev_close(handle); 531 return (-1); 532 } 533 } 534 535 while ((name = (char *)finddev_next(handle)) != NULL) { 536 if (alwaysmatch || 537 regexec(®ex, name, 0, NULL, 0) == 0) { 538 if (strcmp(path, "/") == 0) { 539 (void) snprintf(newpath, 540 MAXPATHLEN, "%s%s", path, name); 541 } else { 542 (void) snprintf(newpath, 543 MAXPATHLEN, "%s/%s", path, name); 544 } 545 546 /* 547 * recurse but adjust what is still left to do 548 */ 549 remainder_path = (p ? 550 left_to_do + (p - left_to_do) + 1 : 551 &left_to_do[strlen(left_to_do)]); 552 if (dir_dev_acc(newpath, remainder_path, 553 uid, gid, mode, line, errmsg)) { 554 err = -1; 555 } 556 } 557 } 558 559 finddev_close(handle); 560 free(newpath); 561 free(match); 562 if (!alwaysmatch) { 563 regfree(®ex); 564 } 565 566 return (err); 567 } 568 569 /* 570 * di_devperm_login - modify access of devices in /etc/logindevperm 571 * by changing owner/group/permissions to that of ttyn. 572 */ 573 int 574 di_devperm_login(const char *ttyn, uid_t uid, gid_t gid, 575 void (*errmsg)(char *)) 576 { 577 int err; 578 struct group grp, *grpp; 579 gid_t tty_gid; 580 char grbuf[NSS_BUFLEN_GROUP]; 581 582 if (errmsg == NULL) 583 errmsg = logerror; 584 585 if (ttyn == NULL) { 586 (*errmsg)("di_devperm_login: NULL tty device\n"); 587 return (-1); 588 } 589 590 if (getgrnam_r("tty", &grp, grbuf, NSS_BUFLEN_GROUP, &grpp) != 0) { 591 tty_gid = grpp->gr_gid; 592 } else { 593 /* 594 * this should never happen, but if it does set 595 * group to tty's traditional value. 596 */ 597 tty_gid = 7; 598 } 599 600 /* set the login console device permission */ 601 err = setdevaccess((char *)ttyn, uid, tty_gid, 602 S_IRUSR|S_IWUSR|S_IWGRP, errmsg); 603 if (err) { 604 return (err); 605 } 606 607 /* set the device permissions */ 608 return (logindevperm(ttyn, uid, gid, errmsg)); 609 } 610 611 /* 612 * di_devperm_logout - clean up access of devices in /etc/logindevperm 613 * by resetting owner/group/permissions. 614 */ 615 int 616 di_devperm_logout(const char *ttyn) 617 { 618 struct passwd *pwd; 619 uid_t root_uid; 620 gid_t root_gid; 621 622 if (ttyn == NULL) 623 return (-1); 624 625 pwd = getpwnam("root"); 626 if (pwd != NULL) { 627 root_uid = pwd->pw_uid; 628 root_gid = pwd->pw_gid; 629 } else { 630 /* 631 * this should never happen, but if it does set user 632 * and group to root's traditional values. 633 */ 634 root_uid = 0; 635 root_gid = 0; 636 } 637 638 return (logindevperm(ttyn, root_uid, root_gid, NULL)); 639 } 640 641 static void 642 logerror(char *errstring) 643 { 644 syslog(LOG_AUTH | LOG_CRIT, "%s", errstring); 645 } 646 647 648 /* 649 * Tokens are separated by ' ', '\t', ':', '=', '&', '|', ';', '\n', or '\0' 650 */ 651 static int 652 getnexttoken(char *next, char **nextp, char **tokenpp, char *tchar) 653 { 654 char *cp; 655 char *cp1; 656 char *tokenp; 657 658 cp = next; 659 while (*cp == ' ' || *cp == '\t') { 660 cp++; /* skip leading spaces */ 661 } 662 tokenp = cp; /* start of token */ 663 while (*cp != '\0' && *cp != '\n' && *cp != ' ' && *cp != '\t' && 664 *cp != ':' && *cp != '=' && *cp != '&' && 665 *cp != '|' && *cp != ';') { 666 cp++; /* point to next character */ 667 } 668 /* 669 * If terminating character is a space or tab, look ahead to see if 670 * there's another terminator that's not a space or a tab. 671 * (This code handles trailing spaces.) 672 */ 673 if (*cp == ' ' || *cp == '\t') { 674 cp1 = cp; 675 while (*++cp1 == ' ' || *cp1 == '\t') 676 ; 677 if (*cp1 == '=' || *cp1 == ':' || *cp1 == '&' || *cp1 == '|' || 678 *cp1 == ';' || *cp1 == '\n' || *cp1 == '\0') { 679 *cp = '\0'; /* terminate token */ 680 cp = cp1; 681 } 682 } 683 if (tchar != NULL) { 684 *tchar = *cp; /* save terminating character */ 685 if (*tchar == '\0') { 686 *tchar = '\n'; 687 } 688 } 689 *cp++ = '\0'; /* terminate token, point to next */ 690 *nextp = cp; /* set pointer to next character */ 691 if (cp - tokenp - 1 == 0) { 692 return (0); 693 } 694 *tokenpp = tokenp; 695 return (1); 696 } 697 698 /* 699 * get a decimal octal or hex number. Handle '~' for one's complement. 700 */ 701 static int 702 getvalue(char *token, int *valuep) 703 { 704 int radix; 705 int retval = 0; 706 int onescompl = 0; 707 int negate = 0; 708 char c; 709 710 if (*token == '~') { 711 onescompl++; /* perform one's complement on result */ 712 token++; 713 } else if (*token == '-') { 714 negate++; 715 token++; 716 } 717 if (*token == '0') { 718 token++; 719 c = *token; 720 721 if (c == '\0') { 722 *valuep = 0; /* value is 0 */ 723 return (0); 724 } 725 726 if (c == 'x' || c == 'X') { 727 radix = 16; 728 token++; 729 } else { 730 radix = 8; 731 } 732 } else 733 radix = 10; 734 735 while ((c = *token++)) { 736 switch (radix) { 737 case 8: 738 if (c >= '0' && c <= '7') { 739 c -= '0'; 740 } else { 741 /* invalid number */ 742 return (0); 743 } 744 retval = (retval << 3) + c; 745 break; 746 case 10: 747 if (c >= '0' && c <= '9') { 748 c -= '0'; 749 } else { 750 /* invalid number */ 751 return (0); 752 } 753 retval = (retval * 10) + c; 754 break; 755 case 16: 756 if (c >= 'a' && c <= 'f') { 757 c = c - 'a' + 10; 758 } else if (c >= 'A' && c <= 'F') { 759 c = c - 'A' + 10; 760 } else if (c >= '0' && c <= '9') { 761 c -= '0'; 762 } else { 763 /* invalid number */ 764 return (0); 765 } 766 retval = (retval << 4) + c; 767 break; 768 } 769 } 770 if (onescompl) { 771 retval = ~retval; 772 } 773 if (negate) { 774 retval = -retval; 775 } 776 *valuep = retval; 777 return (1); 778 } 779 780 /* 781 * Read /etc/minor_perm, return mperm list of entries 782 */ 783 struct mperm * 784 i_devfs_read_minor_perm(char *drvname, void (*errcb)(minorperm_err_t, int)) 785 { 786 FILE *pfd; 787 struct mperm *mp; 788 char line[MAX_MINOR_PERM_LINE]; 789 char *cp, *p, t; 790 struct mperm *minor_perms = NULL; 791 struct mperm *mptail = NULL; 792 struct passwd *pw; 793 struct group *gp; 794 uid_t root_uid; 795 gid_t sys_gid; 796 int ln = 0; 797 798 /* 799 * Get root/sys ids, these being the most common 800 */ 801 if ((pw = getpwnam(DEFAULT_DEV_USER)) != NULL) { 802 root_uid = pw->pw_uid; 803 } else { 804 (*errcb)(MP_CANT_FIND_USER_ERR, 0); 805 root_uid = (uid_t)0; /* assume 0 is root */ 806 } 807 if ((gp = getgrnam(DEFAULT_DEV_GROUP)) != NULL) { 808 sys_gid = gp->gr_gid; 809 } else { 810 (*errcb)(MP_CANT_FIND_GROUP_ERR, 0); 811 sys_gid = (gid_t)3; /* assume 3 is sys */ 812 } 813 814 if ((pfd = fopen(MINOR_PERM_FILE, "r")) == NULL) { 815 (*errcb)(MP_FOPEN_ERR, errno); 816 return (NULL); 817 } 818 while (fgets(line, MAX_MINOR_PERM_LINE, pfd) != NULL) { 819 ln++; 820 /* cut off comments starting with '#' */ 821 if ((cp = strchr(line, '#')) != NULL) 822 *cp = '\0'; 823 /* ignore comment or blank lines */ 824 if (is_blank(line)) 825 continue; 826 mp = (struct mperm *)calloc(1, sizeof (struct mperm)); 827 if (mp == NULL) { 828 (*errcb)(MP_ALLOC_ERR, sizeof (struct mperm)); 829 continue; 830 } 831 cp = line; 832 /* sanity-check */ 833 if (getnexttoken(cp, &cp, &p, &t) == 0) { 834 (*errcb)(MP_IGNORING_LINE_ERR, ln); 835 devfs_free_minor_perm(mp); 836 continue; 837 } 838 mp->mp_drvname = strdup(p); 839 if (mp->mp_drvname == NULL) { 840 (*errcb)(MP_ALLOC_ERR, strlen(p)+1); 841 devfs_free_minor_perm(mp); 842 continue; 843 } else if (t == '\n' || t == '\0') { 844 (*errcb)(MP_IGNORING_LINE_ERR, ln); 845 devfs_free_minor_perm(mp); 846 continue; 847 } 848 if (t == ':') { 849 if (getnexttoken(cp, &cp, &p, &t) == 0) { 850 (*errcb)(MP_IGNORING_LINE_ERR, ln); 851 devfs_free_minor_perm(mp); 852 } 853 mp->mp_minorname = strdup(p); 854 if (mp->mp_minorname == NULL) { 855 (*errcb)(MP_ALLOC_ERR, strlen(p)+1); 856 devfs_free_minor_perm(mp); 857 continue; 858 } 859 } else { 860 mp->mp_minorname = NULL; 861 } 862 863 if (t == '\n' || t == '\0') { 864 devfs_free_minor_perm(mp); 865 (*errcb)(MP_IGNORING_LINE_ERR, ln); 866 continue; 867 } 868 if (getnexttoken(cp, &cp, &p, &t) == 0) { 869 goto link; 870 } 871 if (getvalue(p, (int *)&mp->mp_mode) == 0) { 872 goto link; 873 } 874 if (t == '\n' || t == '\0') { /* no owner or group */ 875 goto link; 876 } 877 if (getnexttoken(cp, &cp, &p, &t) == 0) { 878 goto link; 879 } 880 mp->mp_owner = strdup(p); 881 if (mp->mp_owner == NULL) { 882 (*errcb)(MP_ALLOC_ERR, strlen(p)+1); 883 devfs_free_minor_perm(mp); 884 continue; 885 } else if (t == '\n' || t == '\0') { /* no group */ 886 goto link; 887 } 888 if (getnexttoken(cp, &cp, &p, 0) == 0) { 889 goto link; 890 } 891 mp->mp_group = strdup(p); 892 if (mp->mp_group == NULL) { 893 (*errcb)(MP_ALLOC_ERR, strlen(p)+1); 894 devfs_free_minor_perm(mp); 895 continue; 896 } 897 link: 898 if (drvname != NULL) { 899 /* 900 * We only want the minor perm entry for a 901 * the named driver. The driver name is the 902 * minor in the clone case. 903 */ 904 if (strcmp(mp->mp_drvname, "clone") == 0) { 905 if (mp->mp_minorname == NULL || 906 strcmp(drvname, mp->mp_minorname) != 0) { 907 devfs_free_minor_perm(mp); 908 continue; 909 } 910 } else { 911 if (strcmp(drvname, mp->mp_drvname) != 0) { 912 devfs_free_minor_perm(mp); 913 continue; 914 } 915 } 916 } 917 if (minor_perms == NULL) { 918 minor_perms = mp; 919 } else { 920 mptail->mp_next = mp; 921 } 922 mptail = mp; 923 924 /* 925 * Compute the uid's and gid's here - there are 926 * fewer lines in the /etc/minor_perm file than there 927 * are devices to be stat(2)ed. And almost every 928 * device is 'root sys'. See 1135520. 929 */ 930 if (mp->mp_owner == NULL || 931 strcmp(mp->mp_owner, DEFAULT_DEV_USER) == 0 || 932 (pw = getpwnam(mp->mp_owner)) == NULL) { 933 mp->mp_uid = root_uid; 934 } else { 935 mp->mp_uid = pw->pw_uid; 936 } 937 938 if (mp->mp_group == NULL || 939 strcmp(mp->mp_group, DEFAULT_DEV_GROUP) == 0 || 940 (gp = getgrnam(mp->mp_group)) == NULL) { 941 mp->mp_gid = sys_gid; 942 } else { 943 mp->mp_gid = gp->gr_gid; 944 } 945 } 946 947 if (fclose(pfd) == EOF) { 948 (*errcb)(MP_FCLOSE_ERR, errno); 949 } 950 951 return (minor_perms); 952 } 953 954 struct mperm * 955 devfs_read_minor_perm(void (*errcb)(minorperm_err_t, int)) 956 { 957 return (i_devfs_read_minor_perm(NULL, errcb)); 958 } 959 960 static struct mperm * 961 i_devfs_read_minor_perm_by_driver(char *drvname, 962 void (*errcb)(minorperm_err_t mp_err, int key)) 963 { 964 return (i_devfs_read_minor_perm(drvname, errcb)); 965 } 966 967 /* 968 * Free mperm list of entries 969 */ 970 void 971 devfs_free_minor_perm(struct mperm *mplist) 972 { 973 struct mperm *mp, *next; 974 975 for (mp = mplist; mp != NULL; mp = next) { 976 next = mp->mp_next; 977 978 if (mp->mp_drvname) 979 free(mp->mp_drvname); 980 if (mp->mp_minorname) 981 free(mp->mp_minorname); 982 if (mp->mp_owner) 983 free(mp->mp_owner); 984 if (mp->mp_group) 985 free(mp->mp_group); 986 free(mp); 987 } 988 } 989 990 static int 991 i_devfs_add_perm_entry(nvlist_t *nvl, struct mperm *mp) 992 { 993 int err; 994 995 err = nvlist_add_string(nvl, mp->mp_drvname, mp->mp_minorname); 996 if (err != 0) 997 return (err); 998 999 err = nvlist_add_int32(nvl, "mode", (int32_t)mp->mp_mode); 1000 if (err != 0) 1001 return (err); 1002 1003 err = nvlist_add_uint32(nvl, "uid", mp->mp_uid); 1004 if (err != 0) 1005 return (err); 1006 1007 err = nvlist_add_uint32(nvl, "gid", mp->mp_gid); 1008 return (err); 1009 } 1010 1011 static nvlist_t * 1012 i_devfs_minor_perm_nvlist(struct mperm *mplist, 1013 void (*errcb)(minorperm_err_t, int)) 1014 { 1015 int err; 1016 struct mperm *mp; 1017 nvlist_t *nvl = NULL; 1018 1019 if ((err = nvlist_alloc(&nvl, 0, 0)) != 0) { 1020 (*errcb)(MP_NVLIST_ERR, err); 1021 return (NULL); 1022 } 1023 1024 for (mp = mplist; mp != NULL; mp = mp->mp_next) { 1025 if ((err = i_devfs_add_perm_entry(nvl, mp)) != 0) { 1026 (*errcb)(MP_NVLIST_ERR, err); 1027 nvlist_free(nvl); 1028 return (NULL); 1029 } 1030 } 1031 1032 return (nvl); 1033 } 1034 1035 /* 1036 * Load all minor perm entries into the kernel 1037 * Done at boot time via devfsadm 1038 */ 1039 int 1040 devfs_load_minor_perm(struct mperm *mplist, 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, void (*errcb)(minorperm_err_t, int)) 1098 { 1099 return (i_devfs_update_minor_perm(drv, MODADDMINORPERM, errcb)); 1100 } 1101 1102 int 1103 devfs_rm_minor_perm(char *drv, 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