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