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