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) 1996, 2010, Oracle and/or its affiliates. All rights reserved. 23 * 24 */ 25 26 #include <string.h> 27 #include <strings.h> 28 #include <stdlib.h> 29 #include <unistd.h> 30 #include <limits.h> 31 #include <bsm/devices.h> 32 #include <bsm/devalloc.h> 33 34 char *strtok_r(char *, const char *, char **); 35 36 /* externs from getdaent.c */ 37 extern char *trim_white(char *); 38 extern int pack_white(char *); 39 extern char *getdadmfield(char *, char *); 40 extern int getdadmline(char *, int, FILE *); 41 42 static struct _dmapbuff { 43 FILE *_dmapf; /* for /etc/security/device_maps */ 44 devmap_t _interpdevmap; 45 char _interpdmline[DA_BUFSIZE + 1]; 46 char *_DEVMAP; 47 } *__dmapbuff; 48 49 #define dmapf (_dmap->_dmapf) 50 #define interpdevmap (_dmap->_interpdevmap) 51 #define interpdmline (_dmap->_interpdmline) 52 #define DEVMAPS_FILE (_dmap->_DEVMAP) 53 54 devmap_t *dmap_interpret(char *, devmap_t *); 55 static devmap_t *dmap_interpretf(char *, devmap_t *); 56 static devmap_t *dmap_dlexpand(devmap_t *); 57 static int dmap_resolve_link(char *devpath, char **devfs_path); 58 59 int dmap_matchdev(devmap_t *, char *); 60 int dmap_matchname(devmap_t *, char *); 61 62 63 /* 64 * _dmapalloc - 65 * allocates common buffers and structures. 66 * returns pointer to the new structure, else returns NULL on error. 67 */ 68 static struct _dmapbuff * 69 _dmapalloc(void) 70 { 71 struct _dmapbuff *_dmap = __dmapbuff; 72 73 if (_dmap == NULL) { 74 _dmap = (struct _dmapbuff *)calloc((unsigned)1, 75 (unsigned)sizeof (*__dmapbuff)); 76 if (_dmap == NULL) 77 return (NULL); 78 DEVMAPS_FILE = "/etc/security/device_maps"; 79 dmapf = NULL; 80 __dmapbuff = _dmap; 81 } 82 83 return (_dmap); 84 } 85 86 /* 87 * setdmapent - 88 * rewinds the device_maps file to the beginning. 89 */ 90 void 91 setdmapent(void) 92 { 93 struct _dmapbuff *_dmap = _dmapalloc(); 94 95 if (_dmap == NULL) 96 return; 97 if (dmapf == NULL) 98 dmapf = fopen(DEVMAPS_FILE, "rF"); 99 else 100 rewind(dmapf); 101 } 102 103 /* 104 * enddmapent - 105 * closes device_maps file. 106 */ 107 void 108 enddmapent(void) 109 { 110 struct _dmapbuff *_dmap = _dmapalloc(); 111 112 if (_dmap == NULL) 113 return; 114 if (dmapf != NULL) { 115 (void) fclose(dmapf); 116 dmapf = NULL; 117 } 118 } 119 120 void 121 freedmapent(devmap_t *dmap) 122 { 123 char **darp; 124 125 if ((darp = dmap->dmap_devarray) != NULL) { 126 while (*darp != NULL) 127 free(*darp++); 128 free(dmap->dmap_devarray); 129 dmap->dmap_devarray = NULL; 130 } 131 } 132 133 /* 134 * setdmapfile - 135 * changes the default device_maps file to the one specified. 136 * It does not close the previous file. If this is desired, enddmapent 137 * should be called prior to setdampfile. 138 */ 139 void 140 setdmapfile(char *file) 141 { 142 struct _dmapbuff *_dmap = _dmapalloc(); 143 144 if (_dmap == NULL) 145 return; 146 if (dmapf != NULL) { 147 (void) fclose(dmapf); 148 dmapf = NULL; 149 } 150 DEVMAPS_FILE = file; 151 } 152 153 /* 154 * getdmapent - 155 * When first called, returns a pointer to the first devmap_t structure 156 * in device_maps; thereafter, it returns a pointer to the next devmap_t 157 * structure in the file. Thus successive calls can be used to read the 158 * entire file. 159 * call to getdmapent should be bracketed by setdmapent and enddmapent. 160 * returns pointer to devmap_t found, else returns NULL if no entry found 161 * or on error. 162 */ 163 devmap_t * 164 getdmapent(void) 165 { 166 devmap_t *dmap; 167 struct _dmapbuff *_dmap = _dmapalloc(); 168 169 if ((_dmap == 0) || (dmapf == NULL)) 170 return (NULL); 171 172 while (getdadmline(interpdmline, (int)sizeof (interpdmline), 173 dmapf) != 0) { 174 if ((dmap = dmap_interpret(interpdmline, 175 &interpdevmap)) == NULL) 176 continue; 177 return (dmap); 178 } 179 180 return (NULL); 181 } 182 183 /* 184 * getdmapnam - 185 * searches from the beginning of device_maps for the device specified by 186 * its name. 187 * call to getdmapnam should be bracketed by setdmapent and enddmapent. 188 * returns pointer to devmapt_t for the device if it is found, else 189 * returns NULL if device not found or in case of error. 190 */ 191 devmap_t * 192 getdmapnam(char *name) 193 { 194 devmap_t *dmap; 195 struct _dmapbuff *_dmap = _dmapalloc(); 196 197 if ((name == NULL) || (_dmap == 0) || (dmapf == NULL)) 198 return (NULL); 199 200 while (getdadmline(interpdmline, (int)sizeof (interpdmline), 201 dmapf) != 0) { 202 if (strstr(interpdmline, name) == NULL) 203 continue; 204 if ((dmap = dmap_interpretf(interpdmline, 205 &interpdevmap)) == NULL) 206 continue; 207 if (dmap_matchname(dmap, name)) { 208 if ((dmap = dmap_dlexpand(dmap)) == NULL) 209 continue; 210 enddmapent(); 211 return (dmap); 212 } 213 freedmapent(dmap); 214 } 215 216 return (NULL); 217 } 218 219 /* 220 * getdmapdev - 221 * searches from the beginning of device_maps for the device specified by 222 * its logical name. 223 * call to getdmapdev should be bracketed by setdmapent and enddmapent. 224 * returns pointer to the devmap_t for the device if device is found, 225 * else returns NULL if device not found or on error. 226 */ 227 devmap_t * 228 getdmapdev(char *dev) 229 { 230 devmap_t *dmap; 231 struct _dmapbuff *_dmap = _dmapalloc(); 232 233 if ((dev == NULL) || (_dmap == 0) || (dmapf == NULL)) 234 return (NULL); 235 236 while (getdadmline(interpdmline, (int)sizeof (interpdmline), 237 dmapf) != 0) { 238 if ((dmap = dmap_interpret(interpdmline, 239 &interpdevmap)) == NULL) 240 continue; 241 if (dmap_matchdev(dmap, dev)) { 242 enddmapent(); 243 return (dmap); 244 } 245 freedmapent(dmap); 246 } 247 248 return (NULL); 249 } 250 251 /* 252 * getdmaptype - 253 * searches from the beginning of device_maps for the device specified by 254 * its type. 255 * call to getdmaptype should be bracketed by setdmapent and enddmapent. 256 * returns pointer to devmap_t found, else returns NULL if no entry found 257 * or on error. 258 */ 259 devmap_t * 260 getdmaptype(char *type) 261 { 262 devmap_t *dmap; 263 struct _dmapbuff *_dmap = _dmapalloc(); 264 265 if ((type == NULL) || (_dmap == 0) || (dmapf == NULL)) 266 return (NULL); 267 268 while (getdadmline(interpdmline, (int)sizeof (interpdmline), 269 dmapf) != 0) { 270 if ((dmap = dmap_interpretf(interpdmline, 271 &interpdevmap)) == NULL) 272 continue; 273 if (dmap->dmap_devtype != NULL && 274 strcmp(type, dmap->dmap_devtype) == 0) { 275 if ((dmap = dmap_dlexpand(dmap)) == NULL) 276 continue; 277 return (dmap); 278 } 279 freedmapent(dmap); 280 } 281 282 return (NULL); 283 } 284 285 /* 286 * dmap_match_one_dev - 287 * Checks if the specified devmap_t contains strings 288 * for the same link as the device specified. 289 * 290 * Returns 1 for a match, else returns 0. 291 */ 292 static int 293 dmap_match_one_dev(devmap_t *dmap, char *dev) 294 { 295 char **dva; 296 char *dv; 297 char *dmap_link; 298 char *dev_link; 299 char stage_link[PATH_MAX + 1]; 300 301 if (dmap->dmap_devarray == NULL) 302 return (0); 303 304 for (dva = dmap->dmap_devarray; (dv = *dva) != NULL; dva++) { 305 if (strstr(dev, dv) != NULL) 306 return (1); 307 } 308 /* check if both refer to same physical device */ 309 (void) strncpy(stage_link, dmap->dmap_devarray[0], sizeof (stage_link)); 310 if (dmap_resolve_link(stage_link, &dmap_link) != 0) 311 return (0); 312 (void) strncpy(stage_link, dev, sizeof (stage_link)); 313 if (dmap_resolve_link(stage_link, &dev_link) != 0) { 314 free(dmap_link); 315 return (0); 316 } 317 if (strcmp(dev_link, dmap_link) == 0) { 318 free(dmap_link); 319 free(dev_link); 320 return (1); 321 } 322 free(dmap_link); 323 free(dev_link); 324 return (0); 325 } 326 327 /* 328 * dmap_matchdev - 329 * checks if the specified devmap_t is for the device specified. 330 * returns 1 if it is, else returns 0. 331 */ 332 int 333 dmap_matchdev(devmap_t *dmap, char *dev) 334 { 335 char **dva; 336 char *dv; 337 338 if (dmap->dmap_devarray == NULL) 339 return (0); 340 for (dva = dmap->dmap_devarray; (dv = *dva) != NULL; dva ++) { 341 if (strcmp(dv, dev) == 0) 342 return (1); 343 } 344 345 return (0); 346 } 347 348 /* 349 * Requires a match of the /dev/? links, not just the logical devname 350 * Returns 1 for match found, 0 for match not found, 2 for invalid arguments. 351 * 352 * Also looks for an instance number at the end of the logical name, and 353 * puts instance or -1 into *num. 354 */ 355 int 356 dmap_exact_dev(devmap_t *dmap, char *dev, int *num) 357 { 358 char *dv; 359 360 if ((dev == NULL) || (dmap->dmap_devname == NULL)) 361 return (2); 362 dv = dmap->dmap_devname; 363 dv += strcspn(dmap->dmap_devname, "0123456789"); 364 if (sscanf(dv, "%d", num) != 1) 365 *num = -1; 366 /* during some add processes, dev can be shorter than dmap */ 367 return (dmap_match_one_dev(dmap, dev)); 368 } 369 370 /* 371 * dmap_matchtype - 372 * checks if the specified devmap_t is for the device specified. 373 * returns 1 if it is, else returns 0. 374 */ 375 int 376 dmap_matchtype(devmap_t *dmap, char *type) 377 { 378 if ((dmap->dmap_devtype == NULL) || (type == NULL)) 379 return (0); 380 381 return ((strcmp(dmap->dmap_devtype, type) == 0)); 382 } 383 384 /* 385 * dmap_matchname - 386 * checks if the specified devmap_t is for the device specified. 387 * returns 1 if it is, else returns 0. 388 */ 389 int 390 dmap_matchname(devmap_t *dmap, char *name) 391 { 392 if (dmap->dmap_devname == NULL) 393 return (0); 394 395 return ((strcmp(dmap->dmap_devname, name) == 0)); 396 } 397 398 /* 399 * Temporarily duplicated directly from libdevinfo's is_minor_node(), 400 * and renamed, because we cannot link this from libdevinfo. 401 * 402 * To be resolved in a couple of builds. 403 * 404 * The real fix is to move device allocation out of libbsm. 405 * 406 * returns 1 if contents is a minor node in /devices. 407 * If mn_root is not NULL, mn_root is set to: 408 * if contents is a /dev node, mn_root = contents 409 * OR 410 * if contents is a /devices node, mn_root set to the '/' 411 * following /devices. 412 */ 413 static int 414 dmap_minor_root(const char *contents, const char **mn_root) 415 { 416 char *ptr, *prefix; 417 418 prefix = "../devices/"; 419 420 if ((ptr = strstr(contents, prefix)) != NULL) { 421 422 /* mn_root should point to the / following /devices */ 423 if (mn_root != NULL) { 424 *mn_root = ptr += strlen(prefix) - 1; 425 } 426 return (1); 427 } 428 429 prefix = "/devices/"; 430 431 if (strncmp(contents, prefix, strlen(prefix)) == 0) { 432 433 /* mn_root should point to the / following /devices/ */ 434 if (mn_root != NULL) { 435 *mn_root = contents + strlen(prefix) - 1; 436 } 437 return (1); 438 } 439 440 if (mn_root != NULL) { 441 *mn_root = contents; 442 } 443 return (0); 444 } 445 446 /* 447 * Temporarily duplicated directly from libdevinfo's devfs_resolve_link(), 448 * and renamed, because we cannot link this from libdevinfo now, and will 449 * create a sensible solution in the near future. 450 * 451 * returns 0 if resolved, -1 otherwise. 452 * devpath: Absolute path to /dev link 453 * devfs_path: Returns malloced string: /devices path w/out "/devices" 454 */ 455 static int 456 dmap_resolve_link(char *devpath, char **devfs_path) 457 { 458 char contents[PATH_MAX + 1]; 459 char stage_link[PATH_MAX + 1]; 460 char *ptr; 461 int linksize; 462 char *slashdev = "/dev/"; 463 464 if (devfs_path) { 465 *devfs_path = NULL; 466 } 467 468 linksize = readlink(devpath, contents, PATH_MAX); 469 470 if (linksize <= 0) { 471 return (-1); 472 } else { 473 contents[linksize] = '\0'; 474 } 475 476 /* 477 * if the link contents is not a minor node assume 478 * that link contents is really a pointer to another 479 * link, and if so recurse and read its link contents. 480 */ 481 if (dmap_minor_root((const char *)contents, (const char **)&ptr) != 482 1) { 483 if (strncmp(contents, slashdev, strlen(slashdev)) == 0) { 484 /* absolute path, starting with /dev */ 485 (void) strcpy(stage_link, contents); 486 } else { 487 /* relative path, prefix devpath */ 488 if ((ptr = strrchr(devpath, '/')) == NULL) { 489 /* invalid link */ 490 return (-1); 491 } 492 *ptr = '\0'; 493 (void) strcpy(stage_link, devpath); 494 *ptr = '/'; 495 (void) strcat(stage_link, "/"); 496 (void) strcat(stage_link, contents); 497 498 } 499 return (dmap_resolve_link(stage_link, devfs_path)); 500 } 501 502 if (devfs_path) { 503 *devfs_path = strdup(ptr); 504 if (*devfs_path == NULL) { 505 return (-1); 506 } 507 } 508 509 return (0); 510 } 511 512 /* 513 * dmap_physname: path to /devices device 514 * Returns: 515 * strdup'd (i.e. malloc'd) real device file if successful 516 * NULL on error 517 */ 518 char * 519 dmap_physname(devmap_t *dmap) 520 { 521 char *oldlink; 522 char stage_link[PATH_MAX + 1]; 523 524 if ((dmap == NULL) || (dmap->dmap_devarray == NULL) || 525 (dmap->dmap_devarray[0] == NULL)) 526 return (NULL); 527 528 (void) strncpy(stage_link, dmap->dmap_devarray[0], sizeof (stage_link)); 529 530 if (dmap_resolve_link(stage_link, &oldlink) == 0) 531 return (oldlink); 532 return (NULL); 533 } 534 535 /* 536 * dm_match - 537 * calls dmap_matchname or dmap_matchtype as appropriate. 538 */ 539 int 540 dm_match(devmap_t *dmap, da_args *dargs) 541 { 542 if (dargs->devinfo->devname) 543 return (dmap_matchname(dmap, dargs->devinfo->devname)); 544 else if (dargs->devinfo->devtype) 545 return (dmap_matchtype(dmap, dargs->devinfo->devtype)); 546 547 return (0); 548 } 549 550 /* 551 * dmap_interpret - 552 * calls dmap_interpretf and dmap_dlexpand to parse devmap_t line. 553 * returns pointer to parsed devmapt_t entry, else returns NULL on error. 554 */ 555 devmap_t * 556 dmap_interpret(char *val, devmap_t *dm) 557 { 558 if (dmap_interpretf(val, dm) == NULL) 559 return (NULL); 560 561 return (dmap_dlexpand(dm)); 562 } 563 564 /* 565 * dmap_interpretf - 566 * parses string "val" and initializes pointers in the given devmap_t to 567 * fields in "val". 568 * returns pointer to updated devmap_t. 569 */ 570 static devmap_t * 571 dmap_interpretf(char *val, devmap_t *dm) 572 { 573 dm->dmap_devname = getdadmfield(val, KV_TOKEN_DELIMIT); 574 dm->dmap_devtype = getdadmfield(NULL, KV_TOKEN_DELIMIT); 575 dm->dmap_devlist = getdadmfield(NULL, KV_TOKEN_DELIMIT); 576 dm->dmap_devarray = NULL; 577 if (dm->dmap_devname == NULL || 578 dm->dmap_devtype == NULL || 579 dm->dmap_devlist == NULL) 580 return (NULL); 581 582 return (dm); 583 } 584 585 /* 586 * dmap_dlexpand - 587 * expands dmap_devlist of the form `devlist_generate` 588 * returns unexpanded form if there is no '\`' or in case of error. 589 */ 590 static devmap_t * 591 dmap_dlexpand(devmap_t *dmp) 592 { 593 char tmplist[DA_BUFSIZE + 1]; 594 char *cp, *cpl, **darp; 595 int count; 596 FILE *expansion; 597 598 dmp->dmap_devarray = NULL; 599 if (dmp->dmap_devlist == NULL) 600 return (NULL); 601 if (*(dmp->dmap_devlist) != '`') { 602 (void) strcpy(tmplist, dmp->dmap_devlist); 603 } else { 604 (void) strcpy(tmplist, dmp->dmap_devlist + 1); 605 if ((cp = strchr(tmplist, '`')) != NULL) 606 *cp = '\0'; 607 if ((expansion = popen(tmplist, "rF")) == NULL) 608 return (NULL); 609 count = fread(tmplist, 1, sizeof (tmplist) - 1, expansion); 610 (void) pclose(expansion); 611 tmplist[count] = '\0'; 612 } 613 614 /* cleanup the list */ 615 count = pack_white(tmplist); 616 dmp->dmap_devarray = darp = 617 (char **)malloc((count + 2) * sizeof (char *)); 618 if (darp == NULL) 619 return (NULL); 620 cp = tmplist; 621 while ((cp = strtok_r(cp, " ", &cpl)) != NULL) { 622 *darp = strdup(cp); 623 if (*darp == NULL) { 624 freedmapent(dmp); 625 return (NULL); 626 } 627 darp++; 628 cp = NULL; 629 } 630 *darp = NULL; 631 632 return (dmp); 633 } 634 635 /* 636 * dmapskip - 637 * scans input string to find next colon or end of line. 638 * returns pointer to next char. 639 */ 640 static char * 641 dmapskip(char *p) 642 { 643 while (*p && *p != ':' && *p != '\n') 644 ++p; 645 if (*p == '\n') 646 *p = '\0'; 647 else if (*p != '\0') 648 *p++ = '\0'; 649 650 return (p); 651 } 652 653 /* 654 * dmapdskip - 655 * scans input string to find next space or end of line. 656 * returns pointer to next char. 657 */ 658 static char * 659 dmapdskip(p) 660 register char *p; 661 { 662 while (*p && *p != ' ' && *p != '\n') 663 ++p; 664 if (*p != '\0') 665 *p++ = '\0'; 666 667 return (p); 668 } 669 670 char * 671 getdmapfield(char *ptr) 672 { 673 static char *tptr; 674 675 if (ptr == NULL) 676 ptr = tptr; 677 if (ptr == NULL) 678 return (NULL); 679 tptr = dmapskip(ptr); 680 ptr = trim_white(ptr); 681 if (ptr == NULL) 682 return (NULL); 683 if (*ptr == NULL) 684 return (NULL); 685 686 return (ptr); 687 } 688 689 char * 690 getdmapdfield(char *ptr) 691 { 692 static char *tptr; 693 if (ptr != NULL) { 694 ptr = trim_white(ptr); 695 } else { 696 ptr = tptr; 697 } 698 if (ptr == NULL) 699 return (NULL); 700 tptr = dmapdskip(ptr); 701 if (ptr == NULL) 702 return (NULL); 703 if (*ptr == NULL) 704 return (NULL); 705 706 return (ptr); 707 } 708