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 /* 23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * Copyright (c) 1988 AT&T 29 * All Rights Reserved 30 */ 31 32 #pragma ident "%Z%%M% %I% %E% SMI" 33 34 /* 35 * PATH setup and search directory functions. 36 */ 37 38 #include <stdio.h> 39 #include <limits.h> 40 #include <fcntl.h> 41 #include <string.h> 42 #include <sys/systeminfo.h> 43 #include <debug.h> 44 #include <conv.h> 45 #include "_rtld.h" 46 #include "msg.h" 47 48 /* 49 * Given a search rule type, return a list of directories to search according 50 * to the specified rule. 51 */ 52 static Pnode * 53 get_dir_list(uchar_t rules, Rt_map *lmp, uint_t flags) 54 { 55 Pnode * dirlist = (Pnode *)0; 56 Lm_list * lml = LIST(lmp); 57 int search; 58 59 /* 60 * Determine whether ldd -s is in effect - ignore when we're searching 61 * for audit libraries as these will be added to their own link-map. 62 */ 63 if ((lml->lm_flags & LML_FLG_TRC_SEARCH) && 64 ((FLAGS1(lmp) & FL1_RT_LDDSTUB) == 0) && 65 ((flags & FLG_RT_AUDIT) == 0)) 66 search = 1; 67 else 68 search = 0; 69 70 switch (rules) { 71 case RPLENV: 72 /* 73 * Initialize the replaceable environment variable 74 * (LD_LIBRARY_PATH) search path list. Note, we always call 75 * Dbg_libs_path() so that every library lookup diagnostic can 76 * be preceded with the appropriate search path information. 77 */ 78 if (rpl_libpath) { 79 uint_t mode = (LA_SER_LIBPATH | PN_FLG_UNIQUE); 80 81 /* 82 * Note, this path may have originated from the users 83 * environment or from a configuration file. 84 */ 85 if (env_info & ENV_INF_PATHCFG) 86 mode |= LA_SER_CONFIG; 87 88 DBG_CALL(Dbg_libs_path(lml, rpl_libpath, mode, 89 config->c_name)); 90 91 /* 92 * For ldd(1) -s, indicate the search paths that'll 93 * be used. If this is a secure program then some 94 * search paths may be ignored, therefore reset the 95 * rpl_libdirs pointer each time so that the 96 * diagnostics related to these unsecure directories 97 * will be output for each image loaded. 98 */ 99 if (search) { 100 const char *fmt; 101 102 if (env_info & ENV_INF_PATHCFG) 103 fmt = MSG_INTL(MSG_LDD_PTH_LIBPATHC); 104 else 105 fmt = MSG_INTL(MSG_LDD_PTH_LIBPATH); 106 107 (void) printf(fmt, rpl_libpath, config->c_name); 108 } 109 if (rpl_libdirs && (rtld_flags & RT_FL_SECURE) && 110 (search || DBG_ENABLED)) { 111 free(rpl_libdirs); 112 rpl_libdirs = 0; 113 } 114 if (!rpl_libdirs) { 115 /* 116 * If this is a secure application we need to 117 * be selective over what directories we use. 118 */ 119 rpl_libdirs = expand_paths(lmp, rpl_libpath, 120 mode, PN_TKN_HWCAP); 121 } 122 dirlist = rpl_libdirs; 123 } 124 break; 125 case PRMENV: 126 /* 127 * Initialize the permanent (LD_LIBRARY_PATH) search path list. 128 * This can only originate from a configuration file. To be 129 * consistent with the debugging display of DEFENV (above), 130 * always call Dbg_libs_path(). 131 */ 132 if (prm_libpath) { 133 uint_t mode = 134 (LA_SER_LIBPATH | LA_SER_CONFIG | PN_FLG_UNIQUE); 135 136 DBG_CALL(Dbg_libs_path(lml, prm_libpath, mode, 137 config->c_name)); 138 139 /* 140 * For ldd(1) -s, indicate the search paths that'll 141 * be used. If this is a secure program then some 142 * search paths may be ignored, therefore reset the 143 * prm_libdirs pointer each time so that the 144 * diagnostics related to these unsecure directories 145 * will be output for each image loaded. 146 */ 147 if (search) 148 (void) printf(MSG_INTL(MSG_LDD_PTH_LIBPATHC), 149 prm_libpath, config->c_name); 150 if (prm_libdirs && (rtld_flags & RT_FL_SECURE) && 151 (search || DBG_ENABLED)) { 152 free(prm_libdirs); 153 prm_libdirs = 0; 154 } 155 if (!prm_libdirs) { 156 /* 157 * If this is a secure application we need to 158 * be selective over what directories we use. 159 */ 160 prm_libdirs = expand_paths(lmp, prm_libpath, 161 mode, PN_TKN_HWCAP); 162 } 163 dirlist = prm_libdirs; 164 } 165 break; 166 case RUNPATH: 167 /* 168 * Initialize the runpath search path list. To be consistent 169 * with the debugging display of DEFENV (above), always call 170 * Dbg_libs_path(). 171 */ 172 if (RPATH(lmp)) { 173 DBG_CALL(Dbg_libs_path(lml, RPATH(lmp), LA_SER_RUNPATH, 174 NAME(lmp))); 175 176 /* 177 * For ldd(1) -s, indicate the search paths that'll 178 * be used. If this is a secure program then some 179 * search paths may be ignored, therefore reset the 180 * runlist pointer each time so that the diagnostics 181 * related to these unsecure directories will be 182 * output for each image loaded. 183 */ 184 if (search) 185 (void) printf(MSG_INTL(MSG_LDD_PTH_RUNPATH), 186 RPATH(lmp), NAME(lmp)); 187 if (RLIST(lmp) && (rtld_flags & RT_FL_SECURE) && 188 (search || DBG_ENABLED)) { 189 free(RLIST(lmp)); 190 RLIST(lmp) = 0; 191 } 192 if (!(RLIST(lmp))) 193 /* 194 * If this is a secure application we need to 195 * be selective over what directories we use. 196 */ 197 RLIST(lmp) = expand_paths(lmp, RPATH(lmp), 198 LA_SER_RUNPATH, PN_TKN_HWCAP); 199 dirlist = RLIST(lmp); 200 } 201 break; 202 case DEFAULT: 203 if ((FLAGS1(lmp) & FL1_RT_NODEFLIB) == 0) { 204 if ((rtld_flags & RT_FL_SECURE) && 205 (flags & (FLG_RT_PRELOAD | FLG_RT_AUDIT))) 206 dirlist = LM_SECURE_DIRS(lmp); 207 else 208 dirlist = LM_DFLT_DIRS(lmp); 209 } 210 211 /* 212 * For ldd(1) -s, indicate the default paths that'll be used. 213 */ 214 if (dirlist && (search || DBG_ENABLED)) { 215 Pnode * pnp = dirlist; 216 int num = 0; 217 218 if (search) 219 (void) printf(MSG_INTL(MSG_LDD_PTH_BGNDFL)); 220 for (; pnp && pnp->p_name; pnp = pnp->p_next, num++) { 221 if (search) { 222 const char *fmt; 223 224 if (num) { 225 fmt = 226 MSG_ORIG(MSG_LDD_FMT_PATHN); 227 } else { 228 fmt = 229 MSG_ORIG(MSG_LDD_FMT_PATH1); 230 } 231 (void) printf(fmt, pnp->p_name); 232 } else 233 DBG_CALL(Dbg_libs_path(lml, pnp->p_name, 234 pnp->p_orig, config->c_name)); 235 } 236 /* BEGIN CSTYLED */ 237 if (search) { 238 if (dirlist->p_orig & LA_SER_CONFIG) 239 (void) printf( 240 MSG_INTL(MSG_LDD_PTH_ENDDFLC), 241 config->c_name); 242 else 243 (void) printf( 244 MSG_INTL(MSG_LDD_PTH_ENDDFL)); 245 } 246 /* END CSTYLED */ 247 } 248 break; 249 default: 250 break; 251 } 252 return (dirlist); 253 } 254 255 /* 256 * Get the next dir in the search rules path. 257 */ 258 Pnode * 259 get_next_dir(Pnode ** dirlist, Rt_map * lmp, uint_t flags) 260 { 261 static unsigned char *rules = NULL; 262 263 /* 264 * Search rules consist of one or more directories names. If this is a 265 * new search, then start at the beginning of the search rules. 266 * Otherwise traverse the list of directories that make up the rule. 267 */ 268 if (!*dirlist) { 269 rules = search_rules; 270 } else { 271 if ((*dirlist = (*dirlist)->p_next) != 0) 272 return (*dirlist); 273 else 274 rules++; 275 } 276 277 while (*rules) { 278 if ((*dirlist = get_dir_list(*rules, lmp, flags)) != 0) 279 return (*dirlist); 280 else 281 rules++; 282 } 283 284 /* 285 * If we got here, no more directories to search, return NULL. 286 */ 287 return (NULL); 288 } 289 290 /* 291 * Process a directory (runpath) or filename (needed or filter) string looking 292 * for tokens to expand. Allocate a new buffer for the string. 293 */ 294 uint_t 295 expand(char **name, size_t *len, char **list, uint_t orig, uint_t omit, 296 Rt_map *lmp) 297 { 298 char _name[PATH_MAX]; 299 char *token = 0, *oname, *optr, *_optr, *nptr, * _list; 300 size_t olen = 0, nlen = 0, _len; 301 int isaflag = 0; 302 uint_t flags = 0; 303 Lm_list *lml = LIST(lmp); 304 305 optr = _optr = oname = *name; 306 nptr = _name; 307 308 while ((olen < *len) && (nlen < PATH_MAX)) { 309 uint_t _flags; 310 311 if ((*optr != '$') || ((olen - *len) == 1)) { 312 /* 313 * When expanding paths while a configuration file 314 * exists that contains directory information, determine 315 * whether the path contains "./". If so, we'll resolve 316 * the path later to remove these relative entries. 317 */ 318 if ((rtld_flags & RT_FL_DIRCFG) && 319 (orig & LA_SER_MASK) && (*optr == '/') && 320 (optr != oname) && (*(optr - 1) == '.')) 321 flags |= TKN_DOTSLASH; 322 323 olen++, optr++; 324 continue; 325 } 326 327 /* 328 * Copy any string we've presently passed over to the new 329 * buffer. 330 */ 331 if ((_len = (optr - _optr)) != 0) { 332 if ((nlen += _len) < PATH_MAX) { 333 (void) strncpy(nptr, _optr, _len); 334 nptr = nptr + _len; 335 } else { 336 eprintf(lml, ERR_FATAL, 337 MSG_INTL(MSG_ERR_EXPAND1), NAME(lmp), 338 oname); 339 return (0); 340 } 341 } 342 343 /* 344 * Skip the token delimiter and determine if a reserved token 345 * match is found. 346 */ 347 olen++, optr++; 348 _flags = 0; 349 token = 0; 350 351 if (strncmp(optr, MSG_ORIG(MSG_TKN_ORIGIN), 352 MSG_TKN_ORIGIN_SIZE) == 0) { 353 token = (char *)MSG_ORIG(MSG_TKN_ORIGIN); 354 355 /* 356 * $ORIGIN expansion is required. Determine this 357 * objects basename. Expansion of $ORIGIN is allowed 358 * for secure applications but must be checked by the 359 * caller to insure the expanded path matches a 360 * registered secure name. 361 */ 362 if (((omit & PN_TKN_ORIGIN) == 0) && 363 (((_len = DIRSZ(lmp)) != 0) || 364 ((_len = fullpath(lmp, 0)) != 0))) { 365 if ((nlen += _len) < PATH_MAX) { 366 (void) strncpy(nptr, 367 ORIGNAME(lmp), _len); 368 nptr = nptr +_len; 369 olen += MSG_TKN_ORIGIN_SIZE; 370 optr += MSG_TKN_ORIGIN_SIZE; 371 _flags |= PN_TKN_ORIGIN; 372 } else { 373 eprintf(lml, ERR_FATAL, 374 MSG_INTL(MSG_ERR_EXPAND1), 375 NAME(lmp), oname); 376 return (0); 377 } 378 } 379 380 } else if (strncmp(optr, MSG_ORIG(MSG_TKN_PLATFORM), 381 MSG_TKN_PLATFORM_SIZE) == 0) { 382 token = (char *)MSG_ORIG(MSG_TKN_PLATFORM); 383 384 /* 385 * $PLATFORM expansion required. This would have been 386 * established from the AT_SUN_PLATFORM aux vector, but 387 * if not attempt to get it from sysconf(). 388 */ 389 if (((omit & PN_TKN_PLATFORM) == 0) && 390 ((platform == 0) && (platform_sz == 0))) { 391 char _info[SYS_NMLN]; 392 long _size; 393 394 _size = sysinfo(SI_PLATFORM, _info, SYS_NMLN); 395 if ((_size != -1) && 396 ((platform = malloc((size_t)_size)) != 0)) { 397 (void) strcpy(platform, _info); 398 platform_sz = (size_t)_size - 1; 399 } 400 } 401 if (((omit & PN_TKN_PLATFORM) == 0) && 402 (platform != 0)) { 403 if ((nlen += platform_sz) < PATH_MAX) { 404 (void) strncpy(nptr, platform, 405 platform_sz); 406 nptr = nptr + platform_sz; 407 olen += MSG_TKN_PLATFORM_SIZE; 408 optr += MSG_TKN_PLATFORM_SIZE; 409 _flags |= PN_TKN_PLATFORM; 410 } else { 411 eprintf(lml, ERR_FATAL, 412 MSG_INTL(MSG_ERR_EXPAND1), 413 NAME(lmp), oname); 414 return (0); 415 } 416 } 417 418 } else if (strncmp(optr, MSG_ORIG(MSG_TKN_OSNAME), 419 MSG_TKN_OSNAME_SIZE) == 0) { 420 token = (char *)MSG_ORIG(MSG_TKN_OSNAME); 421 422 /* 423 * $OSNAME expansion required. This is established 424 * from the sysname[] returned by uname(2). 425 */ 426 if (((omit & PN_TKN_OSNAME) == 0) && (uts == 0)) 427 uts = conv_uts(); 428 429 if (((omit & PN_TKN_OSNAME) == 0) && 430 (uts && uts->uts_osnamesz)) { 431 if ((nlen += uts->uts_osnamesz) < PATH_MAX) { 432 (void) strncpy(nptr, uts->uts_osname, 433 uts->uts_osnamesz); 434 nptr = nptr + uts->uts_osnamesz; 435 olen += MSG_TKN_OSNAME_SIZE; 436 optr += MSG_TKN_OSNAME_SIZE; 437 _flags |= PN_TKN_OSNAME; 438 } else { 439 eprintf(lml, ERR_FATAL, 440 MSG_INTL(MSG_ERR_EXPAND1), 441 NAME(lmp), oname); 442 return (0); 443 } 444 } 445 446 } else if (strncmp(optr, MSG_ORIG(MSG_TKN_OSREL), 447 MSG_TKN_OSREL_SIZE) == 0) { 448 token = (char *)MSG_ORIG(MSG_TKN_OSREL); 449 450 /* 451 * $OSREL expansion required. This is established 452 * from the release[] returned by uname(2). 453 */ 454 if (((omit & PN_TKN_OSREL) == 0) && (uts == 0)) 455 uts = conv_uts(); 456 457 if (((omit & PN_TKN_OSREL) == 0) && 458 (uts && uts->uts_osrelsz)) { 459 if ((nlen += uts->uts_osrelsz) < PATH_MAX) { 460 (void) strncpy(nptr, uts->uts_osrel, 461 uts->uts_osrelsz); 462 nptr = nptr + uts->uts_osrelsz; 463 olen += MSG_TKN_OSREL_SIZE; 464 optr += MSG_TKN_OSREL_SIZE; 465 _flags |= PN_TKN_OSREL; 466 } else { 467 eprintf(lml, ERR_FATAL, 468 MSG_INTL(MSG_ERR_EXPAND1), 469 NAME(lmp), oname); 470 return (0); 471 } 472 } 473 474 } else if ((strncmp(optr, MSG_ORIG(MSG_TKN_ISALIST), 475 MSG_TKN_ISALIST_SIZE) == 0)) { 476 int ok; 477 token = (char *)MSG_ORIG(MSG_TKN_ISALIST); 478 479 /* 480 * $ISALIST expansion required. When accompanied with 481 * a list pointer, this routine updates that pointer 482 * with the new list of potential candidates. Without 483 * this list pointer, only the first expansion is 484 * provided. NOTE, that two $ISLIST expansions within 485 * the same path aren't supported. 486 */ 487 if ((omit & PN_TKN_ISALIST) || isaflag++) 488 ok = 0; 489 else 490 ok = 1; 491 492 if (ok && (isa == 0)) 493 isa = conv_isalist(); 494 495 if (ok && isa && isa->isa_listsz) { 496 size_t no, mlen, tlen, hlen = olen - 1; 497 char *lptr; 498 Isa_opt *opt = isa->isa_opt; 499 500 if ((nlen += opt->isa_namesz) < PATH_MAX) { 501 (void) strncpy(nptr, opt->isa_name, 502 opt->isa_namesz); 503 nptr = nptr + opt->isa_namesz; 504 olen += MSG_TKN_ISALIST_SIZE; 505 optr += MSG_TKN_ISALIST_SIZE; 506 _flags |= PN_TKN_ISALIST; 507 } else { 508 eprintf(lml, ERR_FATAL, 509 MSG_INTL(MSG_ERR_EXPAND1), 510 NAME(lmp), oname); 511 return (0); 512 } 513 514 if (list) { 515 tlen = *len - olen; 516 mlen = ((hlen + tlen) * 517 (isa->isa_optno - 1)) + 518 isa->isa_listsz - opt->isa_namesz + 519 strlen(*list); 520 if ((_list = lptr = malloc(mlen)) == 0) 521 return (0); 522 523 for (no = 1, opt++; no < isa->isa_optno; 524 no++, opt++) { 525 (void) strncpy(lptr, *name, 526 hlen); 527 lptr = lptr + hlen; 528 (void) strncpy(lptr, 529 opt->isa_name, 530 opt->isa_namesz); 531 lptr = lptr + opt->isa_namesz; 532 (void) strncpy(lptr, optr, 533 tlen); 534 lptr = lptr + tlen; 535 *lptr++ = ':'; 536 } 537 if (**list) 538 (void) strcpy(lptr, *list); 539 else 540 *--lptr = '\0'; 541 } 542 } 543 544 } else if (strncmp(optr, MSG_ORIG(MSG_TKN_HWCAP), 545 MSG_TKN_HWCAP_SIZE) == 0) { 546 char *bptr = nptr - 1; 547 char *eptr = optr + MSG_TKN_HWCAP_SIZE; 548 token = (char *)MSG_ORIG(MSG_TKN_HWCAP); 549 550 /* 551 * $HWCAP expansion required. For compatibility with 552 * older environments, only expand this token when hard- 553 * ware capability information is available. This 554 * expansion is only allowed for non-simple pathnames 555 * (must contain a '/'), with the token itself being the 556 * last element of the path. Therefore, all we need do 557 * is test the existence of the string "/$HWCAP\0". 558 */ 559 if (((omit & PN_TKN_HWCAP) == 0) && 560 (rtld_flags2 & RT_FL2_HWCAP) && 561 ((bptr > _name) && (*bptr == '/') && 562 ((*eptr == '\0') || (*eptr == ':')))) { 563 /* 564 * Decrement the present pointer so that the 565 * directories trailing "/" gets nuked later. 566 */ 567 nptr--, nlen--; 568 olen += MSG_TKN_HWCAP_SIZE; 569 optr += MSG_TKN_HWCAP_SIZE; 570 _flags |= PN_TKN_HWCAP; 571 } 572 573 } else { 574 /* 575 * If reserved token was not found, copy the 576 * character. 577 */ 578 *nptr++ = '$'; 579 nlen++; 580 } 581 582 /* 583 * If reserved token was found, and could not be expanded, 584 * diagnose the error condition. 585 */ 586 if (token) { 587 if (_flags) 588 flags |= _flags; 589 else { 590 char buf[PATH_MAX], *str; 591 592 /* 593 * Note, the original string we're expanding 594 * might contain a number of ':' separated 595 * paths. Isolate the path we're processing to 596 * provide a more precise error diagnostic. 597 */ 598 if (str = strchr(oname, ':')) { 599 size_t slen = str - oname; 600 601 (void) strncpy(buf, oname, slen); 602 buf[slen] = '\0'; 603 str = buf; 604 } else 605 str = oname; 606 607 eprintf(lml, ERR_FATAL, 608 MSG_INTL(MSG_ERR_EXPAND2), NAME(lmp), 609 str, token); 610 return (0); 611 } 612 } 613 _optr = optr; 614 } 615 616 /* 617 * First make sure the current length is shorter than PATH_MAX. We may 618 * arrive here if the given path contains '$' characters which are not 619 * the lead of a reserved token. 620 */ 621 if (nlen >= PATH_MAX) { 622 eprintf(lml, ERR_FATAL, MSG_INTL(MSG_ERR_EXPAND1), NAME(lmp), 623 oname); 624 return (0); 625 } 626 627 /* 628 * If any ISALIST processing has occurred not only do we return the 629 * expanded node we're presently working on, but we can also update the 630 * remaining list so that it is effectively prepended with this node 631 * expanded to all remaining ISALIST options. Note that we can only 632 * handle one ISALIST per node. For more than one ISALIST to be 633 * processed we'd need a better algorithm than above to replace the 634 * newly generated list. Whether we want to encourage the number of 635 * pathname permutations this would provide is another question. So, for 636 * now if more than one ISALIST is encountered we return the original 637 * node untouched. 638 */ 639 if (isa && isaflag) { 640 if (isaflag == 1) { 641 if (list) 642 *list = _list; 643 } else { 644 flags &= ~PN_TKN_ISALIST; 645 646 if ((nptr = calloc(1, (*len + 1))) == 0) 647 return (0); 648 (void) strncpy(nptr, *name, *len); 649 *name = nptr; 650 651 return (TKN_NONE); 652 } 653 } 654 655 /* 656 * Copy any remaining string. Terminate the new string with a null as 657 * this string can be displayed via debugging diagnostics. 658 */ 659 if ((_len = (optr - _optr)) != 0) { 660 if ((nlen += _len) < PATH_MAX) { 661 (void) strncpy(nptr, _optr, _len); 662 nptr = nptr + _len; 663 } else { 664 eprintf(lml, ERR_FATAL, MSG_INTL(MSG_ERR_EXPAND1), 665 NAME(lmp), oname); 666 return (0); 667 } 668 } 669 *nptr = '\0'; 670 671 /* 672 * A path that has been expanded, is typically used to create full 673 * pathnames for objects that will be opened. The final pathname is 674 * resolved to simplify it, and set the stage for possible $ORIGIN 675 * processing. Therefore, it's usually unnecessary to resolve the path 676 * at this point. However, if a configuration file, containing 677 * directory information is in use, then we might need to lookup this 678 * path in the configuration file. To keep the number of pathname 679 * resolutions to a minimum, only resolve paths that contain "./". The 680 * use of "$ORIGIN/../lib" will probably only match a configuration file 681 * entry after resolution. 682 */ 683 if (list && ((rtld_flags & (RT_FL_DIRCFG | RT_FL_EXECNAME)) == 684 (RT_FL_DIRCFG | RT_FL_EXECNAME)) && (flags & TKN_DOTSLASH)) { 685 int len; 686 687 if ((len = resolvepath(_name, _name, (PATH_MAX - 1))) >= 0) { 688 nlen = (size_t)len; 689 _name[nlen] = '\0'; 690 } 691 } 692 693 /* 694 * Allocate permanent storage for the new string and return to the user. 695 */ 696 if ((nptr = malloc(nlen + 1)) == 0) 697 return (0); 698 (void) strcpy(nptr, _name); 699 *name = nptr; 700 *len = nlen; 701 702 /* 703 * Return an indication of any token expansion that may have occurred. 704 * Under security, any pathname expanded with the $ORIGIN token must be 705 * validated against any registered secure directories. 706 */ 707 return (flags ? flags : TKN_NONE); 708 } 709 710 /* 711 * Determine whether a pathname is secure. 712 */ 713 static int 714 is_path_secure(char *opath, Rt_map *clmp, uint_t info, uint_t flags) 715 { 716 Pnode *sdir = LM_SECURE_DIRS(LIST(clmp)->lm_head); 717 char buffer[PATH_MAX], *npath; 718 Lm_list *lml; 719 720 /* 721 * If a pathname originates from a configuration file, use it. The use 722 * of a configuration file is already validated for secure applications, 723 * so if we're using a configuration file, we must be able to use all 724 * that it defines. 725 */ 726 if (info & LA_SER_CONFIG) 727 return (1); 728 729 if ((info & LA_SER_MASK) == 0) { 730 char *str; 731 732 /* 733 * If the pathname specifies a file (rather than a directory), 734 * peel off the file before making the comparison. 735 */ 736 str = strrchr(opath, '/'); 737 738 /* 739 * A simple filename (one containing no "/") is fine, as this 740 * will be combined with search paths to determine the complete 741 * path. Other paths are checked: 742 * 743 * . a full path (one starting with "/") is fine, provided 744 * it isn't a preload/audit path. 745 * . any $ORIGIN expansion 746 * . any relative path 747 */ 748 if (((str == 0) || ((*opath == '/') && (str != opath) && 749 ((info & PN_FLG_EXTLOAD) == 0))) && 750 ((flags & PN_TKN_ORIGIN) == 0)) 751 return (1); 752 753 if (str == opath) 754 npath = (char *)MSG_ORIG(MSG_STR_SLASH); 755 else { 756 size_t size; 757 758 if ((size = str - opath) >= PATH_MAX) 759 return (0); 760 761 (void) strncpy(buffer, opath, size); 762 buffer[size] = '\0'; 763 npath = buffer; 764 } 765 } else { 766 /* 767 * A search path, i.e., RPATH, configuration file path, etc. is 768 * used as is. Exceptions to this are: 769 * 770 * . LD_LIBRARY_PATH 771 * . any $ORIGIN expansion 772 * . any relative path 773 */ 774 if (((info & LA_SER_LIBPATH) == 0) && (*opath == '/') && 775 ((flags & PN_TKN_ORIGIN) == 0)) 776 return (1); 777 778 npath = (char *)opath; 779 } 780 781 while (sdir) { 782 if (strcmp(npath, sdir->p_name) == 0) 783 return (1); 784 sdir = sdir->p_next; 785 } 786 787 lml = LIST(clmp); 788 789 /* 790 * The path is insecure, so depending on the caller, provide a 791 * diagnostic. Preloaded, or audit libraries generate a warning, as 792 * the process will run without them. 793 */ 794 if (info & PN_FLG_EXTLOAD) { 795 if (lml->lm_flags & LML_FLG_TRC_ENABLE) { 796 if ((FLAGS1(clmp) & FL1_RT_LDDSTUB) == 0) 797 (void) printf(MSG_INTL(MSG_LDD_FIL_ILLEGAL), 798 opath); 799 } else 800 eprintf(lml, ERR_WARNING, MSG_INTL(MSG_SEC_ILLEGAL), 801 opath); 802 803 return (0); 804 } 805 806 /* 807 * Explicit file references are fatal. 808 */ 809 if ((info & LA_SER_MASK) == 0) { 810 if (lml->lm_flags & LML_FLG_TRC_ENABLE) { 811 /* BEGIN CSTYLED */ 812 if ((FLAGS1(clmp) & FL1_RT_LDDSTUB) == 0) { 813 if (lml->lm_flags & 814 (LML_FLG_TRC_VERBOSE | LML_FLG_TRC_SEARCH)) 815 (void) printf( 816 MSG_INTL(MSG_LDD_FIL_FIND), 817 opath, NAME(clmp)); 818 819 if (((rtld_flags & RT_FL_SILENCERR) == 0) || 820 (lml->lm_flags & LML_FLG_TRC_VERBOSE)) 821 (void) printf( 822 MSG_INTL(MSG_LDD_FIL_ILLEGAL), 823 opath); 824 } 825 /* END CSTYLED */ 826 } else 827 eprintf(lml, ERR_FATAL, MSG_INTL(MSG_SYS_OPEN), opath, 828 strerror(EACCES)); 829 } else { 830 /* 831 * Search paths. 832 */ 833 DBG_CALL(Dbg_libs_ignore(lml, opath)); 834 if ((lml->lm_flags & LML_FLG_TRC_SEARCH) && 835 ((FLAGS1(clmp) & FL1_RT_LDDSTUB) == 0)) 836 (void) printf(MSG_INTL(MSG_LDD_PTH_IGNORE), opath); 837 } 838 return (0); 839 } 840 841 /* 842 * Determine whether a path already exists within the callers Pnode list. 843 */ 844 inline static uint_t 845 is_path_unique(Pnode *pnp, const char *path) 846 { 847 for (; pnp; pnp = pnp->p_next) { 848 if (pnp->p_len && (strcmp(pnp->p_name, path) == 0)) 849 return (PN_FLG_DUPLICAT); 850 } 851 return (0); 852 } 853 854 /* 855 * Expand one or more pathnames. This routine is called for all path strings, 856 * i.e., NEEDED, rpaths, default search paths, configuration file search paths, 857 * filtees, etc. The path may be a single pathname, or a colon separated list 858 * of pathnames. Each individual pathname is processed for possible reserved 859 * token expansion. All string nodes are maintained in allocated memory 860 * (regardless of whether they are constant (":"), or token expanded) to 861 * simplify pnode removal. 862 * 863 * The info argument passes in auxiliary information regarding the callers 864 * intended use of the pathnames. This information may be maintained in the 865 * pnode element produced to describe the pathname (i.e., LA_SER_LIBPATH etc.), 866 * or may be used to determine additional security or diagnostic processing. 867 */ 868 Pnode * 869 expand_paths(Rt_map *clmp, const char *list, uint_t orig, uint_t omit) 870 { 871 char *str, *olist = 0, *nlist = (char *)list; 872 Pnode *pnp, *npnp, *opnp; 873 int fnull = FALSE; /* TRUE if empty final path segment seen */ 874 uint_t unique = 0; 875 876 for (pnp = opnp = 0, str = nlist; *nlist || fnull; str = nlist) { 877 char *ostr; 878 size_t len, olen; 879 uint_t tkns = 0; 880 881 if (*nlist == ';') 882 ++nlist, ++str; 883 if ((*nlist == ':') || fnull) { 884 /* If not a final null segment, check following one */ 885 fnull = !(fnull || *(nlist + 1)); 886 887 if (*nlist) 888 nlist++; 889 890 /* 891 * When the shell sees a null PATH segment, it 892 * treats it as if it were the cwd (.). We mimic 893 * this behavior for LD_LIBRARY_PATH and runpaths 894 * (mainly for backwards compatibility with previous 895 * behavior). For other paths, this makes no sense, 896 * so we simply ignore the segment. 897 */ 898 if (!(orig & (LA_SER_LIBPATH | LA_SER_RUNPATH))) 899 continue; /* Process next segment */ 900 901 if ((str = strdup(MSG_ORIG(MSG_FMT_CWD))) == NULL) 902 return (NULL); 903 len = MSG_FMT_CWD_SIZE; 904 905 } else { 906 char *elist; 907 908 len = 0; 909 while (*nlist && (*nlist != ':') && (*nlist != ';')) { 910 nlist++, len++; 911 } 912 913 /* Check for a following final null segment */ 914 fnull = (*nlist == ':') && !*(nlist + 1); 915 916 if (*nlist) 917 nlist++; 918 919 /* 920 * Expand the captured string. Besides expanding the 921 * present path/file entry, we may have a new list to 922 * deal with (ISALIST expands to multiple new entries). 923 */ 924 elist = nlist; 925 ostr = str; 926 olen = len; 927 if ((tkns = expand(&str, &len, &elist, orig, omit, 928 clmp)) == 0) 929 continue; 930 931 if (elist != nlist) { 932 if (olist) 933 free(olist); 934 nlist = olist = elist; 935 } 936 } 937 938 /* 939 * If this a secure application, validation of the expanded 940 * pathname may be necessary. 941 */ 942 if (rtld_flags & RT_FL_SECURE) { 943 if (is_path_secure(str, clmp, orig, tkns) == 0) { 944 free(str); 945 continue; 946 } 947 } 948 949 /* 950 * If required, ensure that the string is unique. For search 951 * paths such as LD_LIBRARY_PATH, users often inherit multiple 952 * paths which result in unnecessary duplication. Note, if 953 * we're debugging, any duplicate entry is retained and flagged 954 * so that the entry can be diagnosed later as part of unused 955 * processing. 956 */ 957 if (orig & PN_FLG_UNIQUE) { 958 Word tracing; 959 960 tracing = LIST(clmp)->lm_flags & 961 (LML_FLG_TRC_UNREF | LML_FLG_TRC_UNUSED); 962 unique = is_path_unique(pnp, str); 963 964 /* 965 * Note, use the debug strings rpl_debug and prm_debug 966 * as an indicator that debugging has been requested, 967 * rather than DBG_ENABLE(), as the initial use of 968 * LD_LIBRARY_PATH occurs in preparation for loading 969 * our debugging library. 970 */ 971 if ((unique == PN_FLG_DUPLICAT) && (tracing == 0) && 972 (rpl_debug == 0) && (prm_debug == 0)) { 973 free(str); 974 continue; 975 } 976 } 977 978 /* 979 * Allocate a new Pnode for this string. 980 */ 981 if ((npnp = calloc(1, sizeof (Pnode))) == 0) { 982 free(str); 983 return (NULL); 984 } 985 if (opnp == 0) 986 pnp = npnp; 987 else 988 opnp->p_next = npnp; 989 990 if (tkns & PN_TKN_MASK) { 991 char *oname; 992 993 /* 994 * If this is a pathname, and any token expansion 995 * occurred, maintain the original string for possible 996 * diagnostic use. 997 */ 998 if ((oname = malloc(olen + 1)) == 0) { 999 free(str); 1000 return (NULL); 1001 } 1002 (void) strncpy(oname, ostr, olen); 1003 oname[olen] = '\0'; 1004 npnp->p_oname = oname; 1005 } 1006 npnp->p_name = str; 1007 npnp->p_len = len; 1008 npnp->p_orig = (orig & LA_SER_MASK) | unique | 1009 (tkns & PN_TKN_MASK); 1010 1011 opnp = npnp; 1012 } 1013 1014 if (olist) 1015 free(olist); 1016 1017 return (pnp); 1018 } 1019