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 34char *strtok_r(char *, const char *, char **); 35 36/* externs from getdaent.c */ 37extern char *trim_white(char *); 38extern int pack_white(char *); 39extern char *getdadmfield(char *, char *); 40extern int getdadmline(char *, int, FILE *); 41 42static 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 54devmap_t *dmap_interpret(char *, devmap_t *); 55static devmap_t *dmap_interpretf(char *, devmap_t *); 56static devmap_t *dmap_dlexpand(devmap_t *); 57static int dmap_resolve_link(char *devpath, char **devfs_path); 58 59int dmap_matchdev(devmap_t *, char *); 60int 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 */ 68static 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 */ 90void 91setdmapent(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 */ 107void 108enddmapent(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 120void 121freedmapent(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 */ 139void 140setdmapfile(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 */ 163devmap_t * 164getdmapent(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 */ 191devmap_t * 192getdmapnam(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 */ 227devmap_t * 228getdmapdev(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 */ 259devmap_t * 260getdmaptype(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 */ 292static int 293dmap_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 */ 332int 333dmap_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 */ 355int 356dmap_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 */ 375int 376dmap_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 */ 389int 390dmap_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 */ 413static int 414dmap_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 */ 455static int 456dmap_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 */ 518char * 519dmap_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 */ 539int 540dm_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 */ 555devmap_t * 556dmap_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 */ 570static devmap_t * 571dmap_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 */ 590static devmap_t * 591dmap_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 */ 640static char * 641dmapskip(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 */ 658static char * 659dmapdskip(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 670char * 671getdmapfield(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 689char * 690getdmapdfield(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