xref: /onnv-gate/usr/src/cmd/sgs/rtld/common/paths.c (revision 12877:69001e4756ae)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
51618Srie  * Common Development and Distribution License (the "License").
61618Srie  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
21751Srie 
220Sstevel@tonic-gate /*
23*12877SRod.Evans@Sun.COM  * Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved.
240Sstevel@tonic-gate  */
256812Sraf 
266812Sraf /*
276812Sraf  *	Copyright (c) 1988 AT&T
286812Sraf  *	  All Rights Reserved
296812Sraf  */
306812Sraf 
310Sstevel@tonic-gate /*
320Sstevel@tonic-gate  * PATH setup and search directory functions.
330Sstevel@tonic-gate  */
340Sstevel@tonic-gate 
350Sstevel@tonic-gate #include	<stdio.h>
368598SRod.Evans@Sun.COM #include	<unistd.h>
370Sstevel@tonic-gate #include	<limits.h>
380Sstevel@tonic-gate #include	<fcntl.h>
390Sstevel@tonic-gate #include	<string.h>
401618Srie #include	<debug.h>
411618Srie #include	<conv.h>
420Sstevel@tonic-gate #include	"_rtld.h"
430Sstevel@tonic-gate #include	"msg.h"
440Sstevel@tonic-gate 
450Sstevel@tonic-gate /*
468598SRod.Evans@Sun.COM  * Default and secure dependency search path initialization.
478598SRod.Evans@Sun.COM  */
488598SRod.Evans@Sun.COM void
set_dirs(Alist ** alpp,Spath_defn * sdp,uint_t flags)498598SRod.Evans@Sun.COM set_dirs(Alist **alpp, Spath_defn *sdp, uint_t flags)
508598SRod.Evans@Sun.COM {
518598SRod.Evans@Sun.COM 	while (sdp->sd_name) {
528598SRod.Evans@Sun.COM 		Pdesc	*pdp;
538598SRod.Evans@Sun.COM 
549340SRod.Evans@Sun.COM 		if ((pdp = alist_append(alpp, NULL, sizeof (Pdesc),
558598SRod.Evans@Sun.COM 		    AL_CNT_SPATH)) == NULL)
568598SRod.Evans@Sun.COM 			return;
578598SRod.Evans@Sun.COM 
588598SRod.Evans@Sun.COM 		pdp->pd_pname = (char *)sdp->sd_name;
598598SRod.Evans@Sun.COM 		pdp->pd_plen = sdp->sd_len;
608598SRod.Evans@Sun.COM 		pdp->pd_flags = flags;
618598SRod.Evans@Sun.COM 		sdp++;
628598SRod.Evans@Sun.COM 	}
638598SRod.Evans@Sun.COM }
648598SRod.Evans@Sun.COM 
658598SRod.Evans@Sun.COM static void
print_default_dirs(Lm_list * lml,Alist * alp,int search)668598SRod.Evans@Sun.COM print_default_dirs(Lm_list *lml, Alist *alp, int search)
678598SRod.Evans@Sun.COM {
688598SRod.Evans@Sun.COM 	uint_t	flags = 0;
698598SRod.Evans@Sun.COM 	int	num = 0;
708598SRod.Evans@Sun.COM 	Aliste	idx;
718598SRod.Evans@Sun.COM 	Pdesc	*pdp;
728598SRod.Evans@Sun.COM 
738598SRod.Evans@Sun.COM 	if (search)
748598SRod.Evans@Sun.COM 		(void) printf(MSG_INTL(MSG_LDD_PTH_BGNDFL));
758598SRod.Evans@Sun.COM 
768598SRod.Evans@Sun.COM 	for (ALIST_TRAVERSE(alp, idx, pdp)) {
778598SRod.Evans@Sun.COM 		flags = pdp->pd_flags;
788598SRod.Evans@Sun.COM 
798598SRod.Evans@Sun.COM 		if (search) {
808598SRod.Evans@Sun.COM 			const char	*fmt;
818598SRod.Evans@Sun.COM 
828598SRod.Evans@Sun.COM 			if (num++)
838598SRod.Evans@Sun.COM 				fmt = MSG_ORIG(MSG_LDD_FMT_PATHN);
848598SRod.Evans@Sun.COM 			else
858598SRod.Evans@Sun.COM 				fmt = MSG_ORIG(MSG_LDD_FMT_PATH1);
868598SRod.Evans@Sun.COM 
878598SRod.Evans@Sun.COM 			(void) printf(fmt, pdp->pd_pname);
888598SRod.Evans@Sun.COM 		} else
898598SRod.Evans@Sun.COM 			DBG_CALL(Dbg_libs_path(lml, pdp->pd_pname,
908598SRod.Evans@Sun.COM 			    pdp->pd_flags, config->c_name));
918598SRod.Evans@Sun.COM 	}
928598SRod.Evans@Sun.COM 
938598SRod.Evans@Sun.COM 	if (search) {
948598SRod.Evans@Sun.COM 		if (flags & LA_SER_CONFIG)
958598SRod.Evans@Sun.COM 			(void) printf(MSG_INTL(MSG_LDD_PTH_ENDDFLC),
968598SRod.Evans@Sun.COM 			    config->c_name);
978598SRod.Evans@Sun.COM 		else
988598SRod.Evans@Sun.COM 			(void) printf(MSG_INTL(MSG_LDD_PTH_ENDDFL));
998598SRod.Evans@Sun.COM 	}
1008598SRod.Evans@Sun.COM }
1018598SRod.Evans@Sun.COM 
1028598SRod.Evans@Sun.COM /*
1030Sstevel@tonic-gate  * Given a search rule type, return a list of directories to search according
1040Sstevel@tonic-gate  * to the specified rule.
1050Sstevel@tonic-gate  */
1068598SRod.Evans@Sun.COM static Alist **
get_dir_list(uchar_t rules,Rt_map * lmp,uint_t flags)1076387Srie get_dir_list(uchar_t rules, Rt_map *lmp, uint_t flags)
1080Sstevel@tonic-gate {
1098598SRod.Evans@Sun.COM 	Alist	**dalpp = NULL;
1108598SRod.Evans@Sun.COM 	Lm_list *lml = LIST(lmp);
1118598SRod.Evans@Sun.COM 	int	search;
1120Sstevel@tonic-gate 
1130Sstevel@tonic-gate 	/*
1140Sstevel@tonic-gate 	 * Determine whether ldd -s is in effect - ignore when we're searching
1150Sstevel@tonic-gate 	 * for audit libraries as these will be added to their own link-map.
1160Sstevel@tonic-gate 	 */
1170Sstevel@tonic-gate 	if ((lml->lm_flags & LML_FLG_TRC_SEARCH) &&
1180Sstevel@tonic-gate 	    ((FLAGS1(lmp) & FL1_RT_LDDSTUB) == 0) &&
1190Sstevel@tonic-gate 	    ((flags & FLG_RT_AUDIT) == 0))
1200Sstevel@tonic-gate 		search = 1;
1210Sstevel@tonic-gate 	else
1220Sstevel@tonic-gate 		search = 0;
1230Sstevel@tonic-gate 
1240Sstevel@tonic-gate 	switch (rules) {
1250Sstevel@tonic-gate 	case RPLENV:
1260Sstevel@tonic-gate 		/*
1270Sstevel@tonic-gate 		 * Initialize the replaceable environment variable
1280Sstevel@tonic-gate 		 * (LD_LIBRARY_PATH) search path list.  Note, we always call
1290Sstevel@tonic-gate 		 * Dbg_libs_path() so that every library lookup diagnostic can
1300Sstevel@tonic-gate 		 * be preceded with the appropriate search path information.
1310Sstevel@tonic-gate 		 */
1320Sstevel@tonic-gate 		if (rpl_libpath) {
1338598SRod.Evans@Sun.COM 			uint_t	mode = (LA_SER_LIBPATH | PD_FLG_UNIQUE);
1340Sstevel@tonic-gate 
1350Sstevel@tonic-gate 			/*
1360Sstevel@tonic-gate 			 * Note, this path may have originated from the users
1370Sstevel@tonic-gate 			 * environment or from a configuration file.
1380Sstevel@tonic-gate 			 */
1390Sstevel@tonic-gate 			if (env_info & ENV_INF_PATHCFG)
1400Sstevel@tonic-gate 				mode |= LA_SER_CONFIG;
1410Sstevel@tonic-gate 
1421618Srie 			DBG_CALL(Dbg_libs_path(lml, rpl_libpath, mode,
143751Srie 			    config->c_name));
1440Sstevel@tonic-gate 
1450Sstevel@tonic-gate 			/*
1460Sstevel@tonic-gate 			 * For ldd(1) -s, indicate the search paths that'll
1477835SRod.Evans@Sun.COM 			 * be used.  If this is a secure application then some
1480Sstevel@tonic-gate 			 * search paths may be ignored, therefore reset the
1490Sstevel@tonic-gate 			 * rpl_libdirs pointer each time so that the
1500Sstevel@tonic-gate 			 * diagnostics related to these unsecure directories
1510Sstevel@tonic-gate 			 * will be output for each image loaded.
1520Sstevel@tonic-gate 			 */
1530Sstevel@tonic-gate 			if (search) {
1540Sstevel@tonic-gate 				const char	*fmt;
1550Sstevel@tonic-gate 
1560Sstevel@tonic-gate 				if (env_info & ENV_INF_PATHCFG)
1570Sstevel@tonic-gate 					fmt = MSG_INTL(MSG_LDD_PTH_LIBPATHC);
1580Sstevel@tonic-gate 				else
1590Sstevel@tonic-gate 					fmt = MSG_INTL(MSG_LDD_PTH_LIBPATH);
1600Sstevel@tonic-gate 
1610Sstevel@tonic-gate 				(void) printf(fmt, rpl_libpath, config->c_name);
1620Sstevel@tonic-gate 			}
1630Sstevel@tonic-gate 			if (rpl_libdirs && (rtld_flags & RT_FL_SECURE) &&
1648598SRod.Evans@Sun.COM 			    (search || DBG_ENABLED))
165*12877SRod.Evans@Sun.COM 				remove_alist(&rpl_libdirs, 1);
1668598SRod.Evans@Sun.COM 
1678598SRod.Evans@Sun.COM 			if (rpl_libdirs == NULL) {
1680Sstevel@tonic-gate 				/*
1690Sstevel@tonic-gate 				 * If this is a secure application we need to
1700Sstevel@tonic-gate 				 * be selective over what directories we use.
1710Sstevel@tonic-gate 				 */
1728598SRod.Evans@Sun.COM 				(void) expand_paths(lmp, rpl_libpath,
1738598SRod.Evans@Sun.COM 				    &rpl_libdirs, AL_CNT_SEARCH, mode,
17411827SRod.Evans@Sun.COM 				    PD_TKN_CAP);
1750Sstevel@tonic-gate 			}
1768598SRod.Evans@Sun.COM 			dalpp = &rpl_libdirs;
1770Sstevel@tonic-gate 		}
1780Sstevel@tonic-gate 		break;
1790Sstevel@tonic-gate 	case PRMENV:
1800Sstevel@tonic-gate 		/*
1810Sstevel@tonic-gate 		 * Initialize the permanent (LD_LIBRARY_PATH) search path list.
1820Sstevel@tonic-gate 		 * This can only originate from a configuration file.  To be
1830Sstevel@tonic-gate 		 * consistent with the debugging display of DEFENV (above),
1840Sstevel@tonic-gate 		 * always call Dbg_libs_path().
1850Sstevel@tonic-gate 		 */
1860Sstevel@tonic-gate 		if (prm_libpath) {
1876387Srie 			uint_t	mode =
1888598SRod.Evans@Sun.COM 			    (LA_SER_LIBPATH | LA_SER_CONFIG | PD_FLG_UNIQUE);
1896387Srie 
1906387Srie 			DBG_CALL(Dbg_libs_path(lml, prm_libpath, mode,
1916387Srie 			    config->c_name));
1920Sstevel@tonic-gate 
1930Sstevel@tonic-gate 			/*
1940Sstevel@tonic-gate 			 * For ldd(1) -s, indicate the search paths that'll
1957835SRod.Evans@Sun.COM 			 * be used.  If this is a secure application then some
1960Sstevel@tonic-gate 			 * search paths may be ignored, therefore reset the
1970Sstevel@tonic-gate 			 * prm_libdirs pointer each time so that the
1980Sstevel@tonic-gate 			 * diagnostics related to these unsecure directories
1990Sstevel@tonic-gate 			 * will be output for each image loaded.
2000Sstevel@tonic-gate 			 */
2010Sstevel@tonic-gate 			if (search)
2020Sstevel@tonic-gate 				(void) printf(MSG_INTL(MSG_LDD_PTH_LIBPATHC),
2030Sstevel@tonic-gate 				    prm_libpath, config->c_name);
2040Sstevel@tonic-gate 			if (prm_libdirs && (rtld_flags & RT_FL_SECURE) &&
2058598SRod.Evans@Sun.COM 			    (search || DBG_ENABLED))
206*12877SRod.Evans@Sun.COM 				remove_alist(&prm_libdirs, 1);
2078598SRod.Evans@Sun.COM 
2088598SRod.Evans@Sun.COM 			if (prm_libdirs == NULL) {
2090Sstevel@tonic-gate 				/*
2100Sstevel@tonic-gate 				 * If this is a secure application we need to
2110Sstevel@tonic-gate 				 * be selective over what directories we use.
2120Sstevel@tonic-gate 				 */
2138598SRod.Evans@Sun.COM 				(void) expand_paths(lmp, prm_libpath,
2148598SRod.Evans@Sun.COM 				    &prm_libdirs, AL_CNT_SEARCH, mode,
21511827SRod.Evans@Sun.COM 				    PD_TKN_CAP);
2160Sstevel@tonic-gate 			}
2178598SRod.Evans@Sun.COM 			dalpp = &prm_libdirs;
2180Sstevel@tonic-gate 		}
2190Sstevel@tonic-gate 		break;
2200Sstevel@tonic-gate 	case RUNPATH:
2210Sstevel@tonic-gate 		/*
2220Sstevel@tonic-gate 		 * Initialize the runpath search path list.  To be consistent
2230Sstevel@tonic-gate 		 * with the debugging display of DEFENV (above), always call
2240Sstevel@tonic-gate 		 * Dbg_libs_path().
2250Sstevel@tonic-gate 		 */
2260Sstevel@tonic-gate 		if (RPATH(lmp)) {
2271618Srie 			DBG_CALL(Dbg_libs_path(lml, RPATH(lmp), LA_SER_RUNPATH,
2280Sstevel@tonic-gate 			    NAME(lmp)));
2290Sstevel@tonic-gate 
2300Sstevel@tonic-gate 			/*
2310Sstevel@tonic-gate 			 * For ldd(1) -s, indicate the search paths that'll
2327835SRod.Evans@Sun.COM 			 * be used.  If this is a secure application then some
2330Sstevel@tonic-gate 			 * search paths may be ignored, therefore reset the
2340Sstevel@tonic-gate 			 * runlist pointer each time so that the diagnostics
2350Sstevel@tonic-gate 			 * related to these unsecure directories will be
2360Sstevel@tonic-gate 			 * output for each image loaded.
2370Sstevel@tonic-gate 			 */
2380Sstevel@tonic-gate 			if (search)
2393511Srie 				(void) printf(MSG_INTL(MSG_LDD_PTH_RUNPATH),
2400Sstevel@tonic-gate 				    RPATH(lmp), NAME(lmp));
2410Sstevel@tonic-gate 			if (RLIST(lmp) && (rtld_flags & RT_FL_SECURE) &&
2428598SRod.Evans@Sun.COM 			    (search || DBG_ENABLED))
243*12877SRod.Evans@Sun.COM 				remove_alist(&RLIST(lmp), 1);
2448598SRod.Evans@Sun.COM 
2458598SRod.Evans@Sun.COM 			if (RLIST(lmp) == NULL) {
2460Sstevel@tonic-gate 				/*
2470Sstevel@tonic-gate 				 * If this is a secure application we need to
2480Sstevel@tonic-gate 				 * be selective over what directories we use.
2490Sstevel@tonic-gate 				 */
2508598SRod.Evans@Sun.COM 				(void) expand_paths(lmp, RPATH(lmp),
2518598SRod.Evans@Sun.COM 				    &RLIST(lmp), AL_CNT_SEARCH, LA_SER_RUNPATH,
25211827SRod.Evans@Sun.COM 				    PD_TKN_CAP);
2538598SRod.Evans@Sun.COM 			}
2548598SRod.Evans@Sun.COM 			dalpp = &RLIST(lmp);
2550Sstevel@tonic-gate 		}
2560Sstevel@tonic-gate 		break;
2570Sstevel@tonic-gate 	case DEFAULT:
2580Sstevel@tonic-gate 		if ((FLAGS1(lmp) & FL1_RT_NODEFLIB) == 0) {
2590Sstevel@tonic-gate 			if ((rtld_flags & RT_FL_SECURE) &&
2600Sstevel@tonic-gate 			    (flags & (FLG_RT_PRELOAD | FLG_RT_AUDIT)))
2618598SRod.Evans@Sun.COM 				dalpp = LM_SECURE_DIRS(lmp)();
2620Sstevel@tonic-gate 			else
2638598SRod.Evans@Sun.COM 				dalpp = LM_DEFAULT_DIRS(lmp)();
2640Sstevel@tonic-gate 		}
2650Sstevel@tonic-gate 
2660Sstevel@tonic-gate 		/*
2670Sstevel@tonic-gate 		 * For ldd(1) -s, indicate the default paths that'll be used.
2680Sstevel@tonic-gate 		 */
2698598SRod.Evans@Sun.COM 		if (dalpp && (search || DBG_ENABLED))
2708598SRod.Evans@Sun.COM 			print_default_dirs(lml, *dalpp, search);
2710Sstevel@tonic-gate 		break;
2720Sstevel@tonic-gate 	default:
2730Sstevel@tonic-gate 		break;
2740Sstevel@tonic-gate 	}
2758598SRod.Evans@Sun.COM 	return (dalpp);
2760Sstevel@tonic-gate }
2770Sstevel@tonic-gate 
2780Sstevel@tonic-gate /*
27911827SRod.Evans@Sun.COM  * Get the next directory in the search rules path.  The search path "cookie"
2808598SRod.Evans@Sun.COM  * provided by the caller (sdp) maintains the state of a search in progress.
2818598SRod.Evans@Sun.COM  *
2828598SRod.Evans@Sun.COM  * Typically, a search consists of a series of rules that govern the order of
2838598SRod.Evans@Sun.COM  * a search (ie. LD_LIBRARY_PATH, followed by RPATHS, followed by defaults).
2848598SRod.Evans@Sun.COM  * Each rule can establish a corresponding series of path names, which are
2858598SRod.Evans@Sun.COM  * maintained as an Alist.  The index within this Alist determines the present
2868598SRod.Evans@Sun.COM  * search directory.
2870Sstevel@tonic-gate  */
2888598SRod.Evans@Sun.COM Pdesc *
get_next_dir(Spath_desc * sdp,Rt_map * lmp,uint_t flags)2898598SRod.Evans@Sun.COM get_next_dir(Spath_desc *sdp, Rt_map *lmp, uint_t flags)
2900Sstevel@tonic-gate {
2910Sstevel@tonic-gate 	/*
2928598SRod.Evans@Sun.COM 	 * Make sure there are still rules to process.
2930Sstevel@tonic-gate 	 */
2948598SRod.Evans@Sun.COM 	while (*sdp->sp_rule) {
2958598SRod.Evans@Sun.COM 		Alist	*alp;
2960Sstevel@tonic-gate 
2978598SRod.Evans@Sun.COM 		/*
2988598SRod.Evans@Sun.COM 		 * If an Alist for this rule already exists, use if, otherwise
2998598SRod.Evans@Sun.COM 		 * obtain an Alist for this rule.  Providing the Alist has
3008598SRod.Evans@Sun.COM 		 * content, and the present Alist index is less than the number
3018598SRod.Evans@Sun.COM 		 * of Alist members, return the associated path name descriptor.
3028598SRod.Evans@Sun.COM 		 */
3038598SRod.Evans@Sun.COM 		if ((sdp->sp_dalpp || ((sdp->sp_dalpp =
3048598SRod.Evans@Sun.COM 		    get_dir_list(*sdp->sp_rule, lmp, flags)) != NULL)) &&
3058598SRod.Evans@Sun.COM 		    ((alp = *sdp->sp_dalpp) != NULL) &&
3068598SRod.Evans@Sun.COM 		    (alist_nitems(alp) > sdp->sp_idx)) {
3078598SRod.Evans@Sun.COM 			return (alist_item(alp, sdp->sp_idx++));
3088598SRod.Evans@Sun.COM 		}
3098598SRod.Evans@Sun.COM 
3108598SRod.Evans@Sun.COM 		/*
3118598SRod.Evans@Sun.COM 		 * If no Alist for this rule exists, or if this is the last
3128598SRod.Evans@Sun.COM 		 * element of this Alist, reset the Alist pointer and index,
3138598SRod.Evans@Sun.COM 		 * and prepare for the next rule.
3148598SRod.Evans@Sun.COM 		 */
3158598SRod.Evans@Sun.COM 		sdp->sp_rule++;
3168598SRod.Evans@Sun.COM 		sdp->sp_dalpp = NULL;
3178598SRod.Evans@Sun.COM 		sdp->sp_idx = 0;
3180Sstevel@tonic-gate 	}
3190Sstevel@tonic-gate 
3200Sstevel@tonic-gate 	/*
3218598SRod.Evans@Sun.COM 	 * All rules and search paths have been exhausted.
3220Sstevel@tonic-gate 	 */
3232853Srie 	return (NULL);
3240Sstevel@tonic-gate }
3250Sstevel@tonic-gate 
3260Sstevel@tonic-gate /*
3270Sstevel@tonic-gate  * Process a directory (runpath) or filename (needed or filter) string looking
3280Sstevel@tonic-gate  * for tokens to expand.  Allocate a new buffer for the string.
3290Sstevel@tonic-gate  */
3300Sstevel@tonic-gate uint_t
expand(char ** name,size_t * len,char ** list,uint_t orig,uint_t omit,Rt_map * lmp)3310Sstevel@tonic-gate expand(char **name, size_t *len, char **list, uint_t orig, uint_t omit,
3321618Srie     Rt_map *lmp)
3330Sstevel@tonic-gate {
3340Sstevel@tonic-gate 	char	_name[PATH_MAX];
3358598SRod.Evans@Sun.COM 	char	*token = NULL, *oname, *ename, *optr, *_optr, *nptr, *_list;
3360Sstevel@tonic-gate 	size_t	olen = 0, nlen = 0, _len;
3370Sstevel@tonic-gate 	int	isaflag = 0;
3380Sstevel@tonic-gate 	uint_t	flags = 0;
3391618Srie 	Lm_list	*lml = LIST(lmp);
3400Sstevel@tonic-gate 
3418598SRod.Evans@Sun.COM 	optr = _optr = oname = ename = *name;
3428598SRod.Evans@Sun.COM 	ename += *len;
3430Sstevel@tonic-gate 	nptr = _name;
3440Sstevel@tonic-gate 
3450Sstevel@tonic-gate 	while ((olen < *len) && (nlen < PATH_MAX)) {
3460Sstevel@tonic-gate 		uint_t	_flags;
3470Sstevel@tonic-gate 
3480Sstevel@tonic-gate 		if ((*optr != '$') || ((olen - *len) == 1)) {
3490Sstevel@tonic-gate 			/*
3500Sstevel@tonic-gate 			 * When expanding paths while a configuration file
3510Sstevel@tonic-gate 			 * exists that contains directory information, determine
3520Sstevel@tonic-gate 			 * whether the path contains "./".  If so, we'll resolve
3530Sstevel@tonic-gate 			 * the path later to remove these relative entries.
3540Sstevel@tonic-gate 			 */
3550Sstevel@tonic-gate 			if ((rtld_flags & RT_FL_DIRCFG) &&
3560Sstevel@tonic-gate 			    (orig & LA_SER_MASK) && (*optr == '/') &&
3570Sstevel@tonic-gate 			    (optr != oname) && (*(optr - 1) == '.'))
3580Sstevel@tonic-gate 				flags |= TKN_DOTSLASH;
3590Sstevel@tonic-gate 
3600Sstevel@tonic-gate 			olen++, optr++;
3610Sstevel@tonic-gate 			continue;
3620Sstevel@tonic-gate 		}
3630Sstevel@tonic-gate 
3640Sstevel@tonic-gate 		/*
3650Sstevel@tonic-gate 		 * Copy any string we've presently passed over to the new
3660Sstevel@tonic-gate 		 * buffer.
3670Sstevel@tonic-gate 		 */
3680Sstevel@tonic-gate 		if ((_len = (optr - _optr)) != 0) {
3690Sstevel@tonic-gate 			if ((nlen += _len) < PATH_MAX) {
3700Sstevel@tonic-gate 				(void) strncpy(nptr, _optr, _len);
3710Sstevel@tonic-gate 				nptr = nptr + _len;
3720Sstevel@tonic-gate 			} else {
3731618Srie 				eprintf(lml, ERR_FATAL,
3741618Srie 				    MSG_INTL(MSG_ERR_EXPAND1), NAME(lmp),
3751618Srie 				    oname);
3760Sstevel@tonic-gate 				return (0);
3770Sstevel@tonic-gate 			}
3780Sstevel@tonic-gate 		}
3790Sstevel@tonic-gate 
3800Sstevel@tonic-gate 		/*
3810Sstevel@tonic-gate 		 * Skip the token delimiter and determine if a reserved token
3820Sstevel@tonic-gate 		 * match is found.
3830Sstevel@tonic-gate 		 */
3840Sstevel@tonic-gate 		olen++, optr++;
3850Sstevel@tonic-gate 		_flags = 0;
3860Sstevel@tonic-gate 		token = 0;
3870Sstevel@tonic-gate 
3880Sstevel@tonic-gate 		if (strncmp(optr, MSG_ORIG(MSG_TKN_ORIGIN),
3890Sstevel@tonic-gate 		    MSG_TKN_ORIGIN_SIZE) == 0) {
3900Sstevel@tonic-gate 			token = (char *)MSG_ORIG(MSG_TKN_ORIGIN);
3910Sstevel@tonic-gate 
3920Sstevel@tonic-gate 			/*
3930Sstevel@tonic-gate 			 * $ORIGIN expansion is required.  Determine this
3940Sstevel@tonic-gate 			 * objects basename.  Expansion of $ORIGIN is allowed
3950Sstevel@tonic-gate 			 * for secure applications but must be checked by the
3960Sstevel@tonic-gate 			 * caller to insure the expanded path matches a
3970Sstevel@tonic-gate 			 * registered secure name.
3980Sstevel@tonic-gate 			 */
3998598SRod.Evans@Sun.COM 			if (((omit & PD_TKN_ORIGIN) == 0) &&
4000Sstevel@tonic-gate 			    (((_len = DIRSZ(lmp)) != 0) ||
4010Sstevel@tonic-gate 			    ((_len = fullpath(lmp, 0)) != 0))) {
4020Sstevel@tonic-gate 				if ((nlen += _len) < PATH_MAX) {
4030Sstevel@tonic-gate 					(void) strncpy(nptr,
4040Sstevel@tonic-gate 					    ORIGNAME(lmp), _len);
4050Sstevel@tonic-gate 					nptr = nptr +_len;
4060Sstevel@tonic-gate 					olen += MSG_TKN_ORIGIN_SIZE;
4070Sstevel@tonic-gate 					optr += MSG_TKN_ORIGIN_SIZE;
4088598SRod.Evans@Sun.COM 					_flags |= PD_TKN_ORIGIN;
4090Sstevel@tonic-gate 				} else {
4101618Srie 					eprintf(lml, ERR_FATAL,
4110Sstevel@tonic-gate 					    MSG_INTL(MSG_ERR_EXPAND1),
4120Sstevel@tonic-gate 					    NAME(lmp), oname);
4130Sstevel@tonic-gate 					return (0);
4140Sstevel@tonic-gate 				}
4150Sstevel@tonic-gate 			}
4160Sstevel@tonic-gate 
4170Sstevel@tonic-gate 		} else if (strncmp(optr, MSG_ORIG(MSG_TKN_PLATFORM),
4180Sstevel@tonic-gate 		    MSG_TKN_PLATFORM_SIZE) == 0) {
41911827SRod.Evans@Sun.COM 			Syscapset	*scapset;
42011827SRod.Evans@Sun.COM 
42111827SRod.Evans@Sun.COM 			if (FLAGS1(lmp) & FL1_RT_ALTCAP)
42211827SRod.Evans@Sun.COM 				scapset = alt_scapset;
42311827SRod.Evans@Sun.COM 			else
42411827SRod.Evans@Sun.COM 				scapset = org_scapset;
42511827SRod.Evans@Sun.COM 
4260Sstevel@tonic-gate 			token = (char *)MSG_ORIG(MSG_TKN_PLATFORM);
4270Sstevel@tonic-gate 
4280Sstevel@tonic-gate 			/*
42911827SRod.Evans@Sun.COM 			 * $PLATFORM expansion required.
4300Sstevel@tonic-gate 			 */
4318598SRod.Evans@Sun.COM 			if (((omit & PD_TKN_PLATFORM) == 0) &&
43211827SRod.Evans@Sun.COM 			    ((scapset->sc_plat == NULL) &&
43311827SRod.Evans@Sun.COM 			    (scapset->sc_platsz == 0)))
43411827SRod.Evans@Sun.COM 				platform_name(scapset);
4350Sstevel@tonic-gate 
4368598SRod.Evans@Sun.COM 			if (((omit & PD_TKN_PLATFORM) == 0) &&
43711827SRod.Evans@Sun.COM 			    scapset->sc_plat) {
43811827SRod.Evans@Sun.COM 				nlen += scapset->sc_platsz;
43911827SRod.Evans@Sun.COM 				if (nlen < PATH_MAX) {
44011827SRod.Evans@Sun.COM 					(void) strncpy(nptr, scapset->sc_plat,
44111827SRod.Evans@Sun.COM 					    scapset->sc_platsz);
44211827SRod.Evans@Sun.COM 					nptr = nptr + scapset->sc_platsz;
4430Sstevel@tonic-gate 					olen += MSG_TKN_PLATFORM_SIZE;
4440Sstevel@tonic-gate 					optr += MSG_TKN_PLATFORM_SIZE;
4458598SRod.Evans@Sun.COM 					_flags |= PD_TKN_PLATFORM;
4460Sstevel@tonic-gate 				} else {
4471618Srie 					eprintf(lml, ERR_FATAL,
4480Sstevel@tonic-gate 					    MSG_INTL(MSG_ERR_EXPAND1),
4490Sstevel@tonic-gate 					    NAME(lmp), oname);
4500Sstevel@tonic-gate 					return (0);
4510Sstevel@tonic-gate 				}
4520Sstevel@tonic-gate 			}
4530Sstevel@tonic-gate 
45411827SRod.Evans@Sun.COM 		} else if (strncmp(optr, MSG_ORIG(MSG_TKN_MACHINE),
45511827SRod.Evans@Sun.COM 		    MSG_TKN_MACHINE_SIZE) == 0) {
45611827SRod.Evans@Sun.COM 			Syscapset	*scapset;
45711827SRod.Evans@Sun.COM 
45811827SRod.Evans@Sun.COM 			if (FLAGS1(lmp) & FL1_RT_ALTCAP)
45911827SRod.Evans@Sun.COM 				scapset = alt_scapset;
46011827SRod.Evans@Sun.COM 			else
46111827SRod.Evans@Sun.COM 				scapset = org_scapset;
46211827SRod.Evans@Sun.COM 
46311827SRod.Evans@Sun.COM 			token = (char *)MSG_ORIG(MSG_TKN_MACHINE);
46411827SRod.Evans@Sun.COM 
46511827SRod.Evans@Sun.COM 			/*
46611827SRod.Evans@Sun.COM 			 * $MACHINE expansion required.
46711827SRod.Evans@Sun.COM 			 */
46811827SRod.Evans@Sun.COM 			if (((omit & PD_TKN_MACHINE) == 0) &&
46911827SRod.Evans@Sun.COM 			    ((scapset->sc_mach == NULL) &&
47011827SRod.Evans@Sun.COM 			    (scapset->sc_machsz == 0)))
47111827SRod.Evans@Sun.COM 				machine_name(scapset);
47211827SRod.Evans@Sun.COM 
47311827SRod.Evans@Sun.COM 			if (((omit & PD_TKN_MACHINE) == 0) &&
47411827SRod.Evans@Sun.COM 			    scapset->sc_mach) {
47511827SRod.Evans@Sun.COM 				nlen += scapset->sc_machsz;
47611827SRod.Evans@Sun.COM 				if (nlen < PATH_MAX) {
47711827SRod.Evans@Sun.COM 					(void) strncpy(nptr, scapset->sc_mach,
47811827SRod.Evans@Sun.COM 					    scapset->sc_machsz);
47911827SRod.Evans@Sun.COM 					nptr = nptr + scapset->sc_machsz;
48011827SRod.Evans@Sun.COM 					olen += MSG_TKN_MACHINE_SIZE;
48111827SRod.Evans@Sun.COM 					optr += MSG_TKN_MACHINE_SIZE;
48211827SRod.Evans@Sun.COM 					_flags |= PD_TKN_MACHINE;
48311827SRod.Evans@Sun.COM 				} else {
48411827SRod.Evans@Sun.COM 					eprintf(lml, ERR_FATAL,
48511827SRod.Evans@Sun.COM 					    MSG_INTL(MSG_ERR_EXPAND1),
48611827SRod.Evans@Sun.COM 					    NAME(lmp), oname);
48711827SRod.Evans@Sun.COM 					return (0);
48811827SRod.Evans@Sun.COM 				}
48911827SRod.Evans@Sun.COM 			}
49011827SRod.Evans@Sun.COM 
4910Sstevel@tonic-gate 		} else if (strncmp(optr, MSG_ORIG(MSG_TKN_OSNAME),
4920Sstevel@tonic-gate 		    MSG_TKN_OSNAME_SIZE) == 0) {
4930Sstevel@tonic-gate 			token = (char *)MSG_ORIG(MSG_TKN_OSNAME);
4940Sstevel@tonic-gate 
4950Sstevel@tonic-gate 			/*
4960Sstevel@tonic-gate 			 * $OSNAME expansion required.  This is established
4970Sstevel@tonic-gate 			 * from the sysname[] returned by uname(2).
4980Sstevel@tonic-gate 			 */
4998598SRod.Evans@Sun.COM 			if (((omit & PD_TKN_OSNAME) == 0) && (uts == NULL))
5000Sstevel@tonic-gate 				uts = conv_uts();
5010Sstevel@tonic-gate 
5028598SRod.Evans@Sun.COM 			if (((omit & PD_TKN_OSNAME) == 0) &&
5030Sstevel@tonic-gate 			    (uts && uts->uts_osnamesz)) {
5040Sstevel@tonic-gate 				if ((nlen += uts->uts_osnamesz) < PATH_MAX) {
5050Sstevel@tonic-gate 					(void) strncpy(nptr, uts->uts_osname,
5060Sstevel@tonic-gate 					    uts->uts_osnamesz);
5070Sstevel@tonic-gate 					nptr = nptr + uts->uts_osnamesz;
5080Sstevel@tonic-gate 					olen += MSG_TKN_OSNAME_SIZE;
5090Sstevel@tonic-gate 					optr += MSG_TKN_OSNAME_SIZE;
5108598SRod.Evans@Sun.COM 					_flags |= PD_TKN_OSNAME;
5110Sstevel@tonic-gate 				} else {
5121618Srie 					eprintf(lml, ERR_FATAL,
5130Sstevel@tonic-gate 					    MSG_INTL(MSG_ERR_EXPAND1),
5140Sstevel@tonic-gate 					    NAME(lmp), oname);
5150Sstevel@tonic-gate 					return (0);
5160Sstevel@tonic-gate 				}
5170Sstevel@tonic-gate 			}
5180Sstevel@tonic-gate 
5190Sstevel@tonic-gate 		} else if (strncmp(optr, MSG_ORIG(MSG_TKN_OSREL),
5200Sstevel@tonic-gate 		    MSG_TKN_OSREL_SIZE) == 0) {
5210Sstevel@tonic-gate 			token = (char *)MSG_ORIG(MSG_TKN_OSREL);
5220Sstevel@tonic-gate 
5230Sstevel@tonic-gate 			/*
5240Sstevel@tonic-gate 			 * $OSREL expansion required.  This is established
5250Sstevel@tonic-gate 			 * from the release[] returned by uname(2).
5260Sstevel@tonic-gate 			 */
5278598SRod.Evans@Sun.COM 			if (((omit & PD_TKN_OSREL) == 0) && (uts == 0))
5280Sstevel@tonic-gate 				uts = conv_uts();
5290Sstevel@tonic-gate 
5308598SRod.Evans@Sun.COM 			if (((omit & PD_TKN_OSREL) == 0) &&
5310Sstevel@tonic-gate 			    (uts && uts->uts_osrelsz)) {
5320Sstevel@tonic-gate 				if ((nlen += uts->uts_osrelsz) < PATH_MAX) {
5330Sstevel@tonic-gate 					(void) strncpy(nptr, uts->uts_osrel,
5340Sstevel@tonic-gate 					    uts->uts_osrelsz);
5350Sstevel@tonic-gate 					nptr = nptr + uts->uts_osrelsz;
5360Sstevel@tonic-gate 					olen += MSG_TKN_OSREL_SIZE;
5370Sstevel@tonic-gate 					optr += MSG_TKN_OSREL_SIZE;
5388598SRod.Evans@Sun.COM 					_flags |= PD_TKN_OSREL;
5390Sstevel@tonic-gate 				} else {
5401618Srie 					eprintf(lml, ERR_FATAL,
5410Sstevel@tonic-gate 					    MSG_INTL(MSG_ERR_EXPAND1),
5420Sstevel@tonic-gate 					    NAME(lmp), oname);
5430Sstevel@tonic-gate 					return (0);
5440Sstevel@tonic-gate 				}
5450Sstevel@tonic-gate 			}
5460Sstevel@tonic-gate 
5470Sstevel@tonic-gate 		} else if ((strncmp(optr, MSG_ORIG(MSG_TKN_ISALIST),
5480Sstevel@tonic-gate 		    MSG_TKN_ISALIST_SIZE) == 0)) {
5490Sstevel@tonic-gate 			int	ok;
5500Sstevel@tonic-gate 			token = (char *)MSG_ORIG(MSG_TKN_ISALIST);
5510Sstevel@tonic-gate 
5520Sstevel@tonic-gate 			/*
5530Sstevel@tonic-gate 			 * $ISALIST expansion required.  When accompanied with
5540Sstevel@tonic-gate 			 * a list pointer, this routine updates that pointer
5550Sstevel@tonic-gate 			 * with the new list of potential candidates.  Without
5560Sstevel@tonic-gate 			 * this list pointer, only the first expansion is
5570Sstevel@tonic-gate 			 * provided.  NOTE, that two $ISLIST expansions within
5580Sstevel@tonic-gate 			 * the same path aren't supported.
5590Sstevel@tonic-gate 			 */
5608598SRod.Evans@Sun.COM 			if ((omit & PD_TKN_ISALIST) || isaflag++)
5610Sstevel@tonic-gate 				ok = 0;
5620Sstevel@tonic-gate 			else
5630Sstevel@tonic-gate 				ok = 1;
5640Sstevel@tonic-gate 
5658598SRod.Evans@Sun.COM 			if (ok && (isa == NULL))
5660Sstevel@tonic-gate 				isa = conv_isalist();
5670Sstevel@tonic-gate 
5680Sstevel@tonic-gate 			if (ok && isa && isa->isa_listsz) {
5690Sstevel@tonic-gate 				size_t	no, mlen, tlen, hlen = olen - 1;
5700Sstevel@tonic-gate 				char	*lptr;
5710Sstevel@tonic-gate 				Isa_opt *opt = isa->isa_opt;
5720Sstevel@tonic-gate 
5730Sstevel@tonic-gate 				if ((nlen += opt->isa_namesz) < PATH_MAX) {
5740Sstevel@tonic-gate 					(void) strncpy(nptr,  opt->isa_name,
5750Sstevel@tonic-gate 					    opt->isa_namesz);
5760Sstevel@tonic-gate 					nptr = nptr + opt->isa_namesz;
5770Sstevel@tonic-gate 					olen += MSG_TKN_ISALIST_SIZE;
5780Sstevel@tonic-gate 					optr += MSG_TKN_ISALIST_SIZE;
5798598SRod.Evans@Sun.COM 					_flags |= PD_TKN_ISALIST;
5800Sstevel@tonic-gate 				} else {
5811618Srie 					eprintf(lml, ERR_FATAL,
5820Sstevel@tonic-gate 					    MSG_INTL(MSG_ERR_EXPAND1),
5830Sstevel@tonic-gate 					    NAME(lmp), oname);
5840Sstevel@tonic-gate 					return (0);
5850Sstevel@tonic-gate 				}
5860Sstevel@tonic-gate 
5870Sstevel@tonic-gate 				if (list) {
5880Sstevel@tonic-gate 					tlen = *len - olen;
5890Sstevel@tonic-gate 					mlen = ((hlen + tlen) *
5900Sstevel@tonic-gate 					    (isa->isa_optno - 1)) +
5910Sstevel@tonic-gate 					    isa->isa_listsz - opt->isa_namesz +
5920Sstevel@tonic-gate 					    strlen(*list);
5938598SRod.Evans@Sun.COM 					if ((_list = lptr =
5948598SRod.Evans@Sun.COM 					    malloc(mlen)) == NULL)
5950Sstevel@tonic-gate 						return (0);
5960Sstevel@tonic-gate 
5970Sstevel@tonic-gate 					for (no = 1, opt++; no < isa->isa_optno;
5980Sstevel@tonic-gate 					    no++, opt++) {
5990Sstevel@tonic-gate 						(void) strncpy(lptr, *name,
6000Sstevel@tonic-gate 						    hlen);
6010Sstevel@tonic-gate 						lptr = lptr + hlen;
6020Sstevel@tonic-gate 						(void) strncpy(lptr,
6030Sstevel@tonic-gate 						    opt->isa_name,
6040Sstevel@tonic-gate 						    opt->isa_namesz);
6050Sstevel@tonic-gate 						lptr = lptr + opt->isa_namesz;
6060Sstevel@tonic-gate 						(void) strncpy(lptr, optr,
6070Sstevel@tonic-gate 						    tlen);
6080Sstevel@tonic-gate 						lptr = lptr + tlen;
6090Sstevel@tonic-gate 						*lptr++ = ':';
6100Sstevel@tonic-gate 					}
6110Sstevel@tonic-gate 					if (**list)
6120Sstevel@tonic-gate 						(void) strcpy(lptr, *list);
6130Sstevel@tonic-gate 					else
6140Sstevel@tonic-gate 						*--lptr = '\0';
6150Sstevel@tonic-gate 				}
6160Sstevel@tonic-gate 			}
6170Sstevel@tonic-gate 
61811827SRod.Evans@Sun.COM 		} else if (strncmp(optr, MSG_ORIG(MSG_TKN_CAPABILITY),
61911827SRod.Evans@Sun.COM 		    MSG_TKN_CAPABILITY_SIZE) == 0) {
62011827SRod.Evans@Sun.COM 			char	*bptr = nptr - 1;
62111827SRod.Evans@Sun.COM 			char	*eptr = optr + MSG_TKN_CAPABILITY_SIZE;
62211827SRod.Evans@Sun.COM 			token = (char *)MSG_ORIG(MSG_TKN_CAPABILITY);
62311827SRod.Evans@Sun.COM 
62411827SRod.Evans@Sun.COM 			/*
62511827SRod.Evans@Sun.COM 			 * $CAPABILITY expansion required.  Expansion is only
62611827SRod.Evans@Sun.COM 			 * allowed for non-simple path names (must contain a
62711827SRod.Evans@Sun.COM 			 * '/'), with the token itself being the last element
62811827SRod.Evans@Sun.COM 			 * of the path.  Therefore, all we need do is test the
62911827SRod.Evans@Sun.COM 			 * existence of the string "/$CAPABILITY\0".
63011827SRod.Evans@Sun.COM 			 */
63111827SRod.Evans@Sun.COM 			if (((omit & PD_TKN_CAP) == 0) &&
63211827SRod.Evans@Sun.COM 			    ((bptr > _name) && (*bptr == '/') &&
63311827SRod.Evans@Sun.COM 			    ((*eptr == '\0') || (*eptr == ':')))) {
63411827SRod.Evans@Sun.COM 				/*
63511827SRod.Evans@Sun.COM 				 * Decrement the present pointer so that the
63611827SRod.Evans@Sun.COM 				 * directories trailing "/" gets nuked later.
63711827SRod.Evans@Sun.COM 				 */
63811827SRod.Evans@Sun.COM 				nptr--, nlen--;
63911827SRod.Evans@Sun.COM 				olen += MSG_TKN_CAPABILITY_SIZE;
64011827SRod.Evans@Sun.COM 				optr += MSG_TKN_CAPABILITY_SIZE;
64111827SRod.Evans@Sun.COM 				_flags |= PD_TKN_CAP;
64211827SRod.Evans@Sun.COM 			}
64311827SRod.Evans@Sun.COM 
6440Sstevel@tonic-gate 		} else if (strncmp(optr, MSG_ORIG(MSG_TKN_HWCAP),
6450Sstevel@tonic-gate 		    MSG_TKN_HWCAP_SIZE) == 0) {
6460Sstevel@tonic-gate 			char	*bptr = nptr - 1;
6470Sstevel@tonic-gate 			char	*eptr = optr + MSG_TKN_HWCAP_SIZE;
6480Sstevel@tonic-gate 			token = (char *)MSG_ORIG(MSG_TKN_HWCAP);
6490Sstevel@tonic-gate 
6500Sstevel@tonic-gate 			/*
65111827SRod.Evans@Sun.COM 			 * $HWCAP expansion required.  This token has been
65211827SRod.Evans@Sun.COM 			 * superseeded by $CAPABILITY.  For compatibility with
6530Sstevel@tonic-gate 			 * older environments, only expand this token when hard-
6540Sstevel@tonic-gate 			 * ware capability information is available.   This
6557835SRod.Evans@Sun.COM 			 * expansion is only allowed for non-simple path names
6560Sstevel@tonic-gate 			 * (must contain a '/'), with the token itself being the
6570Sstevel@tonic-gate 			 * last element of the path.  Therefore, all we need do
6580Sstevel@tonic-gate 			 * is test the existence of the string "/$HWCAP\0".
6590Sstevel@tonic-gate 			 */
66011827SRod.Evans@Sun.COM 			if (((omit & PD_TKN_CAP) == 0) &&
6610Sstevel@tonic-gate 			    (rtld_flags2 & RT_FL2_HWCAP) &&
6620Sstevel@tonic-gate 			    ((bptr > _name) && (*bptr == '/') &&
6630Sstevel@tonic-gate 			    ((*eptr == '\0') || (*eptr == ':')))) {
6640Sstevel@tonic-gate 				/*
6650Sstevel@tonic-gate 				 * Decrement the present pointer so that the
6660Sstevel@tonic-gate 				 * directories trailing "/" gets nuked later.
6670Sstevel@tonic-gate 				 */
6680Sstevel@tonic-gate 				nptr--, nlen--;
6690Sstevel@tonic-gate 				olen += MSG_TKN_HWCAP_SIZE;
6700Sstevel@tonic-gate 				optr += MSG_TKN_HWCAP_SIZE;
67111827SRod.Evans@Sun.COM 				_flags |= PD_TKN_CAP;
6720Sstevel@tonic-gate 			}
6730Sstevel@tonic-gate 
6740Sstevel@tonic-gate 		} else {
6750Sstevel@tonic-gate 			/*
6760Sstevel@tonic-gate 			 * If reserved token was not found, copy the
6770Sstevel@tonic-gate 			 * character.
6780Sstevel@tonic-gate 			 */
6790Sstevel@tonic-gate 			*nptr++ = '$';
6800Sstevel@tonic-gate 			nlen++;
6810Sstevel@tonic-gate 		}
6820Sstevel@tonic-gate 
6830Sstevel@tonic-gate 		/*
6848598SRod.Evans@Sun.COM 		 * If a reserved token was found, and could not be expanded,
6856040Srie 		 * diagnose the error condition.
6860Sstevel@tonic-gate 		 */
6870Sstevel@tonic-gate 		if (token) {
6880Sstevel@tonic-gate 			if (_flags)
6890Sstevel@tonic-gate 				flags |= _flags;
6900Sstevel@tonic-gate 			else {
6916040Srie 				char	buf[PATH_MAX], *str;
6926040Srie 
6936040Srie 				/*
6946040Srie 				 * Note, the original string we're expanding
6956040Srie 				 * might contain a number of ':' separated
6966040Srie 				 * paths.  Isolate the path we're processing to
6976040Srie 				 * provide a more precise error diagnostic.
6986040Srie 				 */
6996040Srie 				if (str = strchr(oname, ':')) {
7006040Srie 					size_t	slen = str - oname;
7016040Srie 
7026040Srie 					(void) strncpy(buf, oname, slen);
7036040Srie 					buf[slen] = '\0';
7046040Srie 					str = buf;
7056040Srie 				} else
7066040Srie 					str = oname;
7076040Srie 
7081618Srie 				eprintf(lml, ERR_FATAL,
7091618Srie 				    MSG_INTL(MSG_ERR_EXPAND2), NAME(lmp),
7106040Srie 				    str, token);
7110Sstevel@tonic-gate 				return (0);
7120Sstevel@tonic-gate 			}
7130Sstevel@tonic-gate 		}
7140Sstevel@tonic-gate 		_optr = optr;
7150Sstevel@tonic-gate 	}
7160Sstevel@tonic-gate 
7170Sstevel@tonic-gate 	/*
7180Sstevel@tonic-gate 	 * First make sure the current length is shorter than PATH_MAX.  We may
7190Sstevel@tonic-gate 	 * arrive here if the given path contains '$' characters which are not
7200Sstevel@tonic-gate 	 * the lead of a reserved token.
7210Sstevel@tonic-gate 	 */
7220Sstevel@tonic-gate 	if (nlen >= PATH_MAX) {
7231618Srie 		eprintf(lml, ERR_FATAL, MSG_INTL(MSG_ERR_EXPAND1), NAME(lmp),
7241618Srie 		    oname);
7250Sstevel@tonic-gate 		return (0);
7260Sstevel@tonic-gate 	}
7270Sstevel@tonic-gate 
7280Sstevel@tonic-gate 	/*
7290Sstevel@tonic-gate 	 * If any ISALIST processing has occurred not only do we return the
7300Sstevel@tonic-gate 	 * expanded node we're presently working on, but we can also update the
7310Sstevel@tonic-gate 	 * remaining list so that it is effectively prepended with this node
7320Sstevel@tonic-gate 	 * expanded to all remaining ISALIST options.  Note that we can only
7330Sstevel@tonic-gate 	 * handle one ISALIST per node.  For more than one ISALIST to be
7340Sstevel@tonic-gate 	 * processed we'd need a better algorithm than above to replace the
7350Sstevel@tonic-gate 	 * newly generated list.  Whether we want to encourage the number of
7367835SRod.Evans@Sun.COM 	 * path name permutations this would provide is another question.  So,
7377835SRod.Evans@Sun.COM 	 * for now if more than one ISALIST is encountered we return the
7387835SRod.Evans@Sun.COM 	 * original node untouched.
7390Sstevel@tonic-gate 	 */
7406040Srie 	if (isa && isaflag) {
7410Sstevel@tonic-gate 		if (isaflag == 1) {
7420Sstevel@tonic-gate 			if (list)
7430Sstevel@tonic-gate 				*list = _list;
7440Sstevel@tonic-gate 		} else {
7458598SRod.Evans@Sun.COM 			flags &= ~PD_TKN_ISALIST;
7468598SRod.Evans@Sun.COM 			if ((nptr = (char *)stravl_insert(*name, 0,
7478598SRod.Evans@Sun.COM 			    (*len + 1), 1)) == NULL)
7480Sstevel@tonic-gate 				return (0);
7490Sstevel@tonic-gate 			*name = nptr;
7500Sstevel@tonic-gate 			return (TKN_NONE);
7510Sstevel@tonic-gate 		}
7520Sstevel@tonic-gate 	}
7530Sstevel@tonic-gate 
7540Sstevel@tonic-gate 	/*
7550Sstevel@tonic-gate 	 * Copy any remaining string. Terminate the new string with a null as
7560Sstevel@tonic-gate 	 * this string can be displayed via debugging diagnostics.
7570Sstevel@tonic-gate 	 */
7580Sstevel@tonic-gate 	if ((_len = (optr - _optr)) != 0) {
7590Sstevel@tonic-gate 		if ((nlen += _len) < PATH_MAX) {
7600Sstevel@tonic-gate 			(void) strncpy(nptr, _optr, _len);
7610Sstevel@tonic-gate 			nptr = nptr + _len;
7620Sstevel@tonic-gate 		} else {
7631618Srie 			eprintf(lml, ERR_FATAL, MSG_INTL(MSG_ERR_EXPAND1),
7640Sstevel@tonic-gate 			    NAME(lmp), oname);
7650Sstevel@tonic-gate 			return (0);
7660Sstevel@tonic-gate 		}
7670Sstevel@tonic-gate 	}
7680Sstevel@tonic-gate 	*nptr = '\0';
7690Sstevel@tonic-gate 
7700Sstevel@tonic-gate 	/*
7717835SRod.Evans@Sun.COM 	 * A path that has been expanded is typically used to create full
7727835SRod.Evans@Sun.COM 	 * path names for objects that will be opened.  The final path name is
7730Sstevel@tonic-gate 	 * resolved to simplify it, and set the stage for possible $ORIGIN
7746387Srie 	 * processing.  Therefore, it's usually unnecessary to resolve the path
7750Sstevel@tonic-gate 	 * at this point.  However, if a configuration file, containing
7760Sstevel@tonic-gate 	 * directory information is in use, then we might need to lookup this
7777835SRod.Evans@Sun.COM 	 * path in the configuration file.  To keep the number of path name
7780Sstevel@tonic-gate 	 * resolutions to a minimum, only resolve paths that contain "./".  The
7790Sstevel@tonic-gate 	 * use of "$ORIGIN/../lib" will probably only match a configuration file
7800Sstevel@tonic-gate 	 * entry after resolution.
7810Sstevel@tonic-gate 	 */
7828598SRod.Evans@Sun.COM 	if (list && (rtld_flags & RT_FL_DIRCFG) && (flags & TKN_DOTSLASH)) {
7830Sstevel@tonic-gate 		int	len;
7840Sstevel@tonic-gate 
7850Sstevel@tonic-gate 		if ((len = resolvepath(_name, _name, (PATH_MAX - 1))) >= 0) {
7860Sstevel@tonic-gate 			nlen = (size_t)len;
7870Sstevel@tonic-gate 			_name[nlen] = '\0';
7888598SRod.Evans@Sun.COM 			flags |= PD_TKN_RESOLVED;
7890Sstevel@tonic-gate 		}
7900Sstevel@tonic-gate 	}
7910Sstevel@tonic-gate 
7920Sstevel@tonic-gate 	/*
7938598SRod.Evans@Sun.COM 	 * Allocate a new string if necessary.
7948598SRod.Evans@Sun.COM 	 *
7958598SRod.Evans@Sun.COM 	 * If any form of token expansion, or string resolution has occurred,
7968598SRod.Evans@Sun.COM 	 * the storage must be allocated for the new string.
7978598SRod.Evans@Sun.COM 	 *
7988598SRod.Evans@Sun.COM 	 * If we're processing a substring, for example, any string besides the
7998598SRod.Evans@Sun.COM 	 * last string within a search path "A:B:C", then this substring needs
8008598SRod.Evans@Sun.COM 	 * to be isolated with a null terminator.  However, if this search path
8018598SRod.Evans@Sun.COM 	 * was created from a previous ISALIST expansion, then all strings must
8028598SRod.Evans@Sun.COM 	 * be allocated, as the isalist expansion will be freed after expansion
8038598SRod.Evans@Sun.COM 	 * processing.
8040Sstevel@tonic-gate 	 */
8058598SRod.Evans@Sun.COM 	if ((nptr = (char *)stravl_insert(_name, 0, (nlen + 1), 1)) == NULL)
8060Sstevel@tonic-gate 		return (0);
8070Sstevel@tonic-gate 	*name = nptr;
8080Sstevel@tonic-gate 	*len = nlen;
8090Sstevel@tonic-gate 	return (flags ? flags : TKN_NONE);
8100Sstevel@tonic-gate }
8110Sstevel@tonic-gate 
8120Sstevel@tonic-gate /*
8137835SRod.Evans@Sun.COM  * Determine whether a path name is secure.
8140Sstevel@tonic-gate  */
8157835SRod.Evans@Sun.COM int
is_path_secure(char * opath,Rt_map * clmp,uint_t info,uint_t flags)8166387Srie is_path_secure(char *opath, Rt_map *clmp, uint_t info, uint_t flags)
8170Sstevel@tonic-gate {
8188598SRod.Evans@Sun.COM 	Alist		**salpp;
8198598SRod.Evans@Sun.COM 	Aliste		idx;
8208598SRod.Evans@Sun.COM 	char		buffer[PATH_MAX], *npath = NULL;
8218598SRod.Evans@Sun.COM 	Lm_list		*lml = LIST(clmp);
8228598SRod.Evans@Sun.COM 	Pdesc		*pdp;
8230Sstevel@tonic-gate 
8240Sstevel@tonic-gate 	/*
8257835SRod.Evans@Sun.COM 	 * If a path name originates from a configuration file, use it.  The use
8260Sstevel@tonic-gate 	 * of a configuration file is already validated for secure applications,
8270Sstevel@tonic-gate 	 * so if we're using a configuration file, we must be able to use all
8280Sstevel@tonic-gate 	 * that it defines.
8290Sstevel@tonic-gate 	 */
8300Sstevel@tonic-gate 	if (info & LA_SER_CONFIG)
8310Sstevel@tonic-gate 		return (1);
8320Sstevel@tonic-gate 
8330Sstevel@tonic-gate 	if ((info & LA_SER_MASK) == 0) {
8340Sstevel@tonic-gate 		char	*str;
8350Sstevel@tonic-gate 
8360Sstevel@tonic-gate 		/*
8377835SRod.Evans@Sun.COM 		 * If the path name specifies a file (rather than a directory),
8380Sstevel@tonic-gate 		 * peel off the file before making the comparison.
8390Sstevel@tonic-gate 		 */
8400Sstevel@tonic-gate 		str = strrchr(opath, '/');
8410Sstevel@tonic-gate 
8420Sstevel@tonic-gate 		/*
8437668SRod.Evans@Sun.COM 		 * Carry out some initial security checks.
8440Sstevel@tonic-gate 		 *
8457668SRod.Evans@Sun.COM 		 *   .	a simple file name (one containing no "/") is fine, as
8467668SRod.Evans@Sun.COM 		 *	this file name will be combined with search paths to
8477835SRod.Evans@Sun.COM 		 *	determine the complete path.  Note, a secure application
8487835SRod.Evans@Sun.COM 		 *	may provide a configuration file, and this can only be
8497835SRod.Evans@Sun.COM 		 *	a full path name (PN_FLG_FULLPATH).
8500Sstevel@tonic-gate 		 *   .	a full path (one starting with "/") is fine, provided
8517668SRod.Evans@Sun.COM 		 *	this path name isn't a preload/audit path.
8527668SRod.Evans@Sun.COM 		 *   .	provided $ORIGIN expansion has not been employed, the
8537668SRod.Evans@Sun.COM 		 *	above categories of path are deemed secure.
8540Sstevel@tonic-gate 		 */
8558598SRod.Evans@Sun.COM 		if ((((str == 0) && ((info & PD_FLG_FULLPATH) == 0)) ||
8567835SRod.Evans@Sun.COM 		    ((*opath == '/') && (str != opath) &&
8578598SRod.Evans@Sun.COM 		    ((info & PD_FLG_EXTLOAD) == 0))) &&
8588598SRod.Evans@Sun.COM 		    ((flags & PD_TKN_ORIGIN) == 0))
8590Sstevel@tonic-gate 			return (1);
8600Sstevel@tonic-gate 
8617668SRod.Evans@Sun.COM 		/*
8627668SRod.Evans@Sun.COM 		 * Determine the directory name of the present path.
8637668SRod.Evans@Sun.COM 		 */
8647835SRod.Evans@Sun.COM 		if (str) {
8657835SRod.Evans@Sun.COM 			if (str == opath)
8667835SRod.Evans@Sun.COM 				npath = (char *)MSG_ORIG(MSG_STR_SLASH);
8677835SRod.Evans@Sun.COM 			else {
8687835SRod.Evans@Sun.COM 				size_t	size;
8697835SRod.Evans@Sun.COM 
8707835SRod.Evans@Sun.COM 				if ((size = str - opath) >= PATH_MAX)
8717835SRod.Evans@Sun.COM 					return (0);
8720Sstevel@tonic-gate 
8737835SRod.Evans@Sun.COM 				(void) strncpy(buffer, opath, size);
8747835SRod.Evans@Sun.COM 				buffer[size] = '\0';
8757835SRod.Evans@Sun.COM 				npath = buffer;
8767835SRod.Evans@Sun.COM 			}
8770Sstevel@tonic-gate 
8787835SRod.Evans@Sun.COM 			/*
8797835SRod.Evans@Sun.COM 			 * If $ORIGIN processing has been employed, then allow
8807835SRod.Evans@Sun.COM 			 * any directory that has already been used to satisfy
8817835SRod.Evans@Sun.COM 			 * other dependencies, to be used.
8827835SRod.Evans@Sun.COM 			 */
8838598SRod.Evans@Sun.COM 			if ((flags & PD_TKN_ORIGIN) &&
88411827SRod.Evans@Sun.COM 			    pnavl_recorded(&spavl, npath, 0, NULL)) {
8857835SRod.Evans@Sun.COM 				DBG_CALL(Dbg_libs_insecure(lml, npath, 1));
8867835SRod.Evans@Sun.COM 				return (1);
8877835SRod.Evans@Sun.COM 			}
8880Sstevel@tonic-gate 		}
8890Sstevel@tonic-gate 	} else {
8900Sstevel@tonic-gate 		/*
8910Sstevel@tonic-gate 		 * A search path, i.e., RPATH, configuration file path, etc. is
8920Sstevel@tonic-gate 		 * used as is.  Exceptions to this are:
8930Sstevel@tonic-gate 		 *
8947668SRod.Evans@Sun.COM 		 *   .	LD_LIBRARY_PATH.
8957668SRod.Evans@Sun.COM 		 *   .	any $ORIGIN expansion, unless used by a setuid ld.so.1
8967668SRod.Evans@Sun.COM 		 *	to find its own dependencies, or the path name has
8977668SRod.Evans@Sun.COM 		 *	already been used to find other dependencies.
8987668SRod.Evans@Sun.COM 		 *   .	any relative path.
8990Sstevel@tonic-gate 		 */
9000Sstevel@tonic-gate 		if (((info & LA_SER_LIBPATH) == 0) && (*opath == '/') &&
9018598SRod.Evans@Sun.COM 		    ((flags & PD_TKN_ORIGIN) == 0))
9020Sstevel@tonic-gate 			return (1);
9030Sstevel@tonic-gate 
9047668SRod.Evans@Sun.COM 		/*
9057668SRod.Evans@Sun.COM 		 * If $ORIGIN processing is requested, allow a setuid ld.so.1
9067668SRod.Evans@Sun.COM 		 * to use this path for its own dependencies.  Allow the
9077668SRod.Evans@Sun.COM 		 * application to use this path name only if the path name has
9087668SRod.Evans@Sun.COM 		 * already been used to locate other dependencies.
9097668SRod.Evans@Sun.COM 		 */
9108598SRod.Evans@Sun.COM 		if (flags & PD_TKN_ORIGIN) {
9117668SRod.Evans@Sun.COM 			if ((lml->lm_flags & LML_FLG_RTLDLM) &&
9127668SRod.Evans@Sun.COM 			    is_rtld_setuid())
9137668SRod.Evans@Sun.COM 				return (1);
91411827SRod.Evans@Sun.COM 			else if (pnavl_recorded(&spavl, opath, 0, NULL)) {
9157668SRod.Evans@Sun.COM 				DBG_CALL(Dbg_libs_insecure(lml, opath, 1));
9167668SRod.Evans@Sun.COM 				return (1);
9177668SRod.Evans@Sun.COM 			}
9187668SRod.Evans@Sun.COM 		}
9190Sstevel@tonic-gate 		npath = (char *)opath;
9200Sstevel@tonic-gate 	}
9210Sstevel@tonic-gate 
9227835SRod.Evans@Sun.COM 	/*
9237835SRod.Evans@Sun.COM 	 * Determine whether the present directory is trusted.
9247835SRod.Evans@Sun.COM 	 */
9257835SRod.Evans@Sun.COM 	if (npath) {
9268598SRod.Evans@Sun.COM 		salpp = LM_SECURE_DIRS(LIST(clmp)->lm_head)();
9278598SRod.Evans@Sun.COM 		for (ALIST_TRAVERSE(*salpp, idx, pdp)) {
9288598SRod.Evans@Sun.COM 			if (strcmp(npath, pdp->pd_pname) == 0)
9297835SRod.Evans@Sun.COM 				return (1);
9307835SRod.Evans@Sun.COM 		}
9310Sstevel@tonic-gate 	}
9320Sstevel@tonic-gate 
9330Sstevel@tonic-gate 	/*
9340Sstevel@tonic-gate 	 * The path is insecure, so depending on the caller, provide a
9350Sstevel@tonic-gate 	 * diagnostic.  Preloaded, or audit libraries generate a warning, as
9360Sstevel@tonic-gate 	 * the process will run without them.
9370Sstevel@tonic-gate 	 */
9388598SRod.Evans@Sun.COM 	if (info & PD_FLG_EXTLOAD) {
9390Sstevel@tonic-gate 		if (lml->lm_flags & LML_FLG_TRC_ENABLE) {
9400Sstevel@tonic-gate 			if ((FLAGS1(clmp) & FL1_RT_LDDSTUB) == 0)
9410Sstevel@tonic-gate 				(void) printf(MSG_INTL(MSG_LDD_FIL_ILLEGAL),
9420Sstevel@tonic-gate 				    opath);
9430Sstevel@tonic-gate 		} else
9441618Srie 			eprintf(lml, ERR_WARNING, MSG_INTL(MSG_SEC_ILLEGAL),
9451618Srie 			    opath);
9460Sstevel@tonic-gate 
9470Sstevel@tonic-gate 		return (0);
9480Sstevel@tonic-gate 	}
9490Sstevel@tonic-gate 
9500Sstevel@tonic-gate 	/*
9510Sstevel@tonic-gate 	 * Explicit file references are fatal.
9520Sstevel@tonic-gate 	 */
9530Sstevel@tonic-gate 	if ((info & LA_SER_MASK) == 0) {
9540Sstevel@tonic-gate 		if (lml->lm_flags & LML_FLG_TRC_ENABLE) {
9556040Srie 			/* BEGIN CSTYLED */
9560Sstevel@tonic-gate 			if ((FLAGS1(clmp) & FL1_RT_LDDSTUB) == 0) {
9576812Sraf 				if (lml->lm_flags &
9586812Sraf 				    (LML_FLG_TRC_VERBOSE | LML_FLG_TRC_SEARCH))
9596812Sraf 					(void) printf(
9606812Sraf 					    MSG_INTL(MSG_LDD_FIL_FIND),
9616812Sraf 					    opath, NAME(clmp));
9620Sstevel@tonic-gate 
9636812Sraf 				if (((rtld_flags & RT_FL_SILENCERR) == 0) ||
9646812Sraf 				    (lml->lm_flags & LML_FLG_TRC_VERBOSE))
9656812Sraf 					(void) printf(
9666812Sraf 					    MSG_INTL(MSG_LDD_FIL_ILLEGAL),
9676812Sraf 					    opath);
9680Sstevel@tonic-gate 			}
9696040Srie 			/* END CSTYLED */
9700Sstevel@tonic-gate 		} else
9711618Srie 			eprintf(lml, ERR_FATAL, MSG_INTL(MSG_SYS_OPEN), opath,
9720Sstevel@tonic-gate 			    strerror(EACCES));
9730Sstevel@tonic-gate 	} else {
9740Sstevel@tonic-gate 		/*
9750Sstevel@tonic-gate 		 * Search paths.
9760Sstevel@tonic-gate 		 */
9777668SRod.Evans@Sun.COM 		DBG_CALL(Dbg_libs_insecure(lml, opath, 0));
9780Sstevel@tonic-gate 		if ((lml->lm_flags & LML_FLG_TRC_SEARCH) &&
9790Sstevel@tonic-gate 		    ((FLAGS1(clmp) & FL1_RT_LDDSTUB) == 0))
9800Sstevel@tonic-gate 			(void) printf(MSG_INTL(MSG_LDD_PTH_IGNORE), opath);
9810Sstevel@tonic-gate 	}
9820Sstevel@tonic-gate 	return (0);
9830Sstevel@tonic-gate }
9840Sstevel@tonic-gate 
9850Sstevel@tonic-gate /*
9866387Srie  * Determine whether a path already exists within the callers Pnode list.
9876387Srie  */
9886387Srie inline static uint_t
is_path_unique(Alist * alp,const char * path)9898598SRod.Evans@Sun.COM is_path_unique(Alist *alp, const char *path)
9906387Srie {
9918598SRod.Evans@Sun.COM 	Aliste	idx;
9928598SRod.Evans@Sun.COM 	Pdesc	*pdp;
9938598SRod.Evans@Sun.COM 
9948598SRod.Evans@Sun.COM 	for (ALIST_TRAVERSE(alp, idx, pdp)) {
9958598SRod.Evans@Sun.COM 		if (pdp->pd_plen && (strcmp(pdp->pd_pname, path) == 0))
9968598SRod.Evans@Sun.COM 			return (PD_FLG_DUPLICAT);
9976387Srie 	}
9986387Srie 	return (0);
9996387Srie }
10006387Srie 
10016387Srie /*
10027835SRod.Evans@Sun.COM  * Expand one or more path names.  This routine is called for all path strings,
10030Sstevel@tonic-gate  * i.e., NEEDED, rpaths, default search paths, configuration file search paths,
10047835SRod.Evans@Sun.COM  * filtees, etc.  The path may be a single path name, or a colon separated list
10057835SRod.Evans@Sun.COM  * of path names.  Each individual path name is processed for possible reserved
10060Sstevel@tonic-gate  * token expansion.  All string nodes are maintained in allocated memory
10070Sstevel@tonic-gate  * (regardless of whether they are constant (":"), or token expanded) to
10088598SRod.Evans@Sun.COM  * simplify path name descriptor removal.
10090Sstevel@tonic-gate  *
10100Sstevel@tonic-gate  * The info argument passes in auxiliary information regarding the callers
10117835SRod.Evans@Sun.COM  * intended use of the path names.  This information may be maintained in the
10128598SRod.Evans@Sun.COM  * path name descriptor element produced to describe the path name (i.e.,
10138598SRod.Evans@Sun.COM  * LA_SER_LIBPATH etc.), or may be used to determine additional security or
10148598SRod.Evans@Sun.COM  * diagnostic processing.
10150Sstevel@tonic-gate  */
10168598SRod.Evans@Sun.COM int
expand_paths(Rt_map * clmp,const char * list,Alist ** alpp,Aliste alni,uint_t orig,uint_t omit)10178598SRod.Evans@Sun.COM expand_paths(Rt_map *clmp, const char *list, Alist **alpp, Aliste alni,
10188598SRod.Evans@Sun.COM     uint_t orig, uint_t omit)
10190Sstevel@tonic-gate {
10200Sstevel@tonic-gate 	char	*str, *olist = 0, *nlist = (char *)list;
10211210Sab196087 	int	fnull = FALSE;	/* TRUE if empty final path segment seen */
10228598SRod.Evans@Sun.COM 	Pdesc	*pdp = NULL;
10230Sstevel@tonic-gate 
10248598SRod.Evans@Sun.COM 	for (str = nlist; *nlist || fnull; str = nlist) {
10250Sstevel@tonic-gate 		char	*ostr;
10268598SRod.Evans@Sun.COM 		char	*elist = NULL;
10270Sstevel@tonic-gate 		size_t	len, olen;
10280Sstevel@tonic-gate 		uint_t	tkns = 0;
10290Sstevel@tonic-gate 
10300Sstevel@tonic-gate 		if (*nlist == ';')
10310Sstevel@tonic-gate 			++nlist, ++str;
10321210Sab196087 		if ((*nlist == ':') || fnull) {
10331210Sab196087 			/* If not a final null segment, check following one */
10341210Sab196087 			fnull = !(fnull || *(nlist + 1));
10351210Sab196087 
10361210Sab196087 			if (*nlist)
10371210Sab196087 				nlist++;
10381210Sab196087 
10391210Sab196087 			/*
10401210Sab196087 			 * When the shell sees a null PATH segment, it
10411210Sab196087 			 * treats it as if it were the cwd (.). We mimic
10421210Sab196087 			 * this behavior for LD_LIBRARY_PATH and runpaths
10431210Sab196087 			 * (mainly for backwards compatibility with previous
10441210Sab196087 			 * behavior). For other paths, this makes no sense,
10451210Sab196087 			 * so we simply ignore the segment.
10461210Sab196087 			 */
10471210Sab196087 			if (!(orig & (LA_SER_LIBPATH | LA_SER_RUNPATH)))
10481210Sab196087 				continue; /* Process next segment */
10491210Sab196087 
10508598SRod.Evans@Sun.COM 			str = (char *)MSG_ORIG(MSG_FMT_CWD);
10510Sstevel@tonic-gate 			len = MSG_FMT_CWD_SIZE;
10520Sstevel@tonic-gate 
10530Sstevel@tonic-gate 		} else {
10548598SRod.Evans@Sun.COM 			uint_t	_tkns;
10550Sstevel@tonic-gate 
10560Sstevel@tonic-gate 			len = 0;
10570Sstevel@tonic-gate 			while (*nlist && (*nlist != ':') && (*nlist != ';')) {
10588598SRod.Evans@Sun.COM 				if (*nlist == '/')
10598598SRod.Evans@Sun.COM 					tkns |= PD_FLG_PNSLASH;
10600Sstevel@tonic-gate 				nlist++, len++;
10610Sstevel@tonic-gate 			}
10620Sstevel@tonic-gate 
10631210Sab196087 			/* Check for a following final null segment */
10641210Sab196087 			fnull = (*nlist == ':') && !*(nlist + 1);
10651210Sab196087 
10660Sstevel@tonic-gate 			if (*nlist)
10670Sstevel@tonic-gate 				nlist++;
10680Sstevel@tonic-gate 
10690Sstevel@tonic-gate 			/*
10700Sstevel@tonic-gate 			 * Expand the captured string.  Besides expanding the
10710Sstevel@tonic-gate 			 * present path/file entry, we may have a new list to
10720Sstevel@tonic-gate 			 * deal with (ISALIST expands to multiple new entries).
10730Sstevel@tonic-gate 			 */
10740Sstevel@tonic-gate 			elist = nlist;
10750Sstevel@tonic-gate 			ostr = str;
10760Sstevel@tonic-gate 			olen = len;
10778598SRod.Evans@Sun.COM 			if ((_tkns = expand(&str, &len, &elist, orig, omit,
10780Sstevel@tonic-gate 			    clmp)) == 0)
10796040Srie 				continue;
10808598SRod.Evans@Sun.COM 			tkns |= _tkns;
10810Sstevel@tonic-gate 		}
10820Sstevel@tonic-gate 
10830Sstevel@tonic-gate 		/*
10840Sstevel@tonic-gate 		 * If this a secure application, validation of the expanded
10857835SRod.Evans@Sun.COM 		 * path name may be necessary.
10860Sstevel@tonic-gate 		 */
10878598SRod.Evans@Sun.COM 		if ((rtld_flags & RT_FL_SECURE) &&
10888598SRod.Evans@Sun.COM 		    (is_path_secure(str, clmp, orig, tkns) == 0))
10898598SRod.Evans@Sun.COM 			continue;
10906387Srie 
10916387Srie 		/*
10926387Srie 		 * If required, ensure that the string is unique.  For search
10936387Srie 		 * paths such as LD_LIBRARY_PATH, users often inherit multiple
10946387Srie 		 * paths which result in unnecessary duplication.  Note, if
10956387Srie 		 * we're debugging, any duplicate entry is retained and flagged
10966387Srie 		 * so that the entry can be diagnosed later as part of unused
10976387Srie 		 * processing.
10986387Srie 		 */
10998598SRod.Evans@Sun.COM 		if (orig & PD_FLG_UNIQUE) {
11006387Srie 			Word	tracing;
11016387Srie 
11026387Srie 			tracing = LIST(clmp)->lm_flags &
11036387Srie 			    (LML_FLG_TRC_UNREF | LML_FLG_TRC_UNUSED);
11048598SRod.Evans@Sun.COM 			tkns |= is_path_unique(*alpp, str);
11056387Srie 
11066387Srie 			/*
11076387Srie 			 * Note, use the debug strings rpl_debug and prm_debug
11086387Srie 			 * as an indicator that debugging has been requested,
11096387Srie 			 * rather than DBG_ENABLE(), as the initial use of
11106387Srie 			 * LD_LIBRARY_PATH occurs in preparation for loading
11116387Srie 			 * our debugging library.
11126387Srie 			 */
11138598SRod.Evans@Sun.COM 			if ((tkns & PD_FLG_DUPLICAT) && (tracing == 0) &&
11148598SRod.Evans@Sun.COM 			    (rpl_debug == 0) && (prm_debug == 0))
11150Sstevel@tonic-gate 				continue;
11160Sstevel@tonic-gate 		}
11170Sstevel@tonic-gate 
11180Sstevel@tonic-gate 		/*
11198598SRod.Evans@Sun.COM 		 * Create a new pathname descriptor.
11200Sstevel@tonic-gate 		 */
11219340SRod.Evans@Sun.COM 		if ((pdp = alist_append(alpp, NULL, sizeof (Pdesc),
11229340SRod.Evans@Sun.COM 		    alni)) == NULL)
11238598SRod.Evans@Sun.COM 			return (0);
11240Sstevel@tonic-gate 
11258598SRod.Evans@Sun.COM 		pdp->pd_pname = str;
11268598SRod.Evans@Sun.COM 		pdp->pd_plen = len;
11278598SRod.Evans@Sun.COM 		pdp->pd_flags = (orig & LA_SER_MASK) | (tkns & PD_MSK_INHERIT);
11280Sstevel@tonic-gate 
11298598SRod.Evans@Sun.COM 		/*
11308598SRod.Evans@Sun.COM 		 * If token expansion occurred, maintain the original string.
11318598SRod.Evans@Sun.COM 		 * This string can be used to provide a more informative error
11328598SRod.Evans@Sun.COM 		 * diagnostic for a file that fails to load, or for displaying
11338598SRod.Evans@Sun.COM 		 * unused search paths.
11348598SRod.Evans@Sun.COM 		 */
11358598SRod.Evans@Sun.COM 		if ((tkns & PD_MSK_EXPAND) && ((pdp->pd_oname =
11368598SRod.Evans@Sun.COM 		    stravl_insert(ostr, 0, (olen + 1), 1)) == NULL))
11378598SRod.Evans@Sun.COM 			return (0);
11388598SRod.Evans@Sun.COM 
11398598SRod.Evans@Sun.COM 		/*
11408598SRod.Evans@Sun.COM 		 * Now that any duplication of the original string has occurred,
11418598SRod.Evans@Sun.COM 		 * release any previous old listing.
11428598SRod.Evans@Sun.COM 		 */
11438598SRod.Evans@Sun.COM 		if (elist && (elist != nlist)) {
11448598SRod.Evans@Sun.COM 			if (olist)
11458598SRod.Evans@Sun.COM 				free(olist);
11468598SRod.Evans@Sun.COM 			nlist = olist = elist;
11470Sstevel@tonic-gate 		}
11480Sstevel@tonic-gate 	}
11490Sstevel@tonic-gate 
11500Sstevel@tonic-gate 	if (olist)
11510Sstevel@tonic-gate 		free(olist);
11520Sstevel@tonic-gate 
11538598SRod.Evans@Sun.COM 	/*
11548598SRod.Evans@Sun.COM 	 * If no paths could be determined (perhaps because of security), then
11558598SRod.Evans@Sun.COM 	 * indicate a failure.
11568598SRod.Evans@Sun.COM 	 */
11578598SRod.Evans@Sun.COM 	return (pdp != NULL);
11580Sstevel@tonic-gate }
11598598SRod.Evans@Sun.COM 
11608598SRod.Evans@Sun.COM /*
11618598SRod.Evans@Sun.COM  * Establish an objects fully resolved path.
11628598SRod.Evans@Sun.COM  *
11638598SRod.Evans@Sun.COM  * When $ORIGIN was first introduced, the expansion of a relative path name was
11648598SRod.Evans@Sun.COM  * deferred until it was required.  However now we insure a full path name is
11658598SRod.Evans@Sun.COM  * always created - things like the analyzer wish to rely on librtld_db
11668598SRod.Evans@Sun.COM  * returning a full path.  The overhead of this is perceived to be low,
11678598SRod.Evans@Sun.COM  * providing the associated libc version of getcwd is available (see 4336878).
11688598SRod.Evans@Sun.COM  * This getcwd() was ported back to Solaris 8.1.
11698598SRod.Evans@Sun.COM  */
11708598SRod.Evans@Sun.COM size_t
fullpath(Rt_map * lmp,Fdesc * fdp)11718598SRod.Evans@Sun.COM fullpath(Rt_map *lmp, Fdesc *fdp)
11728598SRod.Evans@Sun.COM {
11738598SRod.Evans@Sun.COM 	const char	*name;
11748598SRod.Evans@Sun.COM 
11758598SRod.Evans@Sun.COM 	/*
11768598SRod.Evans@Sun.COM 	 * Determine whether this path name is already resolved.
11778598SRod.Evans@Sun.COM 	 */
11788598SRod.Evans@Sun.COM 	if (fdp && (fdp->fd_flags & FLG_FD_RESOLVED)) {
11798598SRod.Evans@Sun.COM 		/*
11808598SRod.Evans@Sun.COM 		 * If the resolved path differed from the original name, the
11818598SRod.Evans@Sun.COM 		 * resolved path would have been recorded as the fd_pname.
11828598SRod.Evans@Sun.COM 		 * Steal this path name from the file descriptor.  Otherwise,
11838598SRod.Evans@Sun.COM 		 * the path name is the same as the name of this object.
11848598SRod.Evans@Sun.COM 		 */
11858598SRod.Evans@Sun.COM 		if (fdp->fd_pname)
11868598SRod.Evans@Sun.COM 			PATHNAME(lmp) = fdp->fd_pname;
11878598SRod.Evans@Sun.COM 		else
11888598SRod.Evans@Sun.COM 			PATHNAME(lmp) = NAME(lmp);
11898598SRod.Evans@Sun.COM 	} else {
11908598SRod.Evans@Sun.COM 		/*
11918598SRod.Evans@Sun.COM 		 * If this path name has not yet been resolved, resolve the
11928598SRod.Evans@Sun.COM 		 * current name.
11938598SRod.Evans@Sun.COM 		 */
11948598SRod.Evans@Sun.COM 		char		_path[PATH_MAX];
11958598SRod.Evans@Sun.COM 		const char	*path;
11968598SRod.Evans@Sun.COM 		int		size, rsize;
11978598SRod.Evans@Sun.COM 
11988598SRod.Evans@Sun.COM 		if (fdp && fdp->fd_pname)
11998598SRod.Evans@Sun.COM 			PATHNAME(lmp) = fdp->fd_pname;
12008598SRod.Evans@Sun.COM 		else
12018598SRod.Evans@Sun.COM 			PATHNAME(lmp) = NAME(lmp);
12028598SRod.Evans@Sun.COM 
12038598SRod.Evans@Sun.COM 		name = path = PATHNAME(lmp);
12048598SRod.Evans@Sun.COM 		size = strlen(name);
12058598SRod.Evans@Sun.COM 
12068598SRod.Evans@Sun.COM 		if (path[0] != '/') {
12078598SRod.Evans@Sun.COM 			/*
12088598SRod.Evans@Sun.COM 			 * If we can't determine the current directory (possible
12098598SRod.Evans@Sun.COM 			 * if too many files are open - EMFILE), or if the
12108598SRod.Evans@Sun.COM 			 * created path is too big, simply revert back to the
12118598SRod.Evans@Sun.COM 			 * initial path name.
12128598SRod.Evans@Sun.COM 			 */
12138598SRod.Evans@Sun.COM 			if (getcwd(_path, (PATH_MAX - 2 - size)) != NULL) {
12148598SRod.Evans@Sun.COM 				(void) strcat(_path, MSG_ORIG(MSG_STR_SLASH));
12158598SRod.Evans@Sun.COM 				(void) strcat(_path, name);
12168598SRod.Evans@Sun.COM 				path = _path;
12178598SRod.Evans@Sun.COM 				size = strlen(path);
12188598SRod.Evans@Sun.COM 			}
12198598SRod.Evans@Sun.COM 		}
12208598SRod.Evans@Sun.COM 
12218598SRod.Evans@Sun.COM 		/*
12228598SRod.Evans@Sun.COM 		 * See if the path name can be reduced further.
12238598SRod.Evans@Sun.COM 		 */
12248598SRod.Evans@Sun.COM 		if ((rsize = resolvepath(path, _path, (PATH_MAX - 1))) > 0) {
12258598SRod.Evans@Sun.COM 			_path[rsize] = '\0';
12268598SRod.Evans@Sun.COM 			path = _path;
12278598SRod.Evans@Sun.COM 			size = rsize;
12288598SRod.Evans@Sun.COM 		}
12298598SRod.Evans@Sun.COM 
12308598SRod.Evans@Sun.COM 		/*
12318598SRod.Evans@Sun.COM 		 * If the path name is different from the original, duplicate it
12328598SRod.Evans@Sun.COM 		 * so that it is available in a core file.  If the duplication
12338598SRod.Evans@Sun.COM 		 * fails simply leave the original path name alone.
12348598SRod.Evans@Sun.COM 		 */
12358598SRod.Evans@Sun.COM 		if ((PATHNAME(lmp) =
12368598SRod.Evans@Sun.COM 		    stravl_insert(path, 0, (size + 1), 0)) == NULL)
12378598SRod.Evans@Sun.COM 			PATHNAME(lmp) = name;
12388598SRod.Evans@Sun.COM 	}
12398598SRod.Evans@Sun.COM 
12408598SRod.Evans@Sun.COM 	name = ORIGNAME(lmp) = PATHNAME(lmp);
12418598SRod.Evans@Sun.COM 
12428598SRod.Evans@Sun.COM 	/*
12438598SRod.Evans@Sun.COM 	 * Establish the directory name size - this also acts as a flag that the
12448598SRod.Evans@Sun.COM 	 * directory name has been computed.
12458598SRod.Evans@Sun.COM 	 */
12468598SRod.Evans@Sun.COM 	DIRSZ(lmp) = strrchr(name, '/') - name;
12478598SRod.Evans@Sun.COM 	return (DIRSZ(lmp));
12488598SRod.Evans@Sun.COM }
1249