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