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